ECE497 SPI Project

Team members: Sean Richardson, Greg Larmore

Executive Summary
For this project we wanted to further improve documentation available for sysfs kernel drivers with the Beagle Bone. To do this we have interfaced an LED light strand by Adafruit with a sysfs kernel driver utilizing SPI. In doing so we have created an easy to use interface to the light strand while also maximizing data throughput.

Good Websites
BeagleBoard and SPI

O'Reilly Book on Linux Device Drivers

Kset example for sysfs driver

Example SPI device driver

Another example SPI device driver

More Good Websites
Example Kernel Driver (ioctl, no sysfs)

Kernel Driver Tutorial (no sysfs/ioctl)

Installation Instructions 3.2 Kernel
All of these instructions are meant for the A6A BeagleBone running version 3.2.25+ of the kernel. Scripts and Makefiles expect that the kernel be located at

Start by cloning the github repository on both the bone and the host machine by running the following:

both$ git clone git@github.com:larmorgs/FinalProject

On the host machine, run the following:

host$ cd ~/FinalProject host$ ./setup.sh

Make sure you only do this once because it is adding on our modules to the Kconfig and Makefile in. The files are backed up in the same folder with  appended.

Next run the following on the host machine:

host$ make kernel

This will copy the files  and   into the   directory, make the kernel, and then scp the files to the bone.

Moving over to the bone, run the following:

bone$ cd /lib/modules/3.2.25+ bone$ mv modules.dep.bin modules.dep.bin.orig

Next modify  to include   and

Upon successful completion, you should be able to run the following:

bone$ modprobe example bone$ modprobe lpd8806

Installation Instructions 3.8 Kernel
All of these instructions are meant for the BeagleBone running version 3.8 of the kernel with device tree. Scripts and Makefiles expect that the kernel be located at

Start by updating the course materials. (EBC Exercise 05 Getting Exercise Support Materials)

On the host machine, run the following:

host$ cd ~/exercises/LEDstring/driver host$ git pull host$ ./setup.sh

Make sure you only do this once because it is adding on our modules to the Kconfig and Makefile in. The files are backed up in the same folder with  appended.

Next run the following on the host machine:

host$ make kernel

This will copy the file  into the   directory, make the kernel, and then scp the files to the bone. (Look in the Makefile and update the address for you Beagle.)

Moving over to the bone, run the following:

bone$ cd exercises/LEDstring/driver bone$ git pull bone$ make dt

This will make the correct device tree overlay to set the pin muxxes for spi0. You only have to do this once per booting the beagle. Next:

bone$ cd /lib/modules/3.8.13 (or whatever path is correct for you system.) bone$ mv modules.dep.bin modules.dep.bin.orig

Next modify  to include   (This didn't work for me.)

Upon successful completion, you should be able to run the following:

bone$ cd /lib/modules/3.8.13-bone19/kernel/drivers/char/ bone$ insmod lpd8806

Now test it with bone$ cd ~/exercises/LEDstring bone$ make tree bone$ ./tree 3 60000 20000

This will display a sequence of LEDs running up and down the string.

User Instructions 3.2 Kernel
Run  or   to make sure the modules have loaded properly. You should now find two folders inside  called   and. Feel free to play around in the example folder. If you go into this folder and then one folder further into device, you will find two files. The  file holds an unsigned integer, while the   file holds a string of length 10 (initially empty). You can read and write from these files as you would any other sysfs file.

Next we will hook up the LED strand. For this make sure the beagle is powered externally. The USB power supply does not have enough current to run the 5m strand at full brightness.

To get started hook up pins 30 (D12/SPI1_D1) and 31 (A13/SPI1_SCLK) to SPI data and SPI clock on the strand (see the silkscreen for guidance. Hook up the ground to ground (obviously), and the 5V wire on the strand to the 3.3V pin on the bone. Do not use the 5V pin on the bone or the suggested Adafruit 5V power supply directly connected to the strand without also adding logic level voltage shifters to the data and clock lines.



You may need to modify your muxes (if they have been changed from the default). If so, do the following: beagle$ echo 0 > /sys/kernel/debug/omap_mux/spi0_d1 beagle$ echo 0 > /sys/kernel/debug/omap_mux/spi0_scl

Now that the strand is hooked up, we can run the following:

bone$ cd /sys/firmware/lpd8806/device/ bone$ echo "127 127 127" > grb

You should see the first LED (closest to the bone) light up bright white. You can also do something like the following:

bone$ echo "127 0 0 0 127 0 0 0 127" > data

The  file is written directly out to the strand while the   file treats the strand like a big shift register. Be careful with using . Buffering problems can cause the kernel to seg fault or crash completely (see Future Work).

User Instructions 3.8 Kernel
Run  or   to make sure the modules have loaded properly. You should now find a folder inside  called.

Next we will hook up the LED strand. For this make sure the beagle is powered externally. The USB power supply does not have enough current to run the 5m strand at full brightness.

To get started hook up pins 18 (D12/SPI0_D1) and 22 (A13/SPI0_SCLK) to SPI data and SPI clock on the strand (see the silkscreen for guidance. Hook up the ground to ground (obviously), and the 5V wire on the strand to the 3.3V pin on the bone. Do not use the 5V pin on the bone or the suggested Adafruit 5V power supply directly connected to the strand without also adding logic level voltage shifters to the data and clock lines.



Now that the strand is hooked up, we can run the following:

bone$ cd /sys/firmware/lpd8806/device/ bone$ echo "127 127 127 0" > rgb

You should see LED 0 (closest to the bone) light up bright white. You can also do something like the following to turn LED x red.

bone$ echo "127 0 0 x" > rgb

The driver keeps an internal state of the string and shifts it out to update the string. (The shifting is so fast you won't see it.) You can see the state of the string with:

bone$ cat data

You can change the internal state without updating the string by suppressing the 'return' on the echo.

bone$ '''echo -n 0 50 0 10 > rgb"

Highlights
We have created a simple C example that uses this sysfs driver. You can run it by doing the following:

bone$ cd ~/FinalProject bone$ make bone$ ./test

The program shows the capabilities of the strand running in shift mode. Because of the nature of the strand's construction, it would be very simple to daisy chain many chains end to end. Also, the kset implementation means that we can have as many separate strands as there are SPI buses. Normally you could have even more, but the strand only runs 3 wire SPI (no chip select).

The speed is increased greatly when the strand is running in direct access mode by using the  file instead of the   file. However, due to time limitations we were unable to debug the buffering issues. A good place for future work would be to figure out how the buffering problem can be alleviated.

Theory of Operation
There are many layers to the implementation, so we will break them out into subsections.

Light Strand
The light strand uses the LPD8806 controller chip. Each chip controls the brightness of two 3 color GRB LEDs using PWM running at 1.2 MHz. SPI is used to communicate with the controller chips. The controller has its SPI bus rated somewhere around 20 MHz, but we are only running at 10 MHz. Each LED is 21-bit color, which leaves three bits available for latch control.

So, as you write color values out on the SPI, the first chip receives them. If the chip hasn't written a color to one of its to LEDs, it does so and thus ends the transmission. However, if it has already assigned a color to each LED then it passes the SPI along on a separate bus to the next chip. This is how the strand is able to be 5m and still run at 10 MHz.

It is still unknown how exactly the 3 latch bits are used (because the chip manufacturer has kept the interface a secret). However, if you write 6 bytes of "latch" the strand restarts at the beginning. Perhaps less bytes of "latch" can be sent and get the same effect, but due to time limitations we were unable to debug this completely. This is a simple place to start for future work.

SPI Bus
We use the default SPI controller to control the SPI lines. All we had to do is find what master is controlling the bus (2 in our case) and add a device. Initially, the SPI bus we want is being used by the default configuration of spidev. So, all we do is unregister the device from spidev and reregister it for our driver. There are a few things that go into the configuration of the device like speed, number of bits, chip select, and mode. Once these are set, you can add the device and it will then be associated with your driver.

Sysfs Kernel Driver
The sysfs kernel driver is based on the references above. It uses a kset to store kobjects (the basic building block of the kernel). Each kobject has associated with it certain attributes. These attributes are brought out to the user as files. When the files are read from or written to, the kernel calls back into the kobject's read/write routines. Here, we are able to decide which attribute is being addressed and manipulate the buffer as we see fit.

There are some alternative implementations to this that were looked into in addition to kset. The driver could have been implemented as a sysfs device driver instead of a kernel driver so that it shows up in  with everything else. Another option is that it could be implemented as a straight up device/kernel driver making it show up in  instead. There doesn't seem to be much of a difference though between these implementations as far as performance is concerned. However, finding where this driver fits best is another good place for future work.

Sean Richardson

 * Researched using SPI on the BeagleBone
 * Debugged the LED strand (the first one we received was broken)
 * Wrote code to interface with the strand using spidev
 * Helped integrate the SPI interface into the sysfs driver

Greg Larmore

 * Researched kernel drivers and sysfs
 * Helped debug the LED strand
 * Wrote example sysfs driver
 * Integrated the SPI interface into the sysfs driver

Future Work
While the strand works well as it is, there are some things that we would like to see looked into if there is future work on this project.

Latching and Controller Interface
According to Adafruit, the interface to this part has been reverse engineered. The one part that really remains unknown is how exactly the 3-bit control interface works for each LED. We were able to achieve a latching effect by sending 6 empty transmissions. The code in [github.com/adafruit/lpd8806 Adafruit's github] doesn't seem to be using this many latch transmissions.

Buffering Problem
There is a problem with buffered output going into the  file (because it is so big). When the output buffers it causes 2 sequential writes which weren't being handled well by the attribute's store function. This could be alleviated with a circular buffer and an index that can be controlled by the user, but really the driver should be able to handle this condition.

Sysfs File Location
The decision to have these drivers located in  and run out of   was pretty much arbitrary. It would be nice to do some research into how the kernel is supposed to be structured so that we can match it. This also applies to the example driver included in this project.

Conclusions
In this project, we were able to get SPI (a lesser used interface on the bone) to work well with our LED strand. More impressively, we were able to interface with the SPI through a sysfs interface. In the process of developing this sysfs interface to the LED strand, we developed an example sysfs driver that can be used in future ECE497 classes. Lastly, we developed a sample application that uses this sysfs interface for our LED strand to show off the results.