Skip to content

KEY

硬件连接

md
 * 接线说明:按键模块-->ESP32 IO
 *         (K1-K4)-->(14,27,26,25)
 *         
 *         LED模块-->ESP32 IO
 *         (D1-D4)-->(15,2,0,4)

软件实现

1. 实现第一个 key IO25 控制 一个 LED IO5 灯.

key 控制一个LED 代码
cpp
#include <Arduino.h>

#define KEY_1 25
#define LED_1 15

void setup() {
  Serial.begin(115200);
  pinMode(KEY_1, INPUT_PULLUP); //设置引脚为输入上拉模式
  pinMode(LED_1, OUTPUT); //设置引脚为输出模式

  digitalWrite(LED_1,0);//引脚输出低电平
}

void loop() {

  Serial.println(digitalRead(KEY_1));
  delay(80);

  if(digitalRead(KEY_1) == 0) {
      digitalWrite(LED_1,1); //引脚输出高电平
  }
}

2. 如果实现按一个 key 键, LED 亮, 再按一个 key 键, LED 灭.

key 控制一个LED 代码
cpp
#include <Arduino.h>

#define KEY_1 25
#define LED_1 15

int status = 0;

void setup() {
  Serial.begin(115200);
  pinMode(KEY_1, INPUT_PULLUP); //设置引脚为输入上拉模式
  pinMode(LED_1, OUTPUT); //设置引脚为输出模式

  digitalWrite(LED_1,0);//引脚输出低电平
}

void loop() {

  Serial.println(digitalRead(KEY_1));
  delay(80);

  if(digitalRead(KEY_1) == 0) {
    status = !status;
    digitalWrite(LED_1,status); //引脚输出高电平
  }
}

3. 去掉毛刺

key 控制一个LED 代码
cpp
#include <Arduino.h>

#define KEY_1 25
#define LED_1 15

int status = 0;
int flag = 1;

void setup() {
  Serial.begin(115200);
  pinMode(KEY_1, INPUT_PULLUP); //设置引脚为输入上拉模式
  pinMode(LED_1, OUTPUT); //设置引脚为输出模式

  digitalWrite(LED_1,0);//引脚输出低电平
}

void loop() {
  Serial.println(digitalRead(KEY_1));
  delay(80);

  if(digitalRead(KEY_1) == 0) {
    if(flag) {
      flag = 0;
      status = !status;
      digitalWrite(LED_1,status); //引脚输出高电平
    }
  } else {
    flag = 1;
  }
}

中断

使用 ESP32,我们可以将所有 GPIO 引脚配置为硬件中断源。我们可以通过将这些 GPIO 引脚连接到相应的 ISR 来启用中断。

中断处理函数 (ISR)

cpp
#include <Arduino.h>

#define KEY_1 25
#define LED_1 15

int status = 0;

void IRAM_ATTR isr_key () {
  status = !status;
  digitalWrite(LED_1, digitalRead(LED_1) ^ 1);
}

void setup() {
  Serial.begin(115200);
  pinMode(KEY_1, INPUT_PULLUP); //设置引脚为输入上拉模式
  pinMode(LED_1, OUTPUT); //设置引脚为输出模式

  digitalWrite(LED_1,0);//引脚输出低电平

  attachInterrupt(digitalPinToInterrupt(KEY_1), isr_key, FALLING);
}

void loop() {
	
}

实现中断

在 Arduino IDE 中,我们使用一个函数调用attachInterrupt()来逐个引脚设置中断。

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

参数:

  • pin:要配置为中断的引脚 (GPIO Pin)。
  • intRoutine:中断处理程序的函数指针 (ISR)。
  • mode:中断模式。
Mode说明
LOW 底的每当引脚为低电平时触发中断
HIGHT 高的每当引脚为高电平时触发中断
CHANGE 改变每当引脚改变值时触发中断,从 HIGH 到 LOW 或 LOW 到 HIGH
FALLING 坠落当引脚从 HIGH 变为 LOW 时触发中断
RISING 上升当引脚从 LOW 变为 HIGH 时触发中断

关闭中断

当您不再希望 ESP32 监控某个引脚时,您可以选择调用 detachInterrupt() 函数.

cpp
void detachInterrupt(uint8_t pin);

参数:

  • pin:要配置为中断的引脚 (GPIO Pin)。

GPIO中断针脚配置 (GPIO Pin)

digitalPinToInterrupt 是一个宏定义,该函数将使用的引脚编号转换为相应的内部中断编号。

cpp
#define digitalPinToInterrupt(p)    (((p)<40)?(p):-1)

中断函数

cpp
void IRAM_ATTR function_name () {
	// doing
}

什么是 IRAM_ATTR?

通过使用属性标记一段代码,IRAM_ATTR我们声明编译后的代码将放置在 ESP32 的内部 RAM (IRAM) 中。

否则,代码将放在 Flash 中。ESP32 上的闪存比内部 RAM 慢得多。

如果我们要运行的代码是中断服务程序(ISR),我们通常希望尽快执行它。如果我们不得不“等待”从闪存加载 ISR,事情就会大错特错。

cpp
// Forces code into IRAM instead of flash
#define IRAM_ATTR _SECTION_ATTR_IMPL(".iram1", __COUNTER__)

IRAM_ATTR 是一个预处理宏,这意味着它可以在编译期间扩展到几乎任何东西,包括一个空字符串。

它是如何工作的

从概念上讲,它为每个函数提供了一个名为"iram1“的变量,该变量具有唯一的数字。__COUNTER__ 提供的数字仅用于满足值的要求。然后,链接器脚本可以在编译的后期阶段在某些函数上检测到这个变量,并更改将其格式化为最终二进制文件的方式。

为什么使用它?

IRAM_ATTR 是一个 ESP32 的特殊属性,用于指定函数在 IRAM(内部 RAM)中运行,而不是默认的闪存(Flash)中运行。在 ESP32 中,IRAM 是位于处理器内部的高速随机访问存储器,执行速度更快。

使用 IRAM_ATTR 属性可以将函数加载到 IRAM 中,从而提高函数的执行速度和响应性能。在中断服务程序(ISR)中使用 IRAM_ATTR 属性可以确保 ISR 在最短的时间内得到执行,从而更及时地响应中断事件。

因此,IRAM_ATTR 修饰符常常用于将中断服务程序(ISR)函数加载到 IRAM 中,以提高性能。

思考题

使用中断实现一个按键控制一个LED闪烁, 当按下一个按键, LED 闪烁, 再按下一个按键, LED 灭.

思考题 答案
cpp
#include <Arduino.h>

#define KEY_1 25
#define LED_1 15

int status = 0;

void starLed() {
  digitalWrite(LED_1, 1);
  delay(50);
  digitalWrite(LED_1, 0);
  delay(100);
}

void close() {
  digitalWrite(LED_1, 0);
}

void IRAM_ATTR isr_key () {
  status = !status;
}

void setup() {
  pinMode(KEY_1, INPUT_PULLUP); //设置引脚为输入上拉模式
  pinMode(LED_1, OUTPUT); //设置引脚为输出模式

  digitalWrite(LED_1,0); //引脚输出低电平

  attachInterrupt(digitalPinToInterrupt(KEY_1), isr_key, FALLING);
}

void loop() {
  if(status){
    starLed();
  } else {
    close();
  }
}

定时器中断

cpp
#include <Arduino.h>

#define LED_1 15

int status = 0;

hw_timer_t *timer0 = NULL;

//定时器中断函数
void time0_isr(void) {
  status = !status;
  digitalWrite(LED_1, status);
}

//定时器初始化
//per:定时时间,单位us
void timeInit(int per) {
  /* timerBegin:初始化定时器指针
    第一个参数:设置定时器0(一共有四个定时器0、1、2、3)
    第二个参数:80分频(设置APB时钟,ESP32主频80MHz),80则时间单位为1Mhz即1us,1000000us即1s。
    第三个参数:计数方式,true向上计数 false向下计数
 */
  timer0 = timerBegin(0, 80, true);
  /* timerAlarmWrite:配置报警计数器保护值(就是设置时间)
     第一个参数:指向已初始化定时器的指针
     第二个参数:定时时间,这里为500000us  意思为0.5s进入一次中断
     第三个参数:是否重载,false定时器中断触发一次  true:死循环
  */
  timerAlarmWrite(timer0, per, true);
  /* timerAttachInterrupt:绑定定时器
     第一个参数:指向已初始化定时器的指针
     第二个参数:中断服务器函数
     第三个参数:true边沿触发,false电平触发
  */
  timerAttachInterrupt(timer0, &time0_isr, true); 
  timerAlarmEnable(timer0);//启用定时器
  //timerDetachInterrupt(timer0);//关闭定时器
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_1, OUTPUT); //设置引脚为输出模式

  digitalWrite(LED_1,0); //引脚输出低电平

  timeInit(500000);
}

void loop() {
}