¿Se está comunicando a través de USART con un AVR - Input Buffer?

4

Actualmente estoy construyendo un robot (como algunos de ustedes pueden saber de mis preguntas anteriores). La tarea actual con la que estoy tratando es la de la comunicación. Para simplificar las cosas, suponga que tengo 5 comandos para enviar desde mi estación terrestre a mi robot:

  1. conducir hacia adelante
  2. conducir hacia atrás
  3. Activar el servo 1
  4. Activar el servo 2
  5. Velocidad del motor

Ahora, estoy enviando estos comandos desde mi computadora a un adaptador USB a serie y luego a través de un enlace de RF. Pero esto no es importante, por lo que sé, para mi pregunta.

Mi pregunta es, ¿cómo funciona la comunicación en general? Mi idea es que tendré cinco paquetes de datos de 8 bits diferentes (uno para cada comando) que enviaré continuamente desde mi computadora. Así que mantendré un bucle infinito que verificará, digamos, la posición de un joystick analógico. Si apunta hacia arriba, el comando 1 llevará el mensaje de avance de la unidad. Si apunta hacia abajo, el comando 2 transmitirá el mensaje, etc. Dependiendo de qué tan lejos esté el joystick en la posición arriba / abajo dictará el contenido del comando 5. Y el estado de algunas teclas en el teclado determinará si los comandos 4 y 5 contienen información para activar servos.

Nuevamente, planeo tener un bucle continuo que verificará cada uno de los 5 estados y enviará los comandos apropiados a través del USART desde el lado de la computadora.

El problema que tengo, conceptualmente al menos, es que ¿qué pasa si hay un retraso en el extremo del robot para procesar los datos que llegan a través del USART? Según tengo entendido, para garantizar que los datos no se pierdan, los datos se almacenan en un búfer en la MCU. Esencialmente, quiero un sistema de "último en entrar, primero en salir" en mi robot, por lo que incluso si pierdo los comandos más antiguos por cualquier motivo, el robot está haciendo lo que quiero que haga ahora, no lo que quería hace 2 segundos. Pero con esta metodología, también temo que me salteé los paquetes de datos de los comandos 1-4 porque simplemente seguirá leyendo el último paquete de datos (es decir, el comando 5).

Se me ocurrió esta "hoja de ruta" de comunicación por mi cuenta, así que estoy seguro de que hay formas mucho mejores de lograr lo que quiero. Pero espero que entiendas lo que quiero decir. Además, estoy usando un ATmega328 en mi robot, si importa.

Agradecería cualquier consejo relacionado con esta situación.

    
pregunta capcom

3 respuestas

0

Lo que normalmente hago es tratar de asegurarme de que el controlador no envíe comandos más rápido de lo que el robot puede procesar. El envío de instrucciones de forma más rápida de lo que puede ser procesado por el robot siempre resultará en una pérdida de información con FIFO o con un búfer LIFO. Como una copia de seguridad para el retraso intermitente en el robot, usaría un pequeño buffer FIFO. El tamaño exacto depende de cuál sea el equilibrio entre los comandos perdidos y la capacidad de respuesta.

un búfer LIFO realmente no tiene mucho sentido en ningún caso porque terminará con una operación fuera de orden en caso de que el robot responda a los comandos lentamente. considera lo siguiente:

  1. Enviar comando de avance de unidad
  2. Enviar Activar servo 1
  3. comando de devolución de unidad
  4. Enviar Activar servo 2

Con una estructura LIFO, diga que todos los comandos se envían antes de que el robot tenga tiempo para procesar cualquier comando. ¡Los comandos ahora serán ejecutados en orden inverso! El robot activará el servo 2, retrocederá, activará el servo 1 y luego avanzará. Lo más probable es que esto nunca sea lo que quieres.

Si solo estás procesando la última instrucción, entonces no será necesario ningún búfer adicional.

    
respondido por el helloworld922
3

Lo que he hecho en algunos proyectos es usar un búfer circular que se llena con un ISR activado por un UART.

Entonces, cuando el UART recibe un byte, se activa una interrupción y el manejador carga ese byte hasta el final del búfer.

Luego, en el bucle principal, puedes verificar si hay algún byte en el búfer y ejecutar cada comando que se haya puesto en cola.

Mucha información sobre buffers circulares en internet.

Saludos.

    
respondido por el Mark B
0

Esto es bastante similar a un proyecto mío que utiliza un ATmega16 que se comunica a través de un Lantronix XPort . Uso de putty configurado con ACK-character answerback en ENQ El código se ve así (aún no se han solucionado las cosas del temporizador):

#define F_CPU 8000000UL                         // 8MHz - prevents default 1MHz
#define USART_BAUDRATE 38400UL                  //300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#define BUFFER_SIZE 255

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#define pinstate(sfr,bit) ((sfr) & (1<<(bit)))

int coms = 0;                                   // Do we have communication, reset by timer every x milliseconds
int BufferI = 0;                                // Buffer position
char Buffer[BUFFER_SIZE];                       // Receive buffer
char cmd[BUFFER_SIZE];                          // Command variable
int sendheartbeat = 0;
char clearandhome[7] = {'\e', '[', '2', 'J', '\e', '[', 'H'};
#define PROX_SW_EXPANDED            PINB1       // PIN2 POSITION WHEN EXPANDED, READY FOR START
#define MTR_RADIAL_CLOCKWISE        PINC3

void USARTWriteChar(char data) {
    while(!(UCSRA & (1<<UDRE))) { }             //Do nothing until the transmitter is ready
    UDR=data;                                   //Now write the data to USART buffer
}

void USARTWriteLine ( const char *str ) {
    while (*str) {
        USARTWriteChar(*str);
        str++;
    }
    USARTWriteChar(0x0D);                       //Carriage return, 13dec, ^M
    USARTWriteChar(0x0A);                       //Line feed      , 10dec, ^J
}
ISR(USART_RXC_vect) {
    char ReceivedByte;
    ReceivedByte = UDR;                         // Fetch the received byte value into the variable "ReceivedByte"
    coms = 1;                                   // We just received something
    TCNT1 = 0;                                  // reset counter
    if (ReceivedByte == 0x0A) {                 // Use linefeed as command separator
        Buffer[BufferI] = '
#define F_CPU 8000000UL                         // 8MHz - prevents default 1MHz
#define USART_BAUDRATE 38400UL                  //300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#define BUFFER_SIZE 255

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#define pinstate(sfr,bit) ((sfr) & (1<<(bit)))

int coms = 0;                                   // Do we have communication, reset by timer every x milliseconds
int BufferI = 0;                                // Buffer position
char Buffer[BUFFER_SIZE];                       // Receive buffer
char cmd[BUFFER_SIZE];                          // Command variable
int sendheartbeat = 0;
char clearandhome[7] = {'\e', '[', '2', 'J', '\e', '[', 'H'};
#define PROX_SW_EXPANDED            PINB1       // PIN2 POSITION WHEN EXPANDED, READY FOR START
#define MTR_RADIAL_CLOCKWISE        PINC3

void USARTWriteChar(char data) {
    while(!(UCSRA & (1<<UDRE))) { }             //Do nothing until the transmitter is ready
    UDR=data;                                   //Now write the data to USART buffer
}

void USARTWriteLine ( const char *str ) {
    while (*str) {
        USARTWriteChar(*str);
        str++;
    }
    USARTWriteChar(0x0D);                       //Carriage return, 13dec, ^M
    USARTWriteChar(0x0A);                       //Line feed      , 10dec, ^J
}
ISR(USART_RXC_vect) {
    char ReceivedByte;
    ReceivedByte = UDR;                         // Fetch the received byte value into the variable "ReceivedByte"
    coms = 1;                                   // We just received something
    TCNT1 = 0;                                  // reset counter
    if (ReceivedByte == 0x0A) {                 // Use linefeed as command separator
        Buffer[BufferI] = '%pre%';                 // String null terminator
        memcpy(cmd, Buffer, BufferI + 1);       // Copy complete command to command variable    
        BufferI = 0;                            // Reset buffer position
    } else if (ReceivedByte > 0x1F)             // Ignore Control Characters
        Buffer[BufferI++] = ReceivedByte;       // Add received byte to buffer
    #ifdef DEBUG
    switch (ReceivedByte) {
        case 'q': USARTWriteLine(Buffer); break;
        case 'w': USARTWriteLine(cmd); break;
    }    
    #endif  
}
ISR(TIMER1_OVF_vect) {
    coms = 0;                                       // Lost communication
}

ISR(TIMER0_OVF_vect) {
    if (sendheartbeat) USARTWriteChar(0x05);        // 15 times / second, 66,67ms
}

int main(void) {
    TIMSK=(1<<TOIE0) | (1<<TOIE1);      
    TCNT0=0x00;         
    TCCR0 = (1<<CS02) | (1<<CS00);  
    TCCR1B |= (1 << CS10) | (1 << CS12);

    UBRRL = BAUD_PRESCALE;                      // Load lower 8-bits of the baud rate value into the low byte of the UBRR register
    UBRRH = (BAUD_PRESCALE >> 8);               // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
    UCSRB = (1<<RXEN)|(1<<TXEN | 1<< RXCIE);    // Turn on the transmission and USART Receive Complete interrupt (USART_RXC)
    UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);  // Set frame format: 8data, 1stop bit

    DDRB = 0x00;                                // Set all pins on PORTB for output
    DDRC = 0xff;                                // Set all pins on PORTC for output
    sei();                                      // Enable the Global Interrupt Enable flag so that interrupts can be processed

    while(1) {
        if (!coms) PORTC = 0x00;                // If not coms turn of everything   
        else if (cmd[0] != '%pre%') {              // We have coms, check for new command
            if (strcmp("help", cmd) == 0) USARTWriteLine("liberate tuteme ex inferis");
            else if (strcmp("sync", cmd) == 0) sendheartbeat = !sendheartbeat;
            else if (strcmp("clear", cmd) == 0) USARTWriteLine(clearandhome);
            else if (strcmp("expand", cmd) == 0) {
                if (!pinstate(PINB, PROX_SW_EXPANDED))
                    sbi(PORTC, MTR_RADIAL_CLOCKWISE);
            } else if (!(strcmp("", cmd) == 0)) USARTWriteLine("Unknown command");
            cmd[0] = '%pre%';                      // Reset command string with null terminator.
        }
    }
}
'; // String null terminator memcpy(cmd, Buffer, BufferI + 1); // Copy complete command to command variable BufferI = 0; // Reset buffer position } else if (ReceivedByte > 0x1F) // Ignore Control Characters Buffer[BufferI++] = ReceivedByte; // Add received byte to buffer #ifdef DEBUG switch (ReceivedByte) { case 'q': USARTWriteLine(Buffer); break; case 'w': USARTWriteLine(cmd); break; } #endif } ISR(TIMER1_OVF_vect) { coms = 0; // Lost communication } ISR(TIMER0_OVF_vect) { if (sendheartbeat) USARTWriteChar(0x05); // 15 times / second, 66,67ms } int main(void) { TIMSK=(1<<TOIE0) | (1<<TOIE1); TCNT0=0x00; TCCR0 = (1<<CS02) | (1<<CS00); TCCR1B |= (1 << CS10) | (1 << CS12); UBRRL = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register UBRRH = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register UCSRB = (1<<RXEN)|(1<<TXEN | 1<< RXCIE); // Turn on the transmission and USART Receive Complete interrupt (USART_RXC) UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); // Set frame format: 8data, 1stop bit DDRB = 0x00; // Set all pins on PORTB for output DDRC = 0xff; // Set all pins on PORTC for output sei(); // Enable the Global Interrupt Enable flag so that interrupts can be processed while(1) { if (!coms) PORTC = 0x00; // If not coms turn of everything else if (cmd[0] != '%pre%') { // We have coms, check for new command if (strcmp("help", cmd) == 0) USARTWriteLine("liberate tuteme ex inferis"); else if (strcmp("sync", cmd) == 0) sendheartbeat = !sendheartbeat; else if (strcmp("clear", cmd) == 0) USARTWriteLine(clearandhome); else if (strcmp("expand", cmd) == 0) { if (!pinstate(PINB, PROX_SW_EXPANDED)) sbi(PORTC, MTR_RADIAL_CLOCKWISE); } else if (!(strcmp("", cmd) == 0)) USARTWriteLine("Unknown command"); cmd[0] = '%pre%'; // Reset command string with null terminator. } } }

Espera que esto sea útil.

    
respondido por el wittrup

Lea otras preguntas en las etiquetas