I recently received an email from Mark Stephens (mrmorphic on github) pointing out that I could be using the AM3359's GPIO_SETDATAOUT and GPIO_CLEARDATAOUT to set the state of the BeagleBone's GPIO pins in the digitalWrite() routine of my PyBBIO library (Mark has been working on a similar library in Go at github.com/mrmorphic/hwio). I had previously been setting pin states the way I'm used to doing it on AVR and MSP430 microcontrollers, which is to grab the entire pin state register (GPIO_DATAOUT on AM335x chips), which might look something like this:

00000010 00000000 00100000 00000000

Then create an equal length value of all 0s with the bit corresponding to the desired pin set to 1, e.g. if setting the state of pin 7 in the register it would 1<<7, or:

00000000 00000000 00000000 10000000

Then to set the pin high, the GPIO_DATAOUT register is set to its previous value bitwise OR-ed with the new value:

   00000010 00000000 00100000 00000000
OR 00000000 00000000 00000000 10000000
 = 00000010 00000000 00100000 10000000

And to then set the pin low, the GPIO_DATAOUT register is set to its value bitwise AND-ed with the inverse, or twiddled version, of the new value:

 ~ 00000000 00000000 00000000 10000000
 = 11111111 11111111 11111111 01111111

    00000010 00000000 00100000 10000000
AND 11111111 11111111 11111111 01111111
  = 00000010 00000000 00100000 00000000

The issue with using this method in PyBBIO is that packing and unpacking 32-bit memory registers in Python is a relatively slow process. Luckily the AM335x MPUs provide two additional registers for manipulating the state of digital output pins: GPIO_CLEARDATAOUT and GPIO_SETRDATAOUT, which are used to set output pins low and high respectively. As soon as a 1 is written to any of the bits in one of these registers, the according pin is set high or low, and the bit is set back to 0. So writing a 0 to a bit in one of these registers will have no effect, because that is the normal state of all the bits. This means that the SETDATAOUT register can only set pins high, and the CLEARDATAOUT register can only set pins low, and we no longer have to worry about maintaining register states. Using these registers in digitalWrite(), we can cut our register access in half.

To see how much of an improvement this change actually makes, I wrote a quick program to toggle the state of a GPIO pin as fast as possible:

from bbio import *

def setup():
  pinMode(GPIO1_6, OUTPUT)

def loop():
  state = 1
    digitalWrite(GPIO1_6, state)
    state ^= 1

run(setup, loop)

I then hooked my scope and ran the program with digitalWrite() not using the SETDATAOUT and CLEARDATAOUT registers, and I clocked it at around 10.6KHz (frequency is in the bottom right of the image):


Then I made the change to digitalWrite() and ran the program again, and low and behold it just about doubled the speed of the toggling to just under 20KHz: