Sunday, December 20, 2015

Working node.js SPI code connecting a Raspberry PI with a PIC32MX

var SPI = require('spi');

var spi = new SPI.Spi('/dev/spidev0.0', {
    'mode': SPI.MODE['MODE_1'],   // clock idle low, clock phase active to idle.
    'chipSelect': SPI.CS['none'], // 'none', 'high' - defaults to low
    'maxSpeed': 8000000
  }, function(s){s.open();});

var rxbuf = new Buffer([ 0x00 ]);

var state = 0;

function writeToSpi(){
    var txbuf = new Buffer([ state ]);
    spi.transfer(txbuf, rxbuf, function(device, buf) {
        console.log("Wrote " + state + " to SPI");
        console.log("Received " + buf[0] + " from slave");
    });

    state++;
    setTimeout(writeToSpi, 100);
}

writeToSpi();

SPI gotchas

I am working on SPI communication between the Raspberry PI and a PIC32MX microcontroller for use in the Xonik M8 polysynth.

SPI is a very easy protocol once you get the hang of it, but it has a few gotchas:

1) Setup - SPI mode

There are two important parameters that control transmission. It is not essential to understand exactly what they do, but it IS important that they are set up the same way on the master and the slave. The parameters are:

CPOL - clock polarity
This defines when the clock is "idle" and when it is "active".  The clock stays in idle mode whenever data is not transferred and goes active once per bit that should be transferred. The two possible states are:

CPOL=0 - clock is idle low, e.g. it is 0 when it is not running
CPOL=1 - clock is idle high, e.g. it is 1 when it is not running

CPHA - clock phase
This defines when new data should be read from and written to the bus. The two modes are "Idle to Active" and "Active to Idle". When the mode is "Idle to Active", data is READ (captured) when the clock changes from idle to active. Data must be WRITTEN to the bus when the clock changes from active to idle.

It is important to remember that CPHA is in relation to CPOL. Idle to Active does NOT always mean when the clock changes from 0 to 1 - if CPOL is 1, Idle to Active happens when the clock changes from 1 to 0.

The two possible states are:

CPHA=0 - Idle to Active, data is read when the clock changes from idle to active
CPHA=1 - Active to Idle, data is read when the clock changes from active to idle

This may sound a bit complex, but unless you are writing your own SPI slave/master code on a device without hardware support, all is taken care of for you. You just have to select the same config on the master and the slave.

The four common modes are:

0 - CPOL=0, CPHA=0 - clock is idle low, data is read on the rising edge
1 - CPOL=0, CPHA=1 - clock is idle low, data is read on the falling edge
2 - CPOL=1, CPHA=0 - clock is idle high, data is read on the falling edge
3 - CPOL=1, CHPA=1 - clock is idle low, data is read on the rising edge

2) Data transfer

Data transfer is full duplex, meaning that data is sent and received at the same time. One bit of data is transfered every clock cycle - in both directions.

If any data exists in the output buffer, it will be sent when the clock ticks once. Similarly, any data on the wire will be put into the input buffer when the clock ticks once. In other words, anything found in the output buffer on the master AND slave will be put on the wire once the clock starts running.

But what does this really mean?
First of all it means that in order to receive bits, the master has to pulse the clock the same number of times as the number of bits it wants to receive.

To do this, many SPI libraries including those on the Raspberry PI and the Microchip microcontrollers require you to actually send a byte. The byte may be zero as we are often only interested in the received data (this, of course, means that the transfer is only half duplex), but it can also be data usefull to the slave such as the next command/address to retrieve.

Secondly, to retrieve data from a slave you first need to transfer a command from the slave to the master. This could be a memory address, an action or similar, telling the slave to fill its output buffer. After the initial command/once the output buffer has been filled, you have to send a second byte to run the clock in order to transfer the data from the slave to the master. As mentioned above, this byte could either be bogus data or a new command, it all depends on how the slave is implemented. (As a side note, it is not strictly necessary to send a command before receiving data. For example, a ADC SPI slave could continously put its latest sampled value into its output buffer, which the master will read whenever it needs a new value from the ADC).


More info about the SPI bus can be found on wikipedia.

SPI and the Microchip PIC using Mikroelektronika's MikroC

There is another gotcha in the naming of SPI states in MikroC. For the 8 bit microcontrollers, CPHA modes are "high to low" and "low to high". These are NOT describing 0 and 1. Instead they are the same as "Active to Idle" and "Idle to Active".