Search code examples
cpicuart

PIC16F1937 and UART : keep led ON while receiving


I'm trying to keep a led ON while PIC is receiving a character (while a button is pressed), the led being OFF by default, but I can't seem to find a solution.

I'm 3 months in learning using microcontrollers (specifically PIC) coding with MPLABX IDE and started understanding UART communication. At first I tried turning a led ON/OFF when a charater is received, it went pretty good and worked, but right now I'm trying to keep a led active while a button is pressed but can't really get over this problem. I have my code in a interrupt function like this :

//#define LED RB5

void __interrupt () ISR (void)
{   
    if(RCIF) // data is received
    {
        data = RCREG; // get the value
        if (data == '1') // if value received is '1'
            LED = 1; //turn ON led
        RCIF = 0;
    }
    LED = 0; // turn OFF led
}

Using the above code makes so the led turns on/off really fast while I keep pressing a button and isn't really what I wanted.
I hope someone can help me understand what I need to do. Thanks !


Solution

  • I suggest you create a state machine for this in app.c.

    Define some states with an enum and a struct to hold the state and place them in app.h, include app.h in files where you wish to use this module (e.g. your system_interrupt.c file).

    For example you could do this:

    typedef enum{
        IDLE,
        START_RECEIVING,
        STILL_RECEIVING,
    }uart_state_t;
    
    typedef struct{
        uart_state_t current_state;
    }uart_module_t
    
    volatile uart_module_t uart_module = {0}; // initial state = IDLE, needs to be volatile because it will be updated via interrupt
    

    Then create a function to service your state machine. This will handle the state that it is currently in and transition to other states as desired. For instance, the state machine starts in the IDLE state but once your interrupt has fired and RCIF bit is set then you'll transition in to the START_RECEIVING state which will turn the LED on then transition to the STILL_RECEIVING state where it will poll the RCIF bit until cleared. That would look something like this:

    void uartFSM(void){
    
        switch(uart_module.current_state){
    
            case IDLE:
            {
    
                break;
    
            }
    
            case START_RECEIVING:
            {
    
                LED = 1; // Turn LED on
                uart_module.current_state = STILL_RECEIVING; // state update
    
                break;
    
            }
            case STILL_RECEIVING:
            {
    
                if(!RCIF){
    
                    // done receiving
                    LED = 0; // Turn LED off
                    uart_module.current_state = IDLE; // state update
    
                }
    
                break;
    
            }
    
            default:
            {
                // whoops
                break;
            }
    
        }
    
    }
    

    Now your interrupt will look like this:

    void __interrupt () ISR (void)
    {   
        if(RCIF) // data is received
        {
    
            data = RCREG; // get the value
    
            // if value received is '1'
            if (data == '1') uart_module.current_state = START_RECEIVING; // state update
    
        }
    
    }
    

    Now you just have to make sure that you call the uartFSM() some where in APP_Tasks so that the state machine gets serviced.