Arduino millis功能耗时超过预期

gab*_*ace 2 arduino timing

我试图让我的Arduino 草图在每次执行该loop函数时休眠1秒钟.

循环中涉及的方法可能会改变它们的执行时间,这就是我实现millis的原因.

我正在做以下事情:

unsigned long ejecucionExcedida = 0;

int calcularExceso(int tiempo) {
    if (tiempo>1000) {
        ejecucionExcedida = ejecucionExcedida + (tiempo-1000);
        // TO DO agregar alarma si el exceso se incrementa mucho
        if(ejecucionExcedida > 20000) {
            alertas(9);
        }
        // Listo las alertas :D
        return 1000;
    }
    else {
        if(ejecucionExcedida == 0) {
            return tiempo;
        }
        else {
            if (ejecucionExcedida + tiempo < 1000) {
                ejecucionExcedida = 0;
                return ejecucionExcedida + tiempo;
            }
            else {
                int exceso = ejecucionExcedida + tiempo - 1000;
                ejecucionExcedida = exceso;
                return 1000;
            }
        }
    }
}

void loop() {
    unsigned long comienzo = millis();

    // A couple of methods

    unsigned long final = millis();

    delay(calcularExceso(final-comienzo));
}
Run Code Online (Sandbox Code Playgroud)

预计每次执行时草图将延迟一秒钟,但我已经用时钟计时,每次执行时间超过一秒.

Udo*_*ein 5

循环函数与您的代码结合导致问题.

void loop() {
// B 

    unsigned long comienzo = millis();

    // a couple of methods

    unsigned long final = millis();
// C
    delay(calcularExceso(final-comienzo));
// A
}
Run Code Online (Sandbox Code Playgroud)

您没有考虑从A到B使用的时间.您也没有考虑测量和延迟(C)之间的时间.

最大的原因是A到B.如果你查看arduino/hardware/arduino/cores/arduino,你会发现main.cpp.一旦你查看这个文件,很明显为什么这需要很长时间.

#include <Arduino.h>

int main(void)
{
        init();

#if defined(USBCON)
        USB.attach();
#endif

        setup();

        for (;;) {
                loop();
                if (serialEventRun) serialEventRun();
        }

        return 0;
}
Run Code Online (Sandbox Code Playgroud)

它实际上不仅仅是"没有".

我建议切换到以下方法来弥补这一点

void loop() {
    static unsigned long start = millis();

    // a couple of methods

    while (millis() - start < 1000) {
        // busy wating
    }
    // do NOT read again as this would cummulate the drift
    // instead add just one second to start
    start += 1000;
}
Run Code Online (Sandbox Code Playgroud)

与您的代码不同,它将变量start声明为static.这意味着在loop()的第一次传递期间将从millis()初始化start.在loop()的每次传递之后,它的值将保留用于loop()的下一次传递.在后续通过期间,它将不再被初始化.因此,在第一次通过中,启动可能具有任何巧合值,例如42.紧接着,最后的while将等到millis()达到1042,总计运行时间为1s.然后start将增加1000.所以在第二次传递中它将是1042而最后一次将等到millis()达到2042.在第三次传递开始将是2042并且最后一次将等到millis()到达3042等.正如你所看到的那样,最终的结束时间总是相隔1000毫秒.由此得出,loop()的开始将间隔1000毫秒(平均),除了可能由serialEventRun()处理引入的一些抖动.

如果您在此更改后仍然遇到大量漂移,那么代码中的某些内容会阻止中断很长时间.由于Arduino的时间是中断的,所以你不能长时间阻止它们.不幸的是,有些功能可能会阻止中断作为副作用.您必须删除代码段才能找出导致此问题的部分.通常最好添加一些LED并每次切换一次状态.

尽管如此,根据您的Arduino型号,您最终仍会遇到一些漂移.较旧的型号有晶体时钟漂移约10ppm(每天几秒).较新的型号通常具有晶体谐振器(更便宜),可能漂移几千ppm(每小时几秒).我在一篇关于Arduino晶体偏差的文章中对此进行了分析