ESP32开发教程(五)——定时器中断详解
在上最开始所提供的技术文档中,在通用定时器API中,它说给的库函数为"driver/gptimer.h",但是我在学习其他博主的关于ESP32的定时器时,发现他们所用的库函数是"driver/timer.h"。感到很迷茫。后来经过查阅资料发现,"driver/gptimer.h"是更新了的"driver/tmer.h","driver/timer.h"也是可以用的,本文将利用"driver/gptimer.h"这个库进行定时器配置。
一.ESP32的定时器资源
ESP32有两组通用定时器,每组又有两个定时器,具体的博主给出技术文档连接,自己去瞅吧!https://www.espressif.com.cn/sites/default/files/documentation/esp32_datasheet_cn.pdf
二.相关函数
(1)创建定时器
//参数:*config:用于定时器配置的结构体
// *ret_timer:定时器句柄
//作用:创建定时器,新创建的定时器时初始化状态
esp_err_t gptimer_new_timer(const gptimer_config_t *config, gptimer_handle_t *ret_timer)
//定时器配置结构体
typedef struct {
gptimer_clock_source_t clk_src; //中断源
gptimer_count_direction_t direction; //计数方式
uint32_t resolution_hz; //定时时间,单位
struct {
uint32_t intr_shared: 1;
} flags;
} gptimer_config_t;
(2)设置定时器回调函数
//参数:timer:定时器句柄
// *cbs:回调函数结构体
// *user_data:传入参数
//作用:注册中断回调函数
esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer_event_callbacks_t *cbs, void *user_data)
//回调函数结构体
typedef struct {
gptimer_alarm_cb_t on_alarm; //也就是报警回调函数,直接传入函数名
} gptimer_event_callbacks_t;
//回调函数形式
typedef bool (*gptimer_alarm_cb_t) (gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx);
(3)使能定时器中断
esp_err_t gptimer_enable(gptimer_handle_t timer)
(4)设置定时器告警事件动作。
//参数:timer:定时器句柄
// config:警告配置结构体
esp_err_t gptimer_set_alarm_action(gptimer_handle_t timer, const gptimer_alarm_config_t *config)
(5)开始计数
esp_err_t gptimer_start(gptimer_handle_t timer)
三.代码
time.c
#include"time.h"
#include "driver/gptimer.h"
#include "driver/uart.h"
#include "myuart.h"
#include "math.h"
#include "esp_log.h"
#include <string.h>
#include "driver/gpio.h"
static const char *TAG = "TEST";
void IRAM_ATTR timer_on_alarm_cb (gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx){
static int LED_leve = 1;
gpio_set_level(RXD,LED_leve);
gpio_set_level(TXD,LED_leve);
if(LED_leve)
LED_leve = 0;
else
LED_leve = 1;
}
void Time_int(void){
gptimer_handle_t gptimer = NULL; //通用定时器句柄
gptimer_config_t timer_config = { //初始化参数设置
.clk_src = GPTIMER_CLK_SRC_DEFAULT, //选择时钟源
.direction = GPTIMER_COUNT_UP, //向上计数
.resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us 设置定时时间
};
ESP_LOGI(TAG, "Start timer, stop it at alarm event");
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer)); //创建一个通用定时器,返回任务句柄
gptimer_event_callbacks_t cbs = { //中断回调函数(alrm中断)
.on_alarm = timer_on_alarm_cb,
};
ESP_LOGI(TAG, "Enable timer");
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL)); //第一次调用这个函数需要在调用gptimer_enable之前
ESP_ERROR_CHECK(gptimer_enable(gptimer)); //使能定时器中断
ESP_LOGI(TAG, "Start timer, auto-reload at alarm event");
gptimer_alarm_config_t alarm_config = {
.reload_count = 0,
.alarm_count = 5000000, // period = 5s
.flags.auto_reload_on_alarm = true,
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
ESP_ERROR_CHECK(gptimer_start(gptimer));
}
time.h
#ifndef _TIME_H_
#define _TIME_H_
#define RXD GPIO_NUM_19
#define TXD GPIO_NUM_23
#define RXD_OUTPUT_PIN_SEL (1ULL << RXD) //配置GPIO_OUT寄存器
#define TXD_OUTPUT_PIN_SEL (1ULL << TXD)
void Time_int(void);
#endif
main.c
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gptimer.h"
#include "esp_log.h"
#include "time.h"
#include "driver/gpio.h"
void IO_int(void){
gpio_config_t IO_Config;
IO_Config.pin_bit_mask = RXD_OUTPUT_PIN_SEL;
IO_Config.mode = GPIO_MODE_DEF_OUTPUT; /*!< GPIO mode: set input/output mode */
IO_Config.pull_up_en = 1; /*!< GPIO pull-up */
IO_Config.pull_down_en = 0; /*!< GPIO pull-down */
IO_Config.intr_type = GPIO_INTR_DISABLE;
gpio_config(&IO_Config);
IO_Config.pin_bit_mask = TXD_OUTPUT_PIN_SEL;
gpio_config(&IO_Config);
gpio_set_level(RXD,0);
gpio_set_level(TXD,0);
}
void app_main(void)
{
IO_int();
Time_int();
while (1)
{
vTaskDelay(1000/portTICK_PERIOD_MS); //延迟1s
}
}
四.现象
实现两个引脚电平每5s翻转一次。
需要注意的是,我在实现在定时回调函数中打印log时,并不能成功实现(还不知道原因),但是电平是成功翻转了的。在使用esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer_event_callbacks_t *cbs, void *user_data)函数是需要注意,它必须在使能定时中断前。