DCF77 & IF482 improvements

This commit is contained in:
Klaus K Wilting 2019-02-07 23:05:26 +01:00
parent e1b6d9a04c
commit bad5805810
7 changed files with 106 additions and 122 deletions

View File

@ -140,7 +140,7 @@ Paxcounter generates identifiers for sniffed MAC adresses and collects them temp
# Clock controller # Clock controller
Paxcounter can be used to sync a clock which has DCF77 or IF482 time telegram input with an external time source. Supported external time sources are GPS time, LORAWAN network time (v1.1) or on board RTC time. The precision of the generated DCF77 / IF482 signal depends on precision of used on board time base. Supported are both external time base (e.g. pps pin of GPS chip or oscillator output of RTC chip) and ESP32 internal clock. Selection of time base and clock frequency must be given by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h). Paxcounter can be used to sync a clock which has DCF77 or IF482 time telegram input with an external time source. Use case of this function is to have paxcounter hardware integrated in clocks, and use it for both counting of pax and controlling the clock. Supported external time sources are GPS time, LORAWAN network time (v1.1) and on board RTC time. Precision of the synthetic DCF77 signal depends on precision of on board available time base. Supported are both external time base (e.g. pps pin of GPS chip or oscillator output of RTC chip) and ESP32 internal clock. Selection of time base and clock frequency is done by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h).
# Payload format # Payload format

View File

@ -2,14 +2,11 @@
#define _DCF77_H #define _DCF77_H
#include "globals.h" #include "globals.h"
#include "rtctime.h"
extern TaskHandle_t DCF77Task;
extern hw_timer_t *dcfCycle;
enum dcf_pulses { dcf_off, dcf_zero, dcf_one }; enum dcf_pulses { dcf_off, dcf_zero, dcf_one };
enum dcf_pinstate { dcf_low, dcf_high }; enum dcf_pinstate { dcf_low, dcf_high };
void IRAM_ATTR DCF77IRQ(void);
int dcf77_init(void); int dcf77_init(void);
void dcf77_loop(void *pvParameters); void dcf77_loop(void *pvParameters);
void sendDCF77(void); void sendDCF77(void);
@ -17,6 +14,5 @@ void DCF_Out(uint8_t startsec);
void generateTimeframe(time_t t); void generateTimeframe(time_t t);
void set_DCF77_pin(dcf_pinstate state); void set_DCF77_pin(dcf_pinstate state);
uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, uint8_t pArray[]); uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, uint8_t pArray[]);
uint8_t sync_clock(time_t t);
#endif #endif

View File

@ -2,11 +2,9 @@
#define _IF482_H #define _IF482_H
#include "globals.h" #include "globals.h"
#include "rtctime.h"
extern TaskHandle_t IF482Task;
int if482_init(void); int if482_init(void);
void if482_loop(void *pvParameters); void if482_loop(void *pvParameters);
void IRAM_ATTR IF482IRQ(void);
#endif #endif

View File

@ -11,11 +11,19 @@
extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available
extern TaskHandle_t ClockTask;
extern hw_timer_t *clockCycle;
int rtc_init(void); int rtc_init(void);
int set_rtctime(uint32_t t); int set_rtctime(uint32_t t);
int set_rtctime(time_t t); int set_rtctime(time_t t);
void sync_rtctime(void); void sync_rtctime(void);
time_t get_rtctime(void); time_t get_rtctime(void);
float get_rtctemp(void); float get_rtctemp(void);
void IRAM_ATTR CLOCKIRQ();
int pps_init(uint32_t pps_freq);
int pps_init();
void pps_start();
uint8_t sync_clock(time_t t);
#endif // _RTCTIME_H #endif // _RTCTIME_H

View File

@ -16,9 +16,6 @@ https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
TaskHandle_t DCF77Task;
hw_timer_t *dcfCycle = NULL;
#define DCF77_FRAME_SIZE (60) #define DCF77_FRAME_SIZE (60)
#define DCF77_PULSE_DURATION (100) #define DCF77_PULSE_DURATION (100)
@ -39,51 +36,19 @@ int dcf77_init(void) {
2048, // stack size of task 2048, // stack size of task
(void *)1, // parameter of the task (void *)1, // parameter of the task
3, // priority of the task 3, // priority of the task
&DCF77Task, // task handle &ClockTask, // task handle
0); // CPU core 0); // CPU core
assert(DCF77Task); // has dcf77 task started? assert(ClockTask); // has clock task started?
#ifdef RTC_INT // if we have hardware pps signal we use it as precise time base #if defined RTC_INT && (RTC_CLK == DCF77_PULSE_DURATION)
pps_init(); // use pps clock
#ifndef RTC_CLK // assure we know external clock freq #else
#error "External clock cycle not defined in board hal file" pps_init(DCF77_PULSE_DURATION); // use esp32 clock
#endif #endif
// setup external interupt for active low RTC INT pin DCF_Out(sync_clock(now())); // sync DCF time on next second
pinMode(RTC_INT, INPUT_PULLUP); pps_start(); // start pulse
// setup external rtc 1Hz clock for triggering DCF77 telegram
ESP_LOGI(TAG, "Time base external clock");
if (I2C_MUTEX_LOCK()) {
Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
I2C_MUTEX_UNLOCK();
} else {
ESP_LOGE(TAG, "I2c bus busy - RTC initialization error");
return 0; // failure
}
#else // if we don't have pps signal from RTC we use ESP32 hardware timer
#define RTC_CLK (DCF77_PULSE_DURATION) // setup clock cycle
ESP_LOGI(TAG, "Time base ESP32 clock");
dcfCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec
timerAttachInterrupt(dcfCycle, &DCF77IRQ, true);
timerAlarmWrite(dcfCycle, 10 * RTC_CLK, true); // 100ms
#endif
// wait until beginning of next second, then kick off first DCF pulse and
// start clock signal
DCF_Out(sync_clock(now()));
#ifdef RTC_INT // start external clock
attachInterrupt(digitalPinToInterrupt(RTC_INT), DCF77IRQ, FALLING);
#else // start internal clock
timerAlarmEnable(dcfCycle);
#endif
return 1; // success return 1; // success
} // ifdcf77_init } // ifdcf77_init
@ -157,9 +122,8 @@ void dcf77_loop(void *pvParameters) {
&wakeTime, // receives moment of call from isr &wakeTime, // receives moment of call from isr
portMAX_DELAY); // wait forever (missing error handling here...) portMAX_DELAY); // wait forever (missing error handling here...)
#if (RTC_CLK == DCF77_PULSE_DURATION) #if !defined RTC_CLK || (RTC_CLK == DCF77_PULSE_DURATION) // we don't need clock rescaling
DCF_Out(0); // we don't need clock rescaling DCF_Out(0);
#else // we need clock rescaling by software timer #else // we need clock rescaling by software timer
for (uint8_t i = 1; i <= RTC_CLK / DCF77_PULSE_DURATION; i++) { for (uint8_t i = 1; i <= RTC_CLK / DCF77_PULSE_DURATION; i++) {
DCF_Out(0); DCF_Out(0);
@ -253,24 +217,4 @@ void set_DCF77_pin(dcf_pinstate state) {
} // switch } // switch
} // DCF77_pulse } // DCF77_pulse
// helper function to sync phase of DCF output signal to start of second t
uint8_t sync_clock(time_t t) {
time_t tt = t;
// delay until start of next second
do {
tt = now();
} while (t == tt);
ESP_LOGI(TAG, "Sync on Sec %d", second(tt));
return second(tt);
}
// interrupt service routine triggered by external interrupt or internal timer
void IRAM_ATTR DCF77IRQ() {
xTaskNotifyFromISR(DCF77Task, xTaskGetTickCountFromISR(), eSetBits, NULL);
portYIELD_FROM_ISR();
}
#endif // HAS_DCF77 #endif // HAS_DCF77

View File

@ -1,4 +1,4 @@
#if defined HAS_IF482 && defined RTC_INT #if defined HAS_IF482
/* NOTE: /* NOTE:
The IF482 Generator needs an high precise 1 Hz clock signal which cannot be The IF482 Generator needs an high precise 1 Hz clock signal which cannot be
@ -84,15 +84,16 @@ not evaluated by model BU-190
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
TaskHandle_t IF482Task; #define IF482_FRAME_SIZE (17)
#define IF482_PULSE_DURATION (1000)
HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS)
// initialize and configure IF482 Generator // initialize and configure IF482 Generator
int if482_init(void) { int if482_init(void) {
// setup external interupt for active low RTC INT pin // open serial interface
pinMode(RTC_INT, INPUT_PULLUP); IF482.begin(HAS_IF482);
// start if482 serial output feed task // start if482 serial output feed task
xTaskCreatePinnedToCore(if482_loop, // task function xTaskCreatePinnedToCore(if482_loop, // task function
@ -100,49 +101,26 @@ int if482_init(void) {
2048, // stack size of task 2048, // stack size of task
(void *)1, // parameter of the task (void *)1, // parameter of the task
3, // priority of the task 3, // priority of the task
&IF482Task, // task handle &ClockTask, // task handle
0); // CPU core 0); // CPU core
assert(IF482Task); // has if482loop task started? assert(ClockTask); // has clock task started?
// open serial interface #if defined RTC_INT && (RTC_CLK == IF482_PULSE_DURATION)
IF482.begin(HAS_IF482); pps_init(); // use pps clock
// if we have hardware pps signal we use it as precise time base
#ifdef RTC_INT
// assure we know clock freq
#ifndef RTC_CLK
#error "No RTC clock cycle defined in board hal file"
#endif
// use external rtc 1Hz clock for triggering IF482 telegram
if (I2C_MUTEX_LOCK()) {
Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
I2C_MUTEX_UNLOCK();
} else {
ESP_LOGE(TAG, "I2c bus busy - IF482 initialization error");
return 0; // failure
}
attachInterrupt(digitalPinToInterrupt(RTC_INT), IF482IRQ, FALLING);
// no RTC, thus we use less precise ESP32 hardware timer
#else #else
// setup 1000ms clock signal for IF482 generator using esp32 hardware timer 1 pps_init(IF482_PULSE_DURATION); // use esp32 clock
ESP_LOGD(TAG, "Starting IF482 pulse...");
dcfCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec
timerAttachInterrupt(dcfCycle, &IF482IRQ, true);
timerAlarmWrite(dcfCycle, 10000, true); // 1000ms cycle
timerAlarmEnable(dcfCycle);
#endif #endif
pps_start(); // start pulse
return 1; // success return 1; // success
} // if482_init } // if482_init
String IF482_Out(time_t tt) { String IF482_Out(time_t tt) {
time_t t = myTZ.toLocal(tt); time_t t = myTZ.toLocal(tt);
char mon, buf[14], out[17]; char mon, buf[14], out[IF482_FRAME_SIZE];
switch (timeStatus()) { // indicates if time has been set and recently synced switch (timeStatus()) { // indicates if time has been set and recently synced
case timeSet: // time is set and is synced case timeSet: // time is set and is synced
@ -179,12 +157,7 @@ void if482_loop(void *pvParameters) {
pdMS_TO_TICKS(IF482_OFFSET); // duration of telegram transmit pdMS_TO_TICKS(IF482_OFFSET); // duration of telegram transmit
const TickType_t startTime = xTaskGetTickCount(); // now const TickType_t startTime = xTaskGetTickCount(); // now
// wait until begin of a new second sync_clock(now()); // wait until begin of a new second
t = tt = now();
do {
tt = now();
} while (t == tt);
BitsPending = true; // start blink in display BitsPending = true; // start blink in display
// take timestamp at moment of start of new second // take timestamp at moment of start of new second
@ -198,17 +171,18 @@ void if482_loop(void *pvParameters) {
&wakeTime, // receives moment of call from isr &wakeTime, // receives moment of call from isr
portMAX_DELAY); // wait forever (missing error handling here...) portMAX_DELAY); // wait forever (missing error handling here...)
#if !defined RTC_CLK || (RTC_CLK == IF482_PULSE_DURATION) // we don't need clock rescaling
// now we're synced to start of second tt and wait // now we're synced to start of second tt and wait
// until it's time to start transmit telegram for tt+1 // until it's time to start transmit telegram for tt+1
vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot
IF482.print(IF482_Out(now() + 1)); IF482.print(IF482_Out(now() + 1));
#else // we need clock rescaling by software timer
/*
not yet implemented for IF482
*/
#endif
} }
} // if482_loop() } // if482_loop()
// interrupt service routine triggered by RTC 1Hz precise clock
void IRAM_ATTR IF482IRQ() {
xTaskNotifyFromISR(IF482Task, xTaskGetTickCountFromISR(), eSetBits, NULL);
portYIELD_FROM_ISR();
}
#endif // HAS_IF482 #endif // HAS_IF482

View File

@ -7,6 +7,9 @@ static const char TAG[] = "main";
RtcDS3231<TwoWire> Rtc(Wire); // RTC hardware i2c interface RtcDS3231<TwoWire> Rtc(Wire); // RTC hardware i2c interface
TaskHandle_t ClockTask;
hw_timer_t *clockCycle = NULL;
// initialize RTC // initialize RTC
int rtc_init(void) { int rtc_init(void) {
@ -97,4 +100,65 @@ float get_rtctemp(void) {
return 0; return 0;
} // get_rtctemp() } // get_rtctemp()
int pps_init() {
// we have hardware pps signal as time base
#if defined RTC_INT && defined RTC_CLK
// setup external interupt for active low RTC INT pin
pinMode(RTC_INT, INPUT_PULLUP);
// setup external rtc 1Hz clock as pulse per second clock
ESP_LOGI(TAG, "Time base external clock");
if (I2C_MUTEX_LOCK()) {
Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
I2C_MUTEX_UNLOCK();
} else {
ESP_LOGE(TAG, "I2c bus busy - RTC initialization error");
return 0; // failure
}
return 1; // success
#endif
}
int pps_init(uint32_t pps_freq) {
// if we don't have hardware pps we use ESP32 hardware timer
if (pps_freq) {
ESP_LOGI(TAG, "Time base ESP32 clock");
clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler
timerAttachInterrupt(clockCycle, &CLOCKIRQ, true);
timerAlarmWrite(clockCycle, 10 * pps_freq, true);
} else {
ESP_LOGE(TAG, "Invalid pps clock frequency");
return 0; // failure
}
return 1; // success
}
void pps_start() {
#ifdef RTC_INT // start external clock
attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING);
#else // start internal clock
timerAlarmEnable(clockCycle);
#endif
}
// helper function to sync phase of DCF output signal to start of second t
uint8_t sync_clock(time_t t) {
time_t tt = t;
// delay until start of next second
do {
tt = now();
} while (t == tt);
ESP_LOGI(TAG, "Sync on Sec %d", second(tt));
return second(tt);
}
// interrupt service routine triggered by either rtc pps or esp32 hardware
// timer
void IRAM_ATTR CLOCKIRQ() {
xTaskNotifyFromISR(ClockTask, xTaskGetTickCountFromISR(), eSetBits, NULL);
portYIELD_FROM_ISR();
}
#endif // HAS_RTC #endif // HAS_RTC