nerdegutta.no
PIC16F690 - 4 digit 7 segment hour counter
21.04.25
Embedded
In this article I'm using a PIC 16F690.
#include < xc.h > // remove the extra spaces #define _XTAL_FREQ 4000000 // CONFIG #pragma config FOSC = INTRCIO #pragma config WDTE = OFF #pragma config PWRTE = OFF #pragma config MCLRE = ON #pragma config CP = OFF #pragma config CPD = OFF #pragma config BOREN = ON #pragma config IESO = ON #pragma config FCMEN = ON const unsigned char segmentMap_CA[10] = { ~0b00111111, // 0 ~0b00000110, // 1 ~0b01011011, // 2 ~0b01001111, // 3 ~0b01100110, // 4 ~0b01101101, // 5 ~0b01111101, // 6 ~0b00000111, // 7 ~0b01111111, // 8 ~0b01101111 // 9 }; const unsigned char digitMask[4] = { 0b00010000, // RB4 → digit 4 (rightmost) 0b00100000, // RB5 → digit 3 0b01000000, // RB6 → digit 2 0b10000000 // RB7 → digit 1 (leftmost) }; // VARIABLES volatile unsigned int counter = 0; volatile unsigned char digits[4] = {0, 0, 0, 0}; volatile unsigned char currentDigit = 0; volatile unsigned int tickMs = 0; volatile unsigned char dpState = 0; // FUNCTION PROTOTYPE void updateDigits(); void init(); void __interrupt() isr(); void eeprom_write(unsigned char address, unsigned char data); unsigned char eeprom_read(unsigned char address); void saveCounterToEEPROM(unsigned int value); unsigned int loadCounterFromEEPROM(); // Function to update the digits on the 7-seg void updateDigits() { digits[3] = counter % 10; digits[2] = (counter / 10) % 10; digits[1] = (counter / 100) % 10; digits[0] = (counter / 1000) % 10; } // Function to initialize the MCU portts void init() { ANSEL = 0; ANSELH = 0; // Disable input buffer on ANSEL & ANSELH CM1CON0 = 0; CM2CON0 = 0; // Disable comtarators on C1 & C1 //TRISA = 0; TRISA = 0b00100000; // RA5 input -> Reset button TRISB = 0b00001111; // RB4–RB7 as output TRISC = 0x00; // RC0–RC7 as output (segments + DP) PORTAbits.RA2 = 0; // Set RA2 LOW PORTB = 0x00; // Set all to LOW PORTC = 0xFF; // Set all to HIGH WPUA |= 0b00100000; // Enable weak pull-ups on RA2 // --- Timer0 Setup --- OPTION_REG = 0b00000001; // Prescaler 1:256 (with 4MHz -> ~1.024ms per overflow) OPTION_REGbits.nRABPU = 0; // Enable pull-ups on induvidial ports TMR0 = 6; // Preload TMR0 with 6 INTCON = 0b10100000; // Enable GIE, TMR0IE } // Interrupt Service Routine void __interrupt() isr() { if (T0IF) { T0IF = 0; TMR0 = 0; // Turn off all digits PORTB &= 0x0F; PORTC = 0xFF; // Set segments unsigned char seg = segmentMap_CA[digits[currentDigit]]; // Apply DP globally, regardless of digit if (dpState) { seg &= ~(1 << 7); // DP ON } else { seg |= (1 << 7); // DP OFF } PORTC = seg; // Activate digit PORTB |= digitMask[3 - currentDigit]; currentDigit = (currentDigit + 1) % 4; // Tick counter (~1ms per interrupt) tickMs++; // Blink LED (RA2) and update DP state every 500ms if (tickMs % 500 == 0) { dpState = !dpState; RA2 = dpState ? 1 : 0; } // Count hours static unsigned long msCounter = 0; msCounter++; if (msCounter >= 3600000UL) { // 1 hour = 3,600,000 ms msCounter = 0; counter++; if (counter > 9999) counter = 0; updateDigits(); saveCounterToEEPROM(counter); } } } // Function to write data to the eeprom void eeprom_write(unsigned char address, unsigned char data) { while (WR); // Wait for previous write to finish EEADR = address; EEDATA = data; EECON1 = 0b00000100; // Access data EEPROM EECON2 = 0x55; EECON2 = 0xAA; EECON1bits.WR = 1; // Start write } // Function to read data from the eeprom unsigned char eeprom_read(unsigned char address) { while (WR); // Wait for previous write to finish EEADR = address; EECON1 = 0b00000000; // Access data EEPROM EECON1bits.RD = 1; return EEDATA; } // Save function void saveCounterToEEPROM(unsigned int value) { eeprom_write(0x00, (unsigned char)(value & 0xFF)); // Low byte eeprom_write(0x01, (unsigned char)((value >> 8) & 0xFF)); // High byte } // Read function unsigned int loadCounterFromEEPROM() { unsigned char low = eeprom_read(0x00); unsigned char high = eeprom_read(0x01); return ((unsigned int)high << 8) | low; } // Main program void main() { init(); counter = loadCounterFromEEPROM(); updateDigits(); while (1) { // Everything handled in interrupt if (RA5 == 0) { __delay_ms(50); if (RA5 == 0) { counter = 0; updateDigits(); saveCounterToEEPROM(counter); while (RA5 == 0); __delay_ms(50); } } } } /* * PIN - Connection * 1 - VDD * 2 - RA5 => reset button to gnd * 3 - RA4 * 4 - MCLR * 5 - RC5 => segment F * 6 - RC4 => segment E * 7 - RC3 => segment D * 8 - RC6 => segment G * 9 - RC7 => DP * 10 - RB7 => CA Digit 1, leftmost * 11 - RB6 => CA Digit 2 * 12 - RB5 => CA Digit 3 * 13 - RB4 => CA Digit 4, rightmost * 14 - RC2 => segment C * 15 - RC1 => segment B * 16 - RC0 => segment A * 17 - RA2 => LED * 18 - RA1 * 19 - RA0 * 20 - GND * * All the segments are connected with a 240 resistor. */