8.2 Bare Metal Code
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 /*disable Watchdog*/
#pragma config LVP = ON /* Low voltage programming enabled, RE3 pin is MCLR */
Then, the following variables need to be defined:
#define Timer2Period 0x2F /* TMR2 Period is 100ms */ #define DesiredThreshold 300 /* Desired threshold value */ #define AnalogChannel 0x00 /* Use ANA0 as input for ADCC */ volatile uint16_t adcVal; /* ADCC global result value */
The CLK_Initialize
function initializes the HFINTOSC
internal oscillator:
static void CLK_Initialize(void)
{
/* set HFINTOSC Oscillator */
OSCCON1 = 0x60;
/* set HFFRQ to 1 MHz */
OSCFRQ = 0x00;
}
The PORT_Initialize
function has the role to configure the
pin used in this application, which is the RE0 output for LED0:
static void PORT_Initialize(void) { /* Set RE0 digital input buffer disabled */ ANSELE = 0x06; /* Set RE0 pin as output */ TRISE = 0x06; }
The next function initializes the ADCC and configures the TMR2 to be an auto-conversion trigger and enables the ADCC Interrupt flag:
static void ADCC_Initialize(void)
{
/* ADACT Auto-Conversion Trigger Source is TMR2 */
ADACT = 0x04;
/* ADGO stop; ADFM right; ADON enabled; ADCONT disabled; ADCS FRC */
ADCON0 = 0x94;
/* Clear the ADCC interrupt flag */
PIR1bits.ADIF = 0;
/* Enabling ADCC interrupt flag */
PIE1bits.ADIE = 1;
}
The Timer2 initialization function sets the clock source and the registers needed to generate an 100 ms period:
static void TMR2_Initialize(void)
{
/* TMR2 Clock source, LFINTOSC (00100) has 31 kHz */
T2CLKCON = 0x04;
/* T2PSYNC Not Synchronized, T2MODE Software control, T2CKPOL Rising Edge */
T2HLT = 0x00;
/* TMR2ON on; T2CKPS Prescaler 1:64; T2OUTPS Postscaler 1:1
Minimum timer period is 31 kHz/64 = 2.064516 ms */
T2CON = 0xE0;
/* Set TMR2 period, PR2 to 100ms */
T2PR = Timer2Period;
/* Clear the TMR2 interrupt flag */
PIR4bits.TMR2IF = 0;
}
The following initialization function will safely enable the Global and Peripherals interrupts, after they were initialized with proper settings:
static void INTERRUPT_Initialize(void)
{
INTCONbits.GIE = 1; /* Enable Global Interrupts */
INTCONbits.PEIE = 1; /* Enable Peripheral Interrupts */
}
The ADCC interrupt is triggered by TMR2 to complete a conversion at a frequency
determined by the Timer2 Period. The following function handles the interrupts, in this
case ADCC_Interrupt
, checks the status of the ADCC Interrupt flag, and
then calls the ADCC_Interrupt
function:
static void __interrupt() INTERRUPT_InterruptManager(void)
{
if (INTCONbits.PEIE == 1)
{
if (PIE1bits.ADIE == 1 && PIR1bits.ADIF == 1)
{
ADCC_Interrupt();
}
}
}
The ADCC_Interrupt
function is separated from the interrupt
manager to be similar to the code generated by MCC. In this function, the Interrupt flag
is cleared first, the LED0 is toggled (this will happen with Timer2 Period frequency),
and finally the ADCC value for the Analog Channel is read (ANA0 is used in this
example).
static void ADCC_Interrupt(void)
{
/* Clear the ADCC interrupt flag */
PIR1bits.ADIF = 0;
/* Toggle LED0 at the Timer2Period frequency */
LATEbits.LATE0 = ~LATEbits.LATE0;
/* Get the conversion result from ADCC AnalogChannel */
adcVal = ADCC_ReadValue(AnalogChannel);
}
The ADCC read function only needs a parameter, the channel needed to be read:
static uint16_t ADCC_ReadValue(uint8_t channel)
{
ADPCH = channel; /*Set the input channel for ADCC*/
/* TMR2 is trigger source for auto-conversion for ADCC */
return ((uint16_t)((ADRESH << 8) + ADRESL));
}
The next code in the void main
function is an infinite loop
(using a while(1)
), which is used to check for the ADCC value using an
“if
” statement:
void main(void)
{
/* Initialize the device */
CLK_Initialize(); /* Oscillator Initialize function */
PORT_Initialize(); /* Port Initialize function */
ADCC_Initialize(); /* ADCC Initialize function */
TMR2_Initialize(); /* TMR2 Initialize function */
INTERRUPT_Initialize(); /* Interrupt Initialize function */
while (1)
{
if (adcVal > DesiredThreshold)
{
/* turn LED0 ON by writing pin RE0 to low */
LATEbits.LATE0 = 0;
}
}
}
This will check if the read value from Potentiometer (POT click) is above a defined value. If so, the LED0 will turn ON without blinking.