SHOW:
|
|
- or go back to the newest paste.
1 | # From @zmatt, i.e. mvduin | |
2 | ||
3 | # This code assumes /dev/gpio contains symlinks for sysfs gpios. | |
4 | # | |
5 | # Example udev rule: | |
6 | # | |
7 | # SUBSYSTEM=="subsystem", KERNEL=="gpio", ACTION=="add", \ | |
8 | # RUN+="/bin/mkdir -p /dev/gpio" | |
9 | # | |
10 | # SUBSYSTEM=="gpio", ACTION=="add", TEST=="value", ATTR{label}!="sysfs", \ | |
11 | # RUN+="/bin/ln -sT '/sys/class/gpio/%k' /dev/gpio/%s{label}" | |
12 | # | |
13 | # To install, save as e.g. /etc/udev/rules.d/gpio-symlinks.rules | |
14 | # then "sudo update-initramfs -u" (unless you don't use initramfs) and reboot. | |
15 | # | |
16 | # Alternatively, the "name" argument of the functions below also accepts an | |
17 | # absolute path to the sysfs gpio (e.g. "/sys/class/gpio/gpio42"). | |
18 | # | |
19 | # | |
20 | # This code is written to be simple and correct, not necessarily efficient. | |
21 | # Performance can probably be greatly improved by keeping a file descriptor | |
22 | # open for the 'value' attribute and using os.pread() and os.write(). This | |
23 | # would also be step 1 towards supporting input change events. | |
24 | ||
25 | ||
26 | from pathlib import Path | |
27 | ||
28 | ||
29 | # internal helper functions | |
30 | ||
31 | def _gpio_attribute_path( name, attr ): | |
32 | return Path( '/dev/gpio', name, attr ) | |
33 | ||
34 | def _gpio_get_attribute( name, attr ): | |
35 | return _gpio_attribute_path( name, attr ).read_text().rstrip() | |
36 | ||
37 | def _gpio_set_attribute( name, attr, value ): | |
38 | return _gpio_attribute_path( name, attr ).write_text( value ) | |
39 | ||
40 | ||
41 | # get/set output value (0 or 1) for gpio configured as output. | |
42 | # read input value (0 or 1) for gpio configured as input. | |
43 | # trying to set the value of an input will fail, it will not change to output. | |
44 | ||
45 | def gpio_get_value( name ): | |
46 | return int( _gpio_get_attribute( name, 'value' ) ) | |
47 | ||
48 | def gpio_set_value( name, value ): | |
49 | assert type(value) is int and value in (0, 1) | |
50 | _gpio_set_attribute( name, 'value', str(value) ) | |
51 | ||
52 | ||
53 | # get/set direction ('in' or 'out') of gpio. | |
54 | # | |
55 | # NOTE: changing the direction of a gpio may not always be allowed, in which | |
56 | # case attempting to set it will fail even if you're trying to set it to the | |
57 | # state it is already in. If you merely want to ensure the gpio direction | |
58 | # during initialization, check it before trying to set it. | |
59 | ||
60 | def gpio_get_direction( name ): | |
61 | return _gpio_get_attribute( name, 'direction' ) | |
62 | ||
63 | def gpio_set_direction_in( name ): | |
64 | _gpio_set_attribute( name, 'direction', 'in' ) | |
65 | ||
66 | # when changing direction to output, the initial output value must be specified | |
67 | # with a keyword argument, e.g. gpio_set_direction_out( "foo", value=0 ) | |
68 | def gpio_set_direction_out( name, *, value ): | |
69 | level = gpio_get_level_for_value( name, value ) | |
70 | _gpio_set_attribute( name, 'direction', level ) | |
71 | ||
72 | ||
73 | # The state of a gpio can be described in two ways: | |
74 | # - logical value: 0 or 1 | |
75 | # - voltage level: low or high | |
76 | # | |
77 | # It is important not to confuse these since the mapping between logical value | |
78 | # and voltage level is configurable (both in DT and at runtime). | |
79 | # | |
80 | # The level corresponding to value=1 is also known as the "active" level, hence | |
81 | # a gpio configured such that 1=low and 0=high is known as "active-low", while | |
82 | # one configured such that 0=low and 1=high is known as "active-high". | |
83 | # | |
84 | # Below are therefore three ways to get/set this mapping: | |
85 | # - the value (0 or 1) corresponding to low level | |
86 | # - the active level ('low' or 'high'), i.e. corresponding to value=1 | |
87 | # - whether gpio is active-low (bool) | |
88 | # | |
89 | # Generally speaking you should configure this mapping once and then leave it | |
90 | # alone. Ideally you should configure in DT and your software won't need to | |
91 | # care about any of this and can just use the value-based functions above. | |
92 | ||
93 | def gpio_get_low_value( name ): | |
94 | return int( _gpio_get_attribute( name, 'active_low' ) ) | |
95 | ||
96 | def gpio_set_low_value( name, value ): | |
97 | assert type(value) is int and value in (0,1) | |
98 | _gpio_set_attribute( name, 'active_low', str(value) ) | |
99 | ||
100 | def gpio_is_active_low( name ): | |
101 | return bool( gpio_get_low_value( name ) ) | |
102 | ||
103 | def gpio_set_active_low( name, is_active_low ): | |
104 | assert type(is_active_low) is bool | |
105 | gpio_set_low_value( name, int( is_active_low ) ) | |
106 | ||
107 | def gpio_get_active_level( name ): | |
108 | return ('low', 'high')[ gpio_get_low_value( name ) ] | |
109 | ||
110 | def gpio_set_active_level( name, level ): | |
111 | gpio_set_low_value( name, ('low', 'high').index( level ) ) | |
112 | ||
113 | # map between logical value (0 or 1) and voltage level ('low' or 'high') | |
114 | ||
115 | def gpio_get_level_for_value( name, value ): | |
116 | return ('low', 'high')[ gpio_get_low_value( name ) ^ value ] | |
117 | ||
118 | def gpio_get_value_for_level( name, level ): | |
119 | return ('low', 'high').index( level ) ^ gpio_get_low_value( name ) |