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, an initialization of peripherals must be added to the project: 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. The following function is used:

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

    /* set HFFRQ to 32MHz */
    OSCFRQbits.HFFRQ = 0x6;  
}

The GPIO peripheral was configured to use PINB5 as input for the signal that needs to be measured. 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 was configured in Gate Single-Pulse and Toggle combined mode, has a clock source of 1 MHz, the counter is active on a trailing edge and the peripheral’s gate interrupt is Active. The following function is used:

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

    /* Timer gate toggle mode enabled */
    T1GCONbits.GTM = 1;
    
    /* Timer gate active high */
    T1GCONbits.GPOL = 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 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 when the signal measurement is done. 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 measuring the frequency of the external signal, an interrupt will occur in the interrupt manager. It will check for the source of the interrupt and, if it is from TMR1 gate, 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(PIE5bits.TMR1GIE == 1 && PIR5bits.TMR1GIF == 1)
        {
            TMR1_GATE_ISR();
        } 
        else
        {
            //Unhandled Interrupt
        }
    }      
    else
    {
        //Unhandled Interrupt
    }
}

The handler needs to clear the Interrupt flag, read the counted value and reset it afterward, 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)
{
    volatile uint16_t value = 0;

    /* Clearing gate IF flag */
    PIR5bits.TMR1GIF = 0; 

    /* Read TMR1 value */
    value = TMR1_readTimer();

    /* Reset the counted value */
    TMR1_writeTimer(0);

    /* Prepare for next read */
    T1GCONbits.GGO_nDONE = 1;
}

static uint16_t TMR1_readTimer(void)
{
    /* Return TMR1 value */
    return ((uint16_t)TMR1H << 8) | TMR1L; 
}

static void TMR1_writeTimer(uint16_t timerValue)
{
    /* Write TMR1H value */
    TMR1H = timerValue >> 8;
    
    /* Write TMR1L value */
    TMR1L = timerValue;
}
Note: To obtain the frequency of the measured signal from the counted value read, the clock source frequency of the timer needs to be divided by the value.