解決程序堵塞的優化方法(二)

    本文鏈接上篇繼續敘述,如果沒有看到看一篇的,大家可以點擊傳送門觀看。

作者:良知猶存

轉載授權以及圍觀:歡迎添加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; 
      }   
    }
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

    這就是我分享的通過狀態機實現的解決堵塞方法,裏面代碼是實踐過的,如果大家有什麼更好的思路,歡迎分享交流哈

 

四、 鏈表法


       通過上面結構體狀態機程序,我們知曉通過一些特定的延時程序等待,配合狀態機可以替換很多的隊列式延時。可是通過結構體 定義的時候,如果需要很多處的延時阻塞替換,則會產生很多的延時變量,這個時候需要管理很多不同名稱的變量,這樣容易造成混亂。所以我們可以選擇鏈表使用,鏈表定義好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——

                                                   1582516653291945.png

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章