La mejor manera de encontrar el retraso entre la recepción de datos de USART

Escribo un código (para Atmega168P) para recibir datos de la interrupción USART como se muestra a continuación:

ISR     (USART_RX_vect)
{
    unsigned char count=0;      
    unsigned char coder[13];    
    int over_uart=0;

    for (count=0;count<13;count++)
    {
        while (!recive_485)
        {
            over_uart++;                
            wdt;
            if (over_uart>=20000)   {   
                coder[0]=0;     
                count=14;       
                over_uart=0;    
                break;  }
        }
        coder[count]=UDR0;
    }
}

Cada vez que recibo 13 bytes de datos y los coloco en coderuna matriz.

Uso una variable over_uartpara calcular el tiempo entre cada uno de los bytes recibidos. si over_uart>=20000, significa que:

El progreso de recepción se inicia, pero tenemos un problema en las líneas que no obtienen el siguiente byte en 20000 ms. Termina el último comando y busca nuevos 13 bytes que recibirá de la línea.

¿Alguien conoce una forma optimizada que encuentre un retraso entre la recepción de datos de USART (sin ayuda de la over_uartvariable)?

EDITAR :

Corrijo el problema en mi código y elimino el retraso puesto en ISI como golpe:

ISR     (USART_RX_vect)
{
    coder[count]=UDR0;
    if (coder[count] == 20)
        UartFlag=True;
    else
        count++;
}

Y en Mainfunción:

while (1)
    {
        if (UartFlag)
        {
            DoSomthing();
            count=0;
            UartFlag=Fulse;
        }
    }
poner un retraso en el controlador de interrupciones es una mala idea. Simplemente lea todos los datos disponibles en el controlador, tómese un tiempo (de algún temporizador del sistema) y compruébelo después de que ocurra la próxima interrupción.
subdesbordamiento/desbordamiento de búfer? error de bit? ¿control de flujo? ¿Falta de diligencia debida de EMI?
Has escrito esto como si una ISR fuera como una tarea o un hilo. Que no es. Cuando el hardware activa el ISR, usted hace lo mínimo necesario para dar servicio al evento de hardware, luego sale del ISR y realiza el resto de su procesamiento en su código 'principal' (que puede o no ser una tarea dependiendo de su sistema).
Encienda un DIO cuando desee comenzar a medir y apáguelo cuando desee detenerlo. Luego míralo en un visor.
@brhans: Tienes razón, lo uso coderpor la razón. cuando se activa la interrupción, recibo datos, los introduzco codery continúo con otra tarea... el punto es que DEBO obtener 13 bytes y luego hacer otra tarea
@ScottSeidman: ¿y un DIO me muestra el retraso entre la recepción de cada byte?
@TonyStewart.EEsince'75: dice cuello de botella de mi código, ¿verdad?
@combo_ci Incluso si debe obtener 13 bytes, no significa que tenga que esperarlos en la interrupción.
UART ISR simplemente enmascara IRQ, inserta datos y registro de estado, verifica y rearma IRQ y luego muestra los datos y el estado para el control de flujo, errores de paridad, retroalimentación de errores de mensajes más rápido de lo que se puede desbordar el búfer. (usted está utilizando estos controles de integridad, ¿verdad? ¿y el control de flujo de pila?
La interrupción se dispara por 1 byte. Coloque ese 1 byte en coder, luego salga del ISR . Espere la próxima interrupción, repita hasta que haya puesto 'suficientes' bytes codero cualquier otra condición que necesite (como la expiración de un temporizador). No se quede sentado esperando dentro del ISR a que sucedan más cosas.
@brhans tienes razón brhans... es un error mantener el ISI con un temporizador
@TonyStewart.EEsince'75 lo siento, pero aún no entiendo tu objetivo
En cada ISR, debe recopilar el byte único recibido (o condición de error) y, si lo desea, almacenar la marca de tiempo. Si puede actuar en el tiempo entre llegadas de manera eficiente, puede hacerlo en el ISR, de lo contrario, debe manejar eso en otro lugar, tal vez configurando un indicador de "tardó demasiado" o algo así. Recuerde que cualquier variable o estructura de datos compartida entre el ISR y el programa principal debe ser volátil y que el acceso a elementos de varios bytes desde el subproceso principal no es seguro sin deshabilitar las interrupciones para garantizar la atomicidad.
@ChrisStratton Buena guía :)... usted dice almacenar la marca de tiempo... ¿significa que puso un cristal diferente a bordo y lo usó para guardar cualquier marca de tiempo? Y "tardó demasiado" es una buena idea, pero debe usar el temporizador del sistema para verificar y cambiar eso, ¿es correcto?

Respuestas (4)

Tenga en cuenta que la rutina de interrupción es solo el código al que se salta, nada más. Entonces, su código es totalmente un malentendido:

  • La mayoría de sus variables deben ser estáticas. En su código, son locales apilados que se recrean en cada interrupción. Además, debido al flujo de código indeterminado causado por las interrupciones, debe deshabilitar las posibles optimizaciones en las variables vulnerables declarándolas como "volátiles".

  • No hagas ese tipo de bucles allí. Tenga en cuenta que cada entrada de interrupción es parte del bucle de recopilación de datos.

Para resolver el problema, su mejor opción es usar un temporizador de hardware. En cada entrada de interrupción, tome una lectura de un temporizador que se restablece con el código de salida de la interrupción anterior. Por lo tanto, puede determinar fácilmente si hay un desbordamiento y solucionarlo.

Parece que está diseñando algún tipo de protocolo de comunicación que garantiza la integridad de los datos. Su criterio de integridad consiste en recibir una cantidad esperada de datos en un tiempo limitado.

Si bien este enfoque es técnicamente posible, le aconsejo que busque enfoques más estándar (por ejemplo, enviar paquetes con un byte de inicio y una suma de verificación). Dichos enfoques no tienen restricciones de tiempo (en cambio, simplemente analiza un flujo continuo de bytes) y se pueden hacer arbitrariamente robustos (con una suma de verificación lo suficientemente larga, está prácticamente garantizado que nunca recibirá un paquete dañado).

Confiar en el tiempo para las verificaciones de integridad de datos escala muy mal. Su enfoque inevitablemente fallará una vez que su sistema necesite recibir más datos o su código se vuelva lo suficientemente complejo como para introducir demoras internas que no podrá predecir.

Además, si se adhiere a un protocolo estándar como HDLC , XMODEM o MIN , es probable que encuentre bibliotecas que ya lo implementen, lo que le ahorrará mucho tiempo.

Leí Min Document , parece un buen repositorio... ¿lo usas para la serie Atmega hasta ahora?
@combo_ci Afirman que sus requisitos de RAM están en decenas de bytes, por lo que debería funcionar. Intente construir su programa de muestra para una placa basada en Atmega.

Aquí hay una versión de pseudocódigo para su ISR que no entra en un estado de espera en el ISR y no requiere cambios en ninguna otra parte de su programa.

La premisa básica es que el ISR mantiene una copia local del búfer de recepción. Una vez que este búfer tiene 13 caracteres completos dentro del límite de tiempo, pasa el búfer al código principal a través de un búfer global llamado rcv_buff y genera un semáforo llamado data_ready

Para determinar si ha recibido 13 caracteres dentro del límite de tiempo, utiliza la función time() que devuelve el tiempo en segundos desde la época. Si time() no está disponible en su biblioteca, puede crear una función similar desde un reloj del sistema.

ISR:
     // declare local_count,local_buff, last_time as local static variables
     // rcv_buff and data_ready are globals for main()

     local_count++;                   // bump receive counter
     local_buff[local_count-1]=UDR0;  // save in local buffer
                                      // time() returns time in secs (since epoch)
     if time()-last_time>20 then {    // over time limit, empty local buffer
          local_count=0;              // reset the local character counter
          local_buff=''; }            // flush the local buffer
     last_time = time();              // save current time in secs for next pass
     data_ready=False;                // squelch the semaphore
     if (local_count==13) then {      // we have 13 characters, pass to main code
          local_buff[local_count)=\0  // ensure string ends with null
          rcv_buff=local_buff;        // pass the local buffer to main
          data_ready = True;          // raise the semaphore for main
          local_count=0;              // reset local buffer for next packet
          local_buff='';  }           // flush the local buffer

Editar: se limpió la indexación del búfer para la compatibilidad con C. Comentarios mejorados

probablemente quieras entender cómo funcionan las interrupciones en general antes de sumergirte en ellas. Una vez que haga eso, es probable que tenga que volver a escribir su isr por completo.

Dicho esto, una forma de hacer lo que quieres hacer es algo como esto:

  current_time = time_now(); //stamp current time
  time_elapsed = current_time - previous_time; //calculate the time difference
  previous_time = current_time;  //update previous time

requiere un marco totalmente diferente al que tienes ahora, por cierto.