GPS IRQ added to increase gps time sync accuracy

This commit is contained in:
Verkehrsrot 2019-04-14 22:54:27 +02:00
parent 4824e2c096
commit 359b31e9e9
9 changed files with 68 additions and 33 deletions

View File

@ -83,6 +83,7 @@ typedef struct {
uint8_t satellites; uint8_t satellites;
uint16_t hdop; uint16_t hdop;
int16_t altitude; int16_t altitude;
time_t utctime;
} gpsStatus_t; } gpsStatus_t;
typedef struct { typedef struct {
@ -111,12 +112,13 @@ extern uint16_t volatile macs_total, macs_wifi, macs_ble,
batt_voltage; // display values batt_voltage; // display values
extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC
extern timesource_t timeSource; extern timesource_t timeSource;
extern hw_timer_t *displayIRQ, *ppsIRQ; extern hw_timer_t *displayIRQ, *ppsIRQ, *gpsIRQ;
extern SemaphoreHandle_t I2Caccess; extern SemaphoreHandle_t I2Caccess;
extern TaskHandle_t irqHandlerTask, ClockTask; extern TaskHandle_t irqHandlerTask, ClockTask;
extern TimerHandle_t WifiChanTimer; extern TimerHandle_t WifiChanTimer;
extern Timezone myTZ; extern Timezone myTZ;
extern time_t userUTCTime; extern time_t userUTCTime;
extern time_t volatile gps_pps_time;
// application includes // application includes
#include "led.h" #include "led.h"

View File

@ -8,7 +8,7 @@
#define TIMESYNC_IRQ 0x10 #define TIMESYNC_IRQ 0x10
#define MASK_IRQ 0x20 #define MASK_IRQ 0x20
#define UNMASK_IRQ 0x40 #define UNMASK_IRQ 0x40
#define RESERVED_IRQ 0x80 #define GPS_IRQ 0x80
#include "globals.h" #include "globals.h"
#include "cyclic.h" #include "cyclic.h"
@ -27,4 +27,8 @@ void IRAM_ATTR DisplayIRQ();
void IRAM_ATTR ButtonIRQ(); void IRAM_ATTR ButtonIRQ();
#endif #endif
#if (HAS_GPS)
void IRAM_ATTR GpsIRQ();
#endif
#endif #endif

View File

@ -11,9 +11,10 @@ TaskHandle_t GpsTask;
#ifdef GPS_SERIAL #ifdef GPS_SERIAL
HardwareSerial GPS_Serial(1); // use UART #1 HardwareSerial GPS_Serial(1); // use UART #1
static TickType_t gps_txDelay = tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL); static uint16_t nmea_txDelay_ms =
tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL) / portTICK_PERIOD_MS;
#else #else
static TickType_t gps_txDelay = 0; static uint16_t nmea_txDelay_ms = 0;
#endif #endif
// initialize and configure GPS // initialize and configure GPS
@ -71,37 +72,24 @@ void gps_read() {
gps_status.satellites = (uint8_t)gps.satellites.value(); gps_status.satellites = (uint8_t)gps.satellites.value();
gps_status.hdop = (uint16_t)gps.hdop.value(); gps_status.hdop = (uint16_t)gps.hdop.value();
gps_status.altitude = (int16_t)gps.altitude.meters(); gps_status.altitude = (int16_t)gps.altitude.meters();
gps_status.utctime = get_gpstime();
// show NMEA data in debug mode, useful for debugging GPS // show NMEA data in debug mode, useful for debugging GPS
ESP_LOGD(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d", ESP_LOGV(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d",
gps.passedChecksum(), gps.failedChecksum(), gps.sentencesWithFix()); gps.passedChecksum(), gps.failedChecksum(), gps.sentencesWithFix());
} }
// function to fetch current time from gps // function to fetch current time from gps
time_t get_gpstime(void) { time_t get_gpstime(void) {
// set time to wait for arrive next recent NMEA time record
static const uint32_t gpsDelay_ms = 1000 - gps_txDelay / portTICK_PERIOD_MS;
time_t t = 0; time_t t = 0;
uint32_t time_age = gps.time.age(); uint32_t time_age = gps.time.age();
if ((time_age < gpsDelay_ms) && gps.time.isValid() && gps.date.isValid() && if (gps.time.isValid() && gps.date.isValid() && (time_age < 1000))
gps.time.isUpdated()) {
t = tmConvert(gps.date.year(), gps.date.month(), gps.date.day(), t = tmConvert(gps.date.year(), gps.date.month(), gps.date.day(),
gps.time.hour(), gps.time.minute(), gps.time.second()); gps.time.hour(), gps.time.minute(), gps.time.second());
if (time_age < (gpsDelay_ms / 2)) return timeIsValid(time_age > nmea_txDelay_ms ? t : t - 1);
t--;
ESP_LOGD(TAG, "GPS time age: %dms", time_age);
#ifndef GPS_INT
// wait until top of second with millisecond precision
vTaskDelay(pdMS_TO_TICKS(1000 - time_age) - gps_txDelay);
#endif
}
return timeIsValid(t);
} // get_gpstime() } // get_gpstime()
// GPS serial feed FreeRTos Task // GPS serial feed FreeRTos Task

View File

@ -39,6 +39,12 @@ void irqHandler(void *pvParameters) {
refreshtheDisplay(); refreshtheDisplay();
#endif #endif
// gps refresh buffer?
#if (HAS_GPS)
if (InterruptStatus & GPS_IRQ)
gps_read();
#endif
// are cyclic tasks due? // are cyclic tasks due?
if (InterruptStatus & CYCLIC_IRQ) if (InterruptStatus & CYCLIC_IRQ)
doHousekeeping(); doHousekeeping();
@ -47,7 +53,6 @@ void irqHandler(void *pvParameters) {
// is time to be synced? // is time to be synced?
if (InterruptStatus & TIMESYNC_IRQ) { if (InterruptStatus & TIMESYNC_IRQ) {
time_t t = timeProvider(); time_t t = timeProvider();
ESP_LOGD(TAG, "Sync time = %d", t);
if (timeIsValid(t)) if (timeIsValid(t))
setTime(t); setTime(t);
} }
@ -85,6 +90,17 @@ void IRAM_ATTR ButtonIRQ() {
} }
#endif #endif
#if (HAS_GPS)
void IRAM_ATTR GpsIRQ() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(irqHandlerTask, GPS_IRQ, eSetBits,
&xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR();
}
#endif
int mask_user_IRQ() { int mask_user_IRQ() {
// begin of time critical section: lock I2C bus to ensure accurate timing // begin of time critical section: lock I2C bus to ensure accurate timing
if (!I2C_MUTEX_LOCK()) if (!I2C_MUTEX_LOCK())

View File

@ -51,7 +51,7 @@ So don't do it if you do not own a digital oscilloscope.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
0 displayIRQ -> display refresh -> 40ms (DISPLAYREFRESH_MS) 0 displayIRQ -> display refresh -> 40ms (DISPLAYREFRESH_MS)
1 ppsIRQ -> pps clock irq -> 1sec 1 ppsIRQ -> pps clock irq -> 1sec
2 unused 2 gpsIRQ -> gps store data -> 500ms
3 unused 3 unused
@ -61,6 +61,7 @@ So don't do it if you do not own a digital oscilloscope.
fired by hardware fired by hardware
DisplayIRQ -> esp32 timer 0 -> irqHandlerTask (Core 1) DisplayIRQ -> esp32 timer 0 -> irqHandlerTask (Core 1)
CLOCKIRQ -> esp32 timer 1 -> ClockTask (Core 1) CLOCKIRQ -> esp32 timer 1 -> ClockTask (Core 1)
GpsIRQ -> esp32 timer 2 -> irqHandlerTask (Core 1)
ButtonIRQ -> external gpio -> irqHandlerTask (Core 1) ButtonIRQ -> external gpio -> irqHandlerTask (Core 1)
fired by software (Ticker.h) fired by software (Ticker.h)
@ -84,11 +85,12 @@ uint8_t volatile channel = 0; // channel rotation counter
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
batt_voltage = 0; // globals for display batt_voltage = 0; // globals for display
hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL; hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL, *gpsIRQ = NULL;
TaskHandle_t irqHandlerTask = NULL, ClockTask = NULL; TaskHandle_t irqHandlerTask = NULL, ClockTask = NULL;
SemaphoreHandle_t I2Caccess; SemaphoreHandle_t I2Caccess;
bool volatile TimePulseTick = false; bool volatile TimePulseTick = false;
time_t volatile gps_pps_time = 0;
time_t userUTCTime = 0; time_t userUTCTime = 0;
timesource_t timeSource = _unsynced; timesource_t timeSource = _unsynced;
@ -408,7 +410,15 @@ void setup() {
timerAlarmEnable(displayIRQ); timerAlarmEnable(displayIRQ);
#endif #endif
// cyclic function interrupts // gps buffer read interrupt
#if (HAS_GPS)
gpsIRQ = timerBegin(2, 80, true);
timerAttachInterrupt(gpsIRQ, &GpsIRQ, true);
timerAlarmWrite(gpsIRQ, 500 * 1000, true);
timerAlarmEnable(gpsIRQ);
#endif
// cyclic function interrupts
sendcycler.attach(SENDCYCLE * 2, sendcycle); sendcycler.attach(SENDCYCLE * 2, sendcycle);
housekeeper.attach(HOMECYCLE, housekeeping); housekeeper.attach(HOMECYCLE, housekeeping);

View File

@ -253,7 +253,6 @@ void get_status(uint8_t val[]) {
void get_gps(uint8_t val[]) { void get_gps(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get gps status"); ESP_LOGI(TAG, "Remote command: get gps status");
#if(HAS_GPS) #if(HAS_GPS)
gps_read();
payload.reset(); payload.reset();
payload.addGPS(gps_status); payload.addGPS(gps_status);
SendPayload(GPSPORT, prio_high); SendPayload(GPSPORT, prio_high);

View File

@ -87,7 +87,6 @@ void sendCounter() {
case GPS_DATA: case GPS_DATA:
// send GPS position only if we have a fix // send GPS position only if we have a fix
if (gps.location.isValid()) { if (gps.location.isValid()) {
gps_read();
payload.reset(); payload.reset();
payload.addGPS(gps_status); payload.addGPS(gps_status);
SendPayload(GPSPORT, prio_high); SendPayload(GPSPORT, prio_high);

View File

@ -27,7 +27,7 @@ time_t timeProvider(void) {
time_t t = 0; time_t t = 0;
#if (HAS_GPS) #if (HAS_GPS)
t = get_gpstime(); // fetch recent time from last NEMA record t = gps_pps_time; // fetch recent time from last NEMA record
if (t) { if (t) {
#ifdef HAS_RTC #ifdef HAS_RTC
set_rtctime(t, do_mutex); // calibrate RTC set_rtctime(t, do_mutex); // calibrate RTC
@ -106,6 +106,7 @@ uint8_t timepulse_init() {
} // timepulse_init } // timepulse_init
void timepulse_start(void) { void timepulse_start(void) {
#ifdef GPS_INT // start external clock gps pps line #ifdef GPS_INT // start external clock gps pps line
attachInterrupt(digitalPinToInterrupt(GPS_INT), CLOCKIRQ, RISING); attachInterrupt(digitalPinToInterrupt(GPS_INT), CLOCKIRQ, RISING);
#elif defined RTC_INT // start external clock rtc #elif defined RTC_INT // start external clock rtc
@ -114,9 +115,14 @@ void timepulse_start(void) {
timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, true); timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, true);
timerAlarmEnable(ppsIRQ); timerAlarmEnable(ppsIRQ);
#endif #endif
now(); // refresh sysTime to pps
#if (HAS_GPS)
gps_read();
gps_pps_time = gps_status.utctime;
#endif
// start cyclic time sync // start cyclic time sync
now(); // ensure sysTime is ßrecent
timeSync(); // init systime by RTC or GPS or LORA timeSync(); // init systime by RTC or GPS or LORA
timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync);
} }
@ -126,13 +132,20 @@ void IRAM_ATTR CLOCKIRQ(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
SyncToPPS(); // calibrates UTC systime and advances it +1, see microTime.h SyncToPPS(); // advance systime, see microTime.h
// store recent gps time, if we have
#if (HAS_GPS)
gps_pps_time = gps_status.utctime + 1;
#endif
// advance wall clock, if we have
#if (defined HAS_IF482 || defined HAS_DCF77) #if (defined HAS_IF482 || defined HAS_DCF77)
xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits, xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits,
&xHigherPriorityTaskWoken); &xHigherPriorityTaskWoken);
#endif #endif
// flip time pulse ticker, if needed
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
#if (defined GPS_INT || defined RTC_INT) #if (defined GPS_INT || defined RTC_INT)
TimePulseTick = !TimePulseTick; // flip pulse ticker TimePulseTick = !TimePulseTick; // flip pulse ticker

View File

@ -216,8 +216,12 @@ int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) {
// adjust system time, calibrate RTC and RTC_INT pps // adjust system time, calibrate RTC and RTC_INT pps
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec) { void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec) {
// advance time 1 sec wait time time_t time_to_set = (time_t)t_sec;
time_t time_to_set = (time_t)(t_sec + 1);
// advance time 1 sec wait time if we have no pulse clock
#if (!defined GPS_INT) && (!defined RTC_INT)
time_to_set++;
#endif
ESP_LOGD(TAG, "[%0.3f] Calculated UTC epoch time: %d.%03d sec", ESP_LOGD(TAG, "[%0.3f] Calculated UTC epoch time: %d.%03d sec",
millis() / 1000.0, time_to_set, t_msec); millis() / 1000.0, time_to_set, t_msec);