Linux UART 比指定的波特率慢
Linux UART slower than specified Baudrate
我正在尝试通过 UART 在两个 Linux 系统之间进行通信。
我想发送大块数据。使用指定的波特率,它应该需要大约 5 秒,但它花费的时间几乎是预期时间的 10 倍。
由于我发送的内容超出了缓冲区一次可以处理的范围,所以它被分成小部分发送,我正在耗尽中间的缓冲区。如果我测量耗尽所需的时间和写入缓冲区的字节数,我计算出的波特率比指定的波特率低近 10 倍。
我希望传输速度较慢是最佳选择,但不是那么多。
我在设置 UART 或写入时是否遗漏了什么?还是这是正常的?
用于设置的代码:
int bus = open(interface.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); // <- also tryed blocking
if (bus < 0) {
return;
}
struct termios options;
memset (&options, 0, sizeof options);
if(tcgetattr(bus, &options) != 0){
close(bus);
bus = -1;
return;
}
cfsetspeed (&options, B230400);
cfmakeraw(&options); // <- also tried this manually. did not make a difference
if(tcsetattr(bus, TCSANOW, &options) != 0)
{
close(bus);
bus = -1;
return;
}
tcflush(bus, TCIFLUSH);
用于发送的代码:
int32_t res = write(bus, data, dataLength);
while (res < dataLength){
tcdrain(bus); // <- taking way longer than expected
int32_t r = write(bus, &data[res], dataLength - res);
if(r == 0)
break;
if(r == -1){
break;
}
res += r;
}
B230400
文档自相矛盾。 cfsetspeed 被记录为需要 speed_t 类型,而注释说您需要使用 "B" 常量之一,例如 "B230400." 您是否尝试过使用实际的 speed_t 类型?
无论如何,您提供的速度是波特率,在这种情况下,假设没有节流,波特率应该约为 23,000 bytes/second。
速度取决于硬件和 link 限制。串行协议也允许暂停传输。
FWIW,根据您列出的时间和速度,如果一切正常,您将在 50 秒内获得大约 1 MB。您实际获得的速度是多少?
另一个"also"是选项结构。自从我不得不做任何串行 I/O 以来已经有好几年了,但是 IIRC,你需要实际设置你想要的选项并得到你的硬件支持,比如 CTS/RTS、XON/XOFF等
As I'm sending more than the buffer can handle at once it is send in small parts and I'm draining the buffer in between.
您只提供了代码片段(而不是最小的、完整的和可验证的示例),因此您的数据大小未知。
但是 Linux 内核缓冲区大小是已知的。你认为它是什么?
(仅供参考,它是 4KB。)
If I measure the time needed for the drain and the number of bytese written to the buffer I calculate a Baudrate nearly 10 times lower than the specified Baudrate.
您混淆了吞吐量和波特率。
异步串行的最大吞吐量(仅有效负载)link 由于每个字符的帧开销,它可能是帧的十位中的两位(假设 8N1 ).由于您的 termios 配置不完整,因此开销实际上可能是帧的 11 位中的 3 位(假设 8N2)。
为了实现最大吞吐量,传输 UART 必须使线路充满帧并且永远不要让线路空闲。
用户空间程序必须能够足够快地提供数据,最好是通过一个大 write() 来减少系统调用开销。
Did I miss something while setting the UART or while writing?
使用 Linux,您对 UART 硬件的访问受到限制。
您的程序从用户空间访问串行终端。
您的程序以次优方式访问串行终端。
您的 termios 配置似乎不完整。
它使硬件和软件流控制保持不变。
停止位的数量不变。
Ignore modem control lines
和 Enable receiver
标志未启用。
对于原始读数,未分配 VMIN 和 VTIME 值。
Or is this normal?
有多种方法可以轻松加快传输速度。
首先,你的程序结合了非阻塞模式和非规范模式。这是一个退化的接收组合,并且不是最理想的传输组合。
您没有提供使用非阻塞模式的理由,并且您的程序没有正确使用它。
因此,您的程序应该修改为使用阻塞模式而不是非阻塞模式。
其次,在 write() 系统调用之间的 tcdrain() 可以在串行 link 上引入空闲时间。使用阻塞模式消除了 write() 系统调用之间对这种延迟策略的需要。
事实上,在阻塞模式下,只需要一个 write() 系统调用来传输整个 dataLength
。这也将最大限度地减少串行 link.
上引入的任何空闲时间
请注意,第一个 write() 没有正确检查 return 值是否存在可能的错误情况,这总是可能的。
底线:您的程序会更简单,并且通过使用阻塞会提高吞吐量 I/O。
我正在尝试通过 UART 在两个 Linux 系统之间进行通信。 我想发送大块数据。使用指定的波特率,它应该需要大约 5 秒,但它花费的时间几乎是预期时间的 10 倍。
由于我发送的内容超出了缓冲区一次可以处理的范围,所以它被分成小部分发送,我正在耗尽中间的缓冲区。如果我测量耗尽所需的时间和写入缓冲区的字节数,我计算出的波特率比指定的波特率低近 10 倍。
我希望传输速度较慢是最佳选择,但不是那么多。
我在设置 UART 或写入时是否遗漏了什么?还是这是正常的?
用于设置的代码:
int bus = open(interface.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); // <- also tryed blocking
if (bus < 0) {
return;
}
struct termios options;
memset (&options, 0, sizeof options);
if(tcgetattr(bus, &options) != 0){
close(bus);
bus = -1;
return;
}
cfsetspeed (&options, B230400);
cfmakeraw(&options); // <- also tried this manually. did not make a difference
if(tcsetattr(bus, TCSANOW, &options) != 0)
{
close(bus);
bus = -1;
return;
}
tcflush(bus, TCIFLUSH);
用于发送的代码:
int32_t res = write(bus, data, dataLength);
while (res < dataLength){
tcdrain(bus); // <- taking way longer than expected
int32_t r = write(bus, &data[res], dataLength - res);
if(r == 0)
break;
if(r == -1){
break;
}
res += r;
}
B230400
文档自相矛盾。 cfsetspeed 被记录为需要 speed_t 类型,而注释说您需要使用 "B" 常量之一,例如 "B230400." 您是否尝试过使用实际的 speed_t 类型?
无论如何,您提供的速度是波特率,在这种情况下,假设没有节流,波特率应该约为 23,000 bytes/second。
速度取决于硬件和 link 限制。串行协议也允许暂停传输。
FWIW,根据您列出的时间和速度,如果一切正常,您将在 50 秒内获得大约 1 MB。您实际获得的速度是多少?
另一个"also"是选项结构。自从我不得不做任何串行 I/O 以来已经有好几年了,但是 IIRC,你需要实际设置你想要的选项并得到你的硬件支持,比如 CTS/RTS、XON/XOFF等
As I'm sending more than the buffer can handle at once it is send in small parts and I'm draining the buffer in between.
您只提供了代码片段(而不是最小的、完整的和可验证的示例),因此您的数据大小未知。
但是 Linux 内核缓冲区大小是已知的。你认为它是什么?
(仅供参考,它是 4KB。)
If I measure the time needed for the drain and the number of bytese written to the buffer I calculate a Baudrate nearly 10 times lower than the specified Baudrate.
您混淆了吞吐量和波特率。
异步串行的最大吞吐量(仅有效负载)link 由于每个字符的帧开销,它可能是帧的十位中的两位(假设 8N1 ).由于您的 termios 配置不完整,因此开销实际上可能是帧的 11 位中的 3 位(假设 8N2)。
为了实现最大吞吐量,传输 UART 必须使线路充满帧并且永远不要让线路空闲。
用户空间程序必须能够足够快地提供数据,最好是通过一个大 write() 来减少系统调用开销。
Did I miss something while setting the UART or while writing?
使用 Linux,您对 UART 硬件的访问受到限制。
您的程序从用户空间访问串行终端。
您的程序以次优方式访问串行终端。
您的 termios 配置似乎不完整。
它使硬件和软件流控制保持不变。
停止位的数量不变。
Ignore modem control lines
和 Enable receiver
标志未启用。
对于原始读数,未分配 VMIN 和 VTIME 值。
Or is this normal?
有多种方法可以轻松加快传输速度。
首先,你的程序结合了非阻塞模式和非规范模式。这是一个退化的接收组合,并且不是最理想的传输组合。
您没有提供使用非阻塞模式的理由,并且您的程序没有正确使用它。
因此,您的程序应该修改为使用阻塞模式而不是非阻塞模式。
其次,在 write() 系统调用之间的 tcdrain() 可以在串行 link 上引入空闲时间。使用阻塞模式消除了 write() 系统调用之间对这种延迟策略的需要。
事实上,在阻塞模式下,只需要一个 write() 系统调用来传输整个 dataLength
。这也将最大限度地减少串行 link.
上引入的任何空闲时间
请注意,第一个 write() 没有正确检查 return 值是否存在可能的错误情况,这总是可能的。
底线:您的程序会更简单,并且通过使用阻塞会提高吞吐量 I/O。