A (slightly) faster ADC for the Raspberry Pi
I've had a couple of cases where I wanted a "Raspberry Pi with an ADC", having a reasonable data acquisition rate and resolution, and had some problems with getting consistent data rates through the system and getting the higher rates.
I'm going to use a separate microcontroller and use that to acquire ADC samples at a very regular sample rate, and then transfer the samples over to the Pi.
For this prototype I'm using the ST Micro STM32F7; it's a microcontroller with multiple built in ADC converters running at 12 bits and up to 2.4M Samples/s individually. The ADC's can also be chained together to achieve higher sample rates (up to 7.2 MSamples/s), but getting a few hundred K/s will be fine for me.
The STM32F7 is available on a few reference boards, and I'm using the Nucleo-F767ZI.
To get data between the STM32 and the Pi I'll use the SPI bus.
The SPI Bus
The SPI bus is a simple three wire serial bus; one side is the master and the other side is the slave. There's a data line sending from the master to the slave (MOSI), a data line sending from the slave to the master (MISO) and a clock line.
The only real distinction between the slave and master is where the clock comes from - the master is the thing that generates the clock, and the slave just receives it.
Although the Pi and STM32 can both be either master or slave, the Linux Pi driver currently only runs as master: this is a slight problem for this case, since ideally we want the STM32 as the source of data to be the master. However we can get around this with an extra signalling pin.
The Extra Strobe Signal?
In the SPI bus then data is transferred whenever the clock line is active. However in this application then we only want data to clock through when the STM32 has something to send.
The ideal solution here would be to make the STM32 the master, and have it set up a transfer and drive the clock when data is ready to send, and the Pi would just listen continually. However as mentioned if we want to use the stock driver then the Pi has to be the master, and the STM32 the slave.
So either the Pi clocks continually, and we need the STM32 to queue up "No data" messages when there is nothing ready, or we need some way for the STM32 to tell the Pi it has data, so the Pi can start the transfer.
This solution uses the second option - a GPIO pin on the STM32 is acting as a "ready" output, and the Pi reads that line, and will start transfers based on the state of that pin.
Everything here is set up as 3v3 pin levels (since the Pi is only 3v3 capable). On the STM Side the pins are connected to CN7 as
- 8: GND
- 12: MISO/PA6
- 14: MOSI/PA7
- 15: CLK/PB3
- 20: PF12
#define SPIx SPI1 ... #define SPIx_SCK_PIN GPIO_PIN_3 #define SPIx_SCK_GPIO_PORT GPIOB ... #define SPIx_MISO_PIN GPIO_PIN_6 #define SPIx_MISO_GPIO_PORT GPIOA ... #define SPIx_MOSI_PIN GPIO_PIN_7 #define SPIx_MOSI_GPIO_PORT GPIOAPlus the strobe pin out is on pin20, which is "Port F, gpio pin 12".
And On the Pi Side the pins are
- 7: GPIO4
- 19: MOSI
- 21: MISO
- 23: CLK
- 25: GND
From the top view of both connectors then it looks like this:
In part 2 we'll cover the overview of the software side of the solution, part 3 will look at the SPI and ADC data rates, and part 4 will cover the implementation structure in more detail.