CubeMX使用FreeRTOS编程指南_cubemx freertos

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

参数功能
Task Name任务名称,保存在 TCB 结构体中,设置时自己起名字
Priority任务优先级,任务的调度等级,根据自己创建任务的紧急程度设定比如通信任务不能被打断,可以设计较高优先级
Stack Size(Words)设定给任务分配的内存大小,单位是字,对于32位单片机来说占4个字节
Entry Function任务实体,即任务的运行函数名
Code Generation代码生成模式As weak: 产生一个用 __weak 修饰的弱定义任务函数,用户可自己在进行定义;As external: 产生一个外部引用的任务函数,用户需要自己定义该函数;Default: 产生一个默认格式的任务函数,用户需要在该函数内实现自己的功能
Parameter:传入的参数,保持默认就行
Allocation:内存分配方式Static: 静态方式是直接在RAM占据一个静态空间Dynamic:动态方则是在初始配置的内存池大小数组中动态申请、释放空间

设置完成后点击OK,配置就完成了,之后生成代码,使用 MDK 进一步配置任务的具体信息

在生成的代码中,我们打开 freertos.c 文件可以在代码中看到任务的配置信息

20211009123942

在 freertos.c 文件的末尾部分,我们可以看到生成的任务实体

20211009124134

任务实体本身就是一个死循环函数,循环执行程序代码,但循环体代码里面必须要有延时函数,释放当前任务对 MCU 的控制权,使其他低优先级可以执行,此外,关于任务,CubeMX 提供了一系列的用户调用接口函数,具体如下

函数功能
osThreadNew创建新任务
*osThreadGetName获取任务名称
osThreadGetId获取当前任务的控制块(TCB)
osThreadGetState获取当前任务的运行状态
osThreadGetStackSize获取任务的堆栈大小
osThreadGetStackSpace获取任务剩余的堆栈大小
osThreadSetPriority设定任务优先级
osThreadGetPriority获取任务优先级
osThreadYield切换控制权给下一个任务
osThreadSuspend挂起任务
osThreadResume恢复任务(挂起多少次恢复多少次)
osThreadDetach分离任务,方便任务结束进行回收
osThreadJoin等待指定的任务停止
osThreadExit停止当前任务
osThreadTerminate停止指定任务
osThreadGetCount获取激活的任务数量
osThreadEnumerate列举激活的任务
4.2 CubeMX 下队列的创建与配置

队列,又称为消息队列,用于任务间的数据通信,传输数据,在操作系统里面,直接使用全局变量传输数据十分危险,看似正常运行,但不知道啥时候就会因为寄存器或者内存等等原因引起崩溃,所以引入消息,队列的概念,任务发送数据到队列,需要接受消息的任务挂起在队列的挂起列表,等待消息的到来,CubeMX 创建队列的步骤如下:

先点击 Add 添加队列

20211008221618

队列配置参数介绍

参数功能
Queue Name队列名称(自己设定)
Queue Size消息队列大小
Item Size队列传输类型,保持默认16 位就行
Allocation队列内存的分配方式Static: 静态方式是直接在RAM占据一个静态空间Dynamic:动态方则是在初始配置的内存池大小数组中动态申请、释放空间

配置需要的参数后,点击OK,然后生成代码

生成代码后,我们可以在 freertos.c 中系统初始话函数中看到队列的初始化

20211009130435

初始化函数会在一开始被调用,对 FreeRTOS 系统和内核对象进行初始化,初始化后系统就可以进行调度和使用内核对象,CubeMX 生成的代码自动将创建的内核对象放到初始化函数内,所以我们在任务和中断中直接使用就可以,队列的 FreeRTOS API 接口在CubeMX 内再次进行了封装,使用更加简单,使用方式如下:

我们使用的 CMSIS 2.0 版本,所以在任务文件中包含调用声明头文件

#include "cmsis\_os2.h"

在队列头文件内我们可以在 600 多行的位置找到有关队列的 API 函数声明:

20211009131537

下面介绍一下队列有关接口的函数接口:

函数功能
osMessageQueueNew创建并初始化一个新的队列
osMessageQueueGetName获取队列的名字
osMessageQueuePut发送一条消息到队列
osMessageQueueGet从队列等待一条消息
osMessageQueueGetCapacity获取队列传输消息的峰值
osMessageQueueGetMsgSize获取队列使用内存池的最大峰值
osMessageQueueGetCount获取队列的消息数量
osMessageQueueGetSpace获取队列剩余的可用空槽
osMessageQueueReset清空队列
osMessageQueueDelete删除队列

以上的API接口有其对应的传入参数,具体使用方式需要在翻源码的注释,这里我选常用的来介绍一下:

消息队列常用的是插入与获取消息,初始化系统已经帮助我们完成,在初始化的时候会获取一个队列的句柄,之后对队列的操作都是围绕这个句柄展开,比如上面的代码中,句柄就是 myQueue01Handle ,我们发送一个消息到这个队列,就是调用发送函数,对句柄进行操作,先看一下发送消息的函数原型

osStatus\_t osMessageQueuePut (osMessageQueueId\_t mq_id, const void \*msg_ptr, uint8\_t msg_prio, uint32\_t timeout);

参数的功能

参数功能
mq_id传入队列的句柄
*msg_ptr指向需要发送的消息内容的指针
msg_prio本次发送消息的优先级(目前API未加入功能)
timeout发送消息的超时时间(设置为0代表一直等待发送成功)
osStatus_t(返回值)返回执行结果

返回值的可能

错误含义
osOK执行正常
osError系统错误
osErrorTimeout执行超时
osErrorResource资源不可用
osErrorParameter参数无效
osErrorNoMemory内存不足
osErrorISR不允许在中断调用
osStatusReserved防止编译器优化项,不需要管他

所以我们发送一个消息到队列,函数用法如下:

void StartTask02(void \*argument)
{
  /\* USER CODE BEGIN StartTask02 \*/
	osStatus\_t result;
	uint8\_t dat[]="666\r\n";
  /\* Infinite loop \*/
  for(;;)
  {
		result= osMessageQueuePut(myQueue01Handle,dat,1,0);
		if(result == osOK)
		{
			//发送成功
		}else
		{
			//发送失败
		}
    osDelay(1);
  }
  /\* USER CODE END StartTask02 \*/
}

发送消息的优先级暂时无用,CubeMX 对 FreeRTOS 的支持还不完善,发送消息里面的优先级未使用到,并且入队方式使用的是发送到队列尾部,没有从头部插入的方式,有需求可以 通过包含 queue.h 文件,调用 FreeRTOS 的官方代码,或者自己修改 生成代码的 API 接口结合优先级使用队列的向前插入和向后插入,丰富系统功能!

除了发送消息到队列,接受队列的消息 API 接口也经常用到,函数原型如下

osStatus\_t osMessageQueueGet (osMessageQueueId\_t mq_id, void \*msg_ptr, uint8\_t \*msg_prio, uint32\_t timeout);

参数的功能

参数功能
mq_id接受队列的句柄
*msg_ptr用于接受消息内容的指针
msg_prio存放接受消息的优先级(目前API未加入功能)
timeout接受消息的超时时间(设置为10代表,当前任务挂起在挂起列表,直到接收成功时恢复,或者10个TICK等待周期到达然后任务强行恢复,不再等待,为0则是不等待,等待期间任务挂起在内核对象的挂起队列)
osStatus_t(返回值)返回执行结果

函数用法

void StartTask02(void \*argument)
{
  /\* USER CODE BEGIN StartTask02 \*/
	osStatus\_t result;
	uint8\_t dat[10]={};
	uint8\_t \*pro;
  /\* Infinite loop \*/
  for(;;)
  {
		result= osMessageQueueGet(myQueue01Handle,dat,pro,10);
		if(result == osOK)
		{
			//接受成功
		}else
		{
			//接受失败
		}
    osDelay(1);
  }
  /\* USER CODE END StartTask02 \*/
}

注意:FreeRTOS 中获取和发送消息的 API 接口函数分为任务中调用和中断中调用,CubeMX 代码接口将两者整合了,调用时自动判断调用环境是在 ISR 还是正常运行环境中

五、创建定时器和信号量

5.1 CubeMX下定时器的创建和配置

软件定时器本质上就是设置一段时间,当设置的时间到达之后就执行指定的功能函数,调用的这个函数叫做回调函数。回调函数的两次执行间隔叫做定时器的定时周期,简而言之,当定时器的定时周期到了以后就会执行回调函数,下面介绍一下 CubeMX 中开启定时器的方法:

在 CubeMX 里面按下面步骤添加定时器

20211009221917

然后配置具体参数,参数的功能如下:

参数功能
Timer Name设置定时器的名称
Callback设定定时器的回调函数体
Type设定定时器的执行类型osTimerPeriodic 定时器周期执行回调函数osTimerOnce 定时器只执行一次回调函数
Code Generation Option代码生成模式As weak: 产生一个用 __weak 修饰的弱定义任务函数,用户可自己在进行定义;As external: 产生一个外部引用的任务函数,用户需要自己定义该函数;Default: 产生一个默认格式的任务函数,用户需要在该函数内实现自己的功能
Parameter传入参数,保持默认NULL就行
Allocation软件定时器内存的分配方式,一般使用动态Static: 静态方式是直接在RAM占据一个静态空间Dynamic:动态方则是在初始配置的内存池大小数组中动态申请、释放空间

参数配置完成后,生成代码,我们可以在 freertos.c 文件里面看到定时器创建后获得的句柄,以及生成的回调函数:

20211010142155

20211010142213

有了句柄,我们就可以调用 cmsis_os2.c 里面的定时器接口函数对定时器进行操作,先看一下 CubeMX 提供的定时器接口函数及其功能

函数功能
osTimerNew新建定时器,返回定时器控制句柄
osTimerGetName获取定时器名称
osTimerStart设置定时器周期,启动定时器
osTimerStop停止定时器
osTimerIsRunning检测定时器是否在运行
osTimerDelete删除定时器

其中常用的接口是定时器的启动和停止

定时器启动: osTimerStart,函数原型

osStatus\_t osTimerStart (osTimerId\_t timer_id, uint32\_t ticks);

参数介绍:

参数功能
timer_id需要启动的定时器句柄
ticks设置定时器的运行周期

此处的 ticks 设定的数字是定时器两次调用回调函数的周期数目,每个 tick 是一个心跳时钟的长度

使用例程:

void StartTask02(void \*argument)
{
  /\* USER CODE BEGIN StartTask02 \*/
    osStatus\_t result;
    uint8\_t dat[10]={0};
    uint8\_t \*pro;
    result= osTimerStart(myTimer01Handle,10);
    if(result == osOK)
    {
        //启动成功
    }else
    {
        //启动失败
    }
    
  /\* Infinite loop \*/
  for(;;)
  {
		

    osDelay(10);
  }
  /\* USER CODE END StartTask02 \*/
}

按照例程启动定时器,定时器会以 10个tick 的周期,调用回调函数

回调函数不要放阻塞函数,程序尽可能短

定时器启动: osTimerStop,函数原型

osStatus\_t osTimerStop (osTimerId\_t timer_id);

参数只有一个,就是定时器的控制句柄,传入即可停止定时器,例程如下

void StartTask02(void \*argument)
{
  /\* USER CODE BEGIN StartTask02 \*/
	osStatus\_t result;
	uint8\_t dat[10]={0};
	uint8\_t \*pro;
    result= osTimerStop(myTimer01Handle);
    if(result == osOK)
    {
        //停止成功
    }else
    {
        //停止失败
    }
  /\* Infinite loop \*/
  for(;;)
  {
    osDelay(10);
  }
  /\* USER CODE END StartTask02 \*/
}

软件定时器是由软件定时器维护任务进行维护,检测各个定时器的状态,进行处理,回调回调函数,软件定时器维护任务的参数配置在前面的 Config 就已经提到过

5.2 CubeMX下信号量的创建和配置

信号量是 RTOS 的一个内核对象,该对象有一个队列表示该信号量拥有的信号数目,任何任务都可以对这个信号数目进行获取和释放,获取时信号-1,释放时信号+1,为0时不能继续获取,此时有任务想要继续获取信号量的话,任务会挂起在该内核对象的挂起列表,等到信号可以获取时进行恢复,根据这个特性,信号量常用于控制对共享资源的访问和任务同步,下面介绍一下 CubeMX 下信号量的配置:

点开配置页面,可以看到有两个信号量添加页面,其中 Binary Semaphores 是二值信号量,Counting Semaphores 是计数信号量,二进制信号量,仅有一个队列或者说 token,用于同步一个操作;计数信号量则拥有多个 tokens,可用于同步多个操作,或者管理有限资源

20211010153231

二值信号量创建:

点击 Add,配置参数

20211010163823

参数介绍

参数功能
Semaphore Name信号量名称
Allocation内存分配方式,一般使用动态Static: 静态方式是直接在RAM占据一个静态空间Dynamic:动态方则是在初始配置的内存池大小数组中动态申请、释放空间

计数信号量:

点击 Add,配置参数

20211010163959

参数介绍

参数功能
Semaphore Name信号量名称
Count计数信号量的最大数目
Allocation内存分配方式,一般使用动态Static: 静态方式是直接在RAM占据一个静态空间Dynamic:动态方则是在初始配置的内存池大小数组中动态申请、释放空间

配置完成后我们生成代码,在 freertos.c 的初始化代码中可以看到信号量被创建,并且返回了信号量的控制句柄

20211010164347

下面介绍一下 CubeMX 提供的信号量操作函数接口:

函数功能
osSemaphoreNew创建新的信号量
*osSemaphoreGetName获取信号量的名称
osSemaphoreAcquire获取信号量
osSemaphoreRelease释放信号量
osSemaphoreGetCount获取当前可用信号量的数目
osSemaphoreDelete删除信号量

其中常用的函数有获取和释放信号量,下面介绍一下这两个函数的参数和使用方式

获取信号量 osSemaphoreAcquire

函数原型

osStatus\_t osSemaphoreAcquire (osSemaphoreId\_t semaphore_id, uint32\_t timeout);

参数介绍

参数功能
semaphore_id传入要获取信号量的控制句柄
timeout获取等待时间(等待期间任务挂起在内核对象的挂起队列)

使用例程

void StartDefaultTask(void \*argument)
{
  /\* USER CODE BEGIN StartDefaultTask \*/
	osStatus\_t result;
	
	
  /\* Infinite loop \*/
  for(;;)
  {
		result = osSemaphoreAcquire(myBinarySem01Handle,10);
		if(result == osOK)
		{
			//获取成功
		}else
		{
			//获取失败
		}
    osDelay(1);
  }
  /\* USER CODE END StartDefaultTask \*/
}

释放信号量 osSemaphoreRelease

函数原型

osStatus\_t osSemaphoreRelease (osSemaphoreId\_t semaphore_id);

参数功能
semaphore_id传入要释放的信号量控制句柄

使用例程

void StartDefaultTask(void \*argument)
{
  /\* USER CODE BEGIN StartDefaultTask \*/
	osStatus\_t result;
	
	
  /\* Infinite loop \*/
  for(;;)
  {
		result = osSemaphoreRelease(myBinarySem01Handle);
		if(result == osOK)
		{
			//释放成功
		}else
		{
			//释放失败
		}
    osDelay(1);
  }
  /\* USER CODE END StartDefaultTask \*/
}

二值信号量和计数信号量的操作基本一致,没用区别,只是用有的信号队列最大数目不同而已

同时注意信号量在使用过程中会出现优先级反转的Bug,使用时需要注意

六、创建互斥量

6.1 CubeMX下互斥量的创建和配置

互斥量其实就是一个拥有优先级继承的二值信号量,互斥信号量适合用于那些需要互斥访问的应用中,在互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这个钥匙去使用资源,与信号量不同的是,互斥量的释放必须由获取他的任务进行释放,如果不释放,可能会造成死锁

死锁就是两个任务获取对方拥有的锁,各自进入挂起列表,无法释放互斥锁

下面介绍一下 CubeMX 下互斥量的配置,在配置界面我们可用看到两个互斥量配置界面,上面的是普通互斥量,其获取只能获取一次,重复获取是无效的,而第二个则是递归互斥量,递归互斥信号量可以获取多次,但对应的也要释放多次才能让出使用权,比如我获取3次,任务要释放3次才能释放该互斥量的使用权

使用互斥量,需要点击 Add 然后配置参数

20211010173127

参数介绍:

参数功能
Mutex Name互斥量名称
Allocation内存分配方式,一般使用动态Static: 静态方式是直接在RAM占据一个静态空间Dynamic:动态方则是在初始配置的内存池大小数组中动态申请、释放空间

递归互斥信号量的配置方式与其相同,包括配置参数也相同,两者只是在用法上有些许区别,添加方式如下:

20211010173704

添加配置完成后,点击生成代码,在 freertos.c 文件中我们可以看到互斥量初始化完成,并且生成了对应的控制句柄

20211010174350

CubeMX 提供的 API 接口函数如下

函数功能
osMutexNew创建互斥量
*osMutexGetName获取互斥量名称
osMutexAcquire任务获取互斥量
osMutexRelease任务释放互斥量
osMutexGetOwner获取互斥量的拥有任务的任务 TCB
osMutexDelete删除互斥量

主要使用到的还是互斥量的获取与释放,下面分析一下这两个函数:

获取互斥量 osMutexAcquire

函数原型

osStatus\_t osMutexAcquire (osMutexId\_t mutex_id, uint32\_t timeout);

参数介绍:

参数功能
mutex_id互斥量控制句柄
timeout获取互斥量时的等待时间(等待期间任务挂起在内核对象的挂起队列)

使用方式

void StartTask02(void \*argument)
{
  /\* USER CODE BEGIN StartTask02 \*/
	osStatus\_t result;
    result= osMutexAcquire(myMutex01Handle,10);
    if(result == osOK)
    {
        //获取成功
    }else
    {
        //获取失败
    }
  /\* Infinite loop \*/
  for(;;)
  {
    osDelay(10);
  }
  /\* USER CODE END StartTask02 \*/
}

释放互斥量 osMutexRelease

函数原型

osStatus\_t osMutexRelease (osMutexId\_t mutex_id);

参数介绍:

参数功能
mutex_id互斥量控制句柄

使用方式

void StartTask02(void \*argument)
{
  /\* USER CODE BEGIN StartTask02 \*/
	osStatus\_t result;
    result= osMutexRelease(myMutex01Handle);
    if(result == osOK)
    {
        //释放成功
    }else
    {
        //释放失败
    }
  /\* Infinite loop \*/
  for(;;)
  {
    osDelay(10);
  }
  /\* USER CODE END StartTask02 \*/
}

使用方式和信号量基本相同,因为互斥量本质上就是信号量的一种

七、创建事件标志组

7.1 CubeMX下事件的创建和配置

任务间的同步除了信号量还有时间标志组,信号的同步通常是一对一的同步,有的时候系统需要多对一的同步,比如同时满足5个按键按下时,任务启动,如果使用信号会很占据资源,所以 RTOS 引入了事件标志组来满足这一需求,下面我们看一下 CubeMX 内事件标志组的配置方法:

点击 Add 创建事件标志组

20211010201501

配置介绍

参数功能
Event flags Name事件标志组名称
Allocation内存分配方式,一般使用动态Static: 静态方式是直接在RAM占据一个静态空间Dynamic:动态方则是在初始配置的内存池大小数组中动态申请、释放空间

配置完成后,生成代码,在系统初始化内,看有没有生成事件标志组控制句柄,可以看到句柄创建完成

20211010201922

CubeMX 提供的配置事件标志组的接口 API 如下:

函数功能
osEventFlagsNew创建事件标志组
*osEventFlagsGetName获取事件标志组名称
osEventFlagsSet设置事件标志组
osEventFlagsClear清除事件标志组
osEventFlagsGet获取当前事件组标志信息
osEventFlagsWait等待事件标志组触发
osEventFlagsDelete删除事件标志组

常用的 API 接口是设置事件标志组以及等待事件标志组的触发,下面我们分析一下这两个 API

在了解 API 前我们需要简单了解一下事件的触发原理:首先事件标志组的数据类型为 EventGroupHandle_t,事件标志组中的所有事件位都存储在一个无符号的 EventBits_t 类型的变量中,当 configUSE_16_BIT_TICKS 为 1 的时候事件标志组可以存储 8 个事件位,当 configUSE_16_BIT_TICKS 为 0 的时候事件标志组存储 24个事件位,每个事件位其实就是一个0或者1数字,就像下面的24位组成一个事件标志组

20211010203414

我们在使用事件API接口函数前需要先定义我们需要的触发事件位,比如添加如下的代码

#define event1 1<<1 //事件1
#define event2 1<<2 //事件2

编写好触发事件后,我们在看如何使用 API 接口

设置事件标志 osEventFlagsSet

函数原型

uint32\_t osEventFlagsSet (osEventFlagsId\_t ef_id, uint32\_t flags);

参数介绍:

参数功能
ef_id事件标志组控制句柄
flags事件位

使用方式:设置事件1和事件2

void StartDefaultTask(void \*argument)
{
  /\* USER CODE BEGIN StartDefaultTask \*/
	osStatus\_t result;
  /\* Infinite loop \*/
  for(;;)
  {
		result = osEventFlagsSet(myEvent01Handle,event1);
		if(result == osOK)
		{
			//事件1设置成功
		}else
		{
			//事件1设置失败
		}
		result = osEventFlagsSet(myEvent01Handle,event2);
		if(result == osOK)
		{
			//事件2设置成功
		}else
		{
			//事件2设置失败
		} 
    osDelay(1);
  }
  /\* USER CODE END StartDefaultTask \*/
}

等待事件标志 osEventFlagsWait

函数原型

uint32\_t osEventFlagsWait (osEventFlagsId\_t ef_id, uint32\_t flags, uint32\_t options, uint32\_t timeout);

参数介绍:

参数功能
ef_id事件标志组控制句柄
flags等待的事件位
options等待事件位的操作osFlagsWaitAny :等待的事件位有任意一个等到就恢复任务osFlagsWaitAll:等待的事件位全部等到才恢复任务 osFlagsNoClear:等待成功后不清楚所等待的标志位(默认清除)
timeout等待事件组的等待时间(等待期间任务挂起在内核对象的挂起队列)

使用例子:同时等待事件1和事件2,且等待到不清除

void StartDefaultTask(void \*argument)
{
  /\* USER CODE BEGIN StartDefaultTask \*/
	osStatus\_t result;
  /\* Infinite loop \*/
  for(;;)
  {
		result = osEventFlagsWait(myEvent01Handle,event1|event2,osFlagsWaitAll|osFlagsNoClear,10);
		if(result == osOK)
		{
			//等待成功
		}else
		{
			//等待失败
		}
    osDelay(1);
  }
  /\* USER CODE END StartDefaultTask \*/
}

八、用户常量

User Constants 用于添加用户常量,将不变的量转化为常量保存,可以节省 RAM 资源空间,因为常量和变量的保存位置不同,详细了解可以参考这篇文章:C语言:内存四区

九、任务通知

FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量 ulNotifiedValue 就是这个通知值。任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态,CubeMX内没有提供相关的配置项,但在其生成的 FreeRTOS 接口里面有相关函数进行配置,函数位置如下:

1

接口函数功能:

函数功能
osThreadFlagsSet设置任务的通知标志
osThreadFlagsClear清除任务通知
osThreadFlagsGet获取任务标志
osThreadFlagsWait等待特定的任务标志

常用的两个 API 就是设置任务通知和等待任务通知函数

设置通知 osThreadFlagsSet

函数原型

uint32\_t osThreadFlagsSet (osThreadId\_t thread_id, uint32\_t flags);


![img](https://img-blog.csdnimg.cn/img_convert/aeb9c13eb68ed0a5de0cc72d26c5f02b.png)
![img](https://img-blog.csdnimg.cn/img_convert/ee1341b190b52f8ea0ff07ebdb5c06fd.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

edValue 就是这个通知值。任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态,CubeMX内没有提供相关的配置项,但在其生成的 FreeRTOS 接口里面有相关函数进行配置,函数位置如下:


![1](https://img-blog.csdnimg.cn/8542744bd5cb45f4877b012f70237529.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBASmVja1h1NjY2,size_20,color_FFFFFF,t_70,g_se,x_16)


接口函数功能:




| 函数 | 功能 |
| --- | --- |
| osThreadFlagsSet | 设置任务的通知标志 |
| osThreadFlagsClear | 清除任务通知 |
| osThreadFlagsGet | 获取任务标志 |
| osThreadFlagsWait | 等待特定的任务标志 |


常用的两个 API 就是设置任务通知和等待任务通知函数


**设置通知 osThreadFlagsSet**


函数原型



uint32_t osThreadFlagsSet (osThreadId_t thread_id, uint32_t flags);

[外链图片转存中…(img-zjYdwjtH-1715864965218)]
[外链图片转存中…(img-beeGCPL1-1715864965218)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

  • 33
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值