Temporización de alta precisión en Arduino para comunicación serial

11

Estoy usando un Arduino Uno para enviar información de tiempo y voltaje a través del puerto serie a Python para trazar. Sin embargo, los intervalos de tiempo entre marcas de tiempo sucesivas parecen estar aumentando con el tiempo, lo que afecta mi trazado. Esto es especialmente cierto cuando la velocidad en baudios se establece en 9600, donde mis diferencias de tiempo iniciales pueden ser 1320 y aumenta a 16400 después de un período de tiempo relativamente corto. Cuando esta tasa se pone al máximo de 115200 bps, el cambio es más lento y menos notable, de alrededor de 1340 a 1500, incluso después de un envío relativamente largo. Todos los tiempos se dan en microsegundos.

Me gustaría saber si puedo reducir o eliminar este efecto, y si no entiendo por qué existe. He leído cosas sobre las interrupciones y los retrasos que causan esto, pero no aprecio completamente la complejidad de la electrónica en cuestión y me gustaría saber:

  1. ¿Puedo obtener mayor precisión en el tiempo?
  2. ¿Qué causa este cambio en el tiempo?

Esto es lo que tengo actualmente:

#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;
        }
      }
    }
  }
}
usuario3284376
fuente
¿Qué quieres decir con "preciso"? Los tiempos dados por el contador serán bastante precisos, precisos y con una buena resolución. ¿Desea que los tiempos sean deterministas (es decir, siempre iguales)?
Cybergibbons
Disculpas, sí, supongo que eso es lo que quise decir, para que la diferencia entre ellos sea consistente y, si no, la razón por la que no lo son
user3284376
Agregue la marca de tiempo en el extremo de la PC en lugar del extremo de Arduino, o use un módulo RTC (reloj de tiempo real). Los módulos RTC son bastante baratos de encontrar en varias tiendas web, solo asegúrese de que la tienda se vincule a la hoja de datos. Otro método es programar un temporizador y usar una rutina de servicio de interrupción para obtener un tiempo razonablemente preciso.
jippie
¿Qué eHealth.getECG()hacer? ¿Esa llamada siempre dura la misma cantidad de tiempo?
jfpoilpret
¿Puede especificar cuánto dura un "período de tiempo relativamente corto"? ¿Es siempre lo mismo después de reiniciar el Arduino?
jfpoilpret

Respuestas:

4

Use un temporizador e ISR (rutina de servicio de interrupción) para hacer que la sincronización sea más precisa.

Eche un vistazo a mi prueba de concepto de interrupción temporizada de 1 ms . La idea es tener un 'latido' de 1 ms razonablemente preciso en el sistema que pueda usarse para desencadenar otros eventos. En el PoC se usa para parpadear un LED a ½ Hz, pero al tener acceso a las nuevas variables millisecondCountery le secondCounterpermite activar eventos en el bucle principal en momentos arbitrarios (pero sincronizados con precisión).

jippie
fuente
2
Su PoC es muy interesante, pero tiene una falla (fácil de arreglar) en el hecho de que lee un valor de 2 bytes mientras las interrupciones están habilitadas (en loop()), un ISR modifica este valor. Puede suceder que loop()lea un valor incorrecto (en medio de una modificación por el ISR). He publicado un comentario en tu blog al respecto.
jfpoilpret
@jfpoilpret punto interesante que haces allí, nunca pensé en una interrupción a mitad de camino para recuperar el valor de la RAM. Revisaré el desmontaje esta noche y actualizaré el artículo. Tal vez una buena razón para escribir otro artículo también: o)
jippie
Creé una muestra de su PoC y pude ver que el problema ocurre al menos una vez cada 10 segundos en mi UNO. Pero, por supuesto, en realidad depende en gran medida de lo que haga en su loop(): mi muestra acaba de obtener el valor de milisegundos, lo compara con el valor de lectura anterior y si la diferencia> 0 (que no sea restablecer el contador a 0), muestra un mensaje.
jfpoilpret
@jfpoilpret nunca lo notó realmente. Solo lo uso como un latido para monitorear los cubos de comida para mis gatos y hacer un flash LED cuando mis gatos estén potencialmente decepcionados ...; o) Definitivamente cambiará la forma en que uso los ISR en el futuro.
jippie
1
Muestra un cristal conectado a un bloque ATMEGA16U2 y un resonador conectado a ATMEGA328P-PU. El 16U2 es para la interfaz en serie, el 328P es "The Arduino". Curiosamente, el 16U2 podría llevar su reloj a otro chip, por ejemplo, el 328P.
Udo Klein
3

Se me ocurren algunas cosas que pueden afectar la "consistencia" de los tiempos de escritura en serie:

  • tamaño de los datos a imprimir

Esto puede ser lo más obvio en lo que pensar, pero de hecho cuanto más imprima, más se necesitará para manejarlo.

Solución: imprima el formato de la cadena en una cadena de longitud conocida.

  • utilizando serie tamponada

en Unix puede acceder al puerto serie utilizando una forma con o sin búfer. Usar el modo almacenado durante mucho tiempo puede hacerlo un poco más lento a medida que se llena el búfer, por lo general sucede cuando los datos ingresan más rápido de lo que lo está leyendo ...

Solución: utilice la línea serie sin búfer ( por ejemplo : en Darwin / OSX es en /dev/cu.usbmodemXXXlugar de /dev/tty.usbmodemXXX)

  • prioridad de los temporizadores

parece que está usando una interrupción TC, y los AVR tienen prioridades en la forma en que se manejan las interrupciones, no sé el orden de prioridad para el Atmega328, y no es una de las características más documentadas, así que no sé qué tan seguro es TC0 frente a la interrupción UART.

Solución: busque más en la documentación / hoja de datos sobre las prioridades de interrupción y cambie el temporizador si es necesario; y / o hacer una prueba sin tener el otro temporizador funcionando.

  • los datos de los que está leyendo tardan más en leer con el tiempo

algunos controladores necesitan promediar o realizar algunas operaciones sobre los valores anteriores, por lo que cuantos más valores mida, más tiempo será el búfer y más tardará en calcular el valor, hasta que haya alcanzado el tamaño máximo del búfer.

Solución: mire el código fuente de la biblioteca que está utilizando y, bien, optimícelo, elimine el cálculo si hay uno o tenga en cuenta ese aumento del tiempo de procesamiento.

  • evitando la sobrecarga del marco arduino

pero si realmente desea optimizar la salida en serie del arduino, debe evitar usar el arduino de arriba ... Pero es mucho menos elegante y cómodo de usar.

Estoy bastante seguro de que faltan otros puntos, pero eso es lo primero que verificaría antes de seguir investigando.

HTH

zmo
fuente
2

Su código incluye la duración de la salida en mediciones posteriores. Por lo tanto, dependiendo de la longitud de la salida, medirá diferentes tiempos. Esto se puede solucionar formateando a salida de longitud fija.

El siguiente problema es que la ONU tiene una base de tiempo muy pobre. Eche un vistazo aquí para ver una comparación de los diferentes tipos de Arduino y la referencia de tiempo DCF77

Conclusión: si necesita una sincronización precisa, obtenga un Arduino con cristal o elija un RTC. Puedo recomendar altamente los RTC DS3231 / DS3232 ya que estos generalmente alcanzan una precisión de 2 ppm de fábrica.

Udo Klein
fuente