freeRTOS使用方法 freertos从入门到精通( 三 )


互斥信号量和二值信号量比较相似 , 不同之处在于互斥信号量具有优先级继承的特性 , 如果一个互斥信号量正在被一个低优先级的任务使用 , 而此时这个高优先级的任务也希望获取这个互斥信号量的话就会被阻塞 。
互斥信号量能够解决优先级反转的原因:
使用互斥信号量时 , 高优先级的任务会把低优先级的任务的优先级先提高到和自己相同的优先级 , 保证低优先级的任务能够继续运行至结束这样极大减少了因为高优先级获取不到信号量被阻塞过长时间的问题 。
互斥信号量的API函数:
(1)创建互斥信号量
函数原型:
SemaphoreHandle_t xSemaphoreCreateMutex(void)函数描述:函数 xSemaphoreCreateMutex 用于创建互斥信号量 。
返回值:如果创建成功会返回互斥信号量的句柄 , 失败会返回 NULL 。
(2)获取互斥信号量
函数原型:
xSemaphoreTake( SemaphoreHandle_t xSemaphore, /* 信号量句柄 */ TickType_t xTicksToWait ); /* 等待信号量可用的最大等待时间 */函数描述:函数 xSemaphoreTake 用于在任务代码中获取信号量 。
xSemaphore:是信号量句柄 。xTicksToWait:是没有信号量可用时 , 等待信号量可用的最大等待时间 , 单位系统时钟节拍 。返回值:如果创建成功会获取信号量返回 pdTRUE , 否则返回 pdFALSE 。
(3)释放互斥信号量
函数原型:
xSemaphoreGive( SemaphoreHandle_t xSemaphore ); /* 信号量句柄 */函数描述:函数 xSemaphoreGive 用于在任务代码中释放信号量 。
xSemaphore:是信号量句柄 。返回值:如果信号量释放成功返回 pdTRUE , 否则返回 pdFALSE , 因为信号量的实现是基于消息队列 , 返回失败的主要原因是消息队列已经满了 。
4、二值信号量和计数信号量的使用示例二值信号量和计数信号量的使用思路如下:
1)通过任务start_task创建两个任务led0_task、led2_task , 一个二值信号量binary_sem , 一个计数信号量count_sem 。
2)信号量创建成功之后 , 在led0_task任务中等到信号量 , 如果能获取到就翻转LED2的状态 。
3)在led2_task任务中定时翻转LED3的状态 , 表明系统正在运行中 。
4)通过外部中断获取按键的按下 , 更新信号量的值 。
注意:本例程使用的是GD32E103进行演示 , 通过按键更新信号量 , 从而改变LED灯的状态 。
(1)创建任务和二值信号量、计数信号量
/*MCU:GD32E103Vx* RTOS:freeRTOS*/void start_task(void *pvParameters){pvParameters =pvParameters;taskENTER_CRITICAL();//进入临界区binary_sem = xSemaphoreCreateBinary();//创建二值信号量count_sem= xSemaphoreCreateCounting(10,10);//创建计数信号量xTaskCreate((TaskFunction_t)led0_task,(const char*)\"led0_task\",(uint16_t)TASK_STK_LED0_SIZE,(void*)NULL,(UBaseType_t)TASK_LED0_PRIO,NULL );xTaskCreate((TaskFunction_t)led2_task,(const char*)\"led2_task\",(uint16_t)TASK_STK_LED2_SIZE,(void*)NULL,(UBaseType_t)TASK_LED2_PRIO,NULL );vTaskDelete(StartTask_Handler);//删除开始任务taskEXIT_CRITICAL();//退出临界区}
(2)任务函数
void led0_task(void *pvParameters){//pvParameters =pvParameters;BaseType_t err = pdFALSE;for(;;){/* 二值信号量if(binary_sem != NULL){err =xSemaphoreTake(binary_sem,portMAX_DELAY);if(err == pdTRUE){gd_eval_led_toggle(LED2);}}*/if(count_sem != NULL){err = xSemaphoreTake(count_sem,portMAX_DELAY);if(err == pdTRUE){gd_eval_led_toggle(LED2);}}vTaskDelay(100);}}void led2_task(void *pvParameters){pvParameters = pvParameters;for(;;){gd_eval_led_toggle(LED3);vTaskDelay(500);}}
(3)中断处理函数
void EXTI10_15_IRQHandler(void){BaseType_t xHigherPriorityTaskWoken;if(exti_interrupt_flag_get(EXTI_13) != RESET){exti_interrupt_flag_clear(EXTI_13); //清除中断标志位/*if(binary_sem != NULL){//释放二值信号量xSemaphoreGiveFromISR(binary_sem,&xHigherPriorityTaskWoken);//必要时进行任务切换portYIELD_FROM_ISR(xHigherPriorityTaskWoken);}*/if(count_sem != NULL){xSemaphoreGiveFromISR(count_sem,&xHigherPriorityTaskWoken);portYIELD_FROM_ISR(xHigherPriorityTaskWoken);}}}

推荐阅读