計算51單片機定時器定時時間方法


計算定時器定時時間

  • 假設現在使用晶振是 11.0592M

    • 則時鍾周期為:

      \[\frac{1}{11059200} \]

    • 則機器周期為:

      \[\frac{12}{11059200} \]

  • 如果現在要定時20ms, 就是0.02秒, 要經過x個機器周期后得到0.02秒, 列出方程:

    \[x * \frac{12}{11059200} = 0.02 \\ \\ x = 18432 \]

    • x(18432 )個機器周期后剛好達到 65536(溢出)
    • 因為16 位定時器 的溢出值是 65536(因 為65535 +1 才是溢出)
  • 得到寄存器初值y

    \[y = 65536 - 18432 = 47104 \]

    • 將y轉成 16 進制 就是 0xB800
      • TH0 = 0xB8
      • TL0 = 0x00

Matlab計算

  • 如果以上方程懶得手算求解的話,有個好方法就是用Matlab解一下 [滑稽].

    syms x
    
    eqn = x * 12 / 11059200 == 0.02
    
    solve(eqn, x)
    
  • 同樣可以得到18432

定時時間精准性調整

問題提出

  • 單片機系統里,硬件進入中斷需要一定的時間,大概是幾個機器周期,還要進行原始數據保護,就是把進中斷之前程序運行的一些變量先保存起來,專業術語叫做中斷壓棧,進入中斷后,重新給定時器 TH 和 TL 賦值,也需要幾個機器周期,這樣下來就會消耗一定的時間.
  • 雖說這點時間很短,但這所造成的時間誤差是會累計的,沒1秒鍾都差了幾個微秒,時間一久造成的誤差就不可小覷了。

解決方案

  1. 使用軟件debug進行補償:

    • keil Debug觀察程序運行時間, 可以把2次進入中斷的時間間隔觀察出來,比較和實際定時的時間相差了幾個機器周期,然后在進行定時器初值賦值的時候,進行一個調整。
    • 目前我STC89C52上用的是11.0592M晶振,發現差了幾個機器周期,就把定時器初值加上幾個機器周期,這樣相當於進行了一個補償。
  2. 使用累計誤差計算出來:

    • 有時候,除了程序本身存在的誤差外,硬件精度也有可能會影響到時鍾的精度,比如晶振,會隨着溫度變化出現溫漂現象,就是實際值和標稱值要差一點。那么還可以采取累計誤差的方法來提高精度:
      • 比如我們可以讓時鍾運行半個小時或者一個小時,看看最終時間差了幾秒,然后算算一共進了多少次定時器中斷,把這差的幾秒平均分配到每次的定時器中斷中,就可以實現時鍾的調整。
  3. 這個世界上沒有絕對的精確,只能在一定程度上提高精確度,但是永遠不會使誤差為0,只能無限接近0,如果在上兩個基礎上,還是覺得不夠精確的話,那么建議使用時鍾芯片(DS1302),通常時鍾芯片計時的精度比單片機的精度要高一些。

Code

  • 精確到ms定時時間的函數

    /* 定義定時器高低位重裝數據的全局變量 */
    #define CRYSTAL_VALUE 11059200 // 當前單片機所使用的晶振值
    #define MACHINE_CYCLE 12       // 當前單片機的機器周期
    
    unsigned char T0RH = 0; // T0 重載值的高字節
    unsigned char T0RL = 0; // T0 重載值的低字節
    
    /* 配置並啟動 T0,ms-T0 定時時間 */
    void ConfigTimer0(unsigned int ms) {
      unsigned long tmp_value;
      //臨時變量
      tmp_value = CRYSTAL_VALUE / MACHINE_CYCLE;
    
      //定時器計數頻率
      tmp_value = (tmp_value * ms) / 1000;   // 計算所需的計數值
      tmp_value = 65536 - tmp_value;         // 計算定時器重載值
      tmp_value = tmp_value + 18;            // 補償中斷響應延時造成的誤差
      T0RH = (unsigned char)(tmp_value>>8);  // 定時器重載值拆分為高低字節
      T0RL = (unsigned char)tmp_value;
      TMOD &= 0xF0;                   // 清零 T0 的控制位
      TMOD |= 0x01;                   // 配置 T0 為模式 1
      TH0 = T0RH;                     // 加載 T0 重載值
      TL0 = T0RL;
      ET0 = 1;                        // 使能 T0 中斷
      TR0 = 1;                        // 啟動 T0
    
    }
    
    /* 定時器0中斷服務函數 */
    void InterruptTimer0() interrupt 1 {
      TH0 = T0RH; // 重新加載初值
      TL0 = T0RL;
    
    }
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM