ADC 暫存器
ADC (Analog to Digital Convertor) 是類比轉數位的模組。ADC 受底下這幾個暫存器控制:
- ADMUX: 參考電壓的選擇 (透過 REFS0 和 REFS1 位元),轉換結果靠右或靠左對齊的選擇 (透過 ADLAR 位元)以及 Channel 的選擇 (透過 MUX3:MUX0 位元)
- ADCSRA 暫存器: 啟用 ADC(ADEN), 開始 ADC 轉換 (ADSC), 啟用中斷 (ADIE) 以及 Prescale 的設定 (透過ADPS2:ADPS0 位元)
- ADCL 及 ADCH 暫存器: 存放 ADC 轉換結果
ADC 可以用中斷的方式運作,不過不在本文的討論範圍。
底下簡介各個暫存器:
首先是 ADMUX – ADC Multiplexer Selection Register:
值得注意是:
- REFS1 和 REFS0 位元是設定參考電壓,一般來說,使用 AVCC 就可以了 (即 01)
- ADLAR (ADC Left Adjust Result) 用來選擇轉換結果要靠右或靠左對齊,0 代表靠右對齊,1 代表靠左對齊
接著是 ADCSRA – ADC Control and Status Register A:
ADCSRA 是 ADC 模組的主要控制暫存器,用來啟用 (ADEN)、啟動轉換 (ADSC)、設定中斷 (ADIE) 以及選擇 Prescale (ADPS2:ADPS0)。ADPS2:ADPS0 可設定的值為:
值得注意的是,要使用 ADC 轉換:
- 必須把 ADC clock 設在 50 KHz ~ 200 KHz 之間,也就是說,如果是 CPU clock 是 16 MHz,那麼 Prescale 應設為 128,因為 16 MHz / 128 = 125 KHz
- 把 ADEN 設成 1 以啟用 ADC
- 把 ADSC 設定為 1 開始轉換,當轉換完畢時 ADSC 會變成 0
最後是 ADCL 和 ADCH 暫存器:
這兩個暫存器比較簡單,只是用來存放轉換結果。要注意的是,轉換結果會受 ADLAR 位元 (屬於 ADMUX 暫存器)的影響。
要特別注意的是,一定要先讀取 ADCL 再讀 ADCH,因為一讀 ADCL 後,ADCH 就不會再更新,直到 ADCH 被讀取為止。
實驗目的
使用可變電阻 (potentiometer) 控制 LED 的燈光亮度,達到調光的目的。
接線
程式
先來看用 Arduino 寫的程式:
當你旋轉可變電阻,Serial Port 上顯示的數值也會跟著改變,而且 LED 燈號的亮度也會跟著改變。
接著我們自己來寫個 analogRead() 函式:
這個 my_analogRead() 函式跟 Arduino 的 analogRead() 功能是一樣的。
您好, 我在此範例中發現一個可修正之處,
回覆刪除關於ADMUX暫存器選擇ADC Channel(Line34),
此處若使用OR運算更新ADMUX,在切換channel時,
是否可能會造成讀取錯誤:
ex: 若欲讀取channel 0與channel 1,
可能因為OR運算導致MUX0被開啟後便無法清除.
若採用:
ADCMUX = (pin & 0x07) | (1 << REFS0)
似乎可以修正此問題.
煩請版主釋疑解惑:)
非常謝謝您的熱心分享關於Arduino -> AVR的實驗!
祝 順心平安
Rick
ADCMUX = (pin & 0x07) | (1 << REFS0);
回覆刪除你這行跟底下這兩行相比,效果是一樣的喔:
ADMUX |= _BV(REFS0);
ADMUX |= (pin & 0x07);
"若欲讀取channel 0與channel 1, 可能因為OR運算導致MUX0被開啟後便無法清除" ??
應該是不會,因為:
當 pin = 0
pin & 0x07 會等於 0
當 pin = 1
pin & 0x07 會等於 1
所以跑到 pin = 1 的時候,channel 就會從 0 變成 1 了。
謝謝你的鼓勵。
"From Arduino to AVR" 這系列文章可能比較硬一點
沒有人帶的話,可能不容易進入,所以,這系列文章的回應比較少。
不過,只要對人有益,不管回應是多少,只要有回響我就覺得值得了。:)
請問Cooper前輩
回覆刪除ADMUX |= (pin & 0x07);
這段的意思是什麼呢?
為什麼會有0x07這個數字出現
另外pin腳腳位放在這邊的用意是什麼?
為什麼需要和0x07做&?
謝謝~
ADMUX 暫存器的 MUX3:MUX0 是用來選擇 ADC channel 的,比如當 Arduino 下 analogRead(A5) 時,MUX3:MUX0 就要設為 0101,這樣 ADC 就會轉換 channel 5 (對應 Arduino 的 A5) 的類比訊號。
回覆刪除所以,pin 就是 analog pin number,以 UNO 而言是 A0 至 A5 (即 0 至 5)
至於為什麼 pin 要和 0x07 做 AND 動作呢
原因是 UNO 只有 0 至 5 共 6 個 channel
只需用到 MUX2:MUX0 三個位元,為了不影響 ADMUX 暫存器的其它設定,所以就把 pin 跟 0x07 做 AND 動作,然後再 OR 放到 ADMUX 裏。
了解了~
回覆刪除但還是有以下問題
1. 假設我確定我的pin不會寫錯,0x07這樣的設定是否為必要?? 還是說ADMUX |= pin; 其實這樣也ok?
2. 想請教簡單的問題
假設MUX3:MUX0本身裡面的資料在設定前原來是1011
當我們使用ADMUX |= (pin & 0x07);的設定時(pin=5)
那就會變成ADMUX = 1011 & 0101
ADMUX=1111;
這樣的設定就不會是我們原本想要的值(PIN=5)了
請問這樣的問題會出現嗎?
因為先前在您的網誌看到的設定方法都是
TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20);
像這樣一個bit一個bit很謹慎的設定0或是1
但是這邊就比較不一樣, 請問這邊為什麼不使用
ADMUX = _BV(MUX0);這樣的方式設定呢?
謝謝Cooper的解說~~~
假設你確定 pin 不會寫錯,是不需要用 0x07 做 bitmask,寫 ADMUX |= pin 這樣就好沒錯。可是,如果你是寫 library,就像 Arduino 開發者寫 Arduino API 一樣,雖然一般 Arduino 只有 6 個 analog input pins,可是難保使用者不會寫 analogRead(255) 這樣離譜的程式啊。
回覆刪除除非程式去動 MUX3 這個位元,不然它不會是 1 啊。也就是說,MUX3:MUX0 不會預設是 1011,除非程式有去動過 MUX3 的設定才有可能。了解嗎?
ADMUX 開機預設值一定八個位元都是 0,如果沒去動到 MUX3,不然 MUX3 不會是 1。所以那個假設是「在某個但書」下才會發生,比如說 programmer 不呼叫你寫的 API,而是自己去動 ADMUX 暫存器。
ADMUX = _BV(MUX0);這樣的方式設定呢?
哈~ 因為簡單啊!
analogRead(pinNumber), pinNumber = 0 - 5
你想一下,設若 pinNumber 為 5,如果要一個 bit 一個 bit 設定 ADMUX 暫存器,那 analogRead() 該怎麼寫好呢?
analogRead(int pin) {
...
switch (pin) {
case 5:
ADMUX |= _BV(MUX2)| _BV(MUX0);
ADMUX &= ~_BV(MUX1);
...
}
}
這樣的話,analogRead()程式碼會變得很長喔,如果是有 16 組 analog input pins 的 atmega2560,那開發者可能會寫到手痠。haha~
恩恩~~所以較長暫存器的設定都直接使用
回覆刪除暫存器名稱=數字
例如: ADMUX |= 5
這樣就簡單多了~~~~~ :))
Yes, you're right! ^o^
回覆刪除