How to make an accurate timer with PIC16F84A
Posted: Sat Oct 20, 2012 1:45 am
The first important is to create 1 second time base. The oscillator for the project is 4MHz, the time that the timer0 overflow is Fosc/4/prescaler/256. The minimum prescaler is 1:2, so timer0 will overflow every 512 us. To have 1 second, the timer0 would have to overflow 1953 times. The GIE bit and T0IE bit of INTCON register must be set to enable timer0 interrupt, also PS2:PS0 bit of OPTION_REG register is 000 to set up prescaler 1:2
inside the interrupt routine, a counter variable will increase every time that the timer0 interrupt occur. When the counter is count to 1953, a onesec variable flag is set for next proceed.
For this project, a Start/Stop switch(SW4) is toggle ON/OFF operation. when it's ON, the display will count down and will stop when it's zero or SW4 is press to stop operation. There are 2 switches for setting up time( SW2 and SW3 ), one for increasing second and another for increasing minute. You also can save current time into EEPROM and recall it later.
Because PIC16F84A does not have enough I/O ports; a 74164 serial-in, parallel-out is needed to expand for display. Each individual segment cathode is connected to separate 330 ohm current limiting resistors while the common anode of each display is connected to Collector pin of a PNP transistor. Data is send from RA1 pin of MCU to A pin of 74164 ; and RA0 pin is connected to CLK pin of 74164 to send shift clock signal. The figure below shown 74164 logic diagram and truth table.
Press SW4 switch to on - off the timer.
Press SW3 switch to increase second from 0 - 59.
Press SW2 switch to increase minute from 0 - 99.
Press and release SW1 switch to recall time from EEPROM. Press and hold SW1 for 2- 3 second for store current time into EEPROM.
Code: Select all
INTCON.GIE = 1; //Enable all unmasked interrupts.
INTCON.T0IE = 1; //enable timer0 interrupt.
OPTION_REG = 0x00; //timer0 prescal 1:2
Code: Select all
void interrupt(){
counter++;
if(counter >= 1953) onesec = 1; // set a 1 second flag
}
Because PIC16F84A does not have enough I/O ports; a 74164 serial-in, parallel-out is needed to expand for display. Each individual segment cathode is connected to separate 330 ohm current limiting resistors while the common anode of each display is connected to Collector pin of a PNP transistor. Data is send from RA1 pin of MCU to A pin of 74164 ; and RA0 pin is connected to CLK pin of 74164 to send shift clock signal. The figure below shown 74164 logic diagram and truth table.
Press SW4 switch to on - off the timer.
Press SW3 switch to increase second from 0 - 59.
Press SW2 switch to increase minute from 0 - 99.
Press and release SW1 switch to recall time from EEPROM. Press and hold SW1 for 2- 3 second for store current time into EEPROM.
Code: Select all
#define sClock PORTA.F0
#define sData PORTA.F1
#define OutPut PORTA.F2
char onesec = 0;
int counter = 0;
char key_press = 0;
char On_status = 0;
char Digit_map[10] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
char current_sec = 0;
char current_min = 0;
char first_digit, second_digit;
char third_digit, forth_digit;
char timeout = 0;
void interrupt(){
if(INTCON.T0IF == 1) {
counter++;
if(counter >= 1953) {
onesec = 1;
counter = 0;
if(PORTB.F4 == 0) {
timeout ++;
onesec = 0;
}
}
if (onesec == 1){
onesec = 0;
if (current_sec == 0) {
if(current_min > 0) {
current_sec = 59;
current_min --;
}
else current_min = 0;
}
else current_sec --;
first_digit = current_sec % 10;
second_digit = current_sec/10;
third_digit = current_min % 10;
forth_digit = current_min / 10;
}
INTCON.T0IF = 0; // clear interrupt flag
}
}
// ----- Shift Out function --------------------------
void Shift_OutMSB (char sd) {
char i;
char mask = 128;
for (i=1; i<=8; i++){
if (!(sd&mask)) sData = 0; else sData = 1;
sClock= 1;
delay_us (1);
sClock = 0;
mask = mask >> 1;
}
}
void delay(){
delay_ms(5);
}
void delay20ms(){
delay_ms(20);
}
void time_display(){
Shift_OutMSB (Digit_map[first_digit]);
PORTB.F0 = 0;
delay();
PORTB.F0 = 1;
Shift_OutMSB (Digit_map[second_digit]);
PORTB.F1 = 0;
delay();
PORTB.F1 = 1;
Shift_OutMSB (Digit_map[third_digit]);
PORTB.F2 = 0;
delay();
PORTB.F2 = 1;
Shift_OutMSB (Digit_map[forth_digit]);
PORTB.F3 = 0;
delay();
PORTB.F3 = 1;
}
void key_check(){
char temp_sec, temp_min;
if(PORTB.F7 == 0) {
//delay_ms(10);
if (On_status == 1) {
On_status = 0;
OutPut = 0;
INTCON.T0IE = 0; // disable interrupt timer
}
else {
On_status = 1;
OutPut = 1;
TMR0 = 0;
INTCON.T0IE =1; // enable interrupt timer
}
while (PORTB.F7 == 0) { time_display(); }
}
if(PORTB.F6 ==0) {
if (On_status == 0){
if (current_sec < 59 ) current_sec++;
else current_sec = 0;
first_digit = current_sec % 10;
second_digit = current_sec/10;
while (PORTB.F6 == 0) { time_display(); }
}
}
if(PORTB.F5 ==0) {
if (On_status == 0){
if (current_min < 99 ) current_min++;
else current_min = 0;
third_digit = current_min % 10;
forth_digit = current_min/10;
while (PORTB.F5 == 0) { time_display(); }
}
}
if(PORTB.F4 == 0) {
On_status = 0;
OutPut = 0;
INTCON.T0IE = 0; // disable interrupt timer
temp_sec = Eeprom_Read(0x00);
delay20ms();
temp_min = Eeprom_Read(0x01);
delay20ms();
TMR0 = 0;
INTCON.T0IE =1; // enable interrupt timer
while (PORTB.F4 == 0) { time_display(); }
INTCON.T0IE = 0; // disable interrupt timer
if(timeout > 1) {
timeout = 0;
EEprom_Write(0x00, current_sec);
delay20ms();
EEprom_Write(0x01, current_min);
delay20ms();
}
else {
current_sec = temp_sec;
current_min = temp_min;
timeout = 0;
}
first_digit = current_sec % 10;
second_digit = current_sec/10;
third_digit = current_min % 10;
forth_digit = current_min / 10;
}
}
void main() {
TMR0 = 0;
INTCON.GIE = 1; //Enable all unmasked interrupts.
INTCON.T0IE = 0; //disable timer0 interrupt.
OPTION_REG = 0x00; //timer0 prescal 1:2
INTCON.RBIF = 0; // clear interrupt onchange flag
PORTB = 0xFF;
TRISB = 0xF0; //PORTB4-7 = inputs, 0-3 = output.
TRISA = 0; // PORTA = output.
PORTA = 0;
first_digit = current_sec % 10;
second_digit = current_sec/10;
third_digit = current_min % 10;
forth_digit = current_min / 10;
while(1){
key_check();
time_display();
if(current_min == 0 & current_sec == 0){
OutPut = 0;
On_status = 0;
INTCON.T0IE = 0; // disable interrupt timer
}
}
}