Appearance
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 中,以提高性能。
- 可以参考官方文档中 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() {
}