硬件定時器
區別于 rt-thread 內核實現的兩種定時器,這種定時器依賴芯片內置的定時器外設,依靠穩定高速的晶振實現精確定時,可以實現 rt_timer 無法達到的定時精度。硬件定時器最重要的兩個參數是定時器時鐘和定時器重載值。
定時器時鐘越高,定時器精度越高;重載值越大,實現的定時時間越長。
在定時器時鐘一定的前提下,重載值就決定了定時器定時時間的準確性。
兩種計算重載值算法
hwtimer.c 文件timeout_calc
函數實現
1floatoverflow;
2floattimeout;
3rt_uint32_tcounter;
4inti,index=0;
5floattv_sec;
6floatdevi_min=1;
7floatdevi;
8
9/*changedtosecond*/
10overflow=timer->maxcnt/(float)timer->freq;
11tv_sec=tv->sec+tv->usec/(float)1000000;
12
13if(tv_sec(1/(float)timer->freq))
14{
15/*littletimeout*/
16i=0;
17timeout=1/(float)timer->freq;
18}
19else
20{
21for(i=1;i>0;i++)
22{
23timeout=tv_sec/i;
24
25if(timeout<=?overflow)
26{
27counter=timeout*timer->freq;
28devi=tv_sec-(counter/(float)timer->freq)*i;
29/*Minimumcalculationerror*/
30if(devi>devi_min)
31{
32i=index;
33timeout=tv_sec/i;
34break;
35}
36elseif(devi==0)
37{
38break;
39}
40elseif(devi41{
42devi_min=devi;
43index=i;
44}
45}
46}
47}
48
49timer->cycles=i;
50timer->reload=i;
51timer->period_sec=timeout;
52counter=timeout*timer->freq;
53
54returncounter;
第二種實現,
1rt_uint32_tcounter,reload;
2rt_uint32_ttimer_cnt;
3inti,index=0,n0,n1;
4floattv_sec;
5rt_uint32_tdev,dev_min;
6
7/*changedtosecond*/
8tv_sec=tv->sec+tv->usec/(float)1000000.0;
9timer_cnt=tv_sec*timer->freq;
10
11if(timer_cnt==0){
12timer_cnt=1;
13}
14if(timer_cntmaxcnt){
15timer->cycles=timer->reload=1;
16timer->period_sec=tv_sec;
17counter=timer_cnt;
18returncounter;
19}
20if(timer_cnt%timer->maxcnt==0){
21timer->cycles=timer->reload=timer_cnt/timer->maxcnt;
22timer->period_sec=tv_sec;
23counter=timer_cnt;
24returncounter;
25}
26n0=timer_cnt/timer->maxcnt+1;
27n1=timer_cnt/2;
28dev_min=n0;
29for(i=n0;i30reload=(rt_uint32_t)(timer_cnt/i);
31dev=timer_cnt-reload*i;
32if(dev==0){
33//end
34index=i;
35break;
36}elseif(dev37dev_min=dev;
38index=i;
39}
40}
41timer->cycles=timer->reload=index;
42timer->period_sec=index/timer->freq;
43counter=timer_cnt/index;
44returncounter;
測試環境
定時器頻率設定 1M。定時器最大重載值 65535。
系統:win10
IDE:Qt Creator
最大定時范圍
兩種算法,最主要的差別在于前一種用 float 運算,因為 float 可以表達的值范圍更大,定時時間可以更長。
而在 1M 定時器時鐘前提下,用 32 位無符號整型 timer_cnt,最大可以處理時間僅有 4294.967295s。
精度 PK
這里不支持嵌入 html 表格,只好貼圖了
分別選各個量級的時間,用兩種算法計算,第二種算法可以把誤差降低到0,但是也暴露出一些問題,在某些時間,例如 3.230970s、12.230970s、14.230970s... 誤差是很小,定時器重載值也很小,這是我們不愿意看到的。
第一種算法,在計算大于 1000 的數時,誤差也隨之增大。比如 1000s 誤差為 3.236ms;4293.0s 誤差為 64.080ms。
運算速度
測試方法:抽取某幾個時間值,循環 1M 次運算,計量 1M 次運算總耗時時間。
從抽取的幾個值測試結果看,第一種算法耗時比較穩定,第二種算法對不同值的運算時間差異很大。特別的,3.317s 這個值用第二種算法,1M 次運算總耗時時間可能達到 3000s。
從上一小節的精度比對可以看出,第二種算法對精度要求太高了。下面降低第二種算法的精度,達到和第一種一樣的精度再重復一次。修改代碼如下
1if(dev==0){
2//end
3index=i;
4break;
5}elseif(dev>dev_min){
6break;
7}elseif(dev 8dev_min=dev;
9index=i;
10}
再次測試結果:
我們可以看出來,在相同精度條件下,第二種算法的運算速度比第一種快很多,而且耗時反而變得更集中。
其實,對結束條件再次修正,將dev == 0
的嚴苛誤差條件換成dev <= 1
也不會出現上面 3000+s 慢速。
1if(dev<=?1){
2//end
3index=i;
4break;
5}elseif(dev>dev_min){
6break;
7}elseif(dev 8dev_min=dev;
9index=i;
10}
超過 4295s 的超長定時
需要修改rt_uint64_t timer_cnt
的定義為 64 位無符號整型rt_uint64_t timer_cnt
。
又因為定時時間很長很長,對誤差要求可以降低一些,對第二種算法做的第二處修改:
1if(dev<=?500){
2//end
3index=i;
4break;
5}elseif(dev6dev_min=dev;
7index=i;
8}
超長時間,第二種算法的表現也很優秀。第三組數據第一種方法竟然出錯了,沒算出結果。
下面是 10k 次(沒有進行 1W 次是因為有些時間太長了)運算時間統計
返璞歸真
以上是對兩種算法從不同角度進行的比對測驗。看似用 float 可以計算更大的定時數,但是,測試結果并不那么理想。使用 64位整型數計算,可能得到比用 float 更精確的結果。
使用 32 位無符號整型數運算雖然最大定時時間只有 4294.9s 。但是我們也看到了,第一種方法有可能出現計算誤差的,當誤差超過 1ms 我們用 rt_thread_mdelay 或者 rt-thread 的軟/硬定時器,可能結果比硬件定時器更精確了,反而失去了精確定時器的意義。在這個前提下,使用 32 位無符號整型數已經足夠了。
算法及測試源碼見:
https://gitee.com/thewon/rt_thread_repo/tree/master/user
-
算法
+關注
關注
23文章
4600瀏覽量
92646 -
定時器
+關注
關注
23文章
3237瀏覽量
114475 -
RT-Thread
+關注
關注
31文章
1273瀏覽量
39924
原文標題:RT-Thread 驅動篇 之 hwtimer 重載值算法
文章出處:【微信號:RTThread,微信公眾號:RTThread物聯網操作系統】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論