I'm continuing with the scope creep exercise. As a preparation to make my Analog Shield talk to the SAMA5D4, I've been testing the on-board ADC functionality. I've also measured the AC and DC signal path from signal source to ADC chip in. |
The Digilent / Texas Instruments Analog Shield ADC circuit
Our converter is the Texas Instruments ADS8343 16-Bit, 4-Channel Serial Output Sampling Analog-To-Digital Converter.
The signal goes through 2 opamps before entering the ADC, and it is set up in such a way that both positive and negative signals can be sampled.
The first opamp, TI's LM837 Low Noise Quad Operational Amplifier.
It has two duties.
It more than halfs the input signal (0.4 amplification).
And it adds a DC component to the signal.
Check out the schematics for the Analog Shield for the voltage reference design. The circuit, has been taken from TI's application note "Miro Oljaca and Bonnie Baker - How the Voltage Reference Affects ADC Performance Part 3 - Analog Applications Journal (4Q 2009)". |
With my so-so voltmeter I measure a DC out perfectly matching what's sent at the input (2.03V).
The output is balancing around 2.86mV with floating input (this will be the case in my situation, where I have a reasonable high impedance input).
I've added a dc blocking cap at the input because the setup with the first opamp would inject DC into the pickup coil of my turntable.
The signal, clamped on positive and negative side by a pair of Schottky diodes (SDM40E20LS) in a single package, is sent to a second opamp buffer: TI's OPA4322 rail-to-rail CMOS opamp.
That one drives the signal, unchanged, to the ADC.
Sampling
I've used the sampler with the internal power supplies on the analog shield to measure DC capabilities. That's all very easy:
I mounted the shield on the Arduino UNO that came with the challenge, and connected each of the voltage outputs of the shield's power supply connector in turn to the ADC0 input of the ADC connector.
The sketch was only a few lines of code:
#include <analogShield.h> #include <SPI.h> void setup() { Serial.begin(9600); } void loop() { unsigned int sensorValue = analog.read(0); Serial.println(sensorValue); delay(100); }
All worked fine except for one thing. I imported the Analog Shield library with the new Arduino lybrary manager, and I had to add this line to the if defined(__AVR__) section of the library's header file to get a correct compilation:
#include <SPI.h>
Arduino IDE's include strategy be darned *%$£§@.
As a second exercise I connected my turntable to the input and modded my Digital Light Organ Enchantment to work with the Analog Shield ADC.
There was little to change. As a challenge for myself, I added a simple input range optimizer.
In the light organ, the sampled input data is sent to a FFT lib to filter out the highs, mids and lows.
The library range is -128..127. My samples are 0..65535.
But since the range of my audio signal falls roughly between 20000 and 55000 (I sampled the data to check the range, but since my code is dynamically setting it, the actual limits don't really matter), I loose significant resolution if I mix the full range down to -128..127.
To limit the loss in resolution, I calculate the division factor in my code as a function of the effective minimum and maximum.
I set them at he starting limits of 20000 and 50000. If the signal drops below or peaks above, I adjust the range.
I also added a range aging mechanism. The range narrows in each loop of my code. So the upper limit ages towards the lower limit and vice versa.
Only to be pushed back up or down by the signal's peaks.
The aging is there to prevent that a loud part of the signal puts the limits out and spoil the fun any silent part coming later.
Here's a few highlights of the code adaption.
The preparation and declarations:
// http://arduino-guay.blogspot.com.es // adapted to ti/digilent analog shield jc //... #define AUDIO_MINIMUM 20000U #define AUDIO_MAXIMUM 55000U void setup() { //... // jc removed for analogshield analogReference(INTERNAL); //... }
The loop
void loop() { unsigned int sampleValue = 0U; // signal window static unsigned int maxi = AUDIO_MINIMUM; static unsigned int mini = AUDIO_MAXIMUM; unsigned int range = 0U; //...
Range aging:
//... // age the signal window. Let the signal range slowly go narrow, so that the signal peaks can drive it to a good range if (maxi > AUDIO_MINIMUM) { maxi--; } if (mini < AUDIO_MAXIMUM) { mini++; } //...
Sampling
sampleValue = analog.read(0);
Range calculation and limits adjust
if(sampleValue>maxi) { maxi = sampleValue; } if(sampleValue<mini) { mini = sampleValue; } range = maxi - mini;
Down-calculation to -128..127
data[i] = (sampleValue/(range/256)-128); //... }
As always, the chances for bugs in my calculations are high. Beat me when I goofed up.
Top Comments