From: Jan "Yenya" Kasprzak Date: Wed, 21 Nov 2012 17:10:40 +0000 (+0100) Subject: adc: average several measurements X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?p=bike-lights.git;a=commitdiff_plain;h=35a6047369df5398282c6e1f2ec03976e6af76e4 adc: average several measurements Also do not define the MUX values in the table in order to be able to set also GSEL and ADMUX5 if needed. Disable the ADC between the conversions. --- diff --git a/firmware/adc.c b/firmware/adc.c index 123dddd..8bc171a 100644 --- a/firmware/adc.c +++ b/firmware/adc.c @@ -3,54 +3,63 @@ #include "lights.h" -/* ADC numbering: PWM LEDs first, then ambient light sensor, battery sensor */ -static unsigned char adc_mux[] = { // pwmleds should be first - // 0: pwmled 1: 1.1V, ADC0,1 (PA0,1), gain 20 - _BV(REFS1) | _BV(MUX3) | _BV(MUX1) | _BV(MUX0), - // 1: pwmled 2: 1.1V, ADC2,1 (PA2,1), gain 20 - _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1), - // 2: pwmled 3: 1.1V, ADC4 (PA5), single-ended - _BV(REFS1) | _BV(MUX2), - // 3: ambient light: 1.1V, ADC5 (PA6), single-ended - _BV(REFS1) | _BV(MUX2) | _BV(MUX0), - // 4: batt voltage: 1.1V, ADC6 (PA7), single-ended - _BV(REFS1) | _BV(MUX2) | _BV(MUX1), -}; - #define AMBIENT_ADC N_PWMLEDS #define BATTERY_ADC (N_PWMLEDS + 1) -#define LAST_ADC (sizeof(adc_mux)/sizeof(adc_mux[0])) +#define NUM_ADCS 5 volatile static unsigned char current_adc; -static unsigned char adc_ignore; +static uint16_t adc_sum; +static unsigned char sum_shift; +static unsigned char adc_vals; -static void start_next_adc() +static void inline setup_mux(unsigned char n) { - while (current_adc > 0) { - --current_adc; - - // test if current_adc should be measured - if (current_adc < N_PWMLEDS && pwmled_needs_adc(current_adc)) - goto found; - if (current_adc == AMBIENT_ADC) - goto found; - // TODO battery sense, etc. + ADCSRA |= _BV(ADEN); // enable ADC + + /* ADC numbering: PWM LEDs first, then ambient light sensor, battery sensor */ + switch (n) { + case 0: // pwmled 1: 1.1V, ADC0,1 (PA0,1), gain 20 + ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX1) | _BV(MUX0); + sum_shift = 3; // 8 measurements + break; + case 1: // pwmled 2: 1.1V, ADC2,1 (PA2,1), gain 20 + ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); + sum_shift = 3; // 8 measurements + break; + case 2: // pwmled 3: 1.1V, ADC4 (PA5), single-ended + ADMUX = _BV(REFS1) | _BV(MUX2); + sum_shift = 3; // 8 measurements + break; + case 3: // ambient light: 1.1V, ADC5 (PA6), single-ended + ADMUX = _BV(REFS1) | _BV(MUX2) | _BV(MUX0); + sum_shift = 0; // 1 measurement + break; + case 4: // batt voltage: 1.1V, ADC6 (PA7), single-ended + ADMUX = _BV(REFS1) | _BV(MUX2) | _BV(MUX1); + sum_shift = 0; // 1 measurement + break; } - // all ADCs have been handled - current_adc = LAST_ADC; - // TODO: kick the watchdog here. - return; -found: - ADMUX = adc_mux[current_adc]; // set up mux, start one-shot conversion - adc_ignore = 1; // ignore first reading after mux change - ADCSRA |= _BV(ADSC); + adc_sum = 0; + adc_vals = 1 << sum_shift; +} + +static void start_next_adc() +{ + if (current_adc > 0) { + current_adc--; + // set up mux, start one-shot conversion + setup_mux(current_adc); + ADCSRA |= _BV(ADSC); + } else { + current_adc = NUM_ADCS; + // TODO: kick the watchdog here. + } } void init_adc() { - current_adc = LAST_ADC; - adc_ignore = 1; + current_adc = NUM_ADCS; ADCSRA = _BV(ADEN) // enable | _BV(ADPS1) | _BV(ADPS0) // CLK/8 = 125 kHz @@ -68,8 +77,9 @@ void init_adc() while ((ADCSRA & _BV(ADIF)) == 0) ; ADCSRA |= _BV(ADIF); // clear the IRQ flag - ADCSRA |= _BV(ADIE); // enable IRQ + + ADCSRA &= ~_BV(ADEN); // disable until needed } void susp_adc() @@ -81,18 +91,25 @@ void susp_adc() ISR(ADC_vect) { // IRQ handler uint16_t adcval = ADCW; -#if 0 - log_byte(0xF3); - log_byte(current_adc); - log_word(adcval); -#endif - - if (adc_ignore) { + if (adc_vals) + // start the next conversion immediately ADCSRA |= _BV(ADSC); - adc_ignore = 0; + else + ADCSRA &= ~_BV(ADEN); // the last one, disable ADC + + if (adc_vals < (1 << sum_shift)) + // drop the first conversion, use all others + adc_sum += adcval; + + if (adc_vals) { + adc_vals--; return; } + // Now handle the (1 << sum_shift) measurements + + adcval = adc_sum >> sum_shift; + if (current_adc < N_PWMLEDS) pwmled_adc(current_adc, adcval); if (current_adc == AMBIENT_ADC) @@ -105,7 +122,7 @@ ISR(ADC_vect) { // IRQ handler void timer_start_adcs() { - if (current_adc == LAST_ADC) // Don't start if in progress + if (current_adc == NUM_ADCS) // Don't start if in progress start_next_adc(); }