FreeRTOS二值信号量
二值信号量
1. 信号量简介
信号量一般用来进行资源管理和任务同步,FreeRTOS中信号量又分为二值信号量、计数型信号量、互斥信号量和递归互斥信号量。
1
2
3
4
5
6
7
|----- Binary
|
Samaphore -------- Count
|
| |-- RecursiveMutex
|----- Mutex ---
|-- Mutex
2. 二值信号量简介
二值信号量即任务与中断间或者两个任务间的标志,该标志非 “满” 即“空”。也可以理解为只有一个队列项的队列,该队列要么是满要么是空,Send
操作相当把该标志置“满”,Receive
操作相关与把该标志取 “空”,经过 send
和 receive
操作实现任务与中断间或者两任务的操作同步。二值信号量的使用方法如下图所示
3. 二值信号量的函数应用
3.1 创建二值信号量
1
2
3
4
5
6
7
/********************动态创建二值信号量**********************************************/
SemaphoreHandle_t xSemaphoreCreateBinary(void);
/********************静态创建二值信号量**********************************************/
SemaphoreHandle_t xSemaphoreCreateBinaryStatic(StaticSemaphore_t *pxSemaphoreBuffer);
参数:pxSemaphoreBuffer指向一个StaticSemaphore_t类型的变量,用来保存信号量结构体
/***********************************************************************************/
返回值:创建成功返回二值信号量句柄;失败返回NULL
二值信号量创建函数是一个宏,最终是通过 xQueueGenericCreate()
函数来完成,其源码如下:
1
2
3
4
5
6
7
/*其实就是创建了一个长度为1、队列项长度为0、类型为二值信号量的队列*/
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateBinary() \
xQueueGenericCreate((UBaseType_t) 1, \
semSEMAPHORE_QUEUE_ITEM_LENGTH, \
queueQUEUE_TYPE_BINARY_SEMAPHORE) \
#endif
3.2 释放信号量
1
2
3
4
5
6
7
/********************任务级信号量释放**********************************************/
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore)
/********************中断级信号量释放**********************************************/
BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,//要释放的信号量句柄
BaseType_t *pxHigherPriorityTaskWoken)//标记退出后是否切换任务
/***********************************************************************************/
返回值:释放成功返回pdPASS;释放失败返回errQUEUE_FULL
二值信号量释放函数 xSemaphoreGive()
是一个宏,其实就是向队列发送消息,其源码如下:
1
2
3
4
5
6
/*其实就是没有具体消息、阻塞时间为0、后向入队的入队过程*/
#define xSemaphoreGive(xSemaphore) \
xQueueGenericSend((QueueHandle_t) (xSemaphore), \
NULL, \
semGIVE_BLOCK_TIME, \
queueSEND_TO_BACK) \
3.3 获取信号量
1
2
3
4
5
6
7
8
/********************任务级信号量获取**********************************************/
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore//要获取的信号量句柄
TickType_t xBlockTime)//阻塞时间
/********************中断级信号量获取**********************************************/
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,//要获取的信号量句柄
BaseType_t *pxHigherPriorityTaskWoken)//标记退出后是否切换任务
/***********************************************************************************/
返回值:获取成功返回pdPASS;释放失败返回pdFALSE
获取信号量函数也是一个宏,其实就是读取队列的过程,其源码如下
1
2
3
4
5
#define xSemaphoreTake(xSemaphore, xBlockTime) \
xQueueGenericReceive((QueueHandle_t) (xSemaphore), \
NULL, \
(xBlockTime), \
pdFALSE) \
4. 二值信号量的应用实例
本实例介绍如何使用二值信号量来完成任务与中断之间的同步。
使用 STM32CubeMX 将 FreeRTOS 移植到工程中,创建两个任务、一个二值信号量,开启串口中断。
LED_Task:闪烁 LED1,提示系统运行正常 CMDprocess_Task:根据串口收到的指令,控制不同的 LED2/LED3 的亮灭 二值信号量:用于串口中断和 CMDprocess_Task 任务间的同步
4.1 STM32CubeMX 设置
- RCC 设置外接 HSE,时钟设置为 72M
- PC0/PC1/PC2 设置为 GPIO 推挽输出模式、上拉、高速、默认输出电平为高电平
- USART1 选择为异步通讯方式,波特率设置为 115200Bits/s,传输数据长度为 8Bit,无奇偶校验,1 位停止位;开启串口中断
- 激活 FreeRTOS,添加任务,设置任务名称、优先级、堆栈大小、函数名称等参数
- 动态创建二值信号量
- 使用 FreeRTOS 操作系统,一定要将 HAL 库的 Timebase Source 从 SysTick 改为其他定时器,选好定时器后,系统会自动配置 TIM
- 输入工程名,选择路径(不要有中文),选择 MDK-ARM V5;勾选 Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击 GENERATE CODE,生成工程代码
4.2 MDK-ARM 软件编程
- 添加 LEDTask、CMDprocessTask 任务函数代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/******************LEDTask**************************/
void LEDTask(void const * argument){
for(;;){
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
osDelay(500);
}
}
/******************CMDprocessTask*******************/
void CMDprocessTask(void const * argument){
BaseType_t err = pdFALSE;
for(;;){
if(BinarySemHandle != 0){
err = xSemaphoreTake(BinarySemHandle,portMAX_DELAY);
if(err == pdPASS){
printf("CMDprocessTask take the binary Semaphore!\r\n");
printf("Received CMD is:");
for(int i =0;i<8;i++)
printf("%c",RxBuff[i]);
printf("\n");
if(strncmp((char *)RxBuff,"LED2on",6) == 0)
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_RESET);
else if(strncmp((char *)RxBuff,"LED2off",6) == 0)
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);
else if(strncmp((char *)RxBuff,"LED3on",6) == 0)
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_RESET);
else if(strncmp((char *)RxBuff,"LED3off",6) == 0)
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET);
else
printf("invalid CMD,please input LED2on LED2off LED3on or LED3off\r\n");
}
else
osDelay(10);
}
}
}
- 添加串口中断回调函数:串口接收完命令后释放二值信号量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
RxBuff[Rx_Count++]=RxByte;
if((RxByte==0x0A)&&(BinarySemHandle!=0)){
xSemaphoreGiveFromISR(BinarySemHandle,NULL);
printf("Semaphore Give FromISR succesed!\r\n");
Rx_Count=0;
}
if(Rx_Count > 8){
printf("Wrong CMD, Please Check...!\r\n");
memset(RxBuff,0,sizeof(RxBuff));
Rx_Count=0;
}
while(HAL_UART_Receive_IT(&huart1,&RxByte,1)==HAL_OK);
}
- 在 main.c 中开启串口接收中断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main(void){
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("BinarySemaphore test....\r\n");
if(HAL_OK == HAL_UART_Receive_IT(&huart1,&RxByte,1))
printf("UART_Receive_IT successed!\r\n");
else
printf("UART_Receive_IT failed!\r\n");
/* USER CODE END 2 */
MX_FREERTOS_Init();
osKernelStart();
while (1)
{
}
}
4.3 下载验证
编译无误下载到开发板后,打开串口调试助手,LED1 闪烁表示程序正常运行;串口中输入字符串 “LED2on、LED2off、LED3on、LED3off” 可以控制 LED2/LED3 的亮灭并打印出相应的二值信号量的释放和获取的信息
以上转载自博主“安迪西嵌入式”的FreeRTOS专栏,仅作学习记录,如有侵权,请联系删除。
本文由作者按照 CC BY 4.0 进行授权