Search code examples
cmicrocontrollerinterruptpic18xc8

PIC18f4550 XC8 compiler: LCD screen stop working when interrupt service routine function is enabled


Background

I am trying to make 16x2 generic LCD display(HD44780) shows some texts on the screen when data is received from HC-05 Bluetooth module via USART interface using PIC184550.

The simplified connection looks something like this: enter image description here

I have tested the HC05 Bluetooth communication via USART (8bits asynchronous with 9600 baud rates) and found that it is able to respond to the data send to it from another mobile phone.

I have also tested the LCD screen by trying to display some strings at the specific locations and found to be working.

Issue

The USART communication is handled by following interrupt function:

__interrupt() void ISR(void)
{
if(RCIF == 1)
{
    data = RCREG;
    TXREG = data;
    
    if(data == 'a')
    {
        LED = 1;
    }
    else if(data == 'b')
    {
        LED = 0;
    }
    RCIF = 0;
}

Basically, when the data "a" is received, it will turn the LED connected to RB0 ON and turn off when "b" is received.

The issue arise when I try to combine these two functionality together (USART with interrupt and LCD).

As soon as this ISR function is included in the code, the LCD screen stop displaying anything and stop responding to any command whatsoever.

If I remove it from the code and upload it back to the PIC18f4550, after recycling the power, the LCD start working and displaying the texts again.

Note that the ISR function itself is working (i.e. LED turn on and off based on the USART data)

This is how I initialize everything:

//oscillator setup
OSCCON = 0x72;

//initial setup for LCD screen
TRISD = 0;
LATD = 0;
delay_ms(50); // wait for LCD to power up
send_command(0x30);
send_command(0x30);
send_command(0x30);

send_command(0x02);
send_command(0x28); // 4 bits, 2 lines and 5x8 font             
send_command(0x01); // display clear
send_command(0x0C); // display on, cursor off, no blink
send_command(0x06); // increase cursor to the right

//interrupts setup
INTCONbits.GIE = 1;
INTCONbits.PEIE = 1;
PIE1bits.RCIE = 1;
PIE1bits.TXIE = 1;

//led test
TRISB = 0;
LATB = 0;

//USART setup
TRISCbits.TRISC6 = 0; //TX pin as output
TRISCbits.TRISC7 = 1; //RX pin as input
SPBRG = 51; // BRG value for 8MHZ & 9600 baud rate
TXSTA = 0x24;
RCSTA = 0x90;

Does any one know why the inclusion of ISR() function will cause LCD screen to stop working?


Solution

  • Perhaps it's a mistake for me to try answering this question, not being overly familiar with the PIC18F4550 (I have used it, but not a lot and not recently). But, here goes anyway.

    You have PIE1bits.TXIE = 1; that would seem to enable the interrupt for the transmitter side of the EUSART. If you're not actually handling this condition, or rather clearing it's associated flag, the ISR would re-enter repeatedly. Some microcontrollers will always execute one instruction out of the interrupt context before dispatching again; I don't remember whether or not the PIC18F4550 is one of these. Depending on whether or not it does, the unhandled interrupt will result in control never being transferred back to the main line of execution or it will but will make it very slow, to the point where it may look locked up.

    Incidentally, according to the datasheet, the receive interrupt flag is cleared by reading RCREG. Similar logic applies to writing TXREG with respect to clearing its own associated flag.

    As is, it seems if you receive an character on the EUSART, RCIF gets sets, your code executes the ISR. It reads the character from RCREG, thereby clearing the receive flag (which you later do again manually). It transmits via TXREG, clearing any existing transmit interrupt flag, but then later causing one. Once the transmit interrupt flag is set, you are now in your loop of death.

    Either you need to clear the TXIF manually, or by sending when it is set, or don't enable the TXIE.