ECE497 SPI Project
- 1 Grading Template
- 2 Executive Summary
- 3 Good Websites
- 4 More Good Websites
- 5 Installation Instructions
- 6 User Instructions
- 7 Highlights
- 8 Theory of Operation
- 9 Work Breakdown
- 10 Future Work
- 11 Conclusions
I'm using the following template to grade. Each slot is 10 points. 0 = Missing, 5=OK, 10=Wow!
05 Executive Summary (Good start) 02 Installation Instructions 00 User Instructions (Missing) 00 Highlights (Missing) 00 Theory of Operation (Missing) 00 Work Breakdown 00 Future Work 00 Conclusions 00 Demo 00 Late Comments: I'm looking forward to seeing this. Score: 07/100
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.
More Good Websites
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 email@example.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
~/BeagleBoard/kernel/kernel/driver/char. The files are backed up in the same folder with
Next run the following on the host machine:
host$ make kernel
This will copy the files
lpd8806.c into the
~/BeagleBoard/kernel/kernel/driver/char 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
modules.dep to include
Upon successful completion, you should be able to run the following:
bone$ modprobe example bone$ modprobe lpd8806
lsmod to make sure the modules have loaded properly. You should now find two folders inside
lpd8806. 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
test file holds an unsigned integer, while the
data 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_sclk
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
data file is written directly out to the strand while the
grb file treats the strand like a big shift register. Be careful with using
data. Buffering problems can cause the kernel to seg fault or crash completely (see Future Work).
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
data file instead of the
grb 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.
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.
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
/sys/class with everything else. Another option is that it could be implemented as a straight up device/kernel driver making it show up in
/dev 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.
- 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
- Researched kernel drivers and sysfs
- Helped debug the LED strand
- Wrote example sysfs driver
- Integrated the SPI interface into the sysfs driver
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.
There is a problem with buffered output going into the
data 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
/lib/modules/3.2.25+/kernel/drivers/char and run out of
/sys/firmware 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.
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.