diff --git a/include/dcf77.h b/include/dcf77.h index 81c8ea97..e8b7266f 100644 --- a/include/dcf77.h +++ b/include/dcf77.h @@ -6,14 +6,18 @@ #define DCF77_FRAME_SIZE (60) #define DCF77_PULSE_LENGTH (100) -extern uint8_t DCFpulse[]; +#ifdef DCF77_ACTIVE_LOW +enum dcf_pinstate { dcf_high, dcf_low }; +#else +enum dcf_pinstate { dcf_low, dcf_high }; +#endif enum dcf_pulses { dcf_off, dcf_zero, dcf_one }; -enum dcf_pinstate { dcf_low, dcf_high }; void DCF_Pulse(time_t t); -void IRAM_ATTR DCF77_Frame(time_t t); -void set_DCF77_pin(dcf_pinstate state); -uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, uint8_t pArray[]); +uint8_t IRAM_ATTR DCF77_Frame(time_t t); +uint8_t IRAM_ATTR dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, + uint8_t pArray[]); +uint8_t IRAM_ATTR setParityBit(uint8_t p); #endif \ No newline at end of file diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 824a0b81..b8ca988e 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -16,15 +16,20 @@ https://github.com/udoklein/dcf77 // Local logging tag static const char TAG[] = "main"; -// array of dcf pulses for one minute -uint8_t DCFpulse[DCF77_FRAME_SIZE + 1]; +// array of dcf pulses for one minute, secs 0..16 and 20 are never touched, so +// we initialize them statically to avoid dumb recalculation every minute +static uint8_t DCFpulse[DCF77_FRAME_SIZE] = { + dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, + dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, + dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_one}; // triggered by 1 second timepulse to ticker out DCF signal void DCF_Pulse(time_t t) { + TickType_t startTime = xTaskGetTickCount(); uint8_t sec = second(t); - TickType_t startTime = xTaskGetTickCount(); + ESP_LOGD(TAG, "DCF77 sec %d", sec); // induce 10 pulses for (uint8_t pulse = 0; pulse <= 9; pulse++) { @@ -33,18 +38,18 @@ void DCF_Pulse(time_t t) { case 0: // start of second -> start of timeframe for logic signal if (DCFpulse[sec] != dcf_off) - set_DCF77_pin(dcf_low); + digitalWrite(HAS_DCF77, dcf_low); else // 59th second reached, nothing more to do return; break; case 1: // 100ms after start of second -> end of timeframe for logic 0 if (DCFpulse[sec] == dcf_zero) - set_DCF77_pin(dcf_high); + digitalWrite(HAS_DCF77, dcf_high); break; case 2: // 200ms after start of second -> end of timeframe for logic 1 - set_DCF77_pin(dcf_high); + digitalWrite(HAS_DCF77, dcf_high); break; case 9: // 900ms after start -> last pulse @@ -52,32 +57,31 @@ void DCF_Pulse(time_t t) { } // switch + // impulse period pause vTaskDelayUntil(&startTime, pdMS_TO_TICKS(DCF77_PULSE_LENGTH)); } // for } // DCF_Pulse() -void IRAM_ATTR DCF77_Frame(time_t tt) { +uint8_t IRAM_ATTR DCF77_Frame(time_t tt) { uint8_t Parity; time_t t = myTZ.toLocal(tt); // convert to local time - // ENCODE HEAD - // secs 0..19 initialized with zeros - for (int n = 0; n <= 19; n++) - DCFpulse[n] = dcf_zero; - // secs 17..18: adjust for DayLightSaving - DCFpulse[18 - (myTZ.locIsDST(t) ? 1 : 0)] = dcf_one; - // sec 20: must be 1 to indicate time active - DCFpulse[20] = dcf_one; + // ENCODE DST CHANGE ANNOUNCEMENT (Sec 16) + DCFpulse[16] = dcf_zero; // not yet implemented + + // ENCODE DAYLIGHTSAVING (secs 17..18) + DCFpulse[17] = myTZ.locIsDST(t) ? dcf_one : dcf_zero; + DCFpulse[18] = myTZ.locIsDST(t) ? dcf_zero : dcf_one; // ENCODE MINUTE (secs 21..28) Parity = dec2bcd(minute(t), 21, 27, DCFpulse); - DCFpulse[28] = (Parity & 1) ? dcf_one : dcf_zero; + DCFpulse[28] = setParityBit(Parity); // ENCODE HOUR (secs 29..35) Parity = dec2bcd(hour(t), 29, 34, DCFpulse); - DCFpulse[35] = (Parity & 1) ? dcf_one : dcf_zero; + DCFpulse[35] = setParityBit(Parity); // ENCODE DATE (secs 36..58) Parity = dec2bcd(day(t), 36, 41, DCFpulse); @@ -85,30 +89,24 @@ void IRAM_ATTR DCF77_Frame(time_t tt) { Parity += dec2bcd(month(t), 45, 49, DCFpulse); Parity += dec2bcd(year(t) - 2000, 50, 57, DCFpulse); // yes, we have a millenium 3000 bug here ;-) - DCFpulse[58] = (Parity & 1) ? dcf_one : dcf_zero; + DCFpulse[58] = setParityBit(Parity); - // ENCODE TAIL (sec 59) - DCFpulse[59] = dcf_off; - // !! missing code here for leap second !! + // ENCODE MARK (sec 59) + DCFpulse[59] = dcf_off; // !! missing code here for leap second !! - // timestamp the frame with minute pointer - DCFpulse[60] = minute(t); + // return the minute for which this frame is generated + return minute(t); - /* - // for debug: print the DCF77 frame buffer - char out[DCF77_FRAME_SIZE + 1]; - uint8_t i; - for (i = 0; i < DCF77_FRAME_SIZE; i++) { - out[i] = DCFpulse[i] + '0'; // convert int digit to printable ascii - } - out[DCF77_FRAME_SIZE] = '\0'; // string termination char - ESP_LOGD(TAG, "DCF minute %d = %s", DCFpulse[DCF77_FRAME_SIZE], out); - */ +} // DCF77_Frame() + +// helper function to encode parity +uint8_t IRAM_ATTR setParityBit(uint8_t p) { + return ((p & 1) ? dcf_one : dcf_zero); } // helper function to convert decimal to bcd digit uint8_t IRAM_ATTR dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, - uint8_t pArray[]) { + uint8_t pArray[]) { uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); uint8_t parity = 0; @@ -122,24 +120,4 @@ uint8_t IRAM_ATTR dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, return parity; } -// helper function to switch GPIO line with DCF77 signal -void set_DCF77_pin(dcf_pinstate state) { - switch (state) { - case dcf_low: -#ifdef DCF77_ACTIVE_LOW - digitalWrite(HAS_DCF77, HIGH); -#else - digitalWrite(HAS_DCF77, LOW); -#endif - break; - case dcf_high: -#ifdef DCF77_ACTIVE_LOW - digitalWrite(HAS_DCF77, LOW); -#else - digitalWrite(HAS_DCF77, HIGH); -#endif - break; - } // switch -} // DCF77_pulse - #endif // HAS_DCF77 \ No newline at end of file diff --git a/src/timemanager.cpp b/src/timemanager.cpp index 8d559c5b..f78f4127 100644 --- a/src/timemanager.cpp +++ b/src/timemanager.cpp @@ -62,7 +62,7 @@ int syncTime(uint32_t t) { // t is UTC time in seconds epoch // helper function to sync moment on timepulse int wait_for_pulse(void) { // sync on top of next second with 1pps timepulse - if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)) == pdTRUE) + if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1010)) == pdTRUE) return 1; // success ESP_LOGD(TAG, "Missing timepulse"); return 0; // failure @@ -190,13 +190,14 @@ void clock_loop(void *pvParameters) { // ClockTask TickType_t wakeTime; time_t t; + uint8_t current_frame; #define t1(t) (t + DCF77_FRAME_SIZE + 1) // future time for next DCF77 frame #define t2(t) (t + 1) // future time for sync with 1pps trigger // preload first DCF frame before start #ifdef HAS_DCF77 - DCF77_Frame(t1(now())); + current_frame = DCF77_Frame(t1(now())); #endif // output time telegram for second following sec beginning with timepulse @@ -216,9 +217,9 @@ void clock_loop(void *pvParameters) { // ClockTask #elif defined HAS_DCF77 if (second(t) == DCF77_FRAME_SIZE - 1) // is it time to load new frame? - DCF77_Frame(t1(t)); // generate next frame + current_frame = DCF77_Frame(t1(t)); // generate next frame - if (DCFpulse[DCF77_FRAME_SIZE] == + if (current_frame == minute(t1(t))) // have recent frame? (pulses could be missed!) DCF_Pulse(t2(t)); // then output next second of this frame