zmatt icon

simple gpio.py

zmatt | PRO | 05/30/21 04:03:29 PM UTC (Edited) | 0 ⭐ | 1144 👁️ | Never ⏰ | []
Python |

4.42 KB

|

None

|

0 👍

/

0 👎

# This code assumes /dev/gpio contains symlinks for sysfs gpios.
#
# Example udev rule:
#
#   SUBSYSTEM=="subsystem", KERNEL=="gpio", ACTION=="add", \
#       RUN+="/bin/mkdir -p /dev/gpio"
#
#   SUBSYSTEM=="gpio", ACTION=="add", TEST=="value", ATTR{label}!="sysfs", \
#       RUN+="/bin/ln -sT '/sys/class/gpio/%k' /dev/gpio/%s{label}"
#
# To install, save as e.g. /etc/udev/rules.d/gpio-symlinks.rules
# then "sudo update-initramfs -u" (unless you don't use initramfs) and reboot.
#
# Alternatively, the "name" argument of the functions below also accepts an
# absolute path to the sysfs gpio (e.g. "/sys/class/gpio/gpio42").
#
#
# This code is written to be simple and correct, not necessarily efficient.
# Performance can probably be greatly improved by keeping a file descriptor
# open for the 'value' attribute and using os.pread() and os.write().  This
# would also be step 1 towards supporting input change events.
 
 
from pathlib import Path
 
 
# internal helper functions
 
def _gpio_attribute_path( name, attr ):
    return Path( '/dev/gpio', name, attr )
 
def _gpio_get_attribute( name, attr ):
    return _gpio_attribute_path( name, attr ).read_text().rstrip()
 
def _gpio_set_attribute( name, attr, value ):
    return _gpio_attribute_path( name, attr ).write_text( value )
 
 
# get/set output value (0 or 1) for gpio configured as output.
# read input value (0 or 1) for gpio configured as input.
# trying to set the value of an input will fail, it will not change to output.
 
def gpio_get_value( name ):
    return int( _gpio_get_attribute( name, 'value' ) )
 
def gpio_set_value( name, value ):
    assert type(value) is int and value in (0, 1)
    _gpio_set_attribute( name, 'value', str(value) )
 
 
# get/set direction ('in' or 'out') of gpio.
#
# NOTE: changing the direction of a gpio may not always be allowed, in which
# case attempting to set it will fail even if you're trying to set it to the
# state it is already in.  If you merely want to ensure the gpio direction
# during initialization, check it before trying to set it.
 
def gpio_get_direction( name ):
    return _gpio_get_attribute( name, 'direction' )
 
def gpio_set_direction_in( name ):
    _gpio_set_attribute( name, 'direction', 'in' )
 
# when changing direction to output, the initial output value must be specified
# with a keyword argument, e.g.  gpio_set_direction_out( "foo", value=0 )
def gpio_set_direction_out( name, *, value ):
    level = gpio_get_level_for_value( name, value )
    _gpio_set_attribute( name, 'direction', level )
 
 
# The state of a gpio can be described in two ways:
# - logical value: 0 or 1
# - voltage level: low or high
#
# It is important not to confuse these since the mapping between logical value
# and voltage level is configurable (both in DT and at runtime).
#
# The level corresponding to value=1 is also known as the "active" level, hence
# a gpio configured such that 1=low and 0=high is known as "active-low", while
# one configured such that 0=low and 1=high is known as "active-high".
#
# Below are therefore three ways to get/set this mapping:
# - the value (0 or 1) corresponding to low level
# - the active level ('low' or 'high'), i.e. corresponding to value=1
# - whether gpio is active-low (bool)
#
# Generally speaking you should configure this mapping once and then leave it
# alone.  Ideally you should configure in DT and your software won't need to
# care about any of this and can just use the value-based functions above.
 
def gpio_get_low_value( name ):
    return int( _gpio_get_attribute( name, 'active_low' ) )
 
def gpio_set_low_value( name, value ):
    assert type(value) is int and value in (0,1)
    _gpio_set_attribute( name, 'active_low', str(value) )
 
def gpio_is_active_low( name ):
    return bool( gpio_get_low_value( name ) )
 
def gpio_set_active_low( name, is_active_low ):
    assert type(is_active_low) is bool
    gpio_set_low_value( name, int( is_active_low ) )
 
def gpio_get_active_level( name ):
    return ('low', 'high')[ gpio_get_low_value( name ) ]
 
def gpio_set_active_level( name, level ):
    gpio_set_low_value( name, ('low', 'high').index( level ) )
 
# map between logical value (0 or 1) and voltage level ('low' or 'high')
 
def gpio_get_level_for_value( name, value ):
    return ('low', 'high')[ gpio_get_low_value( name ) ^ value ]
 
def gpio_get_value_for_level( name, level ):
    return ('low', 'high').index( level ) ^ gpio_get_low_value( name )

Comments