寫在前面:
本文章旨在總結備份、方便以後查詢,由於是個人總結,如有不對,歡迎指正;另外,內容大部分來自網絡、書籍、和各類手冊,如若侵權請告知,馬上刪帖致歉。
目錄
一、臨界區簡述
基本臨界區是指宏 taskENTER_CRITICAL()與 taskEXIT_CRITICAL()之間的代碼區間,在中斷中則是 taskENTER_CRITICAL_FROM_ISR()和 taskEXIT_CRITICAL_FROM_ISR()之間
例如,在 FreeRTOS篇章之二值信號量中的 USART1_IRQHandler(void)中斷函數中,我們可以加入臨界區操作,變成:
/************************************************************************/
/* STM32F10x USART Interrupt Handlers */
/************************************************************************/
/**
* @brief This function handles USART1 global interrupt request.
* @param None
* @retval None
*/
void USART1_IRQHandler(void)
{
uint32_t ulReturn;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
/* 爲了保證在對 DMA數據進行處理不被打斷,將此操作放入臨界區 */
ulReturn = taskENTER_CRITICAL_FROM_ISR(); // 進入臨界段,可以嵌套
/* 在 taskENTER_CRITICAL_FROM_ISR()與 taskEXIT_CRITICAL_FROM_ISR()之間不會切換到其它任務,
也不會被其他中斷打斷;
而在 taskENTER_CRITICAL()與 taskEXIT_CRITICAL()之間同樣也不會切換到其它任務,並且中斷可以執行,
也允許嵌套,但只是針對優先級高於 configMAX_SYSCALL_INTERRUPT_PRIORITY的中斷 – 而且這些中斷
不允許訪問 FreeRTOS API函數. */
if(USART_GetITStatus(EVAL_COM1, USART_IT_IDLE)!=RESET)
{
DMA_Cmd(USART1_RX_DMA_CHANNEL, DISABLE);
DMA_ClearFlag(DMA1_FLAG_TC5);
Usart1.RxCounter = RxBUFFER_SIZE - DMA_GetCurrDataCounter(USART1_RX_DMA_CHANNEL);
USART1_RX_DMA_CHANNEL->CNDTR = RxBUFFER_SIZE;
DMA_Cmd(USART1_RX_DMA_CHANNEL, ENABLE);
xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken);
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
USART_ReceiveData(EVAL_COM1); // Clear IDLE interrupt flag bit
}
/* 退出臨界段 */
taskEXIT_CRITICAL_FROM_ISR( ulReturn );
// if(USART_GetITStatus(EVAL_COM1, USART_IT_TXE) != RESET)
// {
// /* Write one byte to the transmit data register */
// USART_SendData(EVAL_COM1, TxBuffer[TxCounter++]);
// if(TxCounter == RxBUFFER_SIZE)
// {
// /* Disable the EVAL_COM1 Transmit interrupt */
// USART_ITConfig(EVAL_COM1, USART_IT_TXE, DISABLE);
// }
// }
}
二、臨界區特性
臨界區是提供互斥功能的一種非常原始的實現方法。臨界區的工作僅僅是簡單地把中斷全部關掉,或是關掉優先級在 configMAX_SYSCAL_INTERRUPT_PRIORITY 及以下的中斷 —— 依賴於具體使用的 FreeRTOS移植。搶佔式上下文切換隻可能在某個中斷中完成,所以調用 taskENTER_CRITICAL()的任務可以在中斷關閉的時段一直保持運行態,直到退出臨界區。
臨界區必須只具有很短的時間,否則會反過來影響中斷響應時間;在每次調用 taskENTER_CRITICAL()之後,必須儘快地配套調用一個 taskEXIT_CRITICAL()。
臨界區嵌套是安全的,因爲內核有維護一個嵌套深度計數。臨界區只會在嵌套深度爲 0時纔會真正退出 —— 即在爲每個之前調用的 taskENTER_CRITICAL()都配套調用了 taskEXIT_CRITICAL()之後
三、調度器
創建臨界區也可以通過掛起調度器來實現;掛起調度器有些時候也被稱爲鎖定調度器。
基本臨界區保護一段代碼區間不被其它任務或中斷打斷。由掛起調度器實現的臨界區只可以保護一段代碼區間不被其它任務打斷,因爲這種方式下,中斷是使能的。
如果一個臨界區太長而不適合簡單地關中斷來實現,可以考慮採用掛起調度器的方式;但是喚醒 (resuming, or un-suspending) 調度器卻是一個相對較長的操作,所以評估哪種是最佳方式需要結合實際情況。
利用 vTaskSuspendAll()進行調度器的掛起,掛起調度器可以停止上下文切換而不用關中斷;如果某個中斷在調度器掛起過程中要求進行上下文切換,則個這請求也會被掛起,直到調度器被喚醒後纔會得到執行(即調用 xTaskResumeAll();恢復之前掛起的調度器)。
注意:在調度器處於掛起狀態時,不能調用 FreeRTOS API函數
四、xTaskResumeAll() API 函數
BaseType_t xTaskResumeAll( void );
返回參數:
- 在調度器掛起過程中,上下文切換請求也會被掛起,直到調度器被喚醒後纔會得到執行。如果一個掛起的上下文切換請求在 xTaskResumeAll()返回前得到執行,則函數返回 pdTRUE。在其它情況下,xTaskResumeAll()返回 pdFALSE
嵌套調用 vTaskSuspendAll()和 xTaskResumeAll()是安全的,因爲內核有維護一個嵌套深度計數。調度器只會在嵌套深度計數爲 0時纔會被喚醒 —— 即在爲每個之前調用的 vTaskSuspendAll()都配套調用了 xTaskResumAll()之後