Skip to main content

【Arduino】Timers, Registers, and Fast PWM Mode

由於Arduino預設的PWM控制方法僅有500Hz(好像還有另一個),想要知道怎樣才可以調整成其他頻率,以此做記錄。

先說明Timer設定方式、PWM Mode,之後會在Arduino上實做。

如果對_BV()沒有概念,可以參考【Arduino I/O Ports】Control under avr-libc這篇文章。

Timer & Registers
ATmega328內與Timer相關的Register大略如下(n代表Timern):
  • Timer Counter Control Register A (TCCRnA):決定timer的運作模式(operating mode)
  • Timer Counter Control Register B (TCCRnB):決定timer的prescale value
  • Timer Counter Register (TCNTn):儲存timer目前的累積計數(timer count)
  • Output Compare Register A (OCRnA):當TCNTn累積計數到OCRnA的計數時,觸發中斷(interrupt)
  • Ouput Compare Register B (OCRnB):當TCNTn累積計數到OCRnB的計數時,觸發中斷(interrupt)
Timer0與Timer2皆為8-bit的Timer,表示TCNT0, 2範圍為0~255;Timer1的TCNT1則為16-bit,可以計數0~65535。

除了PWM,Timers也有負責ㄧ些其他功能:
  • Timer0: millis()與delay()函數使用,以及pin 5, 6的analogWrite
  • Timer1: 負責pin9, 10的analogWrite,以及使用Servo library驅動servo時使用
  • Timer2: 負責pin 3, 11的analogWrite

Clock Select and Timer Frequency (Arduino Timers and Interrupts )
不同的單晶片有不同的CPU時脈,實際上的運用不一定需要這麼高的時脈,這時候便需要進行「除頻」的動作,將CPU的時脈除以除率(prescaler),藉此將Timer的時脈降低,ATmega328中Timer0與Timer1使用的是相同的一組prescaler(1, 8, 64, 256, 1024),而Timer2則是有較多的prescaler選項(1, 8, 32, 64, 128, 256, 1024)。

使用Prescaler的效果,在ATmega328的datasheet中有圖可以大略了解一下是怎麼回事,下面是沒有使用Prescaler的圖:
  • clk I/O是原始時脈16MHz
  • clk Tn是經過除頻後的時脈,prescaler為1,clk Tn = (clk I/O) / 1
  • TCNTn是Timer的累積計數(timer counter)
每ㄧ次clk I/O振盪,都會讓clk Tn輸出HIGH,使得TCNTn的計數+1,可以看到TCNTn從MAX-1到MAX(最大值)、BOTTOM(再從0開始)、BOTTOM+1,再接著繼續計數。

此時dlk Tn的時脈與dlk I/O同為16MHz,表示每秒TCNTn會數16000000次。

當prescaler為8時,代表clk Tn = (clk I/O) / 8,此時clk I/O振盪了8次,clk Tn才會有一次振盪,TCNTn才跟著進行計數,所以TCNTn在每秒的計數次數變會從16M降至2M,用這種方式將Timer的頻率拉低成2MHz

上圖中TCNTn的BOTTON、MAX,另外還有TOP在datasheet中的定義如下:
  • BOTTOM:當計數為0的時候
  • MAX:當計數到達最大值時,TCNT0, 2為255,TCNT1為65535
  • TOP:使用者也可以自訂計數的上限,可以指定數值作為上限,或是將該數值儲存在OCRnA暫存器中,要使用TOP的話,記得要調整timer的模式

說了這麼多,到底該怎麼根據期望的Timer frequency、挑選timer以及prescaler呢?
這邊以Arduino UNO的ATmega328為例,希望將Timer1降低至2Hz為目標,進行計算:
  1. ATmega328自帶的CPU frequency為16MHz
  2. 查看timer counter的最大值(check TCNTn MAX, 255 for 8-bit timer, 65535 for 16-bit)
  3. 將CPU除以選好的prescaler值(16000000/256 = 62500)
  4. 將3的結果除以目標頻率(62500/2 = 31250)
  5. 比較4的結果是否小於timer counter的最大值(31250 < 65525, 成功),若大於timer counter的最大值,則選擇更大的prescaler,然後再從步驟3開始運算一次。如果已經是prescaler的最大值還是不夠,建議換成16-bit的Timer1。

接下來的筆記大部分參考Cooper Maa的6.1) PWM modes part1,並非原創,另外手動增加register的對照圖表。

Fast PWM Mode
首先說明最簡單直覺的PWM Mode,Fast PWM,timer counter會從BOTTOM數到MAX,timer counter為0時,OCnA輸出HIGH,接著timer counter繼續計數,直到timer counter(TCNTn)跟output compare register(OCRn)的數值相同時才將輸出關閉(turn off)。因此OCRn的數值越大,duty cycle也跟著越大

下圖為timer counter是不斷從0數到255,OCRnAOCRnB設定在不同數值時,輸出的狀況,可以看出OCRnB的設定值較小,對應OCnB的duty cycle也較小。

Timer與Arduino PWM腳位的關係如下表,其中OCnn便是表示為Timern所控制:

接著來實做把pin 3和11(Timer2)設定成Fast PWM Mode!
Timer相關暫存器所需配置如下:
  1. Waveform Generation Mode Bit (WGM)
    • 首先在datasheet中找到Timer2的操作模式設定表。模式設定是由Waveform Generation Mode Bit所控制,只要在datasheet搜尋「WGMn」(n為Timern之意,此時n=2),便可以找到此表。
    • 表中顯示Mode3與Mode7皆為Fast PWM Mode,不同的是TOP的數值
    • Mode 3的TOP值為0xFF,表示TCNT2將會數到255便歸0,與上面的圖有相同的Timer輸出
    • Mode 7的TOP值為OCRA,是另外設定TOP值的方法,在之後會說明
    • WGM22:0的設定使用Mode 3的011
  2. Compare Match Output A Mode
    • 首先在datasheet中找到Timer2的Compare Match Output A Mode表。只要在datasheet搜尋「COMnA」(n為Timern之意,此時n=2 ),便可以找到此表。COMnA內配置位元將會決定Output Compare pin (OCnA)的行為。
    • COM2A1:0設定為10,選擇OC2A跟OC2B的PWM輸出不為反向
  3. Compare Match Output B Mode
    • 首先在datasheet中找到Timer2的Compare Match Output B Mode表。只要在datasheet搜尋「COMnB」(n為Timern之意,此時n=2 ),便可以找到此表。COMnB內配置位元將會決定Output Compare pin (OCnB)的行為。
    • 與COM2A一樣,COM2B1:0配置為10
  4. Clock Select
    • 首先在datasheet中找到Timer2的Clock Select表。只要在datasheet搜尋「CSn」(n為Timern之意,此時n=2 ),便可以找到此表。CSn2:0內配置位元將會決定prescaler的值。
    • 將CS22:0配置為100,設定prescaler為64,使得clk Tn降為250KHz(16M/64)
上面提到的暫存器,分別位於TCCR2A以及TCCR2B。


Arduino程式碼如下:
pinMode(3, OUTPUT);
pinMode(11, OUTPUT);
TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
TCCR2B = _BV(CS22);
OCR2A = 180;
OCR2B = 50;

在Arduino Uno上,這些配置的效果是:
  • OCR2A (pin 11)
    • frequncy:16MHz / 64 / 256 =  976.5625Hz
    • duty cycle:(180+1) / 256  = 70.7%
  • OCR2B (pin 3)
    • frequncy:16MHz / 64 / 256 =  976.5625Hz
    • duty cycle:(50+1) / 256  = 19.9%
frequency在除以prescaler後,要再除以256,是因為該PWM的timer counter要從0累積計數到255才算一個週期。另外要注意Fast PWM會保持輸出為High比OCR多一個週期。

Fast PWM的頻率計算公式為:
     PWM_frequency = clock_speed/ [Prescaller_Value * (1 + TOP_Value)]


Fast PWM Mode與自訂TOP值
透過上面介紹的調整方式,只能將frequency調成與prescaler相對應的5或7個數值,接下來將介紹如何透過更動timer counter的累計計數上限,來進行frequency的微調。

更動累計計數上限的意思便是timer counter不在是從0數到255,而是從0數到OCRnA,注意在這個模式下,只有OCnB可以做PWM輸出,OCRnA不能同時做為另一個PWM輸出的TOP值,又做為所屬pin腳的比較值。

另外在Compare Match Output A Mode中有一個叫做"Toggle OCnA on Compare Match"的模式,會在每一個周期結束時切換OCnA的輸出,產生一個duty cycle固定為50%且頻率為0.5倍的效果。

實際輸出的圖形如下,timer counter會在與OCRnA相等時歸零,與前面的圖比起來,此時OCnB的輸出頻率是比較快的,因為timer counter還未數到255時便已完成一個週期,透過此種方式,可以對timer counter重置的時間進行調整,間接的也可以對frequency進行微調。

若要將pin 3與11(Timer2)設定成Fast PWM,並使用OCR2A做為TOP值,首先Timer暫存器所需配置如下:
  • WGM22:0配置成111,表示使用Mode 7,參考OCR2A做為TOP值
  • COM2A1:0配置成01,選擇"Toggle OCnA on Compare Match"模式
  • COM2B1:0配置與先前一樣為10
  • CS22:0配置為100,設定prescaler為64
Arduino Uno程式如下:
pinMode(3, OUTPUT);
pinMode(11, OUTPUT);
TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
TCCR2B = _BV(WGM22) | _BV(CS22);
OCR2A = 180;
OCR2B = 50;

在Arduino Uno上,這些設定的效果是:
  • OCR2A (pin 11)
    • frequncy:16 MHz / 64 / (180+1) / 2 = 690.6Hz
    • duty cycle:50%
  • OCR2B (pin 3)
    • frequncy:16 MHz / 64 / (180+1) = 1381.2Hz
    • duty cycle:(50+1) / (180+1) = 28.2%
在這個範例中,timer counter從 0 數到 180,總共花 181 個周期,所以OCnAOCnB輸出頻率要除以 181而非180。最後結果OCnA的頻率只有OCnB的一半。

Summary
最後用8-bit Timer的Block Diagram來總結一下。
  • 右上角是Clock Select的過程,使用Prescaler進行除頻,降低時脈
  • 左邊可以看到Timer/Counter的累積計數(TCNTn)會不斷與OCRnAOCRnB進行比較,若相等則進入Waveform Generation,進行輸出的調整
  • Waveform Generation內可以設定不同模式的輸出,Non-PWM、CTC、Fast PWM、Phase Correct PWM等等模式,這些設定將影響OCnA的輸出行為

Comments

Post a Comment

Popular posts from this blog

淺介I2C

I 2 C 起源 內部整合電路( Inter-Integrated Circuit, I 2 C, 讀做 I-square-C )是由飛利浦半導體公司開發的一種專用介面。 I 2 C 是以最少的連接線進行硬體佈線還要有靈活擴充的特性為目標而設計,最後出現了只有以序列資料線 SDA ( Serial DAta )及序列時脈線 SCL ( Serial CLock )來進行所有通訊的 I 2 C 介面, I 2 C 允許多主( master )多僕( slave )系統,其傳輸系統內每一個裝置都有唯一的地址可供辨識。資料的寫入和讀取都是由 master 主動發起, slave 無法主動向 master 回報,除非使用中斷腳通知 master 。 I 2 C 傳輸速度有慢(小於 100Kbps )、快( 400Kbps )及高速( 3.4Mbps )三種,每一種均可向下相容。 I 2 C 電路配置 如前所述 I 2 C 為兩線式,一為時脈線 SCL ,另一條為資料線 SDA ,硬體線路如圖 1 ,兩線皆為雙向性,且都需要透過高接電阻( pull-up, 對岸說的上拉電阻)接電。平常不使用時, SCL 與 SDA 的訊號都處於高電位。為了多裝置共線的功能,裝置的 SCL 和 SDA 腳位要為 開洩極( open-drain ) 或 開集極( open-collector ) 。一旦有一個腳位的開洩極導通接地,則整條線都為低電位,這種現象稱作 wired-AND 運作 ;如同邏輯 AND 運算,需要共接的腳位都是 1 (開洩極斷路),該條線的電位才是 1 。如果沒有開洩極的腳位,可以使用具內部高接電阻的腳位,當要輸出 1 時,則設定該腳位為高接型輸入腳;而輸出為 0 時,則改設定為輸出腳並輸出 0 的值。 圖 1. I 2 C 傳輸裝置接線 [1] I 2 C 通訊協定 為使說明部分更簡潔,首先介紹幾個名詞: 位元傳輸協定 當 master 要跟 slave 溝通時,會先有個起始條件( start condition )的訊號,結束時也會送出終止條件( stop condition )訊號。起始條件訊號

Emart Sunny Sale: 3D Shadow QR Code

  Emart是韓國連鎖超市,近期他們發現中午12:00到下午1:00的銷售量會明顯減少,為了提高銷售量他們製作出了帶有日晷概念的3D QR code。