FreeRTOS--信号量
一、信号量介绍
信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问。
本质:
- 信号量是一个 计数器,用于控制多任务对共享资源或事件响应的访问。
- 通过“获取”(Take)和“释放”(Give)操作管理任务间的协作,避免资源冲突或竞争条件。
1.1 举个例子:有个人需要在一个停车场停车
1)首先判断是否存在可用停车位(判断是否有信号量资源)。
2)如果存在可用停车位(存在信号量资源),那么就可以直接进去停车(获取信号量资源)
3)如果不存在可用停车位(没有信号量资源),这种情况有两种选择:
第1: 不停了(获取信号量资源失败)
第2:继续等待(阻塞)其他人开走空出车位(释放信号量资源)
- 计数值为 0 时,代表没有信号量资源
- 获取信号量资源,计数值减 1
- 释放信号量资源,计数值加 1
1.2 信号量与队列的对比
信号量 | 队列 | |
存储结构 | 仅存放计数值,无法存放其他数据。 创建信号量,只需分配信号量结构体。 | 可以容纳多个数据。 创建队列有两部分内存:队列结构体+队列项存储空间。 |
写操作 | 释放信号量:不可阻塞,计数值++, 当计数值为最大值时,返回失败 | 写入队列:当队列满时,可阻塞。 |
读操作 | 获取信号量:计数值--,当没有资源时,可阻塞 | 读取队列:当队列为空时,可阻塞。 |
- 信号量的计数值通过队列机制中的核心字段
uxMessagesWaiting (
记录队列中当前消息的数量)
实现。 - 队列长度的值(
uxLength
)在信号量中被用于表示 最大可用资源数量。 - 信号量不存储实际数据,因此创建时队列的
uxItemSize
(数据项大小)被设为 0,pcHead
(队列存储区指针)指向空
注:信号量是基于队列实现的。其底层机制与队列共享相同的代码结构,可以任务信号量是队列移除了数据存储功能,仅保留计数和同步逻辑。
二、二值信号量
1. 介绍
在前面说过,FreeRTOS中说过信号量是基于队列实现的,所以二值信号量的本质是一个队列长度为 1 的队列 ,该队列就只有空和满两种情况。
应用场景:
二值信号量通常用于互斥访问或任务同步, 与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题 ,所以二值信号量更适合用于同步!
2. API函数
函数 | 描述 |
xSemaphoreCreateBinary() | 使用动态方式创建二值信号量 |
xSemaphoreCreateBinaryStatic() | 使用静态方式创建二值信号量 |
xSemaphoreGive() | 释放信号量 |
xSemaphoreGiveFromISR() | 在中断中释放信号量 |
xSemaphoreTake() | 获取信号量 |
xSemaphoreTakeFromISR() | 在中断中获取信号量 |
2.1 创建二值信号量
1)函数原型
/* 动态方式 */
xSemaphoreCreateBinary()
该函数没有任何参数,其实这是一个对于队列动态创建函数的宏定义,如下:
/* 宏定义 */
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
#endif/* xQueueGenericCreate() 函数原型 */
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,const UBaseType_t uxItemSize,const uint8_t ucQueueType )
2) xQueueGenericCreate 参数说明:
- uxQueueLength:队列长度,在信号量中表示最大计数值,二值信号量的计数值为 1。
- uxItemSize:定义每个数据项的大小(字节数),在信号量中大小为为 semSEMAPHORE_QUEUE_ITEM_LENGTH(宏定义为 0)。
- ucQueueType :标识队列类型(普通队列、信号量、互斥量等)
ucQueueType 取值:
#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 ) /* 递归互斥信号量 */
3)
返回值 | 描述 |
NULL | 创建失败 |
其他值 | 创建成功返回二值信号量的句柄 |
2.2 释放二值信号量
1)函数原型
xSemaphoreGive( xSemaphore )
该函数也是个宏定义,如下:
/* 宏定义 */
#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )/* xQueueGenericSend() 函数原型 */
BaseType_t xQueueGenericSend( QueueHandle_t xQueue,const void * const pvItemToQueue,TickType_t xTicksToWait,const BaseType_t xCopyPosition )
当用于信号量时(通过 xSemaphoreGive
调用),xQueueGenericSend()
会将信号量的计数器(uxMessagesWaiting
)原子性地加 1,并唤醒等待该信号量的任务。
2)参数说明:
- xSemaphore :要释放的信号量句柄
- xQueue:信号量的队列句柄(强制转换为队列类型)
- pvItemToQueue:发送的数据指针。普通队列需传递数据地址,信号量设为
NULL
- xTicksToWait:阻塞超时时间。信号量释放通常设为
0
(不阻塞) - xCopyPosition :信号量固定为
queueSEND_TO_BACK
3)
返回值 | 描述 |
pdPASS | 释放信号量成功 |
errQUEUE_FULL | 释放信号量失败 |
2.3 获取二值信号量
1)函数原型
xSemaphoreTake( xSemaphore, xBlockTime )
函数宏定义:
/* 宏定义 */
#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )/* xQueueSemaphoreTake() 函数原型 */
BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue,TickType_t xTicksToWait )
xQueueSemaphoreTake() 函数作用:尝试获取信号量资源,若成功则信号量计数器减 1,失败则根据参数阻塞或立即返回。
2)参数说明:
- xSemaphore:信号量的句柄,获取成功计数值减一
- xTicksToWait :阻塞超时时间
3)
返回值 | 描述 |
pdTRUE | 获取信号量成功 |
pdFALSE | 超时,获取信号量失败 |
3. 代码示例
任务 | 描述 |
start_task | 创建其他任务和二值信号量 |
task1 | 通过按键去释放资源 |
task2 | 获取资源 |
main.c 文件:
#include "malloc.h"
#include "freertos_demo.h"
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "usmart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"int main(void)
{HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72); /* 延时初始化 */usart_init(115200); /* 串口初始化为115200 */led_init(); /* 初始化LED */lcd_init(); /* 初始化LCD */key_init(); /* 初始化按键 */my_mem_init(SRAMIN); /* 初始化内部SRAM内存池 */freertos_demo(); /* 初始化起始任务,启动任务调度器 */
}
freertos_demo.c 文件:
#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"/*FreeRTOS******************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"/***************************************************************************************//* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK_STACK_SIZE 256
#define TASK_PRIORITY 1
TaskHandle_t start_task_handle;
void start_task(void *pvParameters);/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_STACK_SIZE 256
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
void task1(void *pvParameters);/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_STACK_SIZE 256
#define TASK2_PRIORITY 3
TaskHandle_t task2_handle;
void task2(void *pvParameters);/***************************************************************************************//* 在main函数中调用 */
void freertos_demo(void)
{xTaskCreate((TaskFunction_t )start_task, /*函数指针,函数入口*/(char * )"start_task", /*任务名字*/(configSTACK_DEPTH_TYPE)TASK_STACK_SIZE, /*任务堆栈的大小*/(void * )NULL, /*任务参数*/(UBaseType_t )TASK_PRIORITY, /*任务优先级*/(TaskHandle_t * )&start_task_handle /*任务句柄*/);vTaskStartScheduler();
}QueueHandle_t semphr_handle;
/* 起始任务:创建二值信号量和任务1,任务2 */
void start_task(void *pvParameters)
{taskENTER_CRITICAL(); /* 进入临界区 *//* 创建二值信号量 */semphr_handle = xSemaphoreCreateBinary();if(semphr_handle != NULL) printf("二值信号量创建成功\r\n");else printf("二值信号量创建失败d\r\n");/* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(char * )"task1",(configSTACK_DEPTH_TYPE)TASK1_STACK_SIZE,(void * )NULL,(UBaseType_t )TASK1_PRIORITY,(TaskHandle_t * )&task1_handle);/* 创建任务2 */xTaskCreate((TaskFunction_t )task2,(char * )"task2",(configSTACK_DEPTH_TYPE)TASK2_STACK_SIZE,(void * )NULL,(UBaseType_t )TASK2_PRIORITY,(TaskHandle_t * )&task2_handle);taskEXIT_CRITICAL();vTaskDelete(NULL); /*删除当前任务*/
}/* 任务1:通过按键释放二值信号量 */
void task1(void *pvParameters)
{uint8_t key_val;while(1){key_val = key_scan(0);if(key_val == KEY0_PRES){printf("释放二值信号量\r\n");xSemaphoreGive(semphr_handle);}vTaskDelay(10);}
}/* 任务2:获取二值信号量资源 */
void task2(void *pvParameters)
{uint32_t cnt = 0;while(1){printf("开始获取二值信号量资源\r\n");if(xSemaphoreTake(semphr_handle,1000) != pdFALSE ) {printf("二值信号量获取成功\r\n");cnt = 0;}else printf("获取失败: %d\r\n",++cnt);vTaskDelay(10);}
}
三、计数型信号量
1. 介绍
计数型信号量相当于队列长度大于1 的队列,因此计数型信号量能够容纳多个资源
应用场景:
- 事件计数:当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务会获取计数型信号量(计数值-1) ,这种场合一般在创建时将初始计数值设置为 0
- 资源管理:信号量表示有效的资源数目。任务必须先获取信号量(信号量计数值-1 )才能获取资源控制权。当计数值减为零时表示没有的资源。当任务使用完资源后,必须释放信号量(信号量计数值+1)。信号量创建时计数值应等于最大资源数目
2. API函数
函数 | 描述 |
xSemaphoreCreateCounting() | 使用动态方法创建计数型信号量 |
xSemaphoreCreateCountingStatic() | 使用静态方法创建计数型信号量 |
uxSemaphoreGetCount() | 获取信号量的计数值 |
注意:计数型信号量的释放和获取与二值信号量相同 !
所以下面只说明创建函数。
2.1 创建计数型信号量
1)函数原型
/* 宏定义 */
#define xSemaphoreCreateCounting( uxMaxCount , uxInitialCount ) \
xQueueCreateCountingSemaphore( ( uxMaxCount ) , ( uxInitialCount ) )/* 函数原型 */ QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount,const UBaseType_t uxInitialCount )
可以看出该函数也是个宏定义。
2)参数说明:
- uxMaxCount:计数值的最大值限定
- uxInitialCount:计数值的初始值
3)
返回值 | 描述 |
NULL | 创建失败 |
其他值 | 创建成功返回计数型信号量的句柄 |
2.2 获取当前计数值
1)函数原型
/* 宏定义 */
#define uxSemaphoreGetCount( xSemaphore ) \uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )/* 函数原型 */
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue )
也是个宏定义
2)参数说明:
- xSemaphore:信号量句柄
3)
返回值 | 描述 |
整数 | 当前信号量的计数值大小 |
3. 代码示例
任务 | 描述 |
start_task | 创建其他任务和计数型信号量 |
task1 | 通过按键释放资源 |
task2 | 获取资源 |
freertos_demo.c 文件
#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
/*FreeRTOS******************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/***************************************************************************************//* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK_STACK_SIZE 256
#define TASK_PRIORITY 1
TaskHandle_t start_task_handle;
void start_task(void *pvParameters);/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_STACK_SIZE 256
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
void task1(void *pvParameters);/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_STACK_SIZE 256
#define TASK2_PRIORITY 3
TaskHandle_t task2_handle;
void task2(void *pvParameters);/***************************************************************************************/void freertos_demo(void)
{xTaskCreate((TaskFunction_t )start_task, /*函数指针,函数入口*/(char * )"start_task", /*任务名字*/(configSTACK_DEPTH_TYPE)TASK_STACK_SIZE, /*任务堆栈的大小*/(void * )NULL, /*任务参数*/(UBaseType_t )TASK_PRIORITY, /*任务优先级*/(TaskHandle_t * )&start_task_handle /*任务句柄*/);vTaskStartScheduler();
}/*** @brief start_task* @param pvParameters : 无* @retval 无*/
QueueHandle_t countSemphr_Handle;
void start_task(void *pvParameters)
{taskENTER_CRITICAL(); /* 进入临界区 *//* 创建计数型信号量 */countSemphr_Handle = xSemaphoreCreateCounting(100,0);if(countSemphr_Handle != NULL){printf("信号量创建成功\r\n");}else printf("信号量创建失败\r\n");/* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(char * )"task1",(configSTACK_DEPTH_TYPE)TASK1_STACK_SIZE,(void * )NULL,(UBaseType_t )TASK1_PRIORITY,(TaskHandle_t * )&task1_handle);/* 创建任务2 */xTaskCreate((TaskFunction_t )task2,(char * )"task2",(configSTACK_DEPTH_TYPE)TASK2_STACK_SIZE,(void * )NULL,(UBaseType_t )TASK2_PRIORITY,(TaskHandle_t * )&task2_handle);taskEXIT_CRITICAL(); /* 退出临界区 */vTaskDelete(NULL); /*删除当前任务*/
}void task1(void *pvParameters)
{uint8_t keyVal;while(1){keyVal = key_scan(0);if(keyVal == KEY0_PRES){ /* 释放信号量 */ xSemaphoreGive(countSemphr_Handle);}vTaskDelay(10);}
}void task2(void *pvParameters)
{while(1){xSemaphoreTake(countSemphr_Handle,portMAX_DELAY); /* 获取信号量 *//* 打印剩余资源数 */printf("剩余计数值:%d\r\n",uxSemaphoreGetCount(countSemphr_Handle));vTaskDelay(1000);}
}
四、优先级翻转
1.介绍
优先级翻转:高优先级的任务反而慢执行,低优先级的任务反而优先执行
- 优先级翻转在抢占式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果。
- 在使用二值信号量的时候,经常会遇到优先级翻转的问题。
示意图如下:
任务H,任务M,任务L的优先级依次为高,中,低。
高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上看,就像是中优先级的任务比高优先级任务具有更高的优先权(即优先级翻转)
2. 代码示例
任务 | 描述 |
start_task | 创建其他任务和二值信号量 |
task1 | 低优先级任务,竞争二值信号量资源 |
task2 | 中优先级任务,正常打印 |
task3 | 高优先级任务,竞争二值信号量资源 |
#include "freertos_demo.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "lcd.h"
#include "key.h"/*FreeRTOS******************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/***************************************************************************************//* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK_STACK_SIZE 256
#define TASK_PRIORITY 1
TaskHandle_t start_task_handle;
void start_task(void *pvParameters);/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_STACK_SIZE 256
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
void task1(void *pvParameters);/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_STACK_SIZE 256
#define TASK2_PRIORITY 3
TaskHandle_t task2_handle;
void task2(void *pvParameters);/* TASK3 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK3_STACK_SIZE 256
#define TASK3_PRIORITY 4
TaskHandle_t task3_handle;
void task3(void *pvParameters);/***************************************************************************************/void freertos_demo(void)
{xTaskCreate((TaskFunction_t )start_task, /*函数指针,函数入口*/(char * )"start_task", /*任务名字*/(configSTACK_DEPTH_TYPE)TASK_STACK_SIZE, /*任务堆栈的大小*/(void * )NULL, /*任务参数*/(UBaseType_t )TASK_PRIORITY, /*任务优先级*/(TaskHandle_t * )&start_task_handle /*任务句柄*/);vTaskStartScheduler();
}/*** @brief start_task* @param pvParameters : 无* @retval 无*/
QueueHandle_t semphr_Handle;
void start_task(void *pvParameters)
{taskENTER_CRITICAL(); /* 进入临界区 *//* 创建二值信号量,并且释放资源 */semphr_Handle = xSemaphoreCreateBinary();if(semphr_Handle != NULL){xSemaphoreGive(semphr_Handle);printf("二值信号量创建成功\r\n");}else printf("二值信号量创建失败\r\n");/* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(char * )"task1",(configSTACK_DEPTH_TYPE)TASK1_STACK_SIZE,(void * )NULL,(UBaseType_t )TASK1_PRIORITY,(TaskHandle_t * )&task1_handle);/* 创建任务2 */xTaskCreate((TaskFunction_t )task2,(char * )"task2",(configSTACK_DEPTH_TYPE)TASK2_STACK_SIZE,(void * )NULL,(UBaseType_t )TASK2_PRIORITY,(TaskHandle_t * )&task2_handle);/* 创建任务3 */xTaskCreate((TaskFunction_t )task3,(char * )"task3",(configSTACK_DEPTH_TYPE)TASK3_STACK_SIZE,(void * )NULL,(UBaseType_t )TASK3_PRIORITY,(TaskHandle_t * )&task3_handle);taskEXIT_CRITICAL(); /* 退出临界区 */vTaskDelete(NULL); /* 删除当前任务 */
}/* 任务1:低优先级任务,获取二值信号量资源 */
void task1(void *pvParameters)
{while(1){printf("任务1:开始获取二值信号量\r\n");xSemaphoreTake(semphr_Handle,portMAX_DELAY);printf("任务1:获取二值信号量成功\r\n");delay_ms(3000);xSemaphoreGive(semphr_Handle);vTaskDelay(1000); }
}/* 任务2:中优先级任务,正常运行 */
void task2(void *pvParameters)
{while(1){printf("任务2正在运行\r\n");vTaskDelay(500);}
}/* 任务3:高优先级任务,获取二值信号量资源 */
void task3(void *pvParameters)
{while(1){printf("任务3:开始获取二值信号量\r\n");xSemaphoreTake(semphr_Handle,portMAX_DELAY);xSemaphoreGive(semphr_Handle);printf("任务3:获取二值信号量成功\r\n");vTaskDelay(1000); }}
五、互斥信号量
1. 介绍
互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中!
优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。(作用时间是高优先级任务因为获取不到资源而的阻塞时间)
使用互斥量后的示意图:
可以看到任务M不能够抢占任务L,因为此时任务L继承了任务H的优先级。
此时任务H的阻塞时间仅仅是任务L 的执行时间,将优先级翻转的危害降到了最低 ,并不能完全消除任务翻转。
注意:互斥信号量不能用于中断服务函数中,原因如下:
-
互斥信号量有任务优先级继承的机制, 但是中断不是任务,没有任务优先级, 所以互斥信号量只能用与任务中,不能用于中断服务函数。
-
中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。
2. API函数
使用互斥信号量:首先将宏 configUSE_MUTEXES 置 1
函数 | 描述 |
xSemaphoreCreateMutex() | 使用动态方法创建互斥信号量。 |
xSemaphoreCreateMutexStatic() | 使用静态方法创建互斥信号量。 |
互斥信号量的释放和获取函数与二值信号量相同 !只不过互斥信号量不支持中断中调用
注意:创建互斥信号量时,会主动释放一次信号量
2.1 创建互斥信号量
1)函数原型
/* 宏定义 */
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
#endif/* xQueueCreateMutex() 函数原型 */
QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )
2)
返回值 | 描述 |
NULL | 创建失败 |
其他值 | 创建成功返回互斥信号量的句柄 |
3. 代码示例
任务 | 描述 |
start_task | 创建其他任务和二值信号量 |
task1 | 低优先级任务,竞争二值信号量资源 |
task2 | 中优先级任务,正常打印 |
task3 | 高优先级任务,竞争二值信号量资源 |
使用互斥量可以最大程度的降低优先级翻转的危害。
freertos_demo.c 文件
#include "freertos_demo.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
/*FreeRTOS******************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/***************************************************************************************//* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK_STACK_SIZE 256
#define TASK_PRIORITY 1
TaskHandle_t start_task_handle;
void start_task(void *pvParameters);/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_STACK_SIZE 256
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
void task1(void *pvParameters);/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_STACK_SIZE 256
#define TASK2_PRIORITY 3
TaskHandle_t task2_handle;
void task2(void *pvParameters);/* TASK3 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK3_STACK_SIZE 256
#define TASK3_PRIORITY 4
TaskHandle_t task3_handle;
void task3(void *pvParameters);/***************************************************************************************/void freertos_demo(void)
{xTaskCreate((TaskFunction_t )start_task, /*函数指针,函数入口*/(char * )"start_task", /*任务名字*/(configSTACK_DEPTH_TYPE)TASK_STACK_SIZE, /*任务堆栈的大小*/(void * )NULL, /*任务参数*/(UBaseType_t )TASK_PRIORITY, /*任务优先级*/(TaskHandle_t * )&start_task_handle /*任务句柄*/);vTaskStartScheduler();
}/*** @brief start_task* @param pvParameters : 无* @retval 无*/
QueueHandle_t semphr_Handle;
void start_task(void *pvParameters)
{taskENTER_CRITICAL(); /* 进入临界区 *//* 创建二值信号量,并且释放资源 */semphr_Handle = xSemaphoreCreateMutex();if(semphr_Handle != NULL){printf("互斥信号量创建成功\r\n");}else printf("互斥信号量创建失败\r\n");/* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(char * )"task1",(configSTACK_DEPTH_TYPE)TASK1_STACK_SIZE,(void * )NULL,(UBaseType_t )TASK1_PRIORITY,(TaskHandle_t * )&task1_handle);/* 创建任务2 */xTaskCreate((TaskFunction_t )task2,(char * )"task2",(configSTACK_DEPTH_TYPE)TASK2_STACK_SIZE,(void * )NULL,(UBaseType_t )TASK2_PRIORITY,(TaskHandle_t * )&task2_handle);/* 创建任务3 */xTaskCreate((TaskFunction_t )task3,(char * )"task3",(configSTACK_DEPTH_TYPE)TASK3_STACK_SIZE,(void * )NULL,(UBaseType_t )TASK3_PRIORITY,(TaskHandle_t * )&task3_handle);taskEXIT_CRITICAL(); /* 退出临界区 */vTaskDelete(NULL); /* 删除当前任务 */
}/* 低优先级任务,与高优先级任务竞争资源 */
void task1(void *pvParameters)
{while(1){printf("任务1:开始获取信号量\r\n");xSemaphoreTake(semphr_Handle,portMAX_DELAY);printf("任务1:获取信号量成功\r\n");delay_ms(3000);printf("任务1:释放信号量成功\r\n");xSemaphoreGive(semphr_Handle);vTaskDelay(1000); }
}/* 中等优先级任务,正常打印 */
void task2(void *pvParameters)
{while(1){printf("任务2正在运行\r\n");vTaskDelay(500);}
}/* 高优先级任务,获取二值信号量 */
void task3(void *pvParameters)
{while(1){printf("任务3:开始获取信号量\r\n");xSemaphoreTake(semphr_Handle,portMAX_DELAY);printf("任务3:获取信号量成功\r\n");xSemaphoreGive(semphr_Handle);printf("任务3:释放信号量\r\n");vTaskDelay(1000); }}