sntp: fix the handling of the rtc counter wrapping
This reworks the accounting of the time to address the wrapping of the rtc counter. It currently assumes that the time is accessed more frequently than the counter would wrap. It also adds a semaphore to synchronize access to the time storage.
This commit is contained in:
parent
15964efc0f
commit
59ee3d5975
2 changed files with 63 additions and 62 deletions
|
@ -61,10 +61,9 @@ int sntp_set_servers(char *server_url[], int num_servers);
|
||||||
void sntp_set_update_delay(uint32_t ms);
|
void sntp_set_update_delay(uint32_t ms);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the time read from RTC counter, in seconds from Epoch. If
|
* Returns the time read from RTC counter, in micro seconds from Epoch.
|
||||||
* us is not null, it will be filled with the microseconds.
|
|
||||||
*/
|
*/
|
||||||
time_t sntp_get_rtc_time(int32_t *us);
|
uint64_t sntp_get_rtc_time();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update RTC timer. This function is called by the SNTP module each time
|
* Update RTC timer. This function is called by the SNTP module each time
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#include <espressif/esp_common.h>
|
#include <espressif/esp_common.h>
|
||||||
#include <esp/timer.h>
|
#include <esp/timer.h>
|
||||||
#include <esp/rtc_regs.h>
|
#include <esp/rtc_regs.h>
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "semphr.h"
|
||||||
#include "sntp.h"
|
#include "sntp.h"
|
||||||
|
|
||||||
#define TIMER_COUNT RTC.COUNTER
|
#define TIMER_COUNT RTC.COUNTER
|
||||||
|
@ -18,11 +20,14 @@
|
||||||
// daylight settings
|
// daylight settings
|
||||||
// Base calculated with value obtained from NTP server (64 bits)
|
// Base calculated with value obtained from NTP server (64 bits)
|
||||||
#define sntp_base (*((uint64_t*)RTC.SCRATCH))
|
#define sntp_base (*((uint64_t*)RTC.SCRATCH))
|
||||||
// Timer value when base was obtained
|
// Timer value when sntp_base was obtained
|
||||||
#define tim_ref (RTC.SCRATCH[2])
|
#define tim_ref (RTC.SCRATCH[2])
|
||||||
// Calibration value
|
// Calibration value
|
||||||
#define cal (RTC.SCRATCH[3])
|
#define cal (RTC.SCRATCH[3])
|
||||||
|
|
||||||
|
// To protect access to the above.
|
||||||
|
static xSemaphoreHandle sntp_sem = NULL;
|
||||||
|
|
||||||
// Timezone related data.
|
// Timezone related data.
|
||||||
static struct timezone stz;
|
static struct timezone stz;
|
||||||
|
|
||||||
|
@ -52,36 +57,24 @@ void sntp_initialize(const struct timezone *tz) {
|
||||||
// To avoid div by 0 exceptions if requesting time before SNTP config
|
// To avoid div by 0 exceptions if requesting time before SNTP config
|
||||||
cal = 1;
|
cal = 1;
|
||||||
tim_ref = TIMER_COUNT;
|
tim_ref = TIMER_COUNT;
|
||||||
|
vSemaphoreCreateBinary(sntp_sem);
|
||||||
sntp_init();
|
sntp_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a timer wrap has occurred. Compensate sntp_base reference
|
// Return usecs.
|
||||||
// if affirmative.
|
inline uint64_t sntp_get_rtc_time() {
|
||||||
// TODO: think about multitasking and race conditions
|
xSemaphoreTake(sntp_sem, portMAX_DELAY);
|
||||||
static inline void sntp_check_timer_wrap(uint32_t current_value) {
|
uint32_t tim = TIMER_COUNT;
|
||||||
if (current_value < tim_ref) {
|
// Assume the difference does not overflow in which case
|
||||||
// Timer wrap has occurred, compensate by subtracting 2^32 to ref.
|
// wrapping of the RTC timer still yields a good difference.
|
||||||
sntp_base -= 1LLU<<32;
|
uint32_t diff = tim - tim_ref;
|
||||||
// DEBUG
|
tim_ref = tim;
|
||||||
printf("\nTIMER WRAPPED!\n");
|
uint64_t diff_us = ((uint64_t)diff * cal) >> 12;
|
||||||
}
|
uint64_t base = sntp_base + diff_us;
|
||||||
}
|
sntp_base = base;
|
||||||
|
xSemaphoreGive(sntp_sem);
|
||||||
|
|
||||||
// Return secs. If us is not a null pointer, fill it with usecs
|
return base;
|
||||||
inline time_t sntp_get_rtc_time(int32_t *us) {
|
|
||||||
time_t secs;
|
|
||||||
uint32_t tim;
|
|
||||||
uint64_t base;
|
|
||||||
|
|
||||||
tim = TIMER_COUNT;
|
|
||||||
// Check for timer wrap
|
|
||||||
sntp_check_timer_wrap(tim);
|
|
||||||
base = sntp_base + tim - tim_ref;
|
|
||||||
secs = base * cal / (1000000U<<12);
|
|
||||||
if (us) {
|
|
||||||
*us = base * cal % (1000000U<<12);
|
|
||||||
}
|
|
||||||
return secs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Syscall implementation. doesn't seem to use tzp.
|
// Syscall implementation. doesn't seem to use tzp.
|
||||||
|
@ -91,7 +84,10 @@ int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) {
|
||||||
// So it looks like it is not used. Also check tp is not NULL
|
// So it looks like it is not used. Also check tp is not NULL
|
||||||
if (tzp || !tp) return EINVAL;
|
if (tzp || !tp) return EINVAL;
|
||||||
|
|
||||||
tp->tv_sec = sntp_get_rtc_time((int32_t*)&tp->tv_usec);
|
uint64_t base = sntp_get_rtc_time();
|
||||||
|
|
||||||
|
tp->tv_sec = base / 1000000U;
|
||||||
|
tp->tv_usec = base % 1000000U;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,14 +95,20 @@ int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) {
|
||||||
void sntp_update_rtc(time_t t, uint32_t us) {
|
void sntp_update_rtc(time_t t, uint32_t us) {
|
||||||
// Apply daylight and timezone correction
|
// Apply daylight and timezone correction
|
||||||
t += (stz.tz_minuteswest + stz.tz_dsttime * 60) * 60;
|
t += (stz.tz_minuteswest + stz.tz_dsttime * 60) * 60;
|
||||||
// DEBUG: Compute and print drift
|
int64_t sntp_correct = (uint64_t)us + (uint64_t)t * 1000000U;
|
||||||
int64_t sntp_current = sntp_base + TIMER_COUNT - tim_ref;
|
|
||||||
int64_t sntp_correct = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / cal;
|
|
||||||
printf("\nRTC Adjust: drift = %ld ticks, cal = %d\n", (time_t)(sntp_correct - sntp_current), cal);
|
|
||||||
|
|
||||||
tim_ref = TIMER_COUNT;
|
xSemaphoreTake(sntp_sem, portMAX_DELAY);
|
||||||
|
uint32_t tim = TIMER_COUNT;
|
||||||
|
// Assume the difference does not overflow in which case
|
||||||
|
// wrapping of the RTC timer still yields a good difference.
|
||||||
|
uint32_t diff = tim - tim_ref;
|
||||||
|
tim_ref = tim;
|
||||||
|
uint64_t diff_us = ((uint64_t)diff * cal) >> 12;
|
||||||
|
uint64_t sntp_current = sntp_base + diff_us;
|
||||||
|
sntp_base = sntp_correct;
|
||||||
cal = sdk_system_rtc_clock_cali_proc();
|
cal = sdk_system_rtc_clock_cali_proc();
|
||||||
|
xSemaphoreGive(sntp_sem);
|
||||||
|
|
||||||
sntp_base = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / cal;
|
printf("\nRTC Adjust: drift = %d usec, cal = %d\n", (int)(sntp_correct - sntp_current), cal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue