Timer API docs update, refactored example

This commit is contained in:
krzychb 2017-09-25 06:20:12 +02:00
parent 91d9cb98d3
commit 9b7d5d76a7
5 changed files with 270 additions and 186 deletions

View file

@ -26,7 +26,8 @@ extern "C" {
#endif #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 * @brief Selects a Timer-Group out of 2 available groups
*/ */
@ -90,15 +91,15 @@ typedef enum {
} timer_autoreload_t; } timer_autoreload_t;
/** /**
* @brief timer configure struct * @brief Data structure with timer's configuration settings
*/ */
typedef struct { typedef struct {
bool alarm_en; /*!< Timer alarm enable */ bool alarm_en; /*!< Timer alarm enable */
bool counter_en; /*!< Counter enable */ bool counter_en; /*!< Counter enable */
timer_intr_mode_t intr_type; /*!< Interrupt mode */ timer_intr_mode_t intr_type; /*!< Interrupt mode */
timer_count_dir_t counter_dir; /*!< Counter direction */ timer_count_dir_t counter_dir; /*!< Counter direction */
bool auto_reload; /*!< Timer auto-reload */ bool auto_reload; /*!< Timer auto-reload */
uint16_t divider; /*!< Counter clock divider*/ uint32_t divider; /*!< Counter clock divider. The divider's range is from from 2 to 65536. */
} timer_config_t; } 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 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 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 * @return
* - ESP_OK Success * - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error * - 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. * @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); 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. * @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. * The handler will be attached to the same CPU core that this function is running on.
* *
* @param group_num Timer group number * @param group_num Timer group number
* @param timer_num Timer index of timer group * @param timer_num Timer index of timer group
* @param fn Interrupt handler function. * @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 arg Parameter for handler function
* @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) * @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. * 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 * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will
* be returned here. * be returned here.
* @return *
* - ESP_OK Success * @note If the intr_alloc_flags value ESP_INTR_FLAG_IRAM is set,
* - ESP_ERR_INVALID_ARG Function pointer error. * 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 * @return
* - ESP_OK Success * - ESP_OK Success

View file

@ -35,6 +35,7 @@ static const char* TIMER_TAG = "timer_group";
#define TIMER_AUTORELOAD_ERROR "HW TIMER AUTORELOAD ERROR" #define TIMER_AUTORELOAD_ERROR "HW TIMER AUTORELOAD ERROR"
#define TIMER_SCALE_ERROR "HW TIMER SCALE ERROR" #define TIMER_SCALE_ERROR "HW TIMER SCALE ERROR"
#define TIMER_ALARM_ERROR "HW TIMER ALARM 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 timg_dev_t *TG[2] = {&TIMERG0, &TIMERG1};
static portMUX_TYPE timer_spinlock[TIMER_GROUP_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; 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; 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(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_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]); TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
int timer_en = TG[group_num]->hw_timer[timer_num].config.enable; 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.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; TG[group_num]->hw_timer[timer_num].config.enable = timer_en;
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]); TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK; 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(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_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 != 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) { if(group_num == 0) {
periph_module_enable(PERIPH_TIMG0_MODULE); 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]); 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.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.enable = config->counter_en;
TG[group_num]->hw_timer[timer_num].config.increase = config->counter_dir; 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.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->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->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.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; config->counter_en = TG[group_num]->hw_timer[timer_num].config.enable;
if(TG[group_num]->hw_timer[timer_num].config.level_int_en) { 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]); TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);
return ESP_OK; return ESP_OK;

View file

@ -1,20 +1,100 @@
TIMER 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 Application Example
------------------- -------------------
64-bit hardware timer example: :example:`peripherals/timer_group`. The 64-bit hardware timer example: :example:`peripherals/timer_group`.
API Reference API Reference
------------- -------------
.. include:: /_build/inc/timer.inc .. include:: /_build/inc/timer.inc

View file

@ -1,3 +1,33 @@
# Example: timer_group # Example: timer_group
This example uses the timer group driver to generate timer interrupts at two specified alarm intervals. 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
```

View file

@ -16,186 +16,159 @@
#include "driver/periph_ctrl.h" #include "driver/periph_ctrl.h"
#include "driver/timer.h" #include "driver/timer.h"
#define TIMER_INTR_SEL TIMER_INTR_LEVEL /*!< Timer level interrupt */ #define TIMER_DIVIDER 16 // Hardware timer clock divider
#define TIMER_GROUP TIMER_GROUP_0 /*!< Test on timer group 0 */ #define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) // convert counter value to seconds
#define TIMER_DIVIDER 16 /*!< Hardware timer clock divider */ #define TIMER_INTERVAL0_SEC (3.4179) // sample test interval for the first timer
#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) /*!< used to calculate counter value */ #define TIMER_INTERVAL1_SEC (5.78) // sample test interval for the second timer
#define TIMER_FINE_ADJ (1.4*(TIMER_BASE_CLK / TIMER_DIVIDER)/1000000) /*!< used to compensate alarm value */ #define TEST_WITHOUT_RELOAD 0 // testing will be done without auto reload
#define TIMER_INTERVAL0_SEC (3.4179) /*!< test interval for timer 0 */ #define TEST_WITH_RELOAD 1 // testing will be done with auto reload
#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 */
/*
* A sample structure to pass events
* from the timer interrupt handler to the main program.
*/
typedef struct { typedef struct {
int type; /*!< event type */ int type; // the type of timer's event
int group; /*!< timer group */ int timer_group;
int idx; /*!< timer number */ int timer_idx;
uint64_t counter_val; /*!< timer counter value */ uint64_t timer_counter_value;
} timer_event_t; } timer_event_t;
xQueueHandle timer_queue; 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)); 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);
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);
}
} }
/* /*
* @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) void IRAM_ATTR timer_group0_isr(void *para)
{ {
int timer_idx = (int) 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; uint32_t intr_status = TIMERG0.int_st_timers.val;
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;
/* Prepare basic event data
that will be then sent back to the main program task */
timer_event_t evt; timer_event_t evt;
if((intr_status & BIT(timer_idx)) && timer_idx == TIMER_0) { evt.timer_group = 0;
/*Timer0 is an example that doesn't reload counter value*/ evt.timer_idx = timer_idx;
TIMERG0.hw_timer[timer_idx].update = 1; evt.timer_counter_value = timer_counter_value;
/* We don't call a API here because they are not declared with IRAM_ATTR. /* Clear the interrupt
If we're okay with the timer irq not being serviced while SPI flash cache is disabled, and update the alarm time for the timer with without reload */
we can alloc this interrupt without the ESP_INTR_FLAG_IRAM flag and use the normal API. */ if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_0) {
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;
/*Post an event to out example task*/
evt.type = TEST_WITHOUT_RELOAD; evt.type = TEST_WITHOUT_RELOAD;
evt.group = 0; TIMERG0.int_clr_timers.t0 = 1;
evt.idx = timer_idx; timer_counter_value += (uint64_t) (TIMER_INTERVAL0_SEC * TIMER_SCALE);
evt.counter_val = timer_val; TIMERG0.hw_timer[timer_idx].alarm_high = (uint32_t) (timer_counter_value >> 32);
xQueueSendFromISR(timer_queue, &evt, NULL); TIMERG0.hw_timer[timer_idx].alarm_low = (uint32_t) timer_counter_value;
} else if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_1) {
/*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*/
evt.type = TEST_WITH_RELOAD; evt.type = TEST_WITH_RELOAD;
evt.group = 0; TIMERG0.int_clr_timers.t1 = 1;
evt.idx = timer_idx; } else {
evt.counter_val = timer_val; evt.type = -1; // not supported even type
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; /* 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);
}
/*
* 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_timer_init(int timer_idx,
bool auto_reload, double timer_interval_sec)
{
/* Select and initialize basic parameters of the timer */
timer_config_t config;
config.divider = TIMER_DIVIDER;
config.counter_dir = TIMER_COUNT_UP;
config.counter_en = TIMER_PAUSE;
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);
}
/*
* The main task of this example program
*/
static void timer_example_evt_task(void *arg)
{
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 timer group0 hardware timer0 init * In this example, we will test hardware timer0 and timer1 of timer group0.
*/
static void example_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_group0_isr, (void*) timer_idx, ESP_INTR_FLAG_IRAM, NULL);
/*Start timer counter*/
timer_start(timer_group, timer_idx);
}
/*
* @brief timer group0 hardware timer1 init
*/
static void example_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_group0_isr, (void*) timer_idx, ESP_INTR_FLAG_IRAM, NULL);
/*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() void app_main()
{ {
timer_queue = xQueueCreate(10, sizeof(timer_event_t)); timer_queue = xQueueCreate(10, sizeof(timer_event_t));
example_tg0_timer0_init(); example_tg0_timer_init(TIMER_0, TEST_WITHOUT_RELOAD, TIMER_INTERVAL0_SEC);
example_tg0_timer1_init(); example_tg0_timer_init(TIMER_1, TEST_WITH_RELOAD, TIMER_INTERVAL1_SEC);
xTaskCreate(timer_example_evt_task, "timer_evt_task", 2048, NULL, 5, NULL); xTaskCreate(timer_example_evt_task, "timer_evt_task", 2048, NULL, 5, NULL);
} }