最大化 Arduino 串行输出

Maximize Arduino serial output

整个过程中穿插了多个问题 post。请仔细阅读并回答您可以投票的部分。

用例

创建压力读数的时间序列 csv 文件。这些读取需要具有最大频率,但我只需要它持续不到 10 秒左右。

Material

  1. Arduino Uno 克隆(不可更改)
  2. USB 2.0 串口(可变)
  3. pyserial (Python 3)
  4. SSD

问题

识别并修复阻止频率达到最大值的瓶颈 reads/s。

密码

Arduino

void setup() {
  //speed over the usb serial interface
  Serial.begin(450000);
}

void loop() {
    //get sensor data: read the input on analog pin 0:
    int sensorValue = analogRead(A0); //returns value 0-1023
    //send over serial
    Serial.println(sensorValue);
}

Python

...
ser.serial()
ser.baudrate=450000
ser.open()
while True:
    try:
        serialData = float(ser.readline().decode().replace('\n', ''))
    except(ValueError):
        continue #I ran the code without this try/except and got 100 reads higher than when this is included in here. Caused no change in order of magnitude.
    now = datetime.datetime.now()
    serialData2 = serialData * voltageOffset #a couple of these happen
    outfile.write(now+', '+str(serialData2)+'\n')
except(KeyboardInterrupt):
    ...
    os.sys.exit(0)

带宽

一些计算来找出瓶颈所在。

100 µs to read analog pin

1 个数据包 = 10 位

串口线速:450000/10 = 45000包/秒

Arduino samples-into-serial/second 率:

100 µs * 1s/(10^-6 µs) = 1 x 10^4 样本/秒

arduino 硅适配器:便宜的克隆芯片。假设性能不佳,因为它不是 arduino 许可的产品,但这被认为是可以接受的损失,稍后将解释。假定具有无限带宽,尽管这显然会将性能提高一个数量级。

USB 2.0

packets/s = 480 mb/s * 10^6 b/mb * 1 packet/10 b = 48 x 10^6 数据包/秒

PC文件写入速度

400 MB/s = 320 x 10^6 packets/s

PC python 脚本

未知。假设是无限的?已知 datetime.now() 查询大约需要 8 微秒。

结论:瓶颈是串口波特率

调整波特率证实了这一点。我发现 el-cheapo arduino uno 仅支持最大 450000 波特率,而 arduino actually supports 2-7 million 来自我可以在线收集的数据。话虽这么说,还是有些不对劲。

关键问题

回想一下,在这个波特率下,我们应该看到压力读取以理论上的 45000 个数据包/秒生成。

波特率-数据长度-无奇偶校验-1 结束位存在:450000-8-n-1

sensorValue = 1023。进入 println 的数据看起来像这样:1023\n

当我在 arduino 中 运行 serial.println(sensorValue) 时,有多少数据包通过线路发送到 PC?这是如何序列化成数据包的?将通过线路发送多少数据包?以下哪一项是正确的:

一个。 16 位 int 需要传输 2 个数据包。 \n 再精确 2 个。总计 = 4

乙。 sensorValue 被转换为字符串并通过单独的数据包通过网络发送:1 个用于 1,1 个用于 0,1 个用于 2,1 个用于 3 ,1 个代表 \,1 个代表 n。总计 = 6

摄氏度。其他(帮我优化一下??)

测试

我没有看到预期的结果。

baud = 450000.

在 arduino 代码 serial.println(value) 中更改 value 在我的 txt 文件中产生不同的 reads/s 第二个:

value = readPin\n >> 5417压力reads/s

value = 1 >> 10201 reads/s

value = 1023 >> 4279 reads/s

这告诉了我一些事情。读取 analogPin 的值时会产生开销。我还可以得出结论,arduino 也只在必要时发送多个数据包(1 > 8 位数据,1023 > 16 位数据)。 等等,什么? 这没有多大意义。 这是怎么发生的?

调整波特率会导致读取次数发生变化,直到arduino达到最大值。但是让我们找到我应该期望的吞吐量。

450000 b/s *(1 个数据包/10b)* [1 个读取/(4 或 6 个数据包)] = 11250 个读取或 7500 个理论值 reads/s

实际 = 5417 reads/s

2000次阅读消失到哪里去了?

你是对的,瓶颈是串行端口(在你传输数据效率低下的意义上)。但是,您的大多数其他假设都是错误的。

波特率代表什么

波特率是每秒不同符号变化的次数。在这里它相当于每秒位数。波特率将包括传输的所有位,而不仅仅是数据(开始、停止、奇偶校验)。

如何传输 10 位值

因为你有8n1传输,你不能发送正好10位的数据。它必须是 8 的倍数,所以 8、16、24 等。10 位将在两个 8 位部分中发送,就像它们作为 int 存储在 Arduino 内存中一样。

println() 是如何工作的

println() 将数字转换为字符串。您可以为此转换指定基数(DEC、BIN、HEX、OCT)——默认为 DEC,因此 1023 将作为 4 个字节传输 + \n,这是一个单字节 (ASCII 10) 和 \r (ASCII 13) 这也是一个单字节。共 6 个字节。 1 将需要 3 个字节 - 1 个用于数据,2 个用于换行符和回车 return.

如何让它更快

  1. 几乎不做任何更改 - 使用 println(val, HEX) - 对于大于 255 的数字,您最多需要 5 个字节。
  2. 使用Serial.write() - 此函数将原始二进制数据放入串行,因此如果您要发送值 10,它只会发送值 10 的一个字节。但是,当您要发送 10-位变量,事情变得复杂 - 你需要 2 个字节,PC 需要知道哪个是第一部分,哪个是第二部分才能将它们拼接在一起。您将需要想出某种形式的简单传输协议来处理这个问题,所以可能需要一些 start/stop 个字符。
  3. 您需要 10 位分辨率吗?如果你能继续使用 8 位,你就可以只传输普通数据,而不需要任何额外的字符。另外,正如评论中所建议的那样,您可以使用更快的 ADC 时钟。
  4. KIIV 也建议 - 您可以使用 ADC 转换中断,它会在测量完成时立即触发。

如果您仍然使用 10 位,我建议使用 3 字节帧:1 个起始字节和 2 个数据字节。这样你理论上可以达到每秒约 18 000 帧,这高于最大 analogRead() 频率。

一个选项是采样 x 次,然后在发送回 PC 之前取平均值。或者接受压缩 328p 的挑战!如果您的压力读数基本一致,您可能对一些 RLE 没问题。

不要使用 10 位。 ADC 可能具有 10 位分辨率,但精度为 +/- 2 LSB,并且非常依赖于稳定的 vref。因此,降低 2 LSB 并使用前 8 位进行快速 Arduino 到 PC 读数或多次采样并平均噪声。

这是一个嘈杂的、共享的、低分辨率的 ADC。所以不要让其他模拟引脚悬空,因为这会产生噪声(只需将它们接地)。如果可能的话,运行 Arduino 在接地外壳中,因为 ADC 有一个 MUX 导致它有更长的轨迹。切换 ADC 模式或引脚也会产生一些噪音(不是你在这里做的)。如果你能保持稳定的输入电压,电池电量就很棒,否则 AC/DC 噪声可能会导致意外的 vref 波动。

Arduino 上的主循环 运行s 328p 和 8u 之间的串行 IO 以及模拟读取。所以时序瓶颈有点复杂。

UNO 串口到 USB 的转换由 ATMEGA8u2(板上的另一个芯片)处理,它可以 115.2kbaud(不是 450000)通信。

8u 和 328p 上有缓冲区只是为了增加复杂性。并且两个芯片 运行 在不同的时钟上。来自 crystal 的 ATMEGA8u2 运行s 和来自谐振器(均为 16MHz)的 ATMEGA328p(主处理器)运行s。

根据您的需要,Teensy 有一个稍微更准确的 ADC(外部的 ADC 会提供更好的读取)和更快的 CPU、更多的 RAM 等,它可以最大限度地提高USB端口速度。此外,Paul 还提供了一些使事情变得非常快的低级技术的重要信息。