Skip to content

Commit 640ebcb

Browse files
Deomid Ryabkovcesantabot
authored andcommitted
Add mgos_uptime_micros(), fix sleep issues on CC32x and STM32
`mgos_uptime_micros()` provides a reliable, microsecond-accurate monotonic counter on all our supported platforms: * On ESP32 we have esp_timer_get_time() * On ESP8266 we have system_get_time() (plus overflow handling) * On ARM-based platforms we use FreeRTOS xTaskGetTickCount() + SYSTICK for fractional ticks CL: Add mgos_uptime_micros(), fix sleep issues on CC32x and STM32 PUBLISHED_FROM=fb548dc351b24de0f0ceb1b0f974be3b495264d3
1 parent 0b8ac8a commit 640ebcb

File tree

12 files changed

+71
-125
lines changed

12 files changed

+71
-125
lines changed

fw/include/mgos_time.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ struct timezone;
3737
/* Get number of seconds since last reboot */
3838
double mgos_uptime(void);
3939

40+
/* Get number of microseconds since last reboot */
41+
int64_t mgos_uptime_micros(void);
42+
4043
/*
4144
* Format `time` according to a `strftime()`-conformant format.
4245
* Write the result into the `s,size` buffer. Return resulting string length.

fw/platforms/cc32xx/src/cc32xx_hal.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,6 @@
3131
#include "mgos_hal.h"
3232
#include "mgos_mongoose.h"
3333

34-
/* TODO(rojer): make accurate to account for vTaskDelay inaccuracy */
35-
void mgos_usleep(uint32_t usecs) {
36-
int ticks = usecs / (1000000 / configTICK_RATE_HZ);
37-
int remainder = usecs % (1000000 / configTICK_RATE_HZ);
38-
if (ticks > 0) vTaskDelay(ticks);
39-
if (remainder > 0) MAP_UtilsDelay(remainder * (SYS_CLK / 1000000) / 3);
40-
}
41-
4234
void mgos_msleep(uint32_t msecs) {
4335
mgos_usleep(msecs * 1000);
4436
}

fw/platforms/esp32/src/esp32_hal.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,6 @@ void mgos_msleep(uint32_t msecs) {
5858
mgos_usleep(msecs * 1000);
5959
}
6060

61-
IRAM void mgos_usleep(uint32_t usecs) {
62-
uint64_t threshold = esp_timer_get_time() + (uint64_t) usecs;
63-
int ticks = usecs / (1000000 / configTICK_RATE_HZ);
64-
if (ticks > 0) vTaskDelay(ticks);
65-
while (esp_timer_get_time() < threshold) {
66-
}
67-
}
68-
6961
void mgos_wdt_feed(void) {
7062
esp_task_wdt_reset();
7163
}

fw/platforms/esp8266/src/esp_hal.c

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -73,23 +73,6 @@ void mgos_msleep(uint32_t msecs) {
7373
mgos_usleep(msecs * 1000);
7474
}
7575

76-
#ifdef RTOS_SDK
77-
IRAM void mgos_usleep(uint32_t usecs) {
78-
/*
79-
* configTICK_RATE_HZ is 100, implying 10 ms ticks.
80-
* But we run CPU at 160 and tick timer is not updated, hence / 2 below.
81-
* https://github.com/espressif/ESP8266_RTOS_SDK/issues/90
82-
*/
83-
#define USECS_PER_TICK (1000000 / configTICK_RATE_HZ / 2)
84-
uint32_t ticks = usecs / USECS_PER_TICK;
85-
usecs = usecs % USECS_PER_TICK;
86-
if (ticks > 0) vTaskDelay(ticks);
87-
os_delay_us(usecs);
88-
}
89-
#else
90-
/* Provided by linker script as an alias to ets_delay_us */
91-
#endif
92-
9376
IRAM void mgos_ints_disable(void) {
9477
__asm volatile("rsil a2, 3" : : : "a2");
9578
}

fw/platforms/esp8266/src/esp_libc.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#endif
3131

3232
#include "mgos_hal.h"
33+
#include "mgos_time.h"
3334
#include "fw/platforms/esp8266/src/esp_features.h"
3435

3536
#if MGOS_ENABLE_HEAP_LOG
@@ -134,23 +135,21 @@ void _exit(int status) {
134135
* At least Mongoose poll queries time, so we're covered.
135136
*/
136137

137-
static int64_t get_time64_micros(void) {
138+
IRAM int64_t mgos_uptime_micros(void) {
138139
static uint32_t prev_time = 0;
139140
static uint32_t num_overflows = 0;
140-
mgos_ints_disable();
141141
uint32_t time = system_get_time();
142142
int64_t time64 = time;
143143
if (prev_time > 0 && time < prev_time) num_overflows++;
144144
time64 += (((uint64_t) num_overflows) * (1ULL << 32));
145145
prev_time = time;
146-
mgos_ints_enable();
147146
return time64;
148147
}
149148

150149
static int64_t sys_time_adj = 0;
151150

152151
int _gettimeofday_r(struct _reent *r, struct timeval *tv, struct timezone *tz) {
153-
int64_t time64 = get_time64_micros() + sys_time_adj;
152+
int64_t time64 = mgos_uptime_micros() + sys_time_adj;
154153
tv->tv_sec = time64 / 1000000ULL;
155154
tv->tv_usec = time64 % 1000000ULL;
156155
return 0;
@@ -159,7 +158,7 @@ int _gettimeofday_r(struct _reent *r, struct timeval *tv, struct timezone *tz) {
159158
}
160159

161160
int settimeofday(const struct timeval *tv, const struct timezone *tz) {
162-
int64_t time64_cur = get_time64_micros();
161+
int64_t time64_cur = mgos_uptime_micros();
163162
int64_t time64_new = tv->tv_sec * 1000000LL + tv->tv_usec;
164163
sys_time_adj = (time64_new - time64_cur);
165164
(void) tz;

fw/platforms/stm32/include/stm32_system.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#pragma once
1919

20+
#include <stdint.h>
21+
2022
#ifdef __cplusplus
2123
extern "C" {
2224
#endif
@@ -26,6 +28,8 @@ void stm32_clock_config(void);
2628
void stm32_flush_caches(void);
2729
void stm32_set_int_handler(int irqn, void (*handler)(void));
2830

31+
extern uint32_t SystemCoreClockMHZ;
32+
2933
#ifdef __cplusplus
3034
}
3135
#endif

fw/platforms/stm32/src/stm32_hal.c

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -71,26 +71,6 @@ void mgos_msleep(uint32_t msecs) {
7171

7272
void HAL_Delay(__IO uint32_t ms) __attribute__((alias("mgos_msleep")));
7373

74-
static void delay_cycles(unsigned long n) {
75-
__asm(
76-
"dcy: subs %0, #1\n"
77-
" bne dcy\n"
78-
: /* output */
79-
: /* input */ "r"(n)
80-
: /* scratch */);
81-
}
82-
83-
void mgos_usleep(uint32_t usecs) {
84-
#ifndef MGOS_BOOT_BUILD
85-
int ticks = usecs / (1000000 / configTICK_RATE_HZ);
86-
int remainder = usecs % (1000000 / configTICK_RATE_HZ);
87-
if (ticks > 0) vTaskDelay(ticks);
88-
#else
89-
uint32_t remainder = usecs;
90-
#endif
91-
if (remainder > 0) delay_cycles(remainder * (SystemCoreClock / 1000000));
92-
}
93-
9474
/* Note: PLL must be enabled for RNG to work */
9575
int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len) {
9676
RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN;

fw/platforms/stm32/src/stm32_libc.c

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,33 +26,17 @@
2626

2727
#include "mgos_debug.h"
2828
#include "mgos_system.h"
29+
#include "mgos_time.h"
2930

31+
#include "stm32_system.h"
3032
#include "stm32_uart_internal.h"
3133

3234
#include <stm32_sdk_hal.h>
3335

34-
/*
35-
* This will prevent counter wrap if time is read regularly.
36-
* At least Mongoose poll queries time, so we're covered.
37-
*/
38-
static int64_t get_time64_micros(void) {
39-
static uint32_t prev_time = 0;
40-
static uint32_t num_overflows = 0;
41-
mgos_ints_disable();
42-
uint32_t time = xTaskGetTickCount();
43-
int64_t time64 = time;
44-
time64 *= (portTICK_PERIOD_MS * 1000);
45-
if (prev_time > 0 && time < prev_time) num_overflows++;
46-
time64 += (((uint64_t) num_overflows) * (1ULL << 32));
47-
prev_time = time;
48-
mgos_ints_enable();
49-
return time64;
50-
}
51-
5236
static int64_t sys_time_adj = 0;
5337

5438
int _gettimeofday_r(struct _reent *r, struct timeval *tv, struct timezone *tz) {
55-
int64_t time64 = get_time64_micros() + sys_time_adj;
39+
int64_t time64 = mgos_uptime_micros() + sys_time_adj;
5640
tv->tv_sec = time64 / 1000000ULL;
5741
tv->tv_usec = time64 % 1000000ULL;
5842
return 0;
@@ -61,7 +45,7 @@ int _gettimeofday_r(struct _reent *r, struct timeval *tv, struct timezone *tz) {
6145
}
6246

6347
int settimeofday(const struct timeval *tv, const struct timezone *tz) {
64-
int64_t time64_cur = get_time64_micros();
48+
int64_t time64_cur = mgos_uptime_micros();
6549
int64_t time64_new = tv->tv_sec * 1000000LL + tv->tv_usec;
6650
sys_time_adj = (time64_new - time64_cur);
6751
(void) tz;

fw/platforms/stm32/src/stm32_main.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,15 @@ enum mgos_init_result mgos_hal_freertos_pre_init() {
106106
return MGOS_INIT_OK;
107107
}
108108

109-
extern void mgos_nsleep100_cal();
110-
109+
uint32_t SystemCoreClockMHZ = 0;
111110
uint32_t mgos_bitbang_n100_cal = 0;
111+
extern void mgos_nsleep100_cal();
112112

113113
void SystemCoreClockUpdate(void) {
114114
uint32_t presc =
115115
AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos)];
116116
SystemCoreClock = HAL_RCC_GetSysClockFreq() >> presc;
117+
SystemCoreClockMHZ = SystemCoreClock / 1000000;
117118
mgos_nsleep100_cal();
118119
}
119120

fw/src/mgos_hal_freertos.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#ifdef MGOS_HAVE_OTA_COMMON
3636
#include "mgos_ota.h"
3737
#endif
38+
#include "mgos_system.h"
3839
#include "mgos_uart_internal.h"
3940
#include "mgos_utils.h"
4041

@@ -333,4 +334,55 @@ IRAM void mgos_rlock_destroy(struct mgos_rlock_type *l) {
333334
if (l == NULL) return;
334335
vSemaphoreDelete((SemaphoreHandle_t) l);
335336
}
337+
338+
#if CS_PLATFORM == CS_P_ESP32
339+
340+
IRAM int64_t mgos_uptime_micros(void) {
341+
return (int64_t) esp_timer_get_time();
342+
}
343+
344+
#else /* All the ARM cores have SYSTICK */
345+
346+
#if defined(__arm__) || defined(__TI_COMPILER_VERSION__)
347+
#define SYSTICK_VAL (*((volatile uint32_t *) 0xe000e018))
348+
#define SYSTICK_RLD (*((volatile uint32_t *) 0xe000e014))
349+
#else
350+
#error Does not look like an ARM processor to me. Help!
351+
#endif
352+
353+
#if CS_PLATFORM == CS_P_CC3200 || CS_PLATFORM == CS_P_CC3220
354+
#define SystemCoreClockMHZ (SYS_CLK / 1000000)
355+
#else
356+
extern uint32_t SystemCoreClockMHZ;
357+
#endif
358+
359+
IRAM int64_t mgos_uptime_micros(void) {
360+
static uint8_t num_overflows = 0;
361+
static uint32_t prev_tc = 0;
362+
uint32_t tc, frac;
363+
do {
364+
tc = xTaskGetTickCount();
365+
frac = (SYSTICK_RLD - SYSTICK_VAL);
366+
// If a tick happens in between, fraction calculation may be wrong.
367+
} while (xTaskGetTickCount() != tc);
368+
if (tc < prev_tc) num_overflows++;
369+
prev_tc = tc;
370+
return ((((int64_t) num_overflows) << 32) + tc) * portTICK_PERIOD_MS * 1000 +
371+
(int64_t)(frac / SystemCoreClockMHZ);
372+
}
373+
374+
#endif /* CS_P_ESP32 */
375+
376+
IRAM void mgos_usleep(uint32_t usecs) {
377+
if (usecs < (1000000 / configTICK_RATE_HZ)) {
378+
(*mgos_nsleep100)(usecs * 10);
379+
} else {
380+
int64_t threshold = mgos_uptime_micros() + (int64_t) usecs;
381+
int ticks = usecs / (1000000 / configTICK_RATE_HZ);
382+
if (ticks > 0) vTaskDelay(ticks);
383+
while (mgos_uptime_micros() < threshold) {
384+
}
385+
}
386+
}
387+
336388
#endif /* MGOS_BOOT_BUILD */

0 commit comments

Comments
 (0)