Files
2026-08-31 23:12:28 -04:00

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;
}