Arduino上的高精度时序用于串行通信


11

我正在使用Arduino Uno通过串行端口将时间和电压信息发送到Python进行绘图。但是,连续时间戳之间的间隔时间似乎随着时间而增加,影响了我的绘图。当波特率设置为9600时尤其如此,我的初始时间差可能是1320,而在相对较短的时间后增加到16400。当此速率设置为最大115200 bps时,即使经过较长时间的发送,更改也较慢且不那么明显,从1340到1500。所有时间均以微秒为单位。

我想知道我是否可以减少或消除这种影响,以及是否不知道为什么存在。我已经阅读了有关引起这种情况的中断和延迟的内容,但是我不完全了解当前电子设备的复杂性,并且想知道:

  1. 我可以在时间上获得更高的精度吗?
  2. 是什么原因导致时间变化?

这是我目前拥有的:

#include <eHealth.h>

extern volatile unsigned long timer0_overflow_count;
float fanalog0;
int analog0;
unsigned long time;    

byte serialByte;
void setup() {
  Serial.begin(9600);
}

void loop() { 
  while (Serial.available()>0){  
    serialByte=Serial.read();
    if (serialByte=='S'){        
      while(1){
        fanalog0=eHealth.getECG();  
        // Use the timer0 => 1 tick every 4 us
        time=(timer0_overflow_count << 8) + TCNT0;        
        // Microseconds conversion.
        time=(time*4);   
        //Print in a file for simulation
        //Serial.print(time);
        //Serial.print(" ");
        Serial.print(fanalog0,5);
        Serial.print("\n");

        if (Serial.available()>0){
          serialByte=Serial.read();
          if (serialByte=='F')  break;
        }
      }
    }
  }
}

您所说的“精确”是什么意思?计数器给出的时间将相当准确,准确并且具有良好的分辨率。您是否希望时间是确定性的(即始终相同)?
Cyber​​gibbons 2014年

抱歉,是的,我想这就是我的意思,因为它们之间的差异是一致的,如果不一致,则不是它们的原因
user3284376 2014年

在PC端而非Arduino端添加时间戳,或使用RTC(实时时钟)模块。在各种网上商店中找到RTC模块都非常便宜,只需确保商店链接到数据表即可。另一种方法是对计时器进行编程,并使用中断服务程序来获取合理准确的时序。
jippie 2014年

怎么eHealth.getECG()办?该通话是否总是持续相同的时间?
jfpoilpret 2014年

您可以指定“相对较短的时间”持续多长时间吗?重新启动Arduino后总是一样吗?
jfpoilpret 2014年

Answers:


4

使用计时器和ISR(中断服务程序)可以使计时更加准确。

看看我的1ms定时中断概念证明。这个想法是在系统中有一个合理准确的1ms“心跳”,可用于触发其他事件。在PoC中,它用于使LED以½Hz频率闪烁,但可以访问新变量millisecondCountersecondCounter并使您能够在任意(但准确定时)的时刻触发主循环中的事件。


2
您的PoC非常有趣,但是它有一个缺陷(易于修复),因为它在启用中断时(在中loop())读取2字节的值,该值由ISR修改。可能会loop()读取错误的值(在ISR进行修改的中间)。我已经在您的博客上对此发表了评论。
jfpoilpret 2014年

@jfpoilpret有趣的一点是,您从未想到会在从RAM检索值的途中发生中断。我今天晚上要检查反汇编并更新文章。也许也是写另一篇文章的好理由:o)
jippie

我从您的PoC创建了一个样本,可以看到问题至少在我的UNO上每10秒发生一次。但是,当然,实际上,它在很大程度上取决于您的工作loop():我的样本仅获得毫秒值,将其表示为先前的读取值,并且如果差值> 0(将计数器重置为0除外),则会显示一条消息。
jfpoilpret 2014年

@jfpoilpret从未真正注意到它。我只是用它作为心跳来监视猫的食物桶,并在猫可能会感到失望的时候使LED闪烁...; o)肯定会改变我以后使用ISR的方式。
jippie 2014年

1
它显示了连接到模块ATMEGA16U2的晶体和连接到ATMEGA328P-PU的谐振器。16U2用于串行接口,而328P是“ Arduino”。有趣的是,16U2能够将其时钟发送到另一个芯片,例如328P。
Udo Klein

3

我可以想到一些可能影响串行写入时序“一致性”的事情:

  • 要打印的数据大小

这可能是最明显的考虑因素,但实际上打印的次数越多,处理它所花费的时间就越多。

解决方案:将字符串打印格式化为已知长度的字符串。

  • 使用缓冲串行

在Unix上,您可以使用缓冲或非缓冲方式访问串行端口。长时间使用缓冲方式可能会使缓冲速度变慢,通常是在数据输入速度比读取速度快时发生的。

解决方案:使用无缓冲的串行线(例如:在Darwin / OSX上/dev/cu.usbmodemXXX代替/dev/tty.usbmodemXXX

  • 计时器的优先级

看起来您使用的是TC中断,并且AVR在处理中断的方式上具有优先级,我不知道Atmega328的优先级顺序,它也不是最有据可查的功能之一,所以我不知道TC0与UART中断相比有多安全?

解决方案:在文档/数据表中进一步查找有关中断优先级的信息,并在需要时更改定时器;和/或在不运行其他计时器的情况下进行测试。

  • 随着时间的流逝,您正在读取的数据需要更多的时间来读取

一些驱动程序需要对先前的值求平均值或进行一些操作,因此,测量的值越多,缓冲区越长,计算该值所花费的时间就越长,直到达到缓冲区的最大大小为止。

解决方案:查看正在使用的库的源代码,并对其进行优化,如果有的话,将其删除,或者考虑增加的处理时间。

  • 避免arduino框架的开销

但是,如果您真的想优化arduino的串行输出,则应避免使用arduino开销……但是这样使用起来不那么优雅和舒适。

我敢肯定,我还缺少其他要点,但这是我进一步研究之前要检查的第一件事。

高温超导


2

您的代码在后续测量中包括输出的持续时间。因此,根据输出的长度,您将测量不同的时间。可以通过格式化为固定长度的输出来解决此问题。

下一个问题是联合国办事处的时基很差。在这里查看不同Arduino类型与DCF77时间参考的比较。

结论:如果您需要精确的时序,请选择带有晶体的Arduino或使用RTC。我强烈建议使用DS3231 / DS3232 RTC,因为它们通常开箱即用可达到2 ppm的精度。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.