Search code examples
cembeddedinterruptpicpic32

How to Enable UART Interrupt For PIC32MX?


I was trying to create a UART driver for the PIC32MX320F128H, when I noticed that any time the UART IRQ occured, it would crash the system.

The Debugger told me that the program was halting at these lines(119 - 21)? of ctr0.s every time.

\_reset:
jal \_startup

        nop

Here is the BOARD setup

// BOARD Setup
\#pragma config POSCMOD      = XT        // Set oscillator to XT, crystal mode.
\#pragma config FPBDIV       = DIV_2     // Set peripheral clock to run at half sys clock
\#pragma config FWDTEN       = OFF       // Disable watchdog timer
\#pragma config CP           = OFF       // Disable Code protect, ensures all memory is available
\#pragma config FPLLMUL         = MUL_20    // PLL Multiplier
\#pragma config FPLLODIV     = DIV_1     // System PLL Output Clock Divide

\#define SYSTEM_CLOCK 80000000L
\#define  PB_CLOCK SYSTEM_CLOCK/2

\#define TRUE 1
\#define FALSE 0

// UART DEFINES
\#define UART_IBITS 0x1C000000   // ERR, RX, TX interrupt bits
\#define ERR_IBITS 0x4000000
\#define RX_IBITS   0x8000000
\#define TX_IBITS 0x10000000

\#define STA_OERR_BIT 2          // U1STA bit assosciated with OERR

\#define TEST_CHAR 0x41          // char to send and test stuff, A in ascii

\#define U1_PRIORITY 0b110       // don't know what this should be

\#define U1_SUBPRIORITY 0b01     // don't know what this should be either

Here is the UART setup

void Uart_Init(unsigned long baudRate) {
AD1PCFG = 0xffff;               // Set PORTs to digital mode

// Disable interrupts to avoid crazy malloc errors while initing CBuffs
__builtin_disable_interrupts();

// Initialize CBuffers
RXBuff = InitCBuff();
TXBuff = InitCBuff();

// Enable interrupts after cbuffs are initialized
__builtin_enable_interrupts();

// Disable stdout to UART2
setbuf(stdout, NULL);

// Clear control registers
U1MODECLR = 1;
U1STACLR = 1;

// Clear RX, TX registers
U1RXREG = 0;
U1TXREG = 0;

// Set Baudrate, U1BRG should be 20 or 21. In this case it is 21.
U1MODEbits.BRGH = 0;    // enable high baudrate mode
U1BRG = (PB_CLOCK / (16 * baudRate)) - 1;

// Turn on the UART
// Note, that by default the UART is configured for 8 data bits, 1 stop bit, and no parity.
U1MODEbits.ON = 1;    // 8 bit 1 stop bit

// Enable RX and TX pins
U1STAbits.URXEN = 1;
U1STAbits.UTXEN = 1;

// Enable interrupts RX and ERR interrupts. TX will enable when TXBuff has chars
IEC0bits.U1RXIE = 1;
IEC0bits.U1EIE = 1;
IEC0bits.U1TXIE = 0;

// Set Interrupt priority and subpriority
IPC6bits.U1IP = U1_PRIORITY;
IPC6bits.U1IS = U1_SUBPRIORITY;

// Set interrupt modes
U1STAbits.URXISEL = 0b00;   // flag when char received
U1STAbits.UTXISEL = 0b10;   // flag when buffer is empty


}

Finally, here is the ISR definition

void __ISR(_UART1_VECTOR, IPL4AUTO) IntUart1Handler(void) {

I also tried to use a deprecated method to enable the interrupts, but I couldn't even figure out how to get the plib library working so that didn't get far.

// Depreceated method.
void __attribute__ ((nomips16)) INTEnableSystemMultiVectoredInt(void)
{
    unsigned int val;

    // set the CP0 cause IV bit high
    asm volatile("mfc0   %0,$13" : "=r"(val));
    val |= 0x00800000;
    asm volatile("mtc0   %0,$13" : "+r"(val));
    
    INTCONSET = _INTCON_MVEC_MASK;

    // set the CP0 status IE bit high to turn on interrupts
    INTEnableInterrupts();
}

I was really at the end of my wits until Rich TBCO from the MPLAB forums pointed me in the right direction.


Solution

  • I wasn't enabling interrupts correctly. Here is how I enabled the UART interrupts following this guide.

    There's really just seven steps for this. Steps 3 through 7 should be done in order in your program.

    1. include xc.h and sys/attribs.h
    2. Create the ISR. Make sure the priority parameter here matches the priority specified in the interrupt configuration.
    3. Turn off the peripheral.
    4. Configure the Interrupt, in this order: Set the priority, set the sub priority, clear the flags, and then enable the interrupts
    5. Enable multi-vector interrupts.
    6. Enable global interrupts
    7. Enable the peripheral

    Here is how I configured the UART following these steps.

        #include <xc.h>
        #include <sys/attribs.h>
        void Uart_Init(unsigned long baudRate) {
        // Set Ports to digital mode, configure later to only do UART pins.
        AD1PCFG = 0xffff;
    
        // Initialize CBuffers
        RXBuff = InitCBuff();
        TXBuff = InitCBuff();
    
        // Clear control registers
        U1MODECLR = 0xFF;
        U1STACLR = 0xFF;
    
        // Clear RX, TX registers
        U1RXREG = 0;
        U1TXREG = 0;
    
        // Set Baudrate, U1BRG should be 20 or 21. In this case it is 21.
        U1MODEbits.BRGH = 0; // enable high baudrate mode
        U1BRG = (PB_CLOCK / (16 * baudRate)) ;
    
        // Turn off the UART
        U1MODEbits.ON = 0;
    
        // Enable RX and TX pins
        U1STAbits.URXEN = 1;
        U1STAbits.UTXEN = 1;
    
        // Set interrupt modes
        U1STAbits.URXISEL = 0b00; // flag when char received
        U1STAbits.UTXISEL = 0b10; // flag when buffer is empty
    
        // Set Interrupt priority and sub-priority
        IPC6bits.U1IP = 4;
        IPC6bits.U1IS = 1;
    
        // Clear flags
        IFS0bits.U1EIF = 0;
        IFS0bits.U1RXIF = 0;
        IFS0bits.U1TXIF = 0;
    
        // Enable interrupts RX and ERR interrupts. TX not necessary for this simple program
        IEC0bits.U1RXIE = 1;
        IEC0bits.U1EIE = 1;
        IEC0bits.U1TXIE = 0;
        
        // Enable multi-vector interrupts
        INTCONSET = _INTCON_MVEC_MASK;
        
        // Enable Global Interrupts
        __builtin_enable_interrupts();
        
        // Enable UART
        // Note, that by default the UART is configured for 8 data bits, 1 stop bit, and no parity.
        U1MODEbits.ON = 1;
    }