当前位置: 首页 > news >正文

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 */

http://www.xdnf.cn/news/1407133.html

相关文章:

  • 【MySQL自学】SQL语法全解(上篇)
  • 【PS实战】逐步打造静物的艺术色调(大学作业)
  • 从零开始搭建使用 TDengine:新用户快速上手指南
  • windows docker 中的mysql 无法被外部浏览器访问如何解决
  • 自动驾驶中的传感器技术37——Lidar(12)
  • Ansible 临时命令与常用模块实操指南
  • 【人工智能99问】LLaMA中的RoPE是什么?(35/99)
  • Paimon——官网阅读:Spark 引擎
  • Spark内存管理
  • Nginx 502 Bad Gateway:从 upstream 日志到 FastCGI 超时复盘
  • 腾讯浑元最新技术:具有表征对齐的多模态扩散,用于高保真拟音音频生成
  • 【嵌入式DIY实例】-空中鼠标
  • LeetCode算法日记 - Day 27: 计算右侧小于当前元素的个数、翻转对
  • 高校心理教育辅导系统的设计与实现|基于SpringBoot高校心理教育辅导系统的设计与实现
  • USB虚拟化应用5:VirtualFIDO2 虚拟硬件安全密钥,智能卡,yubico,支持X,FB,GITHUB等各种网站双重认证,让你的账户登录绝对安全
  • 在集群级别应用 Pod 安全标准
  • opencv 梯度提取
  • 数据化管理是什么意思?企业该如何进行数据化管理
  • 《SVA断言系统学习之路》【01】即时断言概览
  • 北京博乐科技有限公司2025届程序技术类笔试题
  • 性能测试工具-SkyWalking
  • 元宇宙与旅游产业:虚实融合的文旅新体验
  • Python毕业设计推荐:基于Django+MySQL的养老社区服务管理系统
  • 从 WPF 到 Avalonia 的迁移系列实战篇4:控件模板与 TemplatedControl
  • UniApp 基础开发第一步:HBuilderX 安装与环境配置
  • 【AI智能体技术】如何学习多智能体系统知识并实现SOTA算法?
  • SDL3.0 学习随笔:其一
  • 自底向上了解CPU的运算
  • 嵌入式常见架构
  • 【MYSQL】从混乱到清晰:联合查询帮你打通数据孤岛