1.Timer API 介绍:
ESP32内置4个64bit的通用定时器:每个定时器包含一个16bit预分频器和一个64bit可自动重新加载向上/向下计数器。
定时器分为两组,一组两个:
Timer_Group0:Timer_0, Timer_1;
Timer_Group1:Timer_0, Timer_1;
Timer的workflow如下:
·Timer Initialization: 初始化Timer参数;
·Timer Control: 读取Timer计数值;开始、暂停Timer;
·Alarms: 报警功能;
·Interrupts: 中断功能;
1.1 Timer Initialization:
组间区分结构体: timer_group_t
typedef enum {
TIMER_GROUP_0 = 0, /*!<Hw timer group 0*/
TIMER_GROUP_1 = 1, /*!<Hw timer group 1*/
TIMER_GROUP_MAX,
} timer_group_t;
组内区分结构体: timer_idx_t
typedef enum {
TIMER_0 = 0, /*!<Select timer0 of GROUPx*/
TIMER_1 = 1, /*!<Select timer1 of GROUPx*/
TIMER_MAX,
} timer_idx_t;
初始化Timer之前,我们需要对结构体 timer_config_t
内参数进行初始化:
typedef struct {
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 */
uint32_t divider; /*!< Counter clock divider. The divider's range is from from 2 to 65536. */
} timer_config_t;
· divider: 预分频值,APB_CLK(80MHz)/divider为Timer计数的基石,即一个这样的时钟对应计数器增加/减少1。divider可以对APB时钟进行2~65536的分频。特别说明,divider为1或2时,时钟分频为2;divider为0时,时钟分频为65536.
· counter_dir: 计数器方向。取自 timer_count_dir_t
:
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;
· counter_en: 计数器使能。假如使能的话,再调用 timer_init()
函数之后计数器立即开始计数。其值取自
timer_start_t
:
typedef enum {
TIMER_PAUSE = 0, /*!<Pause timer counter*/
TIMER_START = 1, /*!<Start timer counter*/
} timer_start_t;
初始化结构体时,通常设置为 *.counter_en = TIMER_PAUSE 。
· alarm_en: 报警使能。其值取自 timer_alarm_t
:
typedef enum {
TIMER_ALARM_DIS = 0, /*!< Disable timer alarm*/
TIMER_ALARM_EN = 1, /*!< Enable timer alarm*/
TIMER_ALARM_MAX
} timer_alarm_t;
· auto_reload: 自动重载。计数报警后,是否自动重载如指定的值。
bool型变量:
0 --- without auto_reload
1 --- with auto_reload
· intr_type: 中断类型。计数报警后是否产生中断,其值取自 timer_intr_mode_t
:
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;
调用 timer_get_config()
可获取当前的Timer配置参数。
1.2 Timer Control:
获取计数器开始至当前的时间间隔:
timer_get_counter_value(): 返回时间单位 微妙;
timer_get_counter_time_sec(): 返回时间单位 秒;
设定特定的计数开始时间:
timer_set_counter_value()
暂停&开始:
timer_pause(): 可以在任何时间暂停计数器;
timer_start()
两种改变 Timer Operation 的方法:
1:调用 timer_init() 重新初始化;
2:调用专有函数改变结构体内的配置参数:
· divider value:timer_set_divider()
· Mode :timer_set_counter_mode()
· Auto Reload:timer_set_auto_reload()
1.3 Timer Alarms:
timer_set_alarm_value()
设定计数器的报警值;
timer_set_alarm()
使能报警;
报警之后,根据配置两个action可能发生:
1. 如果配置了中断使能,则触发中断;
2. 如果auto_reload使能,则自动从预设值加载。报警使能后,报警使能位自动清零。如果报警时自动加载未被使能,时基计数器会在报警后继续向上计数或向下计数。
1.4 Timer Interrupts:
中断相关函数:
timer_isr_register(): 注册中断;
timer_group_intr_enable(): enable interrupts for timer group;
timer_group_intr_disable(): disable interrupts for timer group;
timer_enable_intr(): enable interrupts for a specific timer;
timer_disable_intr(): disable interrupts for a specific timer;
更多资料参考: Espressif Timer API
2.Timer Periodic and Once:
2.1 设计目的:
调用 esp_timer_start_periodic()
和 esp_timer_start_once()
函数:
esp_timer_start_periodic()
: 周期(1s)重复运行定时器。计数20s后,闪烁LED灯,5s后重启系统。
esp_timer_start_periodic()
: 单次运行定时器。定时10s之后停止,并删除定时器。
2.2 测试代码:
#include <stdio.h>
#include "esp_types.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "soc/timer_group_struct.h"
#include "driver/periph_ctrl.h"
#include "driver/timer.h"
#include "esp_timer.h"
#define LED_R_GPIO 27
//define two Timer handles
esp_timer_handle_t peri_timer_handle = 0;
esp_timer_handle_t once_timer_handle = 0;
//Periodic Timer callback function;
void peri_timer_cb(void *arg)
{
//int8_t index = 0;
//get time interval after Timer starts;
int64_t tick = esp_timer_get_time();
printf("%s \t, time interval after Timer start is: %lld \r\n", __func__, tick);
if(tick > (20 * 1000 * 1000)) //30s
{
//Timer stop and delete;
esp_timer_stop(peri_timer_handle);
esp_err_t err = esp_timer_delete(peri_timer_handle);
printf("Periodic Timer stop and delete: \t %s", err == ESP_OK ? "ok!\r\n" : "failed!\r\n");
printf("LED turn on... \r\n");
for(int8_t index=5; index > 0; index--){
printf("system will reboot after [%d]s... \r\n", index);
for(int8_t index_t = 0; index_t<5; index_t++){
gpio_set_level(LED_R_GPIO, (index_t%2));
vTaskDelay(200 / portTICK_PERIOD_MS);
}
}
esp_restart();
}
}
//Once Timer callback function;
void once_timer_cb(void *arg)
{
//get time interval after Timer starts;
int64_t tick = esp_timer_get_time();
printf("%s \t, time interval after Timer start is: %lld \r\n", __func__, tick);
//Timer stop and delete;
esp_timer_stop(once_timer_handle);
esp_err_t err = esp_timer_delete(once_timer_handle);
printf("***Once Timer stop and delete : %s *** \r\n", err == ESP_OK ? "ok!" : "failed!");
}
//main function
void app_main() {
//initialization GPIO which will control LED on/off;
gpio_pad_select_gpio(LED_R_GPIO);
gpio_set_direction(LED_R_GPIO, GPIO_MODE_OUTPUT);
gpio_set_level(LED_R_GPIO, 0);
//initialization periodic Timer structure
esp_timer_create_args_t peri_timer =
{
.callback = &peri_timer_cb,
.arg = NULL,
.name = "peri_timer"
};
//initialization once Timer structure
esp_timer_create_args_t once_timer =
{
.callback = &once_timer_cb,
.arg = NULL,
.name = "once_timer"
};
printf("\r\n\n");
//periodic Timer creation and start;
esp_err_t err = esp_timer_create(&peri_timer, &peri_timer_handle);
err = esp_timer_start_periodic(peri_timer_handle, 1000 * 1000); //1s callback
printf("Periodic Timer create and start: %s", err == ESP_OK ? "ok!\r\n" : "failed!\r\n");
//once Timer creation and start;
err = esp_timer_create(&once_timer, &once_timer_handle);
err = esp_timer_start_once(once_timer_handle, 10 * 1000 * 1000); //10s callback
printf("Once Timer create and start: %s", err == ESP_OK ? "ok!\r\n" : "failed!\r\n");
}
2.3 测试结果:
3.Timer Reload:
3.1 设计目的:
调用定时器 TIMER_Group0 的两个定时器:
定时器0不使能报警,从0开始向上计时至5.78s时,不执行Reload,继续向上计数。
定时器1使能报警,从0开始向上计时至5.78s时,执行Reload,重加载预设值(0)后继续计数。
再次计时至5.78s时,由于auto_reload在第一次报警后清零,不执行Reload操作,计数器继续向上计数。
3.2 测试代码:
#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_DIVIDER 16 // Hardware timer clock divider, 80M/16 =5MHz,
// 这里的 TIMER_BASE,比如 80MHz/16=5MHz,意思就是5M个计数为1s;
#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
// timer 初始化
static void example_tg0_timer_init(int timer_idx, bool auto_reload, double timer_interval_sec)
{
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_set_counter_value(TIMER_GROUP_0, timer_idx, 0x00000000ULL);
timer_set_alarm_value(TIMER_GROUP_0, timer_idx, timer_interval_sec * TIMER_SCALE);
timer_start(TIMER_GROUP_0, timer_idx);
}
static void timer_example_evt_task(void *arg)
{
while (1) {
uint64_t task_counter_value;
timer_get_counter_value(TIMER_GROUP_0, TIMER_0, &task_counter_value);
printf("\n******\n");
printf("Timer_group0_timer0 Counter: 0x%08x%08x\n", (uint32_t) (task_counter_value >> 32), (uint32_t) (task_counter_value));
printf("Timer_group0_timer0 Time : %.2f s\n", (double) task_counter_value / TIMER_SCALE);
timer_get_counter_value(TIMER_GROUP_0, TIMER_1, &task_counter_value);
printf("\nTimer_group0_timer1 Counter: 0x%08x%08x\n", (uint32_t) (task_counter_value >> 32), (uint32_t) (task_counter_value));
printf("Timer_group0_timer1 Time : %.2f s\n", (double) task_counter_value / TIMER_SCALE);
printf("******\n\n");
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void app_main()
{
example_tg0_timer_init(TIMER_0, TEST_WITHOUT_RELOAD, TIMER_INTERVAL1_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);
}
3.3 测试结果:
如上图高亮所示:
timer0未使能报警,计数至5.78之后继续向上计数;
timer1使能报警,计数至5.78之后,重加载预设值0,后继续向上计数;
当timer1再次计数至5.78时,由于报警使能位在第一次使能之后已经被清零,计数器继续向上计数。