【问题标题】:FSM setting through UART通过 UART 设置 FSM
【发布时间】:2015-01-16 14:58:12
【问题描述】:

关于通过 UART 在 MCU 中设置有限状态机 (FSM),我有一个相当简单的问题。

我有一个相当小的 FSM,大约有 15 个状态,每个状态只与一个事件相关联。 FSM 通过 UART 发送数据来设置,但是,简单数据也通过 UART 接收。一个例子可能是:

  1. 设置 FSM:(例如,在显示器上显示一些数字)。
  2. 发送要显示的数字(数据流紧随其后)。

由于 UART 作为 SPI 控件工作,我想知道是否有一些标准或智能方法来区分接收到的数据是 Set FSM 还是发送的原始数据。我目前的解决方案仅限于,每次发送两个字节,一个设置 FSM,另一个是原始数据。该解决方案携带大量冗余数据,因为我发送的每个原始数据字节都发送 FSM 部分。此外,数据的数量可能会有所不同,这使寻找方法的任务变得复杂,从而简化了问题并减少了所说的冗余数据的数量。

所以我在想你们中是否有人知道更聪明的方法,或者是否有一些通用标准可以遵循?

【问题讨论】:

  • 当心不区分数据的两个元素的解决方案。许多系统在没有这样的工作的情况下构建了一段时间,直到接收者碰巧听到在它正在收听之前开始的消息的第二个字节,并将其误认为是新消息的第一个字节。对此有许多可能的解决方案,但除非使用其中一种,否则系统不会很健壮。
  • 您正在寻找的“标准解决方案”称为bit stuffing。但是对于串行端口,流量控制信号(RTS,DTR,...)是最简单的方法,如果你有的话。

标签: c embedded uart


【解决方案1】:

虽然开销不是最低的,但在串行流中明确编码结构化数据的最简单方法之一是用人类可读的编码表示所有值,并使用可打印或不可打印字符在它们之间提供框架中断。

在“冗长的极端”中,这可能类似于

display 192\n

但还有更简洁的可能性,例如,仍然非常可读

05c0\n

其中“05”可以是 8 位 FSM 模式的两字节十六进制编码,“C0”是 8 位数据值的两字节十六进制编码,“\n”是换行符。由于换行符永远不会被误认为是十六进制数据,因此您的接收者知道它所听到的任何内容都必须是新消息的模式号。

这有很多优点:

  • 很容易分辨每条消息的开始位置,以及收到的每个字符的作用。
  • 您需要的缓冲存储器非常少,因为您可以在必要时将逐个字符接收到状态机中(并且不需要“计算机” - 如果需要,您可以直接在逻辑和触发器中对其进行编码或解释)李>
  • 您可以轻松地与系统的任一端进行交互,方法是使用终端仿真器将自己替换为另一端,而无需像处理二进制格式时那样添加十六进制转储或二进制编码工具
  • 如果您使用十六进制的文本表示,您传输的记录长度正好是实际数据长度加上任何分隔符的两倍。
  • 相反,如果您使用十进制或浮点的文本表示,您的编码长度会有所不同,但您可以避免系统之间字节顺序或浮点二进制编码的任何不兼容性(在一般情况下,尽管这可能不适用于特定的问题在这里)。

(您当然可以通过添加任何保证长度的校验和,例如在数据末尾和换行符之前,使这样的系统更加强大,以防止传输中的数据损坏)

但是,您确实希望避免冗余数据,这显然增加了一些数据。在简单性和紧凑性之间进行权衡,但有些事情您可以轻松做到:

  • 您只有 15 个状态,因此您可以将 8 位的前两个字符的十六进制表示替换为 4 位半字节的一个字符表示。
  • 如果您的系统经常在多个数据值之间循环,同时保持在相同的操作模式下,您可以允许简单地附加更多的值以在相同的模式下解释,并且仅在您想在其后添加一个新的换行符时才附加一个终止换行符。模式选择。

当然,这仍然是偷听的 2 倍多一点。与许多问题需要移动的数据量相比,串行端口速度通常非常高,这可能不是问题。但也许这不是你愿意付出的代价。在这种情况下,还有一些其他常用的可能性:

  • 您可以使用二进制编码,但将某些值声明为非法字符。例如,假设您将 0xff 设为非法字节,并且无论何时您看到它,都将其视为指示新记录的“标题”。现在的问题是,如果这可能是您需要能够传输的实际值,您需要一种在数据流中转义它的方法。因此,您引入另一个字节作为转义字符,并转义任何出现的帧字节或转义字节。这可能会变得很棘手,但它在实际应用中非常有用——以太网和许多无线电协议之类的东西使用密切相关的想法。与引入帧的其他方法一样,一旦您指定了一种用于划分帧的方案,您就可以将部分帧用于校验和或纠错码。

  • 您可以在传输中使用时间间隔来指示新消息。但是与可打印的十六进制编码的开销相比,这些差距往往会减慢速度。这里的危险是,当您在两个系统上的充分服务的本地总线 UART 之间建立直接连接时,字符之间相当短的间隔可能会很好。但是今天,通常情况下,您最终会在两者之间找到其他东西,例如通过分组 USB 接口(甚至是一些串行以太网桥接器,或蓝牙,或者接下来有人在您的系统中间插入的任何聪明的新发明)上的串行代理年)。当然已经使用了基于时间的框架,但是要以一种面向未来的方式这样做,以防止可能插入它们自己的延迟的中间体,你必须使间隔足够长,除非消息本身也相当长,你失去了任何效率优势。

  • 您可以使用 9 位串行格式,并让该额外位指示消息的开始。诀窍是您的硬件(以及每一端的操作系统)必须支持这一点 - 现在和将来。有时,您可以使用 8 位加奇偶校验编码和实时监控奇偶校验设置来实现与 9 位串行的互操作性,或者甚至使用切换奇偶校验来引入故意错误作为您的帧方案设计。这也已完成,但如果您必须在具有不同 UART 或串行 API 的新平台上重新实现方案的一端,这可能会非常令人头疼。

  • 您可以使用硬件流控制信号(如果存在)作为帧指示器 - 但它们必须连接起来,并且您必须小心以与数据本身同步的方式断言它们。这可能比最初看起来要困难一些 - 几乎所有 UART 在其发送缓冲区为空时都会提供方便的中断,但只有一些 UART 也可以在最后一个字节实际在线路上打卡后提供中断,所以你可以如果您希望它在 个字节之间实际更改,则需要一个计时器稍后返回一个字节时间并更改控制信号。

  • 您可以进行双向通信,接收方确认接收或在失去帧同步时进行投诉。可能您可以使用较长的时间间隔作为谈判的一部分。这当然在可靠性方面具有优势。但是,除了直接的 UART-UART 连接之外,几乎所有当今常用的东西都会导致严重的减速。包化中介,如 USB 串行转换器,比当今实际的本地总线 UART 更常见,最终会引入大量往返延迟,因此需要单独确认短消息的串行协议比那些不需要短消息确认的串行协议慢很多需要它们,或者具有类似 TCP 的能力,可以乐观地向前推进,但如果最终没有收到确认,则回退和恢复。

正如俗话说的那样,您可以选择简单性、可靠性或效率中的任何两个 - 并且几乎可以进行无数程度的权衡,甚至可以定义这些目标的实际含义。

【讨论】:

    猜你喜欢
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 2019-10-26
    • 2022-12-13
    • 1970-01-01
    • 2022-10-13
    相关资源
    最近更新 更多