PIC18x Timer 分享 I

今天課程的重點是 PIC Timer 的介紹,首先我們來觀察一下 Timer 是怎麼一回事呢?




首先開啟 MPLAB 後,點選 "Debugger | Select Tool | MPLAB SIM" 開啟軟體模擬的功能


接著點選 "Debugger | Settings",將處理器的頻率設定為 4MHz (方便後面計算),同時將 Buffer Size 設定為 4M lines (方便捕捉更多的波形)


接著點選 "Debugger | StopWatch",它可說是一個非常好用的工具 (後面的驗證都靠它了,只是不曉得它的名字為甚麼叫 StopWatch?(不要看嗎?))


點選 "View | Watch",把要觀察的 T0CON、TMR0L 加進來,並將初始值分別設定為 0x80、0x00


最後點選 "View | Program Memory" 把程式記憶體叫出來,順便排一下畫面,再按右上角的 Reset 就可以開始觀察了 (如果按 Reset 的話,暫存器的值必須重新設定)


點選右上角的 單步執行 (Step Into),可以發現每執行兩個指令週期, TMR0L 會加 1 (前面有一段不穩定的狀態不會增加,可以先忽略)

同時在 Stopwatch 也可以看到總共執行了 7 個指令週期,花了 7uSecs

按右上角的 Reset 重置,同時將 T0CON、TMR0L 分別輸入 0x81、0x00,再重覆上面的步驟可以發現每執行 4 個指令週期,TMR0L 會加 1

如果把 T0CON 輸入 0x82,則每執行 8 個指令週期,TMR0L 會加 1

為甚麼會這樣呢?


首先請看 PIC18F4520 Data Sheet (Page 126) 關於 TIMER0 在 8-BIT 模式下的方塊圖 (原圖 PSA 選擇有誤,已修正)
  • T0CS 可用來選擇 Timer0 第一階段的 Clock 來源
    • 0 表示使用內部的 Clock Fosc / 4,若晶片使用的頻率為 4MHz (我們先前設定的),則 Clock 為 4MHz / 4 = 1MHz (故週期為 1 / 1MHz = 1us)
    • 1 表示使用外部的 T0CKI pin 當作 Clock 輸入,而 T0SE 則用來控制輸入的相位,0 表示正常輸入,1 表示反向
  • PSA 選擇 Timer0 第二階段的 Clock 來源
    • 0 表示前段的 Clock 需經過一組除頻電路,除頻比例由 T0PS<2:0> 選擇
      • 111 = 1 : 256
      • 110 = 1 : 128
      • 101 = 1 : 64
      • 100 = 1 : 32
      • 011 = 1 : 16
      • 010 = 1 : 8
      • 001 = 1 : 4
      • 000 = 1 : 2
    • 1 表示前段的 Clock 直接輸出
  • 接下來經過 Sync with internal Clocks,這應該是前面觀察時,一開始不穩態的原因
  • 最後存到 TMR0L 就可以開始計時了
對照前面觀察 T0CON 設定為 0x80 時,就是將除頻設定為 1 : 2,所以執行兩個指令週期 TMR0L 才會加 1

同理 T0CON 設定為 0x81 時,就是將除頻設定為 1 : 4;而 0x82 則是 1 : 8

所以如果要 1 : 1 的話,就是將 T1CON 設定為 0x88 (就請大家自己觀察囉!)


觀察到此為止,結下來就是要實作 1ms 的計時了,要達到這個目的,我們可以
  • 1us x 1000 次
  • 1us x 500 次 (1 : 2 除頻)
  • 1us x 250 次 (1 : 4 除頻)
  • 1us x 125 次 (1 : 8 除頻)
因為 8-bit TMR0L 只能計數到 255,所以前 2 項就不用考慮了,這裡就先暫訂 1 : 8  除頻計數 125 次的方式。程式碼如下

main.c


#include <myapp.h>

void main(void)
{
 unsigned char T0BK; // 記錄 Timer0 計數值
 
 CLR(TRISC, BIT0);  // 設定 RC0 為輸出
 T0CON = 0x82;   // 開啟 Timer0 並設定除頻為 1 : 8
 T0BK = TMR0L + 125;  // 初始化 T0BK 為目前 TMR0L 往後數 125 次
 
 while (1)
 {
  while (TMR0L != T0BK) // 若 Timer0 未數到 T0BK 則繼續等待
  //while ((TMR0L-T0BK) < 125)
  {
  }
  
  T0BK += 125;  // 數到 125 次後則繼續往後推遲 125 次
  TGL(LATC, BIT0); // 將 RC0 反向輸出
 }
}

要如何驗證程式是否正確呢?


我們可以在上圖 Line 18 的位置設定中斷點,當第 1 次中斷時,點選 Stopwatch 的 Zero 將 Instruction Cycles & Time 歸零,然後再度執行就可以看到兩次中斷花費的時間約為 1000uSecs (多執行幾次可以看到會有些誤差)

從底下的 RC0 波形輸出可以看到 1 個方波的週期約 2000 指令週期

討論:

  • 由於這是用軟體的方式在控制方波輸出,故多多少少會有一些誤差 (C 語言的 1 行 while 變成組合語言包含 6~8 指令)
  • while 迴圈利用 TMR0L == T0BK 來捕捉計數結束,是否會有漏接的可能?
  • 若利用另一種方式 (TMR0L - T0BK) < 125 來當作計數的條件,則 T0BK += 125; 是否需考慮溢位的可能?(事實證明不用)
待續。。。

留言

Unknown寫道…
Dear Johnny
you provide us this info let me appreciation as well your helpful

because I feel this lesson very hardly...

BR,
BEN(No 26)
牧童叔寫道…
You are welcome. It's my pleasure to share information with you.
Drowsy寫道…
Thanks for your share!

~Joseph
Unknown寫道…
Could you share all EX to me like lesson example?

I have most issue need do that on weekly....

thank
Ben
牧童叔寫道…
Sure, I will continue to do my notes this weekend. Please give me some time to finish it.
Unknown寫道…
Since your help let us very easy clarify

thanks a lot


這個網誌中的熱門文章

Linux 批次檔的寫法

【分享】如何顯示 Debug Message

[分享] Visual Studio 遠端偵錯