2016-09-04 90 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到 標誌來清除。」 –