DCF77 & IF482 improvements
This commit is contained in:
parent
e1b6d9a04c
commit
bad5805810
@ -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
|
||||||
|
|
||||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
Loading…
Reference in New Issue
Block a user