mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
gptimer: new driver for previous timer group
This commit is contained in:
parent
79cc650d75
commit
5deb83b12d
@ -2,8 +2,11 @@ idf_build_get_property(target IDF_TARGET)
|
||||
|
||||
set(srcs
|
||||
"gpio.c"
|
||||
"gptimer.c"
|
||||
"timer_legacy.c"
|
||||
"i2c.c"
|
||||
"ledc.c"
|
||||
"legacy_new_driver_coexist.c"
|
||||
"periph_ctrl.c"
|
||||
"rtc_io.c"
|
||||
"rtc_module.c"
|
||||
@ -14,7 +17,6 @@ set(srcs
|
||||
"spi_master.c"
|
||||
"spi_slave.c"
|
||||
"spi_bus_lock.c"
|
||||
"timer.c"
|
||||
"uart.c")
|
||||
|
||||
set(includes "include" "${target}/include" "deprecated")
|
||||
|
@ -188,4 +188,30 @@ menu "Driver configurations"
|
||||
(e.g. SPI Flash write).
|
||||
endmenu # GDMA Configuration
|
||||
|
||||
menu "GPTimer Configuration"
|
||||
config GPTIMER_CTRL_FUNC_IN_IRAM
|
||||
bool "Place GPTimer control functions into IRAM"
|
||||
default n
|
||||
help
|
||||
Place GPTimer control functions (like start/stop) into IRAM,
|
||||
so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context.
|
||||
Enabling this option can improve driver performance as well.
|
||||
|
||||
config GPTIMER_ISR_IRAM_SAFE
|
||||
bool "GPTimer ISR IRAM-Safe"
|
||||
default n
|
||||
help
|
||||
This will ensure the GPTimer interrupt handle is IRAM-Safe, allow to avoid flash
|
||||
cache misses, and also be able to run whilst the cache is disabled.
|
||||
(e.g. SPI Flash write)
|
||||
|
||||
config GPTIMER_SUPPRESS_DEPRECATE_WARN
|
||||
bool "Suppress leagcy driver deprecated warning"
|
||||
default n
|
||||
help
|
||||
Wether to suppress the deprecation warnings when using legacy timer group driver (driver/timer.h).
|
||||
If you want to continue using the legacy driver, and don't want to see related deprecation warnings,
|
||||
you can enable this option.
|
||||
endmenu # GPTimer Configuration
|
||||
|
||||
endmenu # Driver configurations
|
||||
|
@ -7,147 +7,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_attr.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "hal/timer_types.h"
|
||||
#include "driver/timer_types_legacy.h"
|
||||
|
||||
#if !CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN
|
||||
#warning "legacy timer group driver is deprecated, please migrate to driver/gptimer.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Frequency of the clock on the input of the timer groups
|
||||
* @note This macro is not correct for Timer Groups with multiple clock sources (e.g. APB, XTAL)
|
||||
* So please don't use it in your application, we keep it here only for backward compatible
|
||||
*/
|
||||
#define TIMER_BASE_CLK (APB_CLK_FREQ)
|
||||
|
||||
/**
|
||||
* @brief Selects a Timer-Group out of 2 available groups
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_GROUP_0 = 0, /*!< Hw timer group 0 */
|
||||
#if SOC_TIMER_GROUPS > 1
|
||||
TIMER_GROUP_1 = 1, /*!< Hw timer group 1 */
|
||||
#endif
|
||||
TIMER_GROUP_MAX /*!< Maximum number of Hw timer groups */
|
||||
} timer_group_t;
|
||||
|
||||
/**
|
||||
* @brief Select a hardware timer from timer groups
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_0 = 0, /*!<Select timer0 of GROUPx*/
|
||||
#if SOC_TIMER_GROUP_TIMERS_PER_GROUP > 1
|
||||
TIMER_1 = 1, /*!<Select timer1 of GROUPx*/
|
||||
#endif
|
||||
TIMER_MAX,
|
||||
} timer_idx_t;
|
||||
|
||||
/**
|
||||
* @brief Interrupt types of the timer.
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_INTR_T0 = 1 << 0, /*!< interrupt of timer 0 */
|
||||
#if SOC_TIMER_GROUP_TIMERS_PER_GROUP > 1
|
||||
TIMER_INTR_T1 = 1 << 1, /*!< interrupt of timer 1 */
|
||||
TIMER_INTR_WDT = 1 << 2, /*!< interrupt of watchdog */
|
||||
#else
|
||||
TIMER_INTR_WDT = 1 << 1, /*!< interrupt of watchdog */
|
||||
#endif
|
||||
TIMER_INTR_NONE = 0
|
||||
} timer_intr_t;
|
||||
FLAG_ATTR(timer_intr_t)
|
||||
|
||||
/**
|
||||
* @brief Decides the direction of counter
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_COUNT_DOWN = GPTIMER_COUNT_DOWN, /*!< Descending Count from cnt.high|cnt.low*/
|
||||
TIMER_COUNT_UP = GPTIMER_COUNT_UP, /*!< Ascending Count from Zero*/
|
||||
TIMER_COUNT_MAX /*!< Maximum number of timer count directions */
|
||||
} timer_count_dir_t;
|
||||
|
||||
/**
|
||||
* @brief Decides whether timer is on or paused
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_PAUSE, /*!<Pause timer counter*/
|
||||
TIMER_START, /*!<Start timer counter*/
|
||||
} timer_start_t;
|
||||
|
||||
/**
|
||||
* @brief Decides whether to enable alarm mode
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_ALARM_DIS = 0, /*!< Disable timer alarm*/
|
||||
TIMER_ALARM_EN = 1, /*!< Enable timer alarm*/
|
||||
TIMER_ALARM_MAX
|
||||
} timer_alarm_t;
|
||||
|
||||
/**
|
||||
* @brief Select interrupt type if running in alarm mode.
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_INTR_LEVEL = 0, /*!< Interrupt mode: level mode*/
|
||||
TIMER_INTR_MAX
|
||||
} timer_intr_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Select if Alarm needs to be loaded by software or automatically reload by hardware.
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_AUTORELOAD_DIS = 0, /*!< Disable auto-reload: hardware will not load counter value after an alarm event*/
|
||||
TIMER_AUTORELOAD_EN = 1, /*!< Enable auto-reload: hardware will load counter value after an alarm event*/
|
||||
TIMER_AUTORELOAD_MAX,
|
||||
} timer_autoreload_t;
|
||||
|
||||
/**
|
||||
* @brief Select timer source clock.
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_SRC_CLK_APB = GPTIMER_CLK_SRC_APB, /*!< Select APB as the source clock*/
|
||||
#if SOC_TIMER_GROUP_SUPPORT_XTAL
|
||||
TIMER_SRC_CLK_XTAL = GPTIMER_CLK_SRC_XTAL, /*!< Select XTAL as the source clock*/
|
||||
#endif
|
||||
} timer_src_clk_t;
|
||||
|
||||
/**
|
||||
* @brief Data structure with timer's configuration settings
|
||||
*/
|
||||
typedef struct {
|
||||
timer_alarm_t alarm_en; /*!< Timer alarm enable */
|
||||
timer_start_t counter_en; /*!< Counter enable */
|
||||
timer_intr_mode_t intr_type; /*!< Interrupt mode */
|
||||
timer_count_dir_t counter_dir; /*!< Counter direction */
|
||||
timer_autoreload_t auto_reload; /*!< Timer auto-reload */
|
||||
timer_src_clk_t clk_src; /*!< Selects source clock. */
|
||||
uint32_t divider; /*!< Counter clock divider */
|
||||
} timer_config_t;
|
||||
|
||||
/**
|
||||
* @brief Interrupt handle callback function. User need to retrun a bool value
|
||||
* in callback.
|
||||
*
|
||||
* @return
|
||||
* - True Do task yield at the end of ISR
|
||||
* - False Not do task yield at the end of ISR
|
||||
*
|
||||
* @note If you called FreeRTOS functions in callback, you need to return true or false based on
|
||||
* the retrun value of argument `pxHigherPriorityTaskWoken`.
|
||||
* For example, `xQueueSendFromISR` is called in callback, if the return value `pxHigherPriorityTaskWoken`
|
||||
* of any FreeRTOS calls is pdTRUE, return true; otherwise return false.
|
||||
*/
|
||||
typedef bool (*timer_isr_t)(void *);
|
||||
|
||||
/**
|
||||
* @brief Interrupt handle, used in order to free the isr after use.
|
||||
* Aliases to an int handle for now.
|
||||
*/
|
||||
typedef intr_handle_t timer_isr_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Read the counter value of hardware timer.
|
||||
*
|
149
components/driver/deprecated/driver/timer_types_legacy.h
Normal file
149
components/driver/deprecated/driver/timer_types_legacy.h
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "hal/timer_types.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Frequency of the clock on the input of the timer groups
|
||||
* @note This macro is not correct for Timer Groups with multiple clock sources (e.g. APB, XTAL)
|
||||
* So please don't use it in your application, we keep it here only for backward compatible
|
||||
*/
|
||||
#define TIMER_BASE_CLK _Pragma ("GCC warning \"'TIMER_BASE_CLK' macro is deprecated\"") APB_CLK_FREQ
|
||||
|
||||
/**
|
||||
* @brief Timer-Group ID
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_GROUP_0 = 0, /*!< Hw timer group 0 */
|
||||
#if SOC_TIMER_GROUPS > 1
|
||||
TIMER_GROUP_1 = 1, /*!< Hw timer group 1 */
|
||||
#endif
|
||||
TIMER_GROUP_MAX /*!< Maximum number of Hw timer groups */
|
||||
} timer_group_t;
|
||||
|
||||
/**
|
||||
* @brief Timer ID
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_0 = 0, /*!< Select timer0 of GROUPx*/
|
||||
#if SOC_TIMER_GROUP_TIMERS_PER_GROUP > 1
|
||||
TIMER_1 = 1, /*!< Select timer1 of GROUPx*/
|
||||
#endif
|
||||
TIMER_MAX,
|
||||
} timer_idx_t;
|
||||
|
||||
/**
|
||||
* @brief Interrupt types of the timer.
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_INTR_T0 = 1 << 0, /*!< interrupt of timer 0 */
|
||||
#if SOC_TIMER_GROUP_TIMERS_PER_GROUP > 1
|
||||
TIMER_INTR_T1 = 1 << 1, /*!< interrupt of timer 1 */
|
||||
TIMER_INTR_WDT = 1 << 2, /*!< interrupt of watchdog */
|
||||
#else
|
||||
TIMER_INTR_WDT = 1 << 1, /*!< interrupt of watchdog */
|
||||
#endif
|
||||
TIMER_INTR_NONE = 0
|
||||
} timer_intr_t;
|
||||
FLAG_ATTR(timer_intr_t)
|
||||
|
||||
/**
|
||||
* @brief Timer count direction
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_COUNT_DOWN = GPTIMER_COUNT_DOWN, /*!< Descending Count from cnt.high|cnt.low*/
|
||||
TIMER_COUNT_UP = GPTIMER_COUNT_UP, /*!< Ascending Count from Zero*/
|
||||
TIMER_COUNT_MAX /*!< Maximum number of timer count directions */
|
||||
} timer_count_dir_t;
|
||||
|
||||
/**
|
||||
* @brief Timer start/stop command
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_PAUSE, /*!< Pause timer counter*/
|
||||
TIMER_START, /*!< Start timer counter*/
|
||||
} timer_start_t;
|
||||
|
||||
/**
|
||||
* @brief Timer alarm command
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_ALARM_DIS = 0, /*!< Disable timer alarm*/
|
||||
TIMER_ALARM_EN = 1, /*!< Enable timer alarm*/
|
||||
TIMER_ALARM_MAX
|
||||
} timer_alarm_t;
|
||||
|
||||
/**
|
||||
* @brief Timer interrupt type
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_INTR_LEVEL = 0, /*!< Interrupt mode: level mode*/
|
||||
TIMER_INTR_MAX
|
||||
} timer_intr_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Timer autoreload command
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_AUTORELOAD_DIS = 0, /*!< Disable auto-reload: hardware will not load counter value after an alarm event*/
|
||||
TIMER_AUTORELOAD_EN = 1, /*!< Enable auto-reload: hardware will load counter value after an alarm event*/
|
||||
TIMER_AUTORELOAD_MAX,
|
||||
} timer_autoreload_t;
|
||||
|
||||
/**
|
||||
* @brief Timer group clock source
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_SRC_CLK_APB = GPTIMER_CLK_SRC_APB, /*!< Select APB as the source clock*/
|
||||
#if SOC_TIMER_GROUP_SUPPORT_XTAL
|
||||
TIMER_SRC_CLK_XTAL = GPTIMER_CLK_SRC_XTAL, /*!< Select XTAL as the source clock*/
|
||||
#endif
|
||||
} timer_src_clk_t;
|
||||
|
||||
/**
|
||||
* @brief Interrupt handler callback function
|
||||
*
|
||||
* @return
|
||||
* - True Do task yield at the end of ISR
|
||||
* - False Not do task yield at the end of ISR
|
||||
*
|
||||
* @note If you called FreeRTOS functions in callback, you need to return true or false based on
|
||||
* the retrun value of argument `pxHigherPriorityTaskWoken`.
|
||||
* For example, `xQueueSendFromISR` is called in callback, if the return value `pxHigherPriorityTaskWoken`
|
||||
* of any FreeRTOS calls is pdTRUE, return true; otherwise return false.
|
||||
*/
|
||||
typedef bool (*timer_isr_t)(void *);
|
||||
|
||||
/**
|
||||
* @brief Interrupt handle, used in order to free the isr after use.
|
||||
*/
|
||||
typedef intr_handle_t timer_isr_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Timer configurations
|
||||
*/
|
||||
typedef struct {
|
||||
timer_alarm_t alarm_en; /*!< Timer alarm enable */
|
||||
timer_start_t counter_en; /*!< Counter enable */
|
||||
timer_intr_mode_t intr_type; /*!< Interrupt mode */
|
||||
timer_count_dir_t counter_dir; /*!< Counter direction */
|
||||
timer_autoreload_t auto_reload; /*!< Timer auto-reload */
|
||||
timer_src_clk_t clk_src; /*!< Selects source clock. */
|
||||
uint32_t divider; /*!< Counter clock divider */
|
||||
} timer_config_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
530
components/driver/gptimer.c
Normal file
530
components/driver/gptimer.c
Normal file
@ -0,0 +1,530 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG // uncomment this line to enable debug logs
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/lock.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_pm.h"
|
||||
#include "driver/gptimer.h"
|
||||
#include "hal/timer_types.h"
|
||||
#include "hal/timer_hal.h"
|
||||
#include "hal/timer_ll.h"
|
||||
#include "soc/timer_periph.h"
|
||||
#include "soc/soc_memory_types.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "esp_private/esp_clk.h"
|
||||
|
||||
// If ISR handler is allowed to run whilst cache is disabled,
|
||||
// Make sure all the code and related variables used by the handler are in the SRAM
|
||||
#if CONFIG_GPTIMER_ISR_IRAM_SAFE
|
||||
#define GPTIMER_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED)
|
||||
#define GPTIMER_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#else
|
||||
#define GPTIMER_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED
|
||||
#define GPTIMER_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||
#endif //CONFIG_GPTIMER_ISR_IRAM_SAFE
|
||||
|
||||
#if CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
|
||||
#define GPTIMER_CTRL_FUNC_ATTR IRAM_ATTR
|
||||
#else
|
||||
#define GPTIMER_CTRL_FUNC_ATTR
|
||||
#endif // CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
|
||||
|
||||
#define GPTIMER_PM_LOCK_NAME_LEN_MAX 16
|
||||
|
||||
static const char *TAG = "gptimer";
|
||||
|
||||
typedef struct gptimer_platform_t gptimer_platform_t;
|
||||
typedef struct gptimer_group_t gptimer_group_t;
|
||||
typedef struct gptimer_t gptimer_t;
|
||||
|
||||
struct gptimer_platform_t {
|
||||
_lock_t mutex; // platform level mutex lock
|
||||
gptimer_group_t *groups[SOC_TIMER_GROUPS]; // timer group pool
|
||||
int group_ref_counts[SOC_TIMER_GROUPS]; // reference count used to protect group install/uninstall
|
||||
};
|
||||
|
||||
struct gptimer_group_t {
|
||||
int group_id;
|
||||
portMUX_TYPE spinlock; // to protect per-group register level concurrent access
|
||||
gptimer_t *timers[SOC_TIMER_GROUP_TIMERS_PER_GROUP];
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
GPTIMER_FSM_STOP,
|
||||
GPTIMER_FSM_START,
|
||||
} gptimer_lifecycle_fsm_t;
|
||||
|
||||
struct gptimer_t {
|
||||
gptimer_group_t *group;
|
||||
int timer_id;
|
||||
unsigned int resolution_hz;
|
||||
unsigned long long reload_count;
|
||||
unsigned long long alarm_count;
|
||||
gptimer_count_direction_t direction;
|
||||
timer_hal_context_t hal;
|
||||
gptimer_lifecycle_fsm_t fsm; // access to fsm should be protect by spinlock, as fsm is also accessed from ISR handler
|
||||
intr_handle_t intr;
|
||||
_lock_t mutex; // to protect other resource allocation, like interrupt handle
|
||||
portMUX_TYPE spinlock; // to protect per-timer resources concurent accessed by task and ISR handler
|
||||
gptimer_alarm_cb_t on_alarm;
|
||||
void *user_ctx;
|
||||
esp_pm_lock_handle_t pm_lock; // power management lock
|
||||
#if CONFIG_PM_ENABLE
|
||||
char pm_lock_name[GPTIMER_PM_LOCK_NAME_LEN_MAX]; // pm lock name
|
||||
#endif
|
||||
struct {
|
||||
uint32_t intr_shared: 1;
|
||||
uint32_t auto_reload_on_alarm: 1;
|
||||
uint32_t alarm_en: 1;
|
||||
} flags;
|
||||
};
|
||||
|
||||
// gptimer driver platform, it's always a singleton
|
||||
static gptimer_platform_t s_platform;
|
||||
|
||||
static gptimer_group_t *gptimer_acquire_group_handle(int group_id);
|
||||
static void gptimer_release_group_handle(gptimer_group_t *group);
|
||||
static esp_err_t gptimer_select_periph_clock(gptimer_t *timer, gptimer_clock_source_t src_clk, uint32_t resolution_hz);
|
||||
static esp_err_t gptimer_install_interrupt(gptimer_t *timer);
|
||||
IRAM_ATTR static void gptimer_default_isr(void *args);
|
||||
|
||||
esp_err_t gptimer_new_timer(const gptimer_config_t *config, gptimer_handle_t *ret_timer)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
gptimer_group_t *group = NULL;
|
||||
gptimer_t *timer = NULL;
|
||||
int group_id = -1;
|
||||
int timer_id = -1;
|
||||
ESP_GOTO_ON_FALSE(config && ret_timer, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(config->resolution_hz, ESP_ERR_INVALID_ARG, err, TAG, "invalid timer resolution:%d", config->resolution_hz);
|
||||
|
||||
timer = heap_caps_calloc(1, sizeof(gptimer_t), GPTIMER_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(timer, ESP_ERR_NO_MEM, err, TAG, "no mem for gptimer");
|
||||
|
||||
for (int i = 0; (i < SOC_TIMER_GROUPS) && (timer_id < 0); i++) {
|
||||
group = gptimer_acquire_group_handle(i);
|
||||
ESP_GOTO_ON_FALSE(group, ESP_ERR_NO_MEM, err, TAG, "no mem for group (%d)", group_id);
|
||||
// loop to search free timer in the group
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
for (int j = 0; j < SOC_TIMER_GROUP_TIMERS_PER_GROUP; j++) {
|
||||
if (!group->timers[j]) {
|
||||
group_id = i;
|
||||
timer_id = j;
|
||||
group->timers[j] = timer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
if (timer_id < 0) {
|
||||
gptimer_release_group_handle(group);
|
||||
group = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_GOTO_ON_FALSE(timer_id != -1, ESP_ERR_NOT_FOUND, err, TAG, "no free timer");
|
||||
timer->timer_id = timer_id;
|
||||
timer->group = group;
|
||||
// initialize HAL layer
|
||||
timer_hal_init(&timer->hal, group_id, timer_id);
|
||||
// stop counter, alarm, auto-reload
|
||||
timer_ll_enable_counter(timer->hal.dev, timer_id, false);
|
||||
timer_ll_enable_auto_reload(timer->hal.dev, timer_id, false);
|
||||
timer_ll_enable_alarm(timer->hal.dev, timer_id, false);
|
||||
// select clock source, set clock resolution
|
||||
ESP_GOTO_ON_ERROR(gptimer_select_periph_clock(timer, config->clk_src, config->resolution_hz), err, TAG, "set periph clock failed");
|
||||
// initialize counter value to zero
|
||||
timer_hal_set_counter_value(&timer->hal, 0);
|
||||
// set counting direction
|
||||
timer_ll_set_count_direction(timer->hal.dev, timer_id, config->direction);
|
||||
|
||||
// interrupt register is shared by all timers in the same group
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
timer_ll_enable_intr(timer->hal.dev, TIMER_LL_EVENT_ALARM(timer_id), false); // disable interrupt
|
||||
timer_ll_clear_intr_status(timer->hal.dev, TIMER_LL_EVENT_ALARM(timer_id)); // clear pending interrupt event
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
// initialize other members of timer
|
||||
timer->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
timer->fsm = GPTIMER_FSM_STOP;
|
||||
timer->direction = config->direction;
|
||||
timer->flags.intr_shared = config->flags.intr_shared;
|
||||
_lock_init(&timer->mutex);
|
||||
ESP_LOGD(TAG, "new gptimer (%d,%d) at %p, resolution=%uHz", group_id, timer_id, timer, timer->resolution_hz);
|
||||
*ret_timer = timer;
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (timer) {
|
||||
if (timer->pm_lock) {
|
||||
esp_pm_lock_delete(timer->pm_lock);
|
||||
}
|
||||
free(timer);
|
||||
}
|
||||
if (group) {
|
||||
gptimer_release_group_handle(group);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t gptimer_del_timer(gptimer_handle_t timer)
|
||||
{
|
||||
gptimer_group_t *group = NULL;
|
||||
bool valid_state = true;
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
portENTER_CRITICAL(&timer->spinlock);
|
||||
if (timer->fsm != GPTIMER_FSM_STOP) {
|
||||
valid_state = false;
|
||||
}
|
||||
portEXIT_CRITICAL(&timer->spinlock);
|
||||
ESP_RETURN_ON_FALSE(valid_state, ESP_ERR_INVALID_STATE, TAG, "can't delete timer as it's not in stop state");
|
||||
group = timer->group;
|
||||
int group_id = group->group_id;
|
||||
int timer_id = timer->timer_id;
|
||||
|
||||
if (timer->intr) {
|
||||
esp_intr_free(timer->intr);
|
||||
ESP_LOGD(TAG, "uninstall interrupt service for timer (%d,%d)", group_id, timer_id);
|
||||
}
|
||||
if (timer->pm_lock) {
|
||||
esp_pm_lock_delete(timer->pm_lock);
|
||||
ESP_LOGD(TAG, "uninstall APB_FREQ_MAX lock for timer (%d,%d)", group_id, timer_id);
|
||||
}
|
||||
_lock_close(&timer->mutex);
|
||||
free(timer);
|
||||
ESP_LOGD(TAG, "del timer (%d,%d)", group_id, timer_id);
|
||||
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
group->timers[timer_id] = NULL;
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
// timer has a reference on group, release it now
|
||||
gptimer_release_group_handle(group);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
GPTIMER_CTRL_FUNC_ATTR
|
||||
esp_err_t gptimer_set_raw_count(gptimer_handle_t timer, unsigned long long value)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
portENTER_CRITICAL_SAFE(&timer->spinlock);
|
||||
timer_hal_set_counter_value(&timer->hal, value);
|
||||
portEXIT_CRITICAL_SAFE(&timer->spinlock);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
GPTIMER_CTRL_FUNC_ATTR
|
||||
esp_err_t gptimer_get_raw_count(gptimer_handle_t timer, unsigned long long *value)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer && value, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
portENTER_CRITICAL_SAFE(&timer->spinlock);
|
||||
*value = timer_ll_get_counter_value(timer->hal.dev, timer->timer_id);
|
||||
portEXIT_CRITICAL_SAFE(&timer->spinlock);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer_event_callbacks_t *cbs, void *user_data)
|
||||
{
|
||||
gptimer_group_t *group = NULL;
|
||||
ESP_RETURN_ON_FALSE(timer && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
group = timer->group;
|
||||
|
||||
#if CONFIG_GPTIMER_ISR_IRAM_SAFE
|
||||
if (cbs->on_alarm) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_alarm), ESP_ERR_INVALID_ARG, TAG, "on_alarm callback not in IRAM");
|
||||
}
|
||||
if (user_data) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_dram(user_data) ||
|
||||
esp_ptr_in_diram_dram(user_data) ||
|
||||
esp_ptr_in_rtc_dram_fast(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in DRAM");
|
||||
}
|
||||
#endif
|
||||
|
||||
// lazy install interrupt service
|
||||
ESP_RETURN_ON_ERROR(gptimer_install_interrupt(timer), TAG, "install interrupt service failed");
|
||||
|
||||
// enable/disable GPTimer interrupt events
|
||||
portENTER_CRITICAL_SAFE(&group->spinlock);
|
||||
timer_ll_enable_intr(timer->hal.dev, TIMER_LL_EVENT_ALARM(timer->timer_id), cbs->on_alarm); // enable timer interrupt
|
||||
portEXIT_CRITICAL_SAFE(&group->spinlock);
|
||||
|
||||
timer->on_alarm = cbs->on_alarm;
|
||||
timer->user_ctx = user_data;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
GPTIMER_CTRL_FUNC_ATTR
|
||||
esp_err_t gptimer_set_alarm_action(gptimer_handle_t timer, const gptimer_alarm_config_t *config)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
if (config) {
|
||||
// When auto_reload is enabled, alarm_count should not be equal to reload_count
|
||||
bool valid_auto_reload = !config->flags.auto_reload_on_alarm || config->alarm_count != config->reload_count;
|
||||
ESP_RETURN_ON_FALSE(valid_auto_reload, ESP_ERR_INVALID_ARG, TAG, "reload count can't equal to alarm count");
|
||||
|
||||
timer->reload_count = config->reload_count;
|
||||
timer->alarm_count = config->alarm_count;
|
||||
timer->flags.auto_reload_on_alarm = config->flags.auto_reload_on_alarm;
|
||||
timer->flags.alarm_en = true;
|
||||
|
||||
portENTER_CRITICAL_SAFE(&timer->spinlock);
|
||||
timer_ll_set_reload_value(timer->hal.dev, timer->timer_id, config->reload_count);
|
||||
timer_ll_set_alarm_value(timer->hal.dev, timer->timer_id, config->alarm_count);
|
||||
portEXIT_CRITICAL_SAFE(&timer->spinlock);
|
||||
} else {
|
||||
timer->flags.auto_reload_on_alarm = false;
|
||||
timer->flags.alarm_en = false;
|
||||
}
|
||||
|
||||
portENTER_CRITICAL_SAFE(&timer->spinlock);
|
||||
timer_ll_enable_auto_reload(timer->hal.dev, timer->timer_id, timer->flags.auto_reload_on_alarm);
|
||||
timer_ll_enable_alarm(timer->hal.dev, timer->timer_id, timer->flags.alarm_en);
|
||||
portEXIT_CRITICAL_SAFE(&timer->spinlock);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
GPTIMER_CTRL_FUNC_ATTR
|
||||
esp_err_t gptimer_start(gptimer_handle_t timer)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
// acquire power manager lock
|
||||
if (timer->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(timer->pm_lock), TAG, "acquire APB_FREQ_MAX lock failed");
|
||||
}
|
||||
// interrupt interupt service
|
||||
if (timer->intr) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_enable(timer->intr), TAG, "enable interrupt service failed");
|
||||
}
|
||||
|
||||
portENTER_CRITICAL_SAFE(&timer->spinlock);
|
||||
timer_ll_enable_counter(timer->hal.dev, timer->timer_id, true);
|
||||
timer_ll_enable_alarm(timer->hal.dev, timer->timer_id, timer->flags.alarm_en);
|
||||
timer->fsm = GPTIMER_FSM_START;
|
||||
portEXIT_CRITICAL_SAFE(&timer->spinlock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
GPTIMER_CTRL_FUNC_ATTR
|
||||
esp_err_t gptimer_stop(gptimer_handle_t timer)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
// disable counter, alarm, autoreload
|
||||
portENTER_CRITICAL_SAFE(&timer->spinlock);
|
||||
timer_ll_enable_counter(timer->hal.dev, timer->timer_id, false);
|
||||
timer_ll_enable_alarm(timer->hal.dev, timer->timer_id, false);
|
||||
timer->fsm = GPTIMER_FSM_STOP;
|
||||
portEXIT_CRITICAL_SAFE(&timer->spinlock);
|
||||
|
||||
// disable interrupt service
|
||||
if (timer->intr) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_disable(timer->intr), TAG, "disable interrupt service failed");
|
||||
}
|
||||
// release power manager lock
|
||||
if (timer->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_release(timer->pm_lock), TAG, "release APB_FREQ_MAX lock failed");
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static gptimer_group_t *gptimer_acquire_group_handle(int group_id)
|
||||
{
|
||||
// esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
bool new_group = false;
|
||||
gptimer_group_t *group = NULL;
|
||||
|
||||
// prevent install timer group concurrently
|
||||
_lock_acquire(&s_platform.mutex);
|
||||
if (!s_platform.groups[group_id]) {
|
||||
group = heap_caps_calloc(1, sizeof(gptimer_group_t), GPTIMER_MEM_ALLOC_CAPS);
|
||||
if (group) {
|
||||
new_group = true;
|
||||
s_platform.groups[group_id] = group;
|
||||
// initialize timer group members
|
||||
group->group_id = group_id;
|
||||
group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
// enable APB access timer registers
|
||||
periph_module_enable(timer_group_periph_signals.groups[group_id].module);
|
||||
}
|
||||
} else {
|
||||
group = s_platform.groups[group_id];
|
||||
}
|
||||
// someone acquired the group handle means we have a new object that refer to this group
|
||||
s_platform.group_ref_counts[group_id]++;
|
||||
_lock_release(&s_platform.mutex);
|
||||
|
||||
if (new_group) {
|
||||
ESP_LOGD(TAG, "new group (%d) @%p", group_id, group);
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
static void gptimer_release_group_handle(gptimer_group_t *group)
|
||||
{
|
||||
int group_id = group->group_id;
|
||||
bool do_deinitialize = false;
|
||||
|
||||
_lock_acquire(&s_platform.mutex);
|
||||
s_platform.group_ref_counts[group_id]--;
|
||||
if (s_platform.group_ref_counts[group_id] == 0) {
|
||||
assert(s_platform.groups[group_id]);
|
||||
do_deinitialize = true;
|
||||
s_platform.groups[group_id] = NULL;
|
||||
// Theoretically we need to disable the peripheral clock for the timer group
|
||||
// However, next time when we enable the peripheral again, the registers will be reset to default value, including the watchdog registers inside the group
|
||||
// Then the watchdog will go into reset state, e.g. the flash boot watchdog is enabled again and reset the system very soon
|
||||
// periph_module_disable(timer_group_periph_signals.groups[group_id].module);
|
||||
}
|
||||
_lock_release(&s_platform.mutex);
|
||||
|
||||
if (do_deinitialize) {
|
||||
free(group);
|
||||
ESP_LOGD(TAG, "del group (%d)", group_id);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t gptimer_select_periph_clock(gptimer_t *timer, gptimer_clock_source_t src_clk, uint32_t resolution_hz)
|
||||
{
|
||||
unsigned int counter_src_hz = 0;
|
||||
esp_err_t ret = ESP_OK;
|
||||
int timer_id = timer->timer_id;
|
||||
switch (src_clk) {
|
||||
case GPTIMER_CLK_SRC_APB:
|
||||
counter_src_hz = esp_clk_apb_freq();
|
||||
#if CONFIG_PM_ENABLE
|
||||
sprintf(timer->pm_lock_name, "gptimer_%d_%d", timer->group->group_id, timer_id); // e.g. gptimer_0_0
|
||||
ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, timer->pm_lock_name, &timer->pm_lock);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "create APB_FREQ_MAX lock failed");
|
||||
ESP_LOGD(TAG, "install APB_FREQ_MAX lock for timer (%d,%d)", timer->group->group_id, timer_id);
|
||||
#endif
|
||||
break;
|
||||
#if SOC_TIMER_GROUP_SUPPORT_XTAL
|
||||
case GPTIMER_CLK_SRC_XTAL:
|
||||
counter_src_hz = esp_clk_xtal_freq();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "clock source %d is not support", src_clk);
|
||||
break;
|
||||
}
|
||||
timer_ll_set_clock_source(timer->hal.dev, timer_id, src_clk);
|
||||
unsigned int prescale = counter_src_hz / resolution_hz; // potential resolution loss here
|
||||
timer_ll_set_clock_prescale(timer->hal.dev, timer_id, prescale);
|
||||
timer->resolution_hz = counter_src_hz / prescale; // this is the real resolution
|
||||
if (timer->resolution_hz != resolution_hz) {
|
||||
ESP_LOGW(TAG, "resolution lost, expect %ul, real %ul", resolution_hz, timer->resolution_hz);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t gptimer_install_interrupt(gptimer_t *timer)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
gptimer_group_t *group = timer->group;
|
||||
int group_id = group->group_id;
|
||||
int timer_id = timer->timer_id;
|
||||
bool new_isr = false;
|
||||
|
||||
if (!timer->intr) {
|
||||
_lock_acquire(&timer->mutex);
|
||||
if (!timer->intr) {
|
||||
// if user wants to control the interrupt allocation more precisely, we can expose more flags in `gptimer_config_t`
|
||||
int extra_isr_flags = timer->flags.intr_shared ? ESP_INTR_FLAG_SHARED : 0;
|
||||
ret = esp_intr_alloc_intrstatus(timer_group_periph_signals.groups[group_id].timer_irq_id[timer_id], extra_isr_flags | GPTIMER_INTR_ALLOC_FLAGS,
|
||||
(uint32_t)timer_ll_get_intr_status_reg(timer->hal.dev), TIMER_LL_EVENT_ALARM(timer_id),
|
||||
gptimer_default_isr, timer, &timer->intr);
|
||||
new_isr = (ret == ESP_OK);
|
||||
}
|
||||
_lock_release(&timer->mutex);
|
||||
}
|
||||
|
||||
if (new_isr) {
|
||||
ESP_LOGD(TAG, "install interrupt service for timer (%d,%d)", group_id, timer_id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Put the default ISR handler in the IRAM for better performance
|
||||
IRAM_ATTR static void gptimer_default_isr(void *args)
|
||||
{
|
||||
bool need_yield = false;
|
||||
gptimer_t *timer = (gptimer_t *)args;
|
||||
gptimer_group_t *group = timer->group;
|
||||
gptimer_alarm_cb_t on_alarm_cb = timer->on_alarm;
|
||||
uint32_t intr_status = timer_ll_get_intr_status(timer->hal.dev);
|
||||
|
||||
if (intr_status & TIMER_LL_EVENT_ALARM(timer->timer_id)) {
|
||||
// Note: when alarm event happends, the alarm will be disabled automatically by hardware
|
||||
gptimer_alarm_event_data_t edata = {
|
||||
.count_value = timer_ll_get_counter_value(timer->hal.dev, timer->timer_id),
|
||||
.alarm_value = timer->alarm_count,
|
||||
};
|
||||
|
||||
portENTER_CRITICAL_ISR(&group->spinlock);
|
||||
timer_ll_clear_intr_status(timer->hal.dev, TIMER_LL_EVENT_ALARM(timer->timer_id));
|
||||
// for auto-reload, we need to re-enable the alarm manually
|
||||
if (timer->flags.auto_reload_on_alarm) {
|
||||
timer_ll_enable_alarm(timer->hal.dev, timer->timer_id, true);
|
||||
}
|
||||
portEXIT_CRITICAL_ISR(&group->spinlock);
|
||||
|
||||
if (on_alarm_cb) {
|
||||
if (on_alarm_cb(timer, &edata, timer->user_ctx)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (need_yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///// The Following APIs are for internal use only (e.g. unit test) /////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
esp_err_t gptimer_get_intr_handle(gptimer_handle_t timer, intr_handle_t *ret_intr_handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer && ret_intr_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
*ret_intr_handle = timer->intr;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t gptimer_get_pm_lock(gptimer_handle_t timer, esp_pm_lock_handle_t *ret_pm_lock)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer && ret_pm_lock, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
*ret_pm_lock = timer->pm_lock;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function will be called during start up, to check that gptimer driver is not running along with the legacy timer group driver
|
||||
*/
|
||||
__attribute__((constructor))
|
||||
static void check_gptimer_driver_conflict(void)
|
||||
{
|
||||
extern int timer_group_driver_init_count;
|
||||
timer_group_driver_init_count++;
|
||||
if (timer_group_driver_init_count > 1) {
|
||||
ESP_EARLY_LOGE(TAG, "CONFLICT! The gptimer driver can't work along with the legacy timer group driver");
|
||||
abort();
|
||||
}
|
||||
}
|
197
components/driver/include/driver/gptimer.h
Normal file
197
components/driver/include/driver/gptimer.h
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "hal/timer_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of General Purpose Timer handle
|
||||
*/
|
||||
typedef struct gptimer_t *gptimer_handle_t;
|
||||
|
||||
/**
|
||||
* @brief GPTimer event data
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t count_value; /*!< Current count value */
|
||||
uint64_t alarm_value; /*!< Current alarm value */
|
||||
} gptimer_alarm_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Timer alarm callback prototype
|
||||
*
|
||||
* @param[in] timer Timer handle created by `gptimer_new_timer()`
|
||||
* @param[in] edata Alarm event data, fed by driver
|
||||
* @param[in] user_ctx User data, passed from `gptimer_config_t`
|
||||
* @return Whether a high priority task has been waken up by this function
|
||||
*/
|
||||
typedef bool (*gptimer_alarm_cb_t) (gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx);
|
||||
|
||||
/**
|
||||
* @brief Group of supported GPTimer callbacks
|
||||
* @note The callbacks are all running under ISR environment
|
||||
*/
|
||||
typedef struct {
|
||||
gptimer_alarm_cb_t on_alarm; /*!< Timer alarm callback */
|
||||
} gptimer_event_callbacks_t;
|
||||
|
||||
/**
|
||||
* @brief General Purpose Timer configuration
|
||||
*/
|
||||
typedef struct {
|
||||
gptimer_clock_source_t clk_src; /*!< GPTimer clock source */
|
||||
gptimer_count_direction_t direction; /*!< Count direction */
|
||||
uint32_t resolution_hz; /*!< Counter resolution (working frequency) in Hz,
|
||||
hence, the step size of each count tick equals to (1 / resolution_hz) seconds */
|
||||
struct {
|
||||
uint32_t intr_shared: 1; /*!< Set true, the timer interrupt number can be shared with other peripherals */
|
||||
} flags;
|
||||
} gptimer_config_t;
|
||||
|
||||
/**
|
||||
* @brief General Purpose Timer alarm configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t alarm_count; /*!< Alarm target count value */
|
||||
uint64_t reload_count; /*!< Alarm reload count value, effect only when `auto_reload_on_alarm` is set to true */
|
||||
struct {
|
||||
uint32_t auto_reload_on_alarm: 1; /*!< Reload the count value by hardware, immediately at the alarm event */
|
||||
} flags;
|
||||
} gptimer_alarm_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create a new General Purpose Timer, and return the handle
|
||||
*
|
||||
* @note Once a timer is created, it is placed in the stopped state and will not start until `gptimer_start()` is called.
|
||||
*
|
||||
* @param[in] config GPTimer configuration
|
||||
* @param[out] ret_timer Returned timer handle
|
||||
* @return
|
||||
* - ESP_OK: Create GPTimer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Create GPTimer failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Create GPTimer failed because out of memory
|
||||
* - ESP_ERR_NOT_FOUND: Create GPTimer failed because all hardware timers are used up and no more free one
|
||||
* - ESP_FAIL: Create GPTimer failed because of other error
|
||||
*/
|
||||
esp_err_t gptimer_new_timer(const gptimer_config_t *config, gptimer_handle_t *ret_timer);
|
||||
|
||||
/**
|
||||
* @brief Delete the GPTimer handle
|
||||
*
|
||||
* @note A timer must be in a stop state before it can be deleted.
|
||||
*
|
||||
* @param[in] timer Timer handle created by `gptimer_new_timer()`
|
||||
* @return
|
||||
* - ESP_OK: Delete GPTimer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Delete GPTimer failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Delete GPTimer failed because the timer has not stopped
|
||||
* - ESP_FAIL: Delete GPTimer failed because of other error
|
||||
*/
|
||||
esp_err_t gptimer_del_timer(gptimer_handle_t timer);
|
||||
|
||||
/**
|
||||
* @brief Set GPTimer raw count value
|
||||
*
|
||||
* @note When updating the raw count of an active timer, the timer will immediately start counting from the new value.
|
||||
* @note This function is allowed to run within ISR context
|
||||
* @note This function is allowed to be executed when Cache is disabled, by enabling `CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM`
|
||||
*
|
||||
* @param[in] timer Timer handle created by `gptimer_new_timer()`
|
||||
* @param[in] value Count value to be set
|
||||
* @return
|
||||
* - ESP_OK: Set GPTimer raw count value successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set GPTimer raw count value failed because of invalid argument
|
||||
* - ESP_FAIL: Set GPTimer raw count value failed because of other error
|
||||
*/
|
||||
esp_err_t gptimer_set_raw_count(gptimer_handle_t timer, uint64_t value);
|
||||
|
||||
/**
|
||||
* @brief Get GPTimer raw count value
|
||||
*
|
||||
* @note With the raw count value and the resolution set in the `gptimer_config_t`, you can convert the count value into seconds.
|
||||
* @note This function is allowed to run within ISR context
|
||||
* @note This function is allowed to be executed when Cache is disabled, by enabling `CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM`
|
||||
*
|
||||
* @param[in] timer Timer handle created by `gptimer_new_timer()`
|
||||
* @param[out] value Returned GPTimer count value
|
||||
* @return
|
||||
* - ESP_OK: Get GPTimer raw count value successfully
|
||||
* - ESP_ERR_INVALID_ARG: Get GPTimer raw count value failed because of invalid argument
|
||||
* - ESP_FAIL: Get GPTimer raw count value failed because of other error
|
||||
*/
|
||||
esp_err_t gptimer_get_raw_count(gptimer_handle_t timer, uint64_t *value);
|
||||
|
||||
/**
|
||||
* @brief Set callbacks for GPTimer
|
||||
*
|
||||
* @note The user registered callbacks are expected to be runnable within ISR context
|
||||
*
|
||||
* @param[in] timer Timer handle created by `gptimer_new_timer()`
|
||||
* @param[in] cbs Group of callback functions
|
||||
* @param[in] user_data User data, which will be passed to callback functions directly
|
||||
* @return
|
||||
* - ESP_OK: Set event callbacks successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
|
||||
* - ESP_FAIL: Set event callbacks failed because of other error
|
||||
*/
|
||||
esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Set alarm event actions for GPTimer.
|
||||
*
|
||||
* @note This function is allowed to run within ISR context
|
||||
* @note This function is allowed to be executed when Cache is disabled, by enabling `CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM`
|
||||
*
|
||||
* @param[in] timer Timer handle created by `gptimer_new_timer()`
|
||||
* @param[in] config Alarm configuration, especially, set config to NULL means disabling the alarm function
|
||||
* @return
|
||||
* - ESP_OK: Set alarm action for GPTimer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set alarm action for GPTimer failed because of invalid argument
|
||||
* - ESP_FAIL: Set alarm action for GPTimer failed because of other error
|
||||
*/
|
||||
esp_err_t gptimer_set_alarm_action(gptimer_handle_t timer, const gptimer_alarm_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Start GPTimer
|
||||
*
|
||||
* @note This function is allowed to run within ISR context
|
||||
* @note This function is allowed to be executed when Cache is disabled, by enabling `CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM`
|
||||
*
|
||||
* @param[in] timer Timer handle created by `gptimer_new_timer()`
|
||||
* @return
|
||||
* - ESP_OK: Start GPTimer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Start GPTimer failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Start GPTimer failed because the timer is not in stop state
|
||||
* - ESP_FAIL: Start GPTimer failed because of other error
|
||||
*/
|
||||
esp_err_t gptimer_start(gptimer_handle_t timer);
|
||||
|
||||
/**
|
||||
* @brief Stop GPTimer
|
||||
*
|
||||
* @note This function is allowed to run within ISR context
|
||||
* @note This function is allowed to be executed when Cache is disabled, by enabling `CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM`
|
||||
*
|
||||
* @param[in] timer Timer handle created by `gptimer_new_timer()`
|
||||
* @return
|
||||
* - ESP_OK: Stop GPTimer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Stop GPTimer failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Stop GPTimer failed because the timer is not in start state
|
||||
* - ESP_FAIL: Stop GPTimer failed because of other error
|
||||
*/
|
||||
esp_err_t gptimer_stop(gptimer_handle_t timer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
47
components/driver/include/esp_private/gptimer.h
Normal file
47
components/driver/include/esp_private/gptimer.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// DO NOT USE THESE APIS IN YOUR APPLICATIONS
|
||||
// The following APIs are for internal use, public to other IDF components, but not for users' applications.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_pm.h"
|
||||
#include "driver/gptimer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get GPTimer interrupt handle
|
||||
*
|
||||
* @param[in] timer Timer handle created by `gptimer_new_timer()`
|
||||
* @param[out] ret_intr_handle Timer's internal interrupt handle
|
||||
* @return
|
||||
* - ESP_OK: Get GPTimer interrupt handle successfully
|
||||
* - ESP_ERR_INVALID_ARG: Get GPTimer interrupt handle failed because of invalid argument
|
||||
* - ESP_FAIL: Get GPTimer interrupt handle failed because of other error
|
||||
*/
|
||||
esp_err_t gptimer_get_intr_handle(gptimer_handle_t timer, intr_handle_t *ret_intr_handle);
|
||||
|
||||
/**
|
||||
* @brief Get GPTimer power management lock
|
||||
*
|
||||
* @param[in] timer Timer handle created by `gptimer_new_timer()`
|
||||
* @param[out] ret_pm_lock Timer's internal power management lock
|
||||
* @return
|
||||
* - ESP_OK: Get GPTimer power management lock successfully
|
||||
* - ESP_ERR_INVALID_ARG: Get GPTimer power management lock failed because of invalid argument
|
||||
* - ESP_FAIL: Get GPTimer power management lock failed because of other error
|
||||
*/
|
||||
esp_err_t gptimer_get_pm_lock(gptimer_handle_t timer, esp_pm_lock_handle_t *ret_pm_lock);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
11
components/driver/legacy_new_driver_coexist.c
Normal file
11
components/driver/legacy_new_driver_coexist.c
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief This count is used to prevent the coexistence of
|
||||
* the legacy timer group driver (deprecated/driver/timer.h) and the new gptimer driver (driver/gptimer.h).
|
||||
*/
|
||||
int timer_group_driver_init_count = 0;
|
File diff suppressed because it is too large
Load Diff
18
components/driver/test_apps/gptimer/CMakeLists.txt
Normal file
18
components/driver/test_apps/gptimer/CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(gptimer_test)
|
||||
|
||||
if(CONFIG_GPTIMER_ISR_IRAM_SAFE)
|
||||
add_custom_target(check_test_app_sections ALL
|
||||
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
|
||||
--rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/driver/
|
||||
--elf-file ${CMAKE_BINARY_DIR}/gptimer_test.elf
|
||||
find-refs
|
||||
--from-sections=.iram0.text
|
||||
--to-sections=.flash.text,.flash.rodata
|
||||
--exit-code
|
||||
DEPENDS ${elf}
|
||||
)
|
||||
endif()
|
2
components/driver/test_apps/gptimer/README.md
Normal file
2
components/driver/test_apps/gptimer/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- |
|
30
components/driver/test_apps/gptimer/app_test.py
Normal file
30
components/driver/test_apps/gptimer/app_test.py
Normal file
@ -0,0 +1,30 @@
|
||||
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import glob
|
||||
import os
|
||||
|
||||
import ttfw_idf
|
||||
from tiny_test_fw import Utility
|
||||
|
||||
|
||||
@ttfw_idf.idf_component_unit_test(env_tag='COMPONENT_UT_GENERIC', target=['esp32', 'esp32s2', 'esp32s3', 'esp32c3'])
|
||||
def test_component_ut_gptimer(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None
|
||||
# Get the names of all configs (sdkconfig.ci.* files)
|
||||
config_files = glob.glob(os.path.join(os.path.dirname(__file__), 'sdkconfig.ci.*'))
|
||||
config_names = [os.path.basename(s).replace('sdkconfig.ci.', '') for s in config_files]
|
||||
|
||||
# Run test once with binaries built for each config
|
||||
for name in config_names:
|
||||
Utility.console_log(f'Checking config "{name}"... ', end='')
|
||||
dut = env.get_dut('gptimer', 'components/driver/test_apps/gptimer', app_config_name=name)
|
||||
dut.start_app()
|
||||
stdout = dut.expect('Press ENTER to see the list of tests', full_stdout=True)
|
||||
dut.write('*')
|
||||
stdout = dut.expect("Enter next test, or 'enter' to see menu", full_stdout=True, timeout=30)
|
||||
ttfw_idf.ComponentUTResult.parse_result(stdout,ttfw_idf.TestFormat.UNITY_BASIC)
|
||||
env.close_dut(dut.name)
|
||||
Utility.console_log(f'Test config "{name}" done')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_component_ut_gptimer()
|
8
components/driver/test_apps/gptimer/main/CMakeLists.txt
Normal file
8
components/driver/test_apps/gptimer/main/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_gptimer.c"
|
||||
"test_gptimer_iram.c")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES driver unity spi_flash)
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u test_app_include_gptimer" "-u test_app_include_gptimer_iram")
|
51
components/driver/test_apps/gptimer/main/test_app_main.c
Normal file
51
components/driver/test_apps/gptimer/main/test_app_main.c
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
// Some resources are lazy allocated in GPTimer driver, the threshold is left for that case
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-300)
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
ssize_t delta = after_free - before_free;
|
||||
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// ____ ____ _____ _ _____ _
|
||||
// / ___| _ \_ _(_)_ __ ___ ___ _ __ |_ _|__ ___| |_
|
||||
// | | _| |_) || | | | '_ ` _ \ / _ \ '__| | |/ _ \/ __| __|
|
||||
// | |_| | __/ | | | | | | | | | __/ | | | __/\__ \ |_
|
||||
// \____|_| |_| |_|_| |_| |_|\___|_| |_|\___||___/\__|
|
||||
printf(" ____ ____ _____ _ _____ _\r\n");
|
||||
printf(" / ___| _ \\_ _(_)_ __ ___ ___ _ __ |_ _|__ ___| |_\r\n");
|
||||
printf("| | _| |_) || | | | '_ ` _ \\ / _ \\ '__| | |/ _ \\/ __| __|\r\n");
|
||||
printf("| |_| | __/ | | | | | | | | | __/ | | | __/\\__ \\ |_\r\n");
|
||||
printf(" \\____|_| |_| |_|_| |_| |_|\\___|_| |_|\\___||___/\\__|\r\n");
|
||||
unity_run_menu();
|
||||
}
|
487
components/driver/test_apps/gptimer/main/test_gptimer.c
Normal file
487
components/driver/test_apps/gptimer/main/test_gptimer.c
Normal file
@ -0,0 +1,487 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "driver/gptimer.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
#if CONFIG_GPTIMER_ISR_IRAM_SAFE
|
||||
#define TEST_ALARM_CALLBACK_ATTR IRAM_ATTR
|
||||
#else
|
||||
#define TEST_ALARM_CALLBACK_ATTR
|
||||
#endif // CONFIG_GPTIMER_ISR_IRAM_SAFE
|
||||
|
||||
void test_app_include_gptimer(void)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_CASE("gptimer_set_get_raw_count", "[gptimer]")
|
||||
{
|
||||
gptimer_config_t config = {
|
||||
.clk_src = GPTIMER_CLK_SRC_APB,
|
||||
.direction = GPTIMER_COUNT_UP,
|
||||
.resolution_hz = 1 * 1000 * 1000,
|
||||
};
|
||||
gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS];
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_new_timer(&config, &timers[i]));
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, gptimer_new_timer(&config, &timers[0]));
|
||||
unsigned long long get_value = 0;
|
||||
printf("check gptimer initial count value\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_get_raw_count(timers[i], &get_value));
|
||||
TEST_ASSERT_EQUAL(0, get_value);
|
||||
}
|
||||
unsigned long long set_values[] = {100, 500, 666};
|
||||
for (size_t j = 0; j < sizeof(set_values) / sizeof(set_values[0]); j++) {
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
printf("set raw count to %llu for gptimer %d\r\n", set_values[j], i);
|
||||
TEST_ESP_OK(gptimer_set_raw_count(timers[i], set_values[j]));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_get_raw_count(timers[i], &get_value));
|
||||
printf("get raw count of gptimer %d: %llu\r\n", i, get_value);
|
||||
TEST_ASSERT_EQUAL(set_values[j], get_value);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_del_timer(timers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("gptimer_wallclock_with_various_clock_sources", "[gptimer]")
|
||||
{
|
||||
gptimer_clock_source_t test_clk_srcs[] = {
|
||||
GPTIMER_CLK_SRC_APB,
|
||||
#if SOC_TIMER_GROUP_SUPPORT_XTAL
|
||||
GPTIMER_CLK_SRC_XTAL,
|
||||
#endif // SOC_TIMER_GROUP_SUPPORT_XTAL
|
||||
};
|
||||
|
||||
// test with various clock sources
|
||||
for (size_t i = 0; i < sizeof(test_clk_srcs) / sizeof(test_clk_srcs[0]); i++) {
|
||||
gptimer_config_t timer_config = {
|
||||
.clk_src = test_clk_srcs[i],
|
||||
.direction = GPTIMER_COUNT_UP,
|
||||
.resolution_hz = 1 * 1000 * 1000,
|
||||
};
|
||||
gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS];
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i]));
|
||||
}
|
||||
printf("start timers\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_start(timers[i]));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(20)); // 20ms = 20_000 ticks
|
||||
unsigned long long value = 0;
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value));
|
||||
TEST_ASSERT_UINT_WITHIN(1000, 20000, value);
|
||||
}
|
||||
printf("stop timers\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_stop(timers[i]));
|
||||
}
|
||||
printf("check whether timers have stopped\r\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value));
|
||||
printf("get raw count of gptimer %d: %llu\r\n", i, value);
|
||||
TEST_ASSERT_UINT_WITHIN(1000, 20000, value);
|
||||
}
|
||||
printf("restart timers\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_start(timers[i]));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
printf("stop timers again\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_stop(timers[i]));
|
||||
}
|
||||
printf("check whether timers have stopped\r\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value));
|
||||
printf("get raw count of gptimer %d: %llu\r\n", i, value);
|
||||
TEST_ASSERT_UINT_WITHIN(2000, 40000, value);
|
||||
}
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_del_timer(timers[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_stop_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
|
||||
{
|
||||
xTaskHandle task_handle = (xTaskHandle)user_data;
|
||||
BaseType_t high_task_wakeup;
|
||||
gptimer_stop(timer);
|
||||
esp_rom_printf("count=%lld @alarm\n", edata->count_value);
|
||||
vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
TEST_CASE("gptimer_stop_on_alarm", "[gptimer]")
|
||||
{
|
||||
xTaskHandle task_handle = xTaskGetCurrentTaskHandle();
|
||||
|
||||
gptimer_config_t timer_config = {
|
||||
.resolution_hz = 1 * 1000 * 1000,
|
||||
.clk_src = GPTIMER_CLK_SRC_APB,
|
||||
.direction = GPTIMER_COUNT_UP,
|
||||
};
|
||||
gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS];
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i]));
|
||||
}
|
||||
|
||||
gptimer_event_callbacks_t cbs = {
|
||||
.on_alarm = test_gptimer_alarm_stop_callback,
|
||||
};
|
||||
gptimer_alarm_config_t alarm_config = {};
|
||||
|
||||
printf("start timers\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
alarm_config.alarm_count = 100000 * (i + 1);
|
||||
TEST_ESP_OK(gptimer_set_alarm_action(timers[i], &alarm_config));
|
||||
TEST_ESP_OK(gptimer_register_event_callbacks(timers[i], &cbs, task_handle));
|
||||
TEST_ESP_OK(gptimer_start(timers[i]));
|
||||
printf("alarm value for gptimer %d: %llu\r\n", i, alarm_config.alarm_count);
|
||||
}
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
||||
}
|
||||
|
||||
printf("check whether the timers have stopped in the ISR\r\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
unsigned long long value = 0;
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value));
|
||||
printf("get raw count of gptimer %d: %llu\r\n", i, value);
|
||||
TEST_ASSERT_UINT_WITHIN(40, 100000 * (i + 1), value);
|
||||
}
|
||||
|
||||
printf("restart timers\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
alarm_config.alarm_count = 100000 * (i + 1);
|
||||
// reset counter value to zero
|
||||
TEST_ESP_OK(gptimer_set_raw_count(timers[i], 0));
|
||||
TEST_ESP_OK(gptimer_start(timers[i]));
|
||||
}
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
||||
}
|
||||
printf("check whether the timers have stopped in the ISR\r\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value));
|
||||
printf("get raw count of gptimer %d: %llu\r\n", i, value);
|
||||
TEST_ASSERT_UINT_WITHIN(40, 100000 * (i + 1), value);
|
||||
}
|
||||
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_del_timer(timers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_reload_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
|
||||
{
|
||||
xTaskHandle task_handle = (xTaskHandle)user_data;
|
||||
BaseType_t high_task_wakeup;
|
||||
esp_rom_printf("alarm isr count=%llu\r\n", edata->count_value);
|
||||
// check if the count value has been reloaded
|
||||
TEST_ASSERT_UINT_WITHIN(20, 100, edata->count_value);
|
||||
vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
TEST_CASE("gptimer_auto_reload_on_alarm", "[gptimer]")
|
||||
{
|
||||
xTaskHandle task_handle = xTaskGetCurrentTaskHandle();
|
||||
|
||||
gptimer_config_t timer_config = {
|
||||
.resolution_hz = 1 * 1000 * 1000,
|
||||
.clk_src = GPTIMER_CLK_SRC_APB,
|
||||
.direction = GPTIMER_COUNT_UP,
|
||||
};
|
||||
gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS];
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i]));
|
||||
}
|
||||
|
||||
gptimer_event_callbacks_t cbs = {
|
||||
.on_alarm = test_gptimer_alarm_reload_callback,
|
||||
};
|
||||
gptimer_alarm_config_t alarm_config = {
|
||||
.reload_count = 100,
|
||||
.alarm_count = 100000,
|
||||
.flags.auto_reload_on_alarm = true,
|
||||
};
|
||||
|
||||
printf("start timers\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_set_alarm_action(timers[i], &alarm_config));
|
||||
TEST_ESP_OK(gptimer_register_event_callbacks(timers[i], &cbs, task_handle));
|
||||
TEST_ESP_OK(gptimer_start(timers[i]));
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
||||
// delete should fail if timer is not stopped
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, gptimer_del_timer(timers[i]));
|
||||
TEST_ESP_OK(gptimer_stop(timers[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_del_timer(timers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_normal_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
|
||||
{
|
||||
xTaskHandle task_handle = (xTaskHandle)user_data;
|
||||
BaseType_t high_task_wakeup;
|
||||
esp_rom_printf("alarm isr count=%llu\r\n", edata->count_value);
|
||||
// check the count value at alarm event
|
||||
vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
TEST_CASE("gptimer_one_shot_alarm", "[gptimer]")
|
||||
{
|
||||
xTaskHandle task_handle = xTaskGetCurrentTaskHandle();
|
||||
|
||||
gptimer_config_t timer_config = {
|
||||
.resolution_hz = 1 * 1000 * 1000,
|
||||
.clk_src = GPTIMER_CLK_SRC_APB,
|
||||
.direction = GPTIMER_COUNT_UP,
|
||||
};
|
||||
gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS];
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i]));
|
||||
}
|
||||
|
||||
gptimer_event_callbacks_t cbs = {
|
||||
.on_alarm = test_gptimer_alarm_normal_callback,
|
||||
};
|
||||
gptimer_alarm_config_t alarm_config = {
|
||||
.reload_count = 0,
|
||||
.alarm_count = 100000, // 100ms
|
||||
};
|
||||
|
||||
printf("start timers\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_set_alarm_action(timers[i], &alarm_config));
|
||||
TEST_ESP_OK(gptimer_register_event_callbacks(timers[i], &cbs, task_handle));
|
||||
TEST_ESP_OK(gptimer_start(timers[i]));
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
||||
// no alarm event should trigger again, as auto-reload is not enabled and alarm value hasn't changed in the isr
|
||||
TEST_ASSERT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
||||
// the alarm is stopped, but the counter should still work
|
||||
uint64_t value = 0;
|
||||
TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value));
|
||||
TEST_ASSERT_UINT_WITHIN(1000, 1100000, value); // 1100000 = 100ms alarm + 1s delay
|
||||
TEST_ESP_OK(gptimer_stop(timers[i]));
|
||||
}
|
||||
|
||||
printf("restart timers\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_start(timers[i]));
|
||||
// alarm should be triggered immediately as the counter value has across the target alarm value already
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, 0));
|
||||
TEST_ESP_OK(gptimer_stop(timers[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_del_timer(timers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_update_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
|
||||
{
|
||||
xTaskHandle task_handle = (xTaskHandle)user_data;
|
||||
BaseType_t high_task_wakeup;
|
||||
esp_rom_printf("alarm isr count=%llu\r\n", edata->count_value);
|
||||
gptimer_alarm_config_t alarm_config = {
|
||||
.alarm_count = edata->count_value + 100000, // alarm in next 100ms again
|
||||
};
|
||||
gptimer_set_alarm_action(timer, &alarm_config);
|
||||
vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
TEST_CASE("gptimer_update_alarm_dynamically", "[gptimer]")
|
||||
{
|
||||
xTaskHandle task_handle = xTaskGetCurrentTaskHandle();
|
||||
|
||||
gptimer_config_t timer_config = {
|
||||
.resolution_hz = 1 * 1000 * 1000,
|
||||
.clk_src = GPTIMER_CLK_SRC_APB,
|
||||
.direction = GPTIMER_COUNT_UP,
|
||||
};
|
||||
gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS];
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i]));
|
||||
}
|
||||
|
||||
gptimer_event_callbacks_t cbs = {
|
||||
.on_alarm = test_gptimer_alarm_update_callback,
|
||||
};
|
||||
gptimer_alarm_config_t alarm_config = {
|
||||
.alarm_count = 100000, // initial alarm count, 100ms
|
||||
};
|
||||
printf("start timers\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_set_alarm_action(timers[i], &alarm_config));
|
||||
TEST_ESP_OK(gptimer_register_event_callbacks(timers[i], &cbs, task_handle));
|
||||
TEST_ESP_OK(gptimer_start(timers[i]));
|
||||
// check the alarm event for multiple times
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500)));
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500)));
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500)));
|
||||
TEST_ESP_OK(gptimer_stop(timers[i]));
|
||||
// check there won't be more interrupts triggered than expected
|
||||
TEST_ASSERT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500)));
|
||||
}
|
||||
|
||||
printf("restart timers\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_start(timers[i]));
|
||||
// check the alarm event for multiple times
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500)));
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500)));
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500)));
|
||||
TEST_ESP_OK(gptimer_stop(timers[i]));
|
||||
// check there won't be more interrupts triggered than expected
|
||||
TEST_ASSERT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500)));
|
||||
}
|
||||
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_del_timer(timers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_count_down_reload_alarm_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
|
||||
{
|
||||
xTaskHandle task_handle = (xTaskHandle)user_data;
|
||||
BaseType_t high_task_wakeup;
|
||||
esp_rom_printf("alarm isr count=%llu\r\n", edata->count_value);
|
||||
// check if the count value has been reloaded
|
||||
TEST_ASSERT_UINT_WITHIN(20, 200000, edata->count_value);
|
||||
vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
TEST_CASE("gptimer_count_down_reload", "[gptimer]")
|
||||
{
|
||||
xTaskHandle task_handle = xTaskGetCurrentTaskHandle();
|
||||
|
||||
gptimer_config_t timer_config = {
|
||||
.resolution_hz = 1 * 1000 * 1000,
|
||||
.clk_src = GPTIMER_CLK_SRC_APB,
|
||||
.direction = GPTIMER_COUNT_DOWN,
|
||||
};
|
||||
gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS];
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i]));
|
||||
TEST_ESP_OK(gptimer_set_raw_count(timers[i], 200000));
|
||||
}
|
||||
|
||||
gptimer_event_callbacks_t cbs = {
|
||||
.on_alarm = test_gptimer_count_down_reload_alarm_callback,
|
||||
};
|
||||
gptimer_alarm_config_t alarm_config = {
|
||||
.reload_count = 200000, // 200ms
|
||||
.alarm_count = 0,
|
||||
.flags.auto_reload_on_alarm = true,
|
||||
};
|
||||
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_set_alarm_action(timers[i], &alarm_config));
|
||||
TEST_ESP_OK(gptimer_register_event_callbacks(timers[i], &cbs, task_handle));
|
||||
TEST_ESP_OK(gptimer_start(timers[i]));
|
||||
// check twice, as it's a period event
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
||||
TEST_ESP_OK(gptimer_stop(timers[i]));
|
||||
}
|
||||
|
||||
printf("restart gptimer with previous configuration\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_start(timers[i]));
|
||||
// check twice, as it's a period event
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
||||
TEST_ESP_OK(gptimer_stop(timers[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_del_timer(timers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_overflow_reload_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
|
||||
{
|
||||
xTaskHandle task_handle = (xTaskHandle)user_data;
|
||||
BaseType_t high_task_wakeup;
|
||||
// Note: esp_rom_printf can't print value with 64 bit length, so the following print result is meaningless, but as an incidator for test that the alarm has fired
|
||||
esp_rom_printf("alarm isr count=%llu\r\n", edata->count_value);
|
||||
vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
TEST_CASE("gptimer_overflow", "[gptimer]")
|
||||
{
|
||||
xTaskHandle task_handle = xTaskGetCurrentTaskHandle();
|
||||
|
||||
gptimer_config_t timer_config = {
|
||||
.resolution_hz = 1 * 1000 * 1000,
|
||||
.clk_src = GPTIMER_CLK_SRC_APB,
|
||||
.direction = GPTIMER_COUNT_UP,
|
||||
};
|
||||
gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS];
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i]));
|
||||
}
|
||||
#if SOC_TIMER_GROUP_COUNTER_BIT_WIDTH == 64
|
||||
uint64_t reload_at = UINT64_MAX - 100000;
|
||||
#else
|
||||
uint64_t reload_at = (1ULL << SOC_TIMER_GROUP_COUNTER_BIT_WIDTH) - 100000;
|
||||
#endif
|
||||
gptimer_event_callbacks_t cbs = {
|
||||
.on_alarm = test_gptimer_overflow_reload_callback,
|
||||
};
|
||||
gptimer_alarm_config_t alarm_config = {
|
||||
.reload_count = reload_at,
|
||||
.alarm_count = 100000, // 100ms
|
||||
.flags.auto_reload_on_alarm = true,
|
||||
};
|
||||
// The counter should start from [COUNTER_MAX-100000] and overflows to [0] and continue, then reached to alarm value [100000], reloaded to [COUNTER_MAX-100000] automatically
|
||||
// thus the period should be 200ms
|
||||
printf("start timers\r\n");
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_set_alarm_action(timers[i], &alarm_config));
|
||||
TEST_ESP_OK(gptimer_register_event_callbacks(timers[i], &cbs, task_handle));
|
||||
// we start from the reload value
|
||||
TEST_ESP_OK(gptimer_set_raw_count(timers[i], reload_at));
|
||||
TEST_ESP_OK(gptimer_start(timers[i]));
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(400)));
|
||||
TEST_ESP_OK(gptimer_stop(timers[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ESP_OK(gptimer_del_timer(timers[i]));
|
||||
}
|
||||
}
|
107
components/driver/test_apps/gptimer/main/test_gptimer_iram.c
Normal file
107
components/driver/test_apps/gptimer/main/test_gptimer_iram.c
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "unity.h"
|
||||
#include "driver/gptimer.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
void test_app_include_gptimer_iram(void)
|
||||
{
|
||||
}
|
||||
|
||||
#if CONFIG_GPTIMER_ISR_IRAM_SAFE
|
||||
|
||||
typedef struct {
|
||||
size_t buf_size;
|
||||
uint8_t *buf;
|
||||
size_t flash_addr;
|
||||
size_t repeat_count;
|
||||
SemaphoreHandle_t done_sem;
|
||||
} read_task_arg_t;
|
||||
|
||||
typedef struct {
|
||||
size_t delay_time_us;
|
||||
size_t repeat_count;
|
||||
} block_task_arg_t;
|
||||
|
||||
static bool IRAM_ATTR on_gptimer_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
block_task_arg_t *arg = (block_task_arg_t *)user_ctx;
|
||||
esp_rom_delay_us(arg->delay_time_us);
|
||||
arg->repeat_count++;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void flash_read_task(void *varg)
|
||||
{
|
||||
read_task_arg_t *arg = (read_task_arg_t *)varg;
|
||||
for (size_t i = 0; i < arg->repeat_count; i++) {
|
||||
TEST_ESP_OK(spi_flash_read(arg->flash_addr, arg->buf, arg->buf_size));
|
||||
}
|
||||
xSemaphoreGive(arg->done_sem);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("gptimer_iram_interrupt_safe", "[gptimer]")
|
||||
{
|
||||
gptimer_handle_t gptimer = NULL;
|
||||
const size_t size = 128;
|
||||
uint8_t *buf = malloc(size);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
SemaphoreHandle_t done_sem = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_NULL(done_sem);
|
||||
read_task_arg_t read_arg = {
|
||||
.buf_size = size,
|
||||
.buf = buf,
|
||||
.flash_addr = 0,
|
||||
.repeat_count = 1000,
|
||||
.done_sem = done_sem,
|
||||
};
|
||||
|
||||
block_task_arg_t block_arg = {
|
||||
.repeat_count = 0,
|
||||
.delay_time_us = 100,
|
||||
};
|
||||
|
||||
gptimer_config_t timer_config = {
|
||||
.clk_src = GPTIMER_CLK_SRC_APB,
|
||||
.direction = GPTIMER_COUNT_UP,
|
||||
.resolution_hz = 1 * 1000 * 1000,
|
||||
};
|
||||
TEST_ESP_OK(gptimer_new_timer(&timer_config, &gptimer));
|
||||
gptimer_event_callbacks_t cbs = {
|
||||
.on_alarm = on_gptimer_alarm_cb,
|
||||
};
|
||||
gptimer_alarm_config_t alarm_config = {
|
||||
.reload_count = 0,
|
||||
.alarm_count = 120,
|
||||
.flags.auto_reload_on_alarm = true,
|
||||
};
|
||||
TEST_ESP_OK(gptimer_set_alarm_action(gptimer, &alarm_config));
|
||||
TEST_ESP_OK(gptimer_register_event_callbacks(gptimer, &cbs, &block_arg));
|
||||
TEST_ESP_OK(gptimer_start(gptimer));
|
||||
|
||||
xTaskCreatePinnedToCore(flash_read_task, "read_flash", 2048, &read_arg, 3, NULL, portNUM_PROCESSORS - 1);
|
||||
// wait for task done
|
||||
xSemaphoreTake(done_sem, portMAX_DELAY);
|
||||
printf("alarm callback runs %d times\r\n", block_arg.repeat_count);
|
||||
TEST_ASSERT_GREATER_THAN(1000, block_arg.repeat_count);
|
||||
// delete gptimer
|
||||
TEST_ESP_OK(gptimer_stop(gptimer));
|
||||
TEST_ESP_OK(gptimer_del_timer(gptimer));
|
||||
vSemaphoreDelete(done_sem);
|
||||
free(buf);
|
||||
// leave time for IDLE task to recycle deleted task
|
||||
vTaskDelay(2);
|
||||
}
|
||||
|
||||
#endif // CONFIG_GPTIMER_ISR_IRAM_SAFE
|
@ -0,0 +1,5 @@
|
||||
CONFIG_COMPILER_DUMP_RTL_FILES=y
|
||||
CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM=y
|
||||
CONFIG_GPTIMER_ISR_IRAM_SAFE=y
|
||||
# disable log as most of log access rodata string on error, causing RTL check failure
|
||||
CONFIG_LOG_DEFAULT_LEVEL_NONE=y
|
5
components/driver/test_apps/gptimer/sdkconfig.ci.release
Normal file
5
components/driver/test_apps/gptimer/sdkconfig.ci.release
Normal file
@ -0,0 +1,5 @@
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
2
components/driver/test_apps/gptimer/sdkconfig.defaults
Normal file
2
components/driver/test_apps/gptimer/sdkconfig.defaults
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT=n
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -10,14 +10,14 @@
|
||||
#include "esp_check.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "driver/timer.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "driver/timer_types_legacy.h"
|
||||
#include "hal/timer_hal.h"
|
||||
#include "hal/timer_ll.h"
|
||||
#include "hal/check.h"
|
||||
#include "soc/timer_periph.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/timer_group_reg.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
|
||||
static const char *TIMER_TAG = "timer_group";
|
||||
|
||||
@ -230,6 +230,42 @@ static void IRAM_ATTR timer_isr_default(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t timer_enable_intr(timer_group_t group_num, timer_idx_t timer_num)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(group_num < TIMER_GROUP_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_GROUP_NUM_ERROR);
|
||||
ESP_RETURN_ON_FALSE(timer_num < TIMER_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NUM_ERROR);
|
||||
ESP_RETURN_ON_FALSE(p_timer_obj[group_num][timer_num] != NULL, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NEVER_INIT_ERROR);
|
||||
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
|
||||
timer_ll_enable_intr(p_timer_obj[group_num][timer_num]->hal.dev, TIMER_LL_EVENT_ALARM(timer_num), true);
|
||||
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t timer_disable_intr(timer_group_t group_num, timer_idx_t timer_num)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(group_num < TIMER_GROUP_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_GROUP_NUM_ERROR);
|
||||
ESP_RETURN_ON_FALSE(timer_num < TIMER_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NUM_ERROR);
|
||||
ESP_RETURN_ON_FALSE(p_timer_obj[group_num][timer_num] != NULL, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NEVER_INIT_ERROR);
|
||||
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
|
||||
timer_ll_enable_intr(p_timer_obj[group_num][timer_num]->hal.dev, TIMER_LL_EVENT_ALARM(timer_num), false);
|
||||
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num,
|
||||
void (*fn)(void *), void *arg, int intr_alloc_flags, timer_isr_handle_t *handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(group_num < TIMER_GROUP_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_GROUP_NUM_ERROR);
|
||||
ESP_RETURN_ON_FALSE(timer_num < TIMER_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NUM_ERROR);
|
||||
ESP_RETURN_ON_FALSE(fn != NULL, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_PARAM_ADDR_ERROR);
|
||||
ESP_RETURN_ON_FALSE(p_timer_obj[group_num][timer_num] != NULL, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NEVER_INIT_ERROR);
|
||||
timer_hal_context_t *hal = &p_timer_obj[group_num][timer_num]->hal;
|
||||
return esp_intr_alloc_intrstatus(timer_group_periph_signals.groups[group_num].timer_irq_id[timer_num],
|
||||
intr_alloc_flags,
|
||||
(uint32_t)timer_ll_get_intr_status_reg(hal->dev),
|
||||
TIMER_LL_EVENT_ALARM(timer_num), fn, arg, handle);
|
||||
}
|
||||
|
||||
esp_err_t timer_isr_callback_add(timer_group_t group_num, timer_idx_t timer_num, timer_isr_t isr_handler, void *args, int intr_alloc_flags)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(group_num < TIMER_GROUP_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_GROUP_NUM_ERROR);
|
||||
@ -261,20 +297,6 @@ esp_err_t timer_isr_callback_remove(timer_group_t group_num, timer_idx_t timer_n
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num,
|
||||
void (*fn)(void *), void *arg, int intr_alloc_flags, timer_isr_handle_t *handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(group_num < TIMER_GROUP_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_GROUP_NUM_ERROR);
|
||||
ESP_RETURN_ON_FALSE(timer_num < TIMER_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NUM_ERROR);
|
||||
ESP_RETURN_ON_FALSE(fn != NULL, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_PARAM_ADDR_ERROR);
|
||||
ESP_RETURN_ON_FALSE(p_timer_obj[group_num][timer_num] != NULL, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NEVER_INIT_ERROR);
|
||||
timer_hal_context_t *hal = &p_timer_obj[group_num][timer_num]->hal;
|
||||
return esp_intr_alloc_intrstatus(timer_group_periph_signals.groups[group_num].timer_irq_id[timer_num],
|
||||
intr_alloc_flags,
|
||||
(uint32_t)timer_ll_get_intr_status_reg(hal->dev),
|
||||
TIMER_LL_EVENT_ALARM(timer_num), fn, arg, handle);
|
||||
}
|
||||
|
||||
esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, const timer_config_t *config)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(group_num < TIMER_GROUP_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_GROUP_NUM_ERROR);
|
||||
@ -369,28 +391,6 @@ esp_err_t timer_group_intr_disable(timer_group_t group_num, timer_intr_t disable
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t timer_enable_intr(timer_group_t group_num, timer_idx_t timer_num)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(group_num < TIMER_GROUP_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_GROUP_NUM_ERROR);
|
||||
ESP_RETURN_ON_FALSE(timer_num < TIMER_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NUM_ERROR);
|
||||
ESP_RETURN_ON_FALSE(p_timer_obj[group_num][timer_num] != NULL, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NEVER_INIT_ERROR);
|
||||
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
|
||||
timer_ll_enable_intr(p_timer_obj[group_num][timer_num]->hal.dev, TIMER_LL_EVENT_ALARM(timer_num), true);
|
||||
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t timer_disable_intr(timer_group_t group_num, timer_idx_t timer_num)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(group_num < TIMER_GROUP_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_GROUP_NUM_ERROR);
|
||||
ESP_RETURN_ON_FALSE(timer_num < TIMER_MAX, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NUM_ERROR);
|
||||
ESP_RETURN_ON_FALSE(p_timer_obj[group_num][timer_num] != NULL, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NEVER_INIT_ERROR);
|
||||
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
|
||||
timer_ll_enable_intr(p_timer_obj[group_num][timer_num]->hal.dev, TIMER_LL_EVENT_ALARM(timer_num), false);
|
||||
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* This function is deprecated */
|
||||
timer_intr_t IRAM_ATTR timer_group_intr_get_in_isr(timer_group_t group_num)
|
||||
{
|
||||
@ -411,17 +411,17 @@ uint32_t IRAM_ATTR timer_group_get_intr_status_in_isr(timer_group_t group_num)
|
||||
return intr_status;
|
||||
}
|
||||
|
||||
void IRAM_ATTR timer_group_clr_intr_status_in_isr(timer_group_t group_num, timer_idx_t timer_num)
|
||||
{
|
||||
timer_ll_clear_intr_status(p_timer_obj[group_num][timer_num]->hal.dev, TIMER_LL_EVENT_ALARM(timer_num));
|
||||
}
|
||||
|
||||
/* This function is deprecated */
|
||||
void IRAM_ATTR timer_group_intr_clr_in_isr(timer_group_t group_num, timer_idx_t timer_num)
|
||||
{
|
||||
timer_group_clr_intr_status_in_isr(group_num, timer_num);
|
||||
}
|
||||
|
||||
void IRAM_ATTR timer_group_clr_intr_status_in_isr(timer_group_t group_num, timer_idx_t timer_num)
|
||||
{
|
||||
timer_ll_clear_intr_status(p_timer_obj[group_num][timer_num]->hal.dev, TIMER_LL_EVENT_ALARM(timer_num));
|
||||
}
|
||||
|
||||
void IRAM_ATTR timer_group_enable_alarm_in_isr(timer_group_t group_num, timer_idx_t timer_num)
|
||||
{
|
||||
timer_ll_enable_alarm(p_timer_obj[group_num][timer_num]->hal.dev, timer_num, true);
|
||||
@ -474,9 +474,17 @@ esp_err_t IRAM_ATTR timer_spinlock_give(timer_group_t group_num)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
STATIC_HAL_REG_CHECK(TIMER_TAG, TIMER_INTR_T0, TIMG_T0_INT_CLR);
|
||||
#if SOC_TIMER_GROUP_TIMERS_PER_GROUP > 1
|
||||
STATIC_HAL_REG_CHECK(TIMER_TAG, TIMER_INTR_T1, TIMG_T1_INT_CLR);
|
||||
#endif
|
||||
STATIC_HAL_REG_CHECK(TIMER_TAG, TIMER_INTR_WDT, TIMG_WDT_INT_CLR);
|
||||
/**
|
||||
* @brief This function will be called during start up, to check that this legacy timer group driver is not running along with the gptimer driver
|
||||
*/
|
||||
__attribute__((constructor))
|
||||
static void check_legacy_timer_driver_conflict(void)
|
||||
{
|
||||
extern int timer_group_driver_init_count;
|
||||
timer_group_driver_init_count++;
|
||||
if (timer_group_driver_init_count > 1) {
|
||||
ESP_EARLY_LOGE(TIMER_TAG, "CONFLICT! The legacy timer group driver can't work along with the gptimer driver");
|
||||
abort();
|
||||
}
|
||||
ESP_EARLY_LOGW(TIMER_TAG, "legacy timer group driver is deprecated, please migrate to use driver/gptimer.h");
|
||||
}
|
@ -14,6 +14,7 @@ if(NOT BOOTLOADER_BUILD)
|
||||
"spi_slave_hal.c"
|
||||
"spi_slave_hal_iram.c"
|
||||
"timer_hal.c"
|
||||
"timer_hal_iram.c"
|
||||
"ledc_hal.c"
|
||||
"ledc_hal_iram.c"
|
||||
"i2c_hal.c"
|
||||
|
@ -22,3 +22,5 @@ entries:
|
||||
if IDF_TARGET_ESP32 = n:
|
||||
spi_flash_hal_gpspi (noflash)
|
||||
systimer_hal (noflash)
|
||||
if GPTIMER_CTRL_FUNC_IN_IRAM = y:
|
||||
timer_hal_iram (noflash)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -12,13 +12,3 @@ void timer_hal_init(timer_hal_context_t *hal, uint32_t group_num, uint32_t timer
|
||||
hal->dev = TIMER_LL_GET_HW(group_num);
|
||||
hal->timer_id = timer_num;
|
||||
}
|
||||
|
||||
void timer_hal_set_counter_value(timer_hal_context_t *hal, uint64_t load_val)
|
||||
{
|
||||
// save current reload value
|
||||
uint64_t old_reload = timer_ll_get_reload_value(hal->dev, hal->timer_id);
|
||||
timer_ll_set_reload_value(hal->dev, hal->timer_id, load_val);
|
||||
timer_ll_trigger_soft_reload(hal->dev, hal->timer_id);
|
||||
// restore the previous reload value
|
||||
timer_ll_set_reload_value(hal->dev, hal->timer_id, old_reload);
|
||||
}
|
||||
|
24
components/hal/timer_hal_iram.c
Normal file
24
components/hal/timer_hal_iram.c
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "hal/timer_hal.h"
|
||||
#include "hal/timer_ll.h"
|
||||
|
||||
void timer_hal_set_counter_value(timer_hal_context_t *hal, uint64_t load_val)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// - `timer_ll_set_reload_value()` will only indicate the `reload_value`
|
||||
// - `timer_ll_set_reload_value()` + ``timer_ll_trigger_soft_reload()` can update the HW counter value by software
|
||||
// Therefore, after updating the HW counter value, we need to restore the previous `reload_value`.
|
||||
// Attention: The following process should be protected by a lock in the driver layer.
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// save current reload value
|
||||
uint64_t old_reload = timer_ll_get_reload_value(hal->dev, hal->timer_id);
|
||||
timer_ll_set_reload_value(hal->dev, hal->timer_id, load_val);
|
||||
timer_ll_trigger_soft_reload(hal->dev, hal->timer_id);
|
||||
// restore the previous reload value
|
||||
timer_ll_set_reload_value(hal->dev, hal->timer_id, old_reload);
|
||||
}
|
Loading…
Reference in New Issue
Block a user