每当提起FreeRTOS在单片机的应用,我总是会联想到产品的任务功能调度问题。但是感觉相对比裸机开发,我觉得FreeRTOS的使用和理解更加难以掌握。所以写下这个博客来记录一些乱七八糟的问题和感想。
配置流程
1、定义任务(编写任务函数)、定时器(定时器函数会每隔N个时间间隔执行一次定时器函数)、信号量(在函数中分配信号量,根据信号量的获取与否来触发任务函数的运行)和事件标志(不同的任务可以根据自身的执行结果设置相应的标志位,而其他任务可以查询这些标志位,根据不同的状态信息执行不同的操作)的句柄和属性。
2、FreeRTOS初始化函数,用来创建任务、信号量和事件标志,启动定时器。
3、开始默认任务函数(StartDefaultTask), 对任务函数进行编写。编写定时器函数。
4、在函数中分配信号量,完成每个任务函数间的功能调度。
队列 信号量 事件组
简介
近期在做一个关于心率和摔倒检测的项目,其实任务的数量不多不需要用到Freertos来写,不过因为也是刚学习完freertos,所以就想着用freertos来实现,不过主要还是它有开源的文件哈哈哈,也算是在开发的过程中有个参考了。
开发流程
FreeRTOS开发相较于裸机开发的话,个人认为主要的区别还是在于FreeRTOS开发需要额外的在freertos.c文件里进行模块功能的编写以及各模块任务进行调度和协调,任务的优先级以及突发性的任务优先级协调。在裸机开发下可以直接在main.c文件下实现所有的功能,但是加入FreeRTOS后,main.c文件可能仅仅只是一个用来写初始化函数的文件了。因为我们可以在目录下创建一个模块功能的驱动文件夹来写各个模块的驱动函数,然后接下来在FreeRTOS下对有需要的模块进行引用就行了。
开发过程中使用到的任务调度方法
由于项目的需要,更多是要在正常运行中处理突发性摔倒事件以及对心率检测功能任务的唤醒,所以更多会使用事件组、信号量以及软件定时器来处理,对突发性事件需要使用事件组来分配比特位用来为后续触发提供凭证。
说到的软件定时器实际上的理念是想通过系统的中断模式来实现多任务系统以及高实时性的场景。这样的话就需要在程序的编写中加入中断函数以及回调函数,只要数据到达,即可触发中断。非阻塞状态下可以让CPU在等待数据时执行其他任务,因此就需要处理中断优先级、等待数据的过程中设置数据缓冲区来装载数据。
关于信号量,最主要的就是Give操作和Take操作,信号量代表着系统运行的过程中去实现基本的任务以及功能。当触发了某个事件,会给该事件提供一个任务量CNT(就是Give操作),然后通过Take操作来调用Take操作下的功能函数实现该模块的功能。由于CNT并不是无限的,而且具有一定的优先级分配,所以在更加复杂的场景下需要分配各任务的优先级。
事件组是 FreeRTOS 提供的一种 轻量级同步机制,主要用于 任务间通信(IPC) 和 多事件状态管理。充当了一个状态标记器的角色,不同的任务可以根据自身的执行结果设置相应的标志位,而其他任务(如 OLEDShow 任务)可以查询这些标志位,从而根据不同的状态信息执行不同的操作。其核心功能包括:
-
多事件等待
-
允许任务同时等待多个事件(如信号量、消息、硬件中断等),任一事件触发即可唤醒任务。
-
-
事件广播
-
一个任务或中断可以设置(置位)事件标志,其他等待该事件的任务会被唤醒。
-
-
高效同步
-
比信号量、队列更节省内存,适合需要同时处理多种事件条件的场景(如传感器数据就绪 + 用户输入 + 超时)。
-
工作原理:
-
数据结构
-
事件组本质上是一个 位掩码(Bit Mask),每个事件对应一个二进制位(如
bit0表示“数据就绪”,bit1表示“错误发生”)。 -
FreeRTOS 使用
EventBits_t类型(通常为 8/16/24/32 位)存储事件标志。
-
-
核心操作
-
设置事件(置位):通过
xEventGroupSetBits()或xEventGroupSetBitsFromISR()设置指定标志位。 -
等待事件:任务调用
xEventGroupWaitBits()阻塞等待,直到指定事件位被置位(可配置逻辑“与”或“或”)。 -
清除事件:通过
xEventGroupClearBits()手动清除标志位。
-
-
同步逻辑
-
逻辑“或”:等待任一事件触发即可唤醒任务(如
bit0 | bit1)。 -
逻辑“与”:需所有指定事件均触发才会唤醒任务(如
bit0 & bit1)。
-
任务管理,内存管理
抢占式任务调度特性:
协调式任务调度特性:
关于 Memory Management scheme:我们一般采用heap_4,也是系统默认的方式。
程序代码分析
xTimerStop() 是 FreeRTOS 实时操作系统 提供的 API 函数,用于 停止一个已启动的软件定时器(Software Timer)
sprintf函数
sprintf(s, "Heart:%.2f", HeartFre);
-
关键部分:
-
"Heart:%.2f":格式化字符串,%.2f表示将浮点数保留 2位小数。 -
HeartFre:待格式化的浮点数值(如心率数据)。 -
s:存储结果的字符数组(需提前分配足够空间)
-
sprintf函数输出的目标是一个数组,%.2f意思是将浮点数保留2位小数
osTimerStart 函数
-
启动 一个已创建但未运行的定时器。
-
重启 一个正在运行的定时器(重新开始计时)。
调用后,定时器会从初始设定的时间值(period)开始倒计时,超时后会触发绑定的回调函数(如果配置为周期性定时器,则会自动重复)。
xEventGroupSetBits(TaskStauteHandle,1<<1)函数
-
作用
-
将
TaskStatusHandle事件组的 第 1 位(bit 1) 设置为1(其他位保持不变)。 -
1 << 1表示将数字1左移 1 位,即0b00000010(二进制)或2(十进制)。 -
其他任务可以通过
xEventGroupWaitBits()监听这些比特位的变化,实现同步。
-
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)函数
-
中断通知:当串口通过
HAL_UART_Receive_IT()启动的接收操作完成(收到指定长度的数据)后,HAL 库会在中断服务程序(ISR)中调用此函数。 -
用户自定义处理:开发者可以在此函数中编写代码,处理接收到的数据(如解析协议、触发任务等)。
HAL_UART_Receive_IT(&huart1,(void *)&USART1_RXbuff,1);函数
通过中断(非阻塞)方式启动串口接收,每次接收 1 个字节的数据,并将数据存储到 USART1_RXbuff 中。
-
作用:配置串口
huart1以 中断模式 接收数据,每次收到 1 个字节 后,将数据存入USART1_RXbuff,并触发 中断回调函数HAL_UART_RxCpltCallback()。 -
非阻塞:中断模式下CPU可以执行其他任务。函数立即返回,不会阻塞 CPU,后续数据接收由中断处理。

4799

被折叠的 条评论
为什么被折叠?



