ECE497 SPI Project

From eLinux.org
Revision as of 04:40, 14 November 2012 by Larmorgs (talk | contribs) (Highlights)
Jump to: navigation, search

Team members: Sean Richardson, Greg Larmore

Grading Template

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

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

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 ~/BeagleBoard/kernel/kernel

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 ~/BeagleBoard/kernel/kernel/driver/char. The files are backed up in the same folder with .orig appended.

Next run the following on the host machine:

host$ make kernel

This will copy the files example.c and 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

Next modify modules.dep to include kernel/drivers/char/example.ko: and kernel/driver/char/lpd8806.ko:

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

bone$ modprobe example
bone$ modprobe lpd8806

User Instructions

Run dmesg or lsmod to make sure the modules have loaded properly. You should now find two folders inside /sys/firmware called example and 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.

Bone P9 pinout.jpg

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

The 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).

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

{{#ev:youtube|8d9n5QUyH38}}

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.

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 /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.

Work Breakdown

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 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.

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.