One led voltmeter (Part 4)
A/D converter optimization…
…that you could also translate in “Why make things simple, when you can them make complex” !
Here, we get rid of the analogRead() function and we will directly use A/D converter registers . It is strongly advised to read the ATMEGA datasheet in order to get a clear idea about the way A/D works, whatever the mode (Single Conversion or Free Running). The adc routine contains a two steps register configuration for setting the MUX channel (ranging from 0 to 5) and the reference voltage on one hand, and the prescaler (time needed to perform conversion) on the other hand.
Once all parameters have been set, a request for conversion is placed.
ADCSRA |= (1 << ADSC);
The conversion completion is monitored in the
while (ADCSRA & (1 << ADSC));
loop
Here is the whole adc routine
int adc(int channel, boolean highSensitivity) { // Analog to digital conversion // Set ADMUX register // MSB LSB // REFS1 REFS0 ADLAR – MUX3 MUX2 MUX1 MUX0 ADMUX = 0x00; // Clear register ADMUX |= channel; // Set mux channel ADMUX |= (1 << REFS0); // Default reference voltage bit if (highSensitivity) { ADMUX |= (1 << REFS1); // Set REFS1 bit } // Set ADCSRA register // MSB LSB // ADEN ADSC ADATE ADIF ADIE ADPS2 ADPS1 ADPS0 ADCSRA = 0x00; // Clear register ADCSRA |= (1 << ADEN); // Enable ADC ADCSRA |= ((1 << ADPS2) | (1 << ADPS1) |(1 << ADPS0)); // Set prescaler ADCSRA |= (1 << ADSC); // Start conversion while (ADCSRA & (1 << ADSC)); // Wait for completion of conversion // Compute 10bits ADC int adcValue = 0x00; adcValue |= ADCL; // LSBs adcValue |= (ADCH << 8); // MSBs return(adcValue); }
And here is the "whole" project
/* One led voltmeter or 'the voltmeter of the poor' Reads analog port 0 from a bare Arduino board Count long lasting flashes for volts and short lasting flashes for tenths of volts Special features: - Auto-ranging - Optimized adc The routine will also print value on serial comm port No warranty, no claims, just fun Didier Longueville invenit et fecit May 2010 */ int inputVoltagePin = 0; void setup(){ DDRB |= (1 << PINB5); // Led pin PORTB &= ~(1 << PINB5); // Turn led off // Blink status led toggleLed(5, 200, 200); Serial.begin(115200); Serial.println("Ready"); } void toggleLed(int cycles,int onTime, int offTime) { for(int i = 0; i < cycles; i++){ PORTB |= (1 << PINB5); // Turn led on delay(onTime); PORTB &= ~(1 << PINB5); // Turn led off delay(offTime); } } int adc(int channel, boolean highSensitivity) { // Analog to digital conversion // Set ADMUX register // MSB LSB // REFS1 REFS0 ADLAR – MUX3 MUX2 MUX1 MUX0 ADMUX = 0x00; // Clear register ADMUX |= channel; // Set mux channel ADMUX |= (1 << REFS0); // Default reference voltage bit if (highSensitivity) { ADMUX |= (1 << REFS1); // Set REFS1 bit } // Set ADCSRA register // MSB LSB // ADEN ADSC ADATE ADIF ADIE ADPS2 ADPS1 ADPS0 ADCSRA = 0x00; // Clear register ADCSRA |= (1 << ADEN); // Enable ADC ADCSRA |= ((1 << ADPS2) | (1 << ADPS1) |(1 << ADPS0)); // Set prescaler ADCSRA |= (1 << ADSC); // Start conversion while (ADCSRA & (1 << ADSC)); // Wait for completion of conversion // Compute 10bits ADC int adcValue = 0x00; adcValue |= ADCL; // LSBs adcValue |= (ADCH << 8); // MSBs return(adcValue); } void loop() { // Read voltage value long tenthsVolts; int DACValue = adc(inputVoltagePin, false); if (DACValue < 200) { // Approx. less than 1 V in digital counts DACValue = adc(inputVoltagePin, true); tenthsVolts = (DACValue * 11L) >> 10; Serial.print("Internal ref.: "); } else { tenthsVolts = (DACValue * 50L) >> 10; Serial.print(" Default ref.: "); } // Compute integer and fractional parts int integerPart = (tenthsVolts / 10); int fracPart = (tenthsVolts % 10); // Send formated value to serial comm port Serial.print(integerPart, DEC); Serial.print("."); Serial.print(fracPart, DEC); Serial.println(" V"); delay(1000); // Flash Volts toggleLed(integerPart, 500, 500); // Pause delay(1000); // Flash tenths of Volt toggleLed(fracPart, 100, 500); // Pause between readings delay(1000); }