【问题标题】:Arduino stops sending data to the serial port after a long time periodArduino在很长一段时间后停止向串口发送数据
【发布时间】:2011-12-05 17:37:06
【问题描述】:

我使用Arduino Uno rev2 设备作为永久连接的设备,有时会向 PC (Windows 7 x64) 发送信号。使用来自 arduino.cc 的 Arduino 1.0 软件编译的代码

arduino.cc 上的主题,Arduino stops sending data to Serial after a long time period

Souce code

它运行良好,但有时,经过很长一段时间后,PC 会停止从 Arduino 设备接收数据。这不是 PC 软件问题,因为所有软件(putty、telnet 等)的行为都相同 - 我可以向 Arduino 发送数据(设备响应命令);我就是收不回来。

Serial communication stops after long periods. 中描述了类似的问题,但没有提出解决方案。

断开/连接设备暂时解决了问题,但这不是解决方案,因为设备应该永久且完全自动使用。

使用板重置按钮将程序和所有值重置为开始不会有帮助。 PC 未开始接收数据。

注意事项:

  1. millis() 翻转错误无法在使用 Arduino 1.0 软件的 Arduino Uno 板上重现 - 我想这已修复,millis() 现在只在 50 天内真正翻转,就像文档中所说的那样。除了代码有millis() independent code 也没有响应。

  2. 在向 PC 发送数据期间闪烁的 LED 仍然闪烁。

  3. 字符串的使用可能会增加内存的使用,但是这个程序太小了,不会有问题。程序运行 10 多个小时后没有使用额外的内存,所以我真的不会费心用其他东西替换字符串,因为串行端口问题要严重得多。

如果您认为问题出在 arduino 程序错误中,请考虑如何解释 TX 闪烁和重置无济于事。

【问题讨论】:

  • 如果您使用终端程序(TeraTerm、putty),PC 是否会继续接收数据?当 PC 停止接收时,你能看到 Arduino 还在用示波器传输吗?
  • 您仍然忘记了 setup() 函数中的一些变量,例如 lightTimer。重置后,它仍然会在某处遥不可及。再次:这不是关于millis() 环绕,而是关于你的变量 不是环绕。试着想象一下如果 lightTimer 为 0xffffffff 会发生什么:它永远不能设置为新值,因为您只检查millis() > lightTimer。 fixedMillis() 函数无法解决这个问题,因为它仍然受限于 unsigned int 可以表示的值范围。
  • 仍未解决,似乎无法解决。现在正在开发一个允许自行硬件重置 Arduino USB 的组件。
  • 你有什么发现吗?我遇到了同样的问题。
  • 您是否正在切换任何负载?我曾遇到过切换感应负载(如泵)导致 Arduino 失去立足点的情况,需要重新加载驱动程序或连接并重新连接电缆,即使 Arduino 代码将继续运行。

标签: serial-port arduino communication


【解决方案1】:

也许让软件重置您的问题会得到解决。 我运行了以下代码来确定软件重置是否会重置时钟以及 millis() 函数:

void setup()
{
  Serial.begin(9600);
  Serial.println("Will start sending millis values in 5 seconds...");
  delay(5000);
}

void loop()
{
  Serial.println(String(millis()));

  if (Serial.available() > 0)
  {
    if (Serial.read() == '@')
    {
      Serial.println("Rebooting. . .");
      delay(100); // Give the computer time to receive the "Rebooting. . ." message, or it won't show up
      void (*reboot)(void) = 0; // Creating a function pointer to address 0 then calling it reboots the board.
      reboot();
    }
  }
}

如代码中所述,要使软件重新启动,只需创建一个指向地址 0 的函数指针并调用它。 我确实得到了令人满意的结果:

Will start sending clock values in 5 seconds...
5000
5000
5000
5001
5001
5001
5002
5002
5002
. . .
6804
Rebooting...
Will start sending millis value in 5 seconds...
5000
5000
5000
5001
5001
5001
5002
5002
5002
5003
5003
. . .

我希望这能解决你的问题:)

【讨论】:

    【解决方案2】:

    如果您经常轮询 Arduino,那么作为一种解决方法,您可以在 Arduino 中实现一个看门狗,如果它长时间没有向 PC 输出数据,它将重置 Arduino。您可以监控 Arduino TX 引脚,将其带到另一个输入引脚,使用中断...关键是在每次 TX 活动后重置看门狗。

    【讨论】:

    • 重置板子不能解决问题。只有硬件重新连接 USB 才能恢复数据接收。
    【解决方案3】:

    时钟可能会折叠。

    previousclock = millis() 在折叠之前可能会卡在一个高值上。您可以扩展测试以包括 (currentmilis

    顺便说一句,它使用了ignas的源代码(OP源代码没有注册是不可读的,我不想注册)

    编辑:我从 wakkerbot 复制了下面的片段,并对其进行了一些编辑。这只是为了 演示 wraparound 如何让你的 last_action 时间戳停留在 int 间隔的顶部(如果凹凸值不是 int_max 的除数) 您可能可以稍微简化上面/下面的逻辑,因为您只对内部/外部间隔测试感兴趣。 Stamp 的 typedef 当然应该适应millis() 的类型(unsigned long ?),并删除 fakemillis() 并将对它的引用替换为 millis()。

    #include <stdio.h>
    
    #define STAMP_INSIDE 0
    #define STAMP_BELOW -1
    #define STAMP_ABOVE 1
    #define STAMP_BEYONDO -1
    
        /* Intentionally very small, for fast wraparound
        ** Intentionally signed to stress test the logig.
        */
    typedef signed char Stamp;
    
        /* fake clock, returns incrementing value, but folds around
        */
    Stamp fakemillis(void)
    {
    static Stamp ticker =0;
    return ticker++;
    }
    
    /* Check if "test" is inside or above/below the interval {low,high}
    ** low and high may have been wrapped around zero (--> low > high)
    ** return
    **      0 := "test" inside interval
    **      1 := "test" below interval
    **      -1 := "test" above interval (but wrapped)
    ** The two impossible cases return -2.
    */
    static int check_interval(Stamp low, Stamp high, Stamp test)
    {
    switch (4 *(high >= low)
            +2 *(test >= low)
            +1 *(test > high)
            ) {
            case 0: return STAMP_INSIDE;    /* inside (wrapped) */
            case 1:                 /* outside (wrapped) */
                    return ((Stamp)(low - test) < (Stamp)(test - high)) ? STAMP_BELOW : STAMP_ABOVE;
            case 2: break;      /* impossible */
            case 3: return STAMP_INSIDE;    /* inside (wrapped) */
            case 4:                 /* all below */
                    return ((Stamp)(low - test) < (Stamp)(test - high)) ? STAMP_BELOW : STAMP_ABOVE;
            case 5: break;      /* impossible */
            case 6: return STAMP_INSIDE;    /* inside normal case */
            case 7:                 /* all above) */
                    return ((Stamp)(low - test) < (Stamp)(test - high)) ? STAMP_BELOW : STAMP_ABOVE;
            }   
    return STAMP_BEYONDO;
    }   
    
        /* Get new clock value, test if it is inside interval {*old, *old+width)
        ** iff inside: return STAMP_INSIDE;
        ** iff above (or below) return STAMP_ABOVE or STAMP_BELOW
        ** and UPDATE *old
       */
    static int test_or_set(Stamp *old, Stamp width)
    {
    Stamp tick;
    int diff;
    tick = fakemillis();
    
    diff = check_interval( *old, *old+width, tick);
    if (!diff) return 0;
    *old = tick;
    return diff;
    }
    
    
    int main(void) {
    Stamp goodlast=0;
    Stamp tick=0;
    Stamp badlast=0;
    int goodtest;
    int badtest;
    unsigned uu;
    
    for (uu = 0; uu < 260; uu++) {
        tick= fakemillis();
        if (tick > badlast+10) { badlast=tick; badtest=1; } else {badtest =0;}
        goodtest = test_or_set ( &goodlast, 10);
        printf("%x:Tick=%x bad=%x, badtest=%d good=%x goodtest=%d\n"
        , uu, (unsigned) tick
        , (unsigned) badlast, badtest
        , (unsigned) goodlast, goodtest
        );
        }
    return 0;
    }
    

    如果您在“普通”计算机上编译并运行上述程序,您会看到 badlast 和 badtest 卡住了。恕我直言,您的 arduino 也会发生这种情况。

    更新:绝对溢出/翻转。 (GIYF) http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1200662708

    Update2:不相关但不好的编码习惯:

    #define CMD_SET_SPEED "S"
    ...
    /* Internal configuration */
    if(buffer.substring(0,1)==CMD_SET_SPEED) {
      updateSpeed(buffer.substring(1));
      }
    

    你在这里比较两个字符串。 (这可能按 c++ 的意图处理,但在 C 中它是完全错误的。我还建议用一个巨大的 switch 语句替换重复的 if(...) {...} ,这至少可以避免调用substr() 函数重复。(还是内联?)

    更新 20111211:这是一个 wrap-oblivious compare&set 函数,它需要一个 pointer 指向要比较和设置的值,以及预期间隔的宽度:

    int test_and_set_if_beyond( unsigned long *pprev, unsigned long width )
    {
    unsigned long tick, low,high;
    low = *pprev;
    high = low+width;
    tick = millis();
    
    if (low < high) {
        if (tick >= low && tick < high ) return 0; /* normal case */
        }
    else { /* interval is wrapped , clock could have wrapped */
        if (tick >= low || tick < high) return 0;
        }
    *pprev = tick;
    return 1;
    }
    

    该函数在loop()部分使用如下:

    if (test_and_set_if_beyond ( &lightTimer, lightnessCheckPeriod)) {
        int newLightness = analogRead(brightnessPin);
        if(newLightness-lightness > LIGHT_TRESHOLD) {
          say(RESPONSE_FLASH);
        }
        lightness = newLightness;
      }
      if (test_and_set_if_beyond ( &pingTimer, pingTimerPeriod)) {
        say(RESPONSE_PING);
      }
      if (test_and_set_if_beyond ( &pingLEDTimer, pingTimerPeriod*2)) {
        digitalWrite(failPin, HIGH);
      }
      feed();
    

    最后:恕我直言,RESET 不起作用的原因是,并非所有全局变量都在 setup() 函数中初始化。另外:我认为您应该摆脱 String 的东西(运行时中有 GC 吗?)并改用普通字符缓冲区。

    【讨论】:

    • 1.根据文档 millis() 在 50 天内溢出。我在我的代码中使用 (unsigned long) 2. 板在重置后不会恢复发送数据 3. 板闪烁 TX led => 这不是问题
    • 来自论坛的链接(回复#2):“计数器是无符号长整数。在 16Mhz 时,即 550 秒,大约 9 小时。”
    • 是的,我知道有错误的代码 :) 我将尝试实现翻转检测和另外一个根本不使用计时器的输出功能。虽然它不会解释重置程序不会有帮助。 RESET 将所有变量重置为零并从一开始就重新启动程序,不是吗?但 RESET 不会恢复串行连接。
    • millis() 在板复位时会复位。
    【解决方案4】:

    几个小时后,Arduino 无法通过电线执行来自 Arayks 和 TX 900 的命令,但重启后 Arduino 已启用。

    【讨论】:

    • 这可能是一个更有用的答案,如果您要详细说明为什么几个小时后无法执行命令,以及如何在不定期重启的情况下解决问题(这是他们的主旨问题)。建议对他们的代码进行有用的修改也是一个加分项,对任何相关参考源(包括链接)的引用也可能会更清楚地说明情况。
    猜你喜欢
    • 1970-01-01
    • 2019-04-03
    • 2011-04-08
    • 1970-01-01
    • 2012-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多