一.信号量解释
信号量是操作系统中重要的一部分,信号量一般用来进行资源管理和任务同步。
计数信号量和二值信号量用于访问公共资源,信号量也可以进行任务与任务,中断与中断的同步。
在编写中断服务函数的时候我们都知道一定要快进快出,中断服务函数里面不能放太多的代码,否则的话会影响的中断的实时性。。裸机编写中断服务函数的时候一般都只是在中断服务函数中打个标记,然后在其他的地方根据标记来做具体的处理过程。
在使用 RTOS 系统的时候我们就可以借助信号量完成此功能,当中断发生的时候就释放信号量,中断服务函数不做具体的处理。具体的处理过程做成一个任务,这个任务会获取信号量,如果获取到信号量就说明中断发生了,那么就开始完成相应的处理,这样做的好处就是中断执行时间非常短。
二.实验验证
配置二值信号量,实现任务与任务的同步
void StartTask03(void *argument)
{
/* USER CODE BEGIN StartTask03 */
BaseType_t xHigherPriorityTaskWoken;
/* Infinite loop */
for(;;)
{
if(key_num==2)
{
xSemaphoreGive(myBinarySem01Handle);
OLED_ShowNum(0,0,key_num,2,16);
}
osDelay(1);
}
/* USER CODE END StartTask03 */
}
/* USER CODE BEGIN Header_StartTask04 */
/**
* @brief Function implementing the myTask04 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask04 */
void StartTask04(void *argument)
{
/* USER CODE BEGIN StartTask04 */
BaseType_t err=pdFALSE;
uint8_t * str;
/* Infinite loop */
for(;;)
{
if(myBinarySem01Handle!=NULL)
{
err=xSemaphoreTake(myBinarySem01Handle,portMAX_DELAY);//获取信号量
if(err==pdTRUE) //获取信号量成功
{
// sprintf((char*)str,"%s",USART_RX_BUF);
// printf("USART_RX_BUF %s",str);
printf("信号量");
}
}
osDelay(1);
}
/* USER CODE END StartTask04 */
}
按键按下,串口输出信息。
定时器中断与任务的同步
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
extern osSemaphoreId_t myBinarySem01Handle; //二值信号量句柄
BaseType_t xHigherPriorityTaskWoken;
/* USER CODE END Callback 0 */
if (htim->Instance == TIM2)
{
if(myBinarySem01Handle!=NULL))信号量初始化
{
xSemaphoreGiveFromISR(myBinarySem01Handle,&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换
printf("释放二值信号量");
}
}
void StartTask04(void *argument)
{
/* USER CODE BEGIN StartTask04 */
BaseType_t err=pdFALSE;
uint8_t * str;
printf("StartTask04");
/* Infinite loop */
for(;;)
{
if(myBinarySem01Handle!=NULL)
{
err=xSemaphoreTake(myBinarySem01Handle,portMAX_DELAY);//获取信号量
if(err==pdTRUE) //获取信号量成功
{
// sprintf((char*)str,"%s",USART_RX_BUF);
// printf("USART_RX_BUF %s",str);
printf("中断任务同步");
}
}
osDelay(1);
}
/* USER CODE END StartTask04 */
}
此实验只要中断发生,就会执行task里的任务。
串口中断与任务的同步
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
uint8_t str[20];
extern osSemaphoreId_t myBinarySem01Handle; //二值信号量句柄
BaseType_t xHigherPriorityTaskWoken;
if(huart->Instance == USART1) // 判断是由哪个串口触发的中断
{
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE) != RESET)
{
__HAL_UART_CLEAR_IDLEFLAG(huart);
if(rx_lengh<USART_REC_LEN)
{
USART_RX_BUF[rx_lengh++]=Rxbuffer;
}
else
{
rx_flag=1;
}
}
//printf("Rxbuffer %d",Rxbuffer-48);
HAL_UART_Receive_IT(&huart1,&Rxbuffer, 1);
if(myBinarySem01Handle!=NULL)
{
xSemaphoreGiveFromISR(myBinarySem01Handle,&xHigherPriorityTaskWoken);
}
}
}
void StartTask04(void *argument)
{
/* USER CODE BEGIN StartTask04 */
BaseType_t err=pdFALSE;
uint8_t * str;
printf("StartTask04");
/* Infinite loop */
for(;;)
{
//****************定时器2与任务04同步(2)/任务03与任务04同步(2)
if(myBinarySem01Handle!=NULL)
{
err=xSemaphoreTake(myBinarySem01Handle,portMAX_DELAY);//获取信号量
if(err==pdTRUE) //获取信号量成功
{
printf("中断任务同步");
}
}
osDelay(1);
}
/* USER CODE END StartTask04 */
}
接收中断触发后,与任务同步
二.事件
使用信号量来同步的话任务只能与单个的事件或任务进行同步。有时候某个任务可能会需要与多个事件或任务进行同步,此时信号量就无能为力了。FreeRTOS 为此提供了一个可选的解决方法,那就是事件标志组。
即事件可以同步多个任务
使用事件必须配置V2接口
BaseType_t xHigherPriorityTaskWoken;
BaseType_t RE;
if(huart->Instance == USART1)
{
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE) != RESET)
{
__HAL_UART_CLEAR_IDLEFLAG(huart);
if(rx_lengh<USART_REC_LEN)
{
USART_RX_BUF[rx_lengh++]=Rxbuffer;
}
else
{
rx_flag=1;
}
}
printf("Rxbuffer %d",Rxbuffer);
if(Rxbuffer=='0')
{
RE=xEventGroupSetBitsFromISR(myEvent01Handle,EVENTBIT_3,&xHigherPriorityTaskWoken);
if(RE!=pdFAIL)
{
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
HAL_UART_Receive_IT(&huart1,&Rxbuffer, 1);
}
BaseType_t xHigherPriorityTaskWoken;
/* Infinite loop */
for(;;)
{
EventBits_t EventValue;
if(myEvent01Handle!=NULL)
{
EventValue=xEventGroupWaitBits((EventGroupHandle_t )myEvent01Handle,
(EventBits_t ) EVENTBIT_ALL,
(BaseType_t )pdTRUE,
(BaseType_t )pdTRUE,
(TickType_t )portMAX_DELAY);
printf("事件标志组的值:%d\r\n",EventValue);
printf("三个事件同步");
OLED_Full();
}
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
for(;;)
{
if(myEvent01Handle!=NULL)
{
key_num=read_key();
switch(key_num)
{
case 2:printf("2");osEventFlagsSet(myEvent01Handle,EVENTBIT_0);break;
case 3:printf("3");osEventFlagsSet(myEvent01Handle,EVENTBIT_1);break;
}
}
osDelay(1);
}
/* USER CODE END StartTask02 */
}