4.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 _XTAL_FREQ macro needs to be defined in order to use the __delay_ms function. Also, the _XTAL_FREQ value must be the same as the system clock.

#define _XTAL_FREQ 1000000UL

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 a digital input and connected to a button in order to generate the desired interrupt. The RE0 pin is configured as output and connected to an LED. 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 users must add an interrupt initialization function that activates the global and the peripheral interrupts. The function is listed below.

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

The IOC ISR function that will be called when receiving an IOC interrupt is listed below: In this function, the only action needed in this example is to clear the interrupt flag.

static void IOC0_ISR (void)
{
    IOCAFbits.IOCAF0 = 0;   /* Clear the interrupt flag */
}

In this function, the only action needed in this example is to clear the interrupt flag.

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();
    }
}

Additionally, for this example, the users must add the LED blinking functionality in the while(1) loop. After turning the LED on and off, the device enters Sleep mode.

    while(1)
    {
        /* Turn the LED on*/
        LATEbits.LATE0 = 0;
        __delay_ms(DELAY_MS);
        /* Turn the LED off*/
        LATEbits.LATE0 = 1;
        
        SLEEP();
    }