FreeRTOS_信号量之计数型信号量

目录

1. 计数型信号量

1.1 计数型信号量简介

1.2 创建计数型信号量

1.2.1 函数 xSemaphoreCreateCounting()

1.2.2 函数 xSemaphoreCreateCountingStatic()

1.3 计数型信号量创建过程分析

1.4 释放和获取信号量

1.5 计数型信号量操作实验

1.5.1 实验程序设计

1.5.1.1 实验目的

1.5.1.2 实验设计

1.5.2 实验程序

1.5.2.1 main.c


1. 计数型信号量

1.1 计数型信号量简介

        计数型信号量有时也被称为数值信号量,二值信号量相当于长度为 1 的队列,那么计数型信号量就是长度大于 1 的队列同二值信号量一样,用户不需要关心队列中存储了什么数据,只需要关心队列是否为空即可

        计数型信号量常用于如下两个场合:

(1)事件计数

        在这个场合中,每次事件发生的时候就在事件处理函数中释放信号量(增加信号量的计数值),其他任务会获取信号量(信号量计数值减一,信号量就是队列结构体成员变量 uxMessagesWaiting)来处理事件在这种场合中创建的计数型信号量初始计数值为 0

(2)资源管理

        在这个场合中,信号量值代表当前资源的可用数量,比如停车场当前剩余的停车位数量。一个任务要想获得资源的使用权,首先必须获取信号量,信号量获取成功以后信号量值就会减一。当信号量值为 0 的时候说明没有资源了。当一个任务使用完资源以后一定要释放信号量,释放信号量以后信号量值会加一。在这个场合中创建的计数型信号量初始值应该是资源的数量,比如停车场一共有 100 个停车位,那么创建信号量的时候信号量值就应该初始化为 100。

1.2 创建计数型信号量

        FreeRTOS 提供了两个计数型信号量创建函数,如下所示:

xSemaphoreCreateCounting()                        使用动态方法创建计数型信号量。

xSemaphoreCreateCountingStatic()               使用静态方法创建计数型信号量。

1.2.1 函数 xSemaphoreCreateCounting()

        此函数用于创建一个计数型信号量,所需要的内存通过动态内存管理方法分配。此函数本质上是一个宏,真正完成信号量创建的是函数 xQueueCreateCountingSemaphore(),此函数原型如下:

SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t    uxMaxCount,
                                           UBaseType_t    uxInitialCount)

参数:

        uxMaxCount:         计数信号量最大计数值,当信号量值等于此值的时候释放信号量就会失败。

        uxInitialCount:       计数信号量初始值。

返回值:

        NULL:                    计数型信号量创建失败。

        其他值:                   计数值信号量创建成功,返回计数型信号量句柄。

1.2.2 函数 xSemaphoreCreateCountingStatic()

        此函数也是用来创建计数型信号量的,使用此函数创建计数型信号量的时候所需要的内存需要由用户分配。此函数也是一个宏,真正执行的是函数 xQueueCreateCountingSemaphoreStatic(),函数原型如下:

SemaphoreHandle_t xSemaphoreCreateCountingStatic(UBaseType_t    uxMaxCount,
                                                 UBaseType_t    uxInitialCount,
                                                 StaticSemaphore_t*    pxSemaphoreBuffer)

参数:

        uxMaxCount:        计数信号量最大计数值,当信号量值等于此值的时候释放信号量就会失败。

        uxInitialCount:       计数信号量初始值。

        pxSemaphoreBuffer:        指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。

返回值:

        NULL:        计数型信号量创建失败。

        其他值:       计数型信号量创建成功,返回计数型信号量句柄。

1.3 计数型信号量创建过程分析

        这里只分析动态创建计数型信号量函数 xSemaphoreCreateCounting(),此函数是个宏,定义如下:

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) 
    #define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount )                  \ 
        xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )         \ 
#endif

        通过上述可以看出,动态创建计数型信号量函数 xSemaphoreCreateCounting() 真正起作用的是函数 xQueueCreateCountingSemaphore(),此函数在文件 queue.c 中有如下定义:

QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, 
                                             const UBaseType_t uxInitialCount ) 
{
    QueueHandle_t xHandle; 
 
    configASSERT( uxMaxCount != 0 ); 
    configASSERT( uxInitialCount <= uxMaxCount ); 
 
    xHandle = xQueueGenericCreate( uxMaxCount,                                \ (1) 
    queueSEMAPHORE_QUEUE_ITEM_LENGTH,                                         \ 
    queueQUEUE_TYPE_COUNTING_SEMAPHORE ); 
 
    if( xHandle != NULL ) 
    { 
        ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;          (2) 
        traceCREATE_COUNTING_SEMAPHORE(); 
    } 
    else 
    { 
        traceCREATE_COUNTING_SEMAPHORE_FAILED(); 
    } 

    return xHandle; 
} 

(1)、计数型信号量也是在队列的基础上实现的,所以需要调用函数 xQueueGenericCreate() 创建一个队列,队列长度为 uxMaxCount,队列项长度为 queueSEMAPHORE_QUEUE_ITEM_LENGTH (此宏为 0 ),队列的类型为 queueQUEUE_TYPE_COUNTING_SEMAPHORE,表示是个计数型信号量。

(2)、队列结构体的成员变量 uxMessagesWaiting 用于计数型信号量的计数,根据计数型信号量的初始值来设置 uxMessagesWaiting。

1.4 释放和获取信号量

        计数型信号量的释放和获取与二值信号量相同,如有疑问请进一步进行学习 https://blog.csdn.net/light_2025/article/details/133554890

1.5 计数型信号量操作实验

1.5.1 实验程序设计

1.5.1.1 实验目的

        计数型信号量一般用于事件计数和资源管理,计数型信号量在这个场景中的使用方法基本一样,这里来学习一下计数型信号量在事件计数中的使用方法。

1.5.1.2 实验设计

        本实验用 KEY_UP 按键来模拟事件,当 KEY_UP 按下以后就表示事件发生,当检测到 KEY_UP 按下以后就释放计数型信号量,按键的检测和信号量的释放做成一个任务。另外一个任务获取信号量,当信号量获取成功以后就刷新 LCD 上指定区域的背景颜色,并且显示计数型信号量的值。

        本实验设计三个任务:start_task、SemaphoreGive_task、SemapTake_task 这三个任务的任务功能如下:

        start_task:用来创建其他 2 个任务。

        SemaphoreGive_task:获取按键状态,当 KEY_UP 键按下去以后就释放信号量 CountSemaphore,此任务还用来控制 LED0 的亮灭来提示程序正在运行中。

        SemapTake_task:获取信号量 CountSemaphore,当获取信号量成功以后就刷新 LCD 指定区域的背景色。

        实验中创建了一个计数型信号量 CountSemaphore,此信号量用于记录 KEY_UP 按下的次数。硬件部分需要用到 KEY_UP 按键,用于模拟事件发生。

1.5.2 实验程序

1.5.2.1 main.c
#include "stm32f4xx.h"  
#include "FreeRTOS.h" //这里注意必须先引用FreeRTOS的头文件,然后再引用task.h
#include "task.h"     //存在一个先后的关系
#include "LED.h"
#include "LCD.h"
#include "Key.h"
#include "usart.h"
#include "delay.h"
#include "string.h"
#include "beep.h"
#include "malloc.h"
#include "timer.h"
#include "queue.h"
#include "semphr.h"


//任务优先级
#define START_TASK_PRIO     1     //用于创建其他两个任务
//任务堆栈大小
#define START_STK_SIZE      256
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define SEMAPGIVE_TASK_PRIO     2   //获取按键状态,当 KEY_UP 键按下去以后就释放信号量 CountSemaphore
//任务堆栈大小
#define SEMAPGIVE_STK_SIZE      256
//任务句柄
TaskHandle_t SemapGiveTask_Handler;
//任务函数
void SemapGive_task(void *pvParameters);

//任务优先级
#define SEMAPTAKE_TASK_PRIO 3  //获取信号量 CountSemaphore,当获取信号量成功以后就刷新 LCD 指定区域的背景色
//任务堆栈大小	
#define SEMAPTAKE_STK_SIZE  256 
//任务句柄
TaskHandle_t SemapTakeTask_Handler;
//任务函数
void SemapTake_task(void *pvParameters);

//计数型信号量句柄
SemaphoreHandle_t CountSemaphore;    //计数型信号量句柄

//LCD刷屏时使用的颜色
int lcd_discolor[14] = { WHITE, BLACK, BLUE,  BRED,
                         GRED,  GBLUE, RED,   MAGENTA,
                         GREEN, CYAN,  YELLOW,BROWN,
                         BRRED, GRAY};

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);  //设置系统中断优先级分组4
    delay_init(168);
    uart_init(115200);
    LED_Init();
    KEY_Init();
    BEEP_Init();
    LCD_Init();
    my_mem_init(SRAMIN);    //初始化内部内存池
    
    POINT_COLOR=RED;
    LCD_ShowString(30,10,200,16,16,"ATK STM32F407");
    LCD_ShowString(30,30,200,16,16,"FreeRTOS Example");
    LCD_ShowString(30,50,200,16,16,"Count Semaphore");
    LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
    LCD_ShowString(30,90,200,16,16,"2023/10/07");
    
    POINT_COLOR=BLACK;
    LCD_DrawRectangle(5,110,234,314);
    LCD_DrawLine(5,130,234,130);
    POINT_COLOR=RED;
    LCD_ShowString(30,111,200,16,16,"COUNT_SEM Value: 0");
    POINT_COLOR=BLUE;
    
    //创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();
    
    //创建计数型信号量
    CountSemaphore=xSemaphoreCreateCounting(255,0); //第一个参数是计数信号量最大计数值
                                                    //第二个参数是计数信号量初始值;返回计数信号量句柄
    //创建释放信号量任务
    xTaskCreate((TaskFunction_t )SemapGive_task,             
                (const char*    )"semapgive_task",           
                (uint16_t       )SEMAPGIVE_STK_SIZE,        
                (void*          )NULL,                  
                (UBaseType_t    )SEMAPGIVE_TASK_PRIO,        
                (TaskHandle_t*  )&SemapGiveTask_Handler);   
    //创建获取信号量任务
    xTaskCreate((TaskFunction_t )SemapTake_task,     
                (const char*    )"semaptake_task",   
                (uint16_t       )SEMAPTAKE_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )SEMAPTAKE_TASK_PRIO,
                (TaskHandle_t*  )&SemapTakeTask_Handler);
    vTaskDelete(StartTask_Handler);  //删除开始任务
    taskEXIT_CRITICAL();
}

//释放计数型信号量任务函数
void SemapGive_task(void *pvParameters)
{
    u8 key,i=0;
    u8 semavalue;
    BaseType_t err;
    
    while(1)
    {
        key=KEY_Scan(0);  //扫描按键
        if(CountSemaphore!=NULL)  //CountSemaphore 是动态创建计数信号量函数,返回值表示创建成功的计数信号量句柄
            //如果句柄不为空,则表示创建计数型信号量成功
        {
            switch(key) //key 取值为 1 2 3 4 0,0表示无按键按下
            {
                case WKUP_PRES: //key 等于4
                    err=xSemaphoreGive(CountSemaphore); //释放计数型信号量,表示KEY_UP按键按下,释放信号量
                    if(err==pdFALSE)
                    {
                        printf("信号量释放失败!!!\r\n");
                    }
                    semavalue=uxSemaphoreGetCount(CountSemaphore); //获取计数型信号量值
                    LCD_ShowxNum(155,111,semavalue,3,16,0);  //显示信号量值
                    break;
            }
                
        }
        i++;
        if(i==50)
        {
            i=0;
            LED0=!LED0;
        }
        vTaskDelay(10);   //延时10ms,也就是10个时钟节拍
    }
}

//获取计数型信号量任务函数
void SemapTake_task(void *pvParameters)
{
    u8 num;
    u8 semavalue;
    while(1)
    {
        xSemaphoreTake(CountSemaphore,portMAX_DELAY);  //等待数值信号量
        num++;
        semavalue=uxSemaphoreGetCount(CountSemaphore);  //获取数值信号量值
        LCD_ShowxNum(155,111,semavalue,3,16,0);         //显示信号量值
        LCD_Fill(6,131,233,313,lcd_discolor[num%14]);   //刷屏  
        LED1=!LED1;
        vTaskDelay(1000);                               //延时1s,也就是1000个时钟节拍
    }
}




你可能感兴趣的:(FreeRTOS,数据结构,单片机,嵌入式硬件,stm32)