【FreeRTOS-信号量】

参照正点原子以及以下gitee笔记整理本博客,并将实验结果附在文末。

https://gitee.com/xrbin/FreeRTOS_learning/tree/master

一、信号量简介

1、什么是信号量

答:信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问。

假设有一个人需要在停车场停车。

  • 空车位:信号量资源数(计数值)
  • 让出占用车位: 释放信号量(计数值++)
  • 占用车位: 获取信号量(计数值--)
  1. 首先判断停车场是否还有空车位(判断信号量是否有资源)。
  2. 停车场正好有空车位(信号量有资源),那么就可以直接将车开入停车位进行停车(获取信号量成功)。
  3. 停车场已经没有空车位了(信号量没有资源),那么可以选择不停车(获取信号量失败),也可以选择等待(任务阻塞)其他人将车开出停车位(释放信号),然后在将车停如空车位。

2、信号量简介

答:
- 当计数值大于0,表示有信号量资源。

  • 当释放信号量,信号量计数值(资源数)加一。
  • 当获取信号量,信号量计数值(资源数)减一。
  • 信号量的计数值都是有限的:限定最大值。
  • 如果最大值被限定为1,那么它就是二值信号量。
  • 如果最大值不是1,它就是计数型信号量。

注意:信号量用于传递状态。

3、队列与信号量的对比

答:

二、二值信号量

1、二值信号量介绍

答:二值信号量的本质是一个队列长度等于1的队列,该队列就只有空和满两种情况。这就是二值信号量。

注意:二值信号量通常用于互斥访问或任务同步,与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题,所以二值信号量更适合用于同步!!!

2、二值信号量相关API函数

答:使用二值信号量的过程:创建二值信号量 -> 释放二值信号量 -> 获取二值信号量

3、创建二值信号量函数

答:创建二值信号量函数:

c 复制代码
SemaphoreHandle_t  xSemaphoreCreateBinary( void );
c 复制代码
#define   xSemaphoreCreateBinary()
xQueueGenericCreate(1 , semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE)  /*二值信号量就是长度为1的队列、semSEMAPHORE_QUEUE_ITEM_LENGTH队列项大小是0,queueQUEUE_TYPE_BINARY_SEMAPHORE表示二值信号量类型*/
#define   semSEMAPHORE_QUEUE_ITEM_LENGTH   (( uint8_t ) 0U)
c 复制代码
#define   queueQUEUE_TYPE_BASE                           ( ( uint8_t ) 0U ) /* 队列 */
#define   queueQUEUE_TYPE_SET                            ( ( uint8_t ) 0U ) /* 队列集 */
#define   queueQUEUE_TYPE_MUTEX                          ( ( uint8_t ) 1U ) /* 互斥信号量 */
#define   queueQUEUE_TYPE_COUNTING_SEMAPHORE             ( ( uint8_t ) 2U ) /* 计数型信号量 */
#define   queueQUEUE_TYPE_BINARY_SEMAPHORE               ( ( uint8_t ) 3U ) /* 二值信号量 */
#define   queueQUEUE_TYPE_RECURSIVE_MUTEX                ( ( uint8_t ) 4U ) /* 递归互斥信号量 */

返回值:

4、释放二值信号量函数

答:释放二值信号量函数:

c 复制代码
BaseType_t   xSemaphoreGive( xSemaphore ) 
c 复制代码
#define   xSemaphoreGive ( xSemaphore )
xQueueGenericSend((QueueHandle_t)( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK) /*句柄,NULL表示数据,阻塞时间为0,尾部插入*/
#define   semGIVE_BLOCK_TIME       ( ( TickType_t ) 0U )

函数参数:

函数返回值:

5、获取二值信号量函数

答:获取二值信号量函数:

c 复制代码
BaseType_t   xSemaphoreTake( xSemaphore, xBlockTime ) 

函数参数:

函数返回值:

三、计数型信号量

1、计数型信号量介绍

答:计数型信号量相当于队列长度大于1的队列,因此计数型信号量能够容纳多个资源(计数值),这在计数型信号量被创建的时候确定的。

计数型信号量适用场合:

  • 事件计数 : 当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务会获取计数型信号量(计数值-1),这种场合一般在创建时将初始化计数值设置为0.
  • 资源管理 : 信号量表示有效资源数量。任务必须先获取信号量(信号计数值-1)才能获取资源控制权。当计数值减为0时表示没有资源。当任务使用完资源后,必须释放信号量(信号量计数值+1)。信号量创建时计数值应等于最大资源数目。

注意:事件计数和资源管理的区别在于信号计数值的初始值不同。

2、计数型信号量相关API函数

答:使用计数型信号量的过程:创建计数型信号量 -> 释放信号量 -> 获取信号量

注意:计数型信号量的释放与获取的函数和二值信号量一样。

3、计数型信号量创建函数

答:

c 复制代码
#define 	xSemaphoreCreateCounting( uxMaxCount , uxInitialCount )
            xQueueCreateCountingSemaphore( ( uxMaxCount ) , ( uxInitialCount ) ) 

函数参数:

资源数取决于设置的初始计数值!

函数返回值:

4、获取计数型信号量计数值函数

答:

c 复制代码
#define 	uxSemaphoreGetCount( xSemaphore ) 
            uxQueueMessagesWaiting(( QueueHandle_t )( xSemaphore ))

函数参数:

函数返回值:

四、优先级翻转介绍

1、优先级翻转简介

答:优先级翻转:高优先级的任务反而慢执行,低优先级的任务反而优先执行。

优先级翻转在抢占式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果。

在使用二值信号量的时候,经常会遇到优先级翻转的问题。

2、优先级翻转的例子

答:

高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上看,就像是中等优先级的任务比高优先级任务具有更高的优先权(即优先级翻转)。

五、互斥信号量

1、互斥信号量介绍

答:互斥信号量其实就是一个 拥有优先级翻转的二值信号量。

  • 二值信号量更适用于同步的应用。
  • 互斥信号量更适合那些需要互斥访问的应用(资源紧缺,需要资源保护)。

2、什么是优先级继承

答:当一个互斥信号量正在被一个低优先级的任务持有时,如果此时有一个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。

3、优先级继承示例

答:

变为

此时任务H的阻塞时间仅仅是任务L的执行时间,将优先级翻转的危害降低到了最低。

4、互斥信号量的注意事项

答:优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响。

注意:互斥信号量不能用于中断服务函数中,原因如下:

  1. 互斥信号量有任务优先级继承的机制,但是中断不是任务,没有任务优先级,所以互斥信号量只能用于任务中,不能用于中断服务函数中。
  2. 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。

5、互斥信号量相关API函数

答:使用互斥信号量:首先将宏configUSE_MUTEXES置1.

使用流程:创建互斥信号量 -> (task)获取信号量 -> (give)释放信号量

创建互斥信号量函数:

互斥信号量的释放和获取函数与二值信号量相同!!!只不过互斥信号量不支持中断中调用。

注意:创建互斥信号量时,会主动释放一次信号量。而二值信号量需要在创建完后进行手动释放,才能获取该信号量。

6、创建互斥信号量函数

答:

c 复制代码
xxxxxxxxxx1 1#define   xSemaphoreCreateMutex()      xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

函数返回值:

注意:

释放信号量不支持设置阻塞时间;

获取信号量支持设置阻塞时间;

六、二值信号量实验(掌握)

实验简介

实验现象

二值信号量状态变化:

1、创建信号量后的初始状态

c 复制代码
semphore_handle = xSemaphoreCreateBinary();
  • 初始状态:0(空状态)

  • 刚创建的二值信号量是空的,没有可用的信号

2、释放信号量(Give)

c 复制代码
xSemaphoreGive(semphore_handle);
  • 状态变化:0 → 1(空状态 → 满状态)

  • 释放信号量使其变为可用状态

3、获取信号量(take)

c 复制代码
xSemaphoreTake(semphore_handle, portMAX_DELAY);

部分实验代码

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_handle;
void start_task( void * pvParameters );

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

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define TASK2_PRIO  3
#define TASK2_STACK_SIZE  128
TaskHandle_t task2_handle;
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_handle);

    //开启任务调度器
		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_handle);
	xTaskCreate( (TaskFunction_t        )     task2,
								 (char *                )   "task2",
								 (configSTACK_DEPTH_TYPE)   TASK2_STACK_SIZE,
								 (void *                )   NULL,   
								 (UBaseType_t           )   TASK2_PRIO,
								 (TaskHandle_t *        )   &task2_handle);
	
  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;
	while(1)
	{
		xSemaphoreTake(semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
			
		printf("获取信号量成功:%d!!\r\n",++i);
	}
}

七、计数型信号量实验(掌握)

实验简介

实验现象

  1. 信号量创建
  • 创建最大计数值为100的计数型信号量

  • 初始计数值为0

  1. 任务分工
  • Task1(优先级2):监听按键,释放信号量

  • Task2(优先级3):获取信号量,打印计数值

  1. 工作流程
  • Task2在xSemaphoreTake()处阻塞等待,因为初始计数为0

  • 用户按下KEY0按键,Task1调用xSemaphoreGive()释放信号量,计数+1

  • Task2获取到信号量,计数-1,打印当前计数值

  • Task2延时1秒后继续循环

部分实验代码

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 <stdio.h>
#include <stdint.h>
#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_handle;
void start_task( void * pvParameters );

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

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

/* 计数型信号量句柄 */
SemaphoreHandle_t Count_semphore_handle;

/* 统计信息 */
static uint32_t semaphore_give_count = 0;      /* 释放次数统计 */
static uint32_t semaphore_take_count = 0;      /* 获取次数统计 */

/******************************************************************************************************/

/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    /* 创建计数型信号量:最大计数100,初始值0 */
    Count_semphore_handle = xSemaphoreCreateCounting(100, 0);
    if(Count_semphore_handle != NULL)
    {
        printf("计数型信号量创建成功\r\n");
        printf("最大计数值: 100, 初始计数值: 0\r\n");
    }
    else
    {
        printf("计数型信号量创建失败\r\n");
        return;
    }

    /* 创建启动任务 */
    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_handle);

    /* 开启任务调度器 */
    vTaskStartScheduler();
}

void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();                     /* 进入临界区,任务切换不会进行 */
    
    /* 创建任务1:信号量释放任务 */
    xTaskCreate( (TaskFunction_t        )     task1,
                 (char *                )   "semaphore_give_task",
                 (configSTACK_DEPTH_TYPE)   TASK1_STACK_SIZE,
                 (void *                )   NULL,   
                 (UBaseType_t           )   TASK1_PRIO,
                 (TaskHandle_t *        )   &task1_handle);
    
    /* 创建任务2:信号量获取任务 */                 
    xTaskCreate( (TaskFunction_t        )     task2,
                 (char *                )   "semaphore_take_task",
                 (configSTACK_DEPTH_TYPE)   TASK2_STACK_SIZE,
                 (void *                )   NULL,   
                 (UBaseType_t           )   TASK2_PRIO,
                 (TaskHandle_t *        )   &task2_handle);
    
    printf("所有任务创建完成\r\n");
    printf("按下KEY0释放信号量\r\n");
    
    vTaskDelete( NULL );                      /* 删除启动任务 */
    taskEXIT_CRITICAL();                      /* 退出临界区,才会开始任务切换 */
}

/* 任务一,释放计数型信号量 */
void task1(void * pvParameters)
{
    uint8_t key = 0;
    BaseType_t result;
    
    while(1)
    { 
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            if(Count_semphore_handle != NULL)
            {
                result = xSemaphoreGive(Count_semphore_handle);  /* 释放信号量 */
                if(result == pdPASS)
                {
                    semaphore_give_count++;
                    printf("释放成功 [第%lu次] 当前计数值: %u\r\n", 
                           semaphore_give_count, 
                           (unsigned int)uxSemaphoreGetCount(Count_semphore_handle));
                }
                else
                {
                    printf("释放失败 - 信号量已达最大值\r\n");
                }
            }
        }

        vTaskDelay(10);  /* 10ms延时,避免按键抖动 */
    }
}

/* 任务二,获取计数型信号量 */
void task2(void * pvParameters)
{
    BaseType_t result;
    
    while(1)
    {
        /* 获取信号量,最大等待时间5秒 */
        result = xSemaphoreTake(Count_semphore_handle, pdMS_TO_TICKS(5000));
        
        if(result == pdPASS)
        {
            semaphore_take_count++;
            printf("获取成功 [第%lu次] 剩余计数值: %u\r\n", 
                   semaphore_take_count,
                   (unsigned int)uxSemaphoreGetCount(Count_semphore_handle));
                   
            /* 显示统计信息 */
            printf("统计 - 释放: %lu次, 获取: %lu次\r\n", 
                   semaphore_give_count, semaphore_take_count);
        }
        else
        {
            printf("获取超时 - 5秒内未获取到信号量\r\n");
        }
        
        vTaskDelay(pdMS_TO_TICKS(1000));  /* 1000ms延时 */
    }
}

八、优先级翻转实验(掌握)

实验简介

实验现象


部分实验代码

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_handle;
void start_task( void * pvParameters );

/* low_task 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define low_task_PRIO  2
#define low_task_STACK_SIZE  128
TaskHandle_t low_task_handle;
void low_task(void * pvParameters);

/* middle_task 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define middle_task_PRIO  3
#define middle_task_STACK_SIZE  128
TaskHandle_t middle_task_handle;
void middle_task(void * pvParameters);

/* high_task 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define high_task_PRIO  4
#define high_task_STACK_SIZE  128
TaskHandle_t high_task_handle;
void high_task(void * pvParameters);






/******************************************************************************************************/
QueueHandle_t semaphore_handle;
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
	  semaphore_handle = xSemaphoreCreateBinary();  /*创建二值信号量后资源数为0*/
		if(semaphore_handle != NULL)
		{
			printf("二值信号量创建成功!!!\r\n");
		}
		xSemaphoreGive(semaphore_handle);
		
		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_handle);

    //开启任务调度器
		vTaskStartScheduler();
}

void start_task( void * pvParameters )
{
	taskENTER_CRITICAL();                     /*进入临界区,任务切换不会进行*/
	xTaskCreate( (TaskFunction_t        )     low_task,
								 (char *                )   "low_task",
								 (configSTACK_DEPTH_TYPE)   low_task_STACK_SIZE,
								 (void *                )   NULL,   
								 (UBaseType_t           )   low_task_PRIO,
								 (TaskHandle_t *        )   &low_task_handle);
	xTaskCreate( (TaskFunction_t        )     middle_task,
								 (char *                )   "middle_task",
								 (configSTACK_DEPTH_TYPE)   middle_task_STACK_SIZE,
								 (void *                )   NULL,   
								 (UBaseType_t           )   middle_task_PRIO,
								 (TaskHandle_t *        )   &middle_task_handle);
	xTaskCreate( (TaskFunction_t        )     high_task,
								 (char *                )   "high_task",
								 (configSTACK_DEPTH_TYPE)   high_task_STACK_SIZE,
								 (void *                )   NULL,   
								 (UBaseType_t           )   high_task_PRIO,
								 (TaskHandle_t *        )   &high_task_handle);
	
  vTaskDelete( NULL );
 taskEXIT_CRITICAL();                       /*退出临界区,才会开始任务切换*/
	/*
	简单而言,临界区保护,就是保护那些不想被打断的从程序段							 
	*/							 
						 
	
}

/* 任务一,低优先级 */
void low_task(void * pvParameters)
{
	while(1)
	{ 
		printf("low_task is taking semaphore\r\n");
		xSemaphoreTake(semaphore_handle,portMAX_DELAY); 
		printf("low_task is running\r\n");
		delay_ms(3000);
		printf("low_task is giving semaphore\r\n");
		xSemaphoreGive(semaphore_handle);

		vTaskDelay(1000);
	}
}

/* 任务二,中优先级 */
void middle_task(void * pvParameters)
{
	while(1)
	{
		printf("middle_task is running\r\n");
		vTaskDelay(1000);
	}
}

/* 任务三,高优先级 */
void high_task(void * pvParameters)
{
	while(1)
	{
		printf("high_task is taking semaphore\r\n");
		xSemaphoreTake(semaphore_handle,portMAX_DELAY); 
		printf("high_task is running\r\n");
		delay_ms(1000);
		printf("high_task is giving semaphore\r\n");
		xSemaphoreGive(semaphore_handle);

		vTaskDelay(10);
	}
}

九、互斥信号量实验(掌握)

实验简介

实验现象

解决M打断L执行,从而使得高优先级等待更久时间的问题。

部分实验代码

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_handle;
void start_task( void * pvParameters );

/* low_task 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define low_task_PRIO  2
#define low_task_STACK_SIZE  128
TaskHandle_t low_task_handle;
void low_task(void * pvParameters);

/* middle_task 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define middle_task_PRIO  3
#define middle_task_STACK_SIZE  128
TaskHandle_t middle_task_handle;
void middle_task(void * pvParameters);

/* high_task 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define high_task_PRIO  4
#define high_task_STACK_SIZE  128
TaskHandle_t high_task_handle;
void high_task(void * pvParameters);






/******************************************************************************************************/
QueueHandle_t mutex_semaphore_handle;
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
	  mutex_semaphore_handle = xSemaphoreCreateMutex();  /*创建互斥信号量,并且主动释放一次信号量*/
		if(mutex_semaphore_handle != NULL)
		{
			printf("互斥信号量创建成功!!!\r\n");
		}
		//xSemaphoreGive(mutex_semaphore_handle);  //互斥信号量在创建的时候默认自动释放信号量
		
		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_handle);

    //开启任务调度器
		vTaskStartScheduler();
}

void start_task( void * pvParameters )
{
	taskENTER_CRITICAL();                     /*进入临界区,任务切换不会进行*/
	xTaskCreate( (TaskFunction_t        )     low_task,
								 (char *                )   "low_task",
								 (configSTACK_DEPTH_TYPE)   low_task_STACK_SIZE,
								 (void *                )   NULL,   
								 (UBaseType_t           )   low_task_PRIO,
								 (TaskHandle_t *        )   &low_task_handle);
	xTaskCreate( (TaskFunction_t        )     middle_task,
								 (char *                )   "middle_task",
								 (configSTACK_DEPTH_TYPE)   middle_task_STACK_SIZE,
								 (void *                )   NULL,   
								 (UBaseType_t           )   middle_task_PRIO,
								 (TaskHandle_t *        )   &middle_task_handle);
	xTaskCreate( (TaskFunction_t        )     high_task,
								 (char *                )   "high_task",
								 (configSTACK_DEPTH_TYPE)   high_task_STACK_SIZE,
								 (void *                )   NULL,   
								 (UBaseType_t           )   high_task_PRIO,
								 (TaskHandle_t *        )   &high_task_handle);
	
  vTaskDelete( NULL );
 taskEXIT_CRITICAL();                       /*退出临界区,才会开始任务切换*/
	/*
	简单而言,临界区保护,就是保护那些不想被打断的从程序段							 
	*/							 
						 
	
}

/* 任务一,低优先级 */
void low_task(void * pvParameters)
{
	while(1)
	{ 
		printf("low_task is taking semaphore\r\n");
		xSemaphoreTake(mutex_semaphore_handle,portMAX_DELAY); 
		printf("low_task is running\r\n");
		delay_ms(3000);
		printf("low_task is giving semaphore\r\n");
		xSemaphoreGive(mutex_semaphore_handle);

		vTaskDelay(1000);
	}
}

/* 任务二,中优先级 */
void middle_task(void * pvParameters)
{
	while(1)
	{
		printf("middle_task is running\r\n");
		vTaskDelay(1000);
	}
}

/* 任务三,高优先级 */
void high_task(void * pvParameters)
{
	while(1)
	{
		printf("high_task is taking semaphore\r\n");
		xSemaphoreTake(mutex_semaphore_handle,portMAX_DELAY); 
		printf("high_task is running\r\n");
		delay_ms(1000);
		printf("high_task is giving semaphore\r\n");
		xSemaphoreGive(mutex_semaphore_handle);

		vTaskDelay(10);
	}
}

十、课堂总结





相关推荐
饺子大魔王的男人3 小时前
【Three.js】机器人管线包模拟
javascript·机器人
知否技术3 小时前
知道这10个npm工具包,开发效率提高好几倍!第2个大家都用过!
前端·npm
希希不嘻嘻~傻希希4 小时前
CSS 字体与文本样式笔记
开发语言·前端·javascript·css·ecmascript
石小石Orz4 小时前
分享10个吊炸天的油猴脚本,2025最新!
前端
爷_5 小时前
Nest.js 最佳实践:异步上下文(Context)实现自动填充
前端·javascript·后端
爱上妖精的尾巴5 小时前
3-19 WPS JS宏调用工作表函数(JS 宏与工作表函数双剑合壁)学习笔记
服务器·前端·javascript·wps·js宏·jsa
草履虫建模5 小时前
Web开发全栈流程 - Spring boot +Vue 前后端分离
java·前端·vue.js·spring boot·阿里云·elementui·mybatis
—Qeyser5 小时前
让 Deepseek 写电器电费计算器(html版本)
前端·javascript·css·html·deepseek
UI设计和前端开发从业者6 小时前
从UI前端到数字孪生:构建数据驱动的智能生态系统
前端·ui
Junerver7 小时前
Kotlin 2.1.0的新改进带来哪些改变
前端·kotlin