driver: add hardware timer code.

1. add timer driver code
2. add timer example code
3. replace api for enable interrupt in task_wdt.c
This commit is contained in:
Wangjialin 2016-11-23 02:15:27 +08:00
parent d0c9c1de57
commit a3c4a70ba3
6 changed files with 841 additions and 2 deletions

View file

@ -0,0 +1,343 @@
// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _DRIVER_TIMER_H_
#define _DRIVER_TIMER_H_
#include "esp_err.h"
#include "esp_attr.h"
#include "soc/soc.h"
#include "soc/timer_group_reg.h"
#include "soc/timer_group_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
#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*/
TIMER_GROUP_1 = 1, /*!<Hw timer group 1*/
TIMER_GROUP_MAX,
} timer_group_t;
/**
* @brief Select a hardware timer from timer groups
*/
typedef enum {
TIMER_0 = 0, /*!<Select timer0 of GROUPx*/
TIMER_1 = 1, /*!<Select timer1 of GROUPx*/
TIMER_MAX,
} timer_idx_t;
/**
* @brief Decides the direction of counter
*/
typedef enum {
TIMER_COUNT_DOWN = 0, /*!< Descending Count from cnt.high|cnt.low*/
TIMER_COUNT_UP = 1, /*!< Ascending Count from Zero*/
TIMER_COUNT_MAX
} timer_count_dir_t;
/**
* @brief Decides whether timer is on or paused
*/
typedef enum {
TIMER_PAUSE = 0, /*!<Pause timer counter*/
TIMER_START = 1, /*!<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_EDGE = 1, /*!< Interrupt mode: edge mode, Not supported Now*/
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 timer configure struct
*/
typedef struct {
bool alarm_en; /*!< Timer alarm enable */
bool counter_en; /*!< Counter enable */
timer_count_dir_t counter_dir; /*!< Counter direction */
timer_intr_mode_t intr_type; /*!< Interrupt mode */
bool auto_reload; /*!< Timer auto-reload */
uint16_t divider; /*!< Counter clock divider*/
} timer_config_t;
/**
* @brief Read the counter value of hardware timer.
*
* @param group_num Timer group, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index, 0 for hw_timer[0] & 1 for hw_timer[1]
* @param timer_val Pointer to accept timer counter value.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_get_counter_value(timer_group_t group_num, timer_idx_t timer_num, uint64_t* timer_val);
/**
* @brief Read the counter value of hardware timer, in unit of a given scale.
*
* @param group_num Timer group, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index, 0 for hw_timer[0] & 1 for hw_timer[1]
* @param time Pointer, type of double*, to accept timer counter value, in seconds.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_get_counter_time_sec(timer_group_t group_num, timer_idx_t timer_num, double* time);
/**
* @brief Set counter value to hardware timer.
*
* @param group_num Timer group, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index, 0 for hw_timer[0] & 1 for hw_timer[1]
* @param load_val Counter value to write to the hardware timer.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_set_counter_value(timer_group_t group_num, timer_idx_t timer_num, uint64_t load_val);
/**
* @brief Start the counter of hardware timer.
*
* @param group_num Timer group number, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index, 0 for hw_timer[0] & 1 for hw_timer[1]
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_start(timer_group_t group_num, timer_idx_t timer_num);
/**
* @brief Pause the counter of hardware timer.
*
* @param group_num Timer group number, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index, 0 for hw_timer[0] & 1 for hw_timer[1]
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_pause(timer_group_t group_num, timer_idx_t timer_num);
/**
* @brief Set counting mode for hardware timer.
*
* @param group_num Timer group number, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index, 0 for hw_timer[0] & 1 for hw_timer[1]
* @param counter_dir Counting direction of timer, count-up or count-down
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_set_counter_mode(timer_group_t group_num, timer_idx_t timer_num, timer_count_dir_t counter_dir);
/**
* @brief Enable or disable counter reload function when alarm event occurs.
*
* @param group_num Timer group number, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index, 0 for hw_timer[0] & 1 for hw_timer[1]
* @param reload Counter reload mode.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_set_auto_reload(timer_group_t group_num, timer_idx_t timer_num, timer_autoreload_t reload);
/**
* @brief Set hardware timer source clock divider. Timer groups clock are divider from APB clock.
*
* @param group_num Timer group number, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index, 0 for hw_timer[0] & 1 for hw_timer[1]
* @param divider Timer clock divider value.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_set_divider(timer_group_t group_num, timer_idx_t timer_num, uint16_t divider);
/**
* @brief Set timer alarm value.
*
* @param group_num Timer group, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index, 0 for hw_timer[0] & 1 for hw_timer[1]
* @param alarm_value A 64-bit value to set the alarm value.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_set_alarm_value(timer_group_t group_num, timer_idx_t timer_num, uint64_t alarm_value);
/**
* @brief Get timer alarm value.
*
* @param group_num Timer group, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index, 0 for hw_timer[0] & 1 for hw_timer[1]
* @param alarm_value Pointer of A 64-bit value to accept the alarm value.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_get_alarm_value(timer_group_t group_num, timer_idx_t timer_num, uint64_t* alarm_value);
/**
* @brief Get timer alarm value.
*
* @param group_num Timer group, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index, 0 for hw_timer[0] & 1 for hw_timer[1]
* @param alarm_en To enable or disable timer alarm function.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_set_alarm(timer_group_t group_num, timer_idx_t timer_num, timer_alarm_t alarm_en);
/**
* @brief register Timer interrupt handler, the handler is an ISR.
* The handler will be attached to the same CPU core that this function is running on.
* @note
* Users should know that which CPU is running and then pick a INUM that is not used by system.
* We can find the information of INUM and interrupt level in soc.h.
*
* @param group_num Timer group number
* @param timer_num Timer index of timer group
* @param timer_intr_num TIMER interrupt number, check the info in soc.h, and please see the core-isa.h for more details
* @param intr_type Timer interrupt type
* @param fn Interrupt handler function.
* @note
* Note that the handler function MUST be defined with attribution of "IRAM_ATTR".
* @param arg Parameter for handler function
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Function pointer error.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, int timer_intr_num, timer_intr_mode_t intr_type, void (*fn)(void*), void * arg);
/** @brief Initializes and configure the timer.
*
* @param group_num Timer group number, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index, 0 for hw_timer[0] & 1 for hw_timer[1]
* @param config Pointer to timer initialization parameters.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, timer_config_t* config);
/** @brief Get timer configure value.
*
* @param group_num Timer group number, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index, 0 for hw_timer[0] & 1 for hw_timer[1]
* @param config Pointer of struct to accept timer parameters.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_get_config(timer_group_t group_num, timer_idx_t timer_num, timer_config_t *config);
/** @brief Enable timer group interrupt, by enable mask
*
* @param group_num Timer group number, 0 for TIMERG0 or 1 for TIMERG1
* @param en_mask Timer interrupt enable mask.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_group_intr_enable(timer_group_t group_num, uint32_t en_mask);
/** @brief Disable timer group interrupt, by disable mask
*
* @param group_num Timer group number, 0 for TIMERG0 or 1 for TIMERG1
* @param disable_mask Timer interrupt disable mask.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_group_intr_disable(timer_group_t group_num, uint32_t disable_mask);
/** @brief Enable timer interrupt
*
* @param group_num Timer group number, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_enable_intr(timer_group_t group_num, timer_idx_t timer_num);
/** @brief Disable timer interrupt
*
* @param group_num Timer group number, 0 for TIMERG0 or 1 for TIMERG1
* @param timer_num Timer index.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_disable_intr(timer_group_t group_num, timer_idx_t timer_num);
#ifdef __cplusplus
}
#endif
#endif /* _TIMER_H_ */

276
components/driver/timer.c Normal file
View file

@ -0,0 +1,276 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include "esp_log.h"
#include "esp_err.h"
#include "esp_intr.h"
#include "freertos/FreeRTOS.h"
#include "freertos/xtensa_api.h"
#include "driver/timer.h"
#include "driver/periph_ctrl.h"
static const char* TIMER_TAG = "TIMER_GROUP";
#define TIMER_CHECK(a, str, ret_val) if (!(a)) { \
ESP_LOGE(TIMER_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
return (ret_val); \
}
#define TIMER_GROUP_NUM_ERROR "TIMER GROUP NUM ERROR"
#define TIMER_NUM_ERROR "HW TIMER NUM ERROR"
#define TIMER_PARAM_ADDR_ERROR "HW TIMER PARAM ADDR ERROR"
#define TIMER_COUNT_DIR_ERROR "HW TIMER COUNTER DIR ERROR"
#define TIMER_AUTORELOAD_ERROR "HW TIMER AUTORELOAD ERROR"
#define TIMER_SCALE_ERROR "HW TIMER SCALE ERROR"
#define TIMER_ALARM_ERROR "HW TIMER ALARM ERROR"
static timg_dev_t *TG[2] = {&TIMERG0, &TIMERG1};
static portMUX_TYPE timer_spinlock[TIMER_GROUP_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED};
#define TIMER_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux);
#define TIMER_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux);
esp_err_t timer_get_counter_value(timer_group_t group_num, timer_idx_t timer_num, uint64_t* timer_val)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_val != NULL, TIMER_PARAM_ADDR_ERROR, ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&timer_spinlock[group_num]);
TG[group_num]->hw_timer[timer_num].update = 1;
*timer_val = ((uint64_t) TG[group_num]->hw_timer[timer_num].cnt_high << 32)
| (TG[group_num]->hw_timer[timer_num].cnt_low);
portEXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_get_counter_time_sec(timer_group_t group_num, timer_idx_t timer_num, double* time)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(time != NULL, TIMER_PARAM_ADDR_ERROR, ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&timer_spinlock[group_num]);
TG[group_num]->hw_timer[timer_num].update = 1;
uint64_t timer_val = ((uint64_t) TG[group_num]->hw_timer[timer_num].cnt_high << 32)
| (TG[group_num]->hw_timer[timer_num].cnt_low);
uint16_t div = TG[group_num]->hw_timer[timer_num].config.divider;
*time = (double)timer_val * div / TIMER_BASE_CLK;
portEXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_set_counter_value(timer_group_t group_num, timer_idx_t timer_num, uint64_t load_val)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
TG[group_num]->hw_timer[timer_num].load_high = (uint32_t) (load_val >> 32);
TG[group_num]->hw_timer[timer_num].load_low = (uint32_t) load_val;
TG[group_num]->hw_timer[timer_num].reload = 1;
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_start(timer_group_t group_num, timer_idx_t timer_num)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
TG[group_num]->hw_timer[timer_num].config.enable = 1;
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_pause(timer_group_t group_num, timer_idx_t timer_num)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
TG[group_num]->hw_timer[timer_num].config.enable = 0;
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_set_counter_mode(timer_group_t group_num, timer_idx_t timer_num, timer_count_dir_t counter_dir)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(counter_dir < TIMER_COUNT_MAX, TIMER_COUNT_DIR_ERROR, ESP_ERR_INVALID_ARG);
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
TG[group_num]->hw_timer[timer_num].config.increase = counter_dir;
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_set_auto_reload(timer_group_t group_num, timer_idx_t timer_num, timer_autoreload_t reload)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(reload < TIMER_AUTORELOAD_MAX, TIMER_AUTORELOAD_ERROR, ESP_ERR_INVALID_ARG);
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
TG[group_num]->hw_timer[timer_num].config.autoreload = reload;
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_set_divider(timer_group_t group_num, timer_idx_t timer_num, uint16_t divider)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
int timer_en = TG[group_num]->hw_timer[timer_num].config.enable;
TG[group_num]->hw_timer[timer_num].config.enable = 0;
TG[group_num]->hw_timer[timer_num].config.divider = divider;
TG[group_num]->hw_timer[timer_num].config.enable = timer_en;
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_set_alarm_value(timer_group_t group_num, timer_idx_t timer_num, uint64_t alarm_value)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
TG[group_num]->hw_timer[timer_num].alarm_high = (uint32_t) (alarm_value >> 32);
TG[group_num]->hw_timer[timer_num].alarm_low = (uint32_t) alarm_value;
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_get_alarm_value(timer_group_t group_num, timer_idx_t timer_num, uint64_t* alarm_value)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(alarm_value != NULL, TIMER_PARAM_ADDR_ERROR, ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&timer_spinlock[group_num]);
*alarm_value = ((uint64_t) TG[group_num]->hw_timer[timer_num].alarm_high << 32)
| (TG[group_num]->hw_timer[timer_num].alarm_low);
portEXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_set_alarm(timer_group_t group_num, timer_idx_t timer_num, timer_alarm_t alarm_en)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(alarm_en < TIMER_ALARM_MAX, TIMER_ALARM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
TG[group_num]->hw_timer[timer_num].config.alarm_en = alarm_en;
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, int timer_intr_num,
timer_intr_mode_t intr_type, void (*fn)(void*), void * arg)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(fn != NULL, TIMER_PARAM_ADDR_ERROR, ESP_ERR_INVALID_ARG);
ESP_INTR_DISABLE(timer_intr_num);
int intr_source = 0;
switch(group_num) {
case TIMER_GROUP_0:
default:
if(intr_type == TIMER_INTR_LEVEL) {
intr_source = ETS_TG0_T0_LEVEL_INTR_SOURCE + timer_num;
} else {
intr_source = ETS_TG0_T0_EDGE_INTR_SOURCE + timer_num;
}
break;
case TIMER_GROUP_1:
if(intr_type == TIMER_INTR_LEVEL) {
intr_source = ETS_TG1_T0_LEVEL_INTR_SOURCE + timer_num;
} else {
intr_source = ETS_TG1_T0_EDGE_INTR_SOURCE + timer_num;
}
break;
}
intr_matrix_set(xPortGetCoreID(), intr_source, timer_intr_num);
xt_set_interrupt_handler(timer_intr_num, fn, arg);
ESP_INTR_ENABLE(timer_intr_num);
return ESP_OK;
}
esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, timer_config_t *config)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(config != NULL, TIMER_PARAM_ADDR_ERROR, ESP_ERR_INVALID_ARG);
if(group_num == 0) {
periph_module_enable(PERIPH_TIMG0_MODULE);
} else if(group_num == 1) {
periph_module_enable(PERIPH_TIMG1_MODULE);
}
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
TG[group_num]->hw_timer[timer_num].config.autoreload = config->auto_reload;
TG[group_num]->hw_timer[timer_num].config.divider = config->divider;
TG[group_num]->hw_timer[timer_num].config.enable = config->counter_en;
TG[group_num]->hw_timer[timer_num].config.increase = config->counter_dir;
TG[group_num]->hw_timer[timer_num].config.alarm_en = config->alarm_en;
TG[group_num]->hw_timer[timer_num].config.level_int_en = (config->intr_type == TIMER_INTR_LEVEL ? 1 : 0);
TG[group_num]->hw_timer[timer_num].config.edge_int_en = (config->intr_type == TIMER_INTR_LEVEL ? 0 : 1);
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_get_config(timer_group_t group_num, timer_idx_t timer_num, timer_config_t *config)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(config != NULL, TIMER_PARAM_ADDR_ERROR, ESP_ERR_INVALID_ARG);
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
config->alarm_en = TG[group_num]->hw_timer[timer_num].config.alarm_en;
config->auto_reload = TG[group_num]->hw_timer[timer_num].config.autoreload;
config->counter_dir = TG[group_num]->hw_timer[timer_num].config.increase;
config->counter_dir = TG[group_num]->hw_timer[timer_num].config.divider;
config->counter_en = TG[group_num]->hw_timer[timer_num].config.enable;
if(TG[group_num]->hw_timer[timer_num].config.level_int_en) {
config->intr_type =TIMER_INTR_LEVEL;
}
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_group_intr_enable(timer_group_t group_num, uint32_t en_mask)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&timer_spinlock[group_num]);
TG[group_num]->int_ena.val |= en_mask;
portEXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_group_intr_disable(timer_group_t group_num, uint32_t disable_mask)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&timer_spinlock[group_num]);
TG[group_num]->int_ena.val &= (~disable_mask);
portEXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK;
}
esp_err_t timer_enable_intr(timer_group_t group_num, timer_idx_t timer_num)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
return timer_group_intr_enable(group_num, BIT(timer_num));
}
esp_err_t timer_disable_intr(timer_group_t group_num, timer_idx_t timer_num)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
return timer_group_intr_disable(group_num, BIT(timer_num));
}

View file

@ -32,6 +32,7 @@
#include "soc/timer_group_struct.h" #include "soc/timer_group_struct.h"
#include "soc/timer_group_reg.h" #include "soc/timer_group_reg.h"
#include "esp_log.h" #include "esp_log.h"
#include "driver/timer.h"
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
@ -204,7 +205,7 @@ void esp_task_wdt_init() {
intr_matrix_set(xPortGetCoreID(), ETS_TG0_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM); intr_matrix_set(xPortGetCoreID(), ETS_TG0_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM);
xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL); xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL);
TIMERG0.int_clr_timers.wdt=1; TIMERG0.int_clr_timers.wdt=1;
TIMERG0.int_ena.wdt=1; timer_group_intr_enable(TIMER_GROUP_0, BIT(2));
ESP_INTR_ENABLE(ETS_T0_WDT_INUM); ESP_INTR_ENABLE(ETS_T0_WDT_INUM);
} }

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := timer_group
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,205 @@
/* Timer group-hardware timer example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "esp_types.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "soc/timer_group_struct.h"
#include "driver/periph_ctrl.h"
#include "driver/timer.h"
#define TIMER_INTR_NUM_0 17 /*!< Interrupt number for hardware timer 0 */
#define TIMER_INTR_NUM_1 18 /*!< Interrupt number for hardware timer 1*/
#define TIMER_INTR_SEL TIMER_INTR_LEVEL /*!< Timer level interrupt */
#define TIMER_GROUP TIMER_GROUP_0 /*!< Test on timer group 0 */
#define TIMER_DIVIDER 16 /*!< Hardware timer clock divider */
#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) /*!< used to calculate counter value */
#define TIMER_FINE_ADJ (1.4*(TIMER_BASE_CLK / TIMER_DIVIDER)/1000000) /*!< used to compensate alarm value */
#define TIMER_INTERVAL0_SEC (3.4179) /*!< test interval for timer 0 */
#define TIMER_INTERVAL1_SEC (5.78) /*!< test interval for timer 1 */
#define TEST_WITHOUT_RELOAD 0 /*!< example of auto-reload mode */
#define TEST_WITH_RELOAD 1 /*!< example without auto-reload mode */
typedef struct {
int type; /*!< event type */
int group; /*!< timer group */
int idx; /*!< timer number */
uint64_t counter_val; /*!< timer counter value */
double time_sec; /*!< calculated time from counter value */
} timer_event_t;
xQueueHandle timer_queue;
/*
* @brief Print a uint64_t value
*/
static void inline print_u64(uint64_t val)
{
printf("0x%08x%08x\n", (uint32_t) (val >> 32), (uint32_t) (val));
}
void timer_evt_task(void *arg)
{
while(1) {
timer_event_t evt;
xQueueReceive(timer_queue, &evt, portMAX_DELAY);
if(evt.type == TEST_WITHOUT_RELOAD) {
printf("\n\n example of count-up-timer \n");
} else if(evt.type == TEST_WITH_RELOAD) {
printf("\n\n example of reload-timer \n");
}
/*Show timer event from interrupt*/
printf("-------INTR TIME EVT--------\n");
printf("TG[%d] timer[%d] alarm evt\n", evt.group, evt.idx);
printf("reg: ");
print_u64(evt.counter_val);
printf("time: %.8f S\n", evt.time_sec);
/*Read timer value from task*/
printf("======TASK TIME======\n");
uint64_t timer_val;
timer_get_counter_value(evt.group, evt.idx, &timer_val);
double time;
timer_get_counter_time_sec(evt.group, evt.idx, &time);
printf("TG[%d] timer[%d] alarm evt\n", evt.group, evt.idx);
printf("reg: ");
print_u64(timer_val);
printf("time: %.8f S\n", time);
}
}
/*
* @brief timer group0 ISR handler
*/
void IRAM_ATTR timer_group0_isr(void *para)
{
int timer_idx = (int) para;
uint32_t intr_status = TIMERG0.int_st_timers.val;
timer_event_t evt;
if((intr_status & BIT(timer_idx)) && timer_idx == TIMER_0) {
/*Timer0 is an example that don't reload counter value*/
TIMERG0.hw_timer[timer_idx].update = 1;
/*We don't call a API here because they are not declared with IRAM_ATTR*/
TIMERG0.int_clr_timers.t0 = 1;
uint64_t timer_val = ((uint64_t) TIMERG0.hw_timer[timer_idx].cnt_high) << 32
| TIMERG0.hw_timer[timer_idx].cnt_low;
double time = (double) timer_val / (TIMER_BASE_CLK / TIMERG0.hw_timer[timer_idx].config.divider);
/*Post an event to out example task*/
evt.type = TEST_WITHOUT_RELOAD;
evt.group = 0;
evt.idx = timer_idx;
evt.counter_val = timer_val;
evt.time_sec = time;
xQueueSendFromISR(timer_queue, &evt, NULL);
/*For a timer that will not reload, we need to set the next alarm value each time. */
timer_val +=
(uint64_t) (TIMER_INTERVAL0_SEC * (TIMER_BASE_CLK / TIMERG0.hw_timer[timer_idx].config.divider));
/*Fine adjust*/
timer_val -= TIMER_FINE_ADJ;
TIMERG0.hw_timer[timer_idx].alarm_high = (uint32_t) (timer_val >> 32);
TIMERG0.hw_timer[timer_idx].alarm_low = (uint32_t) timer_val;
/*After set alarm, we set alarm_en bit if we want to enable alarm again.*/
TIMERG0.hw_timer[timer_idx].config.alarm_en = 1;
} else if((intr_status & BIT(timer_idx)) && timer_idx == TIMER_1) {
/*Timer1 is an example that will reload counter value*/
TIMERG0.hw_timer[timer_idx].update = 1;
/*We don't call a API here because they are not declared with IRAM_ATTR*/
TIMERG0.int_clr_timers.t1 = 1;
uint64_t timer_val = ((uint64_t) TIMERG0.hw_timer[timer_idx].cnt_high) << 32
| TIMERG0.hw_timer[timer_idx].cnt_low;
double time = (double) timer_val / (TIMER_BASE_CLK / TIMERG0.hw_timer[timer_idx].config.divider);
/*Post an event to out example task*/
evt.type = TEST_WITH_RELOAD;
evt.group = 0;
evt.idx = timer_idx;
evt.counter_val = timer_val;
evt.time_sec = time;
xQueueSendFromISR(timer_queue, &evt, NULL);
/*For a auto-reload timer, we still need to set alarm_en bit if we want to enable alarm again.*/
TIMERG0.hw_timer[timer_idx].config.alarm_en = 1;
}
}
/*
* @brief timer group0 hardware timer0 init
*/
void tg0_timer0_init()
{
int timer_group = TIMER_GROUP_0;
int timer_idx = TIMER_0;
timer_config_t config;
config.alarm_en = 1;
config.auto_reload = 0;
config.counter_dir = TIMER_COUNT_UP;
config.divider = TIMER_DIVIDER;
config.intr_type = TIMER_INTR_SEL;
config.counter_en = TIMER_PAUSE;
/*Configure timer*/
timer_init(timer_group, timer_idx, &config);
/*Stop timer counter*/
timer_pause(timer_group, timer_idx);
/*Load counter value */
timer_set_counter_value(timer_group, timer_idx, 0x00000000ULL);
/*Set alarm value*/
timer_set_alarm_value(timer_group, timer_idx, TIMER_INTERVAL0_SEC * TIMER_SCALE - TIMER_FINE_ADJ);
/*Enable timer interrupt*/
timer_enable_intr(timer_group, timer_idx);
/*Set ISR handler*/
timer_isr_register(timer_group, timer_idx, TIMER_INTR_NUM_0, TIMER_INTR_SEL, timer_group0_isr, (void*) timer_idx);
/*Start timer counter*/
timer_start(timer_group, timer_idx);
}
/*
* @brief timer group0 hardware timer1 init
*/
void tg0_timer1_init()
{
int timer_group = TIMER_GROUP_0;
int timer_idx = TIMER_1;
timer_config_t config;
config.alarm_en = 1;
config.auto_reload = 1;
config.counter_dir = TIMER_COUNT_UP;
config.divider = TIMER_DIVIDER;
config.intr_type = TIMER_INTR_SEL;
config.counter_en = TIMER_PAUSE;
/*Configure timer*/
timer_init(timer_group, timer_idx, &config);
/*Stop timer counter*/
timer_pause(timer_group, timer_idx);
/*Load counter value */
timer_set_counter_value(timer_group, timer_idx, 0x00000000ULL);
/*Set alarm value*/
timer_set_alarm_value(timer_group, timer_idx, TIMER_INTERVAL1_SEC * TIMER_SCALE);
/*Enable timer interrupt*/
timer_enable_intr(timer_group, timer_idx);
/*Set ISR handler*/
timer_isr_register(timer_group, timer_idx, TIMER_INTR_NUM_1, TIMER_INTR_SEL, timer_group0_isr, (void*) timer_idx);
/*Start timer counter*/
timer_start(timer_group, timer_idx);
}
/**
* @brief In this test, we will test hardware timer0 and timer1 of timer group0.
*/
void app_main()
{
tg0_timer0_init();
tg0_timer1_init();
timer_queue = xQueueCreate(10, sizeof(timer_event_t));
xTaskCreate(timer_evt_task, "timer_evt_task", 1024, NULL, 5, NULL);
}