]> granicus.if.org Git - esp-idf/commitdiff
Timer API docs update, refactored example
authorkrzychb <krzychb@gazeta.pl>
Mon, 25 Sep 2017 04:20:12 +0000 (06:20 +0200)
committerkrzychb <krzychb@gazeta.pl>
Fri, 27 Oct 2017 05:13:59 +0000 (07:13 +0200)
components/driver/include/driver/timer.h
components/driver/timer.c
docs/api-reference/peripherals/timer.rst
examples/peripherals/timer_group/README.md
examples/peripherals/timer_group/main/timer_group_example_main.c

index 2914a90a353ca922d3ec340e735ed61781d9ed7d..6b43eb4704e65bc720c6203aa9f53e31b0434ebd 100644 (file)
@@ -26,7 +26,8 @@ extern "C" {
 #endif
 
 
-#define TIMER_BASE_CLK   (APB_CLK_FREQ)
+#define TIMER_BASE_CLK   (APB_CLK_FREQ)  /*!< Frequency of the clock on the input of the timer groups */
+
 /**
  * @brief Selects a Timer-Group out of 2 available groups
  */
@@ -90,15 +91,15 @@ typedef enum {
 } timer_autoreload_t;
 
 /**
- * @brief timer configure struct
+ * @brief Data structure with timer's configuration settings
  */
 typedef struct {
-    bool alarm_en; /*!< Timer alarm enable */
-    bool counter_en; /*!< Counter enable */
+    bool alarm_en;      /*!< Timer alarm enable */
+    bool counter_en;    /*!< Counter enable */
     timer_intr_mode_t intr_type; /*!< Interrupt mode */
     timer_count_dir_t counter_dir; /*!< Counter direction  */
-    bool auto_reload; /*!< Timer auto-reload */
-    uint16_t divider; /*!< Counter clock divider*/
+    bool auto_reload;   /*!< Timer auto-reload */
+    uint32_t divider;   /*!< Counter clock divider. The divider's range is from from 2 to 65536. */
 } timer_config_t;
 
 
@@ -202,13 +203,13 @@ esp_err_t timer_set_auto_reload(timer_group_t group_num, timer_idx_t timer_num,
  *
  * @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.
+ * @param divider Timer clock divider value. The divider's range is from from 2 to 65536.
  *
  * @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);
+esp_err_t timer_set_divider(timer_group_t group_num, timer_idx_t timer_num, uint32_t divider);
 
 /**
  * @brief Set timer alarm value.
@@ -249,27 +250,23 @@ esp_err_t timer_get_alarm_value(timer_group_t group_num, timer_idx_t timer_num,
  */
 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.
+ * @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.
  *
  * @param group_num Timer group number
  * @param timer_num Timer index of timer group
  * @param fn Interrupt handler function.
- *        @note
- *        In case the this is called with the INIRAM flag, code inside the handler function can 
- *        only call functions in IRAM, so it cannot call other timer APIs.
- *        Use direct register access to access timers from inside the ISR in this case.
- *
  * @param arg Parameter for handler function
- * @param  intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred)
- *            ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info.
- * @param  handle Pointer to return handle. If non-NULL, a handle for the interrupt will
- *            be returned here.
- * @return
- *     - ESP_OK Success
- *     - ESP_ERR_INVALID_ARG Function pointer error.
+ * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred)
+ *        ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info.
+ * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will
+ *        be returned here.
+ *
+ * @note If the intr_alloc_flags value ESP_INTR_FLAG_IRAM is set, 
+ *       the handler function must be declared with IRAM_ATTR attribute 
+ *       and can only call functions in IRAM or ROM. It cannot call other timer APIs. 
+ *       Use direct register access to configure timers from inside the ISR in this case.
  *
  * @return
  *     - ESP_OK Success
index b80ed643e9abf4b48a63ccf651f0ffc47674e3b5..8f881fc5a859167d3b7d72e8dbeb87b588f2bbb2 100644 (file)
@@ -35,6 +35,7 @@ static const char* TIMER_TAG = "timer_group";
 #define TIMER_AUTORELOAD_ERROR  "HW TIMER AUTORELOAD ERROR"
 #define TIMER_SCALE_ERROR       "HW TIMER SCALE ERROR"
 #define TIMER_ALARM_ERROR       "HW TIMER ALARM ERROR"
+#define DIVIDER_RANGE_ERROR     "HW TIMER divider outside of [2, 65536] range error"
 static timg_dev_t *TG[2] = {&TIMERG0, &TIMERG1};
 static portMUX_TYPE timer_spinlock[TIMER_GROUP_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED};
 
@@ -123,14 +124,15 @@ esp_err_t timer_set_auto_reload(timer_group_t group_num, timer_idx_t timer_num,
     return ESP_OK;
 }
 
-esp_err_t timer_set_divider(timer_group_t group_num, timer_idx_t timer_num, uint16_t divider)
+esp_err_t timer_set_divider(timer_group_t group_num, timer_idx_t timer_num, uint32_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_CHECK(divider > 1 && divider < 65537, DIVIDER_RANGE_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.divider = (uint16_t) divider;
     TG[group_num]->hw_timer[timer_num].config.enable = timer_en;
     TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
     return ESP_OK;
@@ -209,6 +211,7 @@ esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, const timer
     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_CHECK(config->divider > 1 && config->divider < 65537, DIVIDER_RANGE_ERROR, ESP_ERR_INVALID_ARG);
 
     if(group_num == 0) {
         periph_module_enable(PERIPH_TIMG0_MODULE);
@@ -217,7 +220,7 @@ esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, const timer
     }
     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.divider = (uint16_t) 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;
@@ -236,10 +239,11 @@ esp_err_t timer_get_config(timer_group_t group_num, timer_idx_t timer_num, timer
     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->divider =  (TG[group_num]->hw_timer[timer_num].config.divider == 0 ?
+        65536 : 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;
+        config->intr_type = TIMER_INTR_LEVEL;
     }
     TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
     return ESP_OK;
index 5c833ebc094cf1553255ea0c0ec360a4391c1c5d..2d7a057cb07264909e180199b427090b7896344a 100644 (file)
 TIMER
-========
+=====
 
-Overview
---------
+Introduction
+------------
 
-ESP32 chip contains two hardware timer groups, each containing two general-purpose hardware timers. 
+The ESP32 chip contains two hardware timer groups. Each group has two general-purpose hardware timers. They are all 64-bit generic timers based on 16-bit prescalers and 64-bit auto-reload-capable up / down counters.
+
+
+Functional Overview
+-------------------
+
+Typical steps to configure an operate the timer are described in the following sections:
+
+* :ref:`timer-api-timer-initialization` - what parameters should be set up to get the timer working and what specific functionality is provided depending on the set up.
+* :ref:`timer-api-timer-control` - how to read the timer's value, pause / start the timer, and change how it operates.
+* :ref:`timer-api-alarms` - setting and using alarms.
+* :ref:`timer-api-interrupts`- how to enable and use interrupts.
+
+
+.. _timer-api-timer-initialization:
+
+Timer Initialization
+^^^^^^^^^^^^^^^^^^^^
+
+The two timer groups on-board of the ESP32 are identified using :cpp:type:`timer_group_t`. Individual timers in a group are identified with :cpp:type:`timer_idx_t`. The two groups, each having two timers, provide the total of four individual timers to our disposal.
+
+Before starting the timer, it should be initialized by calling :cpp:func:`timer_init`. This function should be provided with a structure :cpp:type:`timer_config_t` to define how timer should operate. In particular the following timer's parameters may be set:
+
+    * **Divider**: How quickly the timer's counter is "ticking". This depends on the setting of :cpp:member:`divider`, that will be used as divisor of the incoming 80 MHz APB_CLK clock.
+    * **Mode**: If the the counter is incrementing or decrementing, defined using :cpp:member:`counter_dir` by selecting one of values from :cpp:type:`timer_count_dir_t`.
+    * **Counter Enable**: If the counter is enabled, then it will start incrementing / decrementing immediately after calling :cpp:func:`timer_init`. This action is set using :cpp:member:`counter_en` by selecting one of vales from :cpp:type:`timer_start_t`.
+    * **Alarm Enable**: Determined by the setting of :cpp:member:`alarm_en`.
+    * **Auto Reload**: Whether the counter should :cpp:member:`auto_reload` a specific initial value on the timer's alarm, or continue incrementing or decrementing.
+    * **Interrupt Type**: Whether an interrupt is triggered on timer's alarm. Set the value defined in :cpp:type:`timer_intr_mode_t`.
+
+To get the current values of the timers settings, use function :cpp:func:`timer_get_config`.
+
+
+.. _timer-api-timer-control:
+
+Timer Control
+^^^^^^^^^^^^^
+
+Once the timer is configured and enabled, it is already "ticking". To check it's current value call :cpp:func:`timer_get_counter_value` or :cpp:func:`timer_get_counter_time_sec`. To set the timer to specific starting value call :cpp:func:`timer_set_counter_value`.
+
+The timer may be paused at any time by calling :cpp:func:`timer_pause`. To start it again call :cpp:func:`timer_start`.
+
+To change how the timer operates you can call once more :cpp:func:`timer_init` described in section :ref:`timer-api-timer-initialization`. Another option is to use dedicated functions to change individual settings: 
+
+    * **Divider** value - :cpp:func:`timer_set_divider`. **Note:** the timer should be paused when changing the divider to avoid unpredictable results. If the timer is already running, :cpp:func:`timer_set_divider` will first pause the timer, change the divider, and finally start the timer again.
+    * **Mode** (whether the counter incrementing or decrementing) - :cpp:func:`timer_set_counter_mode`
+    * **Auto Reload** counter on alarm - :cpp:func:`timer_set_auto_reload`
+
+
+.. _timer-api-alarms:
+
+Alarms
+^^^^^^
+
+To set an alarm, call function :cpp:func:`timer_set_alarm_value` and then enable it with :cpp:func:`timer_set_alarm`. The alarm may be also enabled during the timer initialization stage, when :cpp:func:`timer_init` is called.
+
+After the alarm is enabled and the timer reaches the alarm value, depending on configuration, the following two actions may happen:
+
+    * An interrupt will be triggered, if previously configured. See section :ref:`timer-api-interrupts` how to configure interrupts.
+    * When :cpp:member:`auto_reload` is enabled, the timer's counter will be reloaded to start counting from specific initial value. The value to start should be set in advance with :cpp:func:`timer_set_counter_value`.
+
+.. note::
+
+    The alarm will be triggered immediately, if an alarm value is set and the timer has already passed this value.
+
+To check what alarm value has been set up, call :cpp:func:`timer_get_alarm_value`.
+
+
+.. _timer-api-interrupts:
+
+Interrupts
+^^^^^^^^^^
+
+Registration of the interrupt handler for a specific timer group and timer is done be calling :cpp:func:`timer_isr_register`. 
+
+To enable interrupts for a timer group call :cpp:func:`timer_group_intr_enable`. To do it for a specific timer, call :cpp:func:`timer_enable_intr`. Disabling of interrupts is done with corresponding functions :cpp:func:`timer_group_intr_disable` and :cpp:func:`timer_disable_intr`.
+
+When servicing an interrupt within an ISR, the interrupt need to explicitly cleared. To do so, set the ``TIMERGN.int_clr_timers.tM`` structure defined in :component_file:`soc/esp32/include/soc/timer_group_struct.h`, where N is the timer group number [0, 1] and M is the timer number [0, 1]. For example to clear an interrupt for the timer 1 in the timer group 0, call the following::
+
+    TIMERG0.int_clr_timers.t1 = 1
+
+See the application example below how to use interrupts.
 
-They are all 64-bit generic timers based on 16-bit prescalers and 64-bit auto-reload-capable up/down counters.
 
 Application Example
 -------------------
 
-64-bit hardware timer example: :example:`peripherals/timer_group`.
+The 64-bit hardware timer example: :example:`peripherals/timer_group`.
+
 
 API Reference
 -------------
 
 .. include:: /_build/inc/timer.inc
-
index 0be2c847a8f6b5801cf2cd74a8ab6a88474a3744..08135b3a39bc2bb2d4b37e8fc56aac38e55dc45c 100644 (file)
@@ -1,3 +1,33 @@
 # Example: timer_group
 
 This example uses the timer group driver to generate timer interrupts at two specified alarm intervals.
+
+## Functionality Overview
+
+* Two timers are configured
+* Each timer is set with some sample alarm interval
+* On reaching the interval value each timer will generate an alarm
+* One of the timers is configured to automatically reload it's counter value on the alarm
+* The other timer is configured to keep incrementing and is reloaded by the application each time the alarm happens
+* Alarms trigger subsequent interrupts, that is tracked with messages printed on the terminal:
+
+```
+    Example timer with auto reload
+Group[0], timer[1] alarm event
+------- EVENT TIME --------
+Counter: 0x000000000000000a
+Time   : 0.00000200 s
+-------- TASK TIME --------
+Counter: 0x00000000000107ff
+Time   : 0.01351660 s
+
+    Example timer without reload
+Group[0], timer[0] alarm event
+------- EVENT TIME --------
+Counter: 0x00000000092ae316
+Time   : 30.76111800 s
+-------- TASK TIME --------
+Counter: 0x00000000092bd535
+Time   : 30.77351460 s
+
+```
index 97892e5dec79c270016be1b9056d6d268210263b..0e1ee36fc6384110c3e82fcfc8f2888fc4961580 100644 (file)
 #include "driver/periph_ctrl.h"
 #include "driver/timer.h"
 
-#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 */
+#define TIMER_DIVIDER         16  //  Hardware timer clock divider
+#define TIMER_SCALE           (TIMER_BASE_CLK / TIMER_DIVIDER)  // convert counter value to seconds
+#define TIMER_INTERVAL0_SEC   (3.4179) // sample test interval for the first timer
+#define TIMER_INTERVAL1_SEC   (5.78)   // sample test interval for the second timer
+#define TEST_WITHOUT_RELOAD   0        // testing will be done without auto reload
+#define TEST_WITH_RELOAD      1        // testing will be done with auto reload
 
+/*
+ * A sample structure to pass events
+ * from the timer interrupt handler to the main program.
+ */
 typedef struct {
-    int type;                  /*!< event type */
-    int group;                 /*!< timer group */
-    int idx;                   /*!< timer number */
-    uint64_t counter_val;      /*!< timer counter value */
+    int type;  // the type of timer's event
+    int timer_group;
+    int timer_idx;
+    uint64_t timer_counter_value;
 } timer_event_t;
 
 xQueueHandle timer_queue;
 
 /*
- * @brief Print a uint64_t value
+ * A simple helper function to print the raw timer counter value
+ * and the counter value converted to seconds
  */
-static void inline print_u64(uint64_t val)
+static void inline print_timer_counter(uint64_t counter_value)
 {
-    printf("0x%08x%08x\n", (uint32_t) (val >> 32), (uint32_t) (val));
-}
-
-static void timer_example_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);
-
-        double time = (double) evt.counter_val / (TIMER_BASE_CLK / TIMERG0.hw_timer[evt.idx].config.divider);
-        printf("time: %.8f S\n", time);
-        /*Read timer value from task*/
-        printf("======TASK TIME======\n");
-        uint64_t timer_val;
-        timer_get_counter_value(evt.group, evt.idx, &timer_val);
-        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);
-    }
+    printf("Counter: 0x%08x%08x\n", (uint32_t) (counter_value >> 32),
+                                    (uint32_t) (counter_value));
+    printf("Time   : %.8f s\n", (double) counter_value / TIMER_SCALE);
 }
 
 /*
- * @brief timer group0 ISR handler
+ * Timer group0 ISR handler
+ *
+ * Note:
+ * We don't call the timer API here because they are not declared with IRAM_ATTR.
+ * If we're okay with the timer irq not being serviced while SPI flash cache is disabled,
+ * we can allocate this interrupt without the ESP_INTR_FLAG_IRAM flag and use the normal API.
  */
 void IRAM_ATTR timer_group0_isr(void *para)
 {
     int timer_idx = (int) para;
+
+    /* Retrieve the interrupt status and the counter value
+       from the timer that reported the interrupt */
     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 doesn't reload counter value*/
-        TIMERG0.hw_timer[timer_idx].update = 1;
+    TIMERG0.hw_timer[timer_idx].update = 1;
+    uint64_t timer_counter_value = 
+        ((uint64_t) TIMERG0.hw_timer[timer_idx].cnt_high) << 32
+        | TIMERG0.hw_timer[timer_idx].cnt_low;
 
-        /* We don't call a API here because they are not declared with IRAM_ATTR.
-           If we're okay with the timer irq not being serviced while SPI flash cache is disabled,
-           we can alloc this interrupt without the ESP_INTR_FLAG_IRAM flag and use the normal API. */
-        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;
+    /* Prepare basic event data
+       that will be then sent back to the main program task */
+    timer_event_t evt;
+    evt.timer_group = 0;
+    evt.timer_idx = timer_idx;
+    evt.timer_counter_value = timer_counter_value;
 
-        /*Post an event to out example task*/
+    /* Clear the interrupt
+       and update the alarm time for the timer with without reload */
+    if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_0) {
         evt.type = TEST_WITHOUT_RELOAD;
-        evt.group = 0;
-        evt.idx = timer_idx;
-        evt.counter_val = timer_val;
-        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;
-        /*Post an event to out example task*/
+        TIMERG0.int_clr_timers.t0 = 1;
+        timer_counter_value += (uint64_t) (TIMER_INTERVAL0_SEC * TIMER_SCALE);
+        TIMERG0.hw_timer[timer_idx].alarm_high = (uint32_t) (timer_counter_value >> 32);
+        TIMERG0.hw_timer[timer_idx].alarm_low = (uint32_t) timer_counter_value;
+    } else if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_1) {
         evt.type = TEST_WITH_RELOAD;
-        evt.group = 0;
-        evt.idx = timer_idx;
-        evt.counter_val = timer_val;
-        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;
+        TIMERG0.int_clr_timers.t1 = 1;
+    } else {
+        evt.type = -1; // not supported even type
     }
+
+    /* After the alarm has been triggered
+      we need enable it again, so it is triggered the next time */
+    TIMERG0.hw_timer[timer_idx].config.alarm_en = TIMER_ALARM_EN;
+
+    /* Now just send the event data back to the main program task */
+    xQueueSendFromISR(timer_queue, &evt, NULL);
 }
 
 /*
- * @brief timer group0 hardware timer0 init
+ * Initialize selected timer of the timer group 0
+ *
+ * timer_idx - the timer number to initialize
+ * auto_reload - should the timer auto reload on alarm?
+ * timer_interval_sec - the interval of alarm to set
  */
-static void example_tg0_timer0_init()
+static void example_tg0_timer_init(int timer_idx, 
+    bool auto_reload, double timer_interval_sec)
 {
-    int timer_group = TIMER_GROUP_0;
-    int timer_idx = TIMER_0;
+    /* Select and initialize basic parameters of the timer */
     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_dir = TIMER_COUNT_UP;
     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_group0_isr, (void*) timer_idx, ESP_INTR_FLAG_IRAM, NULL);
-    /*Start timer counter*/
-    timer_start(timer_group, timer_idx);
+    config.alarm_en = TIMER_ALARM_EN;
+    config.intr_type = TIMER_INTR_LEVEL;
+    config.auto_reload = auto_reload;
+    timer_init(TIMER_GROUP_0, timer_idx, &config);
+
+    /* Timer's counter will initially start from value below.
+       Also, if auto_reload is set, this value will be automatically reload on alarm */
+    timer_set_counter_value(TIMER_GROUP_0, timer_idx, 0x00000000ULL);
+
+    /* Configure the alarm value and the interrupt on alarm. */
+    timer_set_alarm_value(TIMER_GROUP_0, timer_idx, timer_interval_sec * TIMER_SCALE);
+    timer_enable_intr(TIMER_GROUP_0, timer_idx);
+    timer_isr_register(TIMER_GROUP_0, timer_idx, timer_group0_isr, 
+        (void *) timer_idx, ESP_INTR_FLAG_IRAM, NULL);
+
+    timer_start(TIMER_GROUP_0, timer_idx);
 }
 
 /*
- * @brief timer group0 hardware timer1 init
+ * The main task of this example program
  */
-static void example_tg0_timer1_init()
+static void timer_example_evt_task(void *arg)
 {
-    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_group0_isr, (void*) timer_idx, ESP_INTR_FLAG_IRAM, NULL);
-    /*Start timer counter*/
-    timer_start(timer_group, timer_idx);
+    while (1) {
+        timer_event_t evt;
+        xQueueReceive(timer_queue, &evt, portMAX_DELAY);
+
+        /* Print information that the timer reported an event */
+        if (evt.type == TEST_WITHOUT_RELOAD) {
+            printf("\n    Example timer without reload\n");
+        } else if (evt.type == TEST_WITH_RELOAD) {
+            printf("\n    Example timer with auto reload\n");
+        } else {
+            printf("\n    UNKNOWN EVENT TYPE\n");
+        }
+        printf("Group[%d], timer[%d] alarm event\n", evt.timer_group, evt.timer_idx);
+
+        /* Print the timer values passed by event */
+        printf("------- EVENT TIME --------\n");
+        print_timer_counter(evt.timer_counter_value);
+
+        /* Print the timer values as visible by this task */
+        printf("-------- TASK TIME --------\n");
+        uint64_t task_counter_value;
+        timer_get_counter_value(evt.timer_group, evt.timer_idx, &task_counter_value);
+        print_timer_counter(task_counter_value);
+    }
 }
 
-/**
- * @brief In this test, we will test hardware timer0 and timer1 of timer group0.
+/*
+ * In this example, we will test hardware timer0 and timer1 of timer group0.
  */
 void app_main()
 {
     timer_queue = xQueueCreate(10, sizeof(timer_event_t));
-    example_tg0_timer0_init();
-    example_tg0_timer1_init();
+    example_tg0_timer_init(TIMER_0, TEST_WITHOUT_RELOAD, TIMER_INTERVAL0_SEC);
+    example_tg0_timer_init(TIMER_1, TEST_WITH_RELOAD,    TIMER_INTERVAL1_SEC);
     xTaskCreate(timer_example_evt_task, "timer_evt_task", 2048, NULL, 5, NULL);
 }