05 FreeRTOS 信号量(semaphore)

1、信号量的特性

        队列可以用于传输数据:在任务之间、任务和中断之间。而有时候我们只需要传递状态,并不需要传递具体的信息,在这种情况下,我们可以使用信号量(semaphore),它更节省内存。

1.1 什么是信号量

        信号:起通知作用

        量:还可以用来表示资源的数量,当""没有限制时,它就是"计数型信号量"(Counting Semaphores),当""只有01两个取值时,它就是"二进制信号量"(Binary Semaphores)。

        支持的动作:"give"给出资源,计数值加1"take"获得资源,计数值减1

        计数型信号量的典型场景:

                计数:事件产生时"give"信号量,让计数值加1;处理事件时要先"take"信号量,就是获得信号量, 让计数值减1

                资源管理:要想访问资源需要先"take"信号量,让计数值减1;用完资源后"give"信号量,让计数值 加1

        信号量的"give""take"双方并不需要相同,可以用于生产者-消费者场合:

                生产者为任务AB,消费者为任务CD

                一开始信号量的计数值为0,如果任务CD想获得信号量,会有两种结果: 阻塞:买不到东西咱就等等吧,可以定个闹钟(超时时间);即刻返回失败:不等

                任务A、B可以生产资源,就是让信号量的计数值增加1,并且把等待这个资源的顾客唤醒 。唤醒谁?谁优先级高就唤醒谁,如果大家优先级一样就唤醒等待时间最长的人。

1.2 信号量和队列的对比

队列
信号量
可以容纳多个数据,
创建队列时有 2 部分内存 : 队列结构体、存储数
据的空间
只有计数值,无法容纳其他数据。
创建信号量时,只需要分配信号量结构体
生产者:没有空间存入数据时可以阻塞
生产者:用于不阻塞,计数值已经达到最大时
返回失败
消费者:没有数据时可以阻塞
消费者:没有资源时可以阻塞

1.3 两种信号量的对比

        信号量的计数值都有限制:限定了最大值。如果最大值被限定为1,那么它就是二进制信号量;如果最 大值不是1,它就是计数型信号量。

二进制信号量
技术型信号量
被创建时初始值为 0
被创建时初始值可以设定
其他操作是一样的
其他操作是一样的

2、信号量函数

2.1 创建

        使用信号量之前,要先创建,得到一个句柄;使用信号量时,要使用句柄来表明使用哪个信号量。 对于二进制信号量、计数型信号量,它们的创建函数不一样:

二进制信号量
计数型信号量
动态创建
xSemaphoreCreateBinary
计数值初始值为 0
xSemaphoreCreateCounting
vSemaphoreCreateBinary( 过时了 )
计数值初始值为 1
静态创建
xSemaphoreCreateBinaryStatic
xSemaphoreCreateCountingStatic
/* 创建一个二进制信号量,返回它的句柄。
* 此函数内部会分配信号量结构体
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateBinary( void );
/* 创建一个二进制信号量,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t
                                            *pxSemaphoreBuffer );
/* 创建一个计数型信号量,返回它的句柄。
 * 此函数内部会分配信号量结构体
 * uxMaxCount: 最大计数值
 * uxInitialCount: 初始计数值
 * 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t
                                        uxInitialCount);
/* 创建一个计数型信号量,返回它的句柄。
 * 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针
 * uxMaxCount: 最大计数值
 * uxInitialCount: 初始计数值
 * pxSemaphoreBuffer: StaticSemaphore_t结构体指针
 * 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount,
                                                UBaseType_t uxInitialCount,
                                                StaticSemaphore_t,
                                                *pxSemaphoreBuffer );

2.2 删除

        对于动态创建的信号量,不再需要它们时,可以删除它们以回收内存。vSemaphoreDelete可以用来删除二进制信号量、计数型信号量。

/*
 * xSemaphore: 信号量句柄,你要删除哪个信号量
*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

2.3 give/take

        二进制信号量、计数型信号量的givetake操作函数是一样的。这些函数也分为2个版本:给任务使 用,给ISR使用。

在任务中使用
ISR 中使用
give
xSemaphoreGive
xSemaphoreGiveFromISR
take
xSemaphoreTake
xSemaphoreTakeFromISR
//函数原型
    //xSemaphore 信号量句柄,释放哪个信号量
    //返回值 
        //pdTRUE表示成功,
        //如果二进制信号量的计数值已经是1,再次调用此函数则返回失败;
        //如果计数型信号量的计数值已经是最大值,再次调用此函数则返回失败
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
//函数原型
    //xSemaphore  信号量句柄,释放哪个信号量
    //pxHigherPriorityTaskWoken  如果释放信号量导致更高优先级的任务变为了就绪态,
        //则*pxHigherPriorityTaskWoken = pdTRUE
    //返回值
        //pdTRUE表示成功,如果二进制信号量的计数值已经是1,再次调用此函数则返回失败;
        //如果计数型信号量的计数值已经是最大值,再次调用此函数则返回失败
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,
                                BaseType_t *pxHigherPriorityTaskWoken
);
//函数原型
    //xSemaphore  信号量句柄,获取哪个信号量
    //xTicksToWait  如果无法马上获得信号量,阻塞一会:0:不阻塞,马上返回;
            //portMAX_DELAY: 一直阻塞直到成功;
            //其他值: 阻塞的Tick个数,可以使用 pdMS_TO_TICKS() 来指定阻塞时间为若干ms
    //返回值
        //pdTRUE表示成功
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,
                            TickType_t xTicksToWait
);
//函数原型
    //xSemaphore  信号量句柄,获取哪个信号量
    //pxHigherPriorityTaskWoken  如果获取信号量导致更高优先级的任务变为了就绪态,
            //则*pxHigherPriorityTaskWoken = pdTRUE
    //返回值
        //pdTRUE表示成功
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,
                                BaseType_t *pxHigherPriorityTaskWoken
);

3、示例代码

3.1 同步

        根据错误提示要#define configUSE_COUNTING_SEMAPHORES   1

static int sum = 0;
static volatile int flagCalcEnd = 0;
static SemaphoreHandle_t xSemCalc;

void Task1Function( void * param)
{
	volatile int i = 0;	//使用volatile修饰,让系统不要去优化这个变量
	while(1){
		for(i = 0; i < 10000000; i++){
			sum++;
		}
		xSemaphoreGive(xSemCalc);
		vTaskDelete(NULL);
	}
}

void Task2Function( void * param)
{
	while(1){
		flagCalcEnd = 0;
		xSemaphoreTake(xSemCalc, portMAX_DELAY);
		flagCalcEnd = 1;
		printf("sum = %d\r\n", sum);
	}
}

//main函数中
xSemCalc = xSemaphoreCreateCounting(10, 0);
	
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);

        使用技术性信号量达到同步的效果,可见在这里完成10000000次计数也只是耗时2s。

3.2 互斥

static SemaphoreHandle_t xSemUART;

void TaskGenericFunction( void * param)
{
	while(1){		
		xSemaphoreTake(xSemUART, portMAX_DELAY);	//在使用串口前先take这个信号量
		printf("%s\r\n", (char *)param);
		xSemaphoreGive(xSemUART);	//用完释放掉
		vTaskDelay(1);
		}	
}

//main函数中
xSemUART = xSemaphoreCreateBinary();
xSemaphoreGive(xSemUART);	//二进制信号量初始值为0,这里先给个1,表明这个串口是可以使用的

xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);

        使用二进制信号量达到互斥的效果,任务三和任务四互斥的使用串口。

  • 17
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值