信號量可以用來進行資源管理和任務同步,FreeRTOS中信號量又分為二值信號量、計算型信號量、互斥信號量和遞歸互斥信號量。
0x01 二值信號量
二值信號量其實就是一個只有一個隊列項的隊列,這個特殊的隊列要么是滿的,要么是空的,任務和中斷不用在乎隊列中存的是什么消息,只需要知道這個隊列是滿的還是空的,可以利用這個機制完成任務與中斷之間的同步。
1. 創建二值信號量
vSemaphoreCreateBinary
vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore )
- xSemaphore :保存創建成功的二值信號量
此函數是老版本FreeRTOS創建二值信號量函數,新版本已經不再使用。
xSemaphoreCreateBinary:
SemaphoreHandle_t xSemaphoreCreateBinary( void )
此函數是新版本FreeRTOS中統一使用此函數來創建二值信號量,使用此函數創建二值信號量的話,信號量所需要的內存是有FreeRTOS的內存管理部分動態分配的,此函數創建好的二值信號量默認是空的,也就是說創建好的二值信號量使用函數xSemaphoreTask是獲取不到的。
xSemaphoreCreateBinaryStatic
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )
- pxSemaphoreBuffer :保存信號量結構體
此函數創建二值信號量所需要的內存需要由用戶分配
2. 釋放信號量
xSemaphoreGive
xSemaphoreGive( SemaphoreHandle_t xSemaphore )
- xSemaphore:要釋放的信號量句柄
此函數用于釋放二值信號量、計數型信號量或互斥信號量
xSemaphoreGiveFromISR
xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken)
- xSemaphore:要釋放的信號量句柄
- BaseType_t *pxHigherPriorityTaskWoken:標記退出此函數以后是否進行任務切換,此值為pdTRUE的時候在退出中斷之前一定要進行一次任務切換
此函數只能用來釋放二值信號量和計數型信號量
3. 獲取信號量
xSemaphoreTake
xSemaphoreTake(* SemaphoreHandle_t xSemaphore,* TickType_t xBlockTime* )
- SemaphoreHandle_t xSemaphore:要獲取信號量的句柄
- TickType_t xBlockTime:阻塞時間
此函數用于獲取二值信號量、計數型信號量或互斥信號量
xSemaphoreTakeFromISR
xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken)
- SemaphoreHandle_t xSemaphore:要獲取信號量的句柄
- BaseType_t *pxHigherPriorityTaskWoken:標記退出此函數以后是否進行任務切換,等于pdTURE一定進行任務切換
4. 驗證
設計一個通過串口發送指定的指令來控制開發版上的LED1和BEEP開關的實驗。指令如下:
LED1ON:打開LED1
LED1OFF:關閉LED1
BEEFON:打開BEEF
BEEFOFF:關閉BEEF
設計三個任務,功能如下:
start_task:用來創建其他2個任務
task1_task:控制LED0閃爍,提示系統正在運行
DataProcess_task:指令處理任務,根據接收到的指令來控制不同的外設
實驗中還創建一個二值信號量BinarySemaphore用于完成串口中斷和任務DataProcess_task之間的同步。
DataProcess_task函數
void DataProcess_task(void *pvParameters)
{u8 len = 0;u8 CommandValue = COMMANDERR;BaseType_t err = pdFALSE;u8 *CommandStr;POINT_COLOR = BLUE;while(1){if(BinarySemaphore!=NULL){err=xSemaphoreTake(BinarySemaphore,portMAX_DELAY); //獲取信號量if(err==pdTRUE) //獲取信號量成功{len=USART_RX_STA&0x3fff; //得到此次接收到的數據長度CommandStr=mymalloc(SRAMIN,len+1); //申請內存sprintf((char*)CommandStr,"%s",USART_RX_BUF);CommandStr[len]='\0'; //加上字符串結尾符號LowerToCap(CommandStr,len); //將字符串轉換為大寫 CommandValue=CommandProcess(CommandStr); //命令解析if(CommandValue!=COMMANDERR){LCD_Fill(10,90,210,110,WHITE); //清除顯示區域LCD_ShowString(10,90,200,16,16,CommandStr); //在LCD上顯示命令printf("命令為:%s\r\n",CommandStr);switch(CommandValue) //處理命令{case LED1ON: LED1=0;break;case LED1OFF:LED1=1;break;case BEEPON:BEEP=0;break;case BEEPOFF:BEEP=1;break;}}else{printf("無效的命令,請重新輸入!!\r\n");}USART_RX_STA=0;memset(USART_RX_BUF,0,USART_REC_LEN); //串口接收緩沖區清零myfree(SRAMIN,CommandStr); //釋放內存}}else if(err==pdFALSE){vTaskDelay(10); //延時10ms,也就是10個時鐘節拍 }}
}
DataProcess_task是用來申請獲取信號量,如果申請到了,就往下執行,如果沒事申請到,就會一直阻塞,申請到之后,對數據做處理,然后做出相應的回應。
串口1中斷服務程序
//串口1中斷服務程序
void USART1_IRQHandler(void)
{ u32 timeout=0;u32 maxDelay=0x1FFFF;BaseType_t xHigherPriorityTaskWoken;HAL_UART_IRQHandler(&UART1_Handler); //調用HAL庫中斷處理公用函數timeout=0;while (HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY)//等待就緒{timeout++;超時處理if(timeout>maxDelay) break; } timeout=0;while(HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次處理完成之后,重新開啟中斷并設置RxXferCount為1{timeout++; //超時處理if(timeout>maxDelay) break; }//釋放二值信號量if((USART_RX_STA&0x8000)&&(BinarySemaphore!=NULL))//接收到數據,并且二值信號量有效{xSemaphoreGiveFromISR(BinarySemaphore,&xHigherPriorityTaskWoken); //釋放二值信號量portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的話進行一次任務切換}
}
串口1中斷服務程序是用來接收串口發送的數據,如果接收完畢,將會釋放二值信號量,DataProcess_task才能往下執行,如果沒有接收完數據或者沒有接收到數據,DataProcess_task會一直阻塞。