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

      剛開始學習編寫嵌入式的代碼,例如在單片機上控制LED燈500ms亮滅,因爲屬於初學,對於MCU運行效率沒有要求,所以大部分的教程都是delay_ms(500)。平時在一些基本調試中,對我們影響很小。但是我們需要了解,當程序使用大量類似delay形式的函數時,會對程序造成嚴重阻塞。


         以delay延時函數爲例:一般寫程序,都是通過while()或者for()加上條件判斷,進行循環累加,如果延時函數執行過程中,有中斷響應執行,但是當中斷服務函數執行完畢,程序跳回後臺主循環執行的時候,由於延時函數裏面是條件循環,在條件沒有符合要求的時候,程序將一直堵塞到延時函數,無法執行響應中斷處理函數的次級函數。

    e.g. 曾經在學校項目中,在控制電機的程序中對於勻加速勻減速的部分添加了delay函數,實際設備運行起來後,別說勻加/減速,就是加速減速變化都被阻塞嚴重,嚴重影響了操作的體驗。所以下面介紹一下我操作的幾種解決阻塞的程序實現。

 

 

/*while形式*/
void Delay_ms(u16 nms)
{         
  while(nms--)
  {
    u32 temp;       
    SysTick->LOAD=(u32)1*fac_ms;        
    SysTick->VAL =0x00;              
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;  
    do
    {temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));    
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;  
    SysTick->VAL =0X00;                 
  }
}

​
/*for 形式*/
void delay_ms(void)
​
/{
   for(u8 i =0;i<230;i++)
    for(int j =0;j<320;j++);
}

總述       

1.通過運用RTOS,線程裏面調用系統提供的延時等函數,實現解決阻塞。

2.全局變量方法,定時器計數條件判斷替代延時。

3.使用狀態機方式,分解動作,替代延時實現。

4.使用鏈表式,與第三種相似。

一、 RTOS方法

      誠然現在RTOS很火,大家可以使用各種實時操作系統,利用優化後的delay函數,這個時候我們可以隨便調用delay而不用擔心阻塞,因爲在RTOS中,在線程使用系統提供的delay函數,線程會被內核掛起,系統會自動切換到其他優先級別更高的線程運行。但是一定記住不是所有程序都適合RTOS。

    參照其他人的博文:“如果程序任務可折分性較差,折分後的各個任務之間有 N 多的同步問題和複用資源問題,此時不要用多任務操作系統,我們不能因爲任務而任務,任務多並沒有想象的好,多任務是用降低實時性來換取軟件開發的獨立性,不要被實時多任務操作系統的實時兩個字騙了,這個實時只是相對於其它非實時性多任務操作系統來講的,實時性最高的當然是你自己編寫的單任務程序。”


    RTOS的延時代碼做了底層的變化,和我們寫的while等循環代碼有很大差別。RTOS延時函數進入之後會將需要延時的線程掛起進入阻塞態,底層時鐘節拍計數器進行定時,直至預約的延時時間到來,再利用任務調度器喚醒線程,線程按照RTOS的優先級執行。

1.RT_Thread 延時代碼部分

                            

                           

2.U/COS III 延時代碼部分

                               

                                  

 

3.FreeRTOS 延時代碼部分

                                 

                                         

各大RTOS官網資料

可以按照“RTOS”關鍵詞自行搜索,包括社區資料,源碼以及開發手冊的提供。下面展示國產操作系統rt_thread官網界面。

                              

正點原子與野火等資料

可以按照”正點原子“關鍵詞自行搜索,包括正點原子的BBS以及各大視頻網站都有其資料。

                                

 

                                 

一、 全局變量法

       全局變量的方法其實與時間管理的思路很像,將設置變量放置於定時器,然後進行計數器累加,再在主循環或者可以進行反覆觸發的函數中,通過對此變量數值的判斷,從而實現等量delay函數的執行。實現方法也很簡單,定義一個變量放置於定時器即可,剩下的通過變量值除以間隔時間來判斷時間長短,再進行執行相應的函數。

    雖然此種方法比較簡單,但是需要大家注意,通過設置全局變量法進行替代delay函數,只能放置於主循環或者多觸發的程序執行部分,如果放置在初始化的部分,則沒有實際用處。

代碼實現部分 ->註釋部分需要重點觀看

/*定義一個全局變量*/
static u32 G_timer = 0;
/*定時器的時間間隔由用戶自己把握,此處爲了計算方便,定時器間隔設置爲 1ms*/
void TIM_IRQ(void)
{
  G_timer++;
  if(G_timer % 100 == 0)/*計數器每 100ms 進入執行*/
  {
    taskfun();/*此處函數不可以執行復雜運算,只可以實現簡單的IO控制,
              以及一些基本的變量賦值、加減的。
              如果程序過於複雜,會導致此處中斷服務函數造成堵塞*/
  }
}
/*主程序*/
int main(int argc,char** argv)
{
  SystemInit();
  static u32 lastcnt1 =0 ,lastcnt2 =0 ;
  while(true)
  {
    if(G_timer % 100 == 0)/*此處不能使用如此函數執行,因爲主循環中,
                          一般晶振頻率很高,程序在主循環中執行的間隔小於
                          計數器執行的頻率,也就是計數器計一個數,程序
                          可能執行多次。所以這種方法不能使用,建議使用下面的方法*/
    {
      taskfun1();
    }  
    if(G_timer % 1000 == 0)/*計數器每 1000ms或者叫1s 進入執行*/
    {
      taskfun2();/*此處可以執行復雜的運算,不需要擔憂阻塞*/
       lastcnt1 = G_timer ;
    } 
    if(G_timer % 10000 == 0)/*計數器每 10000ms或者叫10s 進入執行*/
    {
      taskfun3();/*註釋與taskfun2();相同*/
      lastcnt2 = G_timer ;            
    } 
  }
}

     到現在介紹了兩種方法,因爲篇幅有限,過長的文字不適合大家一次性閱讀,我會在下一篇文章繼續分享,解決堵塞的第三種及第四種方法,希望可以幫助到大家,謝謝。

 

                                                                     

                                                                                   掃碼關注更多精彩

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