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
This commit is contained in:
parent
8b0a1ae362
commit
326708c903
2 changed files with 320 additions and 0 deletions
|
@ -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)
|
||||
|
|
317
open_esplibs/libmain/ets_timer.c
Normal file
317
open_esplibs/libmain/ets_timer.c
Normal file
|
@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp/timer_regs.h>
|
||||
#include <FreeRTOS.h>
|
||||
#include <timers.h>
|
||||
#include <queue.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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 */
|
Loading…
Reference in a new issue