Adjustable Push Button Timer – XC8 code for PIC10F322.

In this tutorial, we will expand on the ADC capabilities we’ve explored previously and integrate a push button and an interrupt-based timer to create an adjustable push button timer using the and the compiler.

Setup

  • RA0: LED
  • RA1: Potentiometer
  • RA2: Push Button (connected to ground, with weak pull-up enabled)

High-Level Overview

  1. Press the button
  2. LED lights up
  3. LED remains lit for a duration determined by the potentiometer position

Detailed Explanation

  1. Pin Configuration:
    • RA0: Set as output for the LED.
    • RA1: Set as an analog input for the potentiometer.
    • RA2: Set as input with weak pull-ups enabled for the push button.
  2. Button Press Detection:
    • The push button, when pressed, connects RA2 to ground, causing RA2 to read low (0).
    • The main code loop continuously checks RA2. When it detects a low signal, it initiates a 100ms delay to debounce the switch.
    • If the button remains pressed after the debounce period, the code proceeds; otherwise, it resumes checking the button.
  3. LED Control:
    • When the button press is confirmed, RA0 is set high to turn on the LED.
    • The ADC reads the potentiometer value, which determines the delay duration.
  4. Delay Calculation:
    • The potentiometer value (8-bit ADC result) is multiplied by a predefined delay multiplier (#define DELAY_MULTIPLIER 8). This scaling adjusts the delay to a practical range.
    • For instance, with a multiplier of 8, the maximum delay is approximately 2 seconds (255 * 8 = 2040ms).
    • The computed delay value is passed to the delay function, which pauses the program for the calculated duration.
  5. Turning Off the LED:
    • After the delay, RA0 is set low to turn off the LED.
    • The loop then resumes checking the button.

The Circuit

10f322 adc a1 button

The Code

Here is the complete code for the adjustable push button timer:

/*
 * 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;
}

Summary

This tutorial demonstrates how to create an adjustable push button timer using the PIC10F322 microcontroller. The timer’s duration is controlled by a potentiometer, providing a practical example of integrating ADC and PWM functionalities. By following the detailed steps and code provided, you can build a flexible timing solution for various applications.


Have a Project or Idea!?

Seeking Bespoke Technology Solutions?

jamie@jamiestarling.com


Pin It on Pinterest

Share This