FreeRTOS如何结束和重新启动调度程序
大多數主機或桌面系統(比如Linux,Mac或Windows)都有一個正常的用例,你可以在早上啟動操作系統,然后在晚上關閉它,然后你就離開機器。嵌入式系統是不同的:他們沒有參加,他們應該“永遠”運行。并非每個嵌入式系統都需要運行操作系統(或者在那個世界中:實時操作系統或RTOS),但這同樣適用于:在RTOS啟動后,并不意味著它將關閉并重新啟動。在某種程度上,他們根本不支持“關閉”和“重啟”功能。如果收集覆蓋率信息,這將非常有用:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 來自FreeRTOS應用程序的覆蓋信息
對于FreeRTOS:如果我真的需要關閉RTOS并重新啟動它會怎么樣,因為默認情況下不支持。這就是本文的內容......
介紹
嵌入式系統與桌面系統根本不同:雖然不時關閉和重新啟動臺式機或筆記本電腦系統是正常的,但這不是計劃或打算用于嵌入式系統:本質上它應該“始終”運行。從嵌入式系統的“主要”進一步可以看出這一點:通常主要永遠不會返回并保持運行,如下所示:
| void?main(void) { ??InitClocks(); ??InitPins(); ??InitDrivers(); ??for(;;) { ????AppRun(); ??} ??/* 從未離開主程序 */ } |
如果使用RTOS運行,類似的東西適用于嵌入式系統,其中看起來類似于:
| void?main(void) { ??InitClocks(); ??InitPins(); ??InitDrivers(); ??CreateInitialTasks(); ??StartScheduler(); ??/* 調度程序永遠不會終止,所以不應該到達這里 */ ??for(;;) { } ??/* 從未離開主程序?*/ } |
為什么關機并重啟?
顯然,對于嵌入式RTOS而言,RTOS關閉或重啟的需求可能不是最需要的。我仍然發現它非常有用:
- 在RTOS關閉后運行某些軟件*是有意義的。例如,如果我收集覆蓋率信息(請參閱“?向Eclipse添加GNU覆蓋工具?”和“?使用Eclipse Neon和ARM gcc 5在嵌入式目標上實現GNU代碼覆蓋率?”),我不希望干擾最后一個進程以轉儲數據RTOS。另外,我需要獨占訪問系統資源,包括大量的堆棧空間:關閉RTOS可以讓我恢復文件I / O操作所需的所有RAM和堆棧空間。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?在調度程序關閉后寫入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-M和HCS08應用程序中使用它:-)。
vTaskEndScheduler()和vTaskStartScheduler()
雖然RTOS已準備好將其關閉的API調用,但FreeRTOS在調用vTaskEndScheduler()之后沒有適當的基礎結構來重新啟動RTOS。但這正是我想要的:在結束后重啟RTOS。
要能夠結束調度程序,必須在FreeRTOSConfig.h中將以下宏設置為1:
| #define INCLUDE_vTaskEndScheduler???????????????? 1 |
挑戰在于:在調度程序啟動之后,在vTaskStartScheduler()調用之后立即返回代碼并不容易。因為具有自己的堆棧的任務,加上FreeRTOS和M3的標準端口,M4和M7甚至會重置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/16位S08微控制器,但可以輕松用于任何其他微控制器架構。
參考鏈接
?
聲明:本文為翻譯文章,原文作者是:Erich Styger,原文網址為:https://mcuoneclipse.com/2019/01/20/freertos-how-to-end-and-restart-the-scheduler/
歡迎關注:
總結
以上是生活随笔為你收集整理的FreeRTOS如何结束和重新启动调度程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 滤波器开发之二:基于算数平均的带阻平滑滤
- 下一篇: 在emIDE中创建STM32项目