mmap and bit wise twiddling - Raspberry Pi

Thomas Heller theller at ctypes.org
Wed May 2 16:05:17 EDT 2012


Am 02.05.2012 19:40, schrieb Petr Jakes:
> Hi,
> I am trying to work with HW peripherals on Raspberry Pi
> To achieve this, it is necessary read/write some values from/to the
> memory directly.
>
> I am looking for some wise way how to organize the bit twiddling.
>
> To set some specific bit, for example, it is necessary:
>      - read 4 bytes string representation (I am using mmap)
>      - transform it to the corresponding integer (I am using numpy)
>      - do some bit masking over this integer
>      - transport integer to the string representation (numpy again)
>      - write it back to the memory
>
> In other words I mean: is there wise way to create an instrument/
> machinery to define Class and then simply define all necessary objects
> and set the bit values over object attributes so the whole bit-
> twiddling remains under the hood.
>
> say:
> LED01 = GPIO(4)  # gpio PIN number 4 is assigned to the LED01 name
> (attribute)
> LED01.on()
> LED01.off()
>   or
>
> gpio = GPIO()
> LED01 = gpio.pin04
> LED01 = 1 # led diode is shining (or gpio pin 4 is set)
> LED01 = 0 # led diode is off

I have an abstract BitVector base-class that allows to get/set single
bits or several bits in a convenient way.  You must define concrete
subclasses which define a _value get/set property that actually
updates the byte or word in the hardware.  I use it to access bits
or groups of bits of I2C devices.

You would basically code like this, assuming an 8-bit GPIO port:

class GPIO(BitVector):
     def __init__(self, address, value=0xFF, nbits=8):
         self.address = address
         super(GPIO, self).__init__(value, nbits)
     def _get_value(self):
         "read an 8-bit value from the hardware as 8-bit integer"
         ...
     def _set_value(self, v):
         "write the 8-bit value 'v' to the hardware"
         ...

then you can do:

gpio = GPIO(0x12345678)

led0 = gpio[4] # bit 4
led0.value = 1 # switch led on
print led0.value # get led status

For multiple bits use this (note that different from standard Python
practices, indexing works inclusive and uses [high_bitnum:low_bitnum]:

port = GPIO(0x12345678)
high_nibble = port[7:4]
print high_nibble.value
low_nibble = port[3:0]
low_nibble.value = 0xF

Thomas
-------------- next part --------------
class BitVector(object):
    """BitVector class, represents mutable integers
    constricted to a certain range.

    Single bits can be get/set by indexing with integers.

    Slices can be used to get/set the bits in this range, as an
    integer constricted to this range also.

    Slices must use [MSB:LSB], and are inclusive.

    >>> [x for x in BitVector(0xAA)]
    [0, 1, 0, 1, 0, 1, 0, 1]
    >>> [x for x in BitVector(0xFF)]
    [1, 1, 1, 1, 1, 1, 1, 1]
    >>>
    >>> bv = BitVector(0)
    >>> bv[6:3] = 0xf
    >>> [x for x in bv]
    [0, 0, 0, 1, 1, 1, 1, 0]
    >>> bv[6:3] = -1
    Traceback (most recent call last):
      ...
    ValueError: value out of allowed range
    >>> bv[6:3] = 16
    Traceback (most recent call last):
      ...
    ValueError: value out of allowed range
    >>> hex(bv)
    '0x78'
    >>> bin(bv)
    '0b1111000'
    >>> 
    """
    def __init__(self, value=0, nbits=8):
        self._nbits = nbits
        self._value = value

    def __index__(self):
        return self._value

    def __hex__(self):
        return hex(self._value)

    def __getitem__(self, index):
        if isinstance(index, slice):
            return self._getslice(index)
        if not 0 <= index < self._nbits:
            raise IndexError("invalid index")
        return int(bool(self._value & (1 << index)))

    @property
    def bits(self):
        """
        Return some bits as an object that has a .value property.  The
        .value property represents the current value of the specied
        bits.

        b = BitVector().bits[7:0]
        b.value = ...
        print b.value
        """
        class Bits(object):
            def __init__(self, bv):
                self.bv = bv
            def __getitem__(self, index):
                class GetSet(object):
                    def __init__(self, bv):
                        self.bv = bv
                    def _get(self):
                        return self.bv[index]
                    def _set(self, value):
                        self.bv[index] = value
                    value = property(_get, _set)
                return GetSet(self.bv)
        return Bits(self)

    def _getslice(self, index):
        if index.step is not None:
            raise ValueError("extended slicing not supported")
        stop, start = index.stop, index.start
        if start <= stop or start < 0 or stop >= self._nbits:
            print "START, STOP", start, stop
            raise ValueError("invalid slice range")
        mask = (1 << (start+1)) - 1
        return (self._value & mask) >> stop

    def _setslice(self, index, value):
        if index.step is not None:
            raise ValueError("extended slicing not supported")
        stop, start = index.stop, index.start
        if start <= stop or start < 0 or stop >= self._nbits:
            print "START, STOP", start, stop
            raise ValueError("invalid slice range")
        mask = (1 << (start+1)) - 1
        mask &= ~((1 << stop) - 1)
        value = value << stop
        if value & ~mask:
            raise ValueError("value out of allowed range")
        v = self._value
        v &= ~mask
        v |= mask & value
        self._value = v

    def __setitem__(self, index, value):
        value = int(value)
        if isinstance(index, slice):
            self._setslice(index, value)
            return
        if not 0 <= index < self._nbits:
            raise IndexError("invalid index")
        mask = 1 << index
        if not 0 <= value <= 1:
            raise ValueError("value must be 0 or 1")
        if value:
            self._value |= mask
        else:
            self._value &= ~mask


More information about the Python-list mailing list