Table of Contents
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 PIC10F322 and the XC8 compiler.
Setup
- RA0: LED
- RA1: Potentiometer
- RA2: Push Button (connected to ground, with weak pull-up enabled)
High-Level Overview
- Press the button
- LED lights up
- LED remains lit for a duration determined by the potentiometer position
Detailed Explanation
- 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.
- 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.
- 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.
- 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.
- The potentiometer value (8-bit ADC result) is multiplied by a predefined delay multiplier (
- 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
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 Creative or Technical Project in Mind?
Looking for guidance, insights, or a fresh perspective on your technical or creative journey? Or just somebody to chat with?
Reach Out
jamie@jamiestarling.com