Sunday, 4 March 2018

Raspberry Pi High Speed ADC

A (slightly) faster ADC for the Raspberry Pi

The Problem

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.

The Solution

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.

The Wiring

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
This is the wiring for SPI1, straight out of the sample SPI code as:
#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              GPIOA
Plus 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
So, the Pi SPI pins, plus GPIO4 as the input strobe pin.

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.