ESP32 Arduino学习篇(二):深入理解中断机制

外部中断

一、外部中断简介

单片机最为核心的内容,应该就是中断了吧。相较于其他的单片机而言,ESP32的外部中断是十分强大的,因为其每个引脚均可以设置为外部中断的触发引脚。

ESP32 Arduino 有以下触发方式:

LOW 低电平触发

CHANGE 电平变化

RISING 上升沿触发

FALLING 下降沿触发

HIGH 高电平触发

二、外部中断函数

1. 开启外部中断 attachInterrupt(pin,function,mode);

attachInterrupt(uint8_t pin, std::function<void ()> intRoutine, int mode)

函数功能:配置初始化外部中断

参数1:pin,外部中断所使用的引脚,ESP32所有引脚均可以配置为外部中断引脚

参数2:外部中断回调函数 ,此处填写函数名即可

参数3:中断触发方式,支持以下触发方式:

中断触发模式

说明

RISING

上升沿触发

FALLING

下降沿触发

CHANGE

电平变化触发

ONLOW

低电平触发

ONHIGH

高电平触发

举例:

void func1()
{
  Serial.printf("按键中断触发");
  }
void setup()
{
  Serial.begin(9600);
  attachInterrupt(0,func1,FALLING);
}

void loop()
{
  
}
//中断函数就是当中断被触发后要去执行的函数,该函数不能带有任何参数,且返回类型为空,设置一个外部中断的中断服务函数

2. 关闭引脚中断 detchInterrupt(pin);

无返回值

三、外部配置步骤

步骤:

1.CHUSHIHUA

2.绑定中断服务函数 timerAttachInterrupt()

3.设置触发定外部中断的方式 timerAlarmWrite()

完整代码示例:

#include <Arduino.h>

// 定义外部中断的Mode
// 0: 无中断,读取Touch值
// 1:Touch中断,执行 TouchEvent()
// 2: 外部IO的中断
#define EXT_ISR_MODE 1

void TouchEvent()
{
    Serial.printf("Touch Event.\r\n");
}

void PinIntEvent()
{
    Serial.printf("PinInt Event.\r\n");
}

void setup()
{
    // put your setup code here, to run once:
    Serial.begin(115200);

#if 1 == EXT_ISR_MODE   // 触摸中断
    // Pin: T0(GPIO4), 函数指针:TouchEvent, 阈值: 40
    touchAttachInterrupt(T0, TouchEvent, 40);

#elif 2 == EXT_ISR_MODE   // 下降沿触发 
    pinMode(0, INPUT_PULLUP); //设置引脚0位外部中断引脚
    attachInterrupt(0, PinIntEvent, FALLING);

#endif
}

void loop()
{
    // put your main code here, to run repeatedly:

#if 0 == EXT_ISR_MODE
    Serial.printf("touch:%d\r\n", touchRead(T0));
#endif

    delay(200);
}

电容按键

一、电容按键简介

ESP32专门提供了电容触摸传感器的功能, 共有T0,T2~T9 共 9个touch传感器可用.分别对应引脚4、2、15、13、12、14、27、33、32. 无需设置PinMode

二、电容按键函数

(1) 电容输入 touchRead(pin)

返回值 0~255. 触摸强度

注意: 摸得越瓷实,数值越小

示例:

void setup()
{
  Serial.begin(9600);
}

void loop()
{
   Serial.printf("touch:%d\n",touchRead(4));
}

(2)电容输入中断:touchAttachInterrupt(pin, TSR , threshold);

参数:

  • TSR :中断回调函数, 不能带参数, 不能有返回值。

  • threshold:阈值, 达到该阈值会触发此中断

  • 示例:

    void TSR()
    {
      Serial.printf("我被按下了!\r\n");
      }
    
    void setup()
    {
      Serial.begin(9600);
      touchAttachInterrupt(4, TSR , 20);
    }
    
    void loop()
    {
      
    }

    定时器

    一、定时器简介

    ESP32 芯片包含两个定时器组,每组有两个通用定时器。它们都是基于 16 位预分频器和 64 位自动重载功能的向上/向下计数器的 64 位通用定时器

    二、定时器函数

    (1)timerBegin 初始化(开启)定时器

    hw_timer_t * IRAM_ATTR timerBegin(uint8_t num, uint16_t divider, bool countUp)
  • num : 定时器编号(0到3,对应全部4个硬件定时器)

  • divider: 预分频器数值(ESP32计数器基频为80M,80分频单位是微秒)

  • countUp: 计数器向上(true)或向下(false)计数的标志

  • 返回值:返回一个计时器结构体指针 hw_timer_t * ,预定义一个指针接收它

  • 示例:

    hw_timer_t *timer = NULL;
    timer = timerBegin(0, 80, true);

    (2) 取消初始化定时器:timerEnd

    void timerEnd(hw_timer_t *timer)

    参数:

  • *timer : 目标定时器 ( 计时器结构体指针 hw_timer_t * )

  • (3) 配置定时器中断 :timerAttachInterrupt

    void IRAM_ATTR timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge)

    timer:指向已初始化定时器的指针

    (*fn)():中断服务函数的函数指针

    edge:表示中断触发类型是边沿(true)还是电平(false)的标志

    (4)取消定时器中断 :timerDetachInterrupt

    void timerDetachInterrupt(hw_timer_t *timer)

    参数:

  • *timer : 目标定时器 ( 计时器结构体指针 hw_timer_t * )

  • (5) 定时器设置:timerAlarmWrite

        timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload);

    timer:指向已初始化定时器的指针

    alarm_value: 触发中断的计数器值(1000000 us -> 1s)

    autoreload:定时器在产生中断时是否重新加载的标志

    (6) 使能定时器:timerAlarmEnable

        timerAlarmEnable(hw_timer_t *timer);

    timer:指向已初始化定时器的指针

    (7) 失能定时器 timerAlarmDisable

    void timerAlarmDisable(hw_timer_t *timer)

    (8) 判断定时器是否启动 timerAlarmEnabled

    bool timerAlarmEnabled(hw_timer_t *timer)

    三、定时器配置步骤

    步骤:

    1.选择定时器并设置合适分频系数和计数模式 (两组四个) timerBegin()

    2.绑定中断服务函数 timerAttachInterrupt()

    3.设置触发定时器中断的计数器值 timerAlarmWrite()

    4.使能定时器 timerAlarmEnable()

    代码示例(向串口发送1-5,1s为间隔)

    #include <Arduino.h>
    #include "../lib/Moter/Moter.h"
    #define PMW_EN 0
    int interruptCounter = 0;
    hw_timer_t *timer = NULL;
    // 中断服务函数,为使编译器将代码分配到IRAM内,中断处理程序应该具有 IRAM_ATTR 属性
    void IRAM_ATTR TimerEvent()
    {
        Serial.println(interruptCounter++);
        if (interruptCounter > 5)
        {
            interruptCounter = 1;
        }
    }
    void setup()
    {
        Serial.begin(115200);
    #if PMW_EN
        Motor_Init();
    #endif
        timer = timerBegin(0, 80, true);
        timerAttachInterrupt(timer, &TimerEvent, true);
        timerAlarmWrite(timer, 1000000, true);
        timerAlarmEnable(timer); //使能定时器
    }
    void loop()
    {
    #if PMW_EN
        PWM_SetDuty(200 * interruptCounter, 200 * interruptCounter);
    #endif
    }

    Ticker 定时库

    一、Ticker 定时库简介

    Ticker 是ESP32自带库

    注意: 不建议使用Ticker回调函数来阻塞IO操作(网络、串口、文件);可以在Ticker回调函数中设置一个标记,在loop函数中检测这个标记;

    对于arg,必须是 char, short, int, float, void
    , char 之一;

    二、Ticker 定时库相关函数

    1. 定时状态获取 .active();

    /**
     * Ticker是否激活状态
     * @return bool true表示ticker启用
     */
    bool active();

    2. 终止定时器 .detach()

    /**
     * 停止Ticker
     */
    void detach();

    3. once() —— xx秒后只执行一次

    /**
     * xx秒后只执行一次
     * @param seconds 秒数
     * @param callback 回调函数
     */
    void once(float seconds, callback_function_t callback);
    
    /**
     * xx秒后只执行一次
     * @param seconds 秒数
     * @param callback 回调函数
     * @param arg 回调函数的参数
     */
    void once(float seconds, void (*callback)(TArg), TArg arg)

    示例: 一个传参,一个不传参

    #include "Arduino.h"
    #include "Ticker.h"
    
    Ticker t1;
    Ticker t2;
    
    void func1()
    {
      Serial.println("我是t1的回调,我没参数");
    }
    void func1(int a)
    {
      Serial.print("我是t2的回调,我的参数是:");
      Serial.println(a);
    }
    
    void setup()
    {
      Serial.begin(115200);
      if (1)
      {
        t1.once(10, func1);
        t2.once(20, func1, 8);
        //t1 t2 方法名都叫func1, 但其实是不同的方法, 这涉及到一个方法重载的概念
      }
    }
    
    void loop()
    {
      Serial.println("我来证明我没被阻塞,也没法阻塞Ticker");
      delay(2700);
    }

    4. once_ms() —— xx毫秒后只执行一次

    /**
     * xx毫秒后只执行一次
     * @param seconds 秒数
     * @param callback 回调函数
     */
    void once_ms(float seconds, callback_function_t callback)
    
    /**
     * xx毫秒后只执行一次
     * @param seconds 秒数
     * @param callback 回调函数
     * @param arg 回调函数的参数
     */
    void once_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg);
    

    5. attach() —— 每隔xx秒周期性执行

    和上面一样,只是周期性执行, 需要.detach()结束运行

    /**
     * 每隔xx秒周期性执行
     * @param seconds 秒数
     * @param callback 回调函数
     */
    void attach(float seconds, callback_function_t callback);
    
    /**
     * 每隔xx秒周期性执行
     * @param seconds 秒数
     * @param callback 回调函数
     * @param arg 回调函数的参数
     */
    void attach(float seconds, void (*callback)(TArg), TArg arg)

    6. attach_ms() —— 每隔xx毫秒周期性执行

    /**
     * 每隔xx毫秒周期性执行
     * @param seconds 秒数
     * @param callback 回调函数
     */
    void attach_ms(float seconds, callback_function_t callback);
    
    /**
     * 每隔xx毫秒周期性执行
     * @param seconds 秒数
     * @param callback 回调函数
     * @param arg 回调函数的参数
     */
    void attach_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg)

    引脚脉冲信号检测

    我们的ESP32自带能检测引脚脉冲宽度的功能,也就是检测高电平的时间。

    这里我们以HC-SR04超声波测距模块来介绍这个功能。

    1.前言:

    众所周知传感器(transducer/sensor)是一种检测装置,能感受到被测量的信息,并能将感受到的信息,按一定规律变换成为电信号或其他所需形式的信息输出,以满足信息的传输、处理、存储、显示、记录和控制等要求。传感器在生活中无处不在,小到温湿度传感器测量温湿度,大到当今最火热的领域——物联网IoT,传感器总是发挥着重要的作用。而学习传感器也尤为重要,特别是在电子设计中。

    2.HC-SR04模块介绍

    HC-SR04模块性能稳定,测度距离精确,模块高精度,盲区小。在日常生活中,以下地方都可应用超声波测距模块:

    (1)机器人避障

    (2)物体测距

    (3)液位检测

    (4)公共安防

    (5)停车场检测

    (6)电子设计中的避障智能车也正是应用到了此模块,才能达到避开障碍的功能。

    3.电气参数

    电气参数

    HC-SR04超声波测距模块

    工作电压

    DC 5V

    工作电流

    15mA

    工作频率

    40Hz

    最远射程

    4米

    最近射程

    2厘米

    测量角度

    15度

    输入触发信号

    10us的TTL脉冲

    输出回响信号

    输出与射程成正比的TTL电平信号

    规格尺寸

    40x20x15mm

    4.工作原理

    第一步:通过IO口给Trig接口周期不小于10us的脉冲信号。

    第二步:HC-SR04接收到单片机发来的脉冲信号后自动发送8个频率为4KHz的方波,自动检测是否有信号返回。

    第三步:若有信号返回,则通过Echo接口向单片机相连的IO口发送一个高电平,高电平持续时间就是超声波从发射到返回的总时间。

    假设高电平持续时间为T,声速为v(一般为340m/s),那么测到的距离S=(T*v)/2。

    4.换算单位

    (1)us / 58 ——>cm

    (2)cm / 148 ——>英寸

    5.HC-SR04时序图

    板上接线方式:VCC、trig(控制端)、 echo(接收端)、 out(空脚)、 GND

    6.ESP32相关函数

    pulseIn(pin,state)

    pulseIn(pin,state,timeout)

    参数:

  • pin : 引脚

  • state : 脉冲类型, 可选高或者低

  • timeout : 超时时间, 默认1秒钟. 单位为微秒, 无符号长整型.

  • 返回值: 脉冲宽度, 单位微秒, 数据类型为无符号长整型. 如果超时返回0

    示例:

    #include <Arduino.h>
    int distance = 0;
    void setup()
    {
      Serial.begin(115200);
      pinMode(4, OUTPUT);
      digitalWrite(4, LOW);
    }
    
    void loop()
    {
      digitalWrite(4, HIGH);
      delayMicroseconds(20);
      digitalWrite(4, LOW);
      distance = pulseIn(18,HIGH)/58;
      Serial.printf("当前距离是:%d cm",distance);
      delay(1000);
    }
    

    ESP32其他APi

    一、ESP32时间统计函数

    1. 开机至今的毫秒数 millis

    millis() 返回值是unsigned long 类型, 大约50天溢出一次

    2. 开机至今的微秒数 micros

    micros() 返回值是unsigned long 类型, 大约70分钟溢出一次

    二、 阻塞延时

    时间控制函数

        由于我们接下来的实验程序很多都用到延时函数,那么这里就介绍几个:

    delay() —– 毫秒级延时

    delayMicroseconds() —– 微秒级延时

    三、霍尔传感器

    ESP32自带霍尔传感器 , 当有磁场靠近时,会显示正负值

    hallRead()

    物联沃分享整理
    物联沃-IOTWORD物联网 » ESP32 Arduino学习篇(二):深入理解中断机制

    发表评论