Unverified Commit 5e7db8df authored by P-R-O-C-H-Y's avatar P-R-O-C-H-Y Committed by GitHub

Timer based od ESP-IDF (#5931)

parent fb00b51f
...@@ -13,335 +13,227 @@ ...@@ -13,335 +13,227 @@
// limitations under the License. // limitations under the License.
#include "esp32-hal-timer.h" #include "esp32-hal-timer.h"
#include "freertos/FreeRTOS.h" #include "driver/timer.h"
#ifndef CONFIG_IDF_TARGET_ESP32C3 #include "soc/soc_caps.h"
#include "freertos/xtensa_api.h"
#include "soc/dport_reg.h" typedef union {
#endif struct {
#include "freertos/task.h" uint32_t reserved0: 10;
#include "soc/timer_group_struct.h" uint32_t alarm_en: 1; /*When set alarm is enabled*/
#include "esp_attr.h" uint32_t level_int_en: 1; /*When set level type interrupt will be generated during alarm*/
#include "driver/periph_ctrl.h" 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.*/
#include "esp_system.h" uint32_t autoreload: 1; /*When set timer 0/1 auto-reload at alarming is enabled*/
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+ uint32_t increase: 1; /*When set timer 0/1 time-base counter increment. When cleared timer 0 time-base counter decrement.*/
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 uint32_t enable: 1; /*When set timer 0/1 time-base counter is enabled*/
#include "esp32/rom/ets_sys.h" };
#include "esp_intr_alloc.h" uint32_t val;
#elif CONFIG_IDF_TARGET_ESP32S2 } timer_cfg_t;
#include "esp32s2/rom/ets_sys.h"
#include "esp_intr_alloc.h" #define NUM_OF_TIMERS SOC_TIMER_GROUP_TOTAL_TIMERS
#include "soc/periph_defs.h"
#elif CONFIG_IDF_TARGET_ESP32C3 typedef struct {
#include "esp32c3/rom/ets_sys.h" int timer_group;
#include "esp_intr_alloc.h" int timer_idx;
#include "soc/periph_defs.h" int alarm_interval;
#else bool auto_reload;
#error Target CONFIG_IDF_TARGET is not supported } timer_info_t;
#endif
#else // ESP32 Before IDF 4.0 typedef struct hw_timer_s
#include "rom/ets_sys.h" {
#include "esp_intr.h" uint8_t group;
#endif uint8_t num;
#define HWTIMER_LOCK() portENTER_CRITICAL(timer->lock)
#define HWTIMER_UNLOCK() portEXIT_CRITICAL(timer->lock)
typedef volatile struct {
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;
} config;
uint32_t cnt_low; /*Register to store timer 0/1 time-base counter current value lower 32 bits.*/
uint32_t cnt_high; /*Register to store timer 0 time-base counter current value higher 32 bits.*/
uint32_t update; /*Write any value will trigger a timer 0 time-base counter value update (timer 0 current value will be stored in registers above)*/
uint32_t alarm_low; /*Timer 0 time-base counter value lower 32 bits that will trigger the alarm*/
uint32_t alarm_high; /*Timer 0 time-base counter value higher 32 bits that will trigger the alarm*/
uint32_t load_low; /*Lower 32 bits of the value that will load into timer 0 time-base counter*/
uint32_t load_high; /*higher 32 bits of the value that will load into timer 0 time-base counter*/
uint32_t reload; /*Write any value will trigger timer 0 time-base counter reload*/
} hw_timer_reg_t;
typedef struct hw_timer_s {
hw_timer_reg_t * dev;
uint8_t num;
uint8_t group;
uint8_t timer;
portMUX_TYPE lock;
} hw_timer_t; } hw_timer_t;
static hw_timer_t hw_timer[4] = { // Works for all chips
{(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE),0,0,0,portMUX_INITIALIZER_UNLOCKED}, static hw_timer_t timer_dev[4] = {
{(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE + 0x0024),1,0,1,portMUX_INITIALIZER_UNLOCKED}, {0,0}, {1,0}, {1,0}, {1,1}
{(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE + 0x1000),2,1,0,portMUX_INITIALIZER_UNLOCKED},
{(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE + 0x1024),3,1,1,portMUX_INITIALIZER_UNLOCKED}
}; };
typedef void (*voidFuncPtr)(void); // NOTE: (in IDF 5.0 there wont be need to know groups/numbers
static voidFuncPtr __timerInterruptHandlers[4] = {0,0,0,0}; // timer_init() will list thru all timers and return free timer handle)
void ARDUINO_ISR_ATTR __timerISR(void * arg){
uint32_t s0 = TIMERG0.int_st_timers.val;
uint32_t s1 = TIMERG1.int_st_timers.val;
TIMERG0.int_clr_timers.val = s0;
TIMERG1.int_clr_timers.val = s1;
uint8_t status = (s1 & 3) << 2 | (s0 & 3);
uint8_t i = 4;
//restart the timers that should autoreload
while(i--){
hw_timer_reg_t * dev = hw_timer[i].dev;
if((status & (1 << i)) && dev->config.autoreload){
dev->config.alarm_en = 1;
}
}
i = 4;
//call callbacks
while(i--){
if(__timerInterruptHandlers[i] && (status & (1 << i))){
__timerInterruptHandlers[i]();
}
}
}
uint64_t inline timerRead(hw_timer_t *timer){ uint64_t inline timerRead(hw_timer_t *timer){
timer->dev->update = 1;
while (timer->dev->update) {}; uint64_t value;
uint64_t h = timer->dev->cnt_high; timer_get_counter_value(timer->group, timer->num,&value);
uint64_t l = timer->dev->cnt_low; return value;
return (h << 32) | l;
} }
uint64_t timerAlarmRead(hw_timer_t *timer){ uint64_t timerAlarmRead(hw_timer_t *timer){
uint64_t h = timer->dev->alarm_high; uint64_t value;
uint64_t l = timer->dev->alarm_low; timer_get_alarm_value(timer->group, timer->num, &value);
return (h << 32) | l; return value;
} }
void timerWrite(hw_timer_t *timer, uint64_t val){ void timerWrite(hw_timer_t *timer, uint64_t val){
timer->dev->load_high = (uint32_t) (val >> 32); timer_set_counter_value(timer->group, timer->num, val);
timer->dev->load_low = (uint32_t) (val);
timer->dev->reload = 1;
} }
void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload){ void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload){
timer->dev->alarm_high = (uint32_t) (alarm_value >> 32); timer_set_alarm_value(timer->group, timer->num, alarm_value);
timer->dev->alarm_low = (uint32_t) alarm_value; timerSetAutoReload(timer,autoreload);
timer->dev->config.autoreload = autoreload;
} }
void timerSetConfig(hw_timer_t *timer, uint32_t config){ void timerSetConfig(hw_timer_t *timer, uint32_t config){
timer->dev->config.val = 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){ uint32_t timerGetConfig(hw_timer_t *timer){
return timer->dev->config.val; 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){ void timerSetCountUp(hw_timer_t *timer, bool countUp){
timer->dev->config.increase = countUp; timer_set_counter_mode(timer->group, timer->num,countUp);
} }
bool timerGetCountUp(hw_timer_t *timer){ bool timerGetCountUp(hw_timer_t *timer){
return timer->dev->config.increase; timer_cfg_t config;
config.val = timerGetConfig(timer);
return config.increase;
} }
void timerSetAutoReload(hw_timer_t *timer, bool autoreload){ void timerSetAutoReload(hw_timer_t *timer, bool autoreload){
timer->dev->config.autoreload = autoreload; timer_set_auto_reload(timer->group, timer->num,autoreload);
} }
bool timerGetAutoReload(hw_timer_t *timer){ bool timerGetAutoReload(hw_timer_t *timer){
return timer->dev->config.autoreload; timer_cfg_t config;
config.val= timerGetConfig(timer);
return config.autoreload;
} }
void timerSetDivider(hw_timer_t *timer, uint16_t divider){//2 to 65536 // Set divider from 2 to 65535
if(!divider){ void timerSetDivider(hw_timer_t *timer, uint16_t divider){
divider = 0xFFFF; if(divider < 2)
} else if(divider == 1){ {
divider = 2; log_e("Timer divider must be set in range of 2 to 65535");
return;
} }
int timer_en = timer->dev->config.enable; timer_set_divider(timer->group, timer->num,divider);
timer->dev->config.enable = 0;
timer->dev->config.divider = divider;
timer->dev->config.enable = timer_en;
} }
uint16_t timerGetDivider(hw_timer_t *timer){ uint16_t timerGetDivider(hw_timer_t *timer){
return timer->dev->config.divider; timer_cfg_t config;
config.val = timerGetConfig(timer);
return config.divider;
} }
void timerStart(hw_timer_t *timer){ void timerStart(hw_timer_t *timer){
timer->dev->config.enable = 1; timer_start(timer->group, timer->num);
} }
void timerStop(hw_timer_t *timer){ void timerStop(hw_timer_t *timer){
timer->dev->config.enable = 0; timer_pause(timer->group, timer->num);
} }
void timerRestart(hw_timer_t *timer){ void timerRestart(hw_timer_t *timer){
timer->dev->config.enable = 0; timerWrite(timer,0);
timer->dev->reload = 1;
timer->dev->config.enable = 1;
} }
bool timerStarted(hw_timer_t *timer){ bool timerStarted(hw_timer_t *timer){
return timer->dev->config.enable; timer_cfg_t config;
config.val = timerGetConfig(timer);
return config.enable;
} }
void timerAlarmEnable(hw_timer_t *timer){ void timerAlarmEnable(hw_timer_t *timer){
timer->dev->config.alarm_en = 1; timer_set_alarm(timer->group, timer->num,true);
} }
void timerAlarmDisable(hw_timer_t *timer){ void timerAlarmDisable(hw_timer_t *timer){
timer->dev->config.alarm_en = 0; timer_set_alarm(timer->group, timer->num,false);
} }
bool timerAlarmEnabled(hw_timer_t *timer){ bool timerAlarmEnabled(hw_timer_t *timer){
return timer->dev->config.alarm_en; timer_cfg_t config;
config.val = timerGetConfig(timer);
return config.alarm_en;
} }
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){ 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; hw_timer_t * timer = (hw_timer_t *)arg;
if(ev_type == APB_BEFORE_CHANGE){ if(ev_type == APB_BEFORE_CHANGE){
timer->dev->config.enable = 0; timerStop(timer);
} else { } else {
old_apb /= 1000000; old_apb /= 1000000;
new_apb /= 1000000; new_apb /= 1000000;
timer->dev->config.divider = (new_apb * timer->dev->config.divider) / old_apb; uint16_t divider = (new_apb * timerGetDivider(timer)) / old_apb;
timer->dev->config.enable = 1; timerSetDivider(timer,divider);
timerStart(timer);
} }
} }
hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){ hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){
if(num > 3){ if(num >= NUM_OF_TIMERS)
{
log_e("Timer dont have that timer number.");
return NULL; return NULL;
} }
hw_timer_t * timer = &hw_timer[num];
if(timer->group) { hw_timer_t * timer = &timer_dev[num]; //Get Timer group/num from 0-3 number
periph_module_enable(PERIPH_TIMG1_MODULE);
} else { timer_config_t config = {
periph_module_enable(PERIPH_TIMG0_MODULE); .divider = divider,
} .counter_dir = countUp,
timer->dev->config.enable = 0; .counter_en = TIMER_PAUSE,
if(timer->group) { .alarm_en = TIMER_ALARM_DIS,
#if CONFIG_IDF_TARGET_ESP32 .auto_reload = false,
TIMERG1.int_ena.val &= ~BIT(timer->timer); };
#else
TIMERG1.int_ena_timers.val &= ~BIT(timer->timer); timer_init(timer->group, timer->num, &config);
#endif timer_set_counter_value(timer->group, timer->num, 0);
TIMERG1.int_clr_timers.val |= BIT(timer->timer); timerStart(timer);
} else {
#if CONFIG_IDF_TARGET_ESP32
TIMERG0.int_ena.val &= ~BIT(timer->timer);
#else
TIMERG0.int_ena_timers.val &= ~BIT(timer->timer);
#endif
TIMERG0.int_clr_timers.val |= BIT(timer->timer);
}
#ifdef TIMER_GROUP_SUPPORTS_XTAL_CLOCK
timer->dev->config.use_xtal = 0;
#endif
timerSetDivider(timer, divider);
timerSetCountUp(timer, countUp);
timerSetAutoReload(timer, false);
timerAttachInterrupt(timer, NULL, false);
timerWrite(timer, 0);
timer->dev->config.enable = 1;
addApbChangeCallback(timer, _on_apb_change); addApbChangeCallback(timer, _on_apb_change);
return timer; return timer;
} }
void timerEnd(hw_timer_t *timer){ void timerEnd(hw_timer_t *timer){
timer->dev->config.enable = 0;
timerAttachInterrupt(timer, NULL, false);
removeApbChangeCallback(timer, _on_apb_change); removeApbChangeCallback(timer, _on_apb_change);
timer_deinit(timer->group, timer->num);
} }
void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){ void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){
#if CONFIG_IDF_TARGET_ESP32
if(edge){ if(edge){
log_w("EDGE timer interrupt does not work properly on ESP32! Setting to LEVEL..."); log_w("EDGE timer interrupt is not supported! Setting to LEVEL...");
edge = false; edge = false;
} }
#endif timer_enable_intr(timer->group, timer->num);
static bool initialized = false;
static intr_handle_t intr_handle = NULL; timer_info_t *timer_info = calloc(1, sizeof(timer_info_t));
if(intr_handle){ timer_info->timer_group = timer->group;
esp_intr_disable(intr_handle); timer_info->timer_idx = timer->num;
} timer_info->auto_reload = timerGetAutoReload(timer);
if(fn == NULL){ timer_info->alarm_interval = timerAlarmRead(timer);
timer->dev->config.level_int_en = 0;
timer->dev->config.edge_int_en = 0; timer_isr_callback_add(timer->group, timer->num, (timer_isr_t)fn, timer_info, 0);
timer->dev->config.alarm_en = 0;
if(timer->num & 2){
#if CONFIG_IDF_TARGET_ESP32
TIMERG1.int_ena.val &= ~BIT(timer->timer);
#else
TIMERG1.int_ena_timers.val &= ~BIT(timer->timer);
#endif
TIMERG1.int_clr_timers.val |= BIT(timer->timer);
} else {
#if CONFIG_IDF_TARGET_ESP32
TIMERG0.int_ena.val &= ~BIT(timer->timer);
#else
TIMERG0.int_ena_timers.val &= ~BIT(timer->timer);
#endif
TIMERG0.int_clr_timers.val |= BIT(timer->timer);
}
__timerInterruptHandlers[timer->num] = NULL;
} else {
__timerInterruptHandlers[timer->num] = fn;
timer->dev->config.level_int_en = edge?0:1;//When set, an alarm will generate a level type interrupt.
timer->dev->config.edge_int_en = edge?1:0;//When set, an alarm will generate an edge type interrupt.
int intr_source = 0;
#ifndef CONFIG_IDF_TARGET_ESP32C3
if(!edge){
#endif
if(timer->group){
intr_source = ETS_TG1_T0_LEVEL_INTR_SOURCE + timer->timer;
} else {
intr_source = ETS_TG0_T0_LEVEL_INTR_SOURCE + timer->timer;
}
#ifndef CONFIG_IDF_TARGET_ESP32C3
} else {
if(timer->group){
intr_source = ETS_TG1_T0_EDGE_INTR_SOURCE + timer->timer;
} else {
intr_source = ETS_TG0_T0_EDGE_INTR_SOURCE + timer->timer;
}
}
#endif
if(!initialized){
initialized = true;
esp_intr_alloc(intr_source, (int)(ARDUINO_ISR_FLAG|ESP_INTR_FLAG_LOWMED), __timerISR, NULL, &intr_handle);
} else {
intr_matrix_set(esp_intr_get_cpu(intr_handle), intr_source, esp_intr_get_intno(intr_handle));
}
if(timer->group){
#if CONFIG_IDF_TARGET_ESP32
TIMERG1.int_ena.val |= BIT(timer->timer);
#else
TIMERG1.int_ena_timers.val |= BIT(timer->timer);
#endif
} else {
#if CONFIG_IDF_TARGET_ESP32
TIMERG0.int_ena.val |= BIT(timer->timer);
#else
TIMERG0.int_ena_timers.val |= BIT(timer->timer);
#endif
}
}
if(intr_handle){
esp_intr_enable(intr_handle);
}
} }
void timerDetachInterrupt(hw_timer_t *timer){ void timerDetachInterrupt(hw_timer_t *timer){
...@@ -354,6 +246,12 @@ uint64_t timerReadMicros(hw_timer_t *timer){ ...@@ -354,6 +246,12 @@ uint64_t timerReadMicros(hw_timer_t *timer){
return timer_val * div / (getApbFrequency() / 1000000); return timer_val * div / (getApbFrequency() / 1000000);
} }
uint64_t timerReadMilis(hw_timer_t *timer){
uint64_t timer_val = timerRead(timer);
uint16_t div = timerGetDivider(timer);
return timer_val * div / (getApbFrequency() / 1000);
}
double timerReadSeconds(hw_timer_t *timer){ double timerReadSeconds(hw_timer_t *timer){
uint64_t timer_val = timerRead(timer); uint64_t timer_val = timerRead(timer);
uint16_t div = timerGetDivider(timer); uint16_t div = timerGetDivider(timer);
...@@ -366,6 +264,12 @@ uint64_t timerAlarmReadMicros(hw_timer_t *timer){ ...@@ -366,6 +264,12 @@ uint64_t timerAlarmReadMicros(hw_timer_t *timer){
return timer_val * div / (getApbFrequency() / 1000000); return timer_val * div / (getApbFrequency() / 1000000);
} }
uint64_t timerAlarmReadMilis(hw_timer_t *timer){
uint64_t timer_val = timerAlarmRead(timer);
uint16_t div = timerGetDivider(timer);
return timer_val * div / (getApbFrequency() / 1000);
}
double timerAlarmReadSeconds(hw_timer_t *timer){ double timerAlarmReadSeconds(hw_timer_t *timer){
uint64_t timer_val = timerAlarmRead(timer); uint64_t timer_val = timerAlarmRead(timer);
uint16_t div = timerGetDivider(timer); uint16_t div = timerGetDivider(timer);
......
...@@ -20,13 +20,13 @@ ...@@ -20,13 +20,13 @@
#ifndef MAIN_ESP32_HAL_TIMER_H_ #ifndef MAIN_ESP32_HAL_TIMER_H_
#define MAIN_ESP32_HAL_TIMER_H_ #define MAIN_ESP32_HAL_TIMER_H_
#include "esp32-hal.h"
#include "freertos/FreeRTOS.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include "esp32-hal.h"
#include "freertos/FreeRTOS.h"
struct hw_timer_s; struct hw_timer_s;
typedef struct hw_timer_s hw_timer_t; typedef struct hw_timer_s hw_timer_t;
...@@ -50,6 +50,7 @@ void timerSetAutoReload(hw_timer_t *timer, bool autoreload); ...@@ -50,6 +50,7 @@ void timerSetAutoReload(hw_timer_t *timer, bool autoreload);
bool timerStarted(hw_timer_t *timer); bool timerStarted(hw_timer_t *timer);
uint64_t timerRead(hw_timer_t *timer); uint64_t timerRead(hw_timer_t *timer);
uint64_t timerReadMicros(hw_timer_t *timer); uint64_t timerReadMicros(hw_timer_t *timer);
uint64_t timerReadMilis(hw_timer_t *timer);
double timerReadSeconds(hw_timer_t *timer); double timerReadSeconds(hw_timer_t *timer);
uint16_t timerGetDivider(hw_timer_t *timer); uint16_t timerGetDivider(hw_timer_t *timer);
bool timerGetCountUp(hw_timer_t *timer); bool timerGetCountUp(hw_timer_t *timer);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment