Captain's Universe Home
Captain's Universe Home
Cosmic Ray Muon DetectorTeleGarden Pages
Time on MarsBryophyllum Plants
Jupiter Radio AstronomyAncient Pages
Salzburg Tourist GuideEarth Magnetometer
  H O M E     AJAX & MORE     LINUX & MORE     RTAI     XENOMAI     ADEOS IPIPE      
    JAVA & BROWSERS     *NIX     ELECTRONICS     REVIEWS     ARTEMIA     FAIRY SHRIMP      



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

Google
 
Web www.captain.at
go to top
© 1996-2010 . All rights reserved.
No reproduction, distribution, publishing or transmission of the copyrighted materials at this site is permitted. Policy
go to top