Skip to content

TIMER refactoring #7904

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
326 changes: 123 additions & 203 deletions cores/esp32/esp32-hal-timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,261 +13,181 @@
// limitations under the License.

#include "esp32-hal-timer.h"
#include "driver/timer.h"
#include "driver/gptimer.h"
#include "soc/soc_caps.h"
#include "clk_tree.h"

typedef union {
struct {
uint32_t reserved0: 10;
uint32_t alarm_en: 1; /*When set alarm is enabled*/
uint32_t level_int_en: 1; /*When set level type interrupt will be generated during alarm*/
uint32_t edge_int_en: 1; /*When set edge type interrupt will be generated during alarm*/
uint32_t divider: 16; /*Timer clock (T0/1_clk) pre-scale value.*/
uint32_t autoreload: 1; /*When set timer 0/1 auto-reload at alarming is enabled*/
uint32_t increase: 1; /*When set timer 0/1 time-base counter increment. When cleared timer 0 time-base counter decrement.*/
uint32_t enable: 1; /*When set timer 0/1 time-base counter is enabled*/
};
uint32_t val;
} timer_cfg_t;

#define NUM_OF_TIMERS SOC_TIMER_GROUP_TOTAL_TIMERS
typedef void (*voidFuncPtr)(void);
typedef void (*voidFuncPtrArg)(void*);

typedef struct hw_timer_s
{
uint8_t group;
uint8_t num;
} hw_timer_t;
typedef struct {
voidFuncPtr fn;
void* arg;
} interrupt_config_t;

// Works for all chips
static hw_timer_t timer_dev[4] = {
{0,0}, {1,0}, {0,1}, {1,1}
struct timer_struct_t {
gptimer_handle_t timer_handle;
interrupt_config_t interrupt_handle;
};

// NOTE: (in IDF 5.0 there wont be need to know groups/numbers
// timer_init() will list thru all timers and return free timer handle)


inline uint64_t timerRead(hw_timer_t *timer){

uint64_t value;
timer_get_counter_value(timer->group, timer->num,&value);
return value;
}
inline uint64_t timerRead(hw_timer_t * timer){

uint64_t timerAlarmRead(hw_timer_t *timer){
uint64_t value;
timer_get_alarm_value(timer->group, timer->num, &value);
gptimer_get_raw_count(timer->timer_handle, &value);
return value;
}

void timerWrite(hw_timer_t *timer, uint64_t val){
timer_set_counter_value(timer->group, timer->num, val);
}

void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload){
timer_set_alarm_value(timer->group, timer->num, alarm_value);
timerSetAutoReload(timer,autoreload);
}

void timerSetConfig(hw_timer_t *timer, uint32_t config){
timer_cfg_t cfg;
cfg.val = config;
timer_set_alarm(timer->group, timer->num, cfg.alarm_en);
timerSetDivider(timer,cfg.divider);
timerSetAutoReload(timer,cfg.autoreload);
timerSetCountUp(timer, cfg.increase);

if (cfg.enable) {
timerStart(timer);
}
else{
timerStop(timer);
}
return;
}

uint32_t timerGetConfig(hw_timer_t *timer){
timer_config_t timer_cfg;
timer_get_config(timer->group, timer->num,&timer_cfg);

//Translate to default uint32_t
timer_cfg_t cfg;
cfg.alarm_en = timer_cfg.alarm_en;
cfg.autoreload = timer_cfg.auto_reload;
cfg.divider = timer_cfg.divider;
cfg.edge_int_en = timer_cfg.intr_type;
cfg.level_int_en = !timer_cfg.intr_type;
cfg.enable = timer_cfg.counter_en;
cfg.increase = timer_cfg.counter_dir;

return cfg.val;
}

void timerSetCountUp(hw_timer_t *timer, bool countUp){
timer_set_counter_mode(timer->group, timer->num,countUp);
void timerWrite(hw_timer_t * timer, uint64_t val){
gptimer_set_raw_count(timer->timer_handle, val);
}

bool timerGetCountUp(hw_timer_t *timer){
timer_cfg_t config;
config.val = timerGetConfig(timer);
return config.increase;
}

void timerSetAutoReload(hw_timer_t *timer, bool autoreload){
timer_set_auto_reload(timer->group, timer->num,autoreload);
}

bool timerGetAutoReload(hw_timer_t *timer){
timer_cfg_t config;
config.val= timerGetConfig(timer);
return config.autoreload;
}

// Set divider from 2 to 65535
void timerSetDivider(hw_timer_t *timer, uint16_t divider){
if(divider < 2)
{
log_e("Timer divider must be set in range of 2 to 65535");
return;
}
timer_set_divider(timer->group, timer->num,divider);
}

uint16_t timerGetDivider(hw_timer_t *timer){
timer_cfg_t config;
config.val = timerGetConfig(timer);
return config.divider;
}

void timerStart(hw_timer_t *timer){
timer_start(timer->group, timer->num);
}

void timerStop(hw_timer_t *timer){
timer_pause(timer->group, timer->num);
void timerAlarm(hw_timer_t * timer, uint64_t alarm_value, bool autoreload, uint64_t reload_count){
esp_err_t err = ESP_OK;
gptimer_alarm_config_t alarm_cfg = {
.alarm_count = alarm_value,
.reload_count = reload_count,
.flags.auto_reload_on_alarm = autoreload,
};
err = gptimer_set_alarm_action(timer->timer_handle, &alarm_cfg);
if (err != ESP_OK){
log_e("Timer Alarm Write failed, error num=%d", err);
}
}

void timerRestart(hw_timer_t *timer){
timerWrite(timer,0);
uint32_t timerGetFrequency(hw_timer_t * timer){
uint32_t frequency;
gptimer_get_resolution(timer->timer_handle, &frequency);
return frequency;
}

bool timerStarted(hw_timer_t *timer){
timer_cfg_t config;
config.val = timerGetConfig(timer);
return config.enable;
void timerStart(hw_timer_t * timer){
gptimer_start(timer->timer_handle);
}

void timerAlarmEnable(hw_timer_t *timer){
timer_set_alarm(timer->group, timer->num,true);
void timerStop(hw_timer_t * timer){
gptimer_stop(timer->timer_handle);
}

void timerAlarmDisable(hw_timer_t *timer){
timer_set_alarm(timer->group, timer->num,false);
void timerRestart(hw_timer_t * timer){
gptimer_set_raw_count(timer->timer_handle,0);
}

bool timerAlarmEnabled(hw_timer_t *timer){
timer_cfg_t config;
config.val = timerGetConfig(timer);
return config.alarm_en;
}
hw_timer_t * timerBegin(uint32_t frequency){
esp_err_t err = ESP_OK;
uint32_t counter_src_hz = 0;
uint32_t divider = 0;
soc_periph_gptimer_clk_src_t clk;

static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
hw_timer_t * timer = (hw_timer_t *)arg;
if(ev_type == APB_BEFORE_CHANGE){
timerStop(timer);
} else {
old_apb /= 1000000;
new_apb /= 1000000;
uint16_t divider = (new_apb * timerGetDivider(timer)) / old_apb;
timerSetDivider(timer,divider);
timerStart(timer);
soc_periph_gptimer_clk_src_t gptimer_clks[] = SOC_GPTIMER_CLKS;
for (size_t i = 0; i < sizeof(gptimer_clks) / sizeof(gptimer_clks[0]); i++){
clk = gptimer_clks[i];
clk_tree_src_get_freq_hz(clk, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &counter_src_hz);
divider = counter_src_hz / frequency;
if((divider >= 2) && (divider <= 65536)){
break;
}
else divider = 0;
}
}

hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){
if(num >= NUM_OF_TIMERS)
{
log_e("Timer number %u exceeds available number of Timers.", num);
if(divider == 0){
log_e("Resolution cannot be reached with any clock source, aborting!");
return NULL;
}

hw_timer_t * timer = &timer_dev[num]; //Get Timer group/num from 0-3 number

timer_config_t config = {
.divider = divider,
.counter_dir = countUp,
.counter_en = TIMER_PAUSE,
.alarm_en = TIMER_ALARM_DIS,
.auto_reload = false,
gptimer_config_t config = {
.clk_src = clk,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = frequency,
.flags.intr_shared = true,
};

timer_init(timer->group, timer->num, &config);
timer_set_counter_value(timer->group, timer->num, 0);
timerStart(timer);
addApbChangeCallback(timer, _on_apb_change);
hw_timer_t *timer = malloc(sizeof(hw_timer_t));

err = gptimer_new_timer(&config, &timer->timer_handle);
if (err != ESP_OK){
log_e("Failed to create a new GPTimer, error num=%d", err);
free(timer);
return NULL;
}
gptimer_enable(timer->timer_handle);
gptimer_start(timer->timer_handle);
return timer;
}

void timerEnd(hw_timer_t *timer){
removeApbChangeCallback(timer, _on_apb_change);
timer_deinit(timer->group, timer->num);
void timerEnd(hw_timer_t * timer){
esp_err_t err = ESP_OK;
gptimer_disable(timer->timer_handle);
err = gptimer_del_timer(timer->timer_handle);
if (err != ESP_OK){
log_e("Failed to destroy GPTimer, error num=%d", err);
return;
}
free(timer);
}

bool IRAM_ATTR timerFnWrapper(void *arg){
void (*fn)(void) = arg;
fn();

bool IRAM_ATTR timerFnWrapper(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void * args){
interrupt_config_t * isr = (interrupt_config_t*)args;
if(isr->fn) {
if(isr->arg){
((voidFuncPtrArg)isr->fn)(isr->arg);
} else {
isr->fn();
}
}
// some additional logic or handling may be required here to approriately yield or not
return false;
}

void timerAttachInterruptFlag(hw_timer_t *timer, void (*fn)(void), bool edge, int intr_alloc_flags){
if(edge){
log_w("EDGE timer interrupt is not supported! Setting to LEVEL...");
}
timer_isr_callback_add(timer->group, timer->num, timerFnWrapper, fn, intr_alloc_flags);
}
void timerAttachInterruptFunctionalArg(hw_timer_t * timer, void (*userFunc)(void*), void * arg){
esp_err_t err = ESP_OK;
gptimer_event_callbacks_t cbs = {
.on_alarm = timerFnWrapper,
};

void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){
timerAttachInterruptFlag(timer, fn, edge, 0);
}
timer->interrupt_handle.fn = (voidFuncPtr)userFunc;
timer->interrupt_handle.arg = arg;

void timerDetachInterrupt(hw_timer_t *timer){
timer_isr_callback_remove(timer->group, timer->num);
gptimer_disable(timer->timer_handle);
err = gptimer_register_event_callbacks(timer->timer_handle, &cbs, &timer->interrupt_handle);
if (err != ESP_OK){
log_e("Timer Attach Interrupt failed, error num=%d", err);
}
gptimer_enable(timer->timer_handle);
}

uint64_t timerReadMicros(hw_timer_t *timer){
uint64_t timer_val = timerRead(timer);
uint16_t div = timerGetDivider(timer);
return timer_val * div / (getApbFrequency() / 1000000);

void timerAttachInterruptArg(hw_timer_t * timer, void (*userFunc)(void*), void * arg){
timerAttachInterruptFunctionalArg(timer, userFunc, arg);
}

uint64_t timerReadMilis(hw_timer_t *timer){
uint64_t timer_val = timerRead(timer);
uint16_t div = timerGetDivider(timer);
return timer_val * div / (getApbFrequency() / 1000);
void timerAttachInterrupt(hw_timer_t * timer, voidFuncPtr userFunc){
timerAttachInterruptFunctionalArg(timer, (voidFuncPtrArg)userFunc, NULL);
}

double timerReadSeconds(hw_timer_t *timer){
uint64_t timer_val = timerRead(timer);
uint16_t div = timerGetDivider(timer);
return (double)timer_val * div / getApbFrequency();
void timerDetachInterrupt(hw_timer_t * timer){
esp_err_t err = ESP_OK;
err = gptimer_set_alarm_action(timer->timer_handle, NULL);
timer->interrupt_handle.fn = NULL;
timer->interrupt_handle.arg = NULL;
if (err != ESP_OK){
log_e("Timer Detach Interrupt failed, error num=%d", err);
}
}

uint64_t timerAlarmReadMicros(hw_timer_t *timer){
uint64_t timer_val = timerAlarmRead(timer);
uint16_t div = timerGetDivider(timer);
return timer_val * div / (getApbFrequency() / 1000000);
uint64_t timerReadMicros(hw_timer_t * timer){
uint64_t timer_val = timerRead(timer);
uint32_t frequency = timerGetFrequency(timer);
return timer_val * 1000000 / frequency;
}

uint64_t timerAlarmReadMilis(hw_timer_t *timer){
uint64_t timer_val = timerAlarmRead(timer);
uint16_t div = timerGetDivider(timer);
return timer_val * div / (getApbFrequency() / 1000);
uint64_t timerReadMilis(hw_timer_t * timer){
uint64_t timer_val = timerRead(timer);
uint32_t frequency = timerGetFrequency(timer);
return timer_val * 1000 / frequency;
}

double timerAlarmReadSeconds(hw_timer_t *timer){
uint64_t timer_val = timerAlarmRead(timer);
uint16_t div = timerGetDivider(timer);
return (double)timer_val * div / getApbFrequency();
double timerReadSeconds(hw_timer_t * timer){
uint64_t timer_val = timerRead(timer);
uint32_t frequency = timerGetFrequency(timer);
return (double)timer_val / frequency;
}
Loading