3.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, Timer6, CCP1, 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 and RB3), the following function is used:

static void PORT_init(void)
{
    /*PORT RA2 and RA3 output driver enabled*/
    TRISAbits.TRISA2 = 0;
    TRISAbits.TRISA3 = 0;
    /*PORT RB3 and RB0 output driver enabled*/ 
    TRISBbits.TRISB0 = 0;
    TRISBbits.TRISB3 = 0;      
}

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

static void TMR2_init(void)
{   
    T2CLKCONbits.CS = 1;  /* Timer2 clock source is FOSC/4 */  
    T2HLTbits.MODE = 4;   /* Timer2 resets at rising TMR2_ers*/  
    T2RSTbits.RSEL = 2;   /* Timer2 Reset source is TMR4_postscaled; */
    T2PR = 0x0F;          /* Load period values */  
    T2CON = 0x80;         /* Enable Timer2 */
}

Timer4 is configured to use FOSC/4 as clock source and generate a pulse every 2 us (frequency = 200 kHz). The following function is used:

static void TMR4_init(void)
{
    T4CLKCONbits.CS = 1;   /* Timer4 clock source is FOSC/4 */    
    T4PR = 0x1F;           /* Load period values */   
    T4CON = 0x80;          /* Enable Timer4 */
}

Timer6 is configured to use FOSC/4 as clock source and generate a pulse every 16 us (frequency = 62.5 kHz). The following function is used:

static void TMR6_init(void)
{    
    T6CLKCONbits.CS = 1;   /* Timer6 clock source is FOSC/4 */    
    T6PR = 0xFF;           /* Load period values */    
    T6CON = 0x80;          /* Enable Timer6 */
}

CCP1 works as a PWM with 50% duty cycle and Timer6 as pulse source. The following function is used:

static void CCP1_init(void)
 {   
    CCP1CON = 0x8C;   /* Enable CCP1 in PWM mode*/
    /* Load duty cycle values */
    CCPR1H = 0x01;    
    CCPR1L = 0xFF;       
    CCPTMRS = 0x03;   /* Select Timer6 as pulse source*/
 }

CLC1 is configured in the J-K flip-flop with R mode and uses TMR2 and CCP1 as inputs. The following function is used:

static void CLC1_init(void)
{ 
    CLC1POL = 0x08;    /* Negated output for fourth OR gate*/    
    CLC1SEL0 = 0x13;   /* Configure TMR2_OUT as input for first OR gate */    
    CLC1SEL1 = 0x18;   /* Configure CCP1_OUT as input for second OR gate */   
    CLC1SEL2 = 0x13;   /* Configure TMR2_OUT as input for third OR gate */    
    CLC1SEL3 = 0x13;   /* Configure TMR2_OUT as input for forth OR gate */
    /* All four inputs are not inverted*/
    CLC1GLS0 = 0x02; 
    CLC1GLS1 = 0x08; 
    CLC1GLS2 = 0x00; 
    CLC1GLS3 = 0x00;    
    CLC1CONbits.EN = 1;     /* CLC1 enabled */ 
    CLC1CONbits.MODE = 6;   /* Mode J-K flip-flop with R */
}

CLC2 is configured in the J-K flip-flop with R mode and uses TMR4 and CCP1 as inputs. The following function is used:

static void CLC2_init(void)
{  
    CLC2POL = 0x08;    /* Negated output for fourth OR gate*/   
    CLC2SEL0 = 0x15;   /* Configure TMR4_OUT as input for first OR gate */    
    CLC2SEL1 = 0x18;   /* Configure CCP1_OUT as input for second OR gate */   
    CLC2SEL2 = 0x15;   /* Configure TMR4_OUT as input for third OR gate */  
    CLC2SEL3 = 0x15;   /* Configure TMR4_OUT as input for forth OR gate */
    /* Inputs 1, 3 and 4 are not inverted; Input 2 inverted*/
    CLC2GLS0 = 0x02;
    CLC2GLS1 = 0x04;
    CLC2GLS2 = 0x00; 
    CLC2GLS3 = 0x00;    
    CLC2CONbits.EN = 1;     /* CLC2 enabled */   
    CLC2CONbits.MODE = 6;   /* Mode J-K flip-flop with R */
}

CLC3 is configured in the AND-OR mode and uses CLC1 and CLC2 as inputs. The following function is used:

static void CLC3_init(void)
{  
    CLC3POL = 0x00;    /* Clear the output polarity register */
    CLC3SEL0 = 0x21;   /* Configure CLC1_OUT as input for first OR gate */
    CLC3SEL1 = 0x21;   /* Configure CLC1_OUT as input for second OR gate */
    CLC3SEL2 = 0x22;   /* Configure CLC2_OUT as input for third OR gate */
    CLC3SEL3 = 0x22;   /* Configure CLC2_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 = 0;   /* Mode AND-OR */
}

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

Table 3-2. Peripheral Mapping to I/O Pins for the Example “Using CLCs to Create a Data Signal Modulator”
Internal CIP SignalMicrocontroller Pin
CLC1_OUTRA2
CLC2_OUTRA3
CLC3_OUTRB0
CCP1_OUTRB3

This is done in the following function:

static void PPS_init(void)
{   
    RA2PPS = 0x18;   /*Configure RA2 for CLC1 output*/  
    RA3PPS = 0x19;   /*Configure RA3 for CLC2 output*/  
    RB0PPS = 0x1A;   /*Configure RB0 for CLC3 output*/  
    RB3PPS = 0x05;   /*Configure RB3 for CCP1 output*/
}