UART Interrupt no obtiene más de un Char - PIC32MX110F106B

Realmente estoy luchando con una comunicación UART con mi PIC32MX110F016 . Es la primera vez que intento implementar una comunicación UART con la familia PIC32MX pero me resulta difícil y no sé por qué no funciona correctamente. Así que decidí publicar todo mi código en forma de tutorial (paso a paso) para obtener ayuda de ustedes. Espero que ayude en el futuro a otras personas que enfrentan el mismo problema.

Entonces, en primer lugar, importé las bibliotecas y declaré las configuraciones de este microcontrolador:

#pragma config   FNOSC       = FRCPLL
#pragma config   FPLLIDIV    = DIV_2
#pragma config   FPLLMUL     = MUL_20
#pragma config   FPLLODIV    = DIV_2
#pragma config   FPBDIV      = DIV_1
#pragma config   ICESEL      = ICS_PGx2
#pragma config   WDTPS       = PS16384

#define GetSystemClock()        (40000000ul)
#define GetPeripheralClock()    (GetSystemClock())
#define BaudRate   115200

Luego implementé mi función uartconfig():

void uartconfig(void){
PPSUnLock;                        // Allow PIN Mapping for BLE
  PPSOutput(1, RPA0, U1TX);     // MAP Tx to RA0 set to digital out
  PPSInput (3, U1RX, RPA2);     // MAP Rx to RA2 set to digital in
  PPSOutput(4, RPA3, U1RTS);
  PPSInput (2, U1CTS,RPA1);
PPSLock;                         // Prevent Accidental Mapping

#define UART1TX TRISAbits.TRISA0
#define UART1RX TRISAbits.TRISA2

#define CMD TRISBbits.TRISB5

UART1TX = 0;//output
UART1RX = 1;//input

DDPCONbits.JTAGEN = 0;

UARTConfigure(UART1, UART_ENABLE_PINS_CTS_RTS);
UARTSetFifoMode(UART1, UART_INTERRUPT_ON_TX_NOT_FULL | UART_INTERRUPT_ON_RX_NOT_EMPTY);
UARTSetLineControl(UART1, UART_DATA_SIZE_8_BITS | UART_PARITY_NONE | UART_STOP_BITS_1);
UARTSetDataRate(UART1, GetPeripheralClock(), BaudRate);
UARTEnable(UART1, UART_ENABLE_FLAGS(UART_PERIPHERAL | UART_RX | UART_TX));

INTEnable(INT_SOURCE_UART_RX(UART1), INT_ENABLED);
INTSetVectorPriority(INT_VECTOR_UART(UART1), INT_PRIORITY_LEVEL_2);
INTSetVectorSubPriority(INT_VECTOR_UART(UART1), INT_SUB_PRIORITY_LEVEL_0);

// configure for multi-vectored mode
INTConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR);
INTEnableInterrupts();
}

Entonces he decidido crear una command_printfunción para enviar datos a través de UART:

void command_print(char *buffer){ 
while(*buffer != (char)0)
{
  while(!UARTTransmitterIsReady(UART1));
  UARTSendDataByte(UART1, *buffer++);
}
UARTSendDataByte(UART1, '\r');
while(!UARTTransmissionHasCompleted(UART1));
}

Decidí crear una estructura para crear un búfer para el pin TX/RX (a partir de ahora solo estoy usando inputBuffer):

typedef volatile struct UARTBuffer {
char outputBuffer[ UART_INPUT_BUFFER_SIZE ];
char inputBuffer[ UART_OUTPUT_BUFFER_SIZE ];
unsigned short int inputReaderPos;
unsigned short int inputWriterPos;
unsigned short int outputReaderPos;
unsigned short int outputWriterPos;
} UARTBuffer;

UARTBuffer UARTBuffer1;

En mi rutina de servicio de interrupción, ISRdecidí ignorar la U1TXparte y borrar la bandera siempre. Para el U1RXdefiní el como:

void __ISR(_UART_1_VECTOR, ipl2)UART1HANDLER(void){
char c;
if ( INTGetFlag(INT_U1TX) )
 {
    INTClearFlag(INT_SOURCE_UART_TX(UART1));
}

if ( INTGetFlag(INT_U1RX) )
 {

if( (U1STAbits.OERR == 1) || (U1STAbits.PERR == 1) || (U1STAbits.FERR == 1) ){
    // Ignore
    U1STAbits.OERR = 0;//clears if the Receive buffer has overflowed
    U1STAbits.PERR = 0;//parity error
    U1STAbits.FERR = 0;//framing error
    U1RXREG;
}
else {
    // Check if the buffer is all readed. If so, clear the buffer;
    if( UARTBuffer1.inputWriterPos == UARTBuffer1.inputReaderPos ) {
        UARTBuffer1.inputWriterPos = 0;
        UARTBuffer1.inputReaderPos = 0;
    }
    if (UARTBuffer1.inputWriterPos >= UART_INPUT_BUFFER_SIZE){
        // Buffer overflow
        UARTBuffer1.inputWriterPos = 0;
        UARTBuffer1.inputReaderPos = 0;
    }
    c = U1RXREG;
    UARTBuffer1.inputBuffer[ UARTBuffer1.inputWriterPos++ ] = c;
}
INTClearFlag(INT_SOURCE_UART_RX(UART1));
}
}

Finalmente mi main(void)se define como:

int32_t main(void) {
__asm__("EI");
UARTBuffer1.inputWriterPos = 0;
SYSTEMConfig(GetSystemClock(), SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);

INTEnableSystemMultiVectoredInt(); //enable interrupts

uartconfig();

command_print("SF,1");
while (1);
}

En realidad, mi PIC se ejecuta normalmente y se activa mi interrupción. Lo que no puedo entender es la razón por la cual mi código nunca obtiene más de un carácter en la RXRegParte (es decir, U1RXREG) dentro de la ISR. Estoy limpiando la bandera al final de la ISR y creo que todo está bien configurado. Déjame saber qué debo hacer para obtener mi marco correctamente y no solo el primer carácter. Sé que el UART está bien diseñado y si intento enviar otro comando que "SF,1"el registro RX me da un carácter diferente.

EDITAR: No estoy logrando lo que quiero. Estoy perdiendo caracteres en la interrupción, así que conecto el CTS+ RTSpero desafortunadamente no obtengo mejores resultados. ¿Hay alguien disponible para ayudarme con la configuración CTS+ RTSpara la familia PIC32mx? Sé que tengo que cruzar el CTS con el RTS y el RTS con el CTS del microcontrolador al "otro" componente. Mi problema está más relacionado con las funciones/configuraciones que tengo que hacer.

Está leyendo un carácter a la vez fuera del U1RXREG, creo que puede salirse con la suya guardando todo el U1RXREG en su variable de una sola vez.
No estoy seguro de ello. Ya eché un vistazo a U1RXREG y solo tiene 0x00000041 como valor y no es un resultado aceptable.
0x41 es 'A'. ¿Qué esperabas ver como tu segundo personaje?
'AOK\r\n' es lo que estoy tratando de obtener.
Definitivamente debería hacer que la estructura UartBuffer1 sea 'volátil'.
@justing es un buen consejo y tiene razón sobre el uso de estructuras volátiles dentro de una interrupción, pero en este caso no resuelve el problema. Tenga en cuenta que solo tengo 0x00000041 como valor de U1RXREG.
¿Funciona a velocidades de transmisión más bajas? ¿Se comporta mejor si borra el indicador de interrupción inmediatamente después de verificarlo?
Nop. Ninguna de tus sugerencias surtió efecto.

Respuestas (1)

A una velocidad de transmisión de 115200, sin paridad y con 1 bit de inicio y parada (un total de 10 bits), los datos tardan 86 microsegundos en llegar a su búfer RX desde el otro extremo.

Mirando su ISR, veo que está procesando muchas instrucciones en su ISR.

El escenario que podría estar sucediendo es este: llega tu primer personaje (es decir, "A"),

Se activa la interrupción,

no ha ocurrido ningún error (desbordamiento, paridad, etc.),

ahora tambien llegan el resto de personajes("OK\r\n"),

se establece el indicador de desbordamiento,

borra la interrupción de RX (pero no ingresa ISR nuevamente porque no se recibirán más caracteres).

Soluciones de depuración: 1. Utilice el método de control de flujo (ACK para cada byte)

  1. Verifique el tamaño del Rx FIFO de su uC (si son 16 bytes, entonces no necesita control de flujo. De lo contrario, si no tiene FIFO, necesita control de flujo)

3. Evite las llamadas a funciones en el ISR.

4. Puede habilitar la interrupción para errores de desbordamiento de modo que no tenga que escribir explícitamente una rutina para la interrupción. Si se produce un error de desbordamiento, simplemente verifique el estado del indicador de interrupción y borre el indicador y deseche los datos Rx. Esto lo ayudará a reducir el procesamiento de tales errores en el ISR, ya que se pueden verificar en el bucle principal. Simplemente copie los datos recibidos en un búfer e incremente un conteo. Esto podría ayudar.

  1. Reduzca su tasa de baudios y verifique usando su mismo código
La velocidad del reloj de la CPU es de 40 MHz y la velocidad de transmisión es de 115200 bps. Cada carácter tiene 8 bits de datos, un bit de parada y un bit de inicio, para un total de 10 bits por carácter. Por lo tanto, tiene 40MHz / 115200bps * 10 bits por carácter = 3472 ciclos de CPU para procesar cada carácter antes de que llegue el siguiente. Por lo tanto, es poco probable que su ISR sea demasiado lento para procesar los caracteres a medida que ingresan, por lo que, a menos que esté deshabilitando las interrupciones en otro lugar, es probable que no se desborde.
Ya probé con la tasa de baudios a 19200 y no hubo mejores resultados.