本文鏈接上篇繼續敘述,如果沒有看到看一篇的,大家可以點擊傳送門觀看。
作者:良知猶存
轉載授權以及圍觀:歡迎添加wx:Allen-Iverson-me-LYN
總述
1.通過運用RTOS,線程裏面調用系統提供的延時等函數,實現解決阻塞。
2.全局變量方法,定時器計數條件判斷替代延時。
3.使用狀態機方式,分解動作,替代延時實現。
4.使用鏈表式,與第三種相似。
三、 狀態機法
狀態機執行與全局變量的區別在於,狀態機的方法是更進一層的定時操作,可以通過狀態機在主循環或者觸發函數實現多個動作的延時操作。
狀態機中大致會分爲兩類延時替代操作,一種注重多個動作的延時,一種叫精確時間的延時。第一種情況經常會在一些需要雙方有來有往的應答操作出現,而後一種出現在明確延時操作過程中。接下來我們展示一下代碼風格吧。
/*堵塞型的代碼*/
void clogged(void)
{
taskfun1();
delay(500);
taskfun2();
delay(100);
taskfun3();
delay(20);
}
int main(int argc,char** argv)
{
SystemInit();
while(1)
{
clogged();
taskfun();/*此段代碼執行的時候會被堵塞*/
}
}
1.注重動作的狀態機
/*注重分解動作的狀態機*/
void TIM_IRQ(void)/*定時器中斷服務函數*/
{
FSMtimer ++;/*設置合適的中斷時間間隔*/
}
static u32 FSMtimer = 0,LastFSMtimer = 0;/*用來計時變量*/
static u8 FSMSta = 0;/*設置狀態的工作步驟的變量*/
void actionFSM(void)
{
switch(FSMSta)
{
case 0:
taskfun1();
FSMSta = 1;
break;
case 1:
taskfun2();
FSMSta = 2;
break;
case 2:
taskfun3();
FSMSta = 3;
break;
case 3:
if(FSMtimer - LastFSMtimer >100 )/*定時判斷完成任務*/
{
taskfun4();
FSMSta = 4;
}
else
{
taskfun5();
FSMSta = 1;/*條件判斷出現問題,實現動作跳轉,進行動作重複*/
}
break;
case 4:
taskidle();/*狀態機的狀態執行完成可以進行空閒任務*/
break;
}
int main(int argc,char** argv)
{
SystemInit();
FSMSta = n;/*初始化設置狀態機的起始步*/
while(1)
{
actionFSM();/*替換效果如下*/
taskfun();/*此段代碼執行的時候會被堵塞*/
}
}
2.注重精確延時固定動作的狀態機
/*延時操作的結構體*/
__packed typedef struct{
bool IsFSM_start;/*延時計數的標誌*/
u8 FSM_Sta:7;/*狀態機執行的狀態*/
u32 FSM_target;/*延時定時的目標值*/
u32 FSM_count;/*狀態機用來在計數器中累加計時的變量*/
u8 FSM_flag;/*狀態機part的標誌*/
}DelayTypeDef;
DelayTypeDef DelayFSM;
/*定時中斷服務函數*/
void TIM_IRQ(void)
{
if(DelayFSM.FSM_flag==1)/*執行狀態機部分標誌*/
{
if(DelayFSM.IsFSM_start== 1)/*動作執行代碼,爲零則不進入計時操作*/
{
(DelayFSM.FSM_count<DelayFSM.FSM_target)?
(DelayFSM.FSM_count++):
(DelayFSM.IsFSM_start=0,DelayFSM.FSM_Sta++);/*IsFSM_start==0
進入狀態機執行*/
}
}
}
u8 taskfun(void)
{
if(DelayFSM.FSM_flag==0)/*初始化代碼部分*/
{
DelayFSM.FSM_flag= 1;/*設置狀態機flag爲1 ,執行下一部分程序*/
DelayFSM.IsFSM_start= 0;/*開始狀態機*/
DelayFSM.FSM_Sta= 1;/*初始化第一步*/
}
else if (DelayFSM.FSM_flag==1)
{
if(DelayFSM.IsFSM_start== 0)
{
switch(DelayFSM.FSM_Sta)
{
case 0x01:
DelayFSM.IsFSM_start= 1;/*IsFSM_start==1 進入計時器計時*/
DelayFSM.FSM_count= 0; /*計數器清零*/
DelayFSM.FSM_target= 1000;/*設置計時的目標時間爲
1000個定時器觸發時間間隔*/
taskfun1();/*任意任務函數*/
break;
case 0x02:
DelayFSM.IsFSM_start= 1;/*註釋如上*/
DelayFSM.FSM_count= 0; /*註釋如上*/
DelayFSM.FSM_target= 500;/*目標值爲500基數*/
taskfun2();/*任意任務函數*/
break;
case 0x03:
DelayFSM.IsFSM_start= 0;/*狀態機完成狀態 start清零*/
DelayFSM.FSM_Sta= 0; /*清零代表狀態機將進入空閒模式*/
DelayFSM.FSM_target= 0;/*目標值設置位零*/
DelayFSM.FSM_flag= 3;/*隨意賦值,這個值與你在主程序的判斷,
重啓狀態機的標記相關,可看main中程序*/
taskfun3();/*任意任務函數*/
break;
default:
break;
}
}
}
else
return 0;
return 1;
}
int main(int argc,char** argv)
{
SystemInit();
while(1)
{
taskfun();/*狀態機執行*/
if (DelayFSM.flag==3)/*重複使用次狀態機*/
{
DelayFSM.flag = 0;
}
}
}
這就是我分享的通過狀態機實現的解決堵塞方法,裏面代碼是實踐過的,如果大家有什麼更好的思路,歡迎分享交流哈
四、 鏈表法
通過上面結構體狀態機程序,我們知曉通過一些特定的延時程序等待,配合狀態機可以替換很多的隊列式延時。可是通過結構體 定義的時候,如果需要很多處的延時阻塞替換,則會產生很多的延時變量,這個時候需要管理很多不同名稱的變量,這樣容易造成混亂。所以我們可以選擇鏈表使用,鏈表定義好head之後,只要模式一樣數據變量,我們只需要添加一個節點即可。
注:這裏使用鏈表是單鏈表,雙鏈表暫且不需要用到。鏈表的使用方法我是參考一位博主的文章而使用的,如果需要大家也可以去欣賞他的文章,這裏是傳送門。
#define ONCE 0
#define CYCLE 1
typedef struct Delay_TIMER
{
bool mode;/*設置的模式 0 單次計時 1 循環計時*/
bool isActivate;/*判定是否需要啓動定時*/
volatile u32 cnt;/*計數器*/
u32 target;/*目標數值*/
struct Delay_TIMER* next;/*下一個節點*/
}Delay_TIMER_t;
static struct Delay_TIMER *head = NULL;/*定義鏈表的頭*/
/* 定時器中斷服務程序*/
void TIM_ISR(void)
{
struct Delay_TIMER *t = head;
if (t == NULL)
return;
while(t != NULL)
{
if (t->isActivate == true)
t->cnt++;/*計數器定時累加*/
t = t->next;
}
}
/*插入一個需要定時的鏈表節點,並初始化成員變量*/
void Insert_delay_node(Delay_TIMER_t *node,bool mode,uint32_t target)
{
struct Delay_TIMER *t = head;
node->isActivate = true;/*設置爲開*/
node->mode = mode;/*模式支持自己設置*/
node->cnt = 0;
node->target = target;/*設置目標時長數值*/
node->next = NULL;
if (head == NULL)
{
head = node;
return;
}
while(t->next != NULL )
{
if (t == node) return;
t = t->next;
}
t->next = node;
}
/*刪除不需要使用的延時節點*/
void Delete_delay_node(Delay_TIMER_t *node)
{
struct Delay_TIMER *p = head,*t = head;
if (head == node)
{
head = node->next;
return;
}
while(t->next != NULL )/*當需要刪除尾部最後一個節點的時候,
tail->next == NULL,這個時候循環跳出,節點無法刪除*/
{
if (t == node) /*刪除節點*/
{
p->next = node->next;/*node->next == NULL;*/
node->next = NULL;
}
else
{
p = t;
t = t->next;/* t = p->next;*/
}
}
if(t->next == NULL && head != node)/*刪除最後一個節點*/
{
p->next = NULL;/*尾部最後一個節點直接置NULL*/
}
}
/*查詢軟件計時器是否超時*/
bool Is_delay_timer_Out(Delay_TIMER_t *node)
{
bool res = false;
if (node->cnt >= node->target)
{
res = true;
if (node->mode == CYCLE)
{
node->cnt = 0;
node->isActivate = true;
}else
node->isActivate = false;
}
return res;
}
/*主程序*/
int main(int argc,char** argv)
{
SystemInit();
u8 mode = 0;/*單次模式*/
static u8 STA = 1;/*設置狀態機初始step*/
static Delay_TIMER_t task_delay;
Insert_delay_node(task_delay,mode,1000);/*初始化節點,設置爲單次模式,
計時目標爲1000個計數間隔*/
while(1)
{
switch(STA)
{
case 1:
taskfun1();
STA = 2;
break;
case 2:/*延時等待狀態*/
if (Is_delay_timer_Out(&task_delay) == true)
STA = 1;
break;
}
}
}
——END——