7.2 Bare Metal Code

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

The first step will be to configure the microcontroller to disable the Watchdog Timer and to enable Low-Voltage Programming (LVP):

#pragma config WDTE = OFF /* WDT operating mode->WDT Disabled */
#pragma config LVP = ON /* Low voltage programming enabled, RE3 pin is MCLR */ 

As described in the example functionality, the following peripherals must be initialized: TMR1, the system clock, the GPIO pin and the interrupts.

The system clock was configured to use the HFINTOSC oscillator with an internal frequency of 32 MHz and the clock divided by 32, so the actual system frequency is 1 MHz. The following function is used:

/* Clock initialization function */
static void CLK_Initialize(void)
{
    /* set HFINTOSC as new oscillator source */
    OSCCON1bits.NOSC = 0x6;

    /* set Clock Div by 32 */
    OSCCON1bits.NDIV = 0x5;      
    
    /* set HFFRQ to 32MHz */
    OSCFRQbits.HFFRQ = 0x6;          
}

The GPIO peripheral was configured to use PINB5 as input for TMR1 button. The following function is used:

/* Port initialization function */
static void PORT_Initialize(void)
{
    /* configure RB5 as input */
    TRISBbits.TRISB5 = 1;
    
    /* configure RB5 as digital */
    ANSELBbits.ANSELB5 = 0;
}

The TMR1 peripheral is configured in Gate Single-Pulse mode, has a clock source of 1 MHz, the counter is active on a falling edge and the peripheral gate and overflow interrupts are Active. The following function is used:

/* TMR1 initialization function */
static void TMR1_Initialize(void)
{
    /* Timer controlled by gate function */
    T1GCONbits.GE = 1;

    /* Timer acquistion is ready */
    T1GCONbits.GGO_nDONE = 1;
    
    /* Timer gate single pulse mode enabled */
    T1GCONbits.T1GSPM = 1;  

    /* Source Clock FOSC/4 */
    T1CLKbits.CS = 0x1;      
    
    /* Clearing IF flag before enabling the interrupt */
    PIR4bits.TMR1IF = 0;
    
    /* Enabling TMR1 interrupt */
    PIE4bits.TMR1IE = 1;                                            
    
    /* Clearing gate IF flag before enabling the interrupt */
    PIR5bits.TMR1GIF = 0;
    
    /* Enabling TMR1 gate interrupt */
    PIE5bits.TMR1GIE = 1;
    
    /* CLK Prescaler 1:8 */
    T1CONbits.CKPS = 0x3;

    /* TMR1 enabled */
    T1CONbits.ON = 1;     
}

The microcontroller’s interrupts were enabled and are used to determine the button press time. The following function is used:

/* Interrupt initialization function */
static void INTERRUPT_Initialize(void)
{    
    /* Enable the Global Interrupts */
    INTCONbits.GIE = 1;

    /* Enable the Peripheral Interrupts */
    INTCONbits.PEIE = 1;  
}
When the timer finishes counting the button pressed time, an interrupt will occur in the interrupt manager. It will check for the source of the interrupt and it will call a handler function. The following function is used:
/* Interrupt handler function */
static void __interrupt() INTERRUPT_interruptManager(void)
{
    // interrupt handler
    if(INTCONbits.PEIE == 1)
    {
        if(PIE4bits.TMR1IE == 1 && PIR4bits.TMR1IF == 1)
        {
            TMR1_ISR();
        } 
        else if(PIE5bits.TMR1GIE == 1 && PIR5bits.TMR1GIF == 1)
        {
            TMR1_GATE_ISR();
        } 
        else
        {
            //Unhandled Interrupt
        }
    }      
    else
    {
        //Unhandled Interrupt
    }
}
The overflow interrupt will occur when the button is pressed for so long that the timer maximum value is exceeded. The handler needs to stop the gate control because the button was not released yet and it will generate an undesired interrupt when that will happen. It also needs to clear the Interrupt flag, reset the counted value and re-enable the timer gate control for a new acquisition. The following function is used:
/* TMR1 ISR function */
static void TMR1_ISR(void)
{  
    /* Stop Gate control */
    T1GCONbits.GGO_nDONE = 0;                                     
    
    /* Clearing overflow IF flag */
    PIR4bits.TMR1IF = 0;
    
    /* Clearing gate IF flag */
    PIR5bits.TMR1GIF = 0;                                      
    
    /* Reset the counted value */
    TMR1_writeTimer(0);                                                 
    
    /* Prepare for next read */
    T1GCONbits.GGO_nDONE = 1;                                       
}
The gate interrupt will occur when the button is released before the timer reaches its maximum value. The handler needs to clear the Interrupt flag, reset the counted value and re-enable the timer gate control for a new acquisition. The following function is used:
/* TMR1 GATE ISR function */
static void TMR1_GATE_ISR(void)
{    
    /* Clearing gate IF flag after button release */
    PIR5bits.TMR1GIF = 0;                                   

    /* Reset the counted value */
    TMR1_writeTimer(0);                                                 
    
    /* Prepare for next read */
    T1GCONbits.GGO_nDONE = 1;                                       
}
When any interrupt occurs, the timer counted value is reset to ‘0’. The following function is used:
static void TMR1_writeTimer(uint16_t timerValue)
{
    /* Write TMR1H value */
    TMR1H = timerValue >> 8;
    
    /* Write TMR1L value */
    TMR1L = timerValue; 
}