84 lines
2.1 KiB
C
84 lines
2.1 KiB
C
#include "adc.h"
|
|
|
|
#define ADC_CONVERSION_TIMEOUT 1000U
|
|
|
|
// The ADC needs a 50-200 kHz clock. At F_CPU = 8 MHz that is a /64 prescaler
|
|
// (125 kHz); the old /2 ran it at 4 MHz, far out of spec.
|
|
#define ADC_PRESCALER_64 ((1 << ADPS2) | (1 << ADPS1))
|
|
|
|
// The 1.1 V bandgap reference needs time to settle after the mux is switched.
|
|
#define ADC_SETTLE_US 200
|
|
|
|
int8_t adc_Initialize(void)
|
|
{
|
|
// REFS VAL_0x01; ADLAR disabled; MUX adc0;
|
|
ADMUX = 0x40;
|
|
|
|
// ACME disabled; ADTS VAL_0x00;
|
|
ADCSRB = 0x00;
|
|
|
|
ADCSRA = (1 << ADEN) | ADC_PRESCALER_64;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Power (PRR clock gate) and ADEN are managed together, so the ADC draws
|
|
// nothing between readings. Enable rewrites the full config because register
|
|
// access is unreliable while the clock is gated.
|
|
void adc_Disable(void)
|
|
{
|
|
ADCSRA &= ~(1 << ADEN);
|
|
power_adc_disable();
|
|
}
|
|
|
|
void adc_Enable(void)
|
|
{
|
|
power_adc_enable();
|
|
ADCSRA = (1 << ADEN) | ADC_PRESCALER_64;
|
|
}
|
|
|
|
void adc_StartConversion(uint8_t channel)
|
|
{
|
|
if (channel == 0) {
|
|
|
|
ADMUX = 0b01000000;
|
|
} else if (channel == ADC_CHANNEL_BANDGAP) {
|
|
// ADMUX=0b01001110;
|
|
ADMUX = (0x01 << REFS0) | (0 << ADLAR) | (0x0e << MUX0);
|
|
} else {
|
|
ADMUX &= ~0x0f;
|
|
ADMUX |= channel;
|
|
}
|
|
_delay_us(ADC_SETTLE_US);
|
|
ADCSRA |= (1 << ADSC);
|
|
}
|
|
|
|
bool adc_IsConversionDone(void) { return ((ADCSRA & (1 << ADIF))); }
|
|
|
|
uint16_t adc_GetConversionResult(void)
|
|
{
|
|
// ADC reads ADCL then ADCH in the right order. Reading the two volatile
|
|
// registers in one expression leaves the order unspecified, and taking ADCH
|
|
// first breaks the data-register lock and corrupts the result.
|
|
return ADC;
|
|
}
|
|
|
|
uint16_t adc_GetConversion(uint8_t channel)
|
|
{
|
|
adc_StartConversion(channel);
|
|
|
|
// A conversion is 13 ADC clocks (~104 us at 125 kHz); bail out rather than
|
|
// hang if the ADC is disabled or its clock is gated off.
|
|
uint16_t attempts = 0;
|
|
while (!adc_IsConversionDone()) {
|
|
if (++attempts > ADC_CONVERSION_TIMEOUT) {
|
|
return 0;
|
|
}
|
|
_delay_us(10);
|
|
}
|
|
|
|
uint16_t res = adc_GetConversionResult();
|
|
ADCSRA |= (1 << ADIF);
|
|
return res;
|
|
}
|