Select Page

We are going to take what we have done with the ADC and expand it some more and add in a button and the interrupt base timer code from a previous post.

The setup…

RA0 – LED
RA1 – Potentiometer
RA2 – Push Button (tied to ground) (weakpull-up is enabled)

High level…

Press the button
LED will light
The time the LED is on – is determined by the position of the potentiometer

Digging in…

RA0 is setup as an output
RA1 is setup as the analog input
RA2 is set as input with weakpull ups enabled (button has to be tied to ground)

When the button is pressed it completes the circuit to ground. This causes RA2 to go low.

The main code loop is checking RA2 for this condition – low (0). When this happens the code moves to a 100ms delay. This is for a switch debounce. After 100ms if the switch is still pressed (low) we move on to the rest of the code.. Otherwise it exits and goes back to checking the button again.

When it goes on – it sets the output of RA0 to high – turning on the LED. The code then reads the ADC for the value of the potentiometer.

The return value is then multiplied by a delay multiplier number. This number is set by the #define delay_multiplier in the code. The default value is 8. What this does it take the ADC results and multiplies them by that value.

Why are we doing this – because the ADC only returns a 8bit number – max would be 255. The delay / timer function take the input value to be milliseconds. So, in order to generate a 1 second delay, the input value would need to be 1000. 255ms is too low of a number, so we multiple this. With 8 the max is 2040ms or 2 seconds. If you replace 8 with 16, the max would be 4 seconds.. And so on.

However, when you do this you loose low end numbers, for example with a multiplier of 8, the minimum time would be 8, if you put in 16, 16. and so forth.

Of if you want to push it – put in 255 as the multiplier. Max time would be around 64 seconds

A thing to note is that the multiplication code – uint16_t computed_delay = ADC_Value * delay_multiplier; computed_delay is a 16 bit number, max value would be 65536. You could change uint16_t to uint32_t – for a max of 4,294,967,295

When you make changes like that – it is also a good idea to check the other functions to make sure they don’t require any changes. In this case the delay function takes a uint32_t by default.

Moving on.. The computed value is passed on to the delay function. We hang here until the time runs out. Then we..

Change the output of RA0 back to low – to turn off the LED. Then we go back to checking the button again.

If you wanted you could replace the LED with a relay driven by a transistor.

The Circuit

10f322 adc a1 button

The Code

/*
 * File:   adc_timer.c
 * Author: Jamie
 *
 * Created on 9/14/2021, 8:04 PM
 */

#include <xc.h>
#include <stdint.h>

//Device Configuration
#pragma config FOSC = INTOSC  // Oscillator Selection 
#pragma config BOREN = ON    // Brown-out Reset
#pragma config WDTE = OFF    // Watchdog Timer
#pragma config PWRTE = ON    // Power-up Timer
#pragma config MCLRE = OFF   // MCLR Pin Function Select bit->MCLR pin function is digital input, MCLR internally tied to VDD
#pragma config CP = OFF      // Code Protection 
#pragma config LVP = OFF     // Low-Voltage Programming 
#pragma config LPBOR = ON    // Brown-out Reset Selection bits
#pragma config BORV = LO    // Brown-out Reset Voltage Selection
#pragma config WRT = OFF    // Flash Memory Self-Write Protection

//Used to calculate the delay time - Change depending on processor Speed
#define _XTAL_FREQ 8000000  //8 MHz (default after Reset)

#define delay_multiplier 8  //We mutiple the ADC result by this number
//Since the ADC is only 8bits, 255 is the largest number, the timer funcation is running in MS, then the largest delay will be 255MS.  
//We want a longer delay - so we take max of 255 and say times 8 - 2040 - 2040Ms is just over 2 seconds the max. 
//Changing this number to 16 would allow for a max delay of 4 seconds.. and so on. 

//Function prototypes
void setup (void);
unsigned long millis(void);
void delay(uint32_t ms);
uint8_t ADC_Read(void);

//Global Variables
volatile unsigned long timer0_millis = 0;


//interrupt handler funcation
void __interrupt () isr_routine (void) {

//TMR0 = 8;
timer0_millis +=1;  //increase the counter by 1  
INTCONbits.TMR0IF = 0; //clear tmr0 irq flag
}

void main(void)
{
    setup();
    
    while(1)
    {
        uint8_t ADC_Value = 0; //Value for ADC
        //Check for switch press
        if (PORTAbits.RA2 == 0){
            __delay_ms(100); //delay for switch debounce
            
            if (PORTAbits.RA2 == 0){            
            LATAbits.LATA0 = 1; //turn on port RA0
            ADC_Value = ADC_Read();  //Read the ADC Value
            uint16_t computed_delay = ADC_Value * delay_multiplier;
            delay(computed_delay);
            LATAbits.LATA0 = 0; //turn off port RA0
            
            } //End of debounce code             
        } //End of check switch for press            
        } //End of forever while loop       
}

void setup(){
    
    //Pin LED is connected to
    TRISAbits.TRISA0 = 0;  //Make pin Output RA0
    ANSELAbits.ANSA0 =0;   //Disable Analog RA0
    LATAbits.LATA0 = 0;     //Make the output Low RA0
    
   //######################################################################
    //Setup for pin that the POT is connected to
    TRISAbits.TRISA1 = 1;  //Make pin Input RA1
    ANSELAbits.ANSA1 =1;   //Enable Analog RA1
    
    //Set Analog conversion clock FOSC/32, since we are running 8Mhz, we need to have a conversion time at or greater 1uS
    //FOSC/32 will give us 4uS
    ADCONbits.ADCS = 0b010; 
    
    //Select the Analog channel - RA1 or AN1
    ADCONbits.CHS = 0b001;
    
    //Turn on the ADC module
    ADCONbits.ADON = 1;  
    
    //######################################################################
    //Pin push button is connected to
    TRISAbits.TRISA2 = 1;  //Make pin Input RA2
    ANSELAbits.ANSA2 = 0;   //Disable Analog RA2
    //Enable Weakpull ups on RA2
    WPUAbits.WPUA2 = 1; //Enable Weakpull up on RA2   
    OPTION_REGbits.nWPUEN = 0; //Requires being enabled in option reg as well
    
    
    //######################################################################
    //Setup the TMR0 for timer functions
    /*Set TMR0 to use FOSC/4 At 8Mhz the timer with increment at 2Mhz    
    * That is 500us       
    * TMR0 will overflow ever 128us. Which is faster than we want - I am aiming for 1ms.
    * So lets use a prescaler to get it close
    * Assign prescaler to TMR0 With prescaler of 8 we get  500ns * 256 * 8 = 1.024ms
    * Then set the prescaler */
    
    OPTION_REGbits.PSA = 0;
    OPTION_REGbits.PS = 0b010; //prescaler of 8
    
    //To Start the timer we need to set the clock source to fosc/4 but first clear the timer just to make sure
    
    TMR0 = 0;    
    OPTION_REGbits.T0CS = 0;
    
    //Assign interrupt to timer 
    INTCONbits.TMR0IF = 0; //clear tmr0 irq flag
    INTCONbits.TMR0IE = 1; //enable tmr0 irq enable
    INTCONbits.GIE = 1; //enable global interrupts  
    
}

unsigned long millis(void){
    //Reads the MS variable - we do this because of the interrupt
	unsigned long m;
	// disable interrupts while we read timer0_millis or we might get an
	// inconsistent value (e.g. in the middle of a write to timer0_millis)
	INTCONbits.GIE = 0;
	m = timer0_millis;
    INTCONbits.GIE = 1;
	
	return m;
}


void delay(uint32_t ms){
    //Delay based on the number of MS
    //Get the timer value as we go into the delay
	uint32_t start = millis();
    
    //We wait here until while current timer value, minus the start value, is less than the value we want
	while (millis() - start < ms){}
    
   }

uint8_t ADC_Read(void){
    //Starts the ADC read and waits until a conversion is complete before returning
    //Returns the ADC value
    ADCONbits.GO_nDONE = 1;  //Start the conversion
    
    while (ADCONbits.GO_nDONE == 1){
        NOP();
    }
    
    return ADRES;
}

Have a Project or Idea!?

I am Available for Freelance Projects

My skills are always primed and ready for new opportunities to be put to work, and I am ever on the lookout to connect with individuals who share a similar mindset.

If you’re intrigued and wish to collaborate, connect, or simply indulge in a stimulating conversation, don’t hesitate! Drop me an email and let’s begin our journey. I eagerly anticipate our interaction!

jamie@jamiestarling.com


Pin It on Pinterest

Share This