這篇內容大部份出自 Ken Shirriff "Secrets of Arduino PWM" 一文,所有 credits 均屬 Ken Shirriff。
直接控制 ATmega PWM 的暫存器
ATmega168/328 晶片有三個 PWM timers,控制 6 個 PWM 輸出。直接操控晶片的 timer 暫存器,你會獲得比 analogWrite() 函式更多的控制權。
ATmega328 有三個 Timer: Timer0, Timer1 以及 Timer2。每個 Timer 都有兩個控制 PWM 輸出寬度的 output compare 暫存器: 當 timer 到達 compare 暫存器的值時,對應的輸出就會切換。每個 timer 的兩個輸出的頻率通常是一樣的,不過 duty cycle 可以不一樣,取決於各自的 output compare 暫存器。
每個 Timer 都有一個可以產生 timer clock 的 prescaler,prescaler 提供除頻的效果,除率 (prescale factor) 諸如 1, 8, 64, 246 或 1024。Arduino 的 CPU clock 為 16 MHz,而 timer clock 將是 CPU clock 除以 prescale factor。注意,相較於其它 timers,Timer2 有不一樣的 prescale 設定值。
PWM 有三種模式,底下討論 Fast PWM 和 Phase-correct PWM 兩種模式。Timer 可以從 0 跑到 255 (16-bit 的 Timer1 可以到 65535),或是跑到一個固定的值。每個 PWM 輸出也可以反相 (invert)。
Timers 可以在 overflow 時產生中斷,或者跟 output compare 內容匹配時產生中斷,不過這部份不在本文的討論範圍。
Timer 暫存器
每個 Timer 都受數個暫存器控制。Timer/Counter Control Registers TCCRnA 和 TCCRnB 是 timers 的主要控制暫存器。這些暫存器掌管幾組位元:
- Waveform Generation Mode bits (WGM): 這些控制 timer 的整個模式。(這幾個位元分散在 TCCRnA 和 TCCRnB)
- Clock Select bits (CS): 這些控制 prescaler
- Compare Match Output A Mode bits (COMnA): 這些 enable/disable/invert A 的輸出
- Compare Match Output B Mode bits (COMnB): 這些 enable/disable/invert B 的輸出
注意!TCCRnA 和 TCCRnB 不是對應 A 和 B 兩個輸出。
Output Compare 暫存器 OCRnA 和 OCRnB 的設定影響 A 和 B 的輸出,當 timer 與暫存器內容匹配時,對應的輸出就會根據模式改變。
每個 timer 的這些位元有些不一樣,所以要參閱 datasheet。Timer1 是 16-bit 的,而且還有額外的 Input Capture 模式,而 Timer2 則有不同的 prescale 設定值。
Fast PWM mode (原理說明,Duty Cycle 公式,Frequency 公式):
Fast PWM 是最簡單的 PWM 模式,timer 重覆從 0 數到 255。輸出在 timer 為 0 時打開 (turn on),而在 timer 跟 output compare 暫存器匹配時關掉 (turn off)。output compare 暫存器的數值愈大,duty cycle 就會大。
下圖顯示 OCRnA 和 ORCnB 在兩個不同設定值時的輸出。注意,兩個輸出的頻率是相同的。
底下的片段程式把 pin 3 和 11 (Timer2) 設定成 Fast PWM。摘要說明暫存器的設定,把 waveform generation mode bits WGM 設定為 011 以選擇 Fast PWM,把 COM2A 和 COM2B 位元設定為 10 以選擇 A 和 B 輸出為非反相 (non-inverted) 的 PWM。設定 CS bits 為 100 以選擇 prescaler 為 clock/64。output compare 暫存器只是隨意設成 180 和 50 以控制 A 和 B 輸出的 PWM duty cycle。
在 Arduino Duemilanove 上,這些設定的效果是:
- Output A frequency: 16 MHz / 64 / 256 = 976.5625Hz
- Output A duty cycle: (180+1) / 256 = 70.7%
- Output B frequency: 16 MHz / 64 / 256 = 976.5625Hz
- Output B duty cycle: (50+1) / 256 = 19.9%
輸出頻率是 16 MHz,除以 prescale 設定 (64),除以 256 cycles。注意,fast PWM 會保持輸出為 High 比 compare 暫存器多 1 個 cycle。
Fast PWM 的 frequency 計算公式為:
PWM_fequency = clock_speed / [Prescaller_value * (1 + TOP_Value) ]
Phase-Correct PWM
在這個模式下,timer 從 0 數到 255,然後再倒數回 0。在 timer 往上爬時,當 timer 跟 output compare 暫存器匹配時,輸出會關閉,而在 timer 往下走時,當 timer 跟 output compare 暫存器匹配時,輸出會打開。結果是會是對稱的輸出。Phase-Correct PWM 的輸出頻率將是 fast PWM 的一半,因為 timer 往上數又往下倒數。
底下的片段程式把 pin 3 和 11 (Timer2) 設定成 Phase-Correct PWM。其中 Waveform generation mode bits WGM 設成 001 代表 phase-correct PWM,其它位元的設定跟 fast PWM 一樣。
在 Arduino Duemilanove 上,這些設定的效果是:
- Output A frequency: 16 MHz / 64 / 255 / 2 = 490.196Hz
- Output A duty cycle: 180 / 255 = 70.6%
- Output B frequency: 16 MHz / 64 / 255 / 2 = 490.196Hz
- Output B duty cycle: 50 / 255 = 19.6%
相較於 Fast PWM,Phase-correct PWM 將頻率除以 2,因為 timers 往上數又往下倒數,比較值得注意的是,頻率是除以 255 而非 256,另外 duty cycle 的計算也沒有加 1。
PWM_frequency = clock_speed / (2 * Prescaller_value * TOP_value )
改變 timer 的 TOP 值: Fast PWM
Fast PWM 跟 Phase-Correct PWM 都有一個額外的模式,可以控制輸出的頻率。在這個模式下,Timers 不是從 0 數到 255,而是從 0 數到 OCRA (output compare register A)。
要注意,在這個模式下,只有 B 可做為 PWM 輸出。OCRA 不能既當 TOP 值又當 PWM 比較值。然而,還有一個叫做 "Toggle OCnA on Compare Match" 的特別模式,會在每一個 cycle 結束時切換 A 的輸出,產生一個 duty cycle 固定為 50% 而且頻率為一半的效果。
在下圖中,timer 會在跟 OCRnA 匹配時 reset,跟前面的圖比起來,OCnB 的輸出頻率比較快。注意 OCnA 在每次 timer reset 時切換:
底下的片段程式把 pin 3 和 11 (Timer2) 設定成 Fast PWM,並且使用 OCR2A 當作 TOP 值。waveform generation mode bits 設成 111 以選擇 Fast PWM 且 OCRA 為 TOP 值。ORC2A 也就是 TOP 值設成 180。COM2A 設定 01 以選擇 "Toggle OC2A on Compare Match"。
在 Arduino Duemilanove 上,這些設定的效果是:
- Output A frequency: 16 MHz / 64 / (180+1) / 2 = 690.6Hz
- Output A duty cycle: 50%
- Output B frequency: 16 MHz / 64 / (180+1) = 1381.2Hz
- Output B duty cycle: (50+1) / (180+1) = 28.2%
注意,在這個範例中,timer 從 0 數到 180,總共花 181 個 clock cycles,所以輸出頻率要除以 181。輸出 A 的頻率只有輸出 B 的一半。
改變 timer 的 TOP 值: Phase-Correct PWM
同樣的,在 phase-correct PWM 模式下,timer 也可以設成當它抵達 OCRnA 時 reset:
底下的片段程式把 pin 3 和 11 (Timer2) 設定成 Phase-Correct PWM,並且使用 OCR2A 當作 TOP 值。waveform generation mode bits 設成 101 以選擇 Phase-Correct PWM 且 OCRA 為 TOP 值。ORC2A 也就是 TOP 值設成 180。COM2A 設定 01 以選擇 "Toggle OC2A on Compare Match"。
在 Arduino Duemilanove 上,這些設定的效果是:
- Output A frequency: 16 MHz / 64 / 180 / 2 / 2 = 347.2Hz
- Output A duty cycle: 50%
- Output B frequency: 16 MHz / 64 / 180 / 2 = 694.4Hz
- Output B duty cycle: 50 / 180 = 27.8%
注意,在這個範例中,timer 從 0 數到 180 然後再倒數回 0,總共花 360 個 clock cycles,所以輸出頻率要除以 360 (A) 和 180 (B)。
請問coopermaa大
回覆刪除我如果想看懂這些東西 應該要找帶有哪些關鍵字的資料呢?!
ㄜ......我的程度大概在
暫存器是什麼?!
這個階段...
你是指這一篇還是 "From Arduino to AVR" 這整系列教學?
回覆刪除關於暫存器的概念,你參考一下底下這篇,這篇寫的很好 (作者是外國人,我只是翻譯而已),而且有很漂亮的圖片說明,應該會有一些幫助 (你直接跳到 "暫存器 (Register)" 這段看就好):
http://coopermaa2nd.blogspot.com/2011/06/introduction.html
恩 謝謝你=))
回覆刪除Luck for you! :)
回覆刪除你好~我用pwm.h函數產生2組相同pwm1/pmw2,請問有辦法讓它不要overlap嗎?可以弄成一前一後2組相同的pwm嗎?
回覆刪除