我正试图从软件测试角色切换到嵌入式,并一直在学习STM32,几乎完成了I2C驱动程序的编写,我使用I2C与传感器接口。我看了很多工作描述,其中大多数确实发现了解RTOS是至关重要的,我想知道我是否可以以任何方式将其融入我现有的简单项目中。我想到的一种方法是,可能有两个线程,一个读取传感器上的值,另一个显示在屏幕上。不确定RTOS对于这个特定用例是否真的很重要,只是一个想法。
任何想法都值得赞赏
前言
正如我所理解的你的问题,你正在思考如何编写你自己的RTOS项目,在做了一些HAL编程、做了一段时间的测试人员之后,使用RTOS进行嵌入式编程的一些第一步,现在正试图从测试人员角色"成长"为开发人员角色(从测试到开发是否总是意味着成长,这至少值得怀疑,但至少在某些领域,开发人员的回报往往比测试人员更好。)
因此,我假设您知道如何在STM32上基于主循环架构制作嵌入式软件应用程序。
任何想法都会受到的赞赏
你的问题是非常开放的,所以我敢尝试草拟一个如何制定你(整个)个人教育项目的计划。我向那些认为这个答案过于宽泛的读者道歉。
1.RTOS两个任务示例,从传感器到显示器
你提到的例子是一个非常著名的教科书例子,因为它非常适合在教科书的背景下展示RTOS的好处:通过将两个完全不相关的系统组件部署到不同的任务来支持它们的功能分解。
这是否是一个很好的示例应用程序来学习为个人教育目的实际集成RTOS,这是一个不同的问题。这取决于你对这两项任务中所做事情的熟悉程度,这两项工作应该尽可能好,这样你就可以很好地专注于RTOS驱动的软件体系结构的细节。一个任务("IN")处理
读取传感器的值
- 我假设您使用的是您已经处理过的I²C传感器。-完美的另一项任务("OUT")处理
显示在屏幕上。
我个人已经开发RTOS固件几年了,但我从未真正集成过嵌入式显示器。如果我自己执行这个计划,我会把值转储到另一个串行接口(比如说,当使用普通Nucleo板的STM32时,连接到虚拟串行端口的UART),并在我的电脑上有一个终端窗口作为实际显示器。请根据您自己的技术体验调整您的个人计划。
2.为RTOS准备软件
从你已经做得很好的开始:让一个主循环固件在你的目标上正确运行,在那里你可以很好地分离这两个功能,注意在这两个部分之间有一个良好而精简的接口(IN组件只与OUT通信它需要知道的内容)。最重要的是,IN将数据更新放入OUT的接口应是事件驱动的,而不是轮询,即IN仅在有更新内容时调用函数,而OUT不调用任何函数来"探查"IN是否有新信息要报告。
目前,两个组件都应该有一个从主循环调用的"tick"函数,这样任何一个组件都可以在该函数中为自己做所需的一切。例如,IN任务使用您的驱动程序在I²C外围设备(或其DMA)上周期性地触发获取数据请求。
3a。将RTOS添加到单片固件
现在,请集成RTOS库,使其运行包含以前主循环的单个任务。提示:旧的主循环成为您唯一的"默认任务",main()函数在启动RTOS调用后结束(如果您犯了配置错误,可能会有一个断言语句捕获任何意外返回)。启动调度程序的调用通常不会返回,因为主(系统)上下文将被RTOS任务上下文取代。
如果您还没有决定选择哪种RTOS,我建议您选择freeRTOS(即OSS),并由STM32CubeMX进行预配置(也是免费的),这样您就不必一次解决每个细节。如果您选择freeRTOS,那么启动RTOS的最后一个调用将是"osKernelStart()"。
完成安装和调试,使其再次运行。
3b。让它成为一款多任务软件
这是当你的固件成为一个真正的RTOS固件(而不仅仅是一个)。您需要另一个任务-将它插入RTOS配置中现有任务的旁边,然后创建另一项任务函数。任务函数看起来像"main()"函数,由一个一次性初始化部分和一个无限循环组成。从预先存在的任务中删除一个组件,并将其放入新任务中。
现在,您必须用任务间通信来替换上面的"put"函数调用。设置一个由OUT任务读取的队列。每次OUT都可以从队列中取出数据更新,它必须将其应用于"显示器"(例如,传输一系列UART帧)。让IN任务将数据结构放入您创建的队列中。注意:队列句柄(创建后的常量指针)应是两个任务都看到的唯一变量符号。除此之外,任务将生活在不同的世界中。
3c。任务触发次数
假设您的OUT任务可以使用一次性操作,如将数据结构放入DMA缓冲区并触发DMA,则该任务仅在从IN任务通过"其"队列时运行。也就是说,除非有工作要做,否则它不会占用CPU时间。给这个任务分配比IN任务更高的优先级。
现在,IN任务将只在OUT任务不忙时运行。目前,如果IN任务无所事事,您可以让它闲置,直到您希望它从传感器中检索下一组数据。完成代码并调试它,这样您就有了一个正在运行的系统。
当然,您不希望IN任务消耗几乎所有的CPU时间,而只是它所需要的时间。重新检查您的I²C驱动程序接口,并决定必须多久调用一次。设置重新加载计时器(根据需要使用硬件计时器或软件计时器,稍后尝试其他选项)。定时器回调(针对硬件定时器:ISR处理程序)应向IN任务发送一个事件。IN任务应监听该事件(并在没有事件时阻止)。每当事件来自计时器回调时,IN任务就会唤醒,从传感器获取数据,将其放入队列中以输出,并等待下一个事件再次被阻止。
现在您需要一个空闲任务,RTOS会在所有生产任务都被阻止时将其唤醒。IMHO,闲置的任务不会有任何作用,但这是一个哲学争论。重新完成编码和调试。发现你的系统再次运行,微笑并吃一些冰淇淋:-)
4.添加更多好东西
您掌握了关键点,现在通过以下几点加深您的RTOS知识/经验:
- 检查RTOS库提供的各种进程间通信,并在系统中试用
- 用新奇的东西添加更多的任务。例如,根据传感器值,使用另一个队列/任务来驱动LED
- 设置SWV跟踪/调试,并调查每个任务(ITM/SWO)产生的负载以及任意断点时刻的运行时状态
- 整理你的代码,找出模块边界的位置
- 在RTOS之上使用操作系统抽象层