0%

ESP32_Interrupt

实现两种功能:
利用轮询方式判断按键是否按下,短、长按,连发等功能的实现
利用外部中断判断按键是否按下,获取按下时长

0.原理图

BootKey图片

BOOT KEY 作为本次实验对象

1.按键FIFO轮询采样

功能逻辑简图

KeyScan原理图片

  1. 定义按键FIFO缓冲区Buffer,10个字节大小,设计读/写指针:Read/Write
  2. 定义按键结构体,实现按键防抖,长按识别,长按连发功能
  3. 以10ms的间隔,扫描按键,并将按下、长按、弹起的键值用bsp_PutKey()放入Buffer中,而处理任务通过bspGetKey()获取键值,执行相应策略

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/ledc.h"
#include "driver/gpio.h"
#include "esp_err.h"
#include <esp_log.h>
#include "driver/adc.h"
#include "esp_adc_cal.h"

#define KEY_FIFO_SIZE 10
#define KEY_COUNT 1
#define KEY_FILTER_TIME 5
#define KEY_LONG_TIME 100 /* 单位10ms, 持续1秒,认为长按事件 */
#define KEY1_Pin 0

typedef enum{
KEY_NONE = 0,

K1_DOWN = 1,
K1_UP = 2,
K1_LONG = 3,
}KEY_VALUE_ENUM;

typedef struct{
uint8_t Buffer[KEY_FIFO_SIZE];
uint8_t Read;
uint8_t Write;
}KEY_FIFO_TYPE;

static KEY_FIFO_TYPE s_tKeyFIFO;

void bsp_PutKey(uint8_t keyCode)
{
s_tKeyFIFO.Buffer[s_tKeyFIFO.Write] = keyCode;
if(++s_tKeyFIFO.Write >= KEY_FIFO_SIZE)
{
s_tKeyFIFO.Write = 0;
}
}

uint8_t bsp_GetKey(void)
{
uint8_t ret;
if(s_tKeyFIFO.Read == s_tKeyFIFO.Write)
{
ret = KEY_NONE;
}else{
ret = s_tKeyFIFO.Buffer[s_tKeyFIFO.Read];
if(++s_tKeyFIFO.Read>=KEY_FIFO_SIZE)
{
s_tKeyFIFO.Read=0;
}
}
return ret;
}



typedef struct{
uint8_t (*IsKeyDownFunc)(void);

uint8_t FilterCount; // 滤波计数
uint16_t LongCount; // 长按有效计数值
uint16_t LongTime; // 按下持续时间,0:不检测长按
uint8_t State; // 当前按键状态

uint8_t RepeatSpeed;// 连续按键周期
uint8_t RepeatCount;// 连续按键计数器
}KEY_TYPE;

static KEY_TYPE s_tBtn[KEY_COUNT];

static uint8_t IsKeyDown1(void)
{
uint8_t flag=0;
if(gpio_get_level(KEY1_Pin)==0)
flag = 1;
return flag;
}

static void bsp_InitKeyVar(void)
{
uint8_t i=0;
s_tKeyFIFO.Read = 0;
s_tKeyFIFO.Write = 0;
for(i=0;i<KEY_COUNT;i++)
{
s_tBtn[i].LongTime = KEY_LONG_TIME;
s_tBtn[i].FilterCount = KEY_FILTER_TIME/2;
s_tBtn[i].State = 0;

s_tBtn[i].RepeatSpeed = 0;
s_tBtn[i].RepeatCount = 0;
}

s_tBtn[0].IsKeyDownFunc = IsKeyDown1;

}

static void bsp_InitKeyHard(void)
{
// 配置GPIO结构体
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_ANYEDGE; // 下降沿和上升沿触发中断
io_conf.pin_bit_mask = 1 << KEY1_Pin; // 设置GPIO号
io_conf.mode = GPIO_MODE_INPUT; // 模式输入
io_conf.pull_up_en = GPIO_PULLUP_ENABLE; // 端口上拉使能
gpio_config(&io_conf);
}

void bsp_InitKey(void)
{
bsp_InitKeyVar();
bsp_InitKeyHard();
}

static void bsp_DetectKey(uint8_t i)
{
KEY_TYPE *pBtn;
pBtn = &s_tBtn[i];
if(pBtn->IsKeyDownFunc())
{
// 短按
if(pBtn->FilterCount<KEY_FILTER_TIME)
{
pBtn->FilterCount = KEY_FILTER_TIME;
}else if(pBtn->FilterCount<2*KEY_FILTER_TIME)
{
pBtn->FilterCount++;
}else{
// 之前未按下
if(pBtn->State==0)
{
pBtn->State = 1;
bsp_PutKey((uint8_t)(3*i+1));

}else{
// 1s以内无需处理
}

// 如果支持长按
if(pBtn->LongTime>0)
{
if(pBtn->LongCount<pBtn->LongTime)
{
// 单次长按
if(++pBtn->LongCount==pBtn->LongTime)
{
bsp_PutKey((uint8_t)(3*i+3));
}
}else{
// 超出长按阈值-->>>连发
if(pBtn->RepeatSpeed>0)
{
if(++pBtn->RepeatCount>=pBtn->RepeatSpeed)
{
pBtn->RepeatCount=0;
bsp_PutKey((uint8_t)(3*i+1));
}
}
}
}
}
}else{
// 判断按键是否弹起
if(pBtn->FilterCount>KEY_FILTER_TIME){
pBtn->FilterCount=KEY_FILTER_TIME;
}else if(pBtn->FilterCount!=0){
pBtn->FilterCount--;
}else{
if(pBtn->State==1)
{
pBtn->State = 0;
bsp_PutKey((uint8_t)(3*i+2));
}
}

pBtn->LongCount=0;
pBtn->RepeatCount=0;
}
}

void bsp_KeyScan(void)
{
uint8_t i=0;
for(i=0;i<KEY_COUNT;i++)
{
bsp_DetectKey(i);
}
}

// 任务优先级: 4
void TSK_KeyScan10ms(void *arg)
{
for(;;)
{
bsp_KeyScan();
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}

// TSK_UserIF
// 接口消息处理(获取键值,LED等闪烁)
// 优先级:1
void TSK_UserIF(void *arg)
{
for(;;)
{
uint8_t val = bsp_GetKey();
if(val!=0)
{
switch(val)
{
case K1_DOWN:
ESP_LOGI("KEY查询方式", "按键按下,输入键值:%d",val);
break;
case K1_UP:
ESP_LOGI("KEY查询方式", "按键弹起,输入键值:%d",val);
break;
case K1_LONG:
ESP_LOGI("KEY查询方式", "按键长按,输入键值:%d",val);
break;
}
}
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}

void app_main()
{
ESP_LOGW("KEY查询方式",">>>>==== GPIO初始化... ====<<<<");
bsp_InitKey();
ESP_LOGW("KEY查询方式",">>>>==== 创建KEY轮询任务... ====<<<<");
xTaskCreate(TSK_KeyScan10ms,"KeyScan",1024*2,NULL,4,NULL);
xTaskCreate(TSK_UserIF,"Task_LOG",1024*2,NULL,1,NULL);
}


效果

KEY查询采样

2.中断方式