Mi propia función millis () no es precisa en el arduino

4

Yo mismo escribí un programa para implementar las funciones millis () y delay () sin la biblioteca arduino. Incluí una variable de conteo que cuenta cada segundo y envío su valor cada segundo a través del puerto serie. Lo que encontré es que su valor se aleja del valor real en casi 2 segundos cada 3 minutos. ¿Hay algún problema con mi código? ¿O es ese Serial.print () el culpable que puede retrasar esa rutina? ¿Cuánto tiempo tarda en ejecutarse Serial.print ()?

Aquí está el código:

Editar: Edité el código de esta manera, pero el recuento del arduino sigue demorando alrededor de 4 segundos después de 4 minutos. Se demora 13 segundos después de 10 minutos, es decir, solo cuenta 587 segundos después de los 600 segundos reales.

Editar 2: Aquí está mi código actualizado. Todavía hay retraso en el tiempo. Tengo un retraso de alrededor de 6 segundos en 5 minutos.

#include "Arduino.h"
#include <avr/io.h>
#include <avr/interrupt.h>


void toggle_led(void);

unsigned long volatile millis_count = 0;
volatile char state = 1;
unsigned long volatile current_count = 0;
unsigned int volatile count = 0;

ISR(TIMER0_COMPA_vect) {        //Timer interrupt ISR

    millis_count++;
    if (millis_count - current_count == 1000) {
        current_count = millis_count;
        toggle_led();
        Serial.println(count++);
    }

}


int main(void) {

    init();

    TCCR0B = 0b11;  //Timer settings for interrupt at every millisecond
    OCR0A = 249;
    TIMSK0 |= 0b10;

    sei();
    Serial.begin(9600);
    DDRB |= 1<<5;

    for (;;) {



    }
}

void toggle_led(void) {

    PORTB ^= (1<<5);
}
    
pregunta 0xakhil

4 respuestas

7

Su retraso será la duración de la función newdelay () más el tiempo necesario para enviar los datos en serie.

Tienes:

  1. Enviar el recuento a través de serie
  2. espera 1000ms
  3. LED de alternancia.

Cada uno de esos pasos lleva tiempo.

Para obtener un tiempo exacto de 1000 ms, puede activar el envío en serie desde una interrupción de 1 segundo, o puede examinar el recuento de milisegundos dentro de su bucle y enviar los datos en serie cuando los milisegundos pasen de 1000:

unsigned long lastmil;

lastmil == newmillis();
for(;;)
{
    if(((newmillis()%1000) == 0) && (lastmil != newmillis()))
    {
        lastmil = newmillis();
        Serial.println(count++);
        toggle_led();
    }
}

(otra forma de detenerlo repetidamente varias veces durante un milisegundo sería agregar un breve retraso dentro del if (..) para garantizar que la rutina tome al menos 1 ms)

La función Serial.println () tomará un tiempo variable dependiendo de:

  1. La velocidad de transmisión en uso
  2. El número de caracteres enviados

A 9600 baudios, está enviando 9600 símbolos por segundo. Con el formato "8N1" es de 10 símbolos por byte. Por lo tanto, para una cadena de 3 dígitos, más el retorno de carro y el avance de línea, serán 10 símbolos × 5 caracteres, que son 50 símbolos.

9600 baudios es 0.0001041670.000104167s por símbolo (o 104µS por símbolo), por lo que 50 símbolos serán 0.00520835s (o 5.20835ms).

Ese es el tiempo de transmisión real. A continuación, también debe agregar que el tiempo necesario para realizar el formateo de los datos y la llamada de las rutinas en serie. Todos estos toman una cantidad de tiempo no especificada. Para descubrirlo, deberá conocer el código de ensamblaje que la rutina recopila y luego encontrar la cantidad de ciclos de reloj que toma cada instrucción y sumarlos en su totalidad.

    
respondido por el Majenko
6

Majenko ha explicado en detalle por qué está viendo el cambio de tiempo. La pregunta ahora debería ser cómo hacerlo bien. Ya se ha encontrado con una de las varias razones por las que las esperas ocupadas son una mala manera de hacer un cronograma a largo plazo.

No conozco el hardware AVR en el que se basa el arduino, pero estoy seguro de que tiene temporizadores. Debe haber una manera de configurar una interrupción periódica completamente en el hardware utilizando uno de estos temporizadores. Una interrupción de 1 kHz (período de 1 ms) suele ser una buena compensación entre la resolución de tiempo, no usar demasiado la CPU y un valor suficientemente pequeño que el hardware puede realizar de forma nativa. Esta rutina de interrupción se llamará 1000 veces por segundo regularmente por el hardware, independientemente de lo que esté haciendo su rutina forground. Puede contar fácilmente múltiples tics de 1 ms para hacer tics con períodos más largos, como 1 segundo. Como 1 segundo es bastante lento, la rutina de interrupción puede establecer una marca cada segundo que la rutina Foregrond comprueba en su ciclo de eventos principal, luego se reinicia.

Dependiendo de lo que esté sucediendo, es posible que no pueda utilizar una rutina de interrupción, sino que el hardware simplemente configure una marca cada vez que marque el reloj. En ese caso, es probable que desee que el reloj marque más, como cada 10 ms. El código de primer plano lo controla en el bucle del evento principal y lo divide en la marca de 1 s que realmente desea.

    
respondido por el Olin Lathrop
3

Siguiendo con la respuesta de Olin ... así es como se implementa la función Arduino millis () ... Hay una variable de contador global volátil de 32 bits llamada timer0_millis que se mantiene / actualiza por el TIMER0_OVF_vect ISR, y los millis La función en sí misma simplemente detiene las interrupciones brevemente para leer ese valor en una variable local y devolverlo. Puedes revisar la implementación en el archivo "wiring.c" en la biblioteca central de Arduino para ver cómo lo hicieron realmente.

    
respondido por el vicatcu
1

Además de las respuestas mencionadas, existe otra posibilidad de desvío del tiempo. Los cristales utilizados para marcar el arduino también tienen un rango de tolerancia bastante amplio, de modo que el cristal puede inducir una deriva. Si planea usar esto durante un largo período de tiempo y debe ser preciso en sus mediciones de tiempo, buscaría un chip de reloj externo, por ejemplo el DS1307, que es fácil de conectar al arduino. Estos chips suelen usar un cristal de 32 kHz de "alta precisión" para mantener el tiempo (el mismo cristal que se usa en los relojes de pared comunes).

    
respondido por el wesen

Lea otras preguntas en las etiquetas