07-ESP timer

ESP32-S3 ESPTIMER

介绍

ESP Timer是ESP32-S3的一个强大功能,它允许创建软件定时器并在超时时调用它们的回调函数。这对于需要执行延迟或周期性操作的用户软件非常有用,例如延迟设备启动/停止或周期性采样传感器数据。

对于需要较好实时性能(如生成波形)或可配置定时器分辨率的应用场景,建议改用GPTimer。此外,GPTimer 还具有 ESP Timer 中没有的功能,例如事件捕获。

系统定时器

我们先来简单的了解一下系统定时器的结构图,对于后面的编程会有一个较好的思路

ESP32-S3 芯片内置一组 52 位系统定时器。该定时器可用于生成操作系统所需的滴答定时中断,也可用作普通定时器生成周期或单次延时中断。

系统定时器内置两个计数器(UNIT0UNIT1 )以及三个比较器(COMP 0COMP1COMP2)。比较器用于监控计数器的计数值是否达到报警值。定时器的功能块图见上图

1.计数器

ESP32-S3 内置了一个 52 位的高精度计数器,能够基于外部晶体振荡器 XTAL_CLK 运行,该振荡器工作频率通常为 40MHz。

计数器首先对 XTAL_CLK 进行了分频操作,其中在一个计数周期内产生了一个频率为原始 XTAL_CLK 频率三分之一(fXTAL_CLK/3)的时钟信号。

然后,在接下来的计数周期中,又产生了频率为 XTAL_CLK 一半(fXTAL_CLK/2)的时钟信号。

因此,计数器实际使用的平均时钟频率 CNT_CLK,需要把两个不同频率周期的时钟信号综合考虑。假设这两个阶段交替进行且每个阶段占同样比例的时间,则 CNT_CLK 的实际平均频率为:

C N T _ C L K = 1 2 × ( 1 3 + 1 2 ) × f X T A L _ C L K {CNT\CLK} = \frac{1}{2} \times \left( \frac{1}{3} + \frac{1}{2} \right) \times f{XTAL\_CLK} CNT_CLK=21×(31+21)×fXTAL_CLK

  • 其中,
    f X T A L _ C L K = 40 M H z f_{XTAL\_CLK} = 40MHz fXTAL_CLK=40MHz

  • 代入计算得:
    C N T _ C L K = 1 2 × ( 1 3 + 1 2 ) × 40 , 000 , 000 H z {CNT\_CLK} = \frac{1}{2} \times \left( \frac{1}{3} + \frac{1}{2} \right) \times 40,000,000Hz CNT_CLK=21×(31+21)×40,000,000Hz

  • 简化后:
    C N T _ C L K = 5 12 × 40 , 000 , 000 H z {CNT\_CLK} = \frac{5}{12} \times 40,000,000Hz CNT_CLK=125×40,000,000Hz

  • 最终得出:
    C N T _ C L K ≈ 16 , 666 , 666.67 H z {CNT\_CLK} ≈ 16,666,666.67Hz CNT_CLK≈16,666,666.67Hz

即 16MHz见下图1。每个 CNT_CLK 时钟周期,计数递增 1/16µs,即 16 个周期递增 1µs。

2.比较器

COMP0、COMP1、COMP2 均为 ESP32-S3 系统定时器内置的 52 位比较器。比较器同样使

用 XTAL_CLK 作为时钟源(40MHz)。

上图展示了系统定时器生成报警的过程。在上述过程中用到一个计数器(Timer Countern)和一个比较器(Timer Comparatorx),比较器将根据比较结果,生成报警中断。

关于更多的硬件资料可以看一下官方文档
系统定时器 (SYSTIMER)

特性和概念

ESP Timer API提供了以下功能:

  • 一次性和周期性定时器:一次性定时器在到期后只调用一次其回调函数,然后停止操作。周期性定时器在到期后调用其回调函数并自动重启自身,直到手动停止周期性定时器为止。
  • 多种回调分派方法 :定时器回调可以使用以下方法进行分派:
    • 任务分派方法(默认):从单个高优先级ESP Timer任务(esp_timer任务(由ISR通知)>回调)分派定时器回调。
    • 中断分派方法(ESP_TIMER_ISR):直接从中断处理程序(ISR>回调)分派定时器回调。

使用

以下是使用ESP Timer的一般步骤:

  1. 定时器句柄 :声明一个 esp_timer_handle_t 类型的变量 my_timer,用于存储定时器句柄。
  2. 创建定时器 :使用esp_timer_create()函数创建一个定时器。
  3. 启动定时器 :使用esp_timer_start_once()esp_timer_start_periodic()函数启动定时器。
  4. 停止定时器 :使用esp_timer_stop()函数停止定时器。
  5. 删除定时器 :使用esp_timer_delete()函数删除定时器。

示例代码

以下是一个使用ESP Timer的示例代码:

c 复制代码
#include "esp_timer.h"

void timer_callback(void* arg)
{
    // 在这里执行你的回调函数
}

void app_main(void)
{
    esp_timer_handle_t my_timer;	//用于存储定时器句柄

    // 定义一个定时器结构体
    esp_timer_create_args_t timer_args = {	
        .callback = &timer_callback,	//设置回调函数
        // 可以添加其他参数
    };
    esp_timer_create(&timer_args, &my_timer);	//将创建的定时器句柄存储到 my_timer 中。

    // 启动定时器
    esp_timer_start_once(my_timer, 1000000);  // 1秒后执行回调函数 第二个参数单位为us

    // 停止定时器
    esp_timer_stop(my_timer);

    // 删除定时器
    esp_timer_delete(my_timer);
}

关于更多的API功能可以看一次官方的文档
ESP定时器(高分辨率定时器)

实验例程

实验将配置 ESP 定时器每间隔 1000 毫秒产生一次中断,并在其中断服务函数中改变一次 LED 的状态,具体的步骤如下:

①:设置 ESP 定时器的回调函数

②:创建一个事件

③:每周期内触发一次

ESPtimer.h

cpp 复制代码
/**
 * @file ESPtimer.h
 * @author 宁子希 (1589326497@qq.com)
 * @brief 高分辨率定时器(ESP定时器)驱动代码
 * @version 0.1
 * @date 2024-04-16
 * 
 * @copyright Copyright (c) 2024
 * 
 */

#ifndef __ESPTIMER_H__
#define __ESPTIMER_H__

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_timer.h"
#include "LED.h"


//初始化初始化高分辨率定时器
void ESPtimer_init(uint64_t tps);

//定时器回调函数
void ESPtimer_callback(void* arg);

#endif

ESPtimer.c

cpp 复制代码
/**
 * @file ESPtimer.cpp
 * @author 宁子希 (1589326497@qq.com)
 * @brief   高分辨率定时器(ESP定时器)驱动代码
 * @version 0.1
 * @date 2024-04-16
 * 
 * @copyright Copyright (c) 2024
 * 
 */

#include "ESPtimer.h"

//初始化初始化高分辨率定时器
void ESPtimer_init(uint64_t tps){

    esp_timer_handle_t timer_handle;    //定时器句柄

    esp_timer_create_args_t timer_args={
        .callback=&ESPtimer_callback,    //回调函数
        .arg=NULL,                        //回调函数参数
    };

    esp_timer_create(&timer_args,&timer_handle);    //创建定时器
    
    esp_timer_start_periodic(timer_handle,tps);    //启动定时器 tps单位为us
}

//定时器回调函数
void  IRAM_ATTR ESPtimer_callback(void* arg){
    LED_A_TOGGLE();
    LED_B_TOGGLE();
}

main.c

cpp 复制代码
/**
 * @file main.c
 * @author 宁子希 (1589326497@qq.com)
 * @brief   高分辨率定时器(ESP定时器)实验
 * @version 0.1
 * @date 2024-04-16
 * 
 * @copyright Copyright (c) 2024
 * 
 */
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "nvs_flash.h"
#include "LED.h"
#include "ESPtimer.h"

void app_main(void){
    esp_err_t ret;
    
    ret = nvs_flash_init();         /* 初始化NVS */

    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }

    led_init();
    
    ESPtimer_init(1000000);
}

实验效果

结论

在完成编译和烧录操作后,可以看到板子上的 LED 在闪烁,LED 在 ESP 定时器的中断回调函数中被改变状态,LED 每间隔 1000 毫秒改变一次状态。

相关推荐
唐诺22 分钟前
几种广泛使用的 C++ 编译器
c++·编译器
冷眼看人间恩怨1 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
红龙创客1 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
析木不会编程2 小时前
【小白51单片机专用教程】protues仿真独立按键控制LED
单片机·嵌入式硬件·51单片机
Lenyiin2 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
yuanbenshidiaos3 小时前
c++---------数据类型
java·jvm·c++
十年一梦实验室4 小时前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
taoyong0014 小时前
代码随想录算法训练营第十一天-239.滑动窗口最大值
c++·算法
这是我584 小时前
C++打小怪游戏
c++·其他·游戏·visual studio·小怪·大型·怪物
fpcc4 小时前
跟我学c++中级篇——C++中的缓存利用
c++·缓存