- Radio RF Electronics


|
|
Atmel ATmega (ATmega16) DCF77 time signal decoder and C program (archive page)
This is V1.0 of the Atmel firmware.
See the original page for the current version
Based on the firmware by http://www.ulrichradig.de/ . Thanks :-)
dcf77.c
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <inttypes.h>
#include <avr/iom16.h>
// crystal frequency in Hz
#define F_OSC 2457600
// oscillator frequencies that match well for the timer AND USART BAUD rate:
// frequency div timer count
// 2457600 / 1024 = 2400
// 7372800 / 1024 = 7200
// 11059200 / 1024 = 10800
// 14745600 / 1024 = 14400
#define UART_BAUD_RATE 9600
#define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)
unsigned char sec = 0;
unsigned char min = 0;
unsigned char hour = 0;
unsigned char day = 0;
unsigned char month = 0;
unsigned int year = 0;
unsigned char bit_counter = 0;
// 64 bits: 59 bits are used
unsigned long long dcf77_buffer = 0;
unsigned int timercounts = 0;
#define DCF77_INT SIG_INTERRUPT0
#define INT0_CONTROL MCUCR
#define INIT_TIMER_COUNT 65535 - (F_OSC / 1024)
#define RESET_TIMER0 TCNT1 = INIT_TIMER_COUNT
#define DEBUG
struct DCF77 {
unsigned long long bits :16; // bits 1 to 16: reserved or not needed here
unsigned long long mez_mesz :1; // 1 at transition from MEZ to MESZ or vice versa
unsigned long long zone1 :1; // 0=MEZ, 1=MESZ
unsigned long long zone2 :1; // 0=MESZ, 1=MEZ
unsigned long long leapsecond :1; // If 1, a leap-second is inserted at end of hour
unsigned long long start :1; // start bit is always 1
unsigned long long min :7; // 7 bits for minutes
unsigned long long parity_min :1; // parity bit for minutes
unsigned long long hour :6; // 6 bits for hour
unsigned long long parity_hour :1; // parity bit for hour
unsigned long long day :6; // 6 bits for day
unsigned long long weekday :3; // 3 bits for weekday
unsigned long long month :5; // 5 bits for month
unsigned long long year :8; // 8 bits for year (5 = 2005)
unsigned long long parity_date :1; // parity bit for date
};
struct {
unsigned char parity_flag :1;
unsigned char parity_min :1;
unsigned char parity_hour :1;
unsigned char parity_date :1;
} flags;
void delay_ms(unsigned short ms) {
unsigned short outer1, outer2;
outer1 = 200;
while (outer1) {
outer2 = 1000;
while (outer2) {
while ( ms ) ms--;
outer2--;
}
outer1--;
}
}
int usart_putc(char c) { // output one character via RS232
// wait until UDR ready
while(!(UCSRA & (1 << UDRE)));
UDR = c; // send character
return (0);
}
void uart_puts (char *s) { // output string via RS232
// loop until *s != NULL
while (*s) { usart_putc(*s); s++; }
}
void init(void) {
// set baud rate
UBRRH = (uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) >> 8);
UBRRL = (uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);
// Enable receiver and transmitter; enable RX interrupt
UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE);
//asynchronous 8N1
UCSRC = (1 << URSEL) | (3 << UCSZ0);
// All printf's are sent via the serial port (RS232)
// This is just for a neat output of the date/time data:
// printf incredibly blows up the code
fdevopen(usart_putc, NULL, 0);
}
// INTERRUPT can be interrupted
// SIGNAL can't be interrupted
SIGNAL (SIG_UART_RECV) { // USART RX interrupt
unsigned char c;
// read received character
c = UDR;
// and just send it back
usart_putc(c);
}
// begin DCF77
void add_second (void) {
sec++;
if (sec == 60) {
sec = 0;
min++;
if (min == 60) {
min = 0;
hour++;
if (hour == 24) { hour = 0; }
}
}
}
SIGNAL (SIG_OVERFLOW1) {
struct DCF77 *tmp_buffer;
tmp_buffer = (struct DCF77 *)(unsigned long long)&dcf77_buffer;
// reset timer
RESET_TIMER0;
// check if we've got the 59th bit and the parities match
#ifdef DEBUG
printf("SIG_OVERFLOW1 bc=%d %d %d %d %d %d %d \r", bit_counter,
flags.parity_min, tmp_buffer->parity_min,
flags.parity_hour, tmp_buffer->parity_hour,
flags.parity_date, tmp_buffer->parity_date);
#endif
if (bit_counter == 59 &&
flags.parity_min == tmp_buffer->parity_min &&
flags.parity_hour == tmp_buffer->parity_hour &&
flags.parity_date == tmp_buffer->parity_date) {
// convert all values from BCD (1,2,4,8,10,20,...) to decimal
min = tmp_buffer->min - ((tmp_buffer->min/16)*6);
hour = tmp_buffer->hour - ((tmp_buffer->hour/16)*6);
day = tmp_buffer->day - ((tmp_buffer->day/16)*6);
month = tmp_buffer->month - ((tmp_buffer->month/16)*6);
year = 2000 + tmp_buffer->year - ((tmp_buffer->year/16)*6);
// we received all 59 bits, so we have the start of the minute
sec = 0;
#ifdef DEBUG
printf("SIG_OVERFLOW1: TIME SET\r");
#endif
} else {
// not every 59 bits were received, or no DCF-77 signal at all:
// clock runs on internal timer
add_second();
#ifdef DEBUG
printf("SIG_OVERFLOW1: NO DCF77 - INTERNAL MODE\r");
#endif
}
bit_counter = 0;
dcf77_buffer = 0;
}
SIGNAL (DCF77_INT) {
// if rising edge, pluse signal starts
if (bit_is_set(INT0_CONTROL, ISC00)) {
#ifdef DEBUG
printf("DCF77_INT rising edge \r");
#endif
// add elapsed timer counts to timercounts
timercounts = timercounts + TCNT1 - INIT_TIMER_COUNT;
// reset timer
RESET_TIMER0;
// check if at least 90% of one second have elapsed
if (timercounts > (F_OSC / 1024 / 100 * 90)) {
add_second();
timercounts = 0;
}
// since the pulse' edge was rising, next interrupt must be triggered
// at the falling edge of the DCF-77 pulse
INT0_CONTROL &= ~(1 << ISC00);
} else {
#ifdef DEBUG
printf("DCF77_INT falling edge \r");
#endif
// Falling edge of pulse detected
// Now we check for the actual pulse width
unsigned int pulse_width = TCNT1;
// Reset timer: DCF-77 signal seems to be working, so we must not add
// one second in the timer interrupt service routine (ISR)
// Remember: this additional stuff is necessary, since we're reseting the
// timer to avoid false second counts due the timer interrupt
RESET_TIMER0;
// add elapsed timer counts to timercounts
timercounts = timercounts + pulse_width - INIT_TIMER_COUNT;
// Checking of the parity
// We check only minute, hour and the date for partiy, so reset the
// temporaty parity flag when minute, hour or date bit-stream starts
if (bit_counter == 21 || bit_counter == 29 || bit_counter == 36) {
flags.parity_flag = 0;
}
// If the last bit of minute, hour or data is received, save our caluculated
// parity flag
if (bit_counter == 28) {flags.parity_min = flags.parity_flag;};
if (bit_counter == 35) {flags.parity_hour = flags.parity_flag;};
if (bit_counter == 58) {flags.parity_date = flags.parity_flag;};
// check if we received a 0 or a 1 bit: 100ms = 0; 200ms = 1
// When there is a rising edge, we restart the timer. When the falling edge
// is detected, we save the current timer count register TCNT1 in pulse_width
// We just check if the pulse width is bigger than 150ms, so we have a 1
// (F_OSC / 1024)/100*85 = 85% of the counts of the timer (= 1 second)
// 65535 - (F_OSC / 1024)/100*85) = count of the timer when 150ms are elapsed
if (pulse_width > (65535 - (F_OSC / 1024)/100*85)) {
// set the bit "bit_counter" in "dcf77_buffer" to 1
dcf77_buffer = dcf77_buffer | ((unsigned long long) 1 << bit_counter);
// If we've just set the initial 1 in i.e. the "day", we toggle the
// parity to 1 to match the "day" value to even parity
// That means, "day" + parity-bit = even number
// If we've added the second 1, we toggle the parity back to 0
flags.parity_flag = flags.parity_flag ^ 1;
}
// we just detected the falling edge; next interrupt is at rising edge again
INT0_CONTROL |= (1 << ISC00);
// prepare the bit_counter for the next received bit by incrementing it
bit_counter++;
}
}
void DCF77_init (void) {
//DCF77_INT_ENABLE();
GICR |= (1 << INT0);
// set INT0 to edge detection
INT0_CONTROL |= (1 << ISC01);
// timer overflow interrupt enable
timer_enable_int(_BV(TOIE1));
// timer prescaler to 1024
TCCR1B |= (1<<CS10 | 0<<CS11 | 1<<CS12);
// init timer
RESET_TIMER0;
}
// end DCF77
int main(void) {
init();
sei();
printf("testing printf \r");
DCF77_init();
// enable PD5 as output
DDRD |= (1<<PD5);
while (1) {
// PIN5 PORTD clear -> LED off
PORTD &= ~(1<<PD5);
delay_ms(500);
// PIN5 PORTD set -> LED on
PORTD |= (1<<PD5);
delay_ms(500);
// This is just for a neat output of the date/time data:
// printf incredibly blows up the code
printf("%d-%d-%d Time: %d:%d:%d \r",day, month, year, hour, min, sec);
}
return 0;
}
Last-Modified: Thu, 01 Mar 2007 21:57:34 GMT
|
|