[英]AVR ATMega328P ADC channel selection issue
我現在正在修補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);
}
}
除了頻道選擇之外,這種方式很好。 當我選擇一個頻道時它工作正常,但獨立於所選頻道,頻道0也總是讀取和轉換。 我的意思是,如果我將電纜插入選定的通道引腳,它會正確讀取值。 當我將其插入任何其他通道引腳時,它顯然沒有,除了ADC0。 無論我設置哪個通道,不僅可以讀取,還可以讀取ADC0。
為什么這樣,我該如何解決?
我已經檢查了我的PCB焊接橋,但沒有,我也期望有一些稍微不同的行為。
此外,ADC4和ADC5似乎也沒有正確轉換。 知道為什么會這樣嗎? 我在數據表中找到的唯一線索是,這兩個使用數字電源,而所有其他ADC都使用模擬電源。 有什么區別,為什么它重要,為什么它沒有正確轉換我的anlogue信號?
ARef和AVCC都是根據數據表連接的,但ARef的電感器缺失了。
我認為發生的事情就是這樣
ADMUX &= (0xF0);
將通道設置為0,然后
ADMUX |= (channel & 0x0F);
然后將頻道設置為您想要的頻道。 然后你正在讀取並拋出結果,這意味着初始通道設置為0並不重要。
然而 ,當您嘗試獲取實際讀數時,您將再次設置通道,方法是使用read_adc讀取讀數。 所以,你永遠不會拋棄閱讀。
我要做的是用以下代碼替換你的ADMUX設置命令:
ADMUX = (0xF0) | (channel & 0x0F)
然后將其移動到一個名為set_adc_channel(int channel)
的單獨函數中。 在該函數中包含一次丟棄讀取,然后從read_adc
函數中刪除ADMUX
設置。 只需啟動轉換並獲得結果。
另請注意,由於您只使用一個通道,因此可以將通道設置部件移動到init_adc()
。 我假設它是在一個單獨的函數中,所以你以后可以讀取多個通道。
我希望這很清楚。 如果沒有,請告訴我。
編輯:正如你所說, 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左右。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.