AVR UART sincronización y decodificación de mensajes

0

Tengo un ISR que pone bytes del UART en un AVR XMEGA en un búfer circular. Cada byte recibido, también incrementa RX_COUNT. Copio el búfer circular en un búfer local si tiene al menos dos bytes para un posible comando "#A".

El protocolo que estoy usando es #A, #B, #C, etc. El problema es que estoy buscando el carácter de sincronización, '#' y ellos ven qué es el siguiente byte y comprueban si es válido. mando. El problema es que, a veces, si envío una cadena como aaaaaaa # A, no ejecuta el comando. Estoy simulando datos de basura para ver si alguna vez se sincronizarán con el byte. ¿Cómo puedo asegurarme de que finalmente se sincronizará con el inicio del mensaje?

Periódicamente, desde mi función principal, verifico cuántos datos están disponibles en el búfer circular. Si hay al menos 2 bytes disponibles, copio el búfer circular en un búfer local:

USART_RXBufferData_Available () devuelve true o false si hay datos en el búfer circular.

Otra pregunta es, ¿tengo que copiar el búfer circular en un búfer local? ¿Cuál es generalmente la mejor práctica para decodificar un paquete serial similar a este?

    #define MIN_MSG_SIZE 2 // Command is #A, #B, #C, etc...
    bool dataAvailable = false; 

    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
    {

        // Copy Circular buffer into local buffer

        if (USART_RXBufferData_Available(&UARTCom1))
        {
            if (UARTCom1.buffer.RX_Count >= MIN_MSG_SIZE)
            {
                for (int i = 0; i < MIN_MSG_SIZE; i++)
                {
                    localBuff[i] = USART_RXBuffer_GetByte(&UARTCom1);

                    UARTCom1.buffer.RX_Count--; // decrement RX count
                }

                dataAvailable = true;
            }
        }   

        else

        {

              dataAvailable = false;

        }
   }

if ((dataAvailable) && localBuff[0] == '#') // Check if there is a new Message AND Check Sync Byte
{   
        uint8_t data = localBuff[1];
        PORTR_DIR = 0b00000011;

        char led0Msg[] = "Turning ON LED0\r\n";
        char led1Msg[] = "Turning ON LED1\r\n";
        char ledoffMsg[] = "Turning OFF LEDS\r\n";

        switch (data)
        {
            case 'A':
            UART->transmit(led0Msg, strlen(led0Msg));
            PORTR_OUT = ~(PIN0_bm); // Turn on LED0
            break;

            case 'B':
            UART->transmit(led1Msg, strlen(led1Msg));
            PORTR_OUT = ~(PIN1_bm); // Turn on LED1
            break;

            default:
            UART->transmit(ledoffMsg, strlen(ledoffMsg));
            PORTR_OUT = (PIN1_bm | PIN0_bm); // Turn off BOTH LEDS

        }

        memset(localBuff, 0, sizeof(localBuff)); // Clear the buffer
        return true;

    }
    
pregunta zacharoni16

1 respuesta

1

A veces, crear su propio protocolo y procesar mensajes puede ser complicado (resultados inesperados), y no notará los detalles hasta que comience a realizar las pruebas y la depuración. Lo mejor que puedes hacer es definir el aspecto de tu mensaje e intentar que el procesamiento sea lo más sólido posible. También puede utilizar elementos como un CRC o suma de comprobación y / o acuses de recibo. Este es un ejemplo rápido / breve y no utiliza nada de eso.

Normalmente, es bueno tener un byte de inicio y un byte final, por lo que es fácil detectar el comienzo de un mensaje y luego saber cuándo se completó / se envió el mensaje con éxito. En su caso, ya que solo tiene un byte para el comando / mensaje, el byte final no es realmente necesario.

Sin embargo, usted dice que el tamaño del mensaje es mínimo, por lo tanto, si va a ser más largo, definitivamente agregaré un byte final. Lo voy a mostrar con un byte final. Puede eliminarlo si lo desea y ajustar el código en consecuencia.

Así que definamos su mensaje: # cmd !, donde '#' es su byte de inicio, '!' es el byte final, cmd es lo que te interesa y la longitud es 3.

#define MIN_MSG_SIZE 3 
#define START_BYTE 0x23 //'#' 
#define END_BYTE 0x21   //'!'

Además, para que sea más fácil, use algunas variables globales para controlar su procesamiento. Así:

bool bMsgStart = false;
bool bExpectingEnd = false; // helpful when you know the message should be ending
uint8_t cmd = '
while (USART_RXBufferData_Available(&UARTCom1))             /* Loop until empty */
{
    uint8_t byte = '
#define MIN_MSG_SIZE 3 
#define START_BYTE 0x23 //'#' 
#define END_BYTE 0x21   //'!'
'; /* temp storage for the received byte */ if (UARTCom1.buffer.RX_Count > 0) { byte = USART_RXBuffer_GetByte(&UARTCom1); /* Get the byte */ if(bMsgStart) { if(bExpectingEnd && (byte == END_BYTE)) /* End of message */ processMessage(cmd); /* Process the message now */ else if(!bExpectingEnd) /* Message buffer isn't full but the message could be complete */ { cmd = byte; /* Since you know the message has started and hasn't ended, this is the byte you are interested in */ /* If you were interested in more than one byte for the command/message this is where you'd add it to the buffer and increment the index until you've received everything in between the start byte and end byte */ bExpectingEnd = true; /* The next byte should be the end byte -> Set the message end flag */ } else if((bExpectingEnd && (byte != END_BYTE)) /* Something went wrong or you received a bad message -> start over */ MsgBytesInit(); /* Reset */ else MsgBytesInit(); /* Shouldn't get here but just in case -> Start over */ } else if(byte == START_BYTE) bMsgStart = true; /* Set the start of message flag */ else MsgBytesInit(); /* Not what we are looking for or isn't the start of a message -> Reset */ } } void MsgBytesInit() { bMsgStart = false; /* Reset the message start flag */ bExpectingEnd = false; /* Reset the message end flag */ cmd = '
bool bMsgStart = false;
bool bExpectingEnd = false; // helpful when you know the message should be ending
uint8_t cmd = '
while (USART_RXBufferData_Available(&UARTCom1))             /* Loop until empty */
{
    uint8_t byte = '%pre%';                                    /* temp storage for the received byte */

    if (UARTCom1.buffer.RX_Count > 0)
    {
        byte = USART_RXBuffer_GetByte(&UARTCom1);           /* Get the byte */

        if(bMsgStart)
        {
            if(bExpectingEnd && (byte == END_BYTE))         /* End of message */  
                processMessage(cmd);                        /* Process the message now */
            else if(!bExpectingEnd)                         /* Message buffer isn't full but the message could be complete */
            {
                cmd = byte;                                 /* Since you know the message has started and hasn't ended, this is the byte you are interested in */
                /* If you were interested in more than one byte for the command/message this is where you'd add it to the buffer and increment the index until you've received everything in between the start byte and end byte */
                bExpectingEnd = true;                       /* The next byte should be the end byte -> Set the message end flag */  
            }
            else if((bExpectingEnd && (byte != END_BYTE))   /* Something went wrong or you received a bad message -> start over */
                 MsgBytesInit();                            /* Reset */ 
            else
                MsgBytesInit();                             /* Shouldn't get here but just in case -> Start over */
        }
        else if(byte == START_BYTE)
            bMsgStart = true;                               /* Set the start of message flag */    
        else
            MsgBytesInit();                                 /* Not what we are looking for or isn't the start of a message -> Reset */
    }
}


void MsgBytesInit()
{
    bMsgStart = false;          /* Reset the message start flag */
    bExpectingEnd = false;      /* Reset the message end flag */
    cmd = '%pre%';                 /* Reset the command (or message buffer)*/                  
}


void processMessage(uint8_t cmd)
{
     PORTR_DIR = 0b00000011;

    char led0Msg[] = "Turning ON LED0\r\n";
    char led1Msg[] = "Turning ON LED1\r\n";
    char ledoffMsg[] = "Turning OFF LEDS\r\n";

    switch(cmd)
    {
         case 'A':
         {
            UART->transmit(led0Msg, strlen(led0Msg));
            PORTR_OUT = ~(PIN0_bm); // Turn on LED0
         }
         break;

        case 'B':
        {
            UART->transmit(led1Msg, strlen(led1Msg));
            PORTR_OUT = ~(PIN1_bm); // Turn on LED1
        }
        break;

        default:
        {
            UART->transmit(ledoffMsg, strlen(ledoffMsg));
            PORTR_OUT = (PIN1_bm | PIN0_bm); // Turn off BOTH LEDS
        }
        break;
    }

    /* Done with the message */
    MsgBytesInit(); /* Reset */             
}
'; // This could also be a buffer of known message length if your are interested in more than one byte for the command (e.g. command and parameter)
'; /* Reset the command (or message buffer)*/ } void processMessage(uint8_t cmd) { PORTR_DIR = 0b00000011; char led0Msg[] = "Turning ON LED0\r\n"; char led1Msg[] = "Turning ON LED1\r\n"; char ledoffMsg[] = "Turning OFF LEDS\r\n"; switch(cmd) { case 'A': { UART->transmit(led0Msg, strlen(led0Msg)); PORTR_OUT = ~(PIN0_bm); // Turn on LED0 } break; case 'B': { UART->transmit(led1Msg, strlen(led1Msg)); PORTR_OUT = ~(PIN1_bm); // Turn on LED1 } break; default: { UART->transmit(ledoffMsg, strlen(ledoffMsg)); PORTR_OUT = (PIN1_bm | PIN0_bm); // Turn off BOTH LEDS } break; } /* Done with the message */ MsgBytesInit(); /* Reset */ }
'; // This could also be a buffer of known message length if your are interested in more than one byte for the command (e.g. command and parameter)

Luego, para procesar los bytes recibidos, hay muchas formas de hacerlo según sus necesidades, pero intente algo como esto:

%pre%     
respondido por el DigitalNinja

Lea otras preguntas en las etiquetas