]> granicus.if.org Git - esp-idf/commitdiff
driver: add hardware timer code.
authorWangjialin <wangjialin@espressif.com>
Tue, 22 Nov 2016 18:15:27 +0000 (02:15 +0800)
committerWangjialin <wangjialin@espressif.com>
Tue, 22 Nov 2016 18:15:27 +0000 (02:15 +0800)
1. add timer driver code
2. add timer example code
3. replace api for enable interrupt in task_wdt.c

components/driver/include/driver/timer.h [new file with mode: 0644]
components/driver/timer.c [new file with mode: 0644]
components/esp32/task_wdt.c
examples/13_timer_group/Makefile [new file with mode: 0644]
examples/13_timer_group/main/component.mk [new file with mode: 0644]
examples/13_timer_group/main/timer_group.c [new file with mode: 0644]

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