2016-09-04 92 views
3

我现在正在用ATMega328P摆弄一下,想要通过ADC读取来自引脚的模拟值,并将值简单地输出到4个LED。真的很简单AVR ATMega328P ADC通道选择问题

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

这种工作很好,除了渠道选择。当我选择一个频道时,它工作正常,但与所选频道无关,频道0也会一直读取并转换。我的意思是,如果我将电缆插入选定的通道引脚,它会正确读取值。当我插入任何其他通道引脚时,它显然不会,除了ADC0。不管我设置什么通道,不仅是一个读取,还有ADC0。

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

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

ADC4和ADC5似乎也不能正确转换。任何想法,为什么?我在数据表中发现的唯一线索是,这两个使用数字电源,而所有其他ADC使用模拟电源。有什么区别,为什么它很重要,为什么它不正确地转换我的anlogue信号?

ARef和AVCC都根据数据表连接,除ARef的电感器丢失外。

回答

4

认为正在发生的事情是,

ADMUX &= (0xF0); 

在通道设置为0,然后

ADMUX |= (channel & 0x0F); 

是在设置信道给你想要的。然后,您读取数据并将结果丢弃,这意味着初始通道设置为0并不重要。

Howevever,然后当您尝试进行实际读取时,通过使用read_adc读取读数,再次设置通道。所以,你永远不会扔掉一个阅读。

我会做的是更换您的ADMUX设置命令:

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

然后移动到这个所谓的像set_adc_channel(int channel)一个单独的函数。在该功能中添加一个阅读功能,然后从您的read_adc功能中删除ADMUX设置。只需开始转换并获得结果。

另请注意,由于您只使用过一个频道,因此您可以将频道设置部分移至init_adc()。我假设它在一个单独的函数中,以便稍后可以阅读多个频道。

我希望这很清楚。如果不是,请告诉我。

+0

啊,那工作!谢谢! Mino更正,但该行需要为'ADMUX | =(ADMUX&0xF0)| (通道&0x0F);否则,ANDing将阻止正确的位被设置。 –

+1

正确,你发现我的错字。很高兴有帮助。 – js441

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左右的水平。

+0

从数据表:“或者,ADIF通过写入逻辑1到 标志来清除。” –