0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

ESP32-C3硬件定时器简介及使用

CHANBAEK 来源:编程圈子 作者:编程圈子 2023-03-14 11:43 次阅读

一、 简介

ESP32-C3硬件定时器分辨率高,一般可用于:

  • 获取时间戳
  • 周期性任务

本文主要内容参考 官网API文档

本文主要代码参考 来自这里

ESP32-C3 有2个定时器组,每个组有2个定时器,共有4个定时器。
每组定时器包括一个普通定时器和一个看门狗定时器。

timer_types.h 里可以看到结构体的定义:

typedef enum {
    TIMER_GROUP_0 = 0, /*!

每个通用硬件定时器都是基于16位预分频器和64位自动重载功能的向上/向下计数的64位通用定时器。

二、使用步骤

  1. 资源分配
  2. 设置和获取计数器值
  3. 设置报警动作
  4. 注册事件回调函数
  5. 使能或禁用定时器
  6. 启动和停止定时器

其它的操作有:

三、操作函数

1. 基本操作

(1)定时器实例gptimer_handle_t

(2) 定时器配置结构体gptimer_config_t

使用结构体 gptimer_config_t 来创建定时器实例, gptimer_config_t 结构体的属性值:

  • gptimer_config_t::clk_src 选择定时器的时钟源,枚举值: gptimer_clock_source_t
  • gptimer_config_t::direction 设置定时器的计数方向,枚举值: gptimer_count_direction_t
  • gptimer_config_t::resolution_hz 设置内部计数器的分辨率,计数器滴答一次用时秒数为:1 r e s o l u t i o n _ h z \\frac {1} {resolution_hz} resolution _hz1
  • gptimer_config_t::intr_shared 设置是否将定时器中断源标记为共享源。
    示例:
// 配置定时器,默认时钟源:APB
    timer_config_t config = {
            .divider = TIMER_DIVIDER,       //定时器预分频;esp32-c3的APB_CLK=80MHz,80MHz/TIMER_DIVIDER(16)=5MHz
            .counter_dir = TIMER_COUNT_UP,  //计数器向上计数,从0开始
            .counter_en = TIMER_PAUSE,      //计数器暂时中止
            .alarm_en = TIMER_ALARM_EN,     //定时器警报使能
            .auto_reload = auto_reload,     //1:定时器硬件在警报事件后自动重装载;0:则相反
    };

(3) 定时器初始化timer_init()

示例:

/*
    * 函数功能:初始化和配置定时器
    * group_num:定时器分组值, 从0开始
    * timer_num:定时器序号,从0开始 【一组定时器包含:普通定时器,看门狗定时器】
    * *config:  定时器配置结构体
    */
    timer_init(group, timer, &config);

(3) 设置定时器初值timer_set_counter_value()

示例:

// 设置定时器值,如果设置了auto_reload,则报警后会也会重置为此值
    timer_set_counter_value(group, timer, 0);

(4)设置报警值timer_set_alarm_value()

示例:

// 设置报警值、使能中断ISR
    timer_set_alarm_value(group, timer, timer_interval_sec * TIMER_SCALE);

(5)使用定时器timer_enable_intr()

示例:

// 使能定时器组(group)、定时器x(timer)中断
    timer_enable_intr(group, timer);

(6) 定时器添加ISR中断回调timer_isr_callback_add()

示例:

// 定时器添加ISR中断回调函数
    timer_info_t *timer_info = calloc(1, sizeof(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);//???

(7)启动定时器timer_start()

示例:

timer_start(group, timer);

(8) 获取定时器值timer_get_counter_value()

示例:

uint64_t task_counter_value;
        // 获取定时器组,中定时器,的计数器的值;
        timer_get_counter_value(evt.info.timer_group, evt.info.timer_idx, &task_counter_value);

2. 其它操作

(1) 创建新定时器gptimer_new_timer()

示例: 创建分辨率为1 MHz 的通用定时器:

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_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));

(2) 设置和获取计数值gptimer_get_raw_count()

gptimer_get_raw_count 用来获取计数值。

  • 创建计数器后,内部计数器将默认重置为0
  • 计数值重置时,将会从新值计数。
  • 计数值达最大值后将重置,最大值与SOC宏: SOC_TIMER_GROUP_COUNTER_BIT_WIDTH 有关。

3. 使能和禁用定时器

(1)使能gptimer_enable()

此函数功能:

  • 将定时器驱动的状态从init切换为enable
  • 如果gptimer_register_event_callbacks() 已经延迟安装中断服务,此函数将使能中断服务
  • 如果选择了特定的时钟源,此函数将获取适当的电源管理锁。

(2)禁用gptimer_disable

4. 启动和停止定时器

(1)启动gptimer_start()

(2)停止gptimer_stop()

四、示例程序

#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "driver/timer.h"

#define TIMER_DIVIDER         (16)  //  Hardware timer clock divider
// 计数值转为秒
#define TIMER_SCALE           (TIMER_BASE_CLK / TIMER_DIVIDER)

typedef struct {
    // 定时器组号
    int timer_group;
    // 定时器序号
    int timer_idx;
    // 报警时间
    int alarm_interval;
    // 是否自动重装
    bool auto_reload;
} timer_info_t;

// 定义一个示例结构体
typedef struct {
    // 定时器的参数
    timer_info_t info;
    // 计数器值
    uint64_t timer_counter_value;
} timer_event_t;

// 主程序接收报警数据的队列
static xQueueHandle s_timer_queue;

/*
* A simple helper function to print the raw timer counter value
* and the counter value converted to seconds
*/
static void inline print_timer_counter(uint64_t counter_value)
{
printf("Counter: 0x%08x%08x \\t", (uint32_t) (counter_value >> 32),
(uint32_t) (counter_value));
printf("Time   : %.8f s\\r\\n", (double) counter_value / TIMER_SCALE);
}

/**
 * 报警回调函数
 * @param args timer_info_t结构体
 * @return
 */
static bool IRAM_ATTR timer_group_isr_callback(void *args)
{
    // 计算回调函数返回值
    BaseType_t high_task_awoken = pdFALSE;
    timer_info_t *info = (timer_info_t *) args;

    // 在ISR中获取计数器值
    uint64_t timer_counter_value =
            timer_group_get_counter_value_in_isr(info->timer_group, info->timer_idx);

    // 将定时器中断响应的定时器赋予结构体变量evt
    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
    };

    // 定时器组中的定时器,是否有自动重载
    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);
    }

    // 以队列形式把数据发送到主函数,消息存储在结构体evt中
    // high_task_awoken 用于接收返回值
    xQueueSendFromISR(s_timer_queue, &evt, &high_task_awoken);

    return high_task_awoken == pdTRUE;
}

/**
* @brief 初始化定时器
*
* @param group 定时器组序号,从0开始
* @param timer timer ID, 从0开始
* @param auto_reload 是否自动重载
* @param timer_interval_sec 间隔
*/
static void timer_config_start(int group, int timer, bool auto_reload, int timer_interval_sec)
{
    // 配置定时器,默认时钟源:APB
    timer_config_t config = {
            .divider = TIMER_DIVIDER,       //定时器预分频;esp32-c3的APB_CLK=80MHz,80MHz/TIMER_DIVIDER(16)=5MHz
            .counter_dir = TIMER_COUNT_UP,  //计数器向上计数,从0开始
            .counter_en = TIMER_PAUSE,      //计数器暂时中止
            .alarm_en = TIMER_ALARM_EN,     //定时器警报使能
            .auto_reload = auto_reload,     //1:定时器硬件在警报事件后自动重装载;0:则相反
    };
    /*
    * 函数功能:初始化和配置定时器
    * group_num:定时器分组值, 从0开始
    * timer_num:定时器序号,从0开始 【一组定时器包含:普通定时器,看门狗定时器】
    * *config:  定时器配置结构体
    */
    timer_init(group, timer, &config);

    // 设置定时器值,如果设置了auto_reload,则报警后会也会重置为此值
    timer_set_counter_value(group, timer, 0);

    // 设置报警值、使能中断ISR
    timer_set_alarm_value(group, timer, timer_interval_sec * TIMER_SCALE);

    // 使能定时器组(group)、定时器x(timer)中断
    timer_enable_intr(group, timer);

    // 定时器添加ISR中断回调函数
    timer_info_t *timer_info = calloc(1, sizeof(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);
}

void app_main(void)
{
    // xQueueCreate是freeRTOS宏,用于创建队列实例
    s_timer_queue = xQueueCreate(10, sizeof(timer_event_t));

    // 配置定时器组1,中的定时器0,无自动重装,间隔是5s
    timer_config_start(TIMER_GROUP_1, TIMER_0, false, 10);

    while (1) {
        timer_event_t evt;
        // 等待队列事件,时间是永远等待
        xQueueReceive(s_timer_queue, &evt, portMAX_DELAY);

        // 定时器组自动重装
        if (evt.info.auto_reload) {
            printf("====== Timer Group with auto reload ======\\n");
        } else {
            printf("====== Timer Group without auto reload ======\\n");
        }
        printf("------ Group[%d], timer[%d] alarm event ------\\n", evt.info.timer_group, evt.info.timer_idx);

        // 打印事件上报的计数器值
        printf("[ evt.timer_counter_value ] ");
        print_timer_counter(evt.timer_counter_value);

        // 直接从定时器获取计数器值
        printf("[timer.timer_counter_value] ");
        uint64_t task_counter_value;
        // 获取定时器组,中定时器,的计数器的值;
        timer_get_counter_value(evt.info.timer_group, evt.info.timer_idx, &task_counter_value);
        print_timer_counter(task_counter_value);
    }
}

运行结果:

====== Timer Group without auto reload ======
------ Group[1], timer[0] alarm event ------
[ evt.timer_counter_value ] Counter: 0x0000000002faf089 	Time   : 10.00000180 s
[timer.timer_counter_value] Counter: 0x0000000002fb43ab 	Time   : 10.00425820 s
====== Timer Group without auto reload ======
------ Group[1], timer[0] alarm event ------
[ evt.timer_counter_value ] Counter: 0x0000000005f5e112 	Time   : 20.00000360 s
[timer.timer_counter_value] Counter: 0x0000000005f632f5 	Time   : 20.00419620 s

如果 timer_config_start函数的auto_reload设置为true,则输出示例:

可以看到达到报警值后,计数器值会还原。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 硬件
    +关注

    关注

    11

    文章

    3236

    浏览量

    66080
  • 分频器
    +关注

    关注

    43

    文章

    447

    浏览量

    49785
  • 定时器
    +关注

    关注

    23

    文章

    3236

    浏览量

    114412
  • 函数
    +关注

    关注

    3

    文章

    4299

    浏览量

    62374
  • ESP32-C3
    +关注

    关注

    0

    文章

    9

    浏览量

    361
收藏 人收藏

    评论

    相关推荐

    浅谈ESP32-C3ESP32-S3芯片

    ESP32-C3ESP32-S3的发布,不仅为IOT行业提供了高性价比、稳定、好用、安全的通讯层,支持AI加速,同时在显示层应用也将占有一席之地。
    发表于 04-28 10:37 1.8w次阅读

    ESP32-C3芯片的性价比有多高

    今天小明为大家捋一捋ESP32-C3性价比到底有多高呢?很多在说乐鑫急着发布ESP32-C3ESP32-S3,是在布一个大局,到底在布啥局呢?
    发表于 04-28 09:52 5168次阅读
    <b class='flag-5'>ESP32-C3</b>芯片的性价比有多高

    ESP32-C3芯片特性介绍

    它的性价比到底有多高呢?ESP32-C3首先它是一款安全稳定、低功耗、低成本的物联网芯片,搭载RISC-V32位单核处理,支持2.4GHzWi-Fi和BluetoothLE5.0。为物联网产品提供
    发表于 04-28 09:44 1.2w次阅读

    浅谈ESP32-C3芯片的应用领域

    之前大家期待已久的ESP32-C3,将在4月份量产啦!届时大家可以看到它的真面目。那么介绍了那么多,它的应用有哪些?
    发表于 04-06 14:31 3105次阅读

    ESP32-C3芯片到底有哪些特性

    五一假期,让大家身处快乐星球一般。现在假期结束了,快乐难道就消失了吗?答案是否定的。快乐其实很简单,本月乐鑫esp32驱动86面板,esp32-C3 +2.4寸彩屏,esp32- C3
    发表于 06-01 18:12 2980次阅读

    ESP32-C3模组芯片的功能特性

    重磅消息来喽!本月乐鑫esp32驱动86面板,esp32-C3 +2.4寸彩屏,esp32- C3+1.3寸圆屏 QSPI即将出品!一颗mcu搞定WIFI蓝牙和驱动彩屏,而且
    发表于 06-03 11:22 2042次阅读

    基于nanoESP32-C3开发板用ESP32-c3下试跑Zephyr

    上周MuseLab的吴同学寄来一片nanoESP32-C3–一块带有ESPLink(base DAPlink)的ESP32-C3开发板。 正好最近支持esp32-c3的pr已经merge进入
    的头像 发表于 08-09 09:10 9283次阅读

    ESP32ESP-IDF 教学(三)——通用硬件定时器(Timer)

    ESP32ESP-IDF 学习笔记(三)【通用硬件定时器(Timer)】文章目录ESP32E
    发表于 11-26 11:36 37次下载
    <b class='flag-5'>ESP32</b> 之 <b class='flag-5'>ESP</b>-IDF 教学(三)——通用<b class='flag-5'>硬件</b><b class='flag-5'>定时器</b>(Timer)

    【DFRobot Beetle ESP32-C3开发板试用体验】车载导航天气挂件?

    1602A显示屏 合宙Air 551G导航模块 DFRobot Beetle ESP32-C3开发板 杜邦线若干 连接方式 跟之前的连接一样。 ESP32-C3通过自制一分二的线分别给LCD屏幕和Air
    的头像 发表于 11-28 11:01 1270次阅读
    【DFRobot Beetle <b class='flag-5'>ESP32-C3</b>开发板试用体验】车载导航天气挂件?

    【DFRobot Beetle ESP32-C3开发板试用体验】与GNSS模块串口通信

    Beetle ESP32-C3 合宙Air 551G LCD 1602A显示屏 杜邦线若干 连线 ESP32-C3通过自制一分二的线分别给LCD屏幕和Air 551G供电。 ESP32-C3的Pin 8
    的头像 发表于 11-28 11:06 969次阅读
    【DFRobot Beetle <b class='flag-5'>ESP32-C3</b>开发板试用体验】与GNSS模块串口通信

    【DFRobot Beetle ESP32-C3开发板试用体验】开箱和1602A显示

    提供的 DFRobot Beetle ESP32-C3 开发板 试用机会。 拿到板子后,感觉的确是很小,比之前买的一个esp32 C3板子要小上一半。盒子里包含ESP32-C3开发板、
    的头像 发表于 11-28 14:06 1732次阅读
    【DFRobot Beetle <b class='flag-5'>ESP32-C3</b>开发板试用体验】开箱和1602A显示

    DFRobot Beetle ESP32-C3 DIY运动按钮 让你随时随地动起来

    DFRobot Beetle ESP32-C3 DIY运动按钮是一款基于ESP32-C3 RISC-V 32 位单核处理制作的小型主控。它集成了锂电池充电管理功能,可以通过USB-
    发表于 12-15 15:16 395次阅读
    DFRobot Beetle <b class='flag-5'>ESP32-C3</b> DIY运动按钮 让你随时随地动起来

    启明云端分享|盘点Esp32-C3Esp8266 的区别

    ESP8266&ESP32-C3硬件区别从上表可以看出ESP32-C3ESP8266在硬件上的
    的头像 发表于 10-12 18:31 4948次阅读
    启明云端分享|盘点<b class='flag-5'>Esp32-C3</b>与<b class='flag-5'>Esp</b>8266 的区别

    启明云端测试分享|ESP32-C3连接阿里云

    启明云端手把手教程ESP32-C3连接阿里云
    的头像 发表于 09-13 15:10 2648次阅读
    启明云端测试分享|<b class='flag-5'>ESP32-C3</b>连接阿里云

    ESP32-C3在智能充电桩上的应用,性价比爆棚!

    ESP32-C3在智能充电桩上的应用,性价比爆棚!
    的头像 发表于 01-20 08:03 757次阅读
    <b class='flag-5'>ESP32-C3</b>在智能充电桩上的应用,性价比爆棚!