3.2 Bare Metal Code

The necessary code and functions to implement the presented example are analyzed in this section.

The first step is to configure the microcontroller to disable the Watchdog Timer and enable the Master Clear external pin.

#pragma config WDTE = OFF       /* disable Watchdog */
#pragma config LVP = ON         /* Low voltage programming enabled */

The main clock of the microcontroller must be configured. The following function initializes the system clock to have as input clock the HFINTOSC oscillator and to run at 1 MHz:

static void CLK_Initialize(void)
{
    /* set HFINTOSC Oscillator */
    OSCCON1bits.NOSC = 6;
    /* set HFFRQ to 1 MHz */
    OSCFRQbits.HFFRQ = 0;
}

For this example, the RA0 pin from PORTA is configured as an input pin and connected to a button in order to generate the desired interrupt. The PORT initialization function is listed below:

static void PORT_Initialize(void)
{
    TRISEbits.TRISE0 = 0;   /* Set RE0 pin as output (LED) */
    TRISAbits.TRISA0 = 1;   /* Set RA0 pin as input */
    
    ANSELAbits.ANSELA0 = 0; /* Enable Digital Input buffers for RA0 */
    
    WPUAbits.WPUA0 = 1;     /* Enable weak pull-up for RA0 */
}

The next step is to initialize the IOC. For this example, the IOC is configured to be triggered on the falling edge of the input signal. The code is listed below:

static void IOC_Initialize(void)
{
    IOCAFbits.IOCAF0 = 0;   /* Clear interrupt flag */
    
    IOCANbits.IOCAN0 = 1;   /* Enable IOC on negative change */
    
    PIE0bits.IOCIE = 1;     /* Enable IOC interrupt */
}

In order to work with interrupts, the user must enable the global interrupts by setting the corresponding bit in the INTCON register. The function is listed below:

static void INTERRUPT_Initialize(void)
{
    INTCONbits.GIE = 1;     /* Enable global interrupts */
}

The IOC Interrupt Service Routine (ISR) function that will be called when receiving an IOC interrupt is listed below:

static void IOC0_ISR (void)
{
    /* Clear the interrupt flag */
    IOCAFbits.IOCAF0 = 0;
    /* Toggle the LED */
    LATEbits.LATE0 = ~LATEbits.LATE0;
}

In this function, the IOC interrupt flag must be cleared and, in this instance, the output value is toggled to turn an LED on or off every time an interrupt is triggered.

An interrupt handler is needed to manage all the desired interrupts. For this example, the interrupt handler will call the IOC0_ISR interrupt routine function as listed below:

void __interrupt() INTERRUPT_InterruptManager (void)
{
    if(IOCAFbits.IOCAF0)    /* Check the interrupt flag */
    {
        IOC0_ISR();
    }
}