侧边栏壁纸
  • 累计撰写 131 篇文章
  • 累计创建 16 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录
ESP32   

ESP32-定时器(timer)

概述

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

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

名词扫盲

16-bit 预分频器:分频就是把系统工作频率分频后当做定时器的工作频率,例如系统时钟为12MHz,12分频后定时器的dao工作时钟为1MHz。

按照ESP32的输入时钟频率为80MHZ,换句话说也就是1/80us=0.0125us就会计数加一,如何我们设置分频系数为80,则1us就会计数加一。分频系数范围是0-65536

64-bit 时基计数器:这个更简单,就是累加计数器,按照输出时钟,每过一个’波‘就加一。它的计数范围是0-0xFFFF FFFF FFFF FFFF,非常大大大大大的数。

Arduino层编程

在Arduino编程时因为无需考虑寄存器的设置,我们只需记住该外设的配置思路即可~
定时器的配置思路:

  1. 选择定时器(两组四个)
  2. 配置合适分频系数
  3. 绑定中断函数
  4. 配置报警计数器保护值
  5. 开启报警

其中我们还可以随时停止定时器、停止报警、重启、重设等等……

1、开启定时器

hw_timer_t * timerBegin(uint8_t timer, uint16_t divider, bool countUp);

timer(选择定时器):0-3 divider(分频系数):0-65536 countUp:是否为向上计数
代码中可以看出,程序执行了该语句后,定时器立即按照默认状态开始了工作。

hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){
    if(num > 3){
        return NULL;
    }
    hw_timer_t * timer = &hw_timer[num];
    if(timer->group) {
        DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_TIMERGROUP1_CLK_EN);
        DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_TIMERGROUP1_RST);
        TIMERG1.int_ena.val &= ~BIT(timer->timer);
    } else {
        DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_TIMERGROUP_CLK_EN);
        DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_TIMERGROUP_RST);
        TIMERG0.int_ena.val &= ~BIT(timer->timer);
    }
    timer->dev->config.enable = 0;
    timerSetDivider(timer, divider);
    timerSetCountUp(timer, countUp);
    timerSetAutoReload(timer, false);
    timerAttachInterrupt(timer, NULL, false);
    timerWrite(timer, 0);
    timer->dev->config.enable = 1;
    addApbChangeCallback(timer, _on_apb_change);
    return timer;
}

2、停止定时器

直接调用,即用即停,效果显著。

void timerEnd(hw_timer_t *timer){
    timer->dev->config.enable = 0;
    timerAttachInterrupt(timer, NULL, false);
    removeApbChangeCallback(timer, _on_apb_change);
}

3、设置定时器(细化)

void timerSetConfig(hw_timer_t *timer, uint32_t config);
uint32_t timerGetConfig(hw_timer_t *timer);

提供了可以更细化的配置内容,这里不做详解。

4、开启中断

void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge);
void timerDetachInterrupt(hw_timer_t *timer);

绑定中断函数

5、配置报警计数器保护值

void timerAlarmWrite(hw_timer_t *timer, uint64_t interruptAt, bool autoreload);

timer:目标定时器 interruptAt:报警保护值 autoreload:是否开启自动重载

配置好上面后,使能报警:

bool timerAlarmEnabled(hw_timer_t *timer);

6、杂七杂八

懒得逐一介绍~

void timerStart(hw_timer_t *timer);
void timerStop(hw_timer_t *timer);
void timerRestart(hw_timer_t *timer);
void timerWrite(hw_timer_t *timer, uint64_t val);
void timerSetDivider(hw_timer_t *timer, uint16_t divider);
void timerSetCountUp(hw_timer_t *timer, bool countUp);
void timerSetAutoReload(hw_timer_t *timer, bool autoreload);

bool timerStarted(hw_timer_t *timer);
uint64_t timerRead(hw_timer_t *timer);
uint64_t timerReadMicros(hw_timer_t *timer);
double timerReadSeconds(hw_timer_t *timer);
uint16_t timerGetDivider(hw_timer_t *timer);
bool timerGetCountUp(hw_timer_t *timer);
bool timerGetAutoReload(hw_timer_t *timer);

uint64_t timerAlarmRead(hw_timer_t *timer);
uint64_t timerAlarmReadMicros(hw_timer_t *timer);
double timerAlarmReadSeconds(hw_timer_t *timer);

例子(模拟看门狗)

当GPIO接地超过3s,系统判断程序跑飞,强制重启。

**#include <Arduino.h>
const int button = 0;         //  按键用于触发延时
const int wdtTimeout = 3000;  //  看门狗时间(ms)
hw_timer_t *timer = NULL;

void IRAM_ATTR resetModule() { // 中断函数
  Serial.println("reboot\n");
  esp_restart();
}

void setup() {
  Serial.begin(9600);
  Serial.println();
  Serial.println("running setup");

  pinMode(button, INPUT_PULLUP);                    
  timer = timerBegin(0, 80, true);                  // 选择timer0,分频系数为80,向上计数
  timerAttachInterrupt(timer, &resetModule, true);  // 绑定中断函数
  timerAlarmWrite(timer, wdtTimeout * 1000, false); // 设置报警保护函数
  timerAlarmEnable(timer);                          // 使能报警器
}

void loop() {
  Serial.println("running main loop");

  timerWrite(timer, 0); // 重置定时器,喂狗 (feed watchdog)
  long loopTime = millis();
  // 当按键被按着超过了3秒,看门狗重启程序
  while (!digitalRead(button)) {
    Serial.println("button pressed");
    delay(500);
  }
  delay(1000); 
  loopTime = millis() - loopTime;
  
  Serial.print("loop time is = ");
  Serial.println(loopTime);
}
0

评论区