Timers - mriksman/esp-idf-homekit GitHub Wiki
Timers
FreeRTOS System Tick (CCOMPARE0)
ESP8266 RTOS SDK start-up;
- Calls
call_start_cpu()
instartup.c
.- Starts
uiT
task and callsuser_init_entry
which- Initialises the
WDEV TSF0
interrupt for PWM
/*enable tsf0 interrupt for pwm*/ REG_WRITE(PERIPHS_DPORT_BASEADDR, (REG_READ(PERIPHS_DPORT_BASEADDR) & ~0x1F) | 0x1); REG_WRITE(INT_ENA_WDEV, REG_READ(INT_ENA_WDEV) | WDEV_TSF0_REACH_INT);
- Goes on to start
app_main
- Initialises the
- Starts
vTaskStartScheduler
intasks.c
.- Starts
IDLE
task - Starts
Tmr Svc
task - Calls
xPortStartScheduler
inport.c
. - Runs the following code
/*******software isr*********/ _xt_isr_attach(ETS_SOFT_INUM, SoftIsrHdl, NULL); _xt_isr_unmask(1 << ETS_SOFT_INUM); _xt_isr_attach(ETS_MAX_INUM, xPortSysTickHandle, NULL);
- Starts
- Starts
ETS_MAX_INUM is defined as 6
in rom/ets_sys.h
. This is the CCOUNT CCOMPARE0
timer.
/* Initialize system tick timer interrupt and schedule the first tick. */
_xt_tick_divisor = xtbsp_clock_freq_hz() / XT_TICK_PER_SEC;
g_esp_boot_ccount = soc_get_ccount();
soc_set_ccount(0);
_xt_tick_timer_init();
By default, XT_TICK_PER_SEC
is set to CONFIG_FREERTOS_HZ
which is set to 100Hz in menuconfig
.
In components/esp8266/include/xtensa/config/core-isa.h
#define XCHAL_HAVE_INTERRUPTS 1 /* interrupt option */
#define XCHAL_HAVE_HIGHPRI_INTERRUPTS 1 /* med/high-pri. interrupts */
#define XCHAL_HAVE_NMI 1 /* non-maskable interrupt */
#define XCHAL_HAVE_CCOUNT 1 /* CCOUNT reg. (timer option) */
#define XCHAL_NUM_TIMERS 1 /* number of CCOMPAREn regs */
#define XCHAL_NUM_INTERRUPTS 15 /* number of interrupts */
#define XCHAL_NUM_EXTINTERRUPTS 13 /* num of external interrupts */
#define XCHAL_NUM_INTLEVELS 2 /* number of interrupt levels */
/* Interrupt numbers assigned to specific interrupt sources: */
#define XCHAL_TIMER0_INTERRUPT 6 /* CCOMPARE0 */
#define XCHAL_TIMER1_INTERRUPT XTHAL_TIMER_UNCONFIGURED
#define XCHAL_TIMER2_INTERRUPT XTHAL_TIMER_UNCONFIGURED
#define XCHAL_TIMER3_INTERRUPT XTHAL_TIMER_UNCONFIGURED
#define XCHAL_NMI_INTERRUPT 14 /* non-maskable interrupt */
In components/freertos/port/esp8266/include/freertos/xtensa_timer.h
/* Select timer to use for periodic tick, and determine its interrupt number and priority. ... */
...
#if XCHAL_TIMER0_INTERRUPT != XTHAL_TIMER_UNCONFIGURED
#if XCHAL_INT_LEVEL(XCHAL_TIMER0_INTERRUPT) <= XCHAL_EXCM_LEVEL
#define XT_TIMER_INDEX 0
#endif
#endif
#define XT_CCOMPARE (CCOMPARE + XT_TIMER_INDEX)
In components/freertos/port/esp8266/os_cpu_a.S
_xt_tick_timer_init:
/* Set up the periodic tick timer (assume enough time to complete init). */
movi a2, _xt_tick_divisor
l32i a3, a2, 0
rsr a2, CCOUNT /* current cycle count */
add a2, a2, a3 /* time of first timer interrupt */
wsr a2, XT_CCOMPARE /* set the comparator */
So ESP8266 RTOS SDK uses TIMER0 (CCOUNT CCOUNTER0)
to interrupt on interrupt number 6
to perform the periodic tick timer.
FRC1 and FRC2 Timers
FRC1
is a 24-bit countdown timer which triggers an interrupt when it reaches zero. FRC2
is a 32-bit countup timer which can set a match value to trigger an interrupt.
The original esp-homekit PWM example code uses functions from esp-open-rtos.
ESP-OPEN-RTOS
The PWM code used in esp-homekit seems to use INUM_TIMER_FRC1
;
/* set up ISRs */
_xt_isr_attach(INUM_TIMER_FRC1, frc1_interrupt_handler, NULL);
typedef enum {
...
INUM_TICK = 6, /* RTOS timer tick, possibly xtensa CPU CCOMPARE0(?) */
INUM_SOFT = 7,
INUM_WDT = 8,
INUM_TIMER_FRC1 = 9,
/* FRC2 default handler. Configured by sdk_ets_timer_init, which
runs as part of default libmain.a (now called libcore.a) startup code,
assigns interrupt handler to sdk_vApplicationTickHook+0x68
*/
INUM_TIMER_FRC2 = 10,
} xt_isr_num_t;
ESP8266 RTOS SDK
#define hw_timer_intr_register(a, b) _xt_isr_attach(ETS_FRC_TIMER1_INUM, (a), (b))
In rom/ets_sys.h
#define ETS_SLC_INUM 1
#define ETS_SPI_INUM 2
#define ETS_GPIO_INUM 4
#define ETS_UART_INUM 5
#define ETS_MAX_INUM 6
#define ETS_SOFT_INUM 7
#define ETS_WDT_INUM 8
#define ETS_FRC_TIMER1_INUM 9
#define ETS_INT_MAX 14
So _xt_isr_attach(INUM_TIMER_FRC1, frc1_interrupt_handler, NULL)
can be replaced with hw_timer_intr_register(frc1_interrupt_handler, NULL)
.
Hardware Timer hw_timer
library in ESP8266 uses FRC1
.
A comment from an ESP representative states (from before os_timer
was renamed to esp_timer
) that for ESP8266;
FRC2 is used for the system software timer (os_timer)
FreeRTOS software timers (xTimerCreate
) rely on the System Tick. Looking at the esp_timer
code, it appears in ESP8266 RTOS SDK it is just a wrapper for the FreeRTOS timers. It does not appear to use FRC2
.
However. Looking at the enabled interrupts:
ESP_LOGW(TAG, "INTENABLE %x", xthal_get_intenable() );
0 ETS_WDEV_INUM WDEV process FIQ interrupt. PWM? Wi-Fi? 0
1 ETS_SLC_INUM 0
2 ETS_SPI_INUM 0
3 ETS_RTC_INUM 0
4 ETS_GPIO_INUM 0
5 ETS_UART_INUM 0
6 ETS_MAX_INUM CCOUNT CCOMPARE0 System Tick 1
7 ETS_SOFT_INUM Software ISR SoftIsrHdl in port.c 1
8 ETS_WDT_INUM 1
9 ETS_FRC_TIMER1_INUM FRC1 used in hw_timer.c 0
10 ETS_FRC_TIMER2_INUM FRC2 ? 1
5c0
FRC2
is enabled. But disabling ETS_FRC_TIMER2_INUM
with _xt_isr_mask(1 << 10)
has no impact to operation. Perhaps at one stage, os_timer
and/or esp_timer
did use FRC2
; as it does in ESP-IDF.
The ESP8266 configuration ESP8266_TIME_SYSCALL
defaults to the following
choice ESP8266_TIME_SYSCALL
prompt "Timers used for gettimeofday function"
default ESP8266_TIME_SYSCALL_USE_FRC1
help
This setting defines which hardware timers are used to
implement 'gettimeofday' and 'time' functions in C library.
- If high-resolution timer is used, gettimeofday will
provide time at microsecond resolution.
Time will not be preserved when going into deep sleep mode.
- If no timers are used, gettimeofday and time functions
return -1 and set errno to ENOSYS.
However, I do not think it is using FRC1
at all. When CONFIG_ESP8266_TIME_SYSCALL
is defined in time.c
, it uses esp_timer_get_time()
. This function uses soc_get_ccount()
function, which returns the CCOUNT
register.
int64_t esp_timer_get_time(void) {
extern uint64_t g_esp_os_us;
return (int64_t)(g_esp_os_us + soc_get_ccount() / g_esp_ticks_per_us);
}
FRC1
has also been left available for the Hardware Timer library.
ESP-IDF SDK
In ESP-IDF, the documentation notes the following:
Although FreeRTOS provides software timers, these timers have a few limitations:
- Maximum resolution is equal to RTOS tick period
- Timer callbacks are dispatched from a low-priority task
esp_timer
set of APIs provides one-shot and periodic timers, microsecond time resolution, and 64-bit range. Internally,esp_timer
uses a 64-bit hardware timerCONFIG_ESP_TIMER_IMPL
:FRC2 (legacy)
implementation has been used in ESP-IDF v2.x - v4.1.LAC timer of Timer Group 0
implementation is simpler and has smaller run time overhead because software handling of timer overflow is not needed.SYSTIMER
implementation is similar to LAC timer of Timer Group 0 but for ESP32-S2 chip.
ESP-OPEN-RTOS
#define TIMER_BASE 0x60000600
#define TIMER(i) (*(struct TIMER_REGS *)(TIMER_BASE + (i)*0x20))
#define TIMER_FRC1 TIMER(0)
#define TIMER_FRC2 TIMER(1)
/* TIMER registers
* ESP8266 has two hardware timer counters, FRC1 and FRC2.
* FRC1 is a 24-bit countdown timer, triggers interrupt when reaches zero.
* FRC2 is a 32-bit countup timer, can set a match value to trigger an interrupt.
* FreeRTOS tick timer appears to come from XTensa core tick timer0,
* not either of these. FRC2 is used in the FreeRTOS SDK however. It
* is set to free-run, interrupting periodically via updates to the
* ALARM register. sdk_ets_timer_init configures FRC2 and assigns FRC2
* interrupt handler at sdk_vApplicationTickHook+0x68
*/
struct TIMER_REGS { // FRC1 FRC2
uint32_t volatile LOAD; // 0x00 0x20
uint32_t volatile COUNT; // 0x04 0x24
uint32_t volatile CTRL; // 0x08 0x28
uint32_t volatile STATUS; // 0x0c 0x2c
uint32_t volatile ALARM; // 0x30
};
PWM
The pwm.c
code in the library uses wDev_MacTimSetFunc
. This function sets the first timer built into the wDev/MAC
hardware to fire after the specified delay (measured in ticks). When it fires, it will trigger an NMI
, and bit 27 of [0x3ff20c20]
will be set. wDev
is assumed to mean ‘Wi-Fi Device’? The function wDev_MacTimSetFunc
must be inside the binary files, as it cannot be found with search, and little is documented on it.