ADC 仅在 ATMEGA324PA 上工作一次

ADC only working once on ATMEGA324PA

我有一些代码应该读取几个 ADC 引脚的值,每次围绕换向器循环。

static uint16_t adc0;
static uint16_t adc1;

void init(void) {
    ...
    hw_configure_adcs();
    ...
}

void loop(void) {
    ...
    adc0 = hw_read_adc(0);
    adc1 = hw_read_adc(1);
    ...
}

void hw_configure_adcs(void) {
    ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);
}

uint16_t hw_read_adc(uint8_t n) {
    ADMUX = (1<<REFS0) | (n & 0x07);
    ADCSRA |= (1<<ADSC); // start conversion
    uint16_t count;
    for (count = 0; !(ADCSRA & (1<<ADIF)); count++); // wait for conversion to complete
    // ADCSRA |= (1<<ADIF); // tried with and without this
    return (ADCH << 8) | ADCL; // return ADC value
}

我看到的很奇怪:adc0和adc1的值设置为相同的值并且永远不会改变,直到AVR芯片为restarted/reflashed。

(0.71V时取值为0x00d1,1.00V时取值为0x0128,貌似合理。)

我试过:

从这些测试中,我推断正在读取 ADC,但我遗漏了一些 "clear ADCH and ADCL and get them ready to accept the new reading" 步骤。

我重读了http://www.atmel.com/images/Atmel-8272-8-bit-AVR-microcontroller-ATmega164A_PA-324A_PA-644A_PA-1284_P_datasheet.pdf的第23节很多次,但显然忽略了一些重要的东西。

经过多次谷歌搜索,我发现:http://www.avrfreaks.net/forum/adc-only-happens-once-reset

问题在于 return (ADCH << 8) | ADCL; 被编译为首先读取高位寄存器(如您所料)。

数据表第 252 页说:"Otherwise, ADCL must be read first, then ADCH"。

将我的代码更改为 return ADC 解决了问题。

我对发生的事情的猜测是:

  • 发生了从 ADCH 的读取。
  • 从 ADCL 读取具有锁定 ADC 结果的作用,以防止撕裂。
  • 下一个 ADC 读取无处写入其结果,因为 ADC 结果已锁定。
  • 重复...

您的代码存在问题,您首先阅读了 ADCH。

必须先读取ADCL,再读取ADCH,以确保Data Registers的内容属于同一转换。读取 ADCL 后,ADC 对数据寄存器的访问将被阻止。这意味着,如果已读取 ADCL,并且在读取 ADCH 之前转换完成,则两个寄存器都不会更新且转换结果会丢失。读取 ADCH 时,将重新启用 ADC 对 ADCH 和 ADCL 寄存器的访问。

所以正确的代码应该是-

return ADCL | (ADCH << 8) ;