精妙的微控制器非阻塞延時程式設計

2021-07-05 08:10:50 字數 3218 閱讀 6424

文章出處:

對於每個微控制器愛好者及工程開發設計人員,在剛接觸微控制器的那最初的青蔥歲月裡,都有過點亮跑馬燈的經歷。從看到那一排排小燈按著我們的想法在跳動時激動心情。到隨著經驗越多,越來又會感覺到這個小燈是個好東西,尤其是在除錯資源有限的環境中,有時會幫上大忙。

但對於絕大多數人,我們在最最初讓燈閃爍起來時大約都會用到阻塞延時實現,會像如下**的樣子:

while

(1)

然後,在我們接觸到定時器,我們會發現,原來用定時中斷來處理會更好。比如我們可以500ms中斷一次,讓燈亮或滅,其餘的時間系統還可以做非常之多的事情,效率一下提公升了很多。

這時我們就會慢慢意識到,第一種(阻塞延時)方法效率很低,讓晶元在那兒空執行幾百公釐,什麼也不做,真是莫大的浪費,尤其在晶元頻率較高,任務又很多時,這樣做就像在平坦寬闊的高速公路上挖了一大坑,出現事故可想而知。

但乙個微控制器中的定時器畢竟有限,如果我需要幾十個或者更多不同時間的定時中斷,每乙個時間到都完成不同的處理動作,如何去做呢。一般我們會想到在乙個定時中斷函式中再定義static 變數繼續定時,到了所需時間,做不同的動作。而這樣又會導致在乙個中斷裡做了很多不同的事情,會搶占主輪詢更多時間,有時甚至喧賓奪主,並也不是很如的思維邏輯。

那麼有沒有更好的方法來實現呢,答案是肯定的。下面介紹我在乙個專案中偶遇,乙個精妙設計的非阻塞定時延時軟體的設計(此設計主要針對於無作業系統的裸機程式)。

在上篇文章中有對

systick的介紹,比如我要設定其10ms中斷一次,如何實現呢?

也很簡單,只需呼叫 

core_cm3.h檔案中 systick_config 函式 ,當系統時鐘為72mhz,則設定成如下

即可systick_config(720000 ); (遞減計數720000次後中斷一次) 。此時systick_handler中斷函式就會10ms進入一次;

任務定時用軟體是如何設計的呢 ? 

且先看其資料結構,這也是精妙所在之處,在此作自頂向下的介紹:

其定義結構體型別如:

typedef struct

timer_struct;

其中char_field 為一聯合體,設計如下:

typedef union

char_field

而它內部的timer_bit是乙個可按位訪問的結構體:

typedef struct

timer_bit

此聯合體的這樣設計的目的將在後面的**中體現出來。

如此結構體的設計就完成了。

然後我們定義的一全域性變數,timer_struct  gtimer;

並在標頭檔案中巨集定義如下:

#define bsystem10msec        gtimer.status.field.bit0

#define bsystem50msec        gtimer.status.field.bit1

#define bsystem100msec       gtimer.status.field.bit2

#define bsystem1sec          gtimer.status.field.bit3

#define btemp10msec          gtimer.status.field.bit4

#define btemp50msec          gtimer.status.field.bit5

#define btemp100msec         gtimer.status.field.bit6

#define btemp1sec            gtimer.status.field.bit

另外為了後面程式清晰,再定義一狀態指示:

typedef enum

timerstatus;

至此,準備工作就完成了。下面我們就開始大顯神通了!

首先,10ms定時中斷處理函式如,可以看出,每到達10ms 將把

btemp10msec置1,

每50ms 將把

btemp50msec 

置1,每100ms 將把

btemp100msec 

置1,每1s 將把

btemp1sec 

置1,void systick_handler(void)

if(0 =

=(gtimer.tick10msec % 10))if

(100 =

= gtimer.tick10msec)

} 而這又有什麼用呢 ?

這時,我們需在主輪詢while(1)內最開始呼叫乙個定時處理函式如下:

void systimer _process(void)

if(btemp50msec)

if(btemp100msec)

if(btemp1sec)

gtimer.status.byte &

= 0x0f;

} 此函式開頭與結尾兩句

gtimer.status.byte &

= 0xf0;

gtimer.status.byte &

= 0x0f

就分別巧妙的實現了bsystem*** (低4位) 和 btemp***

(高4位)的清零工作,不用再等定時到達後還需手動把計數值

清零。此處

清零工作用到了聯合體中的變數共用乙個起始儲存空間的特性。

但要保證while(1)輪詢時間要遠小於10ms,否則將導致定時延時不準確。這樣,在每

輪詢一次,就先把

bsystem*** ,再根據

btemp***判斷是否時間到達,並把對應的

bsystem*** 置1,而後面所有的任務就都可以通過bsystem*** 來進行定時延時,在最後函式退出時,又會把

btemp***清零,為下一次時間到達後查詢判斷作好了準備。

說了這麼多,舉例說明一下如何應用:

void task_a_processing(void)

}void task_b_processing(void)

}void task_c_processing(void)

if(5 =

= ticks)

}void task_d_processing(void)

} 以上示例四個任務程序,

在主輪詢裡可進行如下處理:

int main(void)

} 這樣,就可以輕鬆且清晰實現了多個任務,不同時間內處理不同事件。(但注意,每個任務處理中不要有阻塞延時,也不要處理過多的事情,以致處理時間較長。可設計成狀態機來處理不同任務。)

精妙的微控制器非阻塞延時程式設計

對於每個微控制器愛好者及工程開發設計人員,在剛接觸微控制器的那最初的青蔥歲月裡,都有過點亮跑馬燈的經歷。從看到那一排排小燈按著我們的想法在跳動時激動心情。到隨著經驗越多,越來又會感覺到這個小燈是個好東西,尤其是在除錯資源有限的環境中,有時會幫上大忙。但對於絕大多數人,我們在最最初讓燈閃爍起來時大約都...

微控制器延時函式

精確的微控制器常用延時函式 c 誤差0us 12m 並未驗證 1 延時0.5ms void delay0.5ms void 誤差0us 2 延時1ms void delay1ms void 誤差0us 3 延時2ms void delay2ms void 誤差0us 4 延時3ms void del...

51微控制器延時函式

c程式中可使用不同型別的變數來進行延時設計。經實驗測試,使用unsigned char型別具有比unsigned int更優化的 在使用時應該使用unsigned char作為延時變數。以某晶振為12mhz的微控制器為例,晶振為12mhz即乙個機器週期為1us。一.500ms延時子程式 程式 voi...