FreeRTOS如何結束和從新啓動調度程序

  大多數主機或桌面系統(好比LinuxMacWindows)都有一個正常的用例,你能夠在早上啓動操做系統,而後在晚上關閉它,而後你就離開機器。嵌入式系統是不一樣的:他們沒有參加,他們應該永遠運行。並不是每一個嵌入式系統都須要運行操做系統(或者在那個世界中:實時操做系統或RTOS),但這一樣適用於:在RTOS啓動後,並不意味着它將關閉並從新啓動。在某種程度上,他們根本不支持關閉重啓功能。若是收集覆蓋率信息,這將很是有用:html

來自FreeRTOS應用程序的覆蓋信息git

  對於FreeRTOS:若是我真的須要關閉RTOS並從新啓動它會怎麼樣,由於默認狀況下不支持。這就是本文的內容......github

介紹web

  嵌入式系統與桌面系統根本不一樣:雖然不時關閉和從新啓動臺式機或筆記本電腦系統是正常的,但這不是計劃或打算用於嵌入式系統:本質上它應該始終運行。從嵌入式系統的主要進一步能夠看出這一點:一般主要永遠不會返回並保持運行,以下所示:架構

void main(void) {less

  InitClocks();eclipse

  InitPins();ide

  InitDrivers();函數

  for(;;) {工具

    AppRun();

  }

  /* 從未離開主程序 */

}

  若是使用RTOS運行,相似的東西適用於嵌入式系統,其中看起來相似於:

void main(void) {

  InitClocks();

  InitPins();

  InitDrivers();

  CreateInitialTasks();

  StartScheduler();

  /* 調度程序永遠不會終止,因此不該該到達這裏 */

  for(;;) { }

  /* 從未離開主程序 */

}

爲何關機並重啓?

  顯然,對於嵌入式RTOS而言,RTOS關閉或重啓的需求可能不是最須要的。我仍然發現它很是有用:

 

在調度程序關閉後寫入gcov覆蓋信息

  • 在監聽更新時運行RTOS是有意義的。在某些狀況下,在執行更新時運行RTOS確定是可能的,但在某些階段我必須中止並從新啓動它。這能夠經過重置和從新啓動系統來完成(例如,請參閱「 如何使用軟件重置ARM Cortex-M 」)。我發現它是關閉RTOS的更好方法,而後在RTOS以外進行更新並重啓系統。

 

從任務結束FreeRTOS調度程序

  • 另外一個用例是低功耗應用。雖然FreeRTOS在低功耗模式下也很出色(參見「 使用FreeRTOS實現低功耗:無空閒模式 」),但若是能夠關閉更多活動系統,應用程序甚至能夠進入功耗更低的低功耗模式。所以,只有在個人應用程序的使用壽命期間才能運行RTOS纔能有意義,而不是在其餘部分運行RTOS,這爲我提供了更大的靈活性和更低功耗的機會。

 

vTaskStartScheduler()後跟低功耗模式

  因此我但願你明白爲何RTOS的關閉和重啓是有意義的。包括FreeRTOS在內的大多數RTOS都可以靜音調度程序(例如vTaskSuspendAll()),但仍然存在RTOS並使用系統資源。可是,若是須要,徹底刪除並從新啓動它的能力將是很酷的事情。

FreeRTOS

  FreeRTOS確實有一個調度程序啓動函數(vTaskStartScheduler()),甚至在其API中有一個vTaskEndScheduler()函數:

 

FreeRTOS vTaskEndScheduler()API函數

  但正如論壇和API描述中所述,它僅支持PC端口(請參閱API說明):「 注意:這僅適用於x86實模式PC端口。

  原來的FreeRTOS端口也是如此。我擴展的端口確實支持這個,我在ARM Cortex-MHCS08應用程序中使用它:-)

vTaskEndScheduler()和vTaskStartScheduler()

  雖然RTOS已準備好將其關閉的API調用,但FreeRTOS在調用vTaskEndScheduler()以後沒有適當的基礎結構來從新啓動RTOS。但這正是我想要的:在結束後重啓RTOS

  要可以結束調度程序,必須在FreeRTOSConfig.h中將如下宏設置爲1

#define INCLUDE_vTaskEndScheduler                 1

  挑戰在於:在調度程序啓動以後,在vTaskStartScheduler()調用以後當即返回代碼並不容易。由於具備本身的堆棧的任務,加上FreeRTOSM3的標準端口,M4M7甚至會重置MSP堆棧指針(參見本論壇討論)。所以,若是我想返回調度程序已啓動的位置,我須要阻止重置ARM Cortex上的MSP堆棧指針。這就是我爲FreeRTOS添加一個設置來配置它的緣由:

 

重置MSP設置

  這將設置如下配置:

#ifndef configRESET_MSP

  #define configRESET_MSP                         (0)

   /*!< 1: reset MSP at scheduler start (Cortex M3/M4/M7 only); 0: do not reset MSP */

#endif

  將此設置設置爲0,個人端口在調度程序起始點執行**重置MSP

 

端口代碼重置msp堆棧指針

  這意味着並不是完整的MSP堆棧可用於中斷(參見「 ARM Cortex-M中斷和FreeRTOS:第3部分 」),但這一般也能夠。

  爲了可以跳回到調度程序的起點,我使用了C庫的一個很酷的功能:setjmp / longjmp(參見http://web.eecs.utk.edu/~huangj/cs360/360/notes/ Setjmp / lecture.html

  首先,我須要一個跳轉緩衝區變量:

#if INCLUDE_vTaskEndScheduler

#include <setjmp.h>

static jmp_buf xJumpBuf; /* Used to restore the original context when the scheduler is ended. */

#endif

  在xPortStartScheduler()裏面我設置了跳轉緩衝區:

#if INCLUDE_vTaskEndScheduler

    if(setjmp(xJumpBuf) != 0 ) {

      /* here we will get in case of a call to vTaskEndScheduler() */

      __asm volatile(

        " movs r0, #0         \n" /* Reset CONTROL register and switch back to the MSP stack. */

        " msr CONTROL, r0     \n"

        " dsb                 \n"

        " isb                 \n"

      );

      return pdFALSE;

    }

#endif

  vPortStartFirstTask(); /* Start the first task. */

  /* Should not get here! */

  return pdFALSE;

  若是創建跳轉緩衝區,setjmp()返回0,而且在調用setjmp()的狀況下返回!= 0將在vTaskEndScheduler()期間調用。

void vTaskEndScheduler( void )

{

  /* Stop the scheduler interrupts and call the portable scheduler end

     routine so the original ISRs can be restored if necessary.  The port

     layer must ensure interrupts enable bit is left in the correct state. */

  portDISABLE_INTERRUPTS();

  xSchedulerRunning = pdFALSE;

  vPortEndScheduler();

}

  而後,vPortEndscheduler()執行RTOS的全部清理和重置。重置要求會形成調試器對任何RTOS認知的混淆:

void vPortEndScheduler(void) {

  vPortStopTickTimer();

  vPortInitializeHeap();

  uxCriticalNesting = 0xaaaaaaaa;

  /* Jump back to the processor state prior to starting the

     scheduler.  This means we are not going to be using a

     task stack frame so the task can be deleted. */

#if INCLUDE_vTaskEndScheduler

  longjmp(xJumpBuf, 1);

#else

  for(;;){} /* wait here */

#endif

}

  有了這個,調度程序優雅地終止,我又回到了主堆棧上:

 

在調用vTaskEndScheduler()以後在msp堆棧上

摘要

  默認狀況下,對於嵌入式目標,FreeRTOS不支持結束調度程序或在結束後從新啓動調度程序。本文描述了ARM Cortex-M的一個端口,它實現了vTaskEndScheduler(),並可以在沒有上電覆位的狀況下再次調用vTaskStartScheduler()。結束和啓動調度程序對於低功耗應用程序很是有用,在應用程序的實時循環或引導加載程序應用程序中不須要RTOS的狀況。該概念用於恩智浦的不一樣ARM Cortex-M內核,包括8/16S08微控制器,但能夠輕鬆用於任何其餘微控制器架構。

參考連接

 

聲明:本文爲翻譯文章,原文做者是:Erich Styger,原文址爲:https://mcuoneclipse.com/2019/01/20/freertos-how-to-end-and-restart-the-scheduler/

歡迎關注:

相關文章
相關標籤/搜索