/******************************************************************************
seg7_clock_no0.c 4digit clock using PIC 16F628A and 7seg displays
28th Jan 2012 - open source - www.RomanBlack.com
Compiler - MikroC, tested in hardware on EasyPIC4
NOTE! This is a variation of the clock that does not show the leading
zero digit, ie it shows 1:00 and not 01:00.
Written for BigAl, to suit his multiplexed clock hardware (see schematic)
Operation; Interrupt generates a 0.5 second event, and also does the
4 digit multiplexing. The 0.5 second event is used for clock setting buttons,
and also counts 120 halfseconds to generate 1 minute (to operate the clock).
PIC 16F628A, 4MHz xtal
Pins;
PORTA 0,1,2,3 - 4 digit drivers, LO = digit ON
PORTB 0-6 - 7 segment drivers, HI = segment ON
PORTB 0,1 - also used for 2 clockset buttons, HI = pressed
(see schematic for buttons and required button resistors)
PORTB7 - used as a "heartbeat" LED, flashes every second
PIC config; MCLRE_OFF, BODEN_OFF, BORES_OFF, LVP_OFF, WDT_OFF, PWRTE_OFF
******************************************************************************/
// global variables
unsigned char buttons; // used in interrupt for multiplxing etc
unsigned char digit;
unsigned char segments;
unsigned char multiplex;
unsigned char halfsecond;
unsigned char mins; // used for clock time data
unsigned char mins10;
unsigned char hours;
unsigned char hours10;
unsigned long bres; // used to generate accurate halfsecond period
// functions
void get_segments(unsigned char);
void update_clock(void);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void interrupt(void)
{
//-------------------------------------------------------
// This is the TMR0 overflow int.
// 16MHz TMR0 is 1:2 prescale so we get here every 512 PIC instructions
// with 4MHz xtal (1uS per instruction) that is every 512uS (1953 Hz).
// Multplexing is done at 1 digit per interrupt, so is approx 488 Hz
// per digit.
//-------------------------------------------------------
// first do timing sensitive stuff, like dead time to stop
// display ghosting, and during dead time read the 2 buttons.
PORTA = 0b00001111; // all 4 digit drivers OFF
PORTB = 0b00000000; // all 7 segments OFF
asm nop;
asm nop;
TRISB = 0b00000011; // change 2 button pins to inputs again
asm nop;
asm nop;
asm nop;
asm nop;
buttons = PORTB; // read the 2 buttons
TRISB = 0b00000000; // change the 2 button pins back to outputs
if(halfsecond.F0) segments.F7 = 1; // (optional) heartbeat LED on PORTB7
PORTB = segments; // select the segments for the next digit
PORTA = digit; // and light the digit up!
//-------------------------------------------------------
// multiplexing sequencing; 1 2 3 4
// and get digits and segments loaded (ready for next interrupt)
multiplex++;
if(multiplex > 4) multiplex = 1;
if(multiplex == 1)
{
// note, this first digit now suppresses leading zero!
if(hours10 >= 10) get_segments(1); // display the 1 for 10, 11, 12
else segments = 0; // <10 make all segments OFF
digit = 0b00001110; // Al's hardware (has reversed digit order; 4321, and active LOW)
}
if(multiplex == 2)
{
get_segments(hours);
digit = 0b00001101; // Al
}
if(multiplex == 3)
{
get_segments(mins10);
digit = 0b00001011; // Al
}
if(multiplex == 4)
{
get_segments(mins);
digit = 0b00000111; // Al
}
//-------------------------------------------------------
// do timekeeping, calc halfseconds
// this is a simple zero-error Bresenham system, a halfsecond
// is 500000 uS (500000 PIC instructions)
bres += 512; // add another interrupt; +512uS
if(bres >= 500000) // if reached a halfsecond
{
bres -= 500000;
halfsecond++; // add another halfsecond (which is handled in main()
}
else
{
buttons = 0; // kill button read if not a halfsecond
}
//-------------------------------------------------------
// reset the TMR0 int flag and exit
INTCON.T0IF = 0;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//=============================================================================
// GET_SEGMENTS
//=============================================================================
void get_segments(unsigned char numb)
{
//-------------------------------------------------------
// this loads the 7 segments to match a decimal digit
// PORTB0 = segA to PORTB6 = segG, standard 7-seg A-G format.
// 1 = segment ON
//-------------------------------------------------------
if(numb == 0) segments = 0b00111111;
if(numb == 1) segments = 0b00000110;
if(numb == 2) segments = 0b01011011;
if(numb == 3) segments = 0b01001111;
if(numb == 4) segments = 0b01100110;
if(numb == 5) segments = 0b01101101;
if(numb == 6) segments = 0b01111101;
if(numb == 7) segments = 0b00000111;
if(numb == 8) segments = 0b01111111;
if(numb == 9) segments = 0b01101111;
}
//-----------------------------------------------------------------------------
//=============================================================================
// UPDATE_CLOCK
//=============================================================================
void update_clock(void)
{
//-------------------------------------------------------
// this checks if the clock has changed, if so it
// handles all digit overflows.
//-------------------------------------------------------
if(mins >= 10)
{
mins = 0;
mins10++;
}
if(mins10 >= 6)
{
mins10 = 0;
hours++;
}
if(hours >= 10)
{
hours = 0;
hours10++;
}
if(hours10 >= 2 && hours > 4) // if > 24:00
{
hours10 = 0; // set to 00:00
hours = 0;
}
}
//-----------------------------------------------------------------------------
//=============================================================================
// MAIN
//=============================================================================
void main(void)
{
//-------------------------------------------------------
// setup PIC 16F628A ports and timer
PORTA = 0b00001111; // 4 digit drivers all OFF (1 = OFF)
TRISA = 0b11110000; // PORTA0-3, are 4 outputs for digit drivers
PORTB = 0b00000000; // PORTB all 7 segments off (0 = OFF)
TRISB = 0b00000000; // PORTB all 8 pins are outputs
OPTION_REG = 0b10000000; // TMR0 at 2:1 prescaler, PORTB pullups OFF
//-------------------------------------------------------
// setup variables before start (before first interrupt)
bres = 0;
digit = 0b00001111; // digits OFF
segments = 0; // segments OFF
halfsecond = 0;
mins = 0; // set clock start time to 12:00
mins10 = 0;
hours = 2;
hours10 = 1;
INTCON = 0b10100000; // GIE on, T0IE on (turns TMR0 interrupt ON)
//-------------------------------------------------------
// main loop
while(1)
{
//---------------------------------------------
// test for buttons pressed, they are checked in the interrupt
// every halfsecond
if(buttons.F0) // if MINS button pressed
{
buttons = 0;
mins++; // add a minute
halfsecond = 0; // reset seconds
update_clock();
}
if(buttons.F1) // if HOURS button pressed
{
buttons = 0;
hours++; // add an hour
halfsecond = 0; // reset seconds
update_clock();
}
//---------------------------------------------
// test for a minute reached, and update clock
if(halfsecond >= 120) // 120 halfseconds = 1 minute
{
mins++; // add a minute
halfsecond = 0; // reset seconds
update_clock();
}
}
}
//-----------------------------------------------------------------------------