From 326708c9038db2509e1375658be2d3608075f240 Mon Sep 17 00:00:00 2001 From: sheinz Date: Thu, 17 Nov 2016 01:49:27 +0200 Subject: [PATCH] Reverse engineered ets_timers.o Switch from FreeRTOS queue to task notification. Removed unknown/unused code. Rename sdk_ets_handler_isr to process_pending_timers Add function for microseconds Simplify time to ticks conversion --- open_esplibs/include/open_esplibs.h | 3 + open_esplibs/libmain/ets_timer.c | 317 ++++++++++++++++++++++++++++ 2 files changed, 320 insertions(+) create mode 100644 open_esplibs/libmain/ets_timer.c diff --git a/open_esplibs/include/open_esplibs.h b/open_esplibs/include/open_esplibs.h index 8db78c5..bcbbcd4 100644 --- a/open_esplibs/include/open_esplibs.h +++ b/open_esplibs/include/open_esplibs.h @@ -39,6 +39,9 @@ #ifndef OPEN_LIBMAIN_ETS_TIMER #define OPEN_LIBMAIN_ETS_TIMER (OPEN_LIBMAIN) #endif +#ifndef OPEN_LIBMAIN_ETS_TIMER +#define OPEN_LIBMAIN_ETS_TIMER (OPEN_LIBMAIN) +#endif #ifndef OPEN_LIBNET80211 #define OPEN_LIBNET80211 (OPEN_ESPLIBS) diff --git a/open_esplibs/libmain/ets_timer.c b/open_esplibs/libmain/ets_timer.c new file mode 100644 index 0000000..27a74ca --- /dev/null +++ b/open_esplibs/libmain/ets_timer.c @@ -0,0 +1,317 @@ +/** + * Recreated Espressif libmain ets_timer.o contents. + * + * Copyright (C) 2015 Espressif Systems. Derived from MIT Licensed SDK libraries. + * BSD Licensed as described in the file LICENSE + * + * Copyright (c) 2016 sheinz (https://github.com/sheinz) + * + * This module seems to be adapted from NONOS SDK by Espressif to fit into + * RTOS SDK. Function sdk_ets_timer_handler_isr is no longer an ISR handler + * but still holds its name. Espressif just added a task that receives events + * from the real FRC2 timer ISR handler and calls former ISR handler. + * So, timer callbacks are called from the task context rather than an interrupt. + * + * Modifications from the original reverse engineered version: + * - FreeRTOS queue is replaced with Task notifications. + * - Removed unknown queue length monitoring and parameters allocation. + * - Removed unused debug variables + * - xTaskGenericCreate is replaced with xTaskCreate + * - simplified time to ticks conversion (simply multiply by 5) + * + * This timer should be used with caution together with other tasks. As the + * timer callback is executed within timer task context, access to data that + * other tasks accessing should be protected. + */ +#include "open_esplibs.h" + +#if OPEN_LIBMAIN_ETS_TIMER + +#include "open_esplibs.h" +#include +#include +#include +#include +#include +#include +#include + +typedef void ets_timer_func_t(void *); + +/** + * This structure is used for both timers: ets_timer.c and timer.c + */ +typedef struct ets_timer_st { + struct ets_timer_st *next; + TimerHandle_t timer_handle; // not used in ets_timer.c + uint32_t fire_ticks; // FRC2 timer value when timer should fire + uint32_t period_ticks; // timer value in FRC2 ticks for rpeating timers + ets_timer_func_t *callback; + bool repeat; // not used in ets_timer.c + void *timer_arg; +} ets_timer_t; + +/** + * Special values of ets_timer_t::next field + */ +#define ETS_TIMER_NOT_ARMED (ets_timer_t*)(0xffffffff) +#define ETS_TIMER_LIST_END (ets_timer_t*)(0) + +/** + * Linked list of timers + */ +static ets_timer_t* timer_list = 0; + +static TaskHandle_t task_handle = NULL; + +void sdk_ets_timer_setfn(ets_timer_t *timer, ets_timer_func_t *func, void *parg) +{ + timer->callback = func; + timer->timer_arg = parg; + timer->fire_ticks = 0; + timer->period_ticks = 0; + timer->next = ETS_TIMER_NOT_ARMED; +} + +/** + * .Lfunc004 + */ +static inline void set_alarm_value(uint32_t value) +{ + TIMER_FRC2.ALARM = value; +} + +/** + * .Lfunc005 + * + * Set timer alarm and make sure the alarm is set in the future + * and will not be missed by the timer. + */ +static void set_alarm(uint32_t ticks) +{ + uint32_t curr_time = TIMER_FRC2.COUNT; + int32_t delta = (int32_t)ticks - curr_time; + if ((delta - 40) < 1) { + if (delta < 1) { + set_alarm_value(curr_time + 40); + } else { + set_alarm_value(ticks + 44); + } + } else { + set_alarm_value(ticks); + } +} + +/** + * .Lfunc006 + * + * Pending timer list example: + * + * | Timer: | T0 | T1 | T2 | T3 | + * |-------------|----|----|----|----| + * | fire_ticks: | 10 | 20 | 30 | 40 | + * | next: | T1 | T2 | T3 | 0 | + * + * + * For example we need to add a timer that should fire at 25 ticks: + * + * | Timer: | T0 | T1 | new | T2 | T3 | + * |-------------|----|-----|-----|----|----| + * | fire_ticks: | 10 | 20 | 25 | 30 | 40 | + * | next: | T1 | new | T2 | T3 | 0 | + * + * We squeeze the timer into the list so the list will always remain sorted + * + * Note: if add the same timer twice the system halts +*/ +static void add_pending_timer(uint32_t ticks, ets_timer_t *timer) +{ + ets_timer_t *prev = 0; + ets_timer_t *curr = timer_list; + while (curr) { + if (((int32_t)ticks - (int32_t)curr->fire_ticks) < 1) { + // found a timer that should fire later + // so our timer should fire earlier + break; + } + prev = curr; + curr = curr->next; + } + + timer->next = curr; + timer->fire_ticks = ticks; + + if (prev != 0) { + prev->next = timer; + } else { + // Our timer is the first in the line to fire + timer_list = timer; + set_alarm(ticks); + } + + // This situation might happen if adding the same timer twice + if (timer == timer->next) { + // This seems like an error: %s is used for line number + // In the recent SDK Espressif fixed the format to "%s %u\n" + printf("%s %s \n", "ets_timer.c", (char*)209); + while (1); + } +} + +/** + * In the Espressif SDK 0.9.9 if try to arm already armed timer the system halts + * with error message. In the later SDK version Espressif changed the behavior. + * If the timer was previously armed it is disarmed and then armed without errors. + * This version recreates behavior of SDK 0.9.9 + */ +void sdk_ets_timer_arm_ms_us(ets_timer_t *timer, uint32_t value, + bool repeat_flag, bool value_in_ms) +{ + uint32_t ticks = 0; + + if (timer->next != ETS_TIMER_NOT_ARMED) { + // The error message doesn't tell what is wrong + printf("arm new %x %x\n", (uint32_t)timer, (uint32_t)timer->next); + while(1); // halt + } + + if (value_in_ms) { + ticks = value * 5000; + } else { + ticks = value * 5; + } + + if (repeat_flag) { + timer->period_ticks = ticks; + } + vPortEnterCritical(); + add_pending_timer(TIMER_FRC2.COUNT + ticks, timer); + vPortExitCritical(); +} + +void sdk_ets_timer_arm(ets_timer_t *timer, uint32_t milliseconds, + bool repeat_flag) +{ + sdk_ets_timer_arm_ms_us(timer, milliseconds, repeat_flag, + /*value in ms=*/true); +} + +void sdk_ets_timer_arm_us(ets_timer_t *timer, uint32_t useconds, + bool repeat_flag) +{ + sdk_ets_timer_arm_ms_us(timer, useconds, repeat_flag, + /*value in ms=*/false); +} + +/** + * Function removes a timer from the pending timers list. + */ +void sdk_ets_timer_disarm(ets_timer_t *timer) +{ + vPortEnterCritical(); + ets_timer_t *curr = timer_list; + ets_timer_t *prev = 0; + while (curr) { + if (curr == timer) { + if (prev) { + prev->next = curr->next; + } else { + timer_list = curr->next; + } + break; + } + prev = curr; + curr = curr->next; + } + vPortExitCritical(); + timer->next = ETS_TIMER_NOT_ARMED; + timer->period_ticks = 0; +} + +/** + * Check the list of pending timers for expired ones and process them. + */ +static inline void process_pending_timers() +{ + vPortEnterCritical(); + int32_t ticks = TIMER_FRC2.COUNT; + while (timer_list) { + if (((int32_t)timer_list->fire_ticks - ticks) < 1) { + ets_timer_t *timer = timer_list; + timer_list = timer->next; + timer->next = ETS_TIMER_NOT_ARMED; + + vPortExitCritical(); + timer->callback(timer->timer_arg); + vPortEnterCritical(); + + if (timer->next == ETS_TIMER_NOT_ARMED) { + if (timer->period_ticks) { + timer->fire_ticks = timer->fire_ticks + timer->period_ticks; + add_pending_timer(timer->fire_ticks, timer); + } + } + ticks = TIMER_FRC2.COUNT; + } else { + if (timer_list) { + set_alarm(timer_list->fire_ticks); + } + break; + } + } + vPortExitCritical(); +} + +/** + * .Lfunc002 + */ +static void IRAM frc2_isr(void *arg) +{ + BaseType_t task_woken = 0; + + BaseType_t result = xTaskNotifyFromISR(task_handle, 0, eNoAction, &task_woken); + if (result != pdTRUE) { + printf("TIMQ_FL:%d!!", (uint32_t)result); + } + + portEND_SWITCHING_ISR(task_woken); +} + +/** + * .Lfunc007 + * + * Timer task + */ +static void timer_task(void* param) +{ + while (true) { + if (xTaskNotifyWait(0, 0, NULL, portMAX_DELAY) == pdTRUE) { + process_pending_timers(); + } + } +} + +void sdk_ets_timer_init() +{ + timer_list = 0; + + _xt_isr_attach(INUM_TIMER_FRC2, frc2_isr, NULL); + + /* Original code calls xTaskGenericCreate: + * xTaskGenericCreate(task_handle, "rtc_timer_task", 200, 0, 12, &handle, + * NULL, NULL); + */ + xTaskCreate(timer_task, "rtc_timer_task", 200, 0, 12, &task_handle); + printf("frc2_timer_task_hdl:%p, prio:%d, stack:%d\n", task_handle, 12, 200); + + TIMER_FRC2.ALARM = 0; + TIMER_FRC2.CTRL = VAL2FIELD(TIMER_CTRL_CLKDIV, TIMER_CLKDIV_16) + | TIMER_CTRL_RUN; + TIMER_FRC2.LOAD = 0; + + DPORT.INT_ENABLE |= DPORT_INT_ENABLE_TIMER1; + + _xt_isr_unmask(BIT(INUM_TIMER_FRC2)); +} + +#endif /* OPEN_LIBMAIN_ETS_TIMER */