Exploring Edison - SPI
Written by Harry Fairhead   
Thursday, 02 June 2016
Article Index
Exploring Edison - SPI
SPI Functions
A Loop Back Example
Implementing The User Mode Driver
General SPI Problems

The SPI Functions

There are thee functions concerned with enabling and disabling the SPI bus:

mraa_spi_init (int bus)

mraa_spi_stop (mraa_spi_context dev)

mraa_spi_init_raw (
        unsigned int bus, unsigned int cs) 

Initialization

Before you make use of the SPI bus you have to initialize it using:

mraa_spi_context dev SPI=mraa_spi_init (0)

This returns the SPI context  if successful and NULL otherwise. After this the pins allocated to the SPI bus no longer work as general purpose GPIO pins.

When you are finished using the SPI bus you can return the pins to general GPIO lines by calling:

mraa_spi_stop (SPI)

You can also use mraa to initialize a bus without any board configuration using mraa_spi_init_raw, but this isn't something you generally want to do as the basic init doesn't do any initialization if it recognizes that it is working with the mini-expansion board.

Configuration

There are a number of functions that you can use to configure the way the bus works. 

mraa_spi_frequency (mraa_spi_context dev, int hz)
mraa_spi_mode (mraa_spi_context dev, 
                         mraa_spi_mode_t mode)
mraa_spi_lsbmode (mraa_spi_context dev, 
                         mraa_boolean_t lsb)
mraa_spi_bit_per_word (mraa_spi_context dev,
                         unsigned int bits)

The frequency sets the speed of data transfer in Hz

For example:

mraa_spi_frequency (dev, 50000) 

sets the bus frequency to 50kHz.

The theoretical maximum speed is 25MHz but in the current implementation the SPI bus doesn't always work at the speed you set. 

mraa_spi_mode  can be used to set the data transfer to one of:

MRAA_SPI_MODE0 

CPOL = 0, CPHA = 0, Clock idle low, data is clocked in on rising edge, output data (change) on falling edge

MRAA_SPI_MODE1 

CPOL = 0, CPHA = 1, Clock idle low, data is clocked in on falling edge, output data (change) on rising edge

MRAA_SPI_MODE2 

CPOL = 1, CPHA = 0, Clock idle low, data is clocked in on falling edge, output data (change) on rising edge

MRAA_SPI_MODE3 

CPOL = 1, CPHA = 1, Clock idle low, data is clocked in on rising, edge output data (change) on falling edge

 

You can also set the bit order and number of bits in each data transfer:

mraa_spi_lsbmode (dev,TRUE)

mraa_spi_bit_per_word(dev,8)

This sets the bit order to least significant bit first and 8-bit transfers. The lsbmode function seems to have no effect in the current version of mraa. 

Notice that bit_per_word only affects data transferred in word units if it is in the range 16 or less and byte transfers if it is 8 or less.

That is:

  • If you try to send a byte and specify 10 bits - nothing happens.

  • If you try to send a word and specify 10 bits -  only 10 bits are sent.

  • If you specify 5 bits and send a byte - only the low order 5 bits are sent.

You can't specify more than 16 bits for the transmission of a word. 

Although some of the examples in the documentation use the SPI without configuring it, i.e. accepting the defaults, this isn't a good idea. If you do accept the defaults you can find the the bus behaves erratically and incorrectly - e.g. no clock pulse.

Always set the bus to reasonable values such as:

mraa_spi_mode (spi, MRAA_SPI_MODE0 );
mraa_spi_frequency(spi, 400000);
mraa_spi_lsbmode(spi, 0);
mraa_spi_bit_per_word(spi,8); 

Data transfer functions

Because of the way the SPI bus uses a full duplex transfer things are a little different from other buses when it comes to implementing functions to transfer data.  

mraa_spi_write (mraa_spi_context dev,uint8_t data)

mraa_spi_write_word (
               mraa_spi_context dev,uint16_t data) 

mraa_spi_write_buf (mraa_spi_context dev,
                        uint8_t *data, int length)

mraa_spi_write_buf_word (mraa_spi_context dev,
                       uint16_t *data, int length) 

mraa_spi_transfer_buf (mraa_spi_context dev,
        uint8_t *data, uint8_t *rxbuf, int length)

mraa_spi_transfer_buf_word (mraa_spi_context dev,               uint16_t *data, uint16_t *rxbuf, int length)

If you recall that the data transfer sends a byte of data out of the register while shifting in a byte of data then the transfer functions will make sense. 

The most basic of this set of functions is write which sends a single byte to the slave while receiving a single byte sent back. Unlike the underlying protocol it doesn't overwrite the original value with the slave's data.

So, to send and receive data, you would use something like:

uint8_t Send_data=0x55;
int Read_data;
Read_data = mraa_spi_write(dev,Send_data);

Of course, you can always simply throw away the data from the slave if you just want a "write" or send meaningless data to the slave if you just want a "read".

You can specify how many bits are sent using  bit_per_word.
For example:

mraa_spi_bit_per_word(spi,5);
uint8_t Send_data=0x55;
int Read_data;
Read_data = mraa_spi_write(dev,Send_data);

will only send the low-order 5 bits.

The write_word function works in the same way as write but it sends a word containing up to 16 bits without deactivating the CS line. You can use bit_per_word to specify exactly how many bits are sent.

For example:

mraa_spi_bit_per_word(spi,14);
uint16_t trans=0xF000;
uint16_t recv = mraa_spi_write_word(spi,trans );

will transfers the low order 14 bits in trans to recv. In this case this means that recv will contain 0x3000. Not many SPI devices work with anything other than 8 bits.

The remaining functions all send multiple bytes of data stored in a buffer. They differ in how they return the data and each one comes in a byte or a word version. The byte versions always send eight bits at a time and the word versions send anything up to 16 bits. 

Let's look at each one in turn.

The first two send a buffer of data and return a pointer to a buffer of data received. There is a byte version:

mraa_spi_write_buf (dev,data, length)

and a word version:

mraa_spi_write_buf_word (dev, data, length) 

The difference is that in the byte version data is a byte array and in the word version it is a 16-bit array. Again the word version will send the number of bits from each word as set by bit_per_word.

For example,

uint8_t buf[]={0x01,0x02,0x03};
uint8_t *read =mraa_spi_write_buf (spi, buf, 3);

sends three bytes and receives three bytes without deactivating the CS line. It is your responsibility to free up the buffer that the function returns. 

The final two functions work in exactly the same way but with the difference that the received data is stored in a buffer of your choice: 

mraa_spi_transfer_buf (dev, data[],rxbuf[], length)

mraa_spi_transfer_buf_word (dev, data[],
                                   rxbuf[], length)

 

For example:

uint8_t buf[]={0x01,0x02,0x03};
uint8_t recv[3];
mraa_spi_transfer_buf (spi, buf, recv,3);

sends three bytes and receives three bytes, but now into an array that you have created in your program. Using just these functions you should be able to deal with most SPI slaves. 

Now we come to a subtle point.

What is the difference between transferring multiple bytes using write_buf or transfer_buf and simply sending the bytes individually using multiple write calls? 

The answer is that each time you make a write call the chip select line is activated, the data is transferred and then it is deactivated. When you use buffer transfers the chip select is left active for the entire transfer, i.e. it isn't deactivated between each byte.

Sometimes this difference isn't important and you can transfer three bytes using three calls to transfer or one call to tranfernb. However, some slaves will abort the current multibyte operation if the chip select line is deactivated in the middle of a multibyte transfer. 

It is important to realize that the nature of the transfer is that the first byte is sent at the same time that the first byte is received. That is, unlike other protocols, the whole of the send buffer isn't sent before the received data comes back. The whole transfer works a byte at a time - the first byte is sent while the first byte is being received, then the second byte is sent at the same time the second byte is being received and so on. Not fully understanding this idea can lead to some interesting bugs. 



Last Updated ( Thursday, 02 June 2016 )