ESP32学习5:定时器

一、定时器:

        ESP32 内置4 个64-bit 通用定时器。每个定时器包含一个16-bit 预分频器和一个64-bit 可自动重新加载向上/向下计数器。

        ESP32 的定时器分为2 组,每组2 个。TIMGn_Tx 的n 代表组别,x 代表定时器编号。

定时器特性:

  • 16-bit 时钟预分频器,分频系数为2-65536
  • 64-bit 时基计数器
  • 可配置的向上/向下时基计数器:增加或减少
  • 暂停和恢复时基计数器
  • 报警时自动重新加载
  • 当报警值溢出/低于保护值时报警
  • 软件控制的即时重新加载
  • 电平触发中断和边沿触发中断

1.  16-bit 预分频器

         每个定时器都以APB 时钟(缩写APB_CLK,频率通常为80 MHz)作为基础时钟。而预分频器的作用就是对APB时钟进行分频,产生时基计数器时钟(TB_clk)。TB_clk 每过一个周期,时基计数器会向上数一或者向下数一。在使用寄存器TIMGn_Tx_DIVIDER 配置分频器除数前,必须关闭定时器(清零TIMGn_Tx_DIVIDER)。定时器使能时配置预分频器会导致不可预知的结果。

 2. 寄存器

  • TIMGn_Tx_EN 置1 后,定时器x 时基计数器使能。(读/写) 

         TIMGn_Tx_EN 置1 或清零可以使能或关闭计数。

  • TIMGn_Tx_INCREASE 置1 后,定时器x 的时基计数器会在每个时钟周期后增加。清零后,定时器x 时基计数器会在每个时钟周期后减少。(读/写)

         TIMGn_Tx_INCREASE 置1 或清零可以将64-bit 时基计数器分别配置为向上计数或向下计数。同时,64-bit 时基计数器支持自动重新加载和软件即时重新加载,计数器达到软件设定值时会触发报警事件。

  • TIMGn_Tx_AUTORELOAD 置1 后,定时器x 报警时自动重新加载使能。(读/写)
  • TIMGn_Tx_DIVIDER 计时器x 时钟(Tx_clk) 的预分频器值。(读/写)
  • TIMGn_Tx_EDGE_INT_EN 置1 后,报警会产生一个边沿触发中断。(读/写)
  • TIMGn_Tx_LEVEL_INT_EN 置1 后, 报警会产生一个电平触发中断。(读/写)

       电平触发是在高或低电平保持的时间内触发,而边沿触发是由高到低或由低到高这一瞬间触发。

  • TIMGn_Tx_ALARM_EN 置1 后, 报警使能。(读/写)

       TIMGn_TxLO_REG 在TIMGn_TxUPDATE_REG 上写值后,定时器x 时基计数器的低32 位可以被读取。(只读)

         TIMGn_TxHI_REG 在TIMGn_TxUPDATE_REG 上写值后,定时器x 时基计数器的高32 位可以被读取。(只读)

         TIMGn_TxUPDATE_REG 写任何值触发定时器x 时基计数器值更新(定时器x 当前值会被存储到以上寄存器)。(只写)

        以上寄存器只涉及到配置寄存器以及基本的64-bit时基寄存器,还有一些其他的如报警、重新加载等寄存器可以参考官方技术手册。

二、软件程序编写:

time.h文件内容:

#ifndef COMPONENTS_TIME_INCLUDE_TIME_H_
#define COMPONENTS_TIME_INCLUDE_TIME_H_

#include "driver/timer.h"
#include <stdio.h>

void Time_Init();//初始化定时器函数
void IRAM_ATTR timer_group0_isr(void *para); //定时器中断函数

#endif /* COMPONENTS_TIME_INCLUDE_TIME_H_ */

time.c文件内容:

#include "time.h"
#include "led.h"

int led_flag = 0;

void Time_Init(){
	/**
	 * 设置定时器初始化参数
	 */
	timer_config_t config ={
        .divider = 8, //分频系数
		.counter_dir = TIMER_COUNT_UP, //计数方式为向上计数
		.counter_en = TIMER_PAUSE, //调用timer_init函数以后不启动计数,调用timer_start时才开始计数
		.alarm_en = TIMER_ALARM_EN, //到达计数值启动报警(计数值溢出,进入中断)
        .auto_reload = 1, //自动重新装载预装值
	};
    /**
     * 初始化定时器
     *    TIMER_GROUP_0(定时器分组0)
     *    TIMER_0(0号定时器)
     */
	timer_init(TIMER_GROUP_0,TIMER_0,&config);

	/*设置定时器预装值*/
	timer_set_counter_value(TIMER_GROUP_0,TIMER_0,0x00000000ULL);

	/**
	 * 设置报警阈值
	 *    1000[定时1000ms] (TIMER_BASE_CLK[定时器时钟/8[分频系数]/1000[延时为ms级别,因此除以1000])
	 */
	timer_set_alarm_value(TIMER_GROUP_0,TIMER_0,3000*(TIMER_BASE_CLK/8/1000)); //TIMER_BASE_CLK 为80M
	//定时器中断使能
	timer_enable_intr(TIMER_GROUP_0,TIMER_0);
	/**
	 * 注册定时器中断函数
	 */
	timer_isr_register(TIMER_GROUP_0,TIMER_0,
			timer_group0_isr,  //定时器中断回调函数
			(void*)TIMER_0,    //传递给定时器回调函数的参数
			ESP_INTR_FLAG_IRAM, //把中断放到 IRAM 中
			NULL //调用成功以后返回中断函数的地址,一般用不到
			);
	/*启动定时器*/
	timer_start(TIMER_GROUP_0,TIMER_0); //开始计数
}

/**
 * 定时器中断函数
 */
void IRAM_ATTR timer_group0_isr(void *para){
	//获取定时器分组0中的哪一个定时器产生了中断
	    uint32_t timer_intr = timer_group_get_intr_status_in_isr(TIMER_GROUP_0); //获取中断状态
	    if (timer_intr & TIMER_INTR_T0) {//定时器0分组的0号定时器产生中断
	        /*清除中断状态*/
	        timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0);
	        /*重新使能定时器中断*/
	        timer_group_enable_alarm_in_isr(TIMER_GROUP_0, TIMER_0);
	    }
        /*led交替闪烁,时间为定时器时间2s*/
        if(led_flag==0){
        	led_flag =1;
        	led_on();
        }else{
        	led_flag=0;
        	led_off();
        }
}

定时器的初始化如上述代码Time_Init()中所示。主要涉及的API函数有:

  • timer_init() 定时器初始化函数;
  • timer_config_t config 定时器配置结构体,用于配置分频器,计数模式,计数器使能,报警使能,自动重载等。
  • timer_set_alarm_value() 设置警报值(中断),定时器一旦超过该值,会立即触发中断。
  • timer_enable_intr() 使能定时器中断
  • timer_isr_register 注册定时器中断函数
  • timer_start() 启动定时器

main函数:

#include "interrupt.h"
#include "driver/timer.h"
#include "time.h"
#include "led.h"
void app_main(void)
{
//	/**
//	 * 初始化GPIO
//	 */
	led_init();
//	inter_init();
	Time_Init();
    while(1) {
        //必须加延时,任务不能没有延时,否则导致任务无法切换.
        vTaskDelay(1000 / portTICK_RATE_MS);
    }
}

        实验现象,在定时器的驱动下,LED每隔2秒,电平翻转一次。 

软件定时器:

        除了上述硬件定时器之外,还有软件定时器。软件定时器是在硬件定时器的基础之上实现的。软件定时器内部运行着一个1us的硬件定时器,而软件定时器的回调函数都放在了这个1us定时器的中断函数里面。

time2.h文件内容:

#ifndef COMPONENTS_TIME2_INCLUDE_TIME2_H_
#define COMPONENTS_TIME2_INCLUDE_TIME2_H_

#include <stdio.h>
#include "driver/timer.h"
#include "esp_timer.h"
#include "led.h"

void Time2_Init(); //定时器初始化
void esp_timer_cb(void *arg); //定时器中断函数

#endif /* COMPONENTS_TIME2_INCLUDE_TIME2_H_ */

time2.c文件内容:

 

#include "time2.h"

int led_flag = 0;
esp_timer_handle_t esp_timer_handle = 0; //定时器句柄

/**
 * 初始化软件定时器
 */
void Time2_Init(){
	//定时器结构体初始化
	esp_timer_create_args_t fw_timer = {
			.callback = &esp_timer_cb, //定时器回调函数
			.arg = NULL, //传递给回调函数的参数
			.name = "esp_timer", //定时器名称
	};

	/**
	 * 创建定时器
	 *     返回值为定时器句柄,用于后续对定时器进行其他操作。
	 */
	esp_err_t err = esp_timer_create(&fw_timer,&esp_timer_handle);
	//启动定时器 以循环方式启动定时器
	err = esp_timer_start_periodic(esp_timer_handle,1000*1000); //us级定时,1000*1000=1s
	//单次启动
	//err = esp_timer_start_once(esp_timer_handle, 1000 * 1000);
	if(err==ESP_OK){
		printf("ok!\r\n");
	}
}


/**
 * 定时器回调函数
 */
void esp_timer_cb(void *arg){
    /*led交替闪烁,时间为定时器时间2s*/
    if(led_flag==0){
    	led_flag =1;
    	led_on();
    }else{
    	led_flag=0;
    	led_off();
    }
}

        上述软件定时器相当于对开始的硬件定时器进行了1us定时封装,省去了繁琐的配置过程,更易于开发过程。采用这种方法配置定时器用到的API函数有:

创建定时器函数:esp_timer_create(); 

函数原型

esp_err_t esp_timer_create

         const esp_timer_create_args_t* create_args,

         esp_timer_handle_t* out_handle

参数

create_args: 定时器结构体

typedef struct {
        esp_timer_cb_t callback; //定时器时间到回调
        void* arg; //要传入回调的参数
        esp_timer_dispatch_t dispatch_method; //从任务或 ISR 调用回调
        const char* name; //定时器名称,esp_timer_dump 函数使用
} esp_timer_create_args_t;
out_handle:定时器句柄

函数功能创建定时器
返回值ESP_OK:成功
ESP_ERR_INVALID_ARG : 参数错误
ESP_ERR_INVALID_STATE:定时器已经运行

启动单次定时器函数:esp_timer_start_once();

启动周期定时器函数:esp_timer_start_periodic();

函数原型esp_err_t esp_timer_start_periodic(
        esp_timer_handle_t timer,
        uint64_t period
)
参数

timer: 定时器句柄

period: 定时器定时周期,单位us,1000us=1ms

函数功能启动周期定时器。
返回值ESP_OK:成功
ESP_ERR_INVALID_ARG : 参数错误
ESP_ERR_INVALID_STATE:定时器已经运行

        除了上述两个定时器API函数之外, 还有一些其他的定时器函数,可以通过定时器句柄,实现对定时器的控制,如:

  • esp_timer_stop(esp_timer_handle_t timer);    停止定时器
  • esp_timer_delete(esp_timer_handle_t timer);  删除定时器
  • int64_t esp_timer_get_time()  获取定时器时间,返回自调用 esp 计时器 init 以来的微秒数(通常在应用程序启动的早期发生)

三、结束

        本文主要介绍了ESP32通用定时器的使用,主要内容就是定时器的配置, 需要熟悉几个定时器配置使用到的API函数及结构体。

  • 10
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值