Merge branch 'refactor/using_isr_callback_in_timer_example' into 'master'

TIMG: clean up timer example and add example test

Closes IDF-2722, IDF-2766, and IDF-2347

See merge request espressif/esp-idf!12218
This commit is contained in:
Michael (XIAO Xufeng) 2021-03-22 06:36:32 +00:00
commit 3a90d51831
17 changed files with 223 additions and 233 deletions

View File

@ -43,11 +43,9 @@ _Static_assert(TIMER_INTR_WDT == TIMG_WDT_INT_CLR, "Add mapping to LL interrupt
*/
static inline void timer_ll_set_divider(timg_dev_t *hw, timer_idx_t timer_num, uint32_t divider)
{
// refer to TRM 18.2.1
if (divider == 65536) {
assert(divider >= 2 && divider <= 65536);
if (divider >= 65536) {
divider = 0;
} else if (divider == 1) {
divider = 2;
}
int timer_en = hw->hw_timer[timer_num].config.enable;
hw->hw_timer[timer_num].config.enable = 0;

View File

@ -45,10 +45,15 @@ typedef struct {
*
* @return None
*/
static inline void timer_ll_set_divider(timg_dev_t *hw, timer_idx_t timer_num, uint16_t divider)
static inline void timer_ll_set_divider(timg_dev_t *hw, timer_idx_t timer_num, uint32_t divider)
{
assert(divider >= 2 && divider <= 65536);
if (divider >= 65536) {
divider = 0;
}
int timer_en = hw->hw_timer[timer_num].config.enable;
hw->hw_timer[timer_num].config.enable = 0;
hw->hw_timer[timer_num].config.divcnt_rst = 1;
hw->hw_timer[timer_num].config.divider = divider;
hw->hw_timer[timer_num].config.enable = timer_en;
}
@ -67,6 +72,8 @@ static inline void timer_ll_get_divider(timg_dev_t *hw, timer_idx_t timer_num, u
uint32_t d = hw->hw_timer[timer_num].config.divider;
if (d == 0) {
d = 65536;
} else if (d == 1) {
d = 2;
}
*divider = d;
}

View File

@ -43,8 +43,8 @@ _Static_assert(TIMER_INTR_WDT == TIMG_WDT_INT_CLR, "Add mapping to LL interrupt
*/
static inline void timer_ll_set_divider(timg_dev_t *hw, timer_idx_t timer_num, uint32_t divider)
{
// refer to TRM 12.2.1
if (divider == 65536) {
assert(divider >= 2 && divider <= 65536);
if (divider >= 65536) {
divider = 0;
}
int timer_en = hw->hw_timer[timer_num].config.enable;
@ -67,6 +67,8 @@ static inline void timer_ll_get_divider(timg_dev_t *hw, timer_idx_t timer_num, u
uint32_t d = hw->hw_timer[timer_num].config.divider;
if (d == 0) {
d = 65536;
} else if (d == 1) {
d = 2;
}
*divider = d;
}

View File

@ -46,8 +46,12 @@ typedef struct {
*
* @return None
*/
static inline void timer_ll_set_divider(timg_dev_t *hw, timer_idx_t timer_num, uint16_t divider)
static inline void timer_ll_set_divider(timg_dev_t *hw, timer_idx_t timer_num, uint32_t divider)
{
assert(divider >= 2 && divider <= 65536);
if (divider >= 65536) {
divider = 0;
}
int timer_en = hw->hw_timer[timer_num].config.enable;
hw->hw_timer[timer_num].config.enable = 0;
hw->hw_timer[timer_num].config.divider = divider;
@ -68,6 +72,8 @@ static inline void timer_ll_get_divider(timg_dev_t *hw, timer_idx_t timer_num, u
uint32_t d = hw->hw_timer[timer_num].config.divider;
if (d == 0) {
d = 65536;
} else if (d == 1) {
d = 2;
}
*divider = d;
}
@ -326,16 +332,7 @@ FORCE_INLINE_ATTR void timer_ll_get_intr_raw_status(timer_group_t group_num, uin
*/
static inline void timer_ll_set_level_int_enable(timg_dev_t *hw, timer_idx_t timer_num, bool level_int_en)
{
switch (timer_num) {
case 0:
hw->int_ena.t0 = level_int_en;
break;
case 1:
hw->int_ena.t1 = level_int_en;
break;
default:
break;
}
// Only "level" interrupts are supported on this target
}
/**
@ -350,18 +347,7 @@ static inline void timer_ll_set_level_int_enable(timg_dev_t *hw, timer_idx_t tim
*/
static inline bool timer_ll_get_level_int_enable(timg_dev_t *hw, timer_idx_t timer_num)
{
bool enable = false;
switch (timer_num) {
case 0:
enable = hw->int_ena.t0;
break;
case 1:
enable = hw->int_ena.t1;
break;
default:
break;
}
return enable;
return true;
}
/**

View File

@ -209,12 +209,10 @@
#define SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(spi_host) ({(void)spi_host; 1;})
/*-------------------------- TIMER GROUP CAPS --------------------------------*/
#define SOC_TIMER_GROUP_COUNTER_BIT_WIDTH (64)
#define SOC_TIMER_GROUP_PRESCALE_BIT_WIDTH (16)
#define SOC_TIMER_GROUPS (2)
#define SOC_TIMER_GROUP_TIMERS_PER_GROUP (2)
#define SOC_TIMER_GROUPS (2)
#define SOC_TIMER_GROUP_TIMERS_PER_GROUP (2)
#define SOC_TIMER_GROUP_COUNTER_BIT_WIDTH (64)
#define SOC_TIMER_GROUP_TOTAL_TIMERS (SOC_TIMER_GROUPS * SOC_TIMER_GROUP_TIMERS_PER_GROUP)
#define SOC_TIMER_GROUP_LAYOUT {2,2}
/*-------------------------- TOUCH SENSOR CAPS -------------------------------*/
#define SOC_TOUCH_SENSOR_NUM (10)

View File

@ -26,7 +26,6 @@
#include "i2s_caps.h"
#include "rtc_io_caps.h"
#include "soc_caps.h"
#include "timer_group_caps.h"
#include "cpu_caps.h"
#include "gpio_caps.h"
#include "ledc_caps.h"
@ -76,6 +75,12 @@
#define SOC_SHA_SUPPORT_SHA224 (1)
#define SOC_SHA_SUPPORT_SHA256 (1)
/*--------------------------- TIMER GROUP CAPS ---------------------------------------*/
#define SOC_TIMER_GROUPS (2)
#define SOC_TIMER_GROUP_TIMERS_PER_GROUP (1)
#define SOC_TIMER_GROUP_COUNTER_BIT_WIDTH (54)
#define SOC_TIMER_GROUP_SUPPORT_XTAL (1)
#define SOC_TIMER_GROUP_TOTAL_TIMERS (SOC_TIMER_GROUPS * SOC_TIMER_GROUP_TIMERS_PER_GROUP)
/*--------------------------- RMT CAPS ---------------------------------------*/
#define SOC_RMT_GROUPS (1) /*!< One RMT group */

View File

@ -1,24 +0,0 @@
// Copyright 2020 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.
#pragma once
#define SOC_TIMER_GROUP_SUPPORT_XTAL (1)
#define SOC_TIMER_GROUP_XTAL_MHZ (40)
#define SOC_TIMER_GROUP_COUNTER_BIT_WIDTH (54)
#define SOC_TIMER_GROUP_PRESCALE_BIT_WIDTH (16)
#define SOC_TIMER_GROUPS (2)
#define SOC_TIMER_GROUP_TIMERS_PER_GROUP (1)
#define SOC_TIMER_GROUP_TOTAL_TIMERS (SOC_TIMER_GROUPS * SOC_TIMER_GROUP_TIMERS_PER_GROUP)
#define SOC_TIMER_GROUP_LAYOUT {1,1}

View File

@ -206,7 +206,7 @@ typedef volatile struct {
union {
struct {
uint32_t reserved0: 29;
uint32_t clk_is_active: 1;
uint32_t wdt_clk_is_active: 1;
uint32_t timer_clk_is_active: 1;
uint32_t en: 1;
};

View File

@ -217,14 +217,11 @@
#define SOC_SYSTIMER_BIT_WIDTH_HI (32) // Bit width of systimer high part
/*-------------------------- TIMER GROUP CAPS --------------------------------*/
#define SOC_TIMER_GROUP_SUPPORT_XTAL (1)
#define SOC_TIMER_GROUP_XTAL_MHZ (40)
#define SOC_TIMER_GROUP_COUNTER_BIT_WIDTH (64)
#define SOC_TIMER_GROUP_PRESCALE_BIT_WIDTH (16)
#define SOC_TIMER_GROUPS (2)
#define SOC_TIMER_GROUP_TIMERS_PER_GROUP (2)
#define SOC_TIMER_GROUP_COUNTER_BIT_WIDTH (64)
#define SOC_TIMER_GROUPS (2)
#define SOC_TIMER_GROUP_TIMERS_PER_GROUP (2)
#define SOC_TIMER_GROUP_SUPPORT_XTAL (1)
#define SOC_TIMER_GROUP_TOTAL_TIMERS (SOC_TIMER_GROUPS * SOC_TIMER_GROUP_TIMERS_PER_GROUP)
#define SOC_TIMER_GROUP_LAYOUT {2,2}
/*-------------------------- TOUCH SENSOR CAPS -------------------------------*/
#define SOC_TOUCH_SENSOR_NUM (15) /*! 15 Touch channels */

View File

@ -108,13 +108,11 @@
#include "systimer_caps.h"
/*-------------------------- TIMER GROUP CAPS --------------------------------*/
#define SOC_TIMER_GROUP_SUPPORT_XTAL (1)
#define SOC_TIMER_GROUP_COUNTER_BIT_WIDTH (54)
#define SOC_TIMER_GROUP_PRESCALE_BIT_WIDTH (16)
#define SOC_TIMER_GROUPS (2)
#define SOC_TIMER_GROUP_TIMERS_PER_GROUP (2)
#define SOC_TIMER_GROUPS (2)
#define SOC_TIMER_GROUP_TIMERS_PER_GROUP (2)
#define SOC_TIMER_GROUP_COUNTER_BIT_WIDTH (54)
#define SOC_TIMER_GROUP_SUPPORT_XTAL (1)
#define SOC_TIMER_GROUP_TOTAL_TIMERS (SOC_TIMER_GROUPS * SOC_TIMER_GROUP_TIMERS_PER_GROUP)
#define SOC_TIMER_GROUP_LAYOUT {2,2}
/*-------------------------- TOUCH SENSOR CAPS -------------------------------*/
#include "touch_sensor_caps.h"

View File

@ -9,6 +9,7 @@ Peripherals API
ADC <adc>
:SOC_DAC_PERIPH_NUM: DAC <dac>
General Purpose Timer <timer>
GPIO (including RTC low power I/O) <gpio>
:SOC_DEDICATED_GPIO_SUPPORTED: Dedicated GPIO <dedic_gpio>
:SOC_HMAC_SUPPORTED: HMAC <hmac>
@ -29,7 +30,6 @@ Peripherals API
:esp32: Secure Element <secure_element>
:esp32s2: SPI Slave Half Duplex <spi_slave_hd>
:esp32s2: Temp sensor <temp_sensor>
Timer <timer>
:SOC_TOUCH_SENSOR_NUM: Touch Sensor <touch_pad>
:esp32s2: Touch Element <touch_element>
TWAI <twai>

View File

@ -1,13 +1,12 @@
Timer
=====
General Purpose Timer
=====================
:link_to_translation:`zh_CN:[中文]`
{IDF_TARGET_INT_CLR_REG: default="int_clr", esp32="int_clr_timers"}
Introduction
------------
The {IDF_TARGET_NAME} 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 up / down counters which are capable of being auto-reloaded.
The {IDF_TARGET_NAME} chip contains two hardware timer groups. Each group has two general-purpose hardware timers. They are all {SOC_TIMER_GROUP_COUNTER_BIT_WIDTH}-bit generic timers based on 16-bit pre-scalers and {SOC_TIMER_GROUP_COUNTER_BIT_WIDTH}-bit up / down counters which are capable of being auto-reloaded.
Functional Overview
@ -18,7 +17,7 @@ The following sections of this document cover the typical steps to configure and
* :ref:`timer-api-timer-initialization` - covers which parameters should be set up to get the timer working, and also what specific functionality is provided depending on the timer configuration.
* :ref:`timer-api-timer-control` - describes how to read a timer's value, pause or start a timer, and change how it operates.
* :ref:`timer-api-alarms` - shows how to set and use alarms.
* :ref:`timer-api-interrupts`- explains how to enable and use interrupts.
* :ref:`timer-api-interrupts`- explains how to use interrupt callbacks.
.. _timer-api-timer-initialization:
@ -30,12 +29,12 @@ The two {IDF_TARGET_NAME} timer groups, with two timers in each, provide the tot
First of all, the timer should be initialized by calling the function :cpp:func:`timer_init` and passing a structure :cpp:type:`timer_config_t` to it to define how the timer should operate. In particular, the following timer parameters can be set:
* **Divider**: Sets how quickly the timer's counter is "ticking". The setting :cpp:member:`divider` is used as a divisor of the incoming 80 MHz APB_CLK clock.
* **Clock Source**: Select the clock source, which together with the **Divider** define the resolution of the working timer. By default the clock source is APB_CLK (typically 80 MHz).
* **Divider**: Sets how quickly the timer's counter is "ticking". The setting :cpp:member:`divider` is used as a divisor of the clock source.
* **Mode**: Sets if the counter should be incrementing or decrementing. It can be defined using :cpp:member:`counter_dir` by selecting one of the values from :cpp:type:`timer_count_dir_t`.
* **Counter Enable**: If the counter is enabled, it will start incrementing / decrementing immediately after calling :cpp:func:`timer_init`. You can change the behavior with :cpp:member:`counter_en` by selecting one of the values from :cpp:type:`timer_start_t`.
* **Alarm Enable**: Can be set using :cpp:member:`alarm_en`.
* **Auto Reload**: Sets if the counter should :cpp:member:`auto_reload` the initial counter value on the timer's alarm or continue incrementing or decrementing.
* **Interrupt Type**: Select which interrupt type should be triggered on the timer's alarm. Set the value defined in :cpp:type:`timer_intr_mode_t`.
To get the current values of the timer's settings, use the function :cpp:func:`timer_get_config`.
@ -86,14 +85,8 @@ To check the specified alarm value, call :cpp:func:`timer_get_alarm_value`.
Interrupts
^^^^^^^^^^
Registration of the interrupt handler for a specific timer or a timer group can be done by calling :cpp:func:`timer_isr_register`.
To enable interrupts for a timer group, call :cpp:func:`timer_group_intr_enable`, for a specific timer call :cpp:func:`timer_enable_intr`.
To disable interrupts for a timer group, call :cpp:func:`timer_group_intr_disable`, for a specified timer, call :cpp:func:`timer_disable_intr`.
When handling an interrupt within an interrupt serivce routine (ISR), the interrupt status bit needs to be explicitly cleared. To do that, set the ``TIMERGN.{IDF_TARGET_INT_CLR_REG}.tM`` structure, defined in :component_file:`soc/{IDF_TARGET_PATH_NAME}/include/soc/timer_group_struct.h`. In this structure, ``N`` is the timer group number [0, 1], ``M`` is the timer number [0, 1]. For example, to clear an interrupt status bit for the timer 1 in the timer group 0, call the following::
TIMERG0.{IDF_TARGET_INT_CLR_REG}.t1 = 1
Registration of an interrupt callback for a specific timer can be done by calling :cpp:func:`timer_isr_callback_add` and passing in the group ID, timer ID, callback handler and user data. The callback handler will be invoked in ISR context, so user shouldn't put any blocking API in the callback function.
The benefit of using interrupt callback instead of precessing interrupt from scratch is, you don't have to deal with interrupt status check and clean stuffs, they are all addressed before the callback got run in driver's default interrupt handler.
For more information on how to use interrupts, please see the application example below.
@ -101,7 +94,7 @@ For more information on how to use interrupts, please see the application exampl
Application Example
-------------------
The 64-bit hardware timer example: :example:`peripherals/timer_group`.
The {SOC_TIMER_GROUP_COUNTER_BIT_WIDTH}-bit hardware timer example: :example:`peripherals/timer_group`.
API Reference

View File

@ -8,6 +8,7 @@
ADC <adc>
:SOC_DAC_PERIPH_NUM: DAC <dac>
通用定时器 <timer>
GPIO (包括 RTC 低功耗 I/O) <gpio>
:SOC_DEDICATED_GPIO_SUPPORTED: 专用 GPIO <dedic_gpio>
:SOC_HMAC_SUPPORTED: HMAC <hmac>
@ -27,7 +28,6 @@
:esp32: Secure Element <secure_element>
:esp32s2: SPI Slave 半双工 (half duplex) <spi_slave_hd>
:esp32s2: Temp sensor <temp_sensor>
Timer <timer>
:SOC_TOUCH_SENSOR_NUM: Touch Sensor <touch_pad>
:esp32s2: Touch Element <touch_element>
TWAI <twai>

View File

@ -1,40 +1,40 @@
定时器
============
通用定时器
==========
:link_to_translation:`en:[English]`
简介
------------
----
ESP32 芯片提供两组硬件定时器,每组包含两个通用硬件定时器。所有定时器均为 64 位通用定时器,包括 16 位预分频器和 64 位自动重载向上/向下计数器。
{IDF_TARGET_NAME} 芯片提供两组硬件定时器,每组包含两个通用硬件定时器。所有定时器均为 {SOC_TIMER_GROUP_COUNTER_BIT_WIDTH} 位通用定时器,包括 16 位预分频器和 {SOC_TIMER_GROUP_COUNTER_BIT_WIDTH} 位自动重载向上/向下计数器。
功能概述
-------------------
--------
下文介绍了配置和操作定时器的常规步骤:
* :ref:`timer-api-timer-initialization` - 启动定时器前应设置的参数,以及每个设置提供的具体功能。
* :ref:`timer-api-timer-control` - 如何读取定时器的值,如何暂停/启动定时器以及如何改变定时器的操作方式。
* :ref:`timer-api-alarms` - 如何设置和使用警报。
* :ref:`timer-api-interrupts`- 如何使能和使用中断。
* :ref:`timer-api-interrupts`- 如何使用中断提供的回调函数
.. _timer-api-timer-initialization:
定时器初始化
^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^
两个 ESP32 定时器组中每组都有两个定时器两组共有四个定时器供使用。ESP32 定时器组应使用 :cpp:type:`timer_group_t` 识别,而每组中的个体定时器则应使用 :cpp:type:`timer_idx_t` 识别
两个 {IDF_TARGET_NAME} 定时器组中,每组都有两个定时器,两组共有四个定时器供使用。{IDF_TARGET_NAME} 定时器组的类型为 :cpp:type:`timer_group_t`,每组中的个体定时器类型为 :cpp:type:`timer_idx_t`
首先调用 :cpp:func:`timer_init` 函数,并将 :cpp:type:`timer_config_t` 结构体传递给此函数,用于定义定时器的工作方式,实现定时器初始化。特别注意以下定时器参数可设置为:
* **分频器**: 设置定时器中计数器计数的速度,:cpp:member:`divider` 的设置将用作输入的 80 MHz APB_CLK 时钟的除数。
* **时钟源**: 选择时钟源,它同时钟分频器一起决定了定时器的分辨率。默认的时钟源是 APB_CLK (一般是 80 MHz)。
* **分频器**: 设置定时器中计数器计数的速度,:cpp:member:`divider` 的设置将用作输入时钟源的除数。
* **模式**: 设置计数器是递增还是递减。可通过从 :cpp:type:`timer_count_dir_t` 中选取一个值,后使用 :cpp:member:`counter_dir` 来选择模式。
* **计数器使能**: 如果计数器已使能,则在调用 :cpp:func:`timer_init` 后计数器将立即开始递增/递减。您可通过从 :cpp:type:`timer_start_t` 中选取一个值,后使用 :cpp:member:`counter_en` 改变此行为。
* **报警使能**: 可使用 :cpp:member:`alarm_en` 设置。
* **自动重载**: 设置计数器是否应该在定时器警报上使用 :cpp:member:`auto_reload` 自动重载首个计数值,还是继续递增或递减。
* **中断类型**: 选择定时器警报上应触发的中断类型,请设置 :cpp:type:`timer_intr_mode_t` 中定义的值。
要获取定时器设置的当前值,请使用函数 :cpp:func:`timer_get_config`
@ -82,27 +82,22 @@ ESP32 芯片提供两组硬件定时器,每组包含两个通用硬件定时
.. _timer-api-interrupts:
中断
^^^^^^^^^^
处理中断事务
^^^^^^^^^^^^
可通过调用函数 :cpp:func:`timer_isr_register` 为特定定时器组和定时器注册中断处理程序。
调用 :cpp:func:`timer_isr_callback_add` 函数可以给某个定时器注册一个中断回调函数,顾名思义,该函数会在中断上下文中被执行,因此用户不能在回调函数中调用任何会阻塞 CPU 的 API。
相较于从头编写中断处理程序,使用中断回调函数的好处是,用户无需检测和处理中断的状态位,这些操作会由驱动中默认的中断处理程序替我们完成。
调用 :cpp:func:`timer_group_intr_enable` 使能定时器组的中断程序,调用 :cpp:func:`timer_enable_intr` 使能某定时器的中断程序。调用 :cpp:func:`timer_group_intr_disable` 关闭定时器组的中断程序,调用 :cpp:func:`timer_disable_intr` 关闭某定时器的中断程序。
在中断服务程序ISR中处理中断时需要明确地清除中断状态位。为此请设置定义在 :component_file:`soc/esp32/include/soc/timer_group_struct.h` 中的 ``TIMERGN.int_clr_timers.tM`` 结构。该结构中 ``N`` 是定时器组别编号 [0, 1]``M`` 是定时器编号 [0, 1]。例如,要清除定时器组别 0 中定时器 1 的中断状态位,请调用以下命令::
TIMERG0.int_clr_timers.t1 = 1
有关如何使用中断,请参阅应用示例。
有关如何使用中断回调函数,请参考如下应用示例。
应用示例
-------------------
--------
64 位硬件定时器示例::example:`peripherals/timer_group`
{SOC_TIMER_GROUP_COUNTER_BIT_WIDTH} 位通用硬件定时器示例::example:`peripherals/timer_group`
API 参考
-------------
--------
.. include-build-file:: inc/timer.inc
.. include-build-file:: inc/timer_types.inc

View File

@ -1,10 +1,51 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# Example: timer_group
# Example: General Purpose Timer
This example uses the timer group driver to generate timer interrupts at two specified alarm intervals.
## How to Use Example
### Hardware Required
* A development board with ESP SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
* A USB cable for Power supply and programming
### Build and Flash
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
Timer Group with auto reload
Group[0], timer[0] alarm event
------- EVENT TIME --------
Counter: 0x0000000000000008
Time : 0.00000160 s
-------- TASK TIME --------
Counter: 0x0000000000004ed8
Time : 0.00403680 s
Timer Group without auto reload
Group[1], timer[0] alarm event
------- EVENT TIME --------
Counter: 0x00000000017d7848
Time : 5.00000160 s
-------- TASK TIME --------
Counter: 0x00000000017dcb32
Time : 5.00424680 s
Timer Group with auto reload
Group[0], timer[0] alarm event
------- EVENT TIME --------
Counter: 0x0000000000000008
Time : 0.00000160 s
-------- TASK TIME --------
Counter: 0x0000000000004dd4
Time : 0.00398480 s
```
## Functionality Overview
* Two timers are configured
@ -14,23 +55,6 @@ This example uses the timer group driver to generate timer interrupts at two spe
* 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
## Troubleshooting
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
```
For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@ -0,0 +1,39 @@
from __future__ import unicode_literals
import re
from typing import Any
import ttfw_idf
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
def test_examples_timergroup(env, extra_data): # type: (Any, Any) -> None
dut = env.get_dut('timer_group', 'examples/peripherals/timer_group')
dut.start_app()
# check auto reload function
with_auto_reload = dut.expect(re.compile(r'Timer Group (\S+) auto reload'), timeout=30)[0]
assert with_auto_reload == 'with'
select_groups = dut.expect(re.compile(r'Group\[(\d)\], timer\[(\d)\] alarm event'))
timer_group_num = int(select_groups[0])
timer_instance_num = int(select_groups[1])
assert timer_group_num == 0 and timer_instance_num == 0
dut.expect('EVENT TIME')
counter_value = dut.expect(re.compile(r'Counter:\s+(0x\d+)'))[0]
counter_value = int(counter_value, 16)
print('counter value at auto reload event: ', counter_value)
assert counter_value < 20
# check timer interval
dut.expect('Timer Group without auto reload', timeout=5)
dut.expect('EVENT TIME')
event_time0 = dut.expect(re.compile(r'Time\s+:\s+(\d+\.\d+)\s+s'))[0]
dut.expect('Timer Group without auto reload', timeout=6)
dut.expect('EVENT TIME')
event_time1 = dut.expect(re.compile(r'Time\s+:\s+(\d+\.\d+)\s+s'))[0]
print('event0={}, event1={}'.format(event_time0, event_time1))
assert float(event_time1) - float(event_time0) < 5.001
if __name__ == '__main__':
test_examples_timergroup()

View File

@ -1,4 +1,4 @@
/* Timer group-hardware timer example
/* General Purpose Timer example
This example code is in the Public Domain (or CC0 licensed, at your option.)
@ -7,32 +7,31 @@
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 "driver/periph_ctrl.h"
#include "driver/timer.h"
#define TIMER_DIVIDER 16 // Hardware timer clock divider
#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; // the type of timer's event
int timer_group;
int timer_idx;
uint64_t timer_counter_value;
} timer_event_t;
int alarm_interval;
bool auto_reload;
} example_timer_info_t;
xQueueHandle timer_queue;
/**
* @brief A sample structure to pass events from the timer ISR to task
*
*/
typedef struct {
example_timer_info_t info;
uint64_t timer_counter_value;
} example_timer_event_t;
static xQueueHandle s_timer_queue;
/*
* A simple helper function to print the raw timer counter value
@ -40,68 +39,47 @@ xQueueHandle timer_queue;
*/
static void inline print_timer_counter(uint64_t counter_value)
{
printf("Counter: 0x%08x%08x\n", (uint32_t) (counter_value >> 32),
printf("Counter: 0x%08x%08x\r\n", (uint32_t) (counter_value >> 32),
(uint32_t) (counter_value));
printf("Time : %.8f s\n", (double) counter_value / TIMER_SCALE);
printf("Time : %.8f s\r\n", (double) counter_value / TIMER_SCALE);
}
/*
* 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)
static bool IRAM_ATTR timer_group_isr_callback(void *args)
{
timer_spinlock_take(TIMER_GROUP_0);
int timer_idx = (int) para;
BaseType_t high_task_awoken = pdFALSE;
example_timer_info_t *info = (example_timer_info_t *) args;
/* Retrieve the interrupt status and the counter value
from the timer that reported the interrupt */
uint32_t timer_intr = timer_group_get_intr_status_in_isr(TIMER_GROUP_0);
uint64_t timer_counter_value = timer_group_get_counter_value_in_isr(TIMER_GROUP_0, timer_idx);
uint64_t timer_counter_value = timer_group_get_counter_value_in_isr(info->timer_group, info->timer_idx);
/* 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;
/* Prepare basic event data that will be then sent back to task */
example_timer_event_t evt = {
.info.timer_group = info->timer_group,
.info.timer_idx = info->timer_idx,
.info.auto_reload = info->auto_reload,
.info.alarm_interval = info->alarm_interval,
.timer_counter_value = timer_counter_value
};
/* Clear the interrupt
and update the alarm time for the timer with without reload */
if (timer_intr & TIMER_INTR_T0) {
evt.type = TEST_WITHOUT_RELOAD;
timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0);
timer_counter_value += (uint64_t) (TIMER_INTERVAL0_SEC * TIMER_SCALE);
timer_group_set_alarm_value_in_isr(TIMER_GROUP_0, timer_idx, timer_counter_value);
} else if (timer_intr & TIMER_INTR_T1) {
evt.type = TEST_WITH_RELOAD;
timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_1);
} else {
evt.type = -1; // not supported even type
if (!info->auto_reload) {
timer_counter_value += info->alarm_interval * TIMER_SCALE;
timer_group_set_alarm_value_in_isr(info->timer_group, info->timer_idx, timer_counter_value);
}
/* After the alarm has been triggered
we need enable it again, so it is triggered the next time */
timer_group_enable_alarm_in_isr(TIMER_GROUP_0, timer_idx);
/* Now just send the event data back to the main program task */
xQueueSendFromISR(timer_queue, &evt, NULL);
timer_spinlock_give(TIMER_GROUP_0);
xQueueSendFromISR(s_timer_queue, &evt, &high_task_awoken);
return high_task_awoken == pdTRUE; // return whether we need to yield at the end of ISR
}
/*
* Initialize selected timer of the timer group 0
/**
* @brief Initialize selected timer of timer group
*
* 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
* @param group Timer Group number, index from 0
* @param timer timer ID, index from 0
* @param auto_reload whether auto-reload on alarm event
* @param timer_interval_sec interval of alarm
*/
static void example_tg0_timer_init(int timer_idx,
bool auto_reload, double timer_interval_sec)
static void example_tg_timer_init(int group, int timer, bool auto_reload, int timer_interval_sec)
{
/* Select and initialize basic parameters of the timer */
timer_config_t config = {
@ -111,39 +89,44 @@ static void example_tg0_timer_init(int timer_idx,
.alarm_en = TIMER_ALARM_EN,
.auto_reload = auto_reload,
}; // default clock source is APB
timer_init(TIMER_GROUP_0, timer_idx, &config);
timer_init(group, timer, &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);
timer_set_counter_value(group, timer, 0);
/* 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_set_alarm_value(group, timer, timer_interval_sec * TIMER_SCALE);
timer_enable_intr(group, timer);
timer_start(TIMER_GROUP_0, timer_idx);
example_timer_info_t *timer_info = calloc(1, sizeof(example_timer_info_t));
timer_info->timer_group = group;
timer_info->timer_idx = timer;
timer_info->auto_reload = auto_reload;
timer_info->alarm_interval = timer_interval_sec;
timer_isr_callback_add(group, timer, timer_group_isr_callback, timer_info, 0);
timer_start(group, timer);
}
/*
* The main task of this example program
*/
static void timer_example_evt_task(void *arg)
void app_main(void)
{
s_timer_queue = xQueueCreate(10, sizeof(example_timer_event_t));
example_tg_timer_init(TIMER_GROUP_0, TIMER_0, true, 3);
example_tg_timer_init(TIMER_GROUP_1, TIMER_0, false, 5);
while (1) {
timer_event_t evt;
xQueueReceive(timer_queue, &evt, portMAX_DELAY);
example_timer_event_t evt;
xQueueReceive(s_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");
if (evt.info.auto_reload) {
printf("Timer Group with auto reload\n");
} else {
printf("\n UNKNOWN EVENT TYPE\n");
printf("Timer Group without auto reload\n");
}
printf("Group[%d], timer[%d] alarm event\n", evt.timer_group, evt.timer_idx);
printf("Group[%d], timer[%d] alarm event\n", evt.info.timer_group, evt.info.timer_idx);
/* Print the timer values passed by event */
printf("------- EVENT TIME --------\n");
@ -152,18 +135,7 @@ static void timer_example_evt_task(void *arg)
/* 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);
timer_get_counter_value(evt.info.timer_group, evt.info.timer_idx, &task_counter_value);
print_timer_counter(task_counter_value);
}
}
/*
* In this example, we will test hardware timer0 and timer1 of timer group0.
*/
void app_main(void)
{
timer_queue = xQueueCreate(10, sizeof(timer_event_t));
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);
}