计数型信号量又叫数值信号量,二值信号量相当于长度为1的队列,那么计数型信号量就是长度大于1 的队列。同二值信号量一样,用户不需要关心队列中存储了什么数据,只需要关心队列是否为空即可。
计数型信号量运用场合:
事件计数,资源管理。
事件计数:
每次事件发生的时候就在事件处理函数中释放信号量(增加信号量的计数值),其他任务会获取信号量(信号量计数值减1,信号量值就是队列结构体成员变量uxMessageWaiting)来处理事件。在这种场合中创建的计数型信号量初始计数值为0.
资源管理:
在这个场合中,信号值代表当前资源的可用数量,比如停车场剩余的停车位数量。一个任务要想获得资源的使用权,首先必须获取信号量,信号量获取成功以后信号量就会减1。当信号量值为0就表示没有资源了,当一个任务使用完资源以后一定要释放信号量,释放信号量以后信号量值会加1。在这个场合中创建的计数型信号量初始值应该是资源的数量。比如停车场由199个车位,那么创建信号量的时候信号量值就是199.
注意:计数型信号也是在队列的基础上实现的,所以需要调用函数创建队列函数来创建一个队列,队列长度为uxMaxCount。
计数型信号量的释放和获取二值信号量相同。
main.c
#include "sys.h"
#include "usart.h"
#include "led.h"
#include "delay.h"
#include "light.h"
#include "FreeRTOS.h"
#include "semphr.h"//信号量的头文件
#include "task.h"
/*
本次实验:计数型信号量在事件计数中的使用方法。
用光敏模块来给pc15引脚一个电平信号。用这个来模拟按键的功能(因为我的按键坏了)
当检测到一个低电平后释放计数型信号量,另一个任务来获取信号量,获取成功后点亮led灯。
start_task:开始任务
task1:led闪烁,表示任务在运行
task2:检测光敏模块引脚的电平值,低电平就释放计数型信号量,
成功获取6个信号量值(也就是触发6次后)就点亮led
task3:获取信号量,
*/
#define start_task_size 52
#define start_task_pro 1
TaskHandle_t startTask_handler;
void start_task(void *pram);
#define task1_size 100
#define task1_pro 2
TaskHandle_t Task1_handler;
void task1(void *pram);
#define task2_size 100
#define task2_pro 4
TaskHandle_t Task2_handler;
void task2(void *pram);
#define task3_size 100
#define task3_pro 3
TaskHandle_t Task3_handler;
void task3(void *pram);
//计数型信号量句柄
SemaphoreHandle_t countSemaphore;
int main(void)
{
delay_init(); //延时函数初始化
uart_init(115200); //初始化串口
LED_Init(); //初始化LED
Light_GPIO_Init();
xTaskCreate(
(TaskFunction_t )start_task,
(const char * ) "start_task",
(uint16_t )start_task_size ,
(void * )NULL,
(UBaseType_t ) start_task_pro,
(TaskHandle_t * )startTask_handler );
vTaskStartScheduler();//开始任务调度
}
/*88888888888888888888888888888888888888888888888888888888888888888888*/
void start_task(void *pram)
{
taskENTER_CRITICAL();
//创建计数型信号量
countSemaphore = xSemaphoreCreateCounting(20,0);
//计数型信号量 计数值最大是20,所有计数型信号量的初始值为0。
xTaskCreate(
(TaskFunction_t ) task1,
(const char * ) "task1",
(uint16_t ) task1_size ,
(void * ) NULL,
(UBaseType_t ) task1_pro,
(TaskHandle_t * ) Task1_handler);
xTaskCreate(
(TaskFunction_t ) task2,
(const char * ) "task2",
(uint16_t ) task2_size,
(void * ) NULL,
(UBaseType_t ) task2_pro,
(TaskHandle_t * ) Task2_handler);
xTaskCreate(
(TaskFunction_t ) task3,
(const char * ) "task3",
(uint16_t ) task3_size,
(void * ) NULL,
(UBaseType_t ) task3_pro,
(TaskHandle_t * ) Task3_handler);
vTaskDelete(NULL);
taskEXIT_CRITICAL();
}
void task1(void *pram)
{
while(1)
{
LED1 = !LED1;
vTaskDelay(1000);
}
}
void task2(void *pram)//检测引脚电平,释放计数型信号量
{
BaseType_t err;
//用来存储 获取数值信号量的个数
uint8_t semavalue = 0;
while(1)
{
if(countSemaphore != NULL)//创建计数型信号量 成功
{
if( GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_15) == 1 )
{
err = xSemaphoreGive(countSemaphore);//释放计数型信号量
// if(err == pdFALSE)
// {
// printf("semap give false\r\n");
//
// }
// else if(err == pdTRUE)
// {
// printf("give sucess\r\n");
//
// }
}
semavalue = uxSemaphoreGetCount(countSemaphore);//获取数值信号量值(也就是得到由多少个数值信号量)
if(semavalue == 6)
{
LED2_ON();
}
printf("task2:get%d\n",semavalue);
}
vTaskDelay(50);
}
}
void task3(void *pram)
{
//用来存储 获取数值信号量的个数
uint8_t semavalue = 0;
while(1)
{
xSemaphoreTake(countSemaphore,portMAX_DELAY);//等待数值信号量(计数型信号量)
semavalue = uxSemaphoreGetCount(countSemaphore);//获取数值信号量值
printf("task3:%d\n",semavalue);
vTaskDelay(1000);
}
}
(1)
原因是:
创建一个计数型信号量后需要一个返回值(也就是信号句柄)接收。
(2)
任务2和3 的打印信息交叠,原因是:任务2和3 的优先级一样了,把优先级改一下。
(3)我触碰一次光敏电阻后,确实可以成功释放一个信号量,但是这个信号量值没有累加。原因是:(1)因为我是光敏模块来模拟按键,所以本质上和按键的触发方式可能不太一样。(2)基于我知道了(1)点的不同,在经过我把延时改的尽量小的时候,还是隐隐约约的可以实现信号量值的累加。【每触碰一次光敏电阻,就释放一个信号量,然后就把这个缓冲延时改的小点,就可以实现】
现象: