/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See the file COPYING * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Copyright (C) 2010 Frank van Maarseveen */ #include #include #include "common.h" #include "hardware.h" #include "types.h" #include "bits.h" #include "adc.h" struct adc_config { uint8_t admux; /* ADC config for this measurement */ uint8_t irqcount; /* # measurements before picking another admux, >=2 */ uint8_t order; /* need (1 << order) samples. should be <= 6 (10 bit ADC) */ void (*callback)(uint16_t value); uint8_t count; /* # samples we have */ uint16_t sum; /* of the samples */ }; /* * ADC clock frequency is 125kHz => 8µs. Initial sample takes 25 cycles => 200µs. * Subsequent samples take 14 cycles => 112µs. Result time is approximately: * * order 4: 1.8ms * order 5: 3.6ms * order 6: 7.2ms * * but actual times are appended in comment below (they may change as the code changes) */ static struct adc_config adc_config[] = { /* admux irqcount order callback */ { ILED_ADMUX, 8, 4, adc_iled}, /* 5-6ms */ { VBAT_ADMUX, 8, 4, adc_vbat}, /* 5-6ms */ { TEMP_ADMUX, 1, 6, adc_temp} /* 344..345ms? */ }; static struct adc_config * adcp; /* what we currently measure */ static uint8_t adc_pending; /* # pending samples with current config */ static bool_t adc_discard; /* first sample will be discarded */ static uint8_t adc_maxcount; /* cached value for 1 << adcp->order */ __attribute__((noinline)) static void adc_setup(void) { ADMUX = adcp->admux; adc_pending = adcp->irqcount; adc_discard = true; adc_maxcount = 1 << adcp->order; } void adc_init(void) { /* * Start the prescaler. This should be right after pwm1_init(). */ ADCSRA = _BV(ADEN) | /* enable */ _BV(ADIE) | /* interrupt enable */ BIN8(101); /* 4MHz/32 = 125kHz */ adcp = adc_config; adc_setup(); } void adc_cleanup(void) { ADCSRA = 0; ADMUX = 0; } /* * 0x110 (worst case executed code size) / 2 (bytes per instruction) / 4 (MHz) * = 34 us. A minimalistic callback consisting of ee_min16() and ee_max16() calls * and assuming one of them calls ee_write_async() yields an additional * 0x20+0x1a+0x1a+0x28 = 0x7c code bytes. Summed up this is (0x110+0x7c)/2/4 == 49 us. * * The fast path (taken most of the time) is 0x82/2/4 = 16 us. */ ISR(ADC_vect) { uint16_t value; if (adc_discard) { adc_discard = false; goto out; } adcp->sum += ADCW; if (++adcp->count == adc_maxcount) { value = (adcp->sum + (adc_maxcount >> 1)) >> adcp->order; adcp->count = 0; adcp->sum = 0; adcp->callback(value); } if (!--adc_pending) { if (++adcp == adc_config + ARRAYSIZE(adc_config)) adcp = adc_config; adc_setup(); } out: BIT_SET(ADCSRA, ADSC); }