0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

rt-thread 驅(qū)動篇(八)hwtimer 重載算法優(yōu)化

出出 ? 來源:出出 ? 作者:出出 ? 2022-06-23 10:10 ? 次閱讀

硬件定時器

區(qū)別于 rt-thread 內(nèi)核實現(xiàn)的兩種定時器,這種定時器依賴芯片內(nèi)置的定時器外設(shè),依靠穩(wěn)定高速的晶振實現(xiàn)精確定時,可以實現(xiàn) rt_timer 無法達到的定時精度。硬件定時器最重要的兩個參數(shù)是定時器時鐘和定時器重載值。

定時器時鐘越高,定時器精度越高;重載值越大,實現(xiàn)的定時時間越長。

在定時器時鐘一定的前提下,重載值就決定了定時器定時時間的準確性。

兩種計算重載值算法

hwtimer.c 文件 `timeout_calc` 函數(shù)實現(xiàn)

   float overflow;
   float timeout;
   rt_uint32_t counter;
   int i, index = 0;
   float tv_sec;
   float devi_min = 1;
   float devi;
   /* changed to second */
   overflow = timer->maxcnt/(float)timer->freq;
   tv_sec = tv->sec + tv->usec/(float)1000000;

   if (tv_sec < (1/(float)timer->freq))
   {
       /* little timeout */
       i = 0;
       timeout = 1/(float)timer->freq;
   }
   else
   {
       for (i = 1; i > 0; i ++)
       {
           timeout = tv_sec/i;

           if (timeout <= overflow)
           {
               counter = timeout*timer->freq;
               devi = tv_sec - (counter/(float)timer->freq)*i;
               /* Minimum calculation error */
               if (devi > devi_min)
               {
                   i = index;
                   timeout = tv_sec/i;
                   break;
               }
               else if (devi == 0)
               {
                   break;
               }
               else if (devi < devi_min)
               {
                   devi_min = devi;
                   index = i;
               }
           }
       }
   }

   timer->cycles = i;
   timer->reload = i;
   timer->period_sec = timeout;
   counter = timeout*timer->freq;

   return counter;

第二種實現(xiàn),

   rt_uint32_t counter, reload;
   rt_uint32_t timer_cnt;
   int i, index = 0, n0, n1;
   float tv_sec;
   rt_uint32_t dev, dev_min;
   /* changed to second */
   tv_sec = tv->sec + tv->usec/(float)1000000.0;
   timer_cnt = tv_sec * timer->freq;

   if (timer_cnt == 0) {
       timer_cnt = 1;
   }
   if (timer_cnt < timer->maxcnt) {
       timer->cycles = timer->reload = 1;
       timer->period_sec = tv_sec;
       counter = timer_cnt;
       return counter;
   }
   if (timer_cnt % timer->maxcnt == 0) {
       timer->cycles = timer->reload = timer_cnt / timer->maxcnt;
       timer->period_sec = tv_sec;
       counter = timer_cnt;
       return counter;
   }
   n0 = timer_cnt / timer->maxcnt + 1;
   n1 = timer_cnt / 2;
   dev_min = n0;
   for (i = n0; i < n1; i++) {
       reload = (rt_uint32_t)(timer_cnt / i);
       dev = timer_cnt - reload * i;
       if (dev == 0) {
           // end
           index = i;
           break;
       } else if (dev < dev_min) {
           dev_min = dev;
           index = i;
       }
   }
   timer->cycles = timer->reload = index;
   timer->period_sec = index / timer->freq;
   counter = timer_cnt / index;
   return counter;

測試環(huán)境

定時器頻率設(shè)定 1M。定時器最大重載值 65535。

系統(tǒng):win10

IDE:Qt Creator

最大定時范圍

兩種算法,最主要的差別在于前一種用 float 運算,因為 float 可以表達的值范圍更大,定時時間可以更長。

而在 1M 定時器時鐘前提下,用 32 位無符號整型 timer_cnt,最大可以處理時間僅有 4294.967295s。

精度 PK

image.png

這里不支持嵌入 html 表格,只好貼圖了

分別選各個量級的時間,用兩種算法計算,第二種算法可以把誤差降低到0,但是也暴露出一些問題,在某些時間,例如 3.230970s、12.230970s、14.230970s... 誤差是很小,定時器重載值也很小,這是我們不愿意看到的。

第一種算法,在計算大于 1000 的數(shù)時,誤差也隨之增大。比如 1000s 誤差為 3.236ms;4293.0s 誤差為 64.080ms。

運算速度 PK

測試方法:抽取某幾個時間值,循環(huán) 1M 次運算,計量 1M 次運算總耗時時間。

time float uint32
3.317s 98.736ms 3000+s
7.537s 178.545ms 21.921ms
7.000537s 168.549ms 175.530ms
999.999s 17407.468ms 30866.978ms
999.000999s 17458.347ms 337.047ms

從抽取的幾個值測試結(jié)果看,第一種算法耗時比較穩(wěn)定,第二種算法對不同值的運算時間差異很大。特別的,3.317s 這個值用第二種算法,1M 次運算總耗時時間可能達到 3000s。

從上一小節(jié)的精度比對可以看出,第二種算法對精度要求太高了。下面降低第二種算法的精度,達到和第一種一樣的精度再重復(fù)一次。修改代碼如下

       if (dev == 0) {
           // end
           index = i;
           break;
       } else if (dev > dev_min) {
           break;
       } else if (dev < dev_min) {
           dev_min = dev;
           index = i;
       }

再次測試結(jié)果:

time float uint32
3.317s 104.720ms 20.945ms
3.000317s 91.728ms 21.941ms
7.537s 179.519ms 21.941ms
7.000537s 168.549ms 20.944ms
999.999s 17480.734ms 27.927ms
999.000999s 17366.539ms 20.944ms

我們可以看出來,在相同精度條件下,第二種算法的運算速度比第一種快很多,而且耗時反而變得更集中。

其實,對結(jié)束條件再次修正,將 `dev == 0` 的嚴苛誤差條件換成 `dev <= 1` 也不會出現(xiàn)上面 3000+s 慢速。

       if (dev <= 1) {
           // end
           index = i;
           break;
       } else if (dev > dev_min) {
           break;
       } else if (dev < dev_min) {
           dev_min = dev;
           index = i;
       }

超過 4295s 的超長定時

需要修改 `rt_uint64_t timer_cnt` 的定義為 64 位無符號整型 `rt_uint64_t timer_cnt` 。

又因為定時時間很長很長,對誤差要求可以降低一些,對第二種算法做的第二處修改:

       if (dev <= 500) {
           // end
           index = i;
           break;
       } else if (dev < dev_min) {
           dev_min = dev;
           index = i;
       }
image.png

超長時間,第二種算法的表現(xiàn)也很優(yōu)秀。第三組數(shù)據(jù)第一種方法竟然出錯了,沒算出結(jié)果。

下面是 10k 次(沒有進行 1W 次是因為有些時間太長了)運算時間統(tǒng)計

time float uint32
9999.537s 1741.341ms 5.010ms
19999.999s 3481.173ms 27.926ms
1999999.999s - 2616.001ms

返璞歸真

以上是對兩種算法從不同角度進行的比對測驗??此朴?float 可以計算更大的定時數(shù),但是,測試結(jié)果并不那么理想。使用 64位整型數(shù)計算,可能得到比用 float 更精確的結(jié)果。

使用 32 位無符號整型數(shù)運算雖然最大定時時間只有 4294.9s 。但是我們也看到了,第一種方法有可能出現(xiàn)計算誤差的,當誤差超過 1ms 我們用 rt_thread_mdelay 或者 rt-thread 的軟/硬定時器,可能結(jié)果比硬件定時器更精確了,反而失去了精確定時器的意義。在這個前提下,使用 32 位無符號整型數(shù)已經(jīng)足夠了。


算法及測試源碼見: https://gitee.com/thewon/rt_thread_repo/tree/master/user

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 算法
    +關(guān)注

    關(guān)注

    23

    文章

    4552

    瀏覽量

    92027
  • 定時器
    +關(guān)注

    關(guān)注

    23

    文章

    3218

    瀏覽量

    113695
  • RT-Thread
    +關(guān)注

    關(guān)注

    31

    文章

    1239

    瀏覽量

    39442
收藏 人收藏

    評論

    相關(guān)推薦

    RT-Thread記錄(一、版本開發(fā)環(huán)境及配合CubeMX)

    RT-Thread 學(xué)習(xí)記錄的第一文章,RT-Thread記錄(一、RT-Thread 版本、RT-Thread Studio開發(fā)環(huán)境 及
    的頭像 發(fā)表于 06-20 00:28 ?4926次閱讀
    <b class='flag-5'>RT-Thread</b>記錄(一、版本開發(fā)環(huán)境及配合CubeMX)

    RT-Thread NUC97x 移植 LVGL

    不涉及 rt-thread 驅(qū)動,但是它是 LVGL 和 rt-thread 的接口。LVGL 在 rt-thread 上運行的基石。
    發(fā)表于 07-08 09:37 ?1426次閱讀

    基于RT-Thread的SPI通訊

    驅(qū)動層的驅(qū)動。(rt-thread的設(shè)備 I/O 模型有設(shè)備管理層、設(shè)備驅(qū)動框架層、設(shè)備驅(qū)動層),我寫過一
    的頭像 發(fā)表于 08-22 09:28 ?1534次閱讀

    RT-Thread ssd1306驅(qū)動

    RT-Thread 驅(qū)動ssd1306
    的頭像 發(fā)表于 04-21 10:08 ?26.4w次閱讀
    <b class='flag-5'>RT-Thread</b> ssd1306<b class='flag-5'>驅(qū)動</b>

    RT-Thread使用未默認啟用的timer作為硬件定時器HWTIMER的步驟

    初學(xué) RT-Thread,在 RT-Thread studio 環(huán)境下,使用手里的 nucleo_L476 開發(fā)板來學(xué)習(xí) HWTimer 的使用,運行官方 HWTimer 例程。
    的頭像 發(fā)表于 07-19 15:17 ?1521次閱讀
    <b class='flag-5'>RT-Thread</b>使用未默認啟用的timer作為硬件定時器<b class='flag-5'>HWTIMER</b>的步驟

    RT-Thread設(shè)備驅(qū)動開發(fā)指南基礎(chǔ)—以先楫bsp的hwtimer設(shè)備為例

    RT-Thread設(shè)備驅(qū)動開發(fā)指南》書籍是RT-thread官方出品撰寫,系統(tǒng)講解RT-thread IO設(shè)備驅(qū)動開發(fā)方法,從三方面進行講解
    的頭像 發(fā)表于 02-20 16:01 ?1410次閱讀
    <b class='flag-5'>RT-Thread</b>設(shè)備<b class='flag-5'>驅(qū)動</b>開發(fā)指南基礎(chǔ)<b class='flag-5'>篇</b>—以先楫bsp的<b class='flag-5'>hwtimer</b>設(shè)備為例

    RT-Thread驅(qū)動開發(fā)指南進階-動手驅(qū)動先楫未適配的外設(shè)LCD

    經(jīng)過上一的《《RT-Thread設(shè)備驅(qū)動開發(fā)指南》基礎(chǔ)--以先楫bsp的hwtimer設(shè)備為例》闡述,可以大致了解到
    的頭像 發(fā)表于 02-25 11:04 ?1869次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>驅(qū)動</b>開發(fā)指南進階<b class='flag-5'>篇</b>-動手<b class='flag-5'>驅(qū)動</b>先楫未適配的外設(shè)LCD

    【原創(chuàng)精選】RT-Thread征文精選技術(shù)文章合集

    rt-thread 驅(qū)動(六)serialX弊端及解決方法rt-thread 驅(qū)動(七)GP
    發(fā)表于 07-26 14:56

    RT-Thread編程指南

    RT-Thread編程指南——RT-Thread開發(fā)組(2015-03-31)。RT-Thread做為國內(nèi)有較大影響力的開源實時操作系統(tǒng),本文是RT-Thread實時操作系統(tǒng)的編程指南
    發(fā)表于 11-26 16:06 ?0次下載

    RT-Thread Studio驅(qū)動SD卡

    RT-Thread Studio驅(qū)動SD卡前言一、創(chuàng)建基本工程1、創(chuàng)建Bootloader2、創(chuàng)建項目工程二、配置RT-Thread Settings三、代碼分析1.引入庫2.讀入數(shù)據(jù)四、效果驗證
    發(fā)表于 12-27 19:13 ?20次下載
    <b class='flag-5'>RT-Thread</b> Studio<b class='flag-5'>驅(qū)動</b>SD卡

    RT-Thread驅(qū)動hwtimer重載算法

    區(qū)別于 rt-thread 內(nèi)核實現(xiàn)的兩種定時器,這種定時器依賴芯片內(nèi)置的定時器外設(shè),依靠穩(wěn)定高速的晶振實現(xiàn)精確定時,可以實現(xiàn) rt_timer 無法達到的定時精度。硬件定時器最重要的兩個參數(shù)是定時器時鐘和定時器重載值。
    的頭像 發(fā)表于 04-01 10:06 ?1675次閱讀

    RT-Thread文檔_RT-Thread 簡介

    RT-Thread文檔_RT-Thread 簡介
    發(fā)表于 02-22 18:22 ?5次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> 簡介

    RT-Thread文檔_RT-Thread SMP 介紹與移植

    RT-Thread文檔_RT-Thread SMP 介紹與移植
    發(fā)表于 02-22 18:31 ?9次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> SMP 介紹與移植

    RT-Thread文檔_HWTIMER 設(shè)備

    RT-Thread文檔_HWTIMER 設(shè)備
    發(fā)表于 02-22 18:34 ?0次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>HWTIMER</b> 設(shè)備

    RT-Thread設(shè)備驅(qū)動開發(fā)指南》基礎(chǔ)--以先楫bsp的hwtimer設(shè)備為例

    一、概述(一)RT-Thread設(shè)備驅(qū)動RT-Thread設(shè)備驅(qū)動開發(fā)指南》書籍是RT-thread官方出品撰寫,系統(tǒng)講解
    的頭像 發(fā)表于 02-24 08:16 ?954次閱讀
    《<b class='flag-5'>RT-Thread</b>設(shè)備<b class='flag-5'>驅(qū)動</b>開發(fā)指南》基礎(chǔ)<b class='flag-5'>篇</b>--以先楫bsp的<b class='flag-5'>hwtimer</b>設(shè)備為例