FreeRTOS是一个实时操作系统(RTOS),专门为微控制器和小型微处理器设计。它为在资源受限的设备上开发需要精确定时和任务管理的应用程序提供了可靠的基础。
本指南中的所有示例代码都可以在这个repo: FreeRTOS on Xiao ESP32S3上找到
什么是FreeRTOS?
FreeRTOS是一个轻量级操作系统,支持在微控制器上进行多任务处理。它允许开发人员创建可以同时执行多个任务的应用程序,同时有效地管理系统资源。FreeRTOS中的“自由”指的是它的开源性质,在MIT许可下可用,使其可用于商业和个人项目。
嵌入式系统对FreeRTOS的需求
FreeRTOS解决了嵌入式系统开发中简单顺序编程无法有效处理的关键挑战。以下是它的必要性:
传统编程的问题
在传统的“超级循环”微控制器编程中,代码在无限循环中依次运行。这种方法有几个局限性:
•时间问题:当多个任务需要不同的时间要求时,管理它们变得越来越复杂,因为有延迟函数。
•响应性问题:长时间运行的任务会阻塞其他一切。例如,如果您读取传感器需要1秒,则系统在此期间无法响应其他事件。
•代码复杂性:随着项目的增长,在单个循环中管理多个设备交互变得笨拙且容易出错。
•资源争用:如果没有适当的同步,访问共享资源可能导致数据损坏或不可预测的行为。
使用FreeRTOS的好处
FreeRTOS通过以下方式解决这些问题:
1. 真正的多任务处理
FreeRTOS允许多个任务并发运行,而不是一个任务阻塞其他任务。当一个任务等待某些事情(比如传感器读取)时,其他任务可以继续执行。
2. 确定的时间
FreeRTOS提供了机制来确保时间关键的操作在应该的时候发生。任务优先级确保高优先级操作在需要时获得CPU时间。
3. 简化程序结构
你可以这样写,而不是在单个循环中使用复杂的状态机:
处理连接性的一个任务
另一个用于传感器读数
第三个是用户界面元素,每个任务都变得更简单、更集中。
4. 资源管理
FreeRTOS提供了信号量、互斥锁和队列来安全地共享资源和在任务之间通信,防止数据损坏和竞争条件。
5. 功率效率
当任务处于非活动状态时,FreeRTOS可以将处理器置于睡眠模式,从而减少对电池供电设备至关重要的功耗。
现实世界的例子:物联网传感器节点
考虑这样一个设备:
•读取多个传感器
•处理数据
•连接Wi-Fi
•向服务器发送数据
•管理显示
•处理用户输入
如果没有RTOS,时序就会变成一场噩梦——您将需要复杂的状态机、仔细的时序计算,并且仍然会面临响应性问题。
使用FreeRTOS,您可以:
•为每个功能创建单独的任务
•分配适当的优先级
•让调度器处理计时
?使用队列在组件之间传递数据
•实施节能策略
当freeertos是必不可少的
freeertos在以下方面变得尤为必要:
•对时间敏感的应用:工业控制、医疗设备或汽车系统,其中精确定时是至关重要的。
•复杂交互:系统同时管理多个外设、通信和用户界面。
•资源受限的设备:当您需要在处理能力有限的设备上最大化效率时。
•可靠的操作:系统稳定性和可预测行为不可协商的应用程序。
对于骁骁ESP32-S3来说,FreeRTOS非常有价值,因为该板的双核处理器和连接功能(Wi-Fi、蓝牙)与RTOS完美互补,RTOS可以有效地跨核分配任务并管理复杂的通信堆栈。
常见的应用程序
•物联网设备:同时管理传感器、连接和数据处理。
•工业自动化:处理多个控制过程,定时保证。
•消费类电子产品:在执行后台操作时管理用户界面。
•医疗设备:确保关键功能的可靠运行和可预测的时间。
•汽车系统:管理具有不同优先级的多个控制系统。
入门晓ESP32-S3
Seeed Studio Xiao ESP32-S3是一款紧凑但功能强大的开发板,具有:
•ESP32-S3双核处理器
•8MB PSRAM和8MB闪存
•Wi-Fi和蓝牙连接
•USB Type-C,支持本地USB
•多个GPIO引脚在一个小的形式因素
•建立发展环境
•从Arduino .cc安装Arduino IDE
增加ESP32板支持:
•开放Arduino IDE
•转到File > Preferences
•进入Tools > Board > Boards Manager
•搜索“esp32”并安装最新版本
选择正确的板:
•进入Tools > Board > ESP32 Arduino
•选择“XIAO_ESP32S3”
•选择正确的板:进入Tools > board > ESP32 ArduinoSelect “XIAO_ESP32S3”
安装FreeRTOS库:
•FreeRTOS预装了ESP32 Arduino内核
例1:两个led同时闪烁
这个例子演示了如何创建两个独立的任务,每个任务控制一个具有不同闪烁模式的LED。
示意图
代码
在这里查看WOKWI仿真:WOKWI仿真
您可以创建一个副本并使用它进行练习,例如以不同的速率闪烁两个led,甚至添加更多led
例1中的关键概念
包括和定义
该代码包括访问任务创建和管理功能所需的FreeRTOS头文件。
LED引脚被定义为常量,LED1_PIN使用ESP32-S3上的内置LED(引脚2),LED2_PIN表示连接到引脚3的外部LED。
任务处理
这些变量存储对所创建任务的引用。它们用于在以后需要时识别和控制任务(例如,挂起、恢复或删除任务)。
.任务1:LED1闪烁功能
•这个函数将LED1_PIN配置为输出引脚。
•它进入一个无限循环(while(1)),这是需要连续运行的FreeRTOS任务的标准。
循环内部:
•用digitalWrite(LED1_PIN, HIGH)打开LED
•使用vTaskDelay(500 / portTICK_PERIOD_MS)等待500毫秒
•使用digitalWrite(LED1_PIN, LOW)关闭LED
•再等待500毫秒
•环路内部:用digitalWrite(LED1_PIN, HIGH)点亮LED
•使用vTaskDelay(500 / portTICK_PERIOD_MS)等待500毫秒
•使用digitalWrite(LED1_PIN, LOW)关闭LED
•再等待500毫秒
•vTaskDelay函数在这里至关重要。它将控制权交还给FreeRTOS调度器,允许其他任务在延迟期间运行。参数portTICK_PERIOD_MS是一个常量,用于将毫秒转换为FreeRTOS的滴答周期。
任务2:LED2闪烁功能
该函数在结构上与任务1相同,但是:
•它控制LED2_PIN
•它使用更长的延迟1000毫秒(1秒),创建一个不同的闪烁模式
•该函数在结构上与Task 1相同,但是:它控制LED2_PIN,而不是使用更长的延迟1000毫秒(1秒),创建不同的闪烁模式
设置函数
•setup函数以115200波特率初始化串口通信,并等待1秒。
然后使用xTaskCreate()创建两个任务,它有几个参数:
•任务函数:任务(blinkLED1Task或blinkLED2Task)要执行的函数。
•任务名称:用于调试目的的描述性名称
•堆栈大小:为任务堆栈分配的内存量(以字节为单位)(这里是2048字节)
•任务参数:传递给任务的数据(NULL表示没有参数)
•任务优先级:0-24之间的一个数字,数字越高优先级越高;两个任务具有相同的优先级(1)
•任务句柄:存储任务句柄的指针,用于以后的管理
然后使用xTaskCreate()创建两个任务,它有几个参数:任务函数:由任务执行的函数(blinkLED1Task或blinkLED2Task)任务名称:用于调试目的的描述性名称堆栈大小:为任务堆栈分配的内存(以字节为单位)(这里是2048字节)任务参数:传递给任务的数据(NULL表示没有参数)任务优先级:从0到24的数字,其中更高的数字意味着更高的优先级;任务句柄:存储任务句柄的指针,用于以后的管理
循环函数
•循环函数基本上是空的,因为FreeRTOS调度器现在控制任务的执行。
•在循环中包含vTaskDelay()可以防止CPU在空循环中浪费周期。
多任务是如何工作的:
•当ESP32-S3启动时,它运行setup()函数,该函数创建两个LED闪烁任务。
•FreeRTOS调度器接管并开始并发地执行这两个任务。
•任务1一直运行,直到遇到vTaskDelay(),然后调度器暂时挂起它。
•然后调度程序运行Task 2,直到它达到自己的vTaskDelay()。
•当任务进入和退出它们的延迟状态时,调度器会智能地在它们之间切换。
•从用户的角度来看,两个led似乎同时独立闪烁。
•这是FreeRTOS如何在单核或多核微控制器上实现多任务处理的完美示例,允许多个操作并发运行,而无需复杂的手动定时管理。
示例2:使用互联网重新连接的连续数据收集
这个例子演示了持续收集传感器数据,当网络连接中断时将数据存储在本地,并在网络连接恢复后发送数据。
示意图
代码
在这里查看WOKWI仿真:WOKWI仿真
你可以创建一个副本并练习
例2中的关键概念
•任务同步:使用互斥锁(SemaphoreHandle_t)来保护共享资源。
•任务间通信:使用队列(QueueHandle_t)在任务之间传递数据。
•资源管理:在网络不通的情况下,对数据进行本地存储管理
•多任务优先级:赋予连接管理比数据收集和发送更高的优先级。
高级FreeRTOS概念
FreeRTOS中的任务状态
•运行中:任务正在执行。
•Ready:任务可以运行,但需要等待CPU时间。
•阻塞:任务正在等待一个事件(例如,延迟超时,信号量)。
•Suspended:该任务不可调度。
•已删除:任务已删除,但未从内存中移除。
内存管理
FreeRTOS提供的内存分配函数被设计为确定性和避免碎片:
•pvPortMalloc():分配内存
•vPortFree():释放已分配的内存
高效的FreeRTOS应用程序提示
•使用静态分配:在可能的情况下,使用静态分配而不是动态分配,以避免碎片和分配失败。
•谨慎选择任务优先级:为时间紧迫的任务分配更高的优先级,但要注意优先级反转问题。
•避免高优先级任务阻塞:高优先级任务不应该长时间阻塞,因为它们会阻止低优先级任务的运行。
•使用适当的堆栈大小:分配足够的堆栈空间以防止溢出,但不要太多,以免浪费RAM。
•利用事件驱动编程:尽可能使用FreeRTOS通知事件而不是轮询。
•考虑滴答率:根据应用程序的定时要求,适当地配置FreeRTOS滴答率。
•监控CPU使用情况:使用FreeRTOS内置的统计数据收集来识别瓶颈并优化任务性能。
本文编译自hackster.io