❤️作者主页:凉开水白菜
❤️作者简介:共同学习,互相监督,热于分享,多加讨论,一起进步!
❤️专栏资料:https://pan.baidu.com/s/1nc1rfyLiMyw6ZhxiZ1Cumg?pwd=free
❤️点赞 👍 收藏 ⭐再看,养成习惯
订阅的粉丝可通过PC端左侧加我微信,可对文章的内容进行一对一答疑!
前言
什么是信号量?什么是计数型信号量?什么是二进制信号量?以及互斥信号量和递归信号量
信号也就是发出通知的信号,量表示信号发出的数量;让信号的数量没有限制的时候就是计数型信号量;当数量只有两种状态一种是有(1)一种是无(0)也就是二进制信号量;
创建、删除信号量
创建二进制信号量
SemaphoreHandle_t xSemaphoreCreateBinary( void );
返回值: 返回句柄,非NULL表示成功
创建计数型信号量
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
UBaseType_t uxMaxCount: 最大计数值
UBaseType_t uxInitialCount: 初始计数值
返回值: 返回句柄,非NULL表示成功
删除信号量
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
SemaphoreHandle_t xSemaphore:需要删除的句柄
发送、获取信号量
发送(give)信号量
该章节的发送和获取信号量的函数在中断中不可使用,在中断中应该采用**xSemaphoreGiveFromISR()和xSemaphoreTakeFromISR()**他们可以在中断中实现和这两个函数相同的功能(中断章节再说明),二进制信号量与计数型信号量的give、take操作api调用方式相同;
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
SemaphoreHandle_t xSemaphore:信号量句柄,释放哪个信号量
返回值 :pdTRUE表示成功,如果二进制信号量的计数值已经是1,再次调用此函数则返回失败;
如果计数型信号量的计数值已经是最大值,再次调用此函数则返回失败;
获取(take)信号量
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait);
SemaphoreHandle_t xSemaphore:信号量句柄,释放哪个信号量
TickType_t xTicksToWait:如果无法马上获得信号量,阻塞一会:
- 0:不阻塞,马上返回
- portMAX_DELAY: 一直阻塞直到成功,
- 其他值: 阻塞的Tick个数,可以使用pdMS_TO_TICKS()来指定阻塞时间为若干ms;
返回值 :pdTRUE表示成功
示例
信号量需要使用到的头文件
#include "semphr.h"
二进制信号量的使用
线程创建
binary_sem = xSemaphoreCreateBinary(); /* 二进制信号量创建 */
xTaskCreate((TaskFunction_t )SenderTask, /* 任务入口函数 */
(const char* )"send", /* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )2, /* 任务的优先级 */
NULL); /* 任务控制块指针 */
xTaskCreate((TaskFunction_t )RevcerTask, /* 任务入口函数 */
(const char* )"revc", /* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )1, /* 任务的优先级 */
NULL);
发送函数和接收函数
SemaphoreHandle_t binary_sem;
SemaphoreHandle_t count_sem;
uint8_t sem_send_buf[128];
static void SenderTask(void *par)
{
const TickType_t xTicksToWait = pdMS_TO_TICKS(10UL);
int send_count_ok = 0;
int send_count_err = 0;
uint8_t send_count = 0;
while(1)
{
for(int i = 0;i < 3; i++)
{
/* 发送数据 */
sem_send_buf[i] = 'a' + send_count++;
if(xSemaphoreGive(binary_sem) == pdTRUE)
{
printf("xSemaphoreGive(binary_sem) send_count_ok = %d\r\n", send_count_ok++);
}else{
printf("xSemaphoreGive(binary_sem) send_count_err = %d\r\n", send_count_err++);
}
}
vTaskDelay(xTicksToWait); // 延时10ms
}
}
static void RevcerTask(void *par)
{
int revc_count_ok = 0;
int revc_count_err = 0;
while(1)
{
if(xSemaphoreTake(binary_sem, portMAX_DELAY) == pdTRUE) /* 一直等待 */
{
printf("xSemaphoreTake(binary_sem, portMAX_DELAY) revc_count_ok = %d\r\n", revc_count_ok++);
printf("sem_send_buf : %s\r\n", sem_send_buf);
}else{
printf("xSemaphoreTake(binary_sem, portMAX_DELAY) revc_count_err = %d\r\n", revc_count_err++);
}
}
}
从串口助手我们可以得知,我们任务优先级高的发送任务先发送了三次信号量并且存入了三个发送次数的字母到我们的sem_send_buf这个变量中,但是信号量发送只有第一次成功,因为我们的信号量是二进制信号量,然后当循环三次完成过后我们添加了一个延时,这时候系统任务调度就会执行接收任务的代码,我们接收端接收完成过后打印了接收成功,并且从sem_send_buf中取出了刚刚存入的信号量,然后又进入了阻塞状态,因为我们的xSemaphoreTake(binary_sem, portMAX_DELAY)函数是永久等待信号量的到来,然后等待发送任务的阻塞延时结束过后继续三次发送我们的信号量如此循环;
计数型信号量的使用
任务创建和任务函数我们依旧使用上面的进行改动,这里我们只需要新增计数型信号量的创建
count_sem = xSemaphoreCreateCounting(3, 0); /* 最大计数3个、初始值为0 */
因为处理信号量需要时间,防止任务被优先级切换回来所以这里发送任务的阻塞增加到了30ms
static void SenderTask(void *par)
{
const TickType_t xTicksToWait = pdMS_TO_TICKS(30UL);
int send_count_ok = 0;
int send_count_err = 0;
uint8_t send_count = 0;
while(1)
{
#if 0 /* 二进制信号量 */
for(int i = 0;i < 3; i++)
{
/* 发送数据 */
sem_send_buf[i] = 'a' + send_count++;
if(xSemaphoreGive(binary_sem) == pdTRUE)
{
printf("xSemaphoreGive(binary_sem) send_count_ok = %d\r\n", send_count_ok++);
}else{
printf("xSemaphoreGive(binary_sem) send_count_err = %d\r\n", send_count_err++);
}
}
vTaskDelay(xTicksToWait); // 延时10ms
#endif
#if 1 /* 计数型信号量 */
for(int i = 0;i < 3; i++)
{
if(xSemaphoreGive(count_sem) == pdTRUE)
{
printf("xSemaphoreGive(count_sem) send_count_ok = %d\r\n", send_count_ok++);
}else{
printf("xSemaphoreGive(count_sem) send_count_err = %d\r\n", send_count_err++);
}
}
vTaskDelay(xTicksToWait); /* 因为处理信号量需要时间,防止任务被优先级切换回来所以增加 */
#endif
}
}
static void RevcerTask(void *par)
{
int revc_count_ok = 0;
int revc_count_err = 0;
int take_count = 0;
while(1)
{
#if 0 /* 二进制信号量 */
if(xSemaphoreTake(binary_sem, portMAX_DELAY) == pdTRUE) /* 一直等待 */
{
printf("xSemaphoreTake(binary_sem, portMAX_DELAY) revc_count_ok = %d\r\n", revc_count_ok++);
printf("sem_send_buf : %s\r\n", sem_send_buf);
}else{
printf("xSemaphoreTake(binary_sem, portMAX_DELAY) revc_count_err = %d\r\n", revc_count_err++);
}
#endif
#if 1 /* 计数型信号量 */
if(xSemaphoreTake(count_sem, portMAX_DELAY) == pdTRUE) /* 一直等待 */
{
printf("xSemaphoreTake(count_sem, portMAX_DELAY) revc_count_ok = %d\r\n", revc_count_ok++);
}else{
printf("xSemaphoreTake(count_sem, portMAX_DELAY) revc_count_err = %d\r\n", revc_count_err++);
}
#endif
}
}
结尾
我是凉开水白菜,我们下文见~