目前的实现在固件中使用了一个字节循环缓冲区(只是带有读写指针的数组)。当字节进入时,它被写入循环缓冲区。
现在PC应用程序似乎以太快的速度发送数据,导致固件错过了一些字节,从而导致固件返回WRONG_INPUT太多次。
我认为波特率(115200)不是问题。在固件端使用更有效的数据结构可能会有所帮助。对于数据结构的选择有什么建议吗?
一个循环缓冲区是最好的答案。这是在纯软件中模拟硬件FIFO的最简单方法。
实际问题可能是从UART收集字节并放入缓冲区的方式,或者缓冲区溢出。
以115200波特率、通常的1起始位、1停止位和8数据位为例,可以看到每秒钟会有多达11520个字节到达该端口。这给你平均约86.8微秒的每个字节的处理时间。在PC中,这将看起来像是很多时间,但在小型微处理器中,它可能不是总指令数很多,或者在某些情况下非常多的I/O寄存器访问。如果因为字节平均到达速度比你消耗的速度更快而导致缓冲区过满,那么就会产生错误。
一些通用建议:
设置足够大的环形缓冲区来容纳完整的消息非常重要。如果您的协议对消息大小有已知的限制,那么您可以使用更高级别的协议来进行流控制,并且可以在不必处理所有边缘情况下生存,例如让XON/XOFF流量正常工作,或者使RTS/CTS在两端的预期方式中工作,这几乎同样棘手。
如果不能使环形缓冲区那么大,则需要某种形式的流控制。
没有比循环缓冲更好的了。
您可以使用较慢的波特率或加快固件中应用程序的速度,以便它能够处理以全速到达的数据。
如果PC的输出是突发性的,则将缓冲区大小调整为足以处理一个突发可能会有所帮助。
最后一种选择是实现某种形式的流量控制。
嵌入式设备是什么意思?我认为大多数当前的DSP和处理器都可以轻松处理这种负载。问题不在于循环缓冲区,而在于如何从串口收集字节。
你的UART是否有硬件FIFO?如果有,那么你应该启用它。如果每个字节都有一个中断,你可能会很快陷入麻烦,特别是如果你正在使用操作系统或虚拟内存,在这些情况下IRQ成本可能会很高。
如果你的接收固件非常简单(没有多任务),并且你没有硬件FIFO,则轮询模式可能比中断驱动模式更好,因为此时你的处理器只进行UART数据接收,而没有中断开销。
另一个问题可能与传输协议有关。例如,如果你有一长段数据包需要校验和,而你在数据包末尾进行整个校验和计算,那么整个数据包的处理时间都在末尾,这就是为什么你可能会错过下一个数据包的开头。
因此,循环缓冲区很好,你需要改进的是: - 与硬件交互的方式 - 协议(数据包长度、确认等)
在IRQ中使用循环缓冲区是一个很好的建议。如果您的处理器每次接收到一个字节时生成一个中断,请将该字节存储在缓冲区中。如何决定清空该缓冲区取决于您是否正在处理数据流或数据包。如果您正在处理数据流,只需让后台进程从缓冲区中删除字节并按先进先出的顺序处理它们即可。如果您正在处理数据包,则只需继续填充缓冲区,直到您拥有完整的数据包。我过去已经成功地使用了数据包方法。我还会实现某种类型的流量控制,以便在发生诸如缓冲区已满或数据包处理时间长等问题时向PC发出信号,并在准备好下一个数据包时向PC指示。