首页 文章

AVR ATMega328P ADC通道选择问题

提问于
浏览
3

我现在正在修改一个ATMega328P,想要通过ADC读取引脚的模拟值,然后将值输出到4个LED . 真的很简单

#define F_CPU 20000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define BRIGHTNESS_PIN 2
#define ADC_SAMPLES 5

void init_adc()
{
    //set ADC VRef to AVCC
    ADMUX |= (1 << REFS0);
    //enable ADC and set pre-scaler to 128
    ADCSRA = (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) | (1 << ADEN);
}

uint16_t read_adc(unsigned char channel)
{       
    //clear lower 4 bits of ADMUX and select ADC channel according to argument
    ADMUX &= (0xF0);
    ADMUX |= (channel & 0x0F); //set channel, limit channel selection to lower 4 bits

    //start ADC conversion
    ADCSRA |= (1 << ADSC);

    //wait for conversion to finish
    while(!(ADCSRA & (1 << ADIF)));

    ADCSRA |= (1 << ADIF); //reset as required      

    return ADC;
}

int main(void)
{
    uint32_t brightness_total;
    uint16_t brightness = 0;
    uint32_t i = 0;

    init_adc();
    sei();
    while (1) 
    {
        //reset LED pins
        PORTB &= ~(1 << PINB0);
        PORTD &= ~(1 << PIND7);
        PORTD &= ~(1 << PIND6);
        PORTD &= ~(1 << PIND5);

        PORTB |= (1 << PINB1); //just blink

        read_adc(BRIGHTNESS_PIN); //first throw-away read
        //read n sample values from the ADC and average them out
        brightness_total = 0;
        for(i = 0; i < ADC_SAMPLES; ++i)
        {
            brightness_total += read_adc(BRIGHTNESS_PIN);   
        }
        brightness = brightness_total / ADC_SAMPLES;

        //set pins for LEDs depending on read value.
        if(brightness > 768)
        {
            PORTB |= (1 << PINB0);
            PORTD |= (1 << PIND7);
            PORTD |= (1 << PIND6);
            PORTD |= (1 << PIND5);
        }
        else if (brightness <= 768 && brightness > 512)
        {
            PORTB |= (1 << PINB0);
            PORTD |= (1 << PIND7);
            PORTD |= (1 << PIND6);
        }
        else if (brightness <= 512 && brightness > 256)
        {
            PORTB |= (1 << PINB0);
            PORTD |= (1 << PIND7);
        }
        else if (brightness <= 256 && brightness >=64)
        {
            PORTB |= (1 << PINB0);
        }

        _delay_ms(500);
        PORTB &= ~(1 << PINB1); //just blink
        _delay_ms(500);
    }
}

除了 Channels 选择之外,这种方法很好 . 当我选择一个 Channels 时,它工作正常,但独立于所选 Channels , Channels 0也总是读取和转换 . 我的意思是,如果我将电缆插入选定的通道引脚,它会正确读取值 . 当我将其插入任何其他通道引脚时,它显然没有,除了ADC0 . 无论我设置哪个通道,不仅可以读取,还可以读取ADC0 .

为什么这样,我该如何解决?

我已经检查了我的PCB焊接桥,但没有,我也期望有一些稍微不同的行为 .

此外,ADC4和ADC5似乎也没有正确转换 . 知道为什么会这样吗?我在数据表中找到的唯一线索是,这两个使用数字电源,而所有其他ADC都使用模拟电源 . 有什么区别,为什么它重要,为什么它没有正确转换我的anlogue信号?

根据数据表,ARef和AVCC都是连接的,但ARef的电感器缺失了 .

2 回答

  • 0

    编辑:正如你所说, ADIF 真的通过编写逻辑1重置 . 我刚刚测试了你的 adc_read 函数,它对我有用(如果你不介意Arduino混合)

    uint16_t read_adc(unsigned char channel)
    {       
        //clear lower 4 bits of ADMUX and select ADC channel according to argument
        ADMUX &= (0xF0);
        ADMUX |= (channel & 0x0F); //set channel, limit channel selection to lower 4 bits
    
        //start ADC conversion
        ADCSRA |= (1 << ADSC);
    
        //wait for conversion to finish
        while(!(ADCSRA & (1 << ADIF)));
    
        ADCSRA |= (1 << ADIF); //reset as required      
    
        return ADC;
    }
    
    void setup() {
      Serial.begin(57600);
      //set ADC VRef to AVCC
      ADMUX |= (1 << REFS0);
      //enable ADC and set pre-scaler to 128
      ADCSRA = (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) | (1 << ADEN);
    
      pinMode(A0, INPUT_PULLUP);
      pinMode(A1, INPUT_PULLUP);
      pinMode(A2, INPUT_PULLUP);
      pinMode(A3, INPUT_PULLUP);
    }
    
    void loop() {
      Serial.println(read_adc(0));
      Serial.println(read_adc(1)); 
      Serial.println(read_adc(2)); 
      Serial.println(read_adc(3)); 
    
      delay(1000);
    }
    

    我只需将其中一个通道连接到3.3V引脚,它就会读取713 . 其他渠道上调至1017左右 .

  • 4

    我认为发生的事情就是这样

    ADMUX &= (0xF0);
    

    将通道设置为0,然后

    ADMUX |= (channel & 0x0F);
    

    然后将 Channels 设置为您想要的 Channels . 然后你正在读取并抛出结果,这意味着初始通道设置为0并不重要 .

    Howevever ,当您尝试获取实际读数时,您将再次设置通道,方法是使用read_adc读取读数 . 所以,你永远不会抛弃阅读 .

    我要做的是用以下代码替换你的ADMUX设置命令:

    ADMUX = (0xF0) | (channel & 0x0F)
    

    然后将其移动到一个名为 set_adc_channel(int channel) 的单独函数中 . 在该函数中包含一次丢弃读取,然后从 read_adc 函数中删除 ADMUX 设置 . 只需启动转换并获得结果 .

    另请注意,由于您只使用一个通道,因此可以将通道设置部件移动到 init_adc() . 我假设它是在一个单独的函数中,所以你以后可以读取多个通道 .

    我希望这很清楚 . 如果没有,请告诉我 .

相关问题