ESP-C3入门5. 使用通用计时器

news2024/11/14 15:23:56

ESP-C3入门5. 使用通用计时器

  • 一、 简介
  • 二、使用步骤
  • 三、操作函数
    • 1. 基本操作
      • (1)定时器实例 `gptimer_handle_t `
      • (2) 定时器配置结构体 `gptimer_config_t`
      • (3) 定时器初始化 `timer_init()`
      • (3) 设置定时器初值 `timer_set_counter_value()`
      • (4)设置报警值 `timer_set_alarm_value()`
      • (5)使用定时器 `timer_enable_intr()`
      • (6) 定时器添加ISR中断回调 `timer_isr_callback_add()`
      • (7)启动定时器`timer_start()`
      • (8) 获取定时器值 `timer_get_counter_value()`
    • 2. 其它操作
      • (1) 创建新定时器 `gptimer_new_timer()`
      • (2) 设置和获取计数值 `gptimer_get_raw_count()`
    • 3. 使能和禁用定时器
      • (1)使能 `gptimer_enable()`
      • (2)禁用 `gptimer_disable`
    • 4. 启动和停止定时器
      • (1)启动 `gptimer_start()`
      • (2)停止 `gptimer_stop()`
  • 四、示例程序

在这里插入图片描述

一、 简介

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

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

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

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

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

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

typedef enum {
    TIMER_GROUP_0 = 0, /*!<Hw timer group 0*/
#if SOC_TIMER_GROUPS > 1
    TIMER_GROUP_1 = 1, /*!<Hw timer group 1*/
#endif
    TIMER_GROUP_MAX,
} timer_group_t;

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

二、使用步骤

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

其它的操作有:

  • 电源管理
  • IRAM安全
  • 线程安全
  • Kconfig 设置选项

三、操作函数

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,则输出示例:
在这里插入图片描述
可以看到达到报警值后,计数器值会还原。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/182362.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探索 Vue.js 中引用的力量:访问和操作 DOM 元素”

0.简介 Vue.js 是一个用于构建用户界面的渐进式 JavaScript 框架。它由 Evan You 于 2014 年创建&#xff0c;此后作为构建 Web 应用程序的工具越来越受欢迎。 Vue 的核心特性包括&#xff1a; 反应式数据绑定&#xff1a;Vue 使用反应式系统来跟踪对数据模型的更改并自动更新…

22年部署之docker学习

以下记录的是&#xff0c;我在学习中的一些学习笔记&#xff0c;这篇笔记是自己学习的学习大杂烩&#xff0c;主要用于记录&#xff0c;方便查找https://hub.docker.com/ 镜像中心Docker 常用命令&#xff1a; docker 运行相关运行&#xff1a; service docker start停止&#…

【三年面试五年模拟】算法工程师的独孤九剑秘籍(前十二式汇总篇)V1版

写在前面 【三年面试五年模拟】栏目专注于分享AI行业中实习/校招/社招维度的必备面积知识点与面试方法&#xff0c;并向着更实战&#xff0c;更真实&#xff0c;更从容的方向不断优化迭代。也欢迎大家提出宝贵的意见或优化ideas&#xff0c;一起交流学习&#x1f4aa; 大家好&a…

ABB机器人系统输入输出信号System Input和Output详解(一)

ABB机器人系统输入输出信号System Input和Output详解 System Input类型: 输入I/O信号可指定具体的系统输入项,比如Start或Motors on。该输入项会在不使用FlexPendant示教器或其它硬件装置的情况下触发一项交由系统处理的系统行动。 可以用一个PLC来触发相应的系统输入项。 注…

Node.js教程笔记(三)express

学习目标 能够使用express.static()快速托管静态资源 能够使用express路由精简项目结构 能够使用常见的express中间件 能够使用express创建API接口 能够在express中启用cors跨域资源共享 目录 初识Express Express路由 Express中间件 使用Express写接口 1、初识Expres…

【通信原理(含matlab程序)】实验三 数字基带信号及其频谱特性

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; 本人持续分享更多关于电子通信专业内容以及嵌入式和单片机的知识&#xff0c;如果大家喜欢&#xff0c;别忘点个赞加个关注哦&#xff0c;让我们一起共同进步~ &#x…

Android OpenCV(二)主体识别 位置检测

前言 工作中遇到需要通过OpenCV找到图片主体体积占图片百分比的比例&#xff0c;这里做一个问题解决思路的记录。该方面新手小白&#xff0c;有不对的地方可以评论指出哈 。 重要API Sobel算法 Sobel 计算参考文章 索贝尔算子是计算机视觉领域的一种重要处理方法。 主要用于…

内网传输——解决物联网信息安全和隐私保护问题

与普通电脑系统不同&#xff0c;物联网建立在嵌入式系统的基础之上&#xff0c;其通信协议因设备和应用程序而异。目前还没有一个统一的中央系统来构建安全措施&#xff0c;那么&#xff0c;在企业物联网实际应用中&#xff0c;如何保证信息安全&#xff1f;物联网生态系统的安…

OpenHarmony社区运营报告(2022年12月)

本月快讯• 本月新增22款产品通过兼容性测评&#xff0c;累计220款产品通过兼容性测评。• 12月28日&#xff0c;OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;凭借其创新的技术特性和开源生态建设成果&#xff0c;荣膺“InfoQ 2022年度十大开源新锐项…

Redis实现用户签到 | 黑马点评

目录 一、BitMap用法 1、介绍 2、用法 3、练习 二、签到功能 1、需求 2、代码实现 三、签到统计 1、分析 2、接口实现 一、BitMap用法 1、介绍 我们完全可以通过数据库签到表来实现签到功能&#xff0c;但是假如我们的用户达到千万&#xff0c;每年平均签到10次&am…

基于微信小程序的网络安全科普系统小程序

文末联系获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.…

设计模式之简单工厂

现在有一个这样的需求&#xff1a;控制台输入俩个数&#xff0c;并输入运算符&#xff0c;计算并输出结果。上述需求乍一看&#xff0c;特别像一个小型的计算器&#xff0c;记得初学Java时&#xff0c;实现过。 实现一&#xff1a; 创建计算器类&#xff0c;控制台输入俩个数…

windows 10 本地配置Oracle19+用navicat连接

文章目录0.背景环境0.背景知识1.卸载旧版本、安装 oracle 192.配置3.用 Navicat 连接3.1 下载instantclient193.2 配置dll使能连接高版本oracle3.3 配置连接4. 相关操作命令5.本地命令行登录orclpdb下的用户0.背景环境 本机已安装oracle12和Navicat15&#xff0c;需要先彻底卸载…

【docker】基础知识梳理与使用

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 docker基础知识的梳理与使用 1. docker的理解 Registry&#xff08;仓库&#xff09;&#xff1a;是一个集中存储与分发镜像的服务。最常用的Registry是…

油井远程监控解决方案

1.项目背景 油田生产过程中&#xff0c;由于井筒内存在着不同程度的缺陷&#xff0c;会产生各种问题。而油井开采设备的连续稳定运行是保证石油开采的首要条件&#xff0c;但是由于油田地域广阔&#xff0c;油井分布广泛&#xff0c;没有规则性的油井工作状况的监测和控制&…

Acwing4655. 重新排序(差分模板题)

给定一个数组 A 和一些查询 Li,Ri&#xff0c;求数组中第 Li 至第 Ri 个元素之和。 小蓝觉得这个问题很无聊&#xff0c;于是他想重新排列一下数组&#xff0c;使得最终每个查询结果的和尽可能地大。 小蓝想知道相比原数组&#xff0c;所有查询结果的总和最多可以增加多少? …

【树】二叉树的非递归遍历

非递归的遍历需要使用栈保存当前不输出的结点&#xff0c;并且三种遍历顺序步骤有所不同。中序遍历1.查看其当前结点是否为空&#xff1a;若非空则将当前结点入栈&#xff0c;指针指向其左孩子&#xff1b;若当前结点为空&#xff0c;说明上一个入栈的结点没有左孩子&#xff0…

vite+vue3+elementPlus搭建项目

创建基础框架 方式一&#xff1a; 创建命令 npm create vitelatest or yarn create vite 注意&#xff1a;这里可能会出现一个坑&#xff0c;注意你的node版本&#xff08;node版本过低就会报错&#xff09; 创建成功 创建成功后运行以下命令即可 yarn yarn dev 这种创建方…

C技能树-判断语句

三个数从小到大排序并输出 任意输入3个整数&#xff0c;使用if语句对这3个整数由小到大进行升序排序。请判断下面哪一项无法实现该功能。 #include <stdio.h>/* 交换x和y */ void swap(int* x, int* y) {int temp *x;*x *y;*y temp; }int main(int argc, char** arg…

[红明谷CTF 2021]write_shell

目录 信息收集 payload 补充知识 信息收集 代码审计 <?php error_reporting(0); highlight_file(__FILE__); function check($input){if(preg_match("/| |_|php|;|~|\\^|\\|eval|{|}/i",$input)){// if(preg_match("/| |_||php/",$input)){die(h…