You are on page 1of 7

18/3/2014

How to Access Chips Over the SPI on BeagleBone Black | Linux.com

Linux Foundation

Training

Events

Video

search linux.com

Home

News

Linux Community

Home
Learn Linux
BeagleBone Black

Learn Linux

Linux Tutorials

Directory

Jobs

How to Access Chips Over the SPI on

How to Access Chips Over the SPI on BeagleBone Black


Tuesday, 05 November 2013 09:19
Like

14

Tw eet

33

Ben Martin

Exclusive

127

The BeagleBone Black is a Single Board Computer for $45 w hich runs an ARM CPU, 2Gb of flash, 512Mb of RAM and
comes w ith many connectors allow ing you to interface w ith electronics. Unlike the Arduino, the BeagleBone Black runs
a full Linux kernel, allow ing you to talk directly to your electronics from the language of your choice and w ith the
comfort of all that RAM. In my previous article I looked at how to talk to the GPIO pins on the BeagleBone Black. This time
around I'm stepping it up to talk to persistent storage in the form of an EEPROM over the Serial Peripheral Interface Bus
(SPI) on the BeagleBone Black.

The SPI allows data to move in both directions from a bus master (controller) to various chips which are
attached to the bus. Because there are many chips on the bus, you need to have some way to stop chips
talking at the same time or reacting to commands you have sent for another chip on the bus. So each chip
gets its own Chip Select line which tells it that it is the active chip and you want to talk to it (and maybe
hear from it).
The normal interface might have a wire to send data from the master to the bus, a wire to get data back
from the bus, a clock wire to control when a new bit is sent and received, and a chip select wire (for each
chip on the bus). The wire to send from the master to the chip (slave) is called the MOSI (Master Out,
Slave In) and the converse for communication from the chip (Slave) is the MISO (Master In). You send
every byte a single bit at a time. To send a bit you set the MOSI line to the current bit you want to send and
use the clock line to tell the chip to grab the current bit. Then send the next bit the same way and so on.
Each time you send a bit, you run one clock and the chip might also send you one bit back on the MISO
line.

Upcoming Training Courses


LFS541 Introduction to Linux KVM Virtualization
25 Mar 25 Mar - Napa, CA
DETAILS
LFS101 Introduction to Linux
14 Apr 17 Apr - Virtual, Online Linux Training
DETAILS
LFD331 Developing Linux Device Drivers
21 Apr 25 Apr - Virtual
DETAILS
View All Upcom ing Courses

Testing the EEPROM with Arduino


A cheap EEPROM that has an SPI interface allows for testing SPI access without the risk of damaging
more expensive hardware. I choose a 512KBit EEPROM which I got for a few dollars as the first SPI
device. If you are worried about the speed of sending things around a bit at a time, that EEPROM can run
at up to 20Mhz clock speed. The speed of sending the data a bit at a time might not be the slowest part of
the system, for example EEPROM chips take time to write data to permanent storage.
I decided to access the EEPROM from Arduino first for two reasons: the Arduino board is cheaper than a
BeagleBone Black, and I could also test that how I thought the EEPROM worked from reading the
datasheet was how it worked when connected and powered on. Knowing that I can read and write to the
EEPROM over SPI from an Arduino was useful when I need to debug issues accessing the EEPROM from
the BeagleBone Black later.
While there are 8 pins on the EEPROM, half of those are connected to power rails (3 to the 3.3v supply
and one to ground). I've shown all power wires in red, and the light blue wires are ground wires (below).
The clock wire is shown in orange, the chip select in blue, and the MOSI and MISO in black and white
respectively.

Tweets
Marcelo H Fortino @fortinux
4m
ACPI, firmware and your security
markshuttleworth.com/archives/1332
#FreeSoftware #Linux
Expand

Linux User&Developer

5m

@LinuxUserMag

#GOG #Linux support coming later


this year!
linuxuser.co.uk/news/gog-linux

Ned Lilly @nedlilly


7m
Intro to #Linux course now free,
open to all red.ht/1g5ThPh >
@linuxfoundation partners with
#MOOC provider @edXOnline,
Compose new Tweet

http://www.linux.com/learn/tutorials/746860-how-to-access-chips-over-the-spi-on-beaglebone-black%22

1/7

18/3/2014

How to Access Chips Over the SPI on BeagleBone Black | Linux.com

Latest Tutorials
How to Set Up Automatic Filesystem Checks and Repair on
Linux
How to set up Open WebMail in CentOS
How to Look Up the Geographic Location of an IP Address from
the Command Line
How to Use the Super Fast i3 Tiling Window Manager on Linux
BeagleBone Black: How to Get Interrupts Through Linux GPIO

Sign Up For the Linux.com Newsletter


First Nam e
Last Nam e
Em ail
Country
Subscribe
View our Privacy Policy

Latest Software News


Mozilla Stops Development of Metro Firefox for Window s 8
Shuttlew orth: ACPI, Firmw are and Your Security
Google Wants to Let Android and iOS Gamers Play Together
Weston Gets Tailored For Tizen IVI, Car Makers Fond Of
Wayland
All power wires are shown in red, and the light blue wires are ground wires (below). The clock

This Week Is GDC: Great New s For Linux Gamers

wire is shown in orange, the chip select in blue, and the MOSI and MISO in black and white
respectively.

The Arduino program to read and write to the EEPROM is shown below. As you send data over the wire
one bit at a time, you need to know what order the bits are to be sent in: that is, should you send the Most
Significant Bit (MSB) first or last? The datasheet for the EEPROM stipulates that it wants the MSB first so
the setup() function ensures that is the order. The chip select pin (10) is also set for output so we can talk
to the EEPROM.
The main loop() of the program is quite simple, it just reads a byte from address 10, shows what it read to
the user, and then writes the current loop iteration count to the byte at address 10. To read a byte from the
EEPROM the READ instruction is sent, followed by the 2 byte address which you wish to read from. One
might wonder about the bold transfer(0) line that follows the read and address, it seems that we should
be reading the byte at that stage, not writing a zero. That transfer(0) call is used to send one byte, which
will clock the SPI 8 times and thus have a byte of data for us from the chip. It doesn't matter what data we
sent over the SPI after the read and address have been sent, the main thing is to clock the SPI so that the
chip can send back the data we want. The reading will stop once we release the chip select line, and then
we have to send another instruction to the EEPROM.
To write to the EEPROM you send the write enable (INSTWREN) instruction, then a WRITE instruction, the
address to write the data to, and then the byte to write. After that I write disable the IC again for
completeness (INSTWRDI). You have to release the chip select after sending INSTWREN for that
instruction to be acted on by the EEPROM. If you left the chip select LOW after writing INSTWREN and
moved right on to issue the WRITE command, the EEPROM would likely ignore your write.
#include <SPI.h>
const byte INSTREAD = B0000011;
const byte INSTWRITE = B0000010;
const byte INSTWREN = B0000110;
const byte INSTWRDI = B0000100;
const int chipSelectPin = 10;
byte loopcount = 1;
void setup()
{
Serial.begin(9600);
// start the SPI library:
SPI.begin();
SPI.setBitOrder( MSBFIRST );

http://www.linux.com/learn/tutorials/746860-how-to-access-chips-over-the-spi-on-beaglebone-black%22

2/7

18/3/2014

How to Access Chips Over the SPI on BeagleBone Black | Linux.com


pinMode(chipSelectPin, OUTPUT);

}
void loop()
{
byte data = 0;
digitalWrite(chipSelectPin, LOW);
// Read the byte at address 10
SPI.transfer(INSTREAD);
SPI.transfer(0);
SPI.transfer(10);
data = SPI.transfer(0); // <- clock SPI 8 bits
digitalWrite(chipSelectPin, HIGH);
// show the user what we have
Serial.print("read data:");
Serial.print( data, BIN );
Serial.print("\n");
// Set write enable, write a byte with the current loop
// and disable write again
digitalWrite(chipSelectPin, LOW);
SPI.transfer(INSTWREN);
digitalWrite(chipSelectPin, HIGH);
digitalWrite(chipSelectPin, LOW);
SPI.transfer(INSTWRITE);
SPI.transfer(0);
SPI.transfer(10);
SPI.transfer(loopcount);
digitalWrite(chipSelectPin, HIGH);
digitalWrite(chipSelectPin, LOW);
SPI.transfer(INSTWRDI);
digitalWrite(chipSelectPin, HIGH);
loopcount++;
delay(5000);
}
I should also mention that the EEPROM can give and get more data for each READ and WRITE instruction
you send. For example, I could have clocked the SPI 8 times again to read the byte at address 11 without
needing to issue another READ instruction. There are other capabilities to the EEPROM too, but for the
purposes of the article the EEPROM is just used to check that one can read and write data over SPI.

On the BeagleBone Black and Linux


There are two SPI on the BeagleBone Black: SPI0 and SPI1. The four pins for SPI0 appear mid way down
the P9 header and are connected in the below image. Orange is again the clock and blue for chip select.
Notice that there are d0 and d1 instead of MOSI and MISO. That is because you can choose which is input
or output from the BeagleBone's perspective during software setup. See theSystem Reference Manual
(SRM) for details on the pinouts of the large P8 and P9 headers on the BeagleBone Black. There are
some tables on page 84, though as the document may change, it might be quicker to find by looking for
the "Expansion Header P9 Pinout" table in the list of tables of the SRM. The multiplexing for the P9 header
can also be seen in this table.

The four pins for SPI0 appear mid way down the P9 header and are connected in the below image.
Orange is again the clock and blue for chip select.

http://www.linux.com/learn/tutorials/746860-how-to-access-chips-over-the-spi-on-beaglebone-black%22

3/7

18/3/2014

How to Access Chips Over the SPI on BeagleBone Black | Linux.com

Some chips that can be accessed over the SPI on the BeagleBone will have Linux kernel device drivers.
For example, a real time clock on the SPI might be used to provide the system with /dev/rtc. You can also
directly get at the SPI from your programs by using the Linux kernel spidev device driver in the Linux
kernel. That will give you devices like /dev/spidev1.0 which you can use normal file access like open(2),
read(2), and write(2) to get and put data on the SPI. The Linux kernel will automatically handle holding the
chip select line for you, and will clock your data out at the speed you have set for the spidev.
For the EEPROM there is one slight complication. When you do a write(2) to the file the chip select is set,
data is written, and then the chip select is unset. But, recall from the above that to read a byte from the
EEPROM you have to hold the chip select, write the READ instruction and address, then while still holding
the chip select you read from the SPI to get bytes from the EEPROM. As soon as you drop the chip select
line, the EEPROM takes that as the end of your desired read operation and stops doing that. So you need
a way to write to the SPI and then read from the SPI while holding the chip select the whole time from the
BeagleBone Black.
This leads to the use of the SPI_IOC_MESSAGE interface to ioctl(2). That ioctl allows you to both send and
receive at the same time, or perform a sequence of send and receive and nominate SPI timing and chip
deselection policy between each operation in sequence of commands. The spidev_fdx.c example uses
the ioctl to first send and then receive data over SPI. Though spidev_fdx is a half duplex example it gives
insight into how to use this ioctl interface. You can hold the chip select pin between operations using the
cs_change member of the spi_ioc_transfer structure.
The BeagleBone Black has two SPI that you can access, each having multiple chip select pins associated
with it. The SPI1 shares some pins with the HDMI interface, so you will have to disable HDMI to get at both
SPI. Because the pins on the headers can be used for different things, you need to tell the Linux kernel
what you want to use those pins for. This is done using the device tree overlay. One advantage of this is
that the kernel can manage your pins and stop two things from using the same pins at the same time.
You tell the cape manager to load an overlay by writing the overlay name to the
/sys/devices/bone_capemgr.*/slots file. The cape manager looks in /lib/firmware for a matching dtbo file
describing what pins the overlay uses and what mode those pins need to be in. You can create dtbo files
by compiling dts source files which describe this information in a human readable text format. You can
also instruct the cape manager to load some overlays on boot up, so once you have a configuration you
are happy with then you don't have to play with these overlay files anymore.
The elinux site provides files to enable the SPI0 and SPI1 with D1 as output or input for the BeagleBone
Black. As outlined on elinux the procedure to enable those overlays is as follows. I also manually changed
the owner of the spidev files so that I didn't have to be root in order to talk the the EEPROM. I found that on
Cloud9 GNOME Image 2013.05.27 the automatic cape loading at boot for the BB-SPI0 using the uEnv.txt
optargs didn't work for me. After a software update, automatic loading did work.
root@beaglebone:~# dtc -O dtb -o BB-SPI0-01-00A0.dtbo -b 0 -@ BB-SPI0-0100A0.dts
root@beaglebone:~# cp BB-SPI0-01-00A0.dtbo /lib/firmware/
root@beaglebone:~# echo BB-SPI0-01 > /sys/devices/bone_capemgr.*/slots
root@beaglebone:~# ls -lh /dev/spi*
crw------- 1 root root 153, 0 Oct 16 04:12 /dev/spidev1.0
root@beaglebone:~# chown ben /dev/spi*
The below commands tell udev to always make spidev files owned by my user instead of root. The
udevadm is a very handy command as it will show you not only what things you can match against in your
udev rules file to select the device but also can be run again to test that your rules will match and perform
the expected task.
# udevadm test
/sys/devices/ocp.2/48030000.spi/spi_master/spi1/spi1.0/spidev/spidev1.0
...
SUBSYSTEM=spidev
...
# cat /etc/udev/rules.d/99-spidev.rules
SUBSYSTEM=="spidev", OWNER="ben"

Talking with EEPROM over SPI


So, now that a /dev/spidev1.0 file exists, moving on to talking with the EEPROM over SPI from the
BeagleBone Black. The core of a C++ program is shown below which does the same thing that the above
Arduino does. Every iteration a byte is read from a fixed address and shown and then the current iteration
number is written to that same fixed address on the EEPROM.
int main( int argc, char** argv )
{
EEPROMTest obj( argv[1] );
address_t addr(305);
for( uint8_t loopcount = 0; ; loopcount++ )
{
int d = (int)obj.read( addr );
cerr << "MAIN: read data:" << hex << d << endl;
cerr << "MAIN: write data:" << hex << (int)loopcount << endl;
obj.write( addr, loopcount );

http://www.linux.com/learn/tutorials/746860-how-to-access-chips-over-the-spi-on-beaglebone-black%22

4/7

18/3/2014

How to Access Chips Over the SPI on BeagleBone Black | Linux.com


sleep( 1 );
}
return 0;

}
The EEPROMTest class has a few methods, the read() and write() being the most interesting and a
constructor. The EEPROMTest::write() method is shown below. In a similar way to the Arduino program
first write enable is set, then the WRITE instruction ,address, and byte are sent. This is followed by
disabling write again. The tostr() method converts the instruction to a string for use with the spi_write()
function shown at the end of the code block. The spi_write() is a simple wrapper of write() which sends a
std::string to a file descriptor.
void write( address_t addr, uint8_t b )
{
spi_write( m_fd, tostr(INSTWREN), true );
{
stringstream ss;
ss << INSTWRITE << addr << flush;
ss.write( (const char*)&b, sizeof(uint8_t));
spi_write( m_fd, ss.str(), true );
}
spi_write( m_fd, tostr(INSTWRDI), true );
}
std::string tostr( instruction_t v )
{
std::stringstream ss;
ss << v;
return ss.str();
}
static void spi_write( int fd, std::string v )
{
write( fd, v.data(), v.size() );
}
Each call to write() will grab the chip select, put data on the bus, and release the chip select. This is fine
for sending bytes to write and simple instructions which are "send only" to the EEPROM. But to read from
the EEPROM you have to send the READ instruction and address and hold the chip select while you read
bytes back. The EEPROMTest::read() method uses the spi_transfer() function to send and receive data on
the SPI at the same time. Notice that 4 bytes are sent, the 8 bit READ, the 16 bit address and then the 8 bit
value of "d" which can be any value really as the EEPROM will ignore it. Sending the 8 bits of "d" allows the
SPI bus to send back the 8 bits that are contained in the address we are interested in. So the data
returned from spi_transfer() will have the value of the EEPROM at address addr in its array at data[3].
uint8_t read( address_t addr )
{
uint8_t d = 77;
stringstream ss;
ss << INSTREAD << addr;
int prefixlength = ss.str().length();
ss.write( (const char*)&d, sizeof(uint8_t));
std::string data = spi_transfer( m_fd, ss.str() );
data = data.substr(prefixlength);
return data[0];
}
The spi_transfer() takes a string to send and will return a string of equal length which is what has been
sent back to the BeagleBone Black over SPI while the transmission was occurring. So for the EEPROM
the first 24 bits of the data we get back are useless. But after we send the 24 bits (READ instruction and
16 bit address) the next byte sent back is the byte on the EEPROM at that address. By providing the
transmission buffer (tx_buf) and the receive buffer (rx_buf) of the same size, the single ioctl() will perform
both read and write on the SPI at the same time.
static std::string spi_transfer( int fd, std::string v )
{
struct spi_ioc_transfer xfer[2];
int status;
memset(xfer, 0, sizeof xfer);
std::string ret;
ret.resize( v.size() );
xfer[0].tx_buf = (unsigned long)v.data();
xfer[0].rx_buf = (unsigned long)ret.data();
xfer[0].len = v.size();
xfer[0].delay_usecs = 200;
status = ioctl(fd, SPI_IOC_MESSAGE(1), xfer);
if (status < 0) {
perror("SPI_IOC_MESSAGE");

http://www.linux.com/learn/tutorials/746860-how-to-access-chips-over-the-spi-on-beaglebone-black%22

5/7

18/3/2014

How to Access Chips Over the SPI on BeagleBone Black | Linux.com


return "";
}
return ret;

}
The program output looks like the following. In this case I had run 5 iterations the previous time I executed
spibbb which is why the first read got a 5. The top lines are using a slightly modified dumpstat() from
the spidev_fdx.c example to show the SPI settings for the selected spidev device file.
$ ./spibbb /dev/spidev1.0
opened file /dev/spidev1.0
before applying settings: spi mode 0, 8 bits (msb) per word, 500000 Hz max
after applying settings: spi mode 0, 8 bits (msb) per word, 500000 Hz max
MAIN: read data:5
MAIN: write data:0
MAIN: read data:0
MAIN: write data:1
MAIN: read data:1
MAIN: write data:2
The EEPROMTest constructor shows the current SPI settings for the device and then changes things to
ensure the most significant bit is sent first, the clock speed of the SPI is acceptable, and the SPI mode is
something that the EEPROM IC will use.
dumpstat("before applying settings", m_fd );
uint8_t lsb = 0;
ioctl( m_fd, SPI_IOC_WR_LSB_FIRST, &lsb );
uint32_t speed = 500000;
ioctl( m_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed );
uint32_t mode = SPI_MODE_0;
ioctl( m_fd, SPI_IOC_WR_MODE, &mode );
dumpstat("after applying settings", m_fd );
In the above, instructions and addresses are written to std::iostreams without regard to the needed 8 and
16 bit sizes that the EEPROM is expecting. This is handled by overloading the output operators for
instruction_t and address_t so that data is written to the output stream as the chip expects.
enum instruction_t
{
INSTREAD = 0b00000011,
INSTWRITE = 0b00000010,
INSTWREN = 0b00000110,
INSTWRDI = 0b00000100,
INSTRDSR = 0b00000101
};
class address_t
{
public:
uint16_t m_v;
address_t( int v = 0 )
: m_v(v)
{
}
};
std::ostream& operator<<( std::ostream& os, const instruction_t& v )
{
os.write( (const char*)&v, sizeof(uint8_t));
return os;
}
std::ostream& operator<<( std::ostream& os, const address_t& v )
{
os.write( (const char*)&v.m_v, sizeof(uint16_t));
return os;
}
Next time I hope to use the BeagleBone Black to talk over SPI to a triple axis accelerometer and a 7segment serial display which is itself running an ATMega328 on it's baseboard. The accelerometer has
some interrupt pins which I'll need to monitor and react to from the Beagle.
For previous articles in this series see:
Getting Started With the BeagleBone Black: A 1GHz ARM Linux Machine for $45
BeagleBone Black Part 2: Linux Performance Tests

Ben Martin

http://www.linux.com/learn/tutorials/746860-how-to-access-chips-over-the-spi-on-beaglebone-black%22

6/7

18/3/2014

How to Access Chips Over the SPI on BeagleBone Black | Linux.com

Comments

Maddox :

27 Jan

Could you post or send by email the full spibb.c file? I've done the arduino version
with success, but I'm having trouble piecing together the BBB snippets. Good
article!

Report

Reply

Name :

Email :

Comment :

Post Comment

Subscribe to Comments

WHO WE ARE ?

EXPLORE

STAY CURRENT

ABOUT LINUX.COM

The Linux Foundation is a non-profit

Answ ers

Netbooks

How to Participate

Blogs

Cloud Computing

Contact / About

Forums

Enterprise

Advertise

Directory

Embedded & Mobile

Privacy / Terms / Editorial Policy

consortium dedicated to the grow th of


Linux.
More About the foundation...
Frequent Questions
Join / Linux Training / Board

Linux.com 2012 Linux.com. All rights reserved.


The Linux Foundation Symbol is a trademark of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.

http://www.linux.com/learn/tutorials/746860-how-to-access-chips-over-the-spi-on-beaglebone-black%22

7/7

You might also like