固件中存储串口数据的数据结构
我正在通过串行端口将数据从 Linux 应用程序发送到嵌入式设备。
在当前的实现中,固件中使用了字节循环缓冲区。 (只不过是一个带有读写指针的数组) 当字节进入时,它被写入循环缓冲区。
现在,PC 应用程序发送数据的速度似乎太快,固件无法处理。 字节丢失导致固件多次返回 WRONG_INPUT。
我认为波特率(115200)不是问题。 固件端更有效的数据结构可能会有所帮助。 关于数据结构的选择有什么建议吗?
I am sending data from a linux application through serial port to an embedded device.
In the current implementation a byte circular buffer is used in the firmware. (Nothing but an array with a read and write pointer)
As the bytes come in, it is written to the circular bufffer.
Now the PC application appears to be sending the data too fast for the firmware to handle. Bytes are missed resulting in the firmware returning WRONG_INPUT too mant times.
I think baud rate (115200) is not the issue. A more efficient data structure at the firmware side might help. Any suggestions on choice of data structure?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
循环缓冲区是最好的答案。 这是在纯软件中对硬件 FIFO 进行建模的最简单方法。
真正的问题可能是从 UART 收集字节放入缓冲区的方式,或者是缓冲区溢出。
在 115200 波特率下,通常有 1 个起始位、1 个停止位和 8 个数据位,您可以看到每秒有多达 11520 字节到达该端口。 这使得每个字节的平均处理时间仅为 86.8 µs。 在 PC 中,这似乎是很多时间,但在小型微处理器中,可能不是那么多总指令,或者在某些情况下不是很多 I/O 寄存器访问。 如果由于字节到达的平均速度快于消耗它们的速度而导致缓冲区溢出,那么就会出现错误。
一些一般建议:
将环形缓冲区设置得足够大以容纳完整的消息非常重要。 如果您的协议对消息大小有已知的限制,那么您可以使用协议的更高级别进行流量控制并生存,而无需让 XON/XOFF 流在所有边缘情况或 RTS/CTS 中正常工作在几乎同样毛茸茸的电线两端按预期工作。
如果您不能使环形缓冲区那么大,那么您将需要某种流量控制。
A circular buffer is the best answer. It is the easiest way to model a hardware FIFO in pure software.
The real issue is likely to be either the way you are collecting bytes from the UART to put in the buffer, or overflow of that buffer.
At 115200 baud with the usual 1 start bit, 1 stop bit and 8 data bits, you can see as many as 11520 bytes per second arrive at that port. That gives you an average of just about 86.8 µs per byte to work with. In a PC, that will seem like a lot of time, but in a small microprocessor, it might not be all that many total instructions or in some cases very many I/O register accesses. If you overfill your buffer because bytes are arriving on average faster than you can consume them, then you will have errors.
Some general advice:
Sizing your ring buffer large enough to hold a complete message is important. If your protocol has known limits on the message size, then you can use the higher levels of your protocol to do flow control and survive without the pains of getting XON/XOFF flow to work right in all of the edge cases, or RTS/CTS to work as expected in both ends of the wire which can be nearly as hairy.
If you can't make the ring buffer that large, then you will need some kind of flow control.
没有什么比循环缓冲区更好的了。
您可以使用较慢的波特率或加快固件中的应用程序速度,以便它可以全速处理数据。
如果 PC 的输出是突发的,那么将缓冲区设置得足够大以处理一次突发可能会有所帮助。
最后一个选项是实施某种形式的流量控制。
There is nothing better than a circular buffer.
You could use a slower baud rate or speed up the application in the firmware so that it can handle data coming at full speed.
If the output of the PC is in bursts it may help to make the buffer big enough to handle one burst.
The last option is to implement some form of flow control.
嵌入式设备是什么意思? 我认为目前大多数DSP和处理器都可以轻松处理这种负载。 问题不在于循环缓冲区,而在于如何从串行端口收集字节。
你的UART有硬件fifo吗? 如果是,那么您应该启用它。 如果每个字节都有一个中断,那么您很快就会遇到麻烦,特别是当您使用操作系统或虚拟内存时,IRQ 成本可能会很高。
如果您的接收固件非常简单(无多任务处理),并且您没有硬件 fifo,则轮询模式可能是比中断驱动更好的解决方案,因为这样您的处理器仅执行 UART 数据接收,并且您没有中断开销。
另一个问题可能与传输协议有关。 例如,如果您有很长的数据包,您必须对其进行校验和,并且您在数据包的末尾进行整个校验和,那么数据包的所有处理时间都在它的末尾,这就是为什么您可能会错过下一个数据包的开始。
所以循环缓冲区很好,你必须改进:
- 您与硬件交互的方式
- 协议(数据包长度、确认等...)
What do you mean by embedded device ? I think most of current DSP and processor can easily handle this kind of load. The problem is not with the circular buffer, but how do you collect bytes from the serial port.
Does your UART have a hardware fifo ? If yes, then you should enable it. If you have an interrupt per byte, you can quickly get into trouble, especially if you are working with an OS or with virtual memory, where the IRQ cost can be quit high.
If your receiving firmware is very simple (no multitasking), and you don't have an hardware fifo, polled mode can be a better solution than interrupt driven, because then your processor is doing only UART data reception, and you have no interrupt overhead.
Another problem might be with the transfer protocol. For example if you have long packet of data that you have to checksum, and you do the whole checksum at the end of the packet, then all the processing time of the packet is at the end of it, and that is why you may miss the beginning of the next packet.
So circular buffer is fine and you have to way to improve :
- The way you interact with the hardware
- The protocol (packet length, acknoledgment etc ...)
在尝试解决问题之前,首先您需要确定问题到底是什么。 否则,您可能会浪费时间尝试修复实际上没有损坏的东西。
如果不了解更多关于您的设置的信息,就很难给出更具体的建议。 但是您应该进一步调查以确定当字节进入时硬件和软件当前正在做什么,然后它们丢失的弱点是什么。
Before trying to solve the problem, first you need to establish what the problem really is. Otherwise you might waste time trying to fix something that isn't actually broken.
Without knowing more about your set-up it's hard to give more specific advice. But you should investigate further to establish what exactly the hardware and software is currently doing when the bytes come in, and then what is the weak point where they're going missing.
具有中断驱动 IO 的循环缓冲区将在最小且最慢的嵌入式目标上工作。
首先尝试最低波特率,然后再尝试高速。
A circular buffer with Interrupt driven IO will work on the smallest and slowest of embedded targets.
First try it at the lowest baud rate and only then try at high speeds.
将循环缓冲区与 IRQ 结合使用是一个很好的建议。 如果您的处理器在每次接收到一个字节时生成一个中断,则获取该字节并将其存储在缓冲区中。 您决定如何清空该缓冲区取决于您正在处理数据流还是数据包。 如果您正在处理流,只需让后台进程从缓冲区中删除字节并以先进先出的方式处理它们。 如果您正在处理数据包,则只需继续填充缓冲区,直到获得完整的数据包。 我过去多次成功地使用了数据包方法。 如果出现问题(例如缓冲区已满)或者数据包处理时间很长,我还会实现某种类型的流量控制,以向 PC 发出信号,以向 PC 指示何时准备好处理下一个数据包。
Using a circular buffer in conjunction with IRQ is an excellent suggestion. If your processor generates an interrupt each time a byte is received take that byte and store it in the buffer. How you decide to empty that buffer depends on if you are processing a stream of data or data packets. If you are processing a stream simply have your background process remove the bytes from the buffer and process them first-in-first-out. If you are processing packets then just keep filing the buffer until you have a complete packet. I've used the packet method successfully many times in the past. I would implement some type of flow control as well to signal to the PC if something went wrong like a full buffer or if packet-processing time is long to indicate to the PC when it is ready for the next packet.
您可以实现类似 IP 数据报的内容,其中包含数据长度、id 和校验和。
编辑:
然后,您可以为数据包硬编码一些固定长度,例如 1024 字节或对设备有意义的任何长度。 PC端每次写入数据包时都会检查设备上的队列是否已满。 固件端将运行校验和以查看所有数据是否有效,并读取直到数据长度。
You could implement something like IP datagram which contains data length, id, and checksum.
Edit:
Then you could hard-code some fixed length for the packets, for example 1024 byte or whatever that makes sense for the device. PC side would then check if the queue is full at the device every time it writes in a packet. Firmware side would run checksum to see if all data is valid, and read up till the data length.