01.初识freeRTOS
02.FreeRTOS的移植
03.FreeRTOS系统配置
04.FreeRTOS任务创建与删除
05.FreeRTOS任务挂起与恢复
06.FreeRTOS中断管理
07.FreeRTOS列表与列表项
08.FreeRTOS任务调度与任务切换
09.FreeRTOS时间片调度与任务相关函数
10.FreeRTOS队列操作
11.FreeRTOS信号量操作
12.FreeRTOS队列集和事件标志组
13.FreeRTOS任务通知
14.FreeRTOS软件定时器
15.FreeRTOS低功耗
16.FreeRTOS内存管理
11. FreeRTOS信号量操作
1. 信号量的简介
信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问。其中,“同步”指的是任务间的同步,即信号量可以使得一个任务等待另一个任务完成某件事情后,才继续执行:而“有序访问”指的是对被多任务或中断访问的共享资源(如全局变量)的管理,当一个任务在访问(读取或写入)一个共享资源时,信号量可以防止其他任务或中断在这期间访问(读取或写入)这个共享资源。
队列和信号量的对比:
队列 | 信号量 |
---|---|
可以容纳多个数据; 创建队列有两部分内存:队列结构体+队列项存储空间 | 仅存放计数值,无法存放其他数据; 创建信号量,只需分配信号量结构体 |
写入队列:当队列满时,可阻塞; | 释放信号量:不可阻塞,计数值++,当计数值为最大值时,返回失败; |
读取队列:当队列为空时,可阻塞; | 获取信号量:计数值–,当没有资源时,可阻塞; |
2. 二值信号量
二值信号量的简介:
二值信号量的相关API函数:
函数 | 描述 |
---|---|
xSemaphoreCreateBinary() | 使用动态方式创建二值信号量 |
xSemaphoreCreateBinaryStatic() | 使用静态方式创建二值信号量 |
xSemaphoreGive() | 释放信号量 |
xSemaphoreGiveFromISR() | 在中断中释放信号量 |
xSemaphoreTake() | 获取信号量 |
xSemaphoreTakeFromISR() | 在中断中获取信号量 |
创建二值信号量函数:
释放二值信号量函数:
获取二值信号量函数:
二值信号量相关实验:
-
二值信号量的创建:
//定义句柄 QueueHandle_t semphore_handle; semphore_handle = xSemaphoreCreateBinary(); if(semphore_handle != NULL) { printf("二值信号量创建成功!\r\n"); }
-
任务一:释放二值信号量
/*任务一:释放二值信号量*/ void task1(void* pvParamter) { uint8_t key = 0; while(1) { key = key_scan(0); if(key == KEY0_PRES) { if(semphore_handle != NULL) { if(pdPASS == xSemaphoreGive(semphore_handle)) { printf("信号量释放成功!\r\n"); } else { printf("释放信号量失败!\r\n"); } } } vTaskDelay(20); } }
-
任务二:获取二值信号量
/*任务二:获取二值信号量*/ void task2(void* pvParamter) { uint32_t i = 0; while(1) { if(pdTRUE == xSemaphoreTake(semphore_handle, 1000)) //获取信号量死等 { printf("获取信号量成功!\r\n"); } else { printf("已超时 %d\r\n", ++i); } } }
-
实验结果
3. 计数型信号量
计数型信号量的简介:
计数型信号量的相关API函数:
函数 | 描述 |
---|---|
xSemaphoreCreateCounting() | 使用动态方法创建计数型信号量。 |
xSemaphoreCreateCountingStatic() | 使用静态方法创建计数型信号量 |
uxSemaphoreGetCount() | 获取信号量的计数值 |
创建计数型信号量:
获取当前计数值大小:
计数型信号量相关实验:
-
计数型信号量的创建:
//句柄定义 QueueHandle_t count_semphore_handle; count_semphore_handle = xSemaphoreCreateCounting(100, 0); if(count_semphore_handle != NULL) { printf("计数型信号量创建成功!\r\n"); }
-
任务一:释放计数型信号量
/*任务一:释放计数型信号量*/ void task1(void* pvParamter) { uint8_t key = 0, i = 0; while(1) { key = key_scan(0); if(key == KEY0_PRES) { if(count_semphore_handle != NULL) { if(pdPASS == xSemaphoreGive(count_semphore_handle)) { printf("信号量释放成功! %d\r\n",++i); } else { printf("释放信号量失败!\r\n"); } } } vTaskDelay(20); } }
-
任务二:获取计数型信号量
/*任务二:获取计数型信号量*/ void task2(void* pvParamter) { while(1) { if(pdTRUE == xSemaphoreTake(count_semphore_handle, portMAX_DELAY)) //获取信号量死等 { printf("信号量计数值为:%d\r\n", (int)uxSemaphoreGetCount(count_semphore_handle)); } vTaskDelay(1000); } }
-
实验结果
4. 优先级翻转
优先级翻转的简介:
优先级翻转示例:
优先级翻转相关实验:
二值信号量的创建:
QueueHandle_t semphore_handle;
semphore_handle = xSemaphoreCreateBinary();
if(semphore_handle != NULL)
{
printf("二值信号量创建成功!\r\n");
}
低优先级任务:
void low_task(void* pvParamter)
{
while(1)
{
printf("low_task获取信号量\r\n");
xSemaphoreTake(semphore_handle, portMAX_DELAY);
printf("low_task正在运行\r\n");
delay_ms(3000);
xSemaphoreGive(semphore_handle);
printf("low_task释放信号量\r\n");
vTaskDelay(1000);
}
}
中优先级任务:
void middle_task(void* pvParamter)
{
while(1)
{
printf("中优先级任务正在运行!\r\n");
vTaskDelay(1000);
}
}
高优先级任务:
void high_task(void* pvParamter)
{
while(1)
{
printf("high_task获取信号量\r\n");
xSemaphoreTake(semphore_handle, portMAX_DELAY);
printf("high_task正在运行\r\n");
delay_ms(1000);
xSemaphoreGive(semphore_handle);
printf("high_task释放信号量\r\n");
vTaskDelay(1000);
}
}
实验结果:
5. 互斥信号量
互斥信号量的简介:
互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中。在互斥访问中互斥信号量相当于一把钥匙,当任务想要访问共享资源的时候就必须先获得这把钥匙,当访问完共享资源以后就必须归还这把钥匙,这样其他的任务就可以拿着这把钥匙去访问资源。
互斥信号量的示例:
互斥信号量注意事项:
互斥信号量相关API函数:
互斥信号量相关实验:
在优先级翻转实验的基础,加入互斥信号量,解决优先级翻转问题,只需要修改变量的创建函数,把xSemaphoreCreateBinary()
改成xSemaphoreCreateMutex()
。
QueueHandle_t mutex_semphore_handle;
mutex_semphore_handle = xSemaphoreCreateMutex();
if(mutex_semphore_handle != NULL)
{
printf("互斥信号量创建成功!\r\n");
}
实验结果: