Skip to content

Commit

Permalink
TIMER refactoring (#7904)
Browse files Browse the repository at this point in the history
* refactor using GPtimer

* Updated timer HW test

* fix examples

* Add v2.0.7 in issue template (#7871)

* refactor using GPtimer

* Updated timer HW test

* fix examples

* Revert "Add v2.0.7 in issue template (#7871)"

This reverts commit fcc3b17.

* Update upload-artifact to v3 in HW CI

* Revert "Update upload-artifact to v3 in HW CI"

This reverts commit 1ba2280.

* replace resolution with frequency

* remove count_down option

* countup removed from examples + header

* Refactored timer object

* code cleanup + examples and tests fixes

* TimerAlarm fix

---------

Co-authored-by: Vojtěch Bartoška <76958047+VojtechBartoska@users.noreply.github.com>
  • Loading branch information
P-R-O-C-H-Y and VojtechBartoska authored Mar 31, 2023
1 parent f48cb4b commit 73d2862
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 273 deletions.
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

0 comments on commit 73d2862

Please sign in to comment.