嵌入式系统的实时操作系统

电器工程 图片 rtos 嵌入式 微控制器
2022-01-08 01:54:35

我看过很多文章告诉我应该使用 RTOS 进行时间管理和资源管理。我的时间不允许我自己研究,所以我来芯片黑客寻求建议。

我使用低资源微控制器(MSP430、PIC)并且正在寻找我可以使用的 RTOS。

重点:

  1. 系统资源成本
  2. 系统优势
  3. 系统缺点
  4. 实施技巧
  5. 应该/不应该使用 RTOS 的情况。

我不使用像 arduino 这样的系统,我使用的项目无法支持这种系统的成本。

4个回答

除了 QNX 之外,我在 RTOS 方面没有太多的个人经验(总体上很好,但它并不便宜,而且我对特定的主板供应商的体验非常糟糕,而且 QNX 对其他系统的我们不关心的态度比他们最常见的)这对于 PIC 和 MSP430 来说太大了。

您将从 RTOS 中受益的领域包括:

  • 线程管理/调度
  • 线程间通信+同步
  • 具有标准输入/标准输出/标准错误或串行端口或以太网支持或文件系统的系统上的 I/O(大多数情况下不是 MSP430 或 PIC,除了串行端口)

对于 PIC 或 MSP430 的外围设备:对于串行端口,我将使用环形缓冲区 + 中断......我每个系统编写一次并重复使用;其他外围设备 我认为您不会从 RTOS 获得太多支持,因为它们是特定于供应商的。

如果您需要精确到微秒的时序,RTOS 可能无济于事——RTOS 的时序是有限的,但由于上下文切换延迟,它们的调度通常会出现时序抖动……在 PXA270 上运行的 QNX 有抖动通常在几十微秒内,最大为 100-200us,所以我不会将它用于必须运行速度超过 100Hz 或需要比大约 500us 更准确的计时的东西。对于那种东西,您可能必须实现自己的中断处理。一些 RTOS 会很好地解决这个问题,而另一些则会让它成为一种痛苦:你的时间安排和他们的时间安排可能无法很好地共存。

如果时序/调度不太复杂,则最好使用设计良好的状态机。如果您还没有,我强烈建议您阅读C/C++ 中的实用状态图。我们在我工作的一些项目中使用了这种方法,与传统的状态机相比,它在管理复杂性方面具有一些真正的优势……这确实是您需要 RTOS 的唯一原因。

你试过FreeRTOS吗?它是免费的(受 T&C 约束),并且已移植到 MSP430 和多种 PIC 版本。

与其他一些相比,它很小,但这也使它易于学习,特别是如果您以前没有使用过 RTOS。

提供(非免费)商业许可证以及 I​​EC 61508/SIL 3 版本。

我刚刚发现了NuttX RTOS,它甚至可以在 8052(8 位)系统上运行。它没有很多端口,但看起来很有趣。POSIX 可能是一个加分项,因为如果您升级到更强大的处理器并且您想要运行实时 linux 或 QNX,它可能会使您的一些代码更具可移植性。

我自己对商业 RTOS 没有任何经验,但我多年来一直使用自制的!他们非常擅长帮助您将代码开发分配给许多程序员,因为他们基本上每个人都可以获得一个“任务”或“线程”来各自工作。您仍然需要进行协调,并且必须有人监督整个项目,以确保每项任务都能按时完成。

我还建议您在使用 RTOS 时研究速率单调分析或 RMA。这将帮助您保证您的关键任务将按时完成。

我还将研究 Miro Samek 的QP-nano事件驱动编程框架,它可以在有或没有 RTOS 的情况下工作,并且仍然为您提供实时功能。有了它,您可以将设计划分为分层状态机,而不是传统任务。Jason S 在他的帖子中提到了米罗的书。优秀的阅读!

我发现在许多机器上有用的一件事是一个简单的堆栈切换器。我实际上并没有为 PIC 编写一个,但如果两个/所有线程总共使用 31 个或更少的堆栈级别,我希望该方法在 PIC18 上工作得很好。在 8051 上,主要的例程是:

_taskswitch:
  xch a,SP
  xch a,_altSP
  xch a,SP
  ret

在 PIC 上,我忘记了堆栈指针的名称,但例程类似于:

_taskswitch:
  movlb _altSP >> 8
  movf _altSP ,w,b
  movff _STKPTR,altSP
  movwf _STKPTR,c
  返回

在程序开始时,调用 task2() 例程,该例程将 altSP 加载到备用堆栈的地址(对于 PIC18Fxx 来说,16 可能工作得很好)并运行 task2 循环;这个套路绝不能回来,否则事情将痛苦地死去。相反,它应该在想要将控制权交给主要任务时调用 _taskswitch;然后,当主要任务想要让步给次要任务时,它应该调用 _taskswitch。通常,人们会有一些可爱的小程序,例如:

void delay_t1(无符号短值)
{
    任务开关();
  而((无符号短)(毫秒时钟 - val)> 0xFF00);  
}

请注意,任务切换器没有任何“等待条件”的方法;它所支持的只是一个旋转等待。另一方面,任务切换非常快,以至于在另一个任务正在等待计时器到期时尝试 taskswitch() 将切换到另一个任务,检查计时器,然后切换回来比典型的任务切换器更快将确定它不需要任务切换。

请注意,协作式多任务处理有一些限制,但它避免了在可以快速重新建立暂时受到干扰的不变量的情况下需要大量锁定和其他与互斥锁相关的代码。

(编辑):关于自动变量等的一些警告:

  1. 如果从两个线程调用使用任务切换的例程,则通常需要编译该例程的两个副本(可能通过#include 相同的源文件两次,使用不同的#define 语句)。任何给定的源文件要么只包含一个线程的代码,要么包含将被编译两次的代码——每个线程一次——所以我可以使用像“#define delay(x) delay_t1(x)”这样的宏或#define delay(x) delay_tx(x)" 取决于我使用的线程。
  2. 我相信无法“看到”被调用函数的 PIC 编译器会假设这样的函数可能会破坏任何和所有 CPU 寄存器,从而避免需要在任务切换例程中保存任何寄存器 [与抢先式多任务处理]。任何考虑为任何其他 CPU 使用类似任务切换器的人都需要了解使用的寄存器约定。假设存在足够的堆栈空间,在任务切换之前推送寄存器并在之后弹出它们是一种处理事情的简单方法。

协作式多任务处理不允许一个人完全摆脱锁定等问题,但它确实大大简化了事情。例如,在带有压缩垃圾收集器的抢占式 RTOS 中,有必要允许对象被固定。当使用协作切换器时,如果代码假定 GC 对象可以在 taskswitch() 被调用的任何时候移动,则这不是必需的。不必担心固定对象的压缩收集器可能比这样做的要简单得多。