FreeRTOS协程
生活随笔
收集整理的這篇文章主要介紹了
FreeRTOS协程
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
FreeRTOS的協程,實際上是線程并發出來的。從協程控制塊中沒有棧空間就能夠知道,每個線程并發出來的協程共用一個棧空間。
/* 協程控制塊 */ typedef struct corCoRoutineControlBlock {crCOROUTINE_CODE pxCoRoutineFunction; /* 協程函數指針 */ListItem_t xGenericListItem; /* 狀態列表項 */ListItem_t xEventListItem; /* 事件列表項 */UBaseType_t uxPriority; /* 協程優先級 */UBaseType_t uxIndex; /* 協程ID,區別不同協程調用相同函數 */uint16_t uxState; /* 協程狀態 */ }CRCB_t;?
?
創建協程
/* 創建協程 */ BaseType_t xCoRoutineCreate(crCOROUTINE_CODE pxCoRoutineCode, UBaseType_t uxPriority, UBaseType_t uxIndex) {BaseType_t xReturn;CRCB_t *pxCoRoutine;/* 為協程控制塊申請內存空間 */pxCoRoutine = (CRCB_t *)pvPortMalloc(sizeof(CRCB_t));if(pxCoRoutine){/* 當前協程為空 */if(pxCurrentCoRoutine == NULL){/* 將該協程設為當前協程 */pxCurrentCoRoutine = pxCoRoutine;/* 初始化協程列表 */prvInitialiseCoRoutineLists();}/* 初始化協程優先級 */if(uxPriority >= configMAX_CO_ROUTINE_PRIORITIES){uxPriority = configMAX_CO_ROUTINE_PRIORITIES - 1;}/* 協程狀態 */pxCoRoutine->uxState = corINITIAL_STATE;/* 協程優先級 */pxCoRoutine->uxPriority = uxPriority;/* 協程ID */pxCoRoutine->uxIndex = uxIndex;/* 協程函數指針 */pxCoRoutine->pxCoRoutineFunction = pxCoRoutineCode;/* 初始化協程狀態列表項 */vListInitialiseItem(&(pxCoRoutine->xGenericListItem));/* 初始化協程事件列表項 */vListInitialiseItem(&(pxCoRoutine->xEventListItem));/* 將協程狀態列表項所屬協程設為該協程 */listSET_LIST_ITEM_OWNER(&(pxCoRoutine->xGenericListItem), pxCoRoutine);/* 將協程事件列表項所屬協程設為該協程 */listSET_LIST_ITEM_OWNER(&(pxCoRoutine->xEventListItem), pxCoRoutine);/* 設置協程事件列表項值為協程優先級 */listSET_LIST_ITEM_VALUE(&(pxCoRoutine->xEventListItem), ((TickType_t)configMAX_CO_ROUTINE_PRIORITIES - (TickType_t)uxPriority));/* 將協程掛接到就緒協程列表中 */prvAddCoRoutineToReadyQueue(pxCoRoutine);/* 返回成功 */xReturn = pdPASS;}/* 內存不足 */else{xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;}return xReturn; }?
?
協程調度
從代碼中可以看出,調度協程本質上就是查找超時的協程將其轉為就緒,然后調用協程。
也就是說vCoRoutineSchedule函數應該被任務循環調用。
/* 協程調度器 */ void vCoRoutineSchedule(void) {/* 檢查掛起時進入就緒的協程列表,并將協程轉移到就緒列表 */prvCheckPendingReadyList();/* 檢查延時列表,如果有協程超時則掛接到就緒列表 */prvCheckDelayedList();/* 檢查所有就緒列表,是否有協程就緒 */while(listLIST_IS_EMPTY(&(pxReadyCoRoutineLists[uxTopCoRoutineReadyPriority]))){/* 沒有協程就緒直接退出 */if(uxTopCoRoutineReadyPriority == 0){return;}--uxTopCoRoutineReadyPriority;}/* 取出最高優先級的就緒協程 */listGET_OWNER_OF_NEXT_ENTRY(pxCurrentCoRoutine, &(pxReadyCoRoutineLists[uxTopCoRoutineReadyPriority]));/* 運行協程 */(pxCurrentCoRoutine->pxCoRoutineFunction)(pxCurrentCoRoutine, pxCurrentCoRoutine->uxIndex);return; }?
?
協程調用方式
協程調用是有一定格式的,主要有三個宏:crSTART、crEND和crDELAY
void vACoRoutine(CoRoutineHandle_t xHandle, UBaseType_t uxIndex) {static int32_t ulAVariable;static const TickType_t xDelayTime = 200 / portTICK_PERIOD_MS;crSTART(xHandle);for(;;){crDELAY(xHandle, xDelayTime);/* 應用程序 */}crEND(); }將宏展開,可以看出每次調用協程會先運行應用程序,然后將協程掛接到延時列表,最后退出協程
void vACoRoutine(CoRoutineHandle_t xHandle, UBaseType_t uxIndex) {static int32_t ulAVariable;static const TickType_t xDelayTime = 200 / portTICK_PERIOD_MS;switch(((CRCB_t *)(xHandle))->uxState) {case 0:;for(;;){/* 延時時間大于0 */if((xDelayTime) > 0){/* 將協程掛接到延時列表 */vCoRoutineAddToDelayedList((xDelayTime), NULL);}/* 設置協程狀態 */((CRCB_t *)(xHandle))->uxState = (__LINE__ * 2); /* 退出協程,延時結束之后直接進入應用程序 */return;/* 應用程序,運行完之后因為for循環的存在,下一輪又開始延時 */case (__LINE__ * 2):;/* 應用程序 */}}; }?
?
協程間通信
同一任務并發出來的協程,直接使用全局變量即可,不存在共享數據的完整性問題。但是不同任務并發出來的協程之間,如果需要通信,則需要考慮共享數據的完整性問題。FreeRTOS為協程提供了專門的隊列通信API,只能用于協程之間通信,不能用于線程和協程之間通信。
發送隊列消息,是一個宏定義
/* 發送隊列消息 */ #define crQUEUE_SEND(xHandle, pxQueue, pvItemToQueue, xTicksToWait, pxResult) \ { \*(pxResult) = xQueueCRSend((pxQueue), (pvItemToQueue), (xTicksToWait)); \if(*(pxResult) == errQUEUE_BLOCKED) \{ \crSET_STATE0((xHandle)); \*pxResult = xQueueCRSend((pxQueue), (pvItemToQueue), 0); \} \if(*pxResult == errQUEUE_YIELD) \{ \crSET_STATE1((xHandle)); \*pxResult = pdPASS; \} \ }將宏定義完全展開
switch(((CRCB_t *)(xHandle))->uxState) {case 0:;for(;;){ /這一段是展開//* 發送隊列消息 */*(pxResult) = xQueueCRSend((pxQueue), (pvItemToQueue), (xTicksToWait));/* 阻塞 */if(*(pxResult) == errQUEUE_BLOCKED){/* 設置協程狀態 */((CRCB_t *)(xHandle))->uxState = (__LINE__ * 2); /* 退出協程,阻塞結束之后,直接進入(__LINE__ * 2) */return;case (__LINE__ * 2):;/* 退出阻塞,意味著發送成功或者超時 */*pxResult = xQueueCRSend((pxQueue), (pvItemToQueue), 0);}/* 請求切換協程,也意味著發送成功 */if(*pxResult == errQUEUE_YIELD){/* 設置協程狀態 */((CRCB_t *)(xHandle))->uxState = ((__LINE__ * 2)+1);/* 退出協程,下次直接進入((__LINE__ * 2)+1),然后進入應用程序 */return; case ((__LINE__ * 2)+1):;*pxResult = pdPASS;}; //* 應用程序 */} };真正用于發送隊列消息的函數為xQueueCRSend
/* 協程發送隊列消息 */ BaseType_t xQueueCRSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait) {BaseType_t xReturn;Queue_t *const pxQueue = xQueue;/* 禁止中斷 */portDISABLE_INTERRUPTS();{/* 判斷隊列是否已滿,已滿 */if(prvIsQueueFull(pxQueue) != pdFALSE){/* 等待時間大于0 */if(xTicksToWait > (TickType_t)0){/* 將協程掛接到延時列表中 */vCoRoutineAddToDelayedList(xTicksToWait, &(pxQueue->xTasksWaitingToSend));/* 恢復中斷 */portENABLE_INTERRUPTS();/* 返回阻塞 */return errQUEUE_BLOCKED;}/* 等待時間不大于0 */else{/* 恢復中斷 */portENABLE_INTERRUPTS();/* 返回錯誤 */return errQUEUE_FULL;}}}/* 恢復中斷 */portENABLE_INTERRUPTS();/* 運行到這兒說明解除阻塞或者隊列中有位置 *//* 禁止中斷 */portDISABLE_INTERRUPTS();{/* 隊列項數小于隊列長度 */if(pxQueue->uxMessagesWaiting < pxQueue->uxLength){/* 將數據拷貝到隊列中 */prvCopyDataToQueue(pxQueue, pvItemToQueue, queueSEND_TO_BACK);/* 返回成功 */xReturn = pdPASS;/* 判斷是否存在因等待消息而阻塞的協程,有 */if(listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE){/* 將協程從事件列表中移除 */if(xCoRoutineRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE){/* 請求切換協程 */xReturn = errQUEUE_YIELD;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}/* 隊列已滿 */else{/* 返回錯誤 */xReturn = errQUEUE_FULL;}}/* 恢復中斷 */portENABLE_INTERRUPTS();return xReturn; }?
接收隊列消息
/* 接收隊列消息 */ #define crQUEUE_RECEIVE(xHandle, pxQueue, pvBuffer, xTicksToWait, pxResult) \ { \*(pxResult) = xQueueCRReceive((pxQueue), (pvBuffer), (xTicksToWait)); \if(*(pxResult) == errQUEUE_BLOCKED) \{ \crSET_STATE0((xHandle)); \*(pxResult) = xQueueCRReceive((pxQueue), (pvBuffer), 0); \} \if(*(pxResult) == errQUEUE_YIELD) \{ \crSET_STATE1((xHandle)); \*(pxResult) = pdPASS; \} \ }將宏定義完全展開
switch(((CRCB_t *)(xHandle))->uxState) {case 0:;for(;;){/* 接收隊列消息 */*(pxResult) = xQueueCRReceive((pxQueue), (pvBuffer), (xTicksToWait));/* 阻塞 */if(*(pxResult) == errQUEUE_BLOCKED){/* 設置協程狀態 */((CRCB_t *)(xHandle))->uxState = (__LINE__ * 2);/* 退出協程,解除阻塞之后直接進入(__LINE__ * 2) */ return; case (__LINE__ * 2):;/* 運行到這里說明接收到消息或超時 */*(pxResult) = xQueueCRReceive((pxQueue), (pvBuffer), 0);}/* 請求切換協程,也意味著接收到消息 */if(*(pxResult) == errQUEUE_YIELD){/* 設置協程狀態 */((CRCB_t *)(xHandle))->uxState = ((__LINE__ * 2)+1);/* 退出協程,下一次直接接入((__LINE__ * 2)+1),然后運行應用程序 */return; case ((__LINE__ * 2)+1):;/* 接收到消息 */*(pxResult) = pdPASS;}; /* 應用程序 */} };真正用于接收隊列消息的函數為xQueueCRReceive
/* 協程接收隊列消息 */ BaseType_t xQueueCRReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait) {BaseType_t xReturn;Queue_t *const pxQueue = xQueue;/* 禁止中斷 */portDISABLE_INTERRUPTS();{/* 隊列項數為0 */if(pxQueue->uxMessagesWaiting == (UBaseType_t)0){/* 等待時間大于0 */if(xTicksToWait > (TickType_t)0){/* 將協程加入協程延時列表 */vCoRoutineAddToDelayedList(xTicksToWait, &(pxQueue->xTasksWaitingToReceive));/* 恢復中斷 */portENABLE_INTERRUPTS();/* 返回阻塞 */return errQUEUE_BLOCKED;}/* 等待時間不大于0 */else{/* 恢復中斷 */portENABLE_INTERRUPTS();/* 返回錯誤 */return errQUEUE_FULL;}}else{mtCOVERAGE_TEST_MARKER();}}/* 恢復中斷 */portENABLE_INTERRUPTS();/* 運行到這兒說明,解除阻塞或者隊列中就有數據 *//* 禁止中斷 */portDISABLE_INTERRUPTS();{/* 隊列項數大于0 */if(pxQueue->uxMessagesWaiting > (UBaseType_t)0){/* 將指針偏移到新的隊列項 */pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize;if(pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail){pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead;}else{mtCOVERAGE_TEST_MARKER();}/* 隊列項數減一 */--(pxQueue->uxMessagesWaiting);/* 將隊列項內容拷貝出來 */(void)memcpy((void *)pvBuffer, (void *)pxQueue->u.xQueue.pcReadFrom, (unsigned)pxQueue->uxItemSize);/* 返回成功 */xReturn = pdPASS;/* 判斷是否有協程在等待發送 */if(listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToSend)) == pdFALSE){/* 將協程從事件列表中移除 */if(xCoRoutineRemoveFromEventList(&(pxQueue->xTasksWaitingToSend)) != pdFALSE){/* 請求切換協程 */xReturn = errQUEUE_YIELD;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}/* 隊列項為空 */else{/* 返回錯誤 */xReturn = pdFAIL;}}/* 恢復中斷 */portENABLE_INTERRUPTS();return xReturn; }?
FreeRTOS還提供了帶中斷的隊列消息接收/發送函數,原理大同小異,不再深入分析。
/* 帶中斷的發送隊列消息 */ #define crQUEUE_SEND_FROM_ISR(pxQueue, pvItemToQueue, xCoRoutinePreviouslyWoken) xQueueCRSendFromISR((pxQueue), (pvItemToQueue), (xCoRoutinePreviouslyWoken)) /* 帶中斷的接收隊列消息 */ #define crQUEUE_RECEIVE_FROM_ISR(pxQueue, pvBuffer, pxCoRoutineWoken) xQueueCRReceiveFromISR((pxQueue), (pvBuffer), (pxCoRoutineWoken))?
總結
以上是生活随笔為你收集整理的FreeRTOS协程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Simulink之三相桥式全控整流电路
- 下一篇: 开关电源之接地要点