DCF77 improvements
This commit is contained in:
parent
3c913095a2
commit
f5e5bf798a
@ -51,7 +51,7 @@ Depending on board hardware following features are supported:
|
|||||||
- GPS (Generic serial NMEA, or Quectel L76 I2C)
|
- GPS (Generic serial NMEA, or Quectel L76 I2C)
|
||||||
- Environmental sensor (Bosch BME680 I2C)
|
- Environmental sensor (Bosch BME680 I2C)
|
||||||
- Real Time Clock (Maxim DS3231 I2C)
|
- Real Time Clock (Maxim DS3231 I2C)
|
||||||
- IF482 time telegram generator (serial port)
|
- IF482 (serial) and DCF77 (gpio) time telegram generator
|
||||||
|
|
||||||
Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).<br>
|
Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).<br>
|
||||||
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory. If you want to use a ESP32 board which is not yet supported, use hal file generic.h and tailor pin mappings to your needs. Pull requests for new boards welcome.<br>
|
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory. If you want to use a ESP32 board which is not yet supported, use hal file generic.h and tailor pin mappings to your needs. Pull requests for new boards welcome.<br>
|
||||||
@ -138,6 +138,10 @@ Paxcounter generates identifiers for sniffed MAC adresses and collects them temp
|
|||||||
- Red long blink: LoRaWAN stack error
|
- Red long blink: LoRaWAN stack error
|
||||||
- White long blink: Known Beacon detected
|
- White long blink: Known Beacon detected
|
||||||
|
|
||||||
|
# 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).
|
||||||
|
|
||||||
# Payload format
|
# Payload format
|
||||||
|
|
||||||
You can select different payload formats in [paxcounter.conf](src/paxcounter.conf#L12):
|
You can select different payload formats in [paxcounter.conf](src/paxcounter.conf#L12):
|
||||||
|
@ -19,7 +19,8 @@ static const char TAG[] = "main";
|
|||||||
TaskHandle_t DCF77Task;
|
TaskHandle_t DCF77Task;
|
||||||
hw_timer_t *dcfCycle = NULL;
|
hw_timer_t *dcfCycle = NULL;
|
||||||
|
|
||||||
#define DCF77_FRAME_SIZE 60
|
#define DCF77_FRAME_SIZE (60)
|
||||||
|
#define DCF77_PULSE_DURATION (100)
|
||||||
|
|
||||||
// array of dcf pulses for three minutes
|
// array of dcf pulses for three minutes
|
||||||
uint8_t DCFtimeframe[DCF77_FRAME_SIZE];
|
uint8_t DCFtimeframe[DCF77_FRAME_SIZE];
|
||||||
@ -43,14 +44,38 @@ int dcf77_init(void) {
|
|||||||
|
|
||||||
assert(DCF77Task); // has dcf77 task started?
|
assert(DCF77Task); // has dcf77 task started?
|
||||||
|
|
||||||
// setup 100ms clock signal for DCF77 generator using esp32 hardware timer 1
|
// if we have hardware pps signal we use it as precise time base
|
||||||
ESP_LOGD(TAG, "Starting DCF pulse...");
|
#ifdef RTC_INT
|
||||||
|
|
||||||
|
#ifndef RTC_CLK // assure we know external clock freq
|
||||||
|
#error "External clock cycle not defined in board hal file"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// setup external interupt for active low RTC INT pin
|
||||||
|
pinMode(RTC_INT, INPUT_PULLUP);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we don't have pps signal from RTC we emulate it using ESP32 hardware timer
|
||||||
|
#else
|
||||||
|
#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
|
dcfCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec
|
||||||
timerAttachInterrupt(dcfCycle, &DCF77IRQ, true);
|
timerAttachInterrupt(dcfCycle, &DCF77IRQ, true);
|
||||||
timerAlarmWrite(dcfCycle, 1000, true); // 100ms cycle
|
timerAlarmWrite(dcfCycle, 10 * RTC_CLK, true); // RTC_CLK / 1sec = 100ms
|
||||||
|
#endif
|
||||||
|
|
||||||
// wait until beginning of next second, then kick off first DCF pulse and
|
// wait until beginning of next second, then kick off first DCF pulse and
|
||||||
// start timer interrupt
|
// start clock signal
|
||||||
|
|
||||||
t = tt = now();
|
t = tt = now();
|
||||||
do {
|
do {
|
||||||
@ -58,10 +83,14 @@ int dcf77_init(void) {
|
|||||||
} while (t == tt);
|
} while (t == tt);
|
||||||
|
|
||||||
DCF_Out(second(tt));
|
DCF_Out(second(tt));
|
||||||
|
|
||||||
|
#ifdef RTC_INT // start external clock
|
||||||
|
attachInterrupt(digitalPinToInterrupt(RTC_INT), DCF77IRQ, FALLING);
|
||||||
|
#else // start internal clock
|
||||||
timerAlarmEnable(dcfCycle);
|
timerAlarmEnable(dcfCycle);
|
||||||
|
#endif
|
||||||
|
|
||||||
return 1; // success
|
return 1; // success
|
||||||
|
|
||||||
} // ifdcf77_init
|
} // ifdcf77_init
|
||||||
|
|
||||||
void generateTimeframe(time_t tt) {
|
void generateTimeframe(time_t tt) {
|
||||||
@ -113,9 +142,9 @@ void generateTimeframe(time_t tt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// called every 100msec by hardware timer to pulse out DCF signal
|
// called every 100msec by hardware timer to pulse out DCF signal
|
||||||
void DCF_Out(uint8_t startsec) {
|
void DCF_Out(uint8_t startOffset) {
|
||||||
|
|
||||||
static uint8_t bit = startsec;
|
static uint8_t bit = startOffset;
|
||||||
static uint8_t pulse = 0;
|
static uint8_t pulse = 0;
|
||||||
|
|
||||||
if (!BitsPending) {
|
if (!BitsPending) {
|
||||||
@ -143,7 +172,7 @@ void DCF_Out(uint8_t startsec) {
|
|||||||
set_DCF77_pin(dcf_high);
|
set_DCF77_pin(dcf_high);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 9: // last pulse before next second starts
|
case 9: // 900ms after start -> last pulse before next second starts
|
||||||
pulse = 0;
|
pulse = 0;
|
||||||
if (bit++ == (DCF77_FRAME_SIZE - 1)) // end of DCF77 frame (59th second)
|
if (bit++ == (DCF77_FRAME_SIZE - 1)) // end of DCF77 frame (59th second)
|
||||||
{
|
{
|
||||||
@ -154,31 +183,33 @@ void DCF_Out(uint8_t startsec) {
|
|||||||
|
|
||||||
}; // switch
|
}; // switch
|
||||||
}; // if
|
}; // if
|
||||||
|
|
||||||
} // DCF_Out()
|
} // DCF_Out()
|
||||||
|
|
||||||
// interrupt service routine triggered each 100ms by ESP32 hardware timer
|
|
||||||
void IRAM_ATTR DCF77IRQ() {
|
|
||||||
xTaskNotifyFromISR(DCF77Task, 0, eNoAction, NULL);
|
|
||||||
portYIELD_FROM_ISR();
|
|
||||||
}
|
|
||||||
|
|
||||||
void dcf77_loop(void *pvParameters) {
|
void dcf77_loop(void *pvParameters) {
|
||||||
|
|
||||||
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
||||||
|
|
||||||
|
TickType_t wakeTime;
|
||||||
|
|
||||||
// task remains in blocked state until it is notified by isr
|
// task remains in blocked state until it is notified by isr
|
||||||
for (;;) {
|
for (;;) {
|
||||||
xTaskNotifyWait(
|
xTaskNotifyWait(
|
||||||
0x00, // don't clear any bits on entry
|
0x00, // don't clear any bits on entry
|
||||||
ULONG_MAX, // clear all bits on exit
|
ULONG_MAX, // clear all bits on exit
|
||||||
NULL,
|
&wakeTime, // receives moment of call from isr
|
||||||
portMAX_DELAY); // wait forever (missing error handling here...)
|
portMAX_DELAY); // wait forever (missing error handling here...)
|
||||||
|
|
||||||
DCF_Out(0);
|
#if (!defined RTC_INT) || (RTC_CLK == DCF77_PULSE_DURATION)
|
||||||
}
|
DCF_Out(0); // we don't need clock rescaling
|
||||||
BitsPending = false; // stop blink in display, should never be reached
|
|
||||||
vTaskDelete(DCF77Task);
|
#else // we need clock rescaling by software timer
|
||||||
|
for (uint8_t i = 1; i <= RTC_CLK / DCF77_PULSE_DURATION; i++) {
|
||||||
|
DCF_Out(0);
|
||||||
|
vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION));
|
||||||
|
} // for
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // for
|
||||||
} // dcf77_loop()
|
} // dcf77_loop()
|
||||||
|
|
||||||
// helper function to convert decimal to bcd digit
|
// helper function to convert decimal to bcd digit
|
||||||
@ -217,4 +248,10 @@ void set_DCF77_pin(dcf_pinstate state) {
|
|||||||
} // switch
|
} // switch
|
||||||
} // DCF77_pulse
|
} // DCF77_pulse
|
||||||
|
|
||||||
|
// 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
|
@ -51,6 +51,7 @@
|
|||||||
// Pins for on board DS3231 RTC chip
|
// Pins for on board DS3231 RTC chip
|
||||||
#define HAS_RTC MY_OLED_SDA, MY_OLED_SCL // SDA, SCL
|
#define HAS_RTC MY_OLED_SDA, MY_OLED_SCL // SDA, SCL
|
||||||
#define RTC_INT GPIO_NUM_34 // interrupt input from rtc
|
#define RTC_INT GPIO_NUM_34 // interrupt input from rtc
|
||||||
|
#define RTC_CLK (1000) // frequency of RTC clock signal in ms
|
||||||
|
|
||||||
// Settings for IF482 interface
|
// Settings for IF482 interface
|
||||||
#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters
|
#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters
|
||||||
|
@ -94,19 +94,7 @@ int if482_init(void) {
|
|||||||
// setup external interupt for active low RTC INT pin
|
// setup external interupt for active low RTC INT pin
|
||||||
pinMode(RTC_INT, INPUT_PULLUP);
|
pinMode(RTC_INT, INPUT_PULLUP);
|
||||||
|
|
||||||
// open serial interface
|
// start if482 serial output feed task
|
||||||
IF482.begin(HAS_IF482);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
xTaskCreatePinnedToCore(if482_loop, // task function
|
xTaskCreatePinnedToCore(if482_loop, // task function
|
||||||
"if482loop", // name of task
|
"if482loop", // name of task
|
||||||
2048, // stack size of task
|
2048, // stack size of task
|
||||||
@ -116,13 +104,42 @@ int if482_init(void) {
|
|||||||
0); // CPU core
|
0); // CPU core
|
||||||
|
|
||||||
assert(IF482Task); // has if482loop task started?
|
assert(IF482Task); // has if482loop task started?
|
||||||
|
|
||||||
|
// open serial interface
|
||||||
|
IF482.begin(HAS_IF482);
|
||||||
|
|
||||||
|
// 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);
|
attachInterrupt(digitalPinToInterrupt(RTC_INT), IF482IRQ, FALLING);
|
||||||
|
|
||||||
return 1;
|
// no RTC, thus we use less precise ESP32 hardware timer
|
||||||
|
#else
|
||||||
|
// setup 1000ms clock signal for IF482 generator using esp32 hardware timer 1
|
||||||
|
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
|
||||||
|
|
||||||
|
return 1; // success
|
||||||
|
|
||||||
} // if482_init
|
} // if482_init
|
||||||
|
|
||||||
String if482Telegram(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[17];
|
||||||
@ -184,10 +201,8 @@ void if482_loop(void *pvParameters) {
|
|||||||
// 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(if482Telegram(now() + 1));
|
IF482.print(IF482_Out(now() + 1));
|
||||||
}
|
}
|
||||||
BitsPending = false; // stop blink in display
|
|
||||||
vTaskDelete(IF482Task); // shoud never be reached
|
|
||||||
} // if482_loop()
|
} // if482_loop()
|
||||||
|
|
||||||
// interrupt service routine triggered by RTC 1Hz precise clock
|
// interrupt service routine triggered by RTC 1Hz precise clock
|
||||||
|
@ -418,7 +418,7 @@ void setup() {
|
|||||||
|
|
||||||
#if (defined HAS_IF482) && (defined DCF_77)
|
#if (defined HAS_IF482) && (defined DCF_77)
|
||||||
#error "You may define at most one of HAS_IF482 or DCF_77"
|
#error "You may define at most one of HAS_IF482 or DCF_77"
|
||||||
#elif (defined HAS_IF482) && (defined RTC_INT)
|
#elif defined HAS_IF482
|
||||||
ESP_LOGI(TAG, "Starting IF482 Generator...");
|
ESP_LOGI(TAG, "Starting IF482 Generator...");
|
||||||
assert(if482_init());
|
assert(if482_init());
|
||||||
#elif defined HAS_DCF77
|
#elif defined HAS_DCF77
|
||||||
|
Loading…
Reference in New Issue
Block a user