High-speed Analog Reads with AnalogInputArrays

Did you know that since Meadow.OS v1.6, you can use the Direct Memory Access (DMA) feature on the F7 to do Analog to Digital Conversions (ADC) at an incredibly high sampling rate?

While the AnalogInputPort provides a powerful way to do analog port reads and oversampling, the AnalogInputArray adds functionality to sample multiple analog inputs at an incredibly high rate of speed; up to 37,000 samples per second.

Classic AnalogInputPort

To understand how the new functionality differs from traditional analog sampling, consider the following sample code that uses an AnalogInputPort:

var analogIn = Device.CreateAnalogInputPort(
    pin: Device.Pins.A00,
    sampleCount: 5,
    sampleInterval: TimeSpan.FromSeconds(5)
    );

analogIn.Updated += (s, result) =>
{
    Resolver.Log.Info($"Analog event," + 
         $"new voltage: {result.New.Volts:N2}V," + 
         $"old voltage: {result.Old?.Volts:N2}V");
};

In the above sample, an IAnalogInputPort is created IPin, that takes a periodic reading a single analog input every 5 seconds, with an oversample count of 5, which takes 5 consecutive readings and then provides their average to account for noise.

This works great for reading analog sensors like thermistors where you don’t need the value very often; since the AnalogInputPort on the F7Feather can be read up to about twice a second.

But there are many cases where an application needs to read the ADC much faster and the IAnalogInputPort simply cannot deliver the performance needed.

AnalogInputArray

Meadow 1.6 introduced a new type: the AnalogInputArray. This new type has less “frills” than the Port version. For example it has no events, and it does not support Observers. Instead, it is designed to just read data, and read it quickly. Once created, you simply have a single method to update the Array values: Refresh().

For example, assume we want to read 3 Analog input pins, having each fill a 1000-sample buffer as fast as we can. To do this, we simply create an AnalogInputArray with our target pins, then call Refresh 1000 times, copying the read data to a local buffer after every read:

var array = Device.CreateAnalogInputArray(Device.Pins.A00, Device.Pins.A01, Device.Pins.A02);

var readsPerIteration = 1000;
var a0 = new double[readsPerIteration];
var a1 = new double[readsPerIteration];
var a2 = new double[readsPerIteration];

while (true)
{
    // read 1k samples as fast as we can
    for (var i = 0; i < readsPerIteration; i++)
    {
        // tell the Array to read all of its inputs
        array.Refresh();


        // copy the Array data to a local buffer
        a0[i] = array.CurrentValues[0];
        a1[i] = array.CurrentValues[1];
        a2[i] = array.CurrentValues[2];
    }
}

This provides the ability to do very high rates of sampling, and because it’s done via DMA, it won’t tax the processor and lower its ability to perform other tasks!

For more information, check out the High Speed Analog Input guide.