Full Version : Captian's DCF77 RTAT Time Signal Generator
avr >>TECHNICAL & HARDWARE >>Captian's DCF77 RTAT Time Signal Generator


AVR_Admin- 04-29-2006
DCF77 Time Signal Simulator with RTAI 3.2 in hard real time

The DCF-77 Time Signal broadcasted from Germany is a precision atomic clock signal. Many clocks and wrist watches (Funkuhr, Funkarmbanduhr) use this signal for synchronization.

Here is a hard real time DCF77 simulator, which produces a DCF-77 signal on the parallel port data pins. The time sent via the parallel port is the local time of the machine.

DCF77 Time Code Details

dcf77.c is the RTAI3.2/LXRT version in hard real time

dcf77_soft.c is the regular GNU/Linux version: make sure you don't do any CPU or I/O intensive stuff while running the non-RTAI version, otherwise the time signal might be currupted

This simulator is not meant for generating accurate time, but rather to test equipment like embedded hardware etc. which requires a DCF-77 signal without having to use a real DCF77 receiver.


Link to Site: http://www.captain.at/rtai-dcf77-simulator.php

Compile dcf77.c with the Makefile and run it with "run.sh".

dcf77.c
CODE

/* DCF77 Simulator for RTAI 3.2
A DCF77 time signal is produced on the parallel port data pins
Data on pins 2-9 (D0-D7)
GND on pins 18-25
Very handy for testing embedded hardware which needs DCF77
(C) 2005 www.captain.at */
#include <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>

#include <rtai_lxrt.h>
#include <rtai_sem.h>
#include <rtai_usi.h>
#include <sys/io.h>
     
#define BASEPORT 0x378

static SEM *dspsem;
static volatile int end_dcf77 = 1;
static volatile int end = 0;

#define PERIOD 100000000 // 100ms

void sendone(void) {
int t;
outb_p(255, BASEPORT);
rt_task_wait_period(); // 1 = 200ms
rt_task_wait_period();
outb_p(0, BASEPORT);
for (t = 1; t <= 8; t++) { rt_task_wait_period(); }
}

void sendzero(void) {
int t;
outb_p(255, BASEPORT);
rt_task_wait_period(); // 0 = 100ms
outb_p(0, BASEPORT);
for (t = 1; t <= 9; t++) { rt_task_wait_period(); }
}

unsigned int Convert2PacketBCD(unsigned int value) {
unsigned int result;
result = value % 10;
result += (value - result) / 10 * 0x10;
return result;
}

static void *dcf77_generator(void *args) {
RT_TASK *handler;
RTIME period;
int i, min, hour, day, wday, month, year;
int now_min, now_hour, now_day, now_wday, now_month, now_year;
time_t now;
struct tm *now_tm;
unsigned int parity_min, parity_hour, parity_date;

 if (!(handler = rt_task_init_schmod(nam2num("DCFHLR"),
                   0, 0, 0, SCHED_FIFO, 0xF))) {
 printf("CANNOT INIT HANDLER TASK > DCFHLR <\n");
 exit(1);
}

// Get the current date/time before we go to hard real time
// Those are SYSCALLs and would break hard real time
now = time (NULL);
now_tm = localtime (&now);
now_min = now_tm->tm_min;
now_hour = now_tm->tm_hour;
now_day = now_tm->tm_mday;
now_wday = now_tm->tm_wday;
now_month = now_tm->tm_mon + 1;
now_year = now_tm->tm_year - 100;

min = Convert2PacketBCD(now_min);
hour = Convert2PacketBCD(now_hour);
day = Convert2PacketBCD(now_day);
wday = Convert2PacketBCD(now_wday);
month = Convert2PacketBCD(now_month);
year = Convert2PacketBCD(now_year);

rt_allow_nonroot_hrt();
mlockall(MCL_CURRENT | MCL_FUTURE);
rt_make_hard_real_time();
end_dcf77 = 0;

rt_set_oneshot_mode();
start_rt_timer(0);
 period = nano2count(PERIOD);
rt_task_make_periodic(handler, rt_get_time() + period, period);

while ( !end_dcf77 ) {
 rt_sem_signal(dspsem);
 outb_p(0, BASEPORT);
 
 // generate 20 zeros
 for (i = 1; i <= 20; i++ ) {
  sendzero();
  if (end_dcf77) break;
 }
 // send one start bit
 sendone();
 // minute
 parity_min = 0;
 for (i = 1; i <= 7; i++) {
  if (min & 1) {
   sendone();
   parity_min = parity_min ^ 1;
  } else sendzero();
  min = min >> 1;
 }
 if (end_dcf77) break;
 // minute_parity
 if (parity_min) { sendone(); } else { sendzero(); }
 // hour
 parity_hour = 0;
 for (i = 1; i <= 6; i++) {
  if (hour & 1) {
   sendone();
   parity_hour = parity_hour ^ 1;
  } else sendzero();
  hour = hour >> 1;
 }
 // hour_parity
 if (parity_hour) { sendone(); } else { sendzero(); }
 if (end_dcf77) break;
 parity_date = 0;
 // day
 for (i = 1; i <= 6; i++) {
  if (day & 1) {
   sendone();
   parity_date = parity_date ^ 1;
  } else sendzero();
  day = day >> 1;
 }
 if (end_dcf77) break;
 // weekday
 for (i = 1; i <= 3; i++) {
  if (wday & 1) {
   sendone();
   parity_date = parity_date ^ 1;
  } else sendzero();
  wday = wday >> 1;
 }
 if (end_dcf77) break;
 // month
 for (i = 1; i <= 5; i++) {
  if (month & 1) {
   sendone();
   parity_date = parity_date ^ 1;
  } else sendzero();
  month = month >> 1;
 }
 if (end_dcf77) break;
 // year
 for (i = 1; i <= 8; i++) {
  if (year & 1) {
   sendone();
   parity_date = parity_date ^ 1;
  } else sendzero();
  year = year >> 1;
 }
 if (end_dcf77) break;
 // date_parity
 if (parity_date) { sendone(); } else { sendzero(); }
 // wait a second
 if (end_dcf77) break;
 for (i = 0; i < 10; i++) { rt_task_wait_period(); if (end_dcf77) break; }

 // increment minute
 now_min++;
 if (now_min > 59) {
  now_min = 0;
  now_hour++;
  if (now_hour > 23) {
   now_hour = 0;
   now_day++;
   // etc...
  }
 }
 min = Convert2PacketBCD(now_min);
 hour = Convert2PacketBCD(now_hour);
 day = Convert2PacketBCD(now_day);
 wday = Convert2PacketBCD(now_wday);
 month = Convert2PacketBCD(now_month);
 year = Convert2PacketBCD(now_year);
}
stop_rt_timer();
rt_make_soft_real_time();
rt_task_delete(handler);
return 0;
}

void clean_exit(int dummy) {
end = 1;
end_dcf77 = 1;
}

int main(void)
{
RT_TASK *maint;
int dcf77thread;

signal(SIGTERM, clean_exit);
signal(SIGINT, clean_exit);

if (!(maint = rt_task_init(nam2num("MAIN"), 1, 0, 0))) {
 printf("CANNOT INIT MAIN TASK > MAIN <\n");
 exit(1);
}
// create semaphore to notify main() when interrupt occurs
if (!(dspsem = rt_sem_init(nam2num("DSPSEM"), 0))) {
 printf("CANNOT INIT SEMAPHORE > DSPSEM <\n");
 exit(1);
}
// ask for permission to access the parallel port from user-space
if (iopl(3)) {
 printf("iopl err\n");
 rt_task_delete(maint);
 rt_sem_delete(dspsem);
 exit(1);
}

outb_p(0x10, BASEPORT + 2); //set port to interrupt mode; pins are output
// interrupt mode is not used here

// create thread
dcf77thread = rt_thread_create(dcf77_generator, NULL, 10000);
while (end_dcf77) {   // wait until thread went to hard real time
 usleep(100000);
}

while (!end) {
 rt_sem_wait(dspsem);
 printf("New minute starts now [use CTRL-C to end]\n");
}
printf("WAIT FOR COMPLETION\n");
rt_thread_join(dcf77thread);
rt_sem_delete(dspsem);
rt_task_delete(maint);
printf("DCF77SIM HAS ENDED\n");
return 0;
}


Makefile
CODE

prefix := $(shell rtai-config --prefix)

ifeq ($(prefix),)
$(error Please add <rtai-install>/bin to your PATH variable)
endif

CC = $(shell rtai-config --cc)
LXRT_CFLAGS = $(shell rtai-config --lxrt-cflags)
LXRT_LDFLAGS = $(shell rtai-config --lxrt-ldflags)

all: dcf77

dcf77: dcf77.c
$(CC) $(LXRT_CFLAGS) -o $@ $< $(LXRT_LDFLAGS)

clean:
rm -f *.o dcf77

.PHONY: clean


run.sh

#!/bin/sh
TMP=$PWD
cd /usr/realtime/modules/
insmod ./rtai_hal.ko
insmod ./rtai_lxrt.ko
insmod ./rtai_sem.ko
cd $TMP
./dcf77
rmmod rtai_sem
rmmod rtai_lxrt
rmmod rtai_hal



Non-RTAI DCF77 Simulator


Compile with:
gcc -o dcf77_soft dcf77_soft.c


dcf77_soft.c
CODE

/* DCF77 Simulator - Non-Realtime-version
A DCF77 time signal is produced on the parallel port data pins
Data on pins 2-9 (D0-D7)
GND on pins 18-25
Very handy for testing embedded hardware which needs DCF77
(C) 2005 www.captain.at */
#include <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <sys/io.h>
     
#define BASEPORT 0x378

static volatile int end_dcf77 = 1;

void sendone(void) {
int t;
outb_p(255, BASEPORT);
usleep(200000); // 1 = 200ms
outb_p(0, BASEPORT);
for (t = 1; t <= 8; t++) { usleep(100000); }
}

void sendzero(void) {
int t;
outb_p(255, BASEPORT);
usleep(100000); // 0 = 100ms
outb_p(0, BASEPORT);
for (t = 1; t <= 9; t++) { usleep(100000); }
}

unsigned int Convert2PacketBCD(unsigned int value) {
unsigned int result;
result = value % 10;
result += (value - result) / 10 * 0x10;
return result;
}

void dcf77_generator(void) {
int i, min, hour, day, wday, month, year;
int now_min, now_hour, now_day, now_wday, now_month, now_year;
time_t now;
struct tm *now_tm;
unsigned int parity_min, parity_hour, parity_date;

// Get the current date/time
now = time (NULL);
now_tm = localtime (&now);
now_min = now_tm->tm_min;
now_hour = now_tm->tm_hour;
now_day = now_tm->tm_mday;
now_wday = now_tm->tm_wday;
now_month = now_tm->tm_mon + 1;
now_year = now_tm->tm_year - 100;

min = Convert2PacketBCD(now_min);
hour = Convert2PacketBCD(now_hour);
day = Convert2PacketBCD(now_day);
wday = Convert2PacketBCD(now_wday);
month = Convert2PacketBCD(now_month);
year = Convert2PacketBCD(now_year);

end_dcf77 = 0;

while ( !end_dcf77 ) {
 printf("New minute starts now [use CTRL-C to end]\n");
 outb_p(0, BASEPORT);
 
 // generate 20 zeros
 for (i = 1; i <= 20; i++ ) {
  sendzero();
  if (end_dcf77) break;
 }
 // send one start bit
 sendone();
 // minute
 parity_min = 0;
 for (i = 1; i <= 7; i++) {
  if (min & 1) {
   sendone();
   parity_min = parity_min ^ 1;
  } else sendzero();
  min = min >> 1;
 }
 if (end_dcf77) break;
 // minute_parity
 if (parity_min) { sendone(); } else { sendzero(); }
 // hour
 parity_hour = 0;
 for (i = 1; i <= 6; i++) {
  if (hour & 1) {
   sendone();
   parity_hour = parity_hour ^ 1;
  } else sendzero();
  hour = hour >> 1;
 }
 // hour_parity
 if (parity_hour) { sendone(); } else { sendzero(); }
 if (end_dcf77) break;
 parity_date = 0;
 // day
 for (i = 1; i <= 6; i++) {
  if (day & 1) {
   sendone();
   parity_date = parity_date ^ 1;
  } else sendzero();
  day = day >> 1;
 }
 if (end_dcf77) break;
 // weekday
 for (i = 1; i <= 3; i++) {
  if (wday & 1) {
   sendone();
   parity_date = parity_date ^ 1;
  } else sendzero();
  wday = wday >> 1;
 }
 if (end_dcf77) break;
 // month
 for (i = 1; i <= 5; i++) {
  if (month & 1) {
   sendone();
   parity_date = parity_date ^ 1;
  } else sendzero();
  month = month >> 1;
 }
 if (end_dcf77) break;
 // year
 for (i = 1; i <= 8; i++) {
  if (year & 1) {
   sendone();
   parity_date = parity_date ^ 1;
  } else sendzero();
  year = year >> 1;
 }
 if (end_dcf77) break;
 // date_parity
 if (parity_date) { sendone(); } else { sendzero(); }
 // wait a second
 if (end_dcf77) break;
 for (i = 0; i < 10; i++) { usleep(100000); if (end_dcf77) break; }

 // increment minute
 now_min++;
 if (now_min > 59) {
  now_min = 0;
  now_hour++;
  if (now_hour > 23) {
   now_hour = 0;
   now_day++;
   // etc...
  }
 }
 min = Convert2PacketBCD(now_min);
 hour = Convert2PacketBCD(now_hour);
 day = Convert2PacketBCD(now_day);
 wday = Convert2PacketBCD(now_wday);
 month = Convert2PacketBCD(now_month);
 year = Convert2PacketBCD(now_year);
}
}

void clean_exit(int dummy) {
end_dcf77 = 1;
printf("WAIT FOR COMPLETION\n");
}

int main(void) {
signal(SIGTERM, clean_exit);
signal(SIGINT, clean_exit);

// ask for permission to access the parallel port from user-space
if (iopl(3)) {
 printf("iopl err\n");
 exit(1);
}
outb_p(0x10, BASEPORT + 2); //set port to interrupt mode; pins are output
// interrupt mode is not used here

dcf77_generator();
printf("DCF77SIM HAS ENDED\n");
return 0;
}




Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.