ESP32开发(五)——定时器中断

本文介绍了ESP32的通用定时器资源,包括两个定时器组,每个组有两个定时器。使用driver/gptimer.h库进行定时器配置,详细讲解了创建定时器、设置回调函数、使能中断、设置告警事件等步骤。在代码示例中,展示了如何实现两个引脚电平每5秒翻转一次。注意中断回调函数需在使能定时器中断前注册。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        在上最开始所提供的技术文档中,在通用定时器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)函数是需要注意,它必须在使能定时中断前。

### ESP32 定时器中断使用教程 #### 初始化定时器 为了初始化并设置一个定时器,在ESP32中可以利用`timerBegin()`函数来创建一个新的定时器实例。此函数接受三个参数:定时器编号、预分频系数以及自动重载标志[^3]。 ```cpp hw_timer_t * timer = timerBegin(0, 80, true); ``` 这段代码表示选择了第一个定时器(即编号为0),设置了预分频值为80,并启用了自动重载模式。 #### 设置定时时间间隔 通过调用`timerAttachInterrupt()`关联特定的回调函数到该定时器,当定时器触发时会执行这个指定的处理程序;再者,可以通过`timerAlarmWrite()`设定具体的报警周期长度: ```cpp void IRAM_ATTR onTimer() { // 处理逻辑... } // 关联中断服务例程至定时器 timerAttachInterrupt(timer, &onTimer); // 设定每1秒触发一次中断 (单位微秒) timerAlarmWrite(timer, 1000000, true); ``` 这里定义了一个名为`onTimer`的ISR(中断服务子程序)。注意关键字`IRAM_ATTR`用于指示编译器将该函数放置于内部RAM中以便快速访问。最后一步是启动定时器以使其生效: ```cpp timerAlarmEnable(timer); ``` 以上就是基本的定时器配置流程。 #### 常见错误及其解决方案 ##### 参数过多传递给 `timerBegin` 如果遇到如下报错信息:"too many arguments to function 'hw_timer_t* timerBegin(uint32_t)'" ,这表明传入了多余的参数给`timerBegin()` 函数。应确保只提供必要的三个参数——定时器ID、预分频因子和是否启用自动装载选项[^4]。 ##### 队列阻塞影响定时器工作 有时可能会碰到由于队列满而造成定时器未能按时响应的情况。为了避免此类问题的发生,建议适当调整队列大小或将高优先级的任务安排在更早的时间点完成,从而减少延迟风险[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Crystal(mercy)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值