Exploring Edison - SPI AtoD with the SPI Bus
Written by Harry Fairhead   
Monday, 20 June 2016
Article Index
Exploring Edison - SPI AtoD with the SPI Bus
Configuration and Protocol
How Fast?
Listing

How Fast

Once you have the basic facilities working the next question is always how fast does something work. In this case we need to know what sort or data rates we can achieve using this AtoD converter. 

The simplest way fo finding this out is to use the fastest read loop:

for(;;){

int data=readADC(0x5);
}

With a set clock frequency of 60KHz we get a measured clock rate of 60.1kHz the sampling rate is measured to be 1.6K sample/s which is less than the theoretical upper limit of  2.5K samples/s for 24 bits ignoring any dead time between readings. 

If you up the clock rate to 100KHz you will find that while the clock rate does go up to 100KHz the sample rate is only 2.3K samples/s compared to the theoretical upper limit of 4.16K samples/s.

It doesn't matter how fast you attempt to push the clock rate, even to 1MHz you can't do better than about 5K samples/s which is a fundamental bottle neck set by the software.

ADCread

 

Notice that it isn't possible to increase the speed by putting multiple reads into a single transfer because the MCP3008 simply sends zeros after the third byte if the master keeps the clock running. That is you can't use something like:

uint8_t buf[] = {0x01,0x80,0x00,0x01,0x80,0x00};

uint8_t readBuf[6]; 

mraa_spi_transfer_buf (spi, buf, readBuf,6);

To get two readings from the device and so avoid the delays between each group of three bytes. 

Using Software SPI Emulation

One way of getting a higher sampling rate is to use the software emulation introduced at the end of the previous chapter. We could write a general n byte transfer function but as this is specifically aimed at reading the MCP3008 it makes more sense to write a function that transfers three bytes and reads the ADC returning the assembled value.

First we need to add the global variables specifying the GPIO lines to use:

mraa_gpio_context CS1;
mraa_gpio_context SCK;
mraa_gpio_context MOSI;
mraa_gpio_context MISO;

Next we need an initialization function and this is just the initialization code in the last chapter packaged into a function: 

void initSPIsoft() {
CS1 = mraa_gpio_init(9);
mraa_gpio_dir(CS1, MRAA_GPIO_OUT);
mraa_gpio_use_mmaped(CS1, 1);
mraa_gpio_write(CS1, 1);

SCK = mraa_gpio_init(10);
mraa_gpio_dir(SCK, MRAA_GPIO_OUT);
mraa_gpio_use_mmaped(SCK, 1);
mraa_gpio_write(SCK, 0);

MOSI = mraa_gpio_init(11);
mraa_gpio_dir(MOSI, MRAA_GPIO_OUT);
mraa_gpio_use_mmaped(MOSI, 1);
mraa_gpio_write(MOSI, 0);
MISO = mraa_gpio_init(24);
mraa_gpio_use_mmaped(MISO, 1);
mraa_gpio_dir(MISO, MRAA_GPIO_IN);

The read function is essentially the byte transfer function given at the end of the previous chapter but reading three bytes and only changing the CS1 line once a the start and end. The function starts off with some initialization and then it activates the CS1 line: 

int readADCsoft(uint8_t chan, int delay) {
 int i, j;
 int read;
 int result;
 uint8_t byte;
 read = 0;
 mraa_gpio_write(CS1, 0);
 for (j = 1; j < 100; j++) {
 };

The first byte transfer is:

 byte = 0x01;
 for (i = 0; i < 8; i++) {
  mraa_gpio_write(MOSI, byte & 0x80);
  byte = byte << 1;
  for (j = 1; j < delay; j++) {
  };
  mraa_gpio_write(SCK, 1);
  for (j = 1; j < delay / 2; j++) {
  };
  read = read << 1;
  read = read | (mraa_gpio_read(MISO));
  for (j = 1; j < delay / 2; j++) {
  };
  mraa_gpio_write(SCK, 0);
 }

 

Notice we are not interested in what the ADC sends back to us so we can simply ignore the value in read

The second byte starts the data transfer and selects the ADC channel:

 byte = (0x08 | chan) << 4;
 for (i = 0; i < 8; i++) {
  mraa_gpio_write(MOSI, byte & 0x80);
  byte = byte << 1;
  for (j = 1; j < delay; j++) {
  };
  mraa_gpio_write(SCK, 1);
  for (j = 1; j < delay / 2; j++) {
  };
  read = read << 1;
  read = read | (mraa_gpio_read(MISO));
  for (j = 1; j < delay / 2; j++) {
  };
  mraa_gpio_write(SCK, 0);
 }
 result = (int) read & 0x03 << 8;

Notice that this time we collect the bottom two bits of the byte that the ADC sent and store them in result as the most significant bits.

The final byte is the lower eight bits of the result:

 byte = 0;
 for (i = 0; i < 8; i++) {
  mraa_gpio_write(MOSI, byte & 0x80);
  byte = byte << 1;
  for (j = 1; j < delay; j++) {
  };
  mraa_gpio_write(SCK, 1);
  for (j = 1; j < delay / 2; j++) {
  };
  read = read << 1;
  read = read | (mraa_gpio_read(MISO));
  for (j = 1; j < delay / 2; j++) {
  };
  mraa_gpio_write(SCK, 0);
 }

Now all we have to do is deactivate the CS1 line and return the result:

 mraa_gpio_write(CS0, 1);
 for (j = 1; j < 10; j++) {
 };

 return result | read;
}

 

If you try this out with:

int data;
initSPIsoft();
for (;;) {
 data = readADCsoft(0, 0);
}

you will discover that the clock rate is roughly 666KHz and the sample rate is roughly 24K samples per second. You can use lower sampling rates by setting a delay greater than zero. 

As before, notice that this tight sampling loop means that all other processes are locked out of running on the core that your program is running on. 

If you need to go faster than this, the only alternative at the moment is to use some external SPI hardware.  

Summary

  • Making SPI work with any particular device has four steps:
    1. Discover how to connect the device to the SPI.
      This is a matter of identifying pinouts and mostly what chip selects are supported.

    2. Find out how to configure the SPI bus to work with the device. This is mostly a matter of clock speed and mode. 

    3. Identify the commands that you need to send to the device to get it to do something and what data it sends back as a response.

    4. Find, or workout, what the relationship between the raw reading, the voltage and the quantity the voltage represents is. 
  • The Edison has some problems running the SPI bus at high data rates if you cannot group the transaction into a larger block transfer. For the MCP3008 this isn't possible.  

  • Using mraa the fastest data sampling rate from the MCP3008 is 5K samples per second.

  • Using a software simulated SPI bus you can achieve rates of just less than 25K samples per second.


Last Updated ( Monday, 20 June 2016 )