沒有使用RTOS實時作業系統,一個祼奔的單片機如果要每隔20ms掃描一次按鍵,100ms讓LED變化一次,我們應該怎么做?
第一種實作方法:
void main(void)
{
u8 cnt;
HardInit(void);
while(1)
{
if (cnt++ >=5)
{
cnt = 0;
ScanKey();//掃描按鍵
}
LED = ~LED; //LED燈取反一次
delay_ms(20);
DoSomething(); //做其它事情
}
}
這是采用delay延時的方法,LED和按鍵掃描都能執行,但是DoSomething()也會被延時成20ms執行一次,
第二種實作方法:
bool FlagSysClk2ms;
bool FlagSysClk20ms;
bool FlagSysClk100ms;
bool FlagSysClk1s; //1秒
u8 CntSysClk20ms;
u8 CntSysClk100ms;
u8 CntSysClk1s;
void Timer_Init()
{
//清零
FlagSysClk2ms = 0;
FlagSysClk20ms = 0;
FlagSysClk100ms = 0;
FlagSysClk1s = 0;
CntSysClk20ms = 0;
CntSysClk100ms = 0;
CntSysClk1s = 0;
//定時器初始化
//省略....
}
#pragma vector=0x19
__interrupt void TIM4_UPD_OVF_IRQHandler(void)
{
//2ms 產生一次中斷
FlagSysClk2ms = 1;
if(++CntSysClk20ms >= 10) //20ms
{
CntSysClk20ms = 0;
FlagSysClk20ms = 1;
if (++CntSysClk100ms >= 5) //100ms
{
CntSysClk100ms = 0;
FlagSysClk100ms = 1;
if (++CntSysClk1s >= 100) //1s
{
CntSysClk1s = 0;
FlagSysClk1s = 1;
}
}
}
}
void main(void)
{
HardInit(void);
Timer_Init();
while(1)
{
if (FlagSysClk20ms)
{
FlagSysClk20ms = 0;
ScanKey();//掃描按鍵
}
if (FlagSysClk100ms)
{
FlagSysClk100ms = 0;
LED = ~LED; //LED燈取反一次
}
DoSomething();
}
}
我們用一個定時器,每2ms中斷一次,計數到20ms,100ms分別做個標記,在main函式中判斷這個標記來確定時間是否到了,這樣不會像delay_ms函式那樣一直等待,DoSomething()可以無等待執行,
第一種方法可以大致滿足要求,但是程式要執行任務多了,延時函式就可能會影響到回應速度,第二種方法能較好解決延時問題,但是有沒有注意到,定時中定義了太多變數,移植這樣的程式要修改的地方多,不夠方便,又容易出錯,例如,現在要把LED改成250ms執行一次,就需要修改中斷函式,重新計算時間
這樣容易改錯,程式跑不正常,我們把第二種方法優化一下,
第二種方法的演變:
#define DELAY_NUM 5 //要計數的數量
uint16_t DelayTable[DELAY_NUM]={0};
uint8_t i;
void Timer_Init()
{
for (i=0;i<DELAY_NUM;i++)DelayTable[i] = 0;//清零
//定時器初始化
//省略....
}
#pragma vector=0x19
__interrupt void TIM_IRQHandler(void)//1ms 產生一次中斷
{
for (i=0;i<DELAY_NUM;i++) DelayTable[i]++; //陣列中每個元素都加1
//清除中斷
}
//nms:要定時的毫秒數
//index:定時器號(陣列索引)
BitStatus SysGetSignal_1ms(uint16_t nms,uint8_t index)
{
if (index <= DELAY_NUM)
{
if (DelayTable[index] >= nms) //計數大于定時值,說明時間到
{
DelayTable[index] = 0;
return SET;
}
}
return RESET;
}
//復位一個定時信號
//index 定時陣列的索引
void SysResetSignal(uint8_t index)
{
if (index < DELAY_NUM)DelayTable[index] = 0;
}
void main(void)
{
HardInit(void);
Timer_Init();
while(1)
{
if (SysGetSignal_1ms(20,0))
{
ScanKey();//掃描按鍵
}
if (SysGetSignal_1ms(100,1))
{
LED = ~LED; //LED燈取反一次
}
DoSomething();
}
}
聰明的你有沒有發現程式一下子變得如此簡潔高效!
它的運行原理就是:定時器每隔1ms產生一次中斷,定義一個陣列,用來記錄定時產生的次數,在中斷里把所有陣列元素加1,然后通過呼叫SysGetSignal_1ms()判斷定時是否到達,

這樣做的優點:
高效簡潔:中斷函式中只做記數,耗時短,時間確定
修改方便:1、如果需要很多個定時,只要修改DELAY_NUM把陣列擴大,
2、只要改變SysGetSignal_1ms的引數就能改變定時時間 
3、只管呼叫一個函式就行,不需要清除標記,你有沒有經常忘記清除標記而讓自己瞎拆騰半天的經歷?

4、在主函式中干的啥事如此的明了

做的專案多了,51、PIC、STM8、SMT32 不同平臺切換來切換去,就會體會,怎么讓代碼能重復使用,方便移植,安全可靠,是一件多么重要的事,如果你也在追求寫一些可靠優雅的代碼,歡迎留言探討
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/191578.html
標籤:python
上一篇:miniPCIE介面更換M.2/NGFF介面AX200無線網卡教程——華碩X450V筆記本更換intel AX200無線網卡
下一篇:2020年你還在用Arduino??快開始用PlatformIO開發Esp8266/32、Arduino、STM32,十分鐘親測ESP8266
