Sunday, November 4, 2018

Analog to Digital!

When Worlds Collide

Another "feature" of my new improved Arduino architecture is ADC access...

Interrupts

Due to my antipathy for busy waiting -- rather than looking busy I'd prefer to just not do anything -- I implemented an interrupt driven ADC interface.

In the standard Arduino system, calls to analogRead() execute a conversion in place (and block for the time it takes to complete). In my new improved system, conversions free run at the slowest rate available on the Atmega chip in the "background".  And the standard interface calls are redefined to just return the most recent value from the requested channel. This takes a bit of processor -- not user -- attention but makes the getting of values almost immediate.  If one is doing data acquisition one is probably getting ADC values fairly frequently, so it's likely the number of instruction cycles tradeoff is not a big issue.

The number of channels accessed by the background conversion cycle can be set in the schip_analog.h file. Usually 4 is sufficient, but a total of 8 are available on many Atmega chips. Note that I2C communication uses A4 and A5, so if you need them there is a provision for skipping those channels in the sample loop.

ADC Task

A second feature of my interrupt driven conversion code is an averaging algorithm which squeezes 64 samples into one smoothed 8-bit value. This allows a lot of noise and nonsense to be filtered out of the individual conversions. Specifically, for instance, 50-60Hz hum... These values are accessed using analogReadAvg() which will return the most recent average for each channel.

When enabled, the averaging cycle also creates a reasonably timed -- in the multi-millisecond range -- repetitive sample cycle. At the end of each averaging period the ADCTask( num_channels ) function is posted to the task scheduler, which will then execute it very close to the time that all the channel averages become available. The exact timing of this posting is determined by the number of ADC channels being converted:

For the Arduino Uno and PRO-Mini with a 16Mhz Atmega 328, the ADC prescale is set at the maximum 128 clocks, and a single ADC conversion takes about 110uS

With NUM_ADCCHANNEL set to 4:

A four channel ADC cycle takes about 450uS (2222Hz sample rate) so 64 conversion cycles takes about 29mS.

The Leonardo seems to be a bit slower:

A single ADC conversion takes about 120uS, and a four channel ADC cycle takes about 480uS (2085Hz sample rate) so 64 conversion cycles takes about 30.7mS.

With NUM_ADCCHANNEL set to 8 and SCHIPSKIPI2C defined for a total of 6 channels:

A six channel ADC cycle takes about 660uS (1515Hz sample rate) so 64 conversion cycles takes about 43mS.

Audio Data

A tertiary feature -- not used in the Variations context -- is a double buffering scheme for ADC channel 0 which allows 64 10-bit samples to be collected into a local buffer. When the buffer fills it's address is put into the global pointer SchipBUF0, from whence the user can get and manipulate the data. While the user is playing with buffer one, a second buffer is being filled and it's pointer is alternately placed into SchipBUF0. So if you can keep up, you can do some audio processing at a reasonable sample rate. See the SoundBit for an example.

The time between buffer updates and the actual rates for various configurations are listed as the conversion-cycle and sample rate above.

'Kernel' hack

In keeping with not being able to leave anything alone I have made some insertions in the core "kernel" ADC code to remove the old-fashioned analogRead() function. This is included in the "cores" directory of the code bolus. It is not strictly necessary, but is sufficient to avoid confusion and save a few bytes of program memory.

setup()

In all cases, when using the new-improved-interrupt code one needs to kick things off by calling analogRead( n ) once in the setup() method. This will usually return 0, so the value should be ignored. Too bad, in the old world we could have used the value as a random number seed but now have to find some other workaround for that.

More Anon.

No comments:

Post a Comment