Yesterday I used Gordon’s WiringPi library to successfully read analogue voltage values from an MCP3002 chip over SPI. My plan for today was to try and read data as fast as possible, to begin building one version of an ultrasonic “bat detector”. However, I got a bit distracted..
When I looked in detail at the logic signals on the SPI bus during the transfer, I could see that it was valid SPI data, but there was something a bit odd about it. The clock signals and the transferred data were mostly as expected, but the /CS signal was held low for much longer than the time needed to transfer the data. There was an unexplained gap of roughly 10us before the data transfer started, and several times that afterward. Adding this to the time needed to clock the two bytes of data for each sample, the time needed to read a single value came to something like 50us, giving an absolute maximum of 20,000 samples per second. This is far below the 200,000 samples per second claimed by the MCP3002 datasheet, and not even enough for decent audio, let alone ultrasonics!
In my looking around the web I had also come across the bcm2835 library which also claimed to provide easy access to SPI from C. I followed the instructions to download, build, and install it:
wget http://www.airspayce.com/mikem/bcm2835/bcm2835-1.25.tar.gz gunzip /bcm2835-1.25.tar.gz tar xvf bcm2835-1.25.tar cd bcm2835-1.25 ./configure make sudo make install
I then copied the source code from examples/spi and edited the spi example to:
#include#include int main(int argc, char **argv) { if (!bcm2835_init()) { printf("oops, could not init bcm2835\n"); return 1; } bcm2835_spi_begin(); bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // The default bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); // The default bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_64); // ~ 4 MHz bcm2835_spi_chipSelect(BCM2835_SPI_CS0); // The default bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // the default uint8_t mosi[10] = { 0x60, 0x00 }; uint8_t miso[10] = { 0 }; bcm2835_spi_transfernb(mosi, miso, 2); printf("Analogue level from SPI: %04x\n", miso[1] + ((miso[0] & 3) << 8)); bcm2835_spi_end(); bcm2835_close(); return 0; }
There are a few things to note here. the first is that in the hope of getting maximum speed out of the transfer, I have increased the clock frequency to roughly 4MHz (by specifying a "clock divider" of 64.) Try as I might I could not get the library to generate sensible SPI data at any of the possible higher frequencies. This is obviously not a fair comparison with the WiringPi tests which set the clock to roughly 1MHz, but it did reveal some fascinating information about how the bcm2835 library SPI code has been written.
As delivered, the bcm2835 library has a lot of 10us delays in the SPI code. This is especially apparent in high-speed SPI like this. At 4MHz, each byte of data takes about 2us, but this is dwarfed by the extra 10us pauses scattered before, between, and after transferred bytes. And it's not even necessary - removing all these delays and letting the io polling "busy wait" made it all much quicker. Almost up to the 200Ksps maximum speed of the MCP3002. There is some further discussion of this in forum posts.
To fully work out whether this is a viable way to get fast analogue data into a Raspberry Pi I will need to go back and look at the WiringPi tests to see how well it works at higher clock rates, and also put together some software to grab lots of samples in a row to see if the resulting signal is stable enough to be usable, and how much other processing can be done while it is being transferred.
Hi Frank, this is a really intriguing. I am using a different ADC (LTC2315) with the spidev drivers (essentially using highly modified spidev_test.c). I am only able to get a max of 20,000 samples per second and, which is well below what I need. Were you really able to accurately read data at 200Ksps? If so, could you post your modified code that you used to get such a high sampling rate?
Thanks for the article!
-Vince
Pingback: ทดลองใช้ SPI Library ภาษา C สำหรับชิป BCM2835 | Raspberry Pi Thailand
did you manage to compile the code ? cos gcc is complaining of data passed to bcm2835_spi_transfernb
main.c:76:38: error: invalid conversion from ‘uint8_t* {aka unsigned char*}’ to ‘char*’ [-fpermissive]