PIC18x Timer 分享 II

在繼續之前,我們先來看看上次的討論




開啟上次的專案後,點選 View | Disassembly Listing,然後點選 Reset 就可以看到程式對應的組合語言。其中我們有興去的是 while 這一行,它總共被分解成 7 行組合語言,裡面有一些分支跳躍的指令,不同條件的跳躍所花費的指令週期是不一樣的,所以這就是軟體造成誤差的原因之一 (雖然這個差異小到可以忽略)


接下來看看 TMR0L == T0BK 是否有漏接的可能?目前 Timer0 選擇 1:8 除頻方式,也就是 8 個指令週期 Timer 才會加 1,而上面的 while 指令分解成組合語言只有 4 行,所以原則上執行這一行 C 語言的指令時,TMR0L 的值應該不會變 (也就是每次執行 while 時都可以得到 Timer + 1)。

如果今天改成 1:2 甚至 1:1 的方式,那執行完這行 while 後 Timer 就已經加 4 (也就是每執行一次 while 就得到 Timer + 4),如此就有可能跳過 T0BK 造成這一次的計數漏接了。

所以老師說這並不是一個好的方式,建議還是用 (TMR0L - T0BK) < 125 來判斷。


最後就來看看 TMR0L 溢位後,再去減 T0BK 是否正確呢?

假設 T0BK = 253, TMR0L = 2,在程式中 TMR0L - T0BK = 5,但這結果是怎麼來的呢?我們先用數線的方式看看

253----254----255----0----1----2

從 253 到 2 的確是差 5,而原來的計算式如果把進位考慮進來,就可以得到正確的結果了,也就是

(256 + 2) - 253 = 258 - 253 = 5

因此我們就可以得到一個結論:用這種方式相減,不管是否溢位,減出來的結果就是兩者之間的差距 (而這不就是我們要的嗎!)


接下來就回到正題了,現在已經可以計時 1ms,再加一個 counter 計數 1000 次就可以得到 1ms x 1000 = 1sec;同理可以計時分、小時,甚至做一個萬年曆了。看看程式吧

#include <myapp.h>

void main(void)
{
 unsigned char T0BK; // 記錄 Timer0 計數值
 
 unsigned int PP;   // 計數 1000 次得到 1 秒
 unsigned char SS, MM, HH; // 秒數、分鐘、小時
 
 CLR(TRISC, BIT0);  // 設定 RC0 為輸出
 T0BK = TMR0L + 125;  // 初始化 T0BK 為目前 TMR0L 往後數 125 次
 
 PP = SS = MM = HH = 0; // 全部初始化為 0
 
 T0CON = 0x82;   // 開啟 Timer0 並設定除頻為 1 : 8
 
 while (1)
 {
  while (TMR0L != T0BK) // 若 Timer0 未數到 T0BK 則繼續等待
  //while ((TMR0L-T0BK) < 125)
  {
  }
  
  T0BK += 125;  // 數到 125 次後則繼續往後推遲 125 次
  TGL(LATC, BIT0); // 將 RC0 反向輸出
  
  PP++;    // 每 1ms 計數 1 次
  if (PP >= 1000)  // 計數滿 1000 次則歸零
  {
   PP = 0;
   
   SS++;   // 同時將秒數 + 1
   if (SS >= 60) // 滿 60 秒則歸零
   {
    SS = 0;
    
    MM++;   // 同時將分鐘數 + 1
    if (MM >= 60) // 滿 60 分鐘則歸零
    {
     MM = 0;
     
     HH++;  // 同時將小時數 + 1
     if (HH >= 24)
     {
      HH = 0;
     }
    }
   }
  }
 }
}

程式應該沒甚麼問題 (註解都寫在旁邊了,雖然有點亂,有空再找一些小工具來排版程式碼),主要就是
  • 增加一些計數的變數以及初始化
  • 增加計數的串接,例如 PP 每 1ms 就會加 1,當加到 1000 時表示 1 秒鐘,所以就把 PP 歸零,同時把 SS + 1 (分鐘與小時也是相同的道理)

這裡有一個 Debug 的小技巧,程式要模擬跑到 1 分鐘需要很久的時間,所以可以先把 if (SS >= 60) 改成 if (SS >= 3) 假設 3 秒鐘就會跳到分。分別在 S++ 與 M++ 設定中斷點,每次跑到 SS++ 應該是 1 秒鐘 (看 Stopwatch 驗證),跑到 M++ 則是 3 秒鐘。這樣表示程式邏輯沒問題,所以就可以把 if (SS >= 3) 改回 if (SS >= 60) 了

之所以要做這個驗證是為了確定 60 秒的計數是否正確,因為不太確定應該寫 if (SS >= 59) 還是 if (SS >= 60) 才對,故用以上的方式驗證。

待續。。。。

留言

John Kuo寫道…
Johnny你好
想跟你詢問一下程式片段的建議還是用 (TMR0L - T0BK) < 125 來判斷。
請問 125是根據什麼原因要設定125
牧童叔寫道…
抱歉,這麼晚才回覆。
主要的目的是要取得 1ms 的計時,而 OSC 是用 4MHz,所以一個 Clock cycle 是 1us (4MHz / 4 的倒數)
而 Timer 是設定 1:8 除頻,所以計數 125 次 * 8 = 1000 次
而 1us * 1000 次 = 1ms
希望有解答你的疑惑

這個網誌中的熱門文章

Linux 批次檔的寫法

【分享】如何顯示 Debug Message

[分享] Visual Studio 遠端偵錯