
FreeRTOS中的信号量是一种用于任务间同步和资源管理的机制。信号量可以是二进制的只能取0或1也可以是计数型的可以是任意正整数。信号量的基本操作包括“获取”和“释放”。是为了解决同一时刻下资源的争抢问题。我们用CubeMX生成的Rtos工程中自带了一个默认的任务启动任务我们创建信号量时一般是先创建信号量随后创建自己的业务任务最后删除启动任务自身。信号量有二值信号量和计数型信号量我们先来看看二值型信号量二值信号量二值型信号量的内部就是一个长度为1的队列然后他的值只能取0和1。创建二值信号量的函数原型如下/* 创建一个二进制信号量返回它的句柄。 * 此函数内部会分配信号量结构体 * 返回值: 返回句柄非NULL表示成功 */ SemaphoreHandle_t xSemaphoreCreateBinary( void ); //创建后不会主动释放一次信号量 void vSemaphoreCreateBinary( SemaphoreHandle_t ); //创建后会主动释放一次信号量 他们还有两个区别就是x开头的不需要参数他会返回一个队列的句柄内部把队列创建好了。但是v开头的需要传入队列句柄 /* 创建一个二进制信号量返回它的句柄。 * 此函数无需动态分配内存所以需要先有一个StaticSemaphore_t结构体并传入它的指针 * 返回值: 返回句柄非NULL表示成功 */ SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer );这里我们需要注意一点就是当我们把队列添加进队列集的时候会先判断队列内部是否为空如果为空就可以添加成功。vSemaphoreCreateBinary 函数在创建时会主动释放一次就导致内部不为空就添加不进去队列集。释放二值信号量也就是放入二值信号量BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );首先就是我们自己定义一个队列句柄//二值信号传入的参数队列句柄*/ QueueHandle_t Sem_Handle; void StartDefaultTask(void *argument) { /* 在启动函数中创建二值函数 */ vSemaphoreCreateBinary(Sem_Handle); if(Sem_Handle NULL) { printf(Create binary semaphore failed\r\n); } /* 业务任务代码 */ } /* 用于按键扫描当按键检测到按键1按下时释放二值信号量放入 */ void Task1(void *argument) { uint8_t key; while(1) { key Key_Scan(); if(key KEY1_PRESS) { /* 释放二值信? */ BaseType_t res xSemaphoreGive(Sem_Handle); //内部调用的是队列的发送所以这里需要传入一?队列参数 if(res pdPASS) { printf(Give two num success\r\n); } else { printf(Give two num failed\r\n); } } vTaskDelay(500); } } /* 获取二值信号当成功获取后打印提示信息 */ void Task2(void *argument) { while(1) { /* 获取二值信号量 */ BaseType_t res xSemaphoreTake(Sem_Handle, portMAX_DELAY); //他需要传队列句柄和阻塞时间 if(res pdPASS) { printf(success\r\n); } else { printf(failed\r\n); } vTaskDelay(500); } }计数型信号量二值型信号量是数量为1的队列但是计数型信号量是可以计数的队列。我们在创建时会规定计数最大值和初始值。假设我有三个任务ABC然后计数型信号量数值是3我们每个任务执行的时候都会先获取一个计数值让计数值-1任务执行完毕后在释放一次让计数值1.本质上他们是全局变量的替代因为全局变量会导致同时访问时造成混乱创建计数型信号量//二值信号传入的参数队列句柄 QueueHandle_t Sem_Count_Handle; /* 创建计数型信号量 */ Sem_Count_Handle xSemaphoreCreateCounting(100, 0); //参数最大计数值初始计数值 返回值队列句柄在获取和释放是使用的API跟二值信号量是一样的//释放 xSemaphoreGive(队列句柄) 返回值paDASS则说明成功 //接收 xSemaphoreTake(队列句柄) 返回值paDASS则说明成功倒反天罡优先级翻转优先级翻转是一个在实时系统中可能出现的问题特别是在多任务环境中。该问题指的是一个较低优先级的任务阻塞了一个较高优先级任务的执行从而导致高优先级任务无法及时完成。举个例子大家都知道二值型信号量只有一个值比如我现在一个低优先级刚开始执行获取了信号量随后他被高优先级打断然后高优先级也需要获取这个信号量那他就等不到这个信号量如果设置的获取信号量是阻塞模式的话就会导致高优先级一直被阻塞。以此图为例刚开始taskB运行他获取了信号量随后被taskA抢占但是taskB没有释放信号量随后taskA就阻塞等待taskB释放信号量随后来了一个中等优先级的taskC那么此时没有人能打断taskC一直运行到taskC完毕为止随后执行taskB当taskB释放信号量时taskA才开始执行。由于获取信号量的任务优先级较低那么任务一个任务都能打断他一打断他那么他释放信号量就越晚高优先级就越晚执行那么如何解决这个问题呢互斥信号量也称为互斥锁含有优先级继承机制的二值信号量互斥锁互斥锁几乎跟二值信号量几乎一模一样但是他们还是有区别的二值信号量是为了同步但是互斥锁是为了保护所有权二值信号量每个任务都可以give但是互斥锁谁give谁才可以take互斥锁有优先级继承机制初始状态下二值信号量为空而互斥锁不为空二值信号量可以在中断中使用但是互斥锁不可以在中断中使用在上述优先级翻转的场景中当我们使用了互斥锁后看场景此时taskB开始运行他获取了信号量随后被高优先级taskA打断然后互斥锁的优先级继承机制会把taskB的优先级拉到和taskA的优先级一样这样taskA就不会阻塞他此时会跟taskB时间片轮询直到taskB释放了信号量taskA才会获取信号量随后taskA运行完后执行taskC。注意二值信号任务A可以take(取走)信号量任务B可以give(放入)信号量。但是互斥锁只有任务A take(取走)了只有他才能give(放入)还有就是互斥信号量不能在中断中使用创建互斥锁函数描述xSemaphoreCreateMutex()使用动态方法创建互斥信号量。xSemaphoreCreateMutexStatic()使用静态方法创建互斥信号量。创建互斥信号量//互斥信号量的句柄 QueueHandle_t Sem_Handle; Sem_Handle xSemaphoreCreateMutex(); if(Sem_Handle NULL) { printf(互斥信号量创建失败\r\n); } else { printf(互斥信号量创建成功\r\n); }