(第40-44讲)STM32F4单片机,FreeRTOS信号量【二值、计数、翻转、互斥】【代码讲解】【硬件家园、正点原子】【原创】


🔴🟡🟢其他文章链接,独家吐血整理

【吐血总结】FreeRTOS难点、Systick中断-滴答定时器、PendSV中断-任务切换、SVC中断-系统底层、时间片调度-时钟节拍【已完结】
(第1-8讲)STM32F4单片机,FreeRTOS基础知识总结【视频笔记、代码讲解】【正点原子】【原创】
(第9-10讲)STM32F4单片机,FreeRTOS任务创建和删除(动态方法)【视频笔记、代码讲解】【正点原子】【原创】
(第12讲)STM32F4单片机,FreeRTOS任务创建和删除(静态方法)【视频笔记、代码讲解】【正点原子】【原创】
(第13-14讲)STM32F4单片机,FreeRTOS任务挂起和恢复【视频笔记、代码讲解】【正点原子】【原创】
(第16-17讲)STM32F4单片机,FreeRTOS中断管理简介【视频笔记、代码讲解】【正点原子】【原创】
(第18-19讲)32单片机,FreeRTOS临界段代码保护、任务调度器的挂起和恢复【视频笔记、代码讲解】【原创】
(第20-22讲)STM32F4单片机,FreeRTOS列表和列表项API函数讲解【视频笔记、代码讲解、正点原子】【原创】
(第34-36讲)FreeRTOS消息队列知识汇总【B站UP、硬件家园、普中科技、正点原子】【视频笔记】【原创】
(第40-44讲)STM32F4单片机,FreeRTOS信号量【二值、计数、翻转、互斥】【代码讲解】【正点原子】【原创】

1、2024温故知新(纯文字 + 图片)

队列类似于数组,信号量类似于bool型标志变量
信号量本质上也是由队列实现,只不过要简单些
释放信号量(就是写二值信号量)不可阻塞(读空时依然会阻塞),队列写满读满都会阻塞任务
二值信号量=队列长度为1的队列
二值信号量=互斥访问=任务同步(不是同时,是等待一个完成后另一个瞬间继续接上即同步)=不存在优先级翻转
创建二值信号量也是由动态和静态方式(建议还是动态方式)
在这里插入图片描述

计数型信号量=队列长度>1的队列
计数型信号量=事件计数
释放一个计数信号量=写一个信号量=资源+1
获取一个计数信号量=读一个信号量=资源-1

/*
char g_i = 0;

task//1ms的时间片
{	
	if (读信号量)
	{
		//code
	}
	
	vTaskDelay(10); //10ms
	g_i++;
	
	if (g_i > 200) //2s
	{
		g_i = 0;
	}
}//如果读信号量发现为空,则task被阻塞(从运行态变阻塞态),则g_i不会再++
*/

优先级翻转:高优先级的任务慢执行,低优先级的任务先执行(实时性操作系统不允许这种现象出现)
但实际上二值信号量会导致这种情况的发生(如图所示,我们不喜欢这种现象,如何避免?只能使用互斥信号量!!!)
在这里插入图片描述
注:下图中,如果不开启任务调度,只用死延时,那就是时间片调度,此时只会永远执行高优先的任务,低优先级的任务永远执行不了,除非三个任务都是同等优先级才会轮转执行
在这里插入图片描述注:上图中,如果不开启任务调度,只用死延时,那就是时间片调度,此时只会永远执行高优先的任务,低优先级的任务永远执行不了,除非三个任务都是同等优先级才会轮转执行

互斥信号量=带优先级继承的二值信号量=简单来说就是改善了上面优先级翻转的问题(并不是完美解决,只能说把危害程度尽可能降低,会影响比互斥信号量那个任务优先级低的任务,具体可看视频)
互斥信号量只能用于任务中,不能用于中断中(不解释了)
使用互斥信号量前要将一个宏打开
在这里插入图片描述

2、二值信号量

1、队列与信号量(B站硬件家园)

在这里插入图片描述
在这里插入图片描述
队列是数据是结构体,信号量是状态是flag
在这里插入图片描述
在这里插入图片描述
这种特殊的队列,大小为0(所以不能存数据),长度为1
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
即信号量初始值为0
即串口接收到数据时,相当于flag为1
task1得到了1,task1知道串口接收到了数据
然后flag变为0,等待下一次判断(cpu只判断一次省算力)
在这里插入图片描述
在这里插入图片描述
我们很少用这个函数,因为中断要求实时性

2、demo.c

/**
 ****************************************************************************************************
 * @file        freertos.c
 * @author      正点原子团队(ALIENTEK)
 * @version     V1.4
 * @date        2022-01-04
 * @brief       FreeRTOS 移植实验
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:正点原子 F407电机开发板
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 ****************************************************************************************************
 */

#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );


/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );

/******************************************************************************************************/
QueueHandle_t semphore_handle;//定义了一个二值信号量变量
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    semphore_handle = xSemaphoreCreateBinary();//定义二值信号量
    if(semphore_handle != NULL)
    {
        printf("二值信号量创建成功!!!\r\n");
    }
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}


void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                             
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,释放二值信号量 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    BaseType_t err;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            if(semphore_handle != NULL)
            {
                err = xSemaphoreGive(semphore_handle);
                if(err == pdPASS)
                {
                    printf("信号量释放成功!!\r\n");
                }else printf("信号量释放失败!!\r\n");
            }
            
        }
        vTaskDelay(10);
    }
}

/* 任务二,获取二值信号量 */
void task2( void * pvParameters )
{
    uint32_t i = 0;
    BaseType_t err;
    while(1)
    {
        err = xSemaphoreTake(semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
        if(err == pdTRUE)
        {
            printf("获取信号量成功\r\n");
        }else printf("已超时%d\r\n",++i);
        
    }
}

3、代码讲解

void freertos_demo(void)
{    
    semphore_handle = xSemaphoreCreateBinary();
    if(semphore_handle != NULL)
    {
        printf("二值信号量创建成功!!!\r\n");
    }
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}

QueueHandle_t semphore_handle;
semphore_handle 是结构体类型指针变量=它的内容是结构体地址=结构体指针,QueueHandle_t是用typedef封装起来的结构体类型指针,xSemaphoreCreateBinary函数的返回值类型就是QueueHandle_t

    vTaskDelete(NULL);

如果没有这个删除自己的语句,则start任务会一直创建task1与task2,则久而久之堆栈就会消耗完所有单片机内存,

/* 任务一,释放二值信号量 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    BaseType_t err;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            if(semphore_handle != NULL)
            {
                err = xSemaphoreGive(semphore_handle);
                if(err == pdPASS)
                {
                    printf("信号量释放成功!!\r\n");
                }else printf("信号量释放失败!!\r\n");
            }
            
        }
        vTaskDelay(10);
    }
}

err没啥好说的,是long int长整型,一般是64或者32位

/* 任务二,获取二值信号量 */
void task2( void * pvParameters )
{
    uint32_t i = 0;
    BaseType_t err;
    while(1)
    {
        err = xSemaphoreTake(semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
        if(err == pdTRUE)
        {
            printf("获取信号量成功\r\n");
        }else printf("已超时%d\r\n",++i);
        
    }
}

在这里插入图片描述
portMAX_DELAY=0xffffffffUL,portMAX_DELAY的作用看上图,虽然task2的任务优先级高于task1,但是这个获取信号量函数会使task2被阻塞,因此此时task1会优先执行(并且task2随时准备机会抢占task1),i用来记录阻塞时间,每次+1大约是一个时间片
在这里插入图片描述
如图,这里很奇怪,就是说按键key0按下的一瞬间,竟然没有进入if判断,反而先进入的是task2的printf,这是因为task2的任务优先级高,本来就应该是task2先执行,在你按下的瞬间信号在task1中释放了,还没来得及进入if打印,就被task2抢占了(优先级高),这个抢占就是上面所说的task2在找的机会

                err = xSemaphoreGive(semphore_handle);

这个释放函数没有超时机制,没有就不释放(即释放失败),有就释放(即成功)

❤注:硬件家园&&正点原子的区别

在这里插入图片描述
硬件家园里面,这个是void类型=空类型
在这里插入图片描述
这个是正点原子,这个是结构体类型

3、计数型信号量

1、PPT

在这里插入图片描述
没啥好说的,就是二值信号量的延伸

2、demo.c

/**
 ****************************************************************************************************
 * @file        freertos.c
 * @author      正点原子团队(ALIENTEK)
 * @version     V1.4
 * @date        2022-01-04
 * @brief       FreeRTOS 移植实验
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:正点原子 F407电机开发板
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 ****************************************************************************************************
 */

#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );


/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );

/******************************************************************************************************/
QueueHandle_t count_semphore_handle;
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    count_semphore_handle = xSemaphoreCreateCounting(100 , 0);  /* 创建计数型信号量 */
    if(count_semphore_handle != NULL)
    {
        printf("计数型信号量创建成功!!!\r\n");
    }
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}


void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                             
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,释放计数型信号量 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            if(count_semphore_handle != NULL)
            {
                xSemaphoreGive(count_semphore_handle);      /* 释放信号量 */
            }
        }
        vTaskDelay(10);
    }
}

/* 任务二,获取计数型信号量 */
void task2( void * pvParameters )
{
    BaseType_t err = 0;
    while(1)
    {
        err = xSemaphoreTake(count_semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
        if(err == pdTRUE)
        {
            printf("信号量的计数值为:%d\r\n",(int)uxSemaphoreGetCount(count_semphore_handle));
        }
        vTaskDelay(1000);
    }
}

3、代码讲解

count_semphore_handle = xSemaphoreCreateCounting(100 , 0);  /* 创建计数型信号量 */

//100是上限,0是初始值

                xSemaphoreGive(count_semphore_handle);      /* 释放信号量 */

这个函数每执行一次,计数就会+1,至于+1的时间间隔由task的延时来决定,比如正点的这个就是10ms进入一次(前提是按键按下了)

        err = xSemaphoreTake(count_semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */

这个函数每执行一次,计数就会-1,同样时间间隔也由task的延时来决定,比如正点的这个就是1000ms=1s

            printf("信号量的计数值为:%d\r\n",(int)uxSemaphoreGetCount(count_semphore_handle));

获取计数值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值