One led voltmeter (Part 4)

Part 1, 2, 3, 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);                  
}

Leave a Reply

You must be logged in to post a comment.