掌握excel线性回归技巧助力数据分析与决策优化
1981
2022-05-30
文章目录
CubeMX使用FreeRTOS编程指南
一、开发前言
1.1 软件准备
1.2 开启FreeRTOS
二、配置界面
三、系统设置
2.1 调度内核设置
2.2 内存管理设置
2.3 钩子函数配置
2.5 任务运行追踪配置
2.6 协程配置
2.7 软件定时器配置
2.8 中断优先级配置
三、内核裁剪
四、创建任务与队列
4.1 CubeMX 下任务创建与配置
4.2 CubeMX 下队列的创建与配置
五、创建定时器和信号量
5.1 CubeMX下定时器的创建和配置
5.2 CubeMX下信号量的创建和配置
六、创建互斥量
6.1 CubeMX下互斥量的创建和配置
七、创建事件标志组
7.1 CubeMX下事件的创建和配置
八、用户常量
九、任务通知
十、系统内核配置
CubeMX使用FreeRTOS编程指南
一、开发前言
1.1 软件准备
STM32CubeMX 代码生成软件
MDK 集成代码开发环境
1.2 开启FreeRTOS
新建一个 CubeMX 工程,在配置好时钟后,点击 Middleware -> 选择 FreeRTOS -> 下拉框选择 V2 版本 CMSIS
到此在 CubeMX 中就已经开启 FreeRTOS 系统了,下面分享 FreeRTOS 的配置:
二、配置界面
开启 FreeRTOS 之后,可以看到配置项主要分为以下几个部分
这几个部分的主要功能如下表:
以上各个功能分的很清晰,我们需要配置什么功能就去对应的选项下进行配置,下面根据各个配置项进行详细配置介绍
三、系统设置
首先我们先了解一下 Config Parameters,他的配置参数如下
参数功能表:
API 和 Version 不过多解释,显示版本信息
2.1 调度内核设置
Kernel Setting 是 FreeRTOS 的调度内核配置,展开后有下面的配置项,使用时一般保持默认,也可以根据需要修改
USE_PREEMPTION
USE_PREEMPTION 是 RTOS 的调度方式选择,为 1 时使用抢占式调度器,为 0 时使用协程,如果使用抢占式调度器的话内核会在每个时钟节拍中断中进行任务切换,当使用协程的话会在如下地方进行任务切换
一个任务调用了函数 taskYIELD()。
一个任务调用了可以使任务进入阻塞态的 API 函数。
应用程序明确定义了在中断中执行上下文切换。
CPU_CLOCK_HZ
CPU_CLOCK_HZ 是 CPU 系统时钟频率,默认使用的是晶振通过时钟树后获得的时钟频率
TICK_RATE_HZ
TICK_RATE_HZ 是 RTOS 的心跳时钟频率,默认为最大值 1000 ,即心跳时钟 1ms 跳动一次
MAX_PRIORITIES
MAX_PRIORITIES 是 RTOS 任务的最高优先级设置,默认56级,一般来说一个优先级表是32位,这里用了两个,对应64位,其中8位用于系统任务的优先级处理
MINIMAL_STACK_SIZE
MINIMAL_STACK_SIZE 设置分配给空闲任务的堆栈大小,该值是用字(32位)指定的,而不是字节,默认为128个字,如果修改过空闲任务,则根据实际情况修改
MAX_TASK_NAME_LEN
MAX_TASK_NAME_LEN 设置任务名称的最大字符数,默认16位足够
USE_16_BIT_TICKS
USE_16_BIT_TICKS 存放 Tick 周期的计数器的数字位宽,默认为 Disable 即 16 位
IDLE_SHOULD_YIELD
如果IDLE_SHOULD_YIELD 设置为0,则空闲任务永远不会让位于另一个任务,只在被抢占时才会离开运行状态。如果 IDLE_SHOULD_YIELD 设置为1,那么当有另一个空闲优先级任务处于Ready状态时,空闲任务将不会执行它定义的功能的不止一次迭代,而不会让位于另一个任务,这确保当应用程序任务处于空闲状态时,在空闲任务中花费的时间最少,即同在空闲优先级下,空闲任务优先级更高,不会被抢占,不会以时间片运行
USE_MUTEXES、USE_RECURSIVE_MUTEXES、USE_COUNTING_SEMAPHORES
为 1 则开启系统构建过程中的互斥量、递归互斥量和信号量,该值强制为1(ENABLE)
QUEUE_REGISTRY_SIZE
队列注册表的大小,可以用于管理队列名称和队列实体,方便运行中进行查看与管理,默认为8
USE_APPLICATION_TASK_TAG
使能时会给任务一个 TAG 标签,便于用户进行使用
ENABLE_BACKWARD_COMPATIBILITY
一个兼容性使能,使能后, FreeRTOS 8.0.0 之后的版本可以通过宏定义使用 8.0.0 版本之前的函数接口,默认使能
USE_PORT_OPTIMISED_TASK_SELECTION
查找下一个任务方式的选择,查找下一个就绪任务就是查找优先级表,对优先级表进行导0算法,分为通用切换或者针对性切换,一般默认不使能,使用通用切换,通用切换使用C编写,执行效率低,兼容性高;针对性切换使用处理器自带的导0指令,使用汇编编写,切换效率高,但兼容性差
USE_TICKLESS_IDLE
使能后会生成的两个空函数PreSleepProcessing和PostSleepProcessing,用户可以编写代码进入低功耗模式,生成函数如下图
USE_TASK_NOTIFICATIONS
任务通知使能,每个RTOS任务都有一个32位的通知值,RTOS任务通知是一个直接发送给任务的事件,它可以解除接收任务的阻塞,并可选地更新接收任务的通知值,为1开启,为0关闭,关闭可以为每个任务节省8个字节的内存空间
RECORD_STACK_HIGH_ADDRESS
记录任务的堆栈入口地址到TCB,为1使能,为0关闭
2.2 内存管理设置
内存管理可以看到3个配置参数
Memory Allocation
内存分配方式,此处默认动态和静态都可以
TOTAL_HEAP_SIZE
内存堆的分配大小,堆本质上就是一个数组,此处是设置堆数组的大小,设置时要考虑最小要满足所有任务的使用要求,最大不要超过系统的分配上限
Memory Management scheme
内存分配方式,有heap_1.c, heap_2.c, heap_3.c, heap_4.c and heap5.c 5种,其中1、2、4、5都是先建立一个堆数组,从数组中申请,用完再释放,与C语言中molloc和free使用链表的方式不同,该方式在 MCU 中更安全稳定,此处默认使用的方式4,具体申请释放方式可以在heap4.c中阅读到
关于堆和栈的区别,可以阅读我的另外一篇文章进行了解:C语言:内存四区
2.3 钩子函数配置
钩子函数是一种回调函数,用于在任务执行一次之后或者某些事件发生后执行的函数,该配置项里面有五个选项,控制5种不同功能的钩子函数开启,当然用户也可以在代码中自己定义
USE_IDLE_HOOK
使能后,系统生成一个空回调函数,由用户编写函数主体
void vApplicationIdleHook(void)
1
每当空闲任务执行一次,钩子函数都会被执行一次
USE_TICK_HOOK
使能后,系统生成一个空回调函数,由用户编写函数主体
void vApplicationTickHook(void)
1
每个TICK周期,钩子函数都会执行一次
USE_MALLOC_FAILED_HOOK
使能后,系统生成一个空回调函数,由用户编写函数主体
void vApplicationMallocFailedHook(void)
1
当申请动态内存失败时,钩子函数会执行一次
USE_DAEMON_TASK_STARTUP_HOOK
使能后,系统生成一个空回调函数,由用户编写函数主体
void vApplicationDaemonTaskStartupHook(void).
1
任务刚启动时,钩子函数会执行一次
CHECK_FOR_STACK_OVERFLOW
使能后,系统生成一个空回调函数,由用户编写函数主体
void vApplicationStackOverflowHook( xTaskHandle xTask, signed char *pcTaskName );
1
任务栈溢出时,钩子函数会执行一次,传入任务 TCB 和任务名称
当我们在 CubeMX 里面开启对应钩子函数,生成代码之后,在FreeRTOS就可以看到自动生成的钩子函数,我们在里面编写相应的功能就行
2.5 任务运行追踪配置
功能配置项如下:
GENERATE_RUN_TIME_STATS
开启时间统计功能,在调用 vTaskGetRunTimeStats() 函数时,将任务运行时间信息保存到可读列表中
USE_TRACE_FACILITY
使能后会包含额外的结构成员和函数以帮助执行可视化和跟踪,默认开启,方便 MDK 软件工具调试使用
USE_STATS_FORMATTING_FUNCTIONS
使能后会生成 vTaskList() 和 vTaskGetRunTimeStats() 函数用于获取任务运行状态
2.6 协程配置
Co-routine related definitions 是协程的配置项,两个选项用来配置协程是否开启,以及协程的优先级,开启后,需要用户手动创建协程,在协程几乎很少用到了,是 FreeRTOS目前还没有把协程移除的计划,但 FreeRTOS是不会再更新和维护协程了,因此大家解一下就行
协程特点:
堆栈使用
所有的协程使用同一个堆栈(如果是任务的话每个任务都有自己的堆栈),这样就比使用任务消耗更少的 RAM
调度器和优先级
协程使用合作式的调度器,但是可以在使用抢占式的调度器中使用协程
宏实现
协程是通过宏定义来实现的
使用限制
为了降低对 RAM 的消耗做了很多的限制
具体 API 接口和调度原理可以参考这篇文章 : FreeRTOS协程
2.7 软件定时器配置
软件定时器配置的一些相关项如下:
这四个配置项主要与软件定时器处理任务有关,软件定时器任务属于系统任务(守护线程),开启软件定时器后用于维护软件定时器
USE_TIMERS
默认开启软件定时器任务
TIMER_TASK_PRIORITY
软件定时器任务优先级
TIMER_QUEUE_LENGTH
定时器任务队列长度,FreeRTOS 是通过队列来发送控制命令给定时器任务,叫做定时器命令队列,此处设置队列长度
TIMER_TASK_STACK_DEPTH
软件定时器任务堆栈大小
2.8 中断优先级配置
LIBRARY_LOWEST_INTERRUPT_PRIORITY
此宏是用来设置最低优先级,FreeRTOS 使用的4位优先级,对应16位优先级,对应的最低优先级为15
LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
设置FreeRTOS 系统可管理的最大优先级,也就是设置阈值优先级,这个大家可以自由设置,这里设置为5,也就是高于5 的优先级(优先级数小于5)不归 FreeRTOS 管理
三、内核裁剪
Include Parameters 下的选项应用于内核裁剪,裁剪不必要的功能,精简系统功能,减少资源占用,主要有以下几个选项:
配置项可裁剪的函数功能如下:
四、创建任务与队列
4.1 CubeMX 下任务创建与配置
任务(线程)是操作系统运行的基本单元,也是资源分配的基本单元, CubeMX 任务的创建基本以图形化进行,配置方式如下
进入Tashs and Queues 配置,点击 Add 添加新任务
任务配置参数介绍
设置完成后点击OK,配置就完成了,之后生成代码,使用 MDK 进一步配置任务的具体信息
在生成的代码中,我们打开 freertos.c 文件可以在代码中看到任务的配置信息
在 freertos.c 文件的末尾部分,我们可以看到生成的任务实体
任务实体本身就是一个死循环函数,循环执行程序代码,但循环体代码里面必须要有延时函数,释放当前任务对 MCU 的控制权,使其他低优先级可以执行,此外,关于任务,CubeMX 提供了一系列的用户调用接口函数,具体如下
4.2 CubeMX 下队列的创建与配置
队列,又称为消息队列,用于任务间的数据通信,传输数据,在操作系统里面,直接使用全局变量传输数据十分危险,看似正常运行,但不知道啥时候就会因为寄存器或者内存等等原因引起崩溃,所以引入消息,队列的概念,任务发送数据到队列,需要接受消息的任务挂起在队列的挂起列表,等待消息的到来,CubeMX 创建队列的步骤如下:
先点击 Add 添加队列
队列配置参数介绍
配置需要的参数后,点击OK,然后生成代码
生成代码后,我们可以在 freertos.c 中系统初始话函数中看到队列的初始化
初始化函数会在一开始被调用,对 FreeRTOS 系统和内核对象进行初始化,初始化后系统就可以进行调度和使用内核对象,CubeMX 生成的代码自动将创建的内核对象放到初始化函数内,所以我们在任务和中断中直接使用就可以,队列的 FreeRTOS API 接口在CubeMX 内再次进行了封装,使用更加简单,使用方式如下:
我们使用的 CMSIS 2.0 版本,所以在任务文件中包含调用声明头文件
#include "cmsis_os2.h"
1
在队列头文件内我们可以在 600 多行的位置找到有关队列的 API 函数声明:
下面介绍一下队列有关接口的函数接口:
以上的API接口有其对应的传入参数,具体使用方式需要在翻源码的注释,这里我选常用的来介绍一下:
消息队列常用的是插入与获取消息,初始化系统已经帮助我们完成,在初始化的时候会获取一个队列的句柄,之后对队列的操作都是围绕这个句柄展开,比如上面的代码中,句柄就是 myQueue01Handle ,我们发送一个消息到这个队列,就是调用发送函数,对句柄进行操作,先看一下发送消息的函数原型
osStatus_t osMessageQueuePut (osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, uint32_t timeout);
1
参数的功能
返回值的可能
所以我们发送一个消息到队列,函数用法如下:
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 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
发送消息的优先级暂时无用,CubeMX 对 FreeRTOS 的支持还不完善,发送消息里面的优先级未使用到,并且入队方式使用的是发送到队列尾部,没有从头部插入的方式,有需求可以 通过包含 queue.h 文件,调用 FreeRTOS 的官方代码,或者自己修改 生成代码的 API 接口结合优先级使用队列的向前插入和向后插入,丰富系统功能!
除了发送消息到队列,接受队列的消息 API 接口也经常用到,函数原型如下
osStatus_t osMessageQueueGet (osMessageQueueId_t mq_id, void *msg_ptr, uint8_t *msg_prio, uint32_t timeout);
1
参数的功能
函数用法
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 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
注意:FreeRTOS 中获取和发送消息的 API 接口函数分为任务中调用和中断中调用,CubeMX 代码接口将两者整合了,调用时自动判断调用环境是在 ISR 还是正常运行环境中
五、创建定时器和信号量
5.1 CubeMX下定时器的创建和配置
软件定时器本质上就是设置一段时间,当设置的时间到达之后就执行指定的功能函数,调用的这个函数叫做回调函数。回调函数的两次执行间隔叫做定时器的定时周期,简而言之,当定时器的定时周期到了以后就会执行回调函数,下面介绍一下 CubeMX 中开启定时器的方法:
在 CubeMX 里面按下面步骤添加定时器
然后配置具体参数,参数的功能如下:
参数配置完成后,生成代码,我们可以在 freertos.c 文件里面看到定时器创建后获得的句柄,以及生成的回调函数:
有了句柄,我们就可以调用 cmsis_os2.c 里面的定时器接口函数对定时器进行操作,先看一下 CubeMX 提供的定时器接口函数及其功能
其中常用的接口是定时器的启动和停止
定时器启动: osTimerStart,函数原型
osStatus_t osTimerStart (osTimerId_t timer_id, uint32_t ticks);
1
参数介绍:
此处的 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 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
按照例程启动定时器,定时器会以 10个tick 的周期,调用回调函数
回调函数不要放阻塞函数,程序尽可能短
定时器启动: osTimerStop,函数原型
osStatus_t osTimerStop (osTimerId_t timer_id);
1
参数只有一个,就是定时器的控制句柄,传入即可停止定时器,例程如下
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 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
软件定时器是由软件定时器维护任务进行维护,检测各个定时器的状态,进行处理,回调回调函数,软件定时器维护任务的参数配置在前面的 Config 就已经提到过
5.2 CubeMX下信号量的创建和配置
信号量是 RTOS 的一个内核对象,该对象有一个队列表示该信号量拥有的信号数目,任何任务都可以对这个信号数目进行获取和释放,获取时信号-1,释放时信号+1,为0时不能继续获取,此时有任务想要继续获取信号量的话,任务会挂起在该内核对象的挂起列表,等到信号可以获取时进行恢复,根据这个特性,信号量常用于控制对共享资源的访问和任务同步,下面介绍一下 CubeMX 下信号量的配置:
点开配置页面,可以看到有两个信号量添加页面,其中 Binary Semaphores 是二值信号量,Counting Semaphores 是计数信号量,二进制信号量,仅有一个队列或者说 token,用于同步一个操作;计数信号量则拥有多个 tokens,可用于同步多个操作,或者管理有限资源
二值信号量创建:
点击 Add,配置参数
参数介绍
计数信号量:
点击 Add,配置参数
参数介绍
配置完成后我们生成代码,在 freertos.c 的初始化代码中可以看到信号量被创建,并且返回了信号量的控制句柄
下面介绍一下 CubeMX 提供的信号量操作函数接口:
其中常用的函数有获取和释放信号量,下面介绍一下这两个函数的参数和使用方式
获取信号量 osSemaphoreAcquire
函数原型
osStatus_t osSemaphoreAcquire (osSemaphoreId_t semaphore_id, uint32_t timeout);
1
参数介绍
使用例程
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 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
释放信号量 osSemaphoreRelease
函数原型
osStatus_t osSemaphoreRelease (osSemaphoreId_t semaphore_id);
1
使用例程
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 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
二值信号量和计数信号量的操作基本一致,没用区别,只是用有的信号队列最大数目不同而已
同时注意信号量在使用过程中会出现优先级反转的Bug,使用时需要注意
六、创建互斥量
6.1 CubeMX下互斥量的创建和配置
互斥量其实就是一个拥有优先级继承的二值信号量,互斥信号量适合用于那些需要互斥访问的应用中,在互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这个钥匙去使用资源,与信号量不同的是,互斥量的释放必须由获取他的任务进行释放,如果不释放,可能会造成死锁
死锁就是两个任务获取对方拥有的锁,各自进入挂起列表,无法释放互斥锁
下面介绍一下 CubeMX 下互斥量的配置,在配置界面我们可用看到两个互斥量配置界面,上面的是普通互斥量,其获取只能获取一次,重复获取是无效的,而第二个则是递归互斥量,递归互斥信号量可以获取多次,但对应的也要释放多次才能让出使用权,比如我获取3次,任务要释放3次才能释放该互斥量的使用权
使用互斥量,需要点击 Add 然后配置参数
参数介绍:
递归互斥信号量的配置方式与其相同,包括配置参数也相同,两者只是在用法上有些许区别,添加方式如下:
添加配置完成后,点击生成代码,在 freertos.c 文件中我们可以看到互斥量初始化完成,并且生成了对应的控制句柄
CubeMX 提供的 API 接口函数如下
主要使用到的还是互斥量的获取与释放,下面分析一下这两个函数:
获取互斥量 osMutexAcquire
函数原型
osStatus_t osMutexAcquire (osMutexId_t mutex_id, uint32_t timeout);
1
参数介绍:
使用方式
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 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
释放互斥量 osMutexRelease
函数原型
osStatus_t osMutexRelease (osMutexId_t mutex_id);
1
参数介绍:
使用方式
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 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
使用方式和信号量基本相同,因为互斥量本质上就是信号量的一种
七、创建事件标志组
7.1 CubeMX下事件的创建和配置
任务间的同步除了信号量还有时间标志组,信号的同步通常是一对一的同步,有的时候系统需要多对一的同步,比如同时满足5个按键按下时,任务启动,如果使用信号会很占据资源,所以 RTOS 引入了事件标志组来满足这一需求,下面我们看一下 CubeMX 内事件标志组的配置方法:
点击 Add 创建事件标志组
配置介绍
配置完成后,生成代码,在系统初始化内,看有没有生成事件标志组控制句柄,可以看到句柄创建完成
CubeMX 提供的配置事件标志组的接口 API 如下:
常用的 API 接口是设置事件标志组以及等待事件标志组的触发,下面我们分析一下这两个 API
在了解 API 前我们需要简单了解一下事件的触发原理:首先事件标志组的数据类型为 EventGroupHandle_t,事件标志组中的所有事件位都存储在一个无符号的 EventBits_t 类型的变量中,当 configUSE_16_BIT_TICKS 为 1 的时候事件标志组可以存储 8 个事件位,当 configUSE_16_BIT_TICKS 为 0 的时候事件标志组存储 24个事件位,每个事件位其实就是一个0或者1数字,就像下面的24位组成一个事件标志组
我们在使用事件API接口函数前需要先定义我们需要的触发事件位,比如添加如下的代码
#define event1 1<<1 //事件1 #define event2 1<<2 //事件2
1
2
编写好触发事件后,我们在看如何使用 API 接口
设置事件标志 osEventFlagsSet
函数原型
uint32_t osEventFlagsSet (osEventFlagsId_t ef_id, uint32_t flags);
1
参数介绍:
使用方式:设置事件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 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
等待事件标志 osEventFlagsWait
函数原型
uint32_t osEventFlagsWait (osEventFlagsId_t ef_id, uint32_t flags, uint32_t options, uint32_t timeout);
1
参数介绍:
使用例子:同时等待事件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 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
八、用户常量
User Constants 用于添加用户常量,将不变的量转化为常量保存,可以节省 RAM 资源空间,因为常量和变量的保存位置不同,详细了解可以参考这篇文章:C语言:内存四区
九、任务通知
FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量 ulNotifiedValue 就是这个通知值。任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态,CubeMX内没有提供相关的配置项,但在其生成的 FreeRTOS 接口里面有相关函数进行配置,函数位置如下:
接口函数功能:
常用的两个 API 就是设置任务通知和等待任务通知函数
设置通知 osThreadFlagsSet
函数原型
uint32_t osThreadFlagsSet (osThreadId_t thread_id, uint32_t flags);
1
参数介绍:
使用方式
先定义一个事件标志
#define event1 1<<1 //事件1
1
然后调用 API 通知对应任务事件发生
void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ osStatus_t result; /* Infinite loop */ for(;;) { result = osThreadFlagsSet(myTask02Handle,event1); if(result == osOK) { //设置成功 }else { //设置失败 } osDelay(1); } /* USER CODE END StartDefaultTask */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
等待通知 osThreadFlagsWait
函数原型
uint32_t osThreadFlagsWait (uint32_t flags, uint32_t options, uint32_t timeout);
1
参数介绍:
options参数
使用方式
调用 API 等待对应的任务通知就绪,当其他任务设置到对应的通知后,任务恢复运行
void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ osStatus_t result; /* Infinite loop */ for(;;) { result = osThreadFlagsWait(myTask02Handle,osFlagsWaitAll,event1); if(result == osOK) { //等待成功 }else { //等待失败 } osDelay(1); } /* USER CODE END StartDefaultTask */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
任务通知其实个任务事件标志组使用上没有多大的区别,但他们两个的实现原理不同,同时任务通知对资源的占用更少
根据 FreeRTOS 官方的统计,使用任务通知替代二值信号量的时候任务解除阻 塞的时间要快 45%,并且需要的 RAM 也更少
十、系统内核配置
CubeMX 生成的代码中封装了一系列内核配置函数,有些函数也经常使用到,比如获取时间戳和调度器管理的函数,这里不做过多解释,简单的介绍一下函数的功能
API 嵌入式
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。