If this page is translated by Google, the source code below will not be useable : leave the translation to get it correctly.
/*
* DCF-77 LED CLOCK
*
* PIC16F84A
* 10 Mhz crystal, HS clock
*
* PORTA.0->3, out : 7 segment cathod control
* PORTA.4, in ; DCF pulse input
*
* PORTB.0->7, out : 7 segment output
* PORTB.7, in : button input
*
* Author : Bruno Gavand, november 2005
* see more details on www.micro-examples.com
*
*/
/*
* constant definitions
*/
#define MAXCOUNT 9766 // number of TMR0 overflows in 1 second
#define ADJUST 96 // extra ticks in 1 second
/*
* this values are reduced in practice
* to give some flexibility to DCF-77 pulse reception
*/
#define timer_d_min 14000 // number of TMR0 overflows in 2 seconds : 19531
#define timer_h_0 640 // number of TMR0 overflows in 0.1 second : 976
#define timer_h_1 1400 // number of TMR0 overflows in 0.2 second : 1953
/*
* RAM variables
*/
unsigned int tmrh ; // count of TMR0 overflows since pulse is high
unsigned int tmrd ; // count of TMR0 overflows since pulse is down
unsigned char bitnum = 0 ; // number of last valid bit received
char last_bit ; // value of last valid bit received
unsigned char parity ; // count of positive bits received
unsigned char full = 0 ; // set to 1 when DCF frame is complete
unsigned char locked = 0 ; // set to 1 when clock has been adjusted
unsigned char mode = 1 ; // 0:positive logic receiver, 1:negative logic receiver
unsigned char mn ; // next minutes in DCF frame
unsigned char hh ; // next hours in DCF frame
unsigned int scaler ; // count of TMR0 overflows for RTC
unsigned char rhh = 12, rmn = 34, rss = 56 ; // RTC clock : hours, minutes, seconds
unsigned char digiled[4] ; // 7 segment for each 4 displays
/*
* 7 segment encoding for numbers from 0 to 9
*/
unsigned char septSeg[10] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7c, 0x07, 0x7f, 0x67 } ;
unsigned char digit ; // current digit in multiplexing sequence
unsigned char kp ; // keypad : button to press for displaying mn:ss instead of hh:mn
unsigned char dp = 0 ; // decimal points to be reported on display
unsigned char i ; // general purpose register
/*
* interrupt routine called 2500000/256 times by seconds :
* the register TMR0 is increased each 4 clock cycles (quartz frequency is 10 Mhz),
* and overflows when reseting from 255 to 0,
* fetching to the interrupt precedure with bit T0IF set
*/
void interrupt(void)
{
// if(INTCON.T0IF) // TMR0 overflow ?
{
/*
* test DCF pulse, inverts the level if negative logic flag 'mode' is set
*/
if(PORTA.F4 ^ mode) // DCF pulse is high ?
{
tmrh++ ; // yes, increment my high timer
if(tmrd > timer_d_min) // down timer is greater than pulse pause ?
{
bitnum = 0 ; // yes, reset bit number
if(full) // is the DCF frame complete ?
{
rhh = hh ; // yes, set hours, min and seconds of RTC
rmn = mn ;
rss = 0 ;
scaler = 0 ; // reset my scaler
locked = 1 ; // locked flag is set, to enable display
}
mn = hh = 0 ; // reset min and hours for next frame
parity = 0 ; // reset parity counter
full = 0 ; // reset full flag
dp.F3 = 1 ; // set frame flag
}
tmrd = 0 ; // reset down timer
}
else
{
tmrd++ ; // pulse is low
if(tmrh > 0) // test if a high pulse was present
{
if(tmrh > timer_h_1) // yes, was longer than delay for a 1 ?
{
last_bit = 1 ; // yes, last bit was a 1
switch(bitnum) // depending on number of frame bit :
{
case 21: mn++ ; break ; // add minutes weights
case 22: mn += 2 ; break ;
case 23: mn += 4 ; break ;
case 24: mn += 8 ; break ;
case 25: mn += 10 ; break ;
case 26: mn += 20 ; break ;
case 27: mn += 40 ; break ;
case 29: hh++ ; break ; // add hours weights
case 30: hh += 2 ; break ;
case 31: hh += 4 ; break ;
case 32: hh += 8 ; break ;
case 33: hh += 10 ; break ;
case 34: hh += 20 ; break ;
}
/*
* is it a data bit or a parity bit ?
*/
if((bitnum != 28) && (bitnum != 35) && (bitnum != 58))
{
parity++ ; // data bit, increment parity
}
bitnum++ ; // next frame bit
}
else if(tmrh > timer_h_0) // was last pulse a 0 ?
{
if(bitnum == 20) // yes, start bit ?
{
last_bit = -1 ; // bad karma, should be 1 !
bitnum = 0 ; // reset frame bit counter
dp.F3 = 0 ; // clear frame flag indicator
}
else
{
last_bit = 0 ; // not a start bit, is ok
bitnum++ ; // next frame bit
}
}
else
{
last_bit = -1 ; // ouch, last bit was garbage
bitnum = 0 ; // reset frame bit counter
dp.F3 = 0 ; // clear frame flag indicator
}
if(bitnum == 21) // bit 20 is passed ?
{
parity = 0 ; // yes, clear parity counter
}
/*
* last bit was a parity bit ?
*/
if((bitnum == 29) || (bitnum == 36) || (bitnum == 59))
{
if((parity & 1) != last_bit) // yes, but parity is incorrect
{
bitnum = 0 ; // reset frame bit counter
dp.F3 = 0 ; // clear frame flag indicator
}
parity = 0 ; // clear parity counter
}
if(bitnum == 59) // is the frame complete ?
{
full++ ; // yes, should have a pulse pause
}
}
tmrh = 0 ; // clear high pulse counter
}
/*
* real time clock
*/
scaler++ ; // increment the scaler
if(scaler == MAXCOUNT) // a second is passed ?
{
TMR0 += ADJUST ;
scaler = 0 ; // yes, clear scaler
rss++ ; // next second
if(rss == 60) // seconds overflow ?
{
rss = 0 ; // yes, clear second
rmn++ ; // next minute
if(rmn == 60) // minutes overflow ?
{
rmn = 0 ; // yes, clear minute
rhh++ ; // next hour
if(rhh == 24) // hours overflow ?
{
rhh = 0 ; // yes, clear hour
}
}
}
}
dp.F1 = PORTA.F4 ^ mode ; // copy the pulse level to the decimal point for display
INTCON.T0IF = 0 ; // clear interrupt flag to enable next call on overflow
}
}
/*
* program entry point
*/
main()
{
TRISA = 0b00010000 ; // see header
TRISB = 0x00 ; // see header
INTCON = 0b10100000 ; // T0IF and GIE enabled
OPTION_REG = 0b11011000 ; // no prescaler
/*
* main loop
*/
for(;;)
{
if(locked > 0) // is the RTC up to date ?
{
if(kp) // yes, is the key pressed ?
{
/*
* yes, prepare display for MN:SS
*/
digiled[0] = septSeg[rmn / 10] ; // minutes tenth
digiled[1] = septSeg[rmn % 10] ; // minutes unit
digiled[2] = septSeg[rss / 10] ; // seconds tenth
digiled[3] = septSeg[rss % 10] ; // seconds unit
}
else
{
/*
* no key, prepare display for HH:MN
*/
digiled[0] = (rhh < 10) ? 0 : septSeg[rhh / 10] ; // hours tenth, blank if null
digiled[1] = septSeg[rhh % 10] ; // hours unit
digiled[2] = septSeg[rmn / 10] ; // minutes tenth
digiled[3] = septSeg[rmn % 10] ; // minute unit
}
}
else
{
/*
* the RTC is not up to date, display DCF frame info
*/
digiled[0] = 0 ; // nothing on first 2 digits
digiled[1] = 0 ;
digiled[2] = septSeg[bitnum / 10] ; // number of bit tenth
digiled[3] = septSeg[bitnum % 10] ; // number of bit unit
}
/*
* set each decimal points,
* F1 is pulse repeater,
* F3 is frame progress
*/
digiled[0].F7 = dp.F0 ;
digiled[1].F7 = dp.F1 ;
digiled[2].F7 = dp.F2 ;
digiled[3].F7 = dp.F3 ;
PORTA = 0 ; // shut down display
PORTB = 0 ; // clear segment outputs
TRISB = 0x80 ; // set PORTB.F7 as input
kp = PORTB.F7 ; // read key
TRISB = 0x00 ; // set PORTB.F7 as output again
digit++ ; // next digit to be displayed
if(digit > 3) // all done ?
{
digit = 0 ; // yes, start again with first
i = 0x01 ; // assign bit mask for PORTA first display led cathod control
}
else
{
i = 0x01 << digit ; // shift bit for next led display cathod activztion
}
PORTB = digiled[digit] ; // write 7 segment output
PORTA = i ; // light on selected 7 segment display
}
}