【问题标题】:Linux UART slower than specified BaudrateLinux UART 比指定的波特率慢
【发布时间】:2018-08-04 07:46:32
【问题描述】:

我正在尝试通过 UART 在两个 Linux 系统之间进行通信。 我想发送大量数据。使用指定的 Baudrate 大约需要 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;
}

【问题讨论】:

    标签: linux performance system-calls uart baud-rate


    【解决方案1】:

    B230400

    文档是矛盾的。 cfsetspeed 被记录为需要 speed_t 类型,而注释说您需要使用“B”常量之一,例如“B230400”。您是否尝试过使用实际的 speed_t 类型?

    在任何情况下,您提供的速度都是波特率,在这种情况下,假设没有限制,波特率应该可以达到大约 23,000 字节/秒。

    速度取决于硬件和链接限制。串行协议也允许暂停传输。

    FWIW,根据您列出的时间和速度,如果一切正常,您将在 50 秒内获得大约 1 MB。您实际获得的速度是多少?

    另一个“也”是选项结构。自从我不得不做任何串行 I/O 以来已经有好几年了,但是 IIRC,你需要实际设置你想要的选项并且你的硬件支持,比如 CTS/RTS、XON/XOFF 等。

    This might be helpful

    【讨论】:

    • 啊,谢谢。这让人很难堪。我认为波特指的是字节加上开始、停止等。应该知道得更好......然后一切都按它应该的方式工作,但不像我想要的那样......命令 cfmakeraw() 设置每个选项,如 CTS 和依此类推,就像我希望他们那样。
    【解决方案2】:

    因为我发送的缓冲区超出了缓冲区的一次处理能力,所以它是分小部分发送的,我正在耗尽其间的缓冲区。

    您只提供了代码 sn-ps(而不是一个最小、完整且可验证的示例),因此您的数据大小是未知的。
    但是 Linux 内核缓冲区大小是已知的。你觉得是什么?
    (仅供参考,它是 4KB。)

    如果我测量耗尽所需的时间和写入缓冲区的字节数,我计算出的波特率比指定的波特率低近 10 倍。

    您将吞吐量与波特率混淆了。

    由于每个字符的帧开销,异步串行链路的最大吞吐量(仅有效负载)将始终小于波特率,这可能是帧的十位中的两位(假设为 8N1)。由于您的 termios 配置不完整,因此开销实际上可能是帧的 11 位中的 3 位(假设为 8N2)。

    为了达到最大吞吐量,发送 UART 必须用帧使线路饱和,并且永远不要让线路空闲。
    用户空间程序必须能够足够快地提供数据,最好是通过一个大的 write() 来减少系统调用开销。

    我在设置 UART 或写入时是否遗漏了什么?

    使用 Linux,您只能访问 UART 硬件。
    您的程序从用户空间访问串行终端。
    您的程序以次可选方式访问串行终端。

    您的 termios 配置似乎不完整。
    它使硬件和软件流控制保持不变。
    停止位的数量不变。
    Ignore modem control linesEnable receiver 标志未启用。
    对于原始读数,未分配 VMIN 和 VTIME 值。

    这正常吗?

    有一些方法可以轻松加快传输速度。

    首先,您的程序结合了非阻塞模式和非规范模式。这是接收的退化组合,而传输的次优组合。
    您没有提供使用非阻塞模式的理由,并且您的程序未编写为正确使用它。
    因此你的程序应该被修改为使用阻塞模式而不是非阻塞模式。

    其次,write() 系统调用之间的 tcdrain() 会在串行链路上引入空闲时间。使用阻塞模式消除了 write() 系统调用之间的这种延迟策略的需要。

    事实上,在阻塞模式下,只需要一个 write() 系统调用来传输整个dataLength。这也将最大限度地减少串行链路上引入的任何空闲时间。
    请注意,第一个 write() 没有正确检查返回值是否存在可能的错误条件,这总是可能的。


    底线:您的程序会更简单,并且通过使用阻塞 I/O 会提高吞吐量。

    【讨论】:

    • 将效率计算为百分比并不是非常有用...最好将行符号和数据位和字节视为不同的数量,由行代码相关。对于 8N1 双极线路代码(UART 的主要代码),很容易看出转换因子是每个数据字节 10 个符号,最小通道带宽为波特率的一半。如果您使用其他“串行”线路代码,例如 v.42bis,带宽、波特率和数据速率之间的关系很快就会变得非常混乱。 (是的,这通常是 UART 调制解调器的另一端)
    • @BenVoigt -- 删除了百分比数字。
    猜你喜欢
    • 2015-10-28
    • 1970-01-01
    • 2019-01-20
    • 1970-01-01
    • 1970-01-01
    • 2017-01-18
    • 1970-01-01
    • 2011-12-30
    • 2013-12-30
    相关资源
    最近更新 更多