Please note that User Registration has been temporarily disabled due to a recent increase in automated registrations. If anyone needs an account, please request one here: RequestAccount. Thanks for your patience!--Wmat (talk)
Please email User:Wmat if you experience any issues with the Request Account form.

ECE497 SPI Project

From eLinux.org
Revision as of 18:31, 7 June 2013 by Yoder (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

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

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 ~/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/drivers/char/lpd8806.ko:

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

bone$ modprobe example
bone$ modprobe lpd8806

Installation Instructions 3.8 Kernel

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 ~/BeagleBoard/linux-dev/KERNEL

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$ ./patch.sh

Make sure you only do this once because it is adding on our modules to the Kconfig and Makefile in ~/BeagleBoard/linux-dev/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 file lpd8806.c into the ~/BeagleBoard/linux-dev/KERNEL/driver/char 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
bone$ git pull
bone$ ./setup.sh

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

Now test it with

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

User Instructions 3.8 Kernel

Run dmesg or lsmod to make sure the modules have loaded properly. You should now find a folder inside /sys/firmware called lpd8806.

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.

HeaderP9.jpg

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"

References

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