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.
*/