Conectando tres dispositivos UART al Microcontrolador sin pérdida de datos

0

Tengo un microcontrolador TM4C123GXL Launchpad, que tiene 8 puertos UART serie. Estoy usando Keil uVision5 con Tiva Ware para la programación.

Quería conectar 3 dispositivos UART con el microcontrolador sin ninguna pérdida de bytes. La velocidad de transmisión se ha establecido en 115200 bits / seg sin paridad, como se muestra en la siguiente figura. Todos los dispositivos envían marcos de datos periódicamente cada 5ms.

El tiempo de TX y RX se calcula utilizando No_0f_bytes * 10 / BaudRate

He conectado con éxito los dispositivos con el UART de µC. El problema en la comunicación es que estoy perdiendo los bytes. Si hago una comunicación con un solo dispositivo (Dispositivo 2) todavía estoy perdiendo los bytes de todo el marco (20 bytes).

¿Se debe a la limitación FIFO de 16 bytes de Tm4c123 o cualquier otra cosa?

También he implementado la función µDMA de TM4C123. Pero aún así, los bytes se pierden. ¿Qué puedo hacer para mejorar el sistema para la transmisión y recepción sin pérdida de marcos de datos?

Editar:

Aquí está la arquitectura del software:

Estoy utilizando la interrupción periódica del temporizador de 5 ms para recibir y transmitir la trama. Todas las tramas tienen el primer encabezado de 2 bytes y un byte contador como el último byte.

void Timer1IntHandler(void) //  Periodic Service Routine every 5ms
{

DisableIntrupts();

bool Correct=ReadJoystick(); //10 bytes  Device 1

if(Correct)
{
GenerateServoCardsFrame();

SendServo1Frame();   //20 bytes  Device 2
SendServo2Frame(); //17 bytes  Device 3
ReadServo1Frame();  //15 bytes Device 2
ReadServo2Frame();  //20 bytes Device 3

GenerateJoystickFrame();

SendJoystickFrame(); //10 bytes   Device 1

EnableIntrupts();
}

}

main()
{
SetupClock()  ;   //Setup 16 MHz Clock
SetupJoystick();  //Initalize uart1 port for Device1
SetupServoCard1(); //Initalize uart2 port for Device2
SetupServoCard2(); //Initalize uart3 port for Device3

InitalizePeriodicTimerHandler(5);   //Periodic Service Routine every 5ms  (Timer1IntHandler)

while(1)
{
}

}


bool ReadJoystick(void)
{
    int BytePos=0;
    int CountInvalid=0;
    int LoopoutTime=0;

    while(1)
    {
    if (ROM_UARTCharsAvail(UART1))                              
        { 
            ByteRX = ROM_UARTCharGetNonBlocking(UART1);            
            if (BytePos==0)
            {
                if (ByteRX== 0xA1)      //Header1 found                              
                {
                    KArray[0] = Bytebuf ;
                    BytePos ++;
                }
                else
                {
                    CountInvalid++;
                    if (CountInvalid>5) 
                        return 0;
                }           
            }            
            else if (BytePos ==1) 
            {           
                if (ByteRX == 0x66)      //Header2 found                                   
                {   
                    KArray[1] = ByteRX;
                    BytePos ++;
                }
                else                                                   
                    BytePos=0;                                                        
            }            
            else
            {
                KArray[BytePos++] = ByteRX;                
                if (BytePos==10)                                                      
                    return 1;      //Frame Recived                                                   
            }
        }
        else                                                          
        {
            SysCtlDelay(0.25*SysCtlClockGet()/3 / 1000);        //   0.25ms delay
            LoopoutTime++;            
            if (LoopoutTime > 10)                                      
                return 0;            
        }    
    }       

}

Según mi cálculo, 1 byte requiere 10/115200 = 0.08680ms y para leer un FIFO de 16 bytes requiere 1.38ms. La tabla de la figura muestra el tiempo total de Tx de 4.08 ms y el tiempo de respuesta de 3.91 ms, que suma 8 ms. Esto es mayor que mi Rutina de servicio de interrupción periódica.

¿Tengo que aumentar el tiempo de interrupción periódica?

    
pregunta Masood Salik

1 respuesta

5

El diseño del software no es bueno y es probablemente la razón por la que se pierden los bytes entrantes.

Primero, no estoy seguro de si se trata de un error o un error tipográfico. Deshabilita las interrupciones al principio de Timer1IntHandler() , pero luego las vuelve a habilitar si Correct es verdadero. ¿No desea volver a habilitar las interrupciones antes de regresar sin importar el condicional? Parece extraño que las interrupciones puedan dejarse deshabilitadas cuando la función vuelva.

Parece que su código lee caracteres de UART1 solo dentro de la función ReadJoystick() . Y sospecho que UART1 no se lee mientras se llaman todas las funciones de GenerateServoCardsFrame() a SendJoystickFrame() . ¿Cuánto tardan en ejecutarse esas funciones? ¿Podrían esas funciones tomarse el tiempo suficiente para que la FIFO UART1 se llene y se desborde? Esto podría ser cuando se eliminan los bytes entrantes.

Si estuviera diseñando este software, lo implementaría de manera completamente diferente a lo que usted ha hecho. Yo habilitaría la solicitud de interrupción de UART y crearía una rutina rápida de controlador de interrupción de UART. Lo único que haría el ISR de UART es copiar bytes a / desde los registros UART TX / RX. Yo crearía dos buffers circulares (a.k.a. ring) para contener los bytes. El ISR de UART copiaría un byte recibido del registro UART RX al búfer circular de RX. Y el ISR de UART copiaría un byte para transmitir desde el búfer circular de TX al registro de TX de UART. El ISR de UART no intentaría interpretar el significado de ninguno de los bytes. Todo lo que hace es mover bytes entre los buffers de RAM y el periférico UART. Esto mantiene corto el ISR de UART, lo que permite que el programa en general responda mejor a otras interrupciones.

Luego crearía una función main() con un bucle infinito y dentro del bucle infinito llamaría a una función llamada SerialReceive() para leer los mensajes fuera del búfer RX. SerialReceive() se implementaría como una máquina de estado. Si hay bytes disponibles en el búfer RX, procesará un número finito de bytes a través de la máquina de estado. La máquina de estados tendría estados para el encabezado del marco, el cuerpo y el tráiler similares a los que has hecho. SerialReceive() devuelve inmediatamente cuando se completa un mensaje o no hay más bytes disponibles. Cuando un mensaje está incompleto porque no hay más bytes disponibles de inmediato, entonces SerialReceive() no los esperará, sino que recordará el estado actual y el mensaje para que pueda continuar con el mismo mensaje cuando se vuelva a llamar desde main() . .

Si necesita hacer algo periódicamente, configure un temporizador como lo ha hecho usted, pero en lugar de hacer todo el trabajo dentro del ISR del temporizador, simplemente establezca una bandera. El bucle infinito principal debe verificar repetidamente el indicador y hacer lo que sea apropiado cuando el indicador ISR haya establecido el indicador. Hacer el trabajo desde el contexto de main() significa que el sistema puede responder a otras interrupciones mientras realiza el trabajo.

Mantener los ISR cortos permite que el sistema en general responda mejor a otras solicitudes de interrupción. Si pasa demasiado tiempo en un ISR, como creo que está haciendo en su ISR de temporizador, entonces el sistema no responderá.

Actualización: En su comentario usted dice que esas funciones se repiten hasta que las transmisiones están completas y toman más de 7 milisegundos. Es tiempo suficiente para que lleguen a la UART 80 bytes y su código no está leyendo esos bytes durante este tiempo, por lo que, por supuesto, los bytes se perderán.

Sus funciones de transmisión deberían copiar bytes en el búfer de TX y regresar sin esperar a que se transmita todo el mensaje. El ISR de UART debe transmitir un byte en cada invocación, mientras que el búfer de TX contiene bytes.

El búfer RX y TX debe ser más grande que cualquier mensaje. Por lo general, los búferes tienen un tamaño de potencia de dos porque eso hace que sea fácil rodear los punteros del búfer al principio. Así que haz que sean de 256 bytes (o 128 o 64, pero ¿por qué no más grandes?).

Debería tener un conjunto independiente de buffers RX / TX para cada UART.

Cambiar el período de su temporizador periódico ISR no afectará el problema con su código original. Dentro de su ISR periódico, su código está gastando 7 milisegundos NO leyendo el UART. Su código perderá bytes independientemente del período del temporizador.

    
respondido por el kkrambo

Lea otras preguntas en las etiquetas