2.2 Bare Metal Code

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

The first step will be to configure the microcontroller to disable the Watchdog Timer and to enable the 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: Timer2, Timer4, PWM3, PWM4, CLC1, CLC2, CLC3, the I/O PORT and PPS.

The internal oscillator has to be set to the desired value (in this case to 64 MHz), using the following function:

static void CLK_init(void)
{
    OSCCON1bits.NOSC = 6;   /* HFINTOSC oscillator */
    OSCFRQ = 0x08;          /* HFFRQ 64_MHz; */
}

To enable the output driver in the desired I/O pins (RA2, RA3, RB0, RC2 and RC3), the following function is used:

static void PORT_init(void)
{
    /* PORT RA2 and RA3 output driver enabled */
    TRISAbits.TRISA2 = 0;
    TRISAbits.TRISA3 = 0;
    /* PORT RB0 output driver enabled */
    TRISBbits.TRISB0 = 0; 
    /* PORT RC2 and RC3 output driver enabled */
    TRISCbits.TRISC2 = 0;
    TRISCbits.TRISC3 = 0; 
}

For Timer2 to use FOSC/4 as clock source and generate a pulse every 10 us (frequency = 100 kHz), the following function is used:

static void TMR2_init(void)
{
    T2CLKCONbits.CS = 1;  /* Timer2 clock source is FOSC/4 */
    T2PR = 0x9F;          /* Load period values */
    T2CON = 0x80;         /* Enable Timer2 */
}

Timer4 is configured to use FOSC/4 as clock source and generate a pulse every 5 us (frequency = 200 kHz). Timer4 is also set to start at the same time as Timer2. The following function is used:

static void TMR4_init(void)
{
    /* Timer4 clock source is FOSC/4 */
    T4CLKCONbits.CS = 1;
    /* Timer4 resets at rising TMR4_ers*/
    T4HLTbits.MODE = 4; 
    /* Timer4 Reset source is TMR2_postscaled; */
    T4RSTbits.RSEL = 1;
    /* Load period values */
    T4PR = 0x4F;
    /* Enable Timer4 */
    T4CON = 0x80;
}

PWM3 uses Timer2 as pulse source and is configured to generate a pulse with 50% duty cycle. The following function is used:

static void PWM3_init(void)
 {
    PWM3CON = 0x80;   /* Enable PWM3*/
    /* Load duty cycle values */
    PWM3DCH = 0x4F;
    PWM3DCL = 0xC0; 
    CCPTMRS = 0x10;   /* Select Timer2 as pulse source */
 }

PWM4 uses Timer4 as pulse source and it is configured to generate a pulse with 50% duty cycle. The following function is used:

static void PWM4_init(void)
 {   
    PWM4CON = 0x80;           /* Enable PWM4*/
    /* Load duty cycle values */
    PWM4DCH = 0x27; 
    PWM4DCL = 0xC0;
    CCPTMRSbits.P4TSEL = 2;   /*  Select Timer4 as pulse source */
 }

CLC1 is configured in the AND-OR mode and uses PWM3 and PWM4 as inputs with the instructions from the following function:

static void CLC1_init(void)
{
    CLC1POL = 0x00;    /* Clear the output polarity register */
    CLC1SEL0 = 0x1A;   /* Configure PWM3_OUT as input for first OR gate */
    CLC1SEL1 = 0x1A;   /* Configure PWM3_OUT as input for second OR gate */
    CLC1SEL2 = 0x1B;   /* Configure PWM4_OUT as input for third OR gate */
    CLC1SEL3 = 0x1B;   /* Configure PWM4_OUT as input for forth OR gate */
    /* All four inputs are not inverted*/
    CLC1GLS0 = 0x02; 
    CLC1GLS1 = 0x08;
    CLC1GLS2 = 0x20; 
    CLC1GLS3 = 0x80;
    CLC1CON = 0x80;    /* CLC1 enabled; Mode AND-OR*/
}

CLC2 is configured in the 4-input AND mode and uses PWM3 and PWM4 as inputs with the instructions from the following function:

static void CLC2_init(void)
{
    CLC2POL = 0x00;    /* Clear the output polarity register */
    CLC2SEL0 = 0x1A;   /* Configure PWM3_OUT as input for first OR gate */   
    CLC2SEL1 = 0x1A;   /* Configure PWM3_OUT as input for second OR gate */   
    CLC2SEL2 = 0x1B;   /* Configure PWM4_OUT as input for third OR gate */    
    CLC2SEL3 = 0x1B;   /* Configure PWM4_OUT as input for forth OR gate */
    /* All four inputs are not inverted*/
    CLC2GLS0 = 0x02; 
    CLC2GLS1 = 0x08;
    CLC2GLS2 = 0x20;
    CLC2GLS3 = 0x80;   
    CLC2CONbits.EN = 1;     /* CLC2 enabled */
    CLC2CONbits.MODE = 2;   /* Mode 4-input AND */
}

CLC3 is configured in the OR-XOR mode and uses PWM3 and PWM4 as inputs with the instructions from the following function:

static void CLC3_init(void)
{
    CLC3POL = 0x00;    /* Clear the output polarity register */
    CLC3SEL0 = 0x1A;   /* Configure PWM3_OUT as input for first OR gate */   
    CLC3SEL1 = 0x1A;   /* Configure PWM3_OUT as input for second OR gate */ 
    CLC3SEL2 = 0x1B;   /* Configure PWM4_OUT as input for third OR gate */   
    CLC3SEL3 = 0x1B;   /* Configure PWM4_OUT as input for forth OR gate */
    /* All four inputs are not inverted*/
    CLC3GLS0 = 0x02; 
    CLC3GLS1 = 0x08; 
    CLC3GLS2 = 0x20; 
    CLC3GLS3 = 0x80;   
    CLC3CONbits.EN = 1;     /* CLC3 enabled */   
    CLC3CONbits.MODE = 1;   /* Mode OR-XOR */
}

To measure the internal peripheral signals with the oscilloscope, the following link must be made:

Table 2-4. Peripheral Mapping to I/O Pins for the Example “Using Basic Logic Gates”
Internal CIP SignalMicrocontroller Pin
PWM3_OUTRA2
PWM4_OUTRA3
CLC1_OUTRC2
CLC2_OUTRC3
CLC3_OUTRB0

This is done in the following function:

static void PPS_init(void)
{
    RA2PPS = 0x07;   /*Configure RA2 for PWM3 output*/      
    RA3PPS = 0x08;   /*Configure RA3 for PWM4 output*/    
    RB0PPS = 0x1A;   /*Configure RB0 for CLC3 output*/   
    RC2PPS = 0x18;   /*Configure RC2 for CLC1 output*/   
    RC3PPS = 0x19;   /*Configure RC3 for CLC2 output*/
}