DCF77 improvements

This commit is contained in:
Klaus K Wilting 2019-02-07 07:32:55 +01:00
parent 3c913095a2
commit f5e5bf798a
5 changed files with 100 additions and 43 deletions

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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