FreeRTOS实战:任务创建与调度详解
创建工程
选好芯片设置好时钟
注意SYS里原本的嘀嗒定时器已经给RTOS做时钟频率了所以这里我们选定时器8来做时钟频率
点开middleware 可以发现freertos
选择CMSIS_V2
配置好时钟频率
设置好工程
创建工程
打开
你可以发现main函数里多了这几行
osKernelInitialize();初始化任务调度器
osKernelStart();开启任务调度器
main函数的while(1)已经没用了,控制权已经给RTOS开始调度了
创建任务
点击Tasks and Queues
点击add
自行配置,这里默认,创建工程,打开工程,打开freertos可以发现,默认创建的任务defaultTaskHandle和我们自己创建的任务myTask02Handle
osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr);创建一个任务func:任务函数typedef void (*osThreadFunc_t) (void *argument);一个函数指针指向任务
void *argument:传递给任务函数的参数
const osThreadAttr_t *attr:任务属性
返回值osThreadId_t:任务的ID,可以通过任务ID找到任务
对应讲解:
StartDefaultTask为传递的函数,往下滑可以看到该任务函数
任务为死循环
我们可以看到一个新函数osDelay();
osDelay();休眠函数,释放CPU资源,根据指定时间休眠
ticks为你要休眠的时间
我们这里测试一下,让led500ms闪烁一次
串口重定向
配置好串口,再.c添加#include <stdio.h>头文件
/* USER CODE BEGIN 4 */
int fputc(int ch, FILE *f)
{HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);return ch;
}/*** @brief getchar,scanf USARTx* @retval None*/
int fgetc(FILE *f)
{uint8_t ch = 0;HAL_UART_Receive(&huart1, &ch, 1, 0xffff);return ch;
}
void _sys_exit(int x)
{x = x;
}/* 标准库需要的支持类型 */
struct __FILE
{int handle;
};FILE __stdout;
编写串口重定向函数
只要再别的文件加上#include <stdio.h>就可以使用串口重定向功能了
勾一下LIB库
任务参数讲解
查看创建任务的最后一位&defaultTask_attributes
可以看到该任务的参数:名字,栈大小,优先级大小,细心的同学可以发现我们配置时是128字
这里是128*4,实际上它帮我们转换为字节了(所以*4),所以任务默认栈大小为512字节
参数传递
假如我们要传递一个变量int型的cnt
我们要这样才可以传递值
任务优先级
这里可以看见这两个任务优先级:my02<默认任务
我们把这两个任务的休眠时间设置为一样,我们看谁先执行
可以看到任务优先级高的默认任务先执行
在cmsis_os2.h可以看见任务优先级是枚举来表示
任务优先级的数值越大代表任务的优先级越高,也就是任务越先被执行。
任务的栈的作用
存储地址,存储局部变量,保存函数调用的上下文(调用栈),保存寄存器状态,保存任务数据互不干扰
FREERTOS原生创建任务函数
osThreadNew()是stm32CubeMX封装的函数
我们在跳转到这个函数可以找到xTaskCreateStatic();和xTaskCreate();这两个函数,
xTaskCreateStatic()为静态创建任务,
xTaskCreate()为动态创建任务
动态创建任务用的比较多,我们先跳转到动态创建任务函数里面
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */const configSTACK_DEPTH_TYPE usStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask )
TaskFunction_t pxTaskCode:指向任务函数;
const char * const pcName:任务名字
const configSTACK_DEPTH_TYPE usStackDepth:栈的大小
void * const pvParameters:传递给任务函数的参数
UBaseType_t uxPriority:任务优先级
TaskHandle_t * const pxCreatedTas:任务的句柄,可以通过任务句柄找到任务
返回值BaseType_t:任务是否创建成功,创建成功返回pdTRUE,失败返回pdFALSE
动态创建任务和静态创建任务的区别
动态创建任务会动态创建内存,动态分配内存的使用和释放,静态创建任务会规定好一块内存给静态任务
我们看一下静态创建任务函数
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */const uint32_t ulStackDepth,void * const pvParameters,UBaseType_t uxPriority,StackType_t * const puxStackBuffer,StaticTask_t * const pxTaskBuffer )
TaskFunction_t pxTaskCode:指向任务函数;
const char * const pcName:任务名字
const uint32_t ulStackDepth:栈的大小
void * const pvParameters:传递给任务函数的参数
UBaseType_t uxPriority:任务优先级
StackType_t * const puxStackBuffer:静态创建任务栈的空间
StaticTask_t * const pxTaskBuffer:TCB控制块
任务调度算法:
抢占式调度:
我们把默认任务StartDefaultTask优先级调低,比StartTask02任务低
然后在StartDefaultTask里面创建StartTask02任务
程序在StartDefaultTask任务里运行,运行流程:
printf("StartDefaultTask start\r\n"); -->
创建任务2 StartTask02(因为任务2的优先级高所以先执行任务2)-->
printf("StartTask02\r\n");(再回到StartDefaultTask执行)-->
printf("StartDefaultTask end\r\n");
打开串口助手可以看见
前三行打印没问题,第四行会发现先打印任务2,因为我们创建了任务2,任务2的优先级高,先执行
时间片调度
我们把两个任务优先级设置一样
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {.name = "defaultTask",.stack_size = 128 * 4,.priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for myTask02 */
osThreadId_t myTask02Handle;
const osThreadAttr_t myTask02_attributes = {.name = "myTask02",.stack_size = 128 * 4,.priority = (osPriority_t) osPriorityNormal,
};
然后我们在默认任务里面这样写
时间片调度,相同优先级的任务享用同等时间片(1ms),我们在这里消耗完时间片,这时候会进入任务2执行
所以执行顺序:
任务1printf("StartDefaultTask start\r\n"); -->
for()把时间片消耗完了-->
任务2printf("StartTask02 start\r\n")-->
任务2printf("StartTask02 end\r\n")-->
任务1printf("StartDefaultTask end\r\n");
FreeRTOSConfig.h的作用
配置FREERTOS的模式
任务运行状态
空闲任务和钩子函数
开启RTOS调度器会自动创建空闲任务
空闲任务作用:
空闲任务的相关钩子函数
如果我们想自己编写空闲任务我们我们就要使能钩子函数我们要在FreeRTOSConfig.h里把
//开启空闲任务钩子函数
#define configUSE_IDLE_HOOK 1
void vApplicationIdleHook(void)
{//编写自己需要的空闲任务}
任务控制块(TCB)
在 FreeRTOS 中,任务控制块(Task Control Block,简称 TCB)是用来存储和管理任务信息的数据结构。每个任务在系统中都有一个对应的任务控制块,用于记录该任务的状态、优先级、堆栈指针、任务的其他属性等。任务控制块是任务管理的核心,FreeRTOS 使用它来调度和管理各个任务。
任务控制块(TCB)是一个结构体,用来存储一个任务在系统运行时的所有关键信息。每个任务都拥有一个TCB,FreeRTOS 调度器通过
这个 TCB 来跟踪任务的状态和控制任务的执行。
我们可以在tasks.c里面找到tskTCB的结构体


任务删除
在stm32CubeMX给我们封装了两个任务删除函数:
//删除任务自己
void osThreadExit(void)
//删除指定任务
osStatus_t osThreadTerminate(osThreadId_t thread_id)
原生的删除任务函数
void vTaskDelete(TaskHandle_t xTaskToDelete)TaskHandle_t xTaskToDelete:想要删除任务的ID,传入NULL时为删除自己
删除自己(原生函数),我们在默认任务这里进行试验
void StartDefaultTask(void *argument)
{/* USER CODE BEGIN StartDefaultTask *//* Infinite loop */for(;;){for(uint8_t i;i < 5;i++)//循环发送5次{printf("StartDefaultTask\r\n");}vTaskDelete(NULL);//删除自己HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);osDelay(500);}/* USER CODE END StartDefaultTask */
}
删除其他任务实验
void StartDefaultTask(void *argument)
{/* USER CODE BEGIN StartDefaultTask *//* Infinite loop */for(;;){uint16_t i;if(i == 5)//执行5次后删除任务2{vTaskDelete(myTask02Handle);}printf("StartDefaultTask\r\n"); HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);osDelay(500);}/* USER CODE END StartDefaultTask */
}/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{/* USER CODE BEGIN StartTask02 *//* Infinite loop */for(;;){printf("StartTask02\r\n");osDelay(500);}/* USER CODE END StartTask02 */
}
实验现象:
串口打印5次后删除任务2
队列
在 FreeRTOS 中,队列(Queue)是一种非常重要的数据结构,用于在任务之间进行通信和数据传递。它能够提供线程安全的消息传递机制,是多任务操作系统中的常见工具。
队列可以起到传递数据,也可以起到任务通知的作用
创建队列
打开CubeMX,点击Task and Queues
点击add
根据所需进行创建,然后生成工程
我们在freertos.c里面可以找到我们创建好的队列
我们进入osMessageQueueNew,可以知道这个函数是STM32CubeMX给我们封装的函数,
osMessageQueueId_t osMessageQueueNew (uint32_t msg_count, uint32_t msg_size, const osMessageQueueAttr_t *attr)
osMessageQueueId_t:返回值为队列的ID
uint32_t msg_count:队列容量
uint32_t msg_size:元素大小
const osMessageQueueAttr_t *attr:队列属性👇
/// Attributes structure for message queue.
typedef struct {const char *name; ///< name of the message queueuint32_t attr_bits; ///< attribute bitsvoid *cb_mem; ///< memory for control blockuint32_t cb_size; ///< size of provided memory for control blockvoid *mq_mem; ///< memory for data storageuint32_t mq_size; ///< size of provided memory for data storage
} osMessageQueueAttr_t;
我们在freertos上面可以看见,队列属性的结构体只用到了名字
rtos原生创建队列函数
也是分为动态创建xQueueCreate和静态创建xQueueCreateStatic
我们查看一下动态创建队列xQueueCreate函数:
xQueueCreate( uxQueueLength, uxItemSize )
返回值:对列ID
uxQueueLength:队列长度
uxItemSize:每个元素大小
队列创建过程:
我们打开queue.c可以找到xQueueGenericCreate这个函数,先定义了一个队列指针pxNewQueue,再用pvPortMalloc();给队列分配一个空间pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );
xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize );
xQueueSizeInBytes:队列总大小
uxQueueLength:队列容量
uxItemSize:元素个数
队列总大小=队列容量*元素个数
我们发现pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );
还有个Queue_t,Queue_t :队列头部信息
我们跳转到Queue_t 里面查看,我们要用这个头部信息来管理队列
实际上的队列结构
队列使用
使用队列前先要学一下信号量
二进制信号量(保护共享资源)
共享资源:全局变量,串口,定时器
计数型信号量(事件计数和多资源管理)
STM32Cubemx创建信号量
打开STM32Cubemx,点击Timers and Semaphores,我们可以看见Bin ary Semaphores(二值信号量)和Counting Semaphores(计数信号量)
我们在Bin ary Semaphores(二值信号量)点击添加
添加Counting Semaphores(计数信号量)
创建完这两个信号量,我们打开工程
我们可以在freertos里面找到
我们进入这个STM32CubeMX封装好的函数
osSemaphoreId_t osSemaphoreNew (uint32_t max_count, uint32_t initial_count, const osSemaphoreAttr_t *attr)
返回值:信号量ID
uint32_t max_count:信号量的最大值,(二进制信号量最大为1(0和1))(计数信号量值一般大于等于2)
uint32_t initial_count:信号量初始值
const osSemaphoreAttr_t *attr: 信号量的属性(我们在上面可以发现这个属性只用到了名字)
我们看一下
RTOS自带的创建信号量函数:
我们先看动态创建二进制信号量
xSemaphoreCreateBinary();
返回值:ID
动态创建计数型信号量
xSemaphoreCreateCounting (max_count, initial_count);
uxMaxCount:最大值
uxInitialCount:初始值
请求信号量(请求完成信号量后,信号量减1)
osStatus_t osSemaphoreAcquire (osSemaphoreId_t semaphore_id, uint32_t timeout)
返回值:是否请求成功
osSemaphoreId_t semaphore_id, uint32_t timeout:从哪个信号量请求
uint32_t timeout: 超时时间,超过超时时间返回请求失败
释放信号量(释放完成信号量后,信号量加1)
osStatus_t osSemaphoreRelease (osSemaphoreId_t semaphore_id)
返回值:是否释放成功
osSemaphoreId_t semaphore_id:释放哪个信号量
二进制信号量使用方法:
我们举个栗子:我们创建一个全局变量,我们有两个任务同时要让这个全局变量值+1,为了防止这两个任务操作全局变量值+1时相互冲突,我们在这两个任务操作全局变量值+1前先请求信号量,操作完后释放信号量,避免同时操作信号量导致数据丢失
int data;//先创建一个全局变量
/* USER CODE BEGIN Header_StartDefaultTask */
/*** @brief Function implementing the defaultTask thread.* @param argument: Not used* @retval None*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{/* USER CODE BEGIN StartDefaultTask *//* Infinite loop */for(;;){//使用前先获取信号量osSemaphoreAcquire(myBinarySem01Handle,1000);//操作信号量data++;printf("data:%d\r\n",data);//释放信号量osSemaphoreRelease(myBinarySem01Handle);HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);osDelay(500);}/* USER CODE END StartDefaultTask */
}/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{/* USER CODE BEGIN StartTask02 *//* Infinite loop */for(;;){//使用前先获取信号量osSemaphoreAcquire(myBinarySem01Handle,1000);//操作信号量data++;printf("data:%d\r\n",data);//释放信号量osSemaphoreRelease(myBinarySem01Handle);osDelay(500);}/* USER CODE END StartTask02 */
}
我们可以明显的看到串口打印的数据没有错误
计数型信号量使用方法:
我们举个栗子:计数型信号量相当于一个队列,把这个对列看成停车场,小车开入停车场(获取信号量)事件计数+1,小车开出停车场(释放信号量)事件计数-1
void StartDefaultTask(void *argument)
{/* USER CODE BEGIN StartDefaultTask *//* Infinite loop */for(;;){//释放信号量osSemaphoreRelease(myCountingSem01Handle);printf("释放信号量ok 事件计数-1\r\n");// //使用前先获取信号量
// osSemaphoreAcquire(myBinarySem01Handle,1000);
// //操作信号量
// data++;
// printf("data:%d\r\n",data);
// //释放信号量
// osSemaphoreRelease(myBinarySem01Handle);HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);osDelay(500);}/* USER CODE END StartDefaultTask */
}/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{/* USER CODE BEGIN StartTask02 *//* Infinite loop */for(;;){//获取信号量osSemaphoreAcquire(myCountingSem01Handle,100);printf("获取信号量ok 事件计数+1\r\n");
// //使用前先获取信号量
// osSemaphoreAcquire(myBinarySem01Handle,1000);
// //操作信号量
// data++;
// printf("data:%d\r\n",data);
// //释放信号量
// osSemaphoreRelease(myBinarySem01Handle);osDelay(500);}/* USER CODE END StartTask02 */
}
队列和信号量之间的关系
我们可以看到创建信号量的函数用到了队列的函数
我们可以看到函数创建了队列,队列长度为1
信号量是特殊的队列,只有一个存储空间,用于存储信号量的值
互斥量
创建互斥量:
点击Mutexers,点击add
打开工程我们可以看见我们创建的互斥量
我们打开看看
传入的参数为互斥量的属性
osMutexId_t:互斥量的id
const osMutexAttr_t *attr:互斥量的属性
我们看一下
rtos原本的创建互斥量的函数
使用xSemaphoreCreateMutex()来创建互斥量,返回一个句柄,指向创建的互斥信号量
互斥量应用
获取互斥量:
osStatus_t osMutexAcquire (osMutexId_t mutex_id, uint32_t timeout)
返回值:是否获取成功
osMutexId_t mutex_id:获取互斥量的ID(互斥量的值0和1)
uint32_t timeout:超时时间
释放互斥量:
osStatus_t osMutexRelease (osMutexId_t mutex_id)
返回值:是否释放成功
osMutexId_t mutex_id:释放互斥量的ID(互斥量的值0和1)
互斥量的使用与二进制信号量类似
1.创建互斥量 ,
2.获取互斥量 ,
3.访问共享资源 ,
4.释放互斥量
互斥量与二进制信号量最主要的区别:优先级反转和优先级继承
低优先级的任务获取信号量(互斥量)没有及时释放,导致高优先级的任务无法获取信号量
当低优先级的任务获取互斥量后,低优先级的任务会继承高优先级的任务的优先级,
低优先级--变成-->最高优先级,变成最高优先级,当这个低优先级任务(最高优先级)运行完,后执行高优先级
简单来说当高优先级的任务进入休眠状态,低优先级的任务获取互斥量时,低优先及的任务会变成最高优先级的任务,当执行完这个低优先级的任务后再执行高优先级
事件组的概念
简单来说,我有两个任务key和串口都执行完了才执行任务3,这时我们可以用到事件组,举个例子我们用事件组的bit0和bit1来保存key和串口执行完的标志位,当bit0&bit1 = 1或bit0|bit1 = 1时,执行任务3 注:事件组就是事件标志位的集合
创建事件组
选择Events 点击add添加事件组
打开工程
我们跳转到这个软件封装好的函数看一下参数
osEventFlagsId_t osEventFlagsNew (const osEventFlagsAttr_t *attr)
osEventFlagsId_t:返回值,事件组的id
const osEventFlagsAttr_t *attr:创建事件组的参数
RTOS原生创建事件组函数
事件组的使用
设置事件组(设置事件组对应位为1):
uint32_t osEventFlagsSet (osEventFlagsId_t ef_id, uint32_t flags)
uint32_t:返回事件组的状态
osEventFlagsId_t ef_id:要设置事件组的ID
uint32_t flags:设置事件组的哪一位(bit0~bit31)
rtos原生设置事件组函数:
清空事件组(设置事件组对应位为0):
uint32_t osEventFlagsClear (osEventFlagsId_t ef_id, uint32_t flags)
uint32_t:返回事件组的状态
osEventFlagsId_t ef_id:要清空事件组的ID
uint32_t flags:设置事件组的哪一位(bit0~bit31)
rtos原生清空事件组函数:
等待事件组(等待某一位或者某几位)
uint32_t osEventFlagsWait (osEventFlagsId_t ef_id, uint32_t flags, uint32_t options, uint32_t timeout)
uint32_t:返回事件组的状态
osEventFlagsId_t ef_id:要等待事件组的ID
uint32_t flags:等待那些位
uint32_t options:可填两个参数osFlagsWaitAny:等待任意事件;osFlagsWaitAl:等待全部事件
uint32_t timeout:超时时间
事件组实验,当任务1和任务2完成了再执行任务3
/* USER CODE BEGIN Header */
/********************************************************************************* File Name : freertos.c* Description : Code for freertos applications******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header *//* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables *//* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {.name = "defaultTask",.stack_size = 128 * 4,.priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for myTask02 */
osThreadId_t myTask02Handle;
const osThreadAttr_t myTask02_attributes = {.name = "myTask02",.stack_size = 128 * 4,.priority = (osPriority_t) osPriorityLow,
};
/* Definitions for myTask03 */
osThreadId_t myTask03Handle;
const osThreadAttr_t myTask03_attributes = {.name = "myTask03",.stack_size = 128 * 4,.priority = (osPriority_t) osPriorityLow,
};
/* Definitions for myQueue01 */
osMessageQueueId_t myQueue01Handle;
const osMessageQueueAttr_t myQueue01_attributes = {.name = "myQueue01"
};
/* Definitions for myMutex01 */
osMutexId_t myMutex01Handle;
const osMutexAttr_t myMutex01_attributes = {.name = "myMutex01"
};
/* Definitions for myBinarySem01 */
osSemaphoreId_t myBinarySem01Handle;
const osSemaphoreAttr_t myBinarySem01_attributes = {.name = "myBinarySem01"
};
/* Definitions for myCountingSem01 */
osSemaphoreId_t myCountingSem01Handle;
const osSemaphoreAttr_t myCountingSem01_attributes = {.name = "myCountingSem01"
};
/* Definitions for myEvent01 */
osEventFlagsId_t myEvent01Handle;
const osEventFlagsAttr_t myEvent01_attributes = {.name = "myEvent01"
};/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes *//* USER CODE END FunctionPrototypes */void StartDefaultTask(void *argument);
void StartTask02(void *argument);
void StartTask03(void *argument);void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) *//*** @brief FreeRTOS initialization* @param None* @retval None*/
void MX_FREERTOS_Init(void) {/* USER CODE BEGIN Init *//* USER CODE END Init *//* Create the mutex(es) *//* creation of myMutex01 */myMutex01Handle = osMutexNew(&myMutex01_attributes);/* USER CODE BEGIN RTOS_MUTEX *//* add mutexes, ... *//* USER CODE END RTOS_MUTEX *//* Create the semaphores(s) *//* creation of myBinarySem01 */myBinarySem01Handle = osSemaphoreNew(1, 1, &myBinarySem01_attributes);/* creation of myCountingSem01 */myCountingSem01Handle = osSemaphoreNew(2, 0, &myCountingSem01_attributes);/* USER CODE BEGIN RTOS_SEMAPHORES *//* add semaphores, ... *//* USER CODE END RTOS_SEMAPHORES *//* USER CODE BEGIN RTOS_TIMERS *//* start timers, add new ones, ... *//* USER CODE END RTOS_TIMERS *//* Create the queue(s) *//* creation of myQueue01 */myQueue01Handle = osMessageQueueNew (16, sizeof(uint16_t), &myQueue01_attributes);/* USER CODE BEGIN RTOS_QUEUES *//* add queues, ... *//* USER CODE END RTOS_QUEUES *//* Create the thread(s) *//* creation of defaultTask */defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);/* creation of myTask02 */myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes);/* creation of myTask03 */myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes);/* USER CODE BEGIN RTOS_THREADS *//* add threads, ... *//* USER CODE END RTOS_THREADS *//* creation of myEvent01 */myEvent01Handle = osEventFlagsNew(&myEvent01_attributes);/* USER CODE BEGIN RTOS_EVENTS *//* add events, ... *//* USER CODE END RTOS_EVENTS */}
#define FLAG1 (1<<0)//bit0
#define FLAG2 (1<<1)//bit1
/* USER CODE BEGIN Header_StartDefaultTask */
/*** @brief Function implementing the defaultTask thread.* @param argument: Not used* @retval None*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{/* USER CODE BEGIN StartDefaultTask *//* Infinite loop */for(;;){osEventFlagsWait(myEvent01Handle,FLAG1|FLAG2,osFlagsWaitAll,3000);printf("wait ok\r\n");HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);osDelay(1000);// osEventFlagsClear(myEvent01Handle, FLAG1 | FLAG2);}/* USER CODE END StartDefaultTask */
}/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{/* USER CODE BEGIN StartTask02 *//* Infinite loop */for(;;){osEventFlagsSet(myEvent01Handle,FLAG1);//设置事件1printf("Set FLAG1 OK\r\n");osDelay(500);}/* USER CODE END StartTask02 */
}/* USER CODE BEGIN Header_StartTask03 */
/**
* @brief Function implementing the myTask03 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask03 */
void StartTask03(void *argument)
{/* USER CODE BEGIN StartTask03 *//* Infinite loop */for(;;){osEventFlagsSet(myEvent01Handle,FLAG2);//设置事件2printf("Set FLAG2 OK\r\n");osDelay(500);}/* USER CODE END StartTask03 */
}/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application *//* USER CODE END Application */
任务通知
任务通知功能在TCB控制块中
volatile uint32_t ulNotifiedValue;
volatile uint8_t ucNotifyState;
volatile uint32_t ulNotifiedValue:任务通知的值(32位的标志)事件组
volatile uint8_t ucNotifyState:任务通知的状态
//任务通知的状态
1.表示任务正在等待通知
2.表示任务已经接收到通知
3.表示任务通知被取消
设置任务通知值
uint32_t osThreadFlagsSet (osThreadId_t thread_id, uint32_t flags)
osThreadId_t thread_id:哪个任务
uint32_t flags:要设置的某一位
RTOS原生设置任务通知值函数
清除任务通知值函数:(当前任务调用 清除某一位)
uint32_t osThreadFlagsClear (uint32_t flags)
uint32_t flags:要清除的某一位
等待任务通知值:
uint32_t osThreadFlagsWait (uint32_t flags, uint32_t options, uint32_t timeout)
uint32_t flags:要等待的位
uint32_t options:等待全部事件,还是等待任意事件
uint32_t timeout:超时时间
软件定时器
创建软件定时器
创建软件定时器函数
osTimerId_t osTimerNew (osTimerFunc_t func, osTimerType_t type, void *argument, const osTimerAttr_t *attr)
osTimerId_t:返回值软件定时器的id
osTimerFunc_t func:软件定时器的回调函数
osTimerType_t type:软件定时器的类型(一次性,周期性)
void *argument:传递给回调函数的参数
const osTimerAttr_t *attr:软件定时器的属性
rtos原生创建软件定时器函数
开启软件定时器函数:
osStatus_t osTimerStart (osTimerId_t timer_id, uint32_t ticks)
osStatus_t:返回值是否成功开启软件定时器
osTimerId_t timer_id:要开启定时器的id
uint32_t ticks:定时器的触发时间(定时器的定时时间)单位是ms
rtos原生开启定时器函数
关闭软件定时器函数:
osStatus_t osTimerStop (osTimerId_t timer_id)
osStatus_t:返回值是否成功关闭软件定时器
osTimerId_t timer_id:要关闭定时器的id
/* USER CODE BEGIN Header */
/********************************************************************************* File Name : freertos.c* Description : Code for freertos applications******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header *//* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables *//* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {.name = "defaultTask",.stack_size = 128 * 4,.priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for myTask02 */
osThreadId_t myTask02Handle;
const osThreadAttr_t myTask02_attributes = {.name = "myTask02",.stack_size = 128 * 4,.priority = (osPriority_t) osPriorityLow,
};
/* Definitions for myTask03 */
osThreadId_t myTask03Handle;
const osThreadAttr_t myTask03_attributes = {.name = "myTask03",.stack_size = 128 * 4,.priority = (osPriority_t) osPriorityLow,
};
/* Definitions for myQueue01 */
osMessageQueueId_t myQueue01Handle;
const osMessageQueueAttr_t myQueue01_attributes = {.name = "myQueue01"
};
/* Definitions for myTimer01 */
osTimerId_t myTimer01Handle;
const osTimerAttr_t myTimer01_attributes = {.name = "myTimer01"
};
/* Definitions for myMutex01 */
osMutexId_t myMutex01Handle;
const osMutexAttr_t myMutex01_attributes = {.name = "myMutex01"
};
/* Definitions for myBinarySem01 */
osSemaphoreId_t myBinarySem01Handle;
const osSemaphoreAttr_t myBinarySem01_attributes = {.name = "myBinarySem01"
};
/* Definitions for myCountingSem01 */
osSemaphoreId_t myCountingSem01Handle;
const osSemaphoreAttr_t myCountingSem01_attributes = {.name = "myCountingSem01"
};
/* Definitions for myEvent01 */
osEventFlagsId_t myEvent01Handle;
const osEventFlagsAttr_t myEvent01_attributes = {.name = "myEvent01"
};/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes *//* USER CODE END FunctionPrototypes */void StartDefaultTask(void *argument);
void StartTask02(void *argument);
void StartTask03(void *argument);
void Callback01(void *argument);void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) *//*** @brief FreeRTOS initialization* @param None* @retval None*/
void MX_FREERTOS_Init(void) {/* USER CODE BEGIN Init *//* USER CODE END Init *//* Create the mutex(es) *//* creation of myMutex01 */myMutex01Handle = osMutexNew(&myMutex01_attributes);/* USER CODE BEGIN RTOS_MUTEX *//* add mutexes, ... *//* USER CODE END RTOS_MUTEX *//* Create the semaphores(s) *//* creation of myBinarySem01 */myBinarySem01Handle = osSemaphoreNew(1, 1, &myBinarySem01_attributes);/* creation of myCountingSem01 */myCountingSem01Handle = osSemaphoreNew(2, 0, &myCountingSem01_attributes);/* USER CODE BEGIN RTOS_SEMAPHORES *//* add semaphores, ... *//* USER CODE END RTOS_SEMAPHORES *//* Create the timer(s) *//* creation of myTimer01 */myTimer01Handle = osTimerNew(Callback01, osTimerPeriodic, NULL, &myTimer01_attributes);osTimerStart(myTimer01Handle,1000);//开启软件定时器/* USER CODE BEGIN RTOS_TIMERS *//* start timers, add new ones, ... *//* USER CODE END RTOS_TIMERS *//* Create the queue(s) *//* creation of myQueue01 */myQueue01Handle = osMessageQueueNew (16, sizeof(uint16_t), &myQueue01_attributes);/* USER CODE BEGIN RTOS_QUEUES *//* add queues, ... *//* USER CODE END RTOS_QUEUES *//* Create the thread(s) *//* creation of defaultTask */defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);/* creation of myTask02 */myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes);/* creation of myTask03 */myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes);/* USER CODE BEGIN RTOS_THREADS *//* add threads, ... *//* USER CODE END RTOS_THREADS *//* creation of myEvent01 */myEvent01Handle = osEventFlagsNew(&myEvent01_attributes);/* USER CODE BEGIN RTOS_EVENTS *//* add events, ... *//* USER CODE END RTOS_EVENTS */}/* USER CODE BEGIN Header_StartDefaultTask */
/*** @brief Function implementing the defaultTask thread.* @param argument: Not used* @retval None*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{/* USER CODE BEGIN StartDefaultTask *//* Infinite loop */for(;;){HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5); osDelay(1000);// osEventFlagsClear(myEvent01Handle, FLAG1 | FLAG2);}/* USER CODE END StartDefaultTask */
}/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{/* USER CODE BEGIN StartTask02 *//* Infinite loop */for(;;){osDelay(500);}/* USER CODE END StartTask02 */
}/* USER CODE BEGIN Header_StartTask03 */
/**
* @brief Function implementing the myTask03 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask03 */
void StartTask03(void *argument)
{/* USER CODE BEGIN StartTask03 *//* Infinite loop */for(;;){osDelay(500);}/* USER CODE END StartTask03 */
}/* Callback01 function */
void Callback01(void *argument)//定时器回调函数
{/* USER CODE BEGIN Callback01 */printf("Hello\r\n");/* USER CODE END Callback01 */
}/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application *//* USER CODE END Application */