当前位置:网站首页>How to realize the short press and long press detection of the button?
How to realize the short press and long press detection of the button?
2022-08-05 09:13:00 【Embedded Linux,】
Keys are often used in electronic products,尤其是经常需要MCU判断短按和长按these two actions,In this article, we will discuss this topic specifically.

Talking about theory is boring,We still combine the practical application to illustrate.之前写过一篇关于《CH573第一篇:Realize the selfie stick bluetooth remote control1》的文章,The default function of the example is to continuously send data after the Bluetooth connection,So keep taking pictures.The actual remote control is usually a button press,控制一次,We are here to implement that functionality.

There are only two buttons on the board,一个是RESET按键,一个是DOWNLOAD按键,我们使用DOWNLAOD按键,One end of the button is connectedGND,另外一端接CH573的PB22引脚.

There is one in the schematicNC的C5,But I didn't find it on the actual board,可能是版本不一致.
提前说明一下:CH573ran in the codeTMOS(Task Management Operating System),Can be understood as a simple operating system,So the code below looks slightly different from general bare metal code,But the core idea is the same,It is also easy to port to other places,Just need to rewrite the timer part of it.
Initially I did this,把PB22配置为上拉输入,Enable falling edge interrupt,in the interrupt service function,Start an event,Perform Bluetooth transmission.代码如下:
void Key_Init()
{
GPIOB_ModeCfg( GPIO_Pin_22, GPIO_ModeIN_PU );
GPIOB_ITModeCfg( GPIO_Pin_22, GPIO_ITMode_FallEdge );
PFIC_EnableIRQ( GPIO_B_IRQn );
}void GPIOB_IRQHandler( void )
{
if(GPIOB_ReadPortPin(GPIO_Pin_22)==0)
{
GPIOB_ClearITFlagBit( GPIO_Pin_22);
tmos_set_event( hidEmuTaskId, START_REPORT_EVT );
}
}Writing this way works,但是有问题,It is often that one click is misjudged as multiple clicks.The reason should be clear to everyone,Because the keys vibrate,So it is possible to enter multiple entry interrupts with one press.
The ideal press-The bounce waveform looks like this:

But actually due to the existence of button jitter,The actual waveform might look like this:

If you don't believe me, you can connect an oscilloscope to see it,Or software verification,比如在GPIOin the interrupt service function,设置一个全局变量,Make it add every time it enters an interrupt1,Press the key to observe the value of this variable.
So how to eliminate jitter?One way is hardware debounce,That is, a small capacitor is connected in parallel at both ends of the button(The size of the capacitance is determined by the mechanical properties of the button),Another method is the software debounce that we are going to focus on today.
方法一:Commonly used delay function
Add an eg in the interrupt service function10ms的延时函数,The length of the delay time depends on the actual key characteristics used,As long as the delay time is slightly larger than the jitter time.原理很简单,Adding a delay avoids this period of jitter,Determine the pin level after a delay,If it is low, it means that it is pressed.
void GPIOB_IRQHandler( void )
{
if(GPIOB_ReadPortPin(GPIO_Pin_22)==0)
{
mDelaymS(10);
if(GPIOB_ReadPortPin(GPIO_Pin_22)==0)
tmos_set_event( hidEmuTaskId, START_REPORT_EVT );
GPIOB_ClearITFlagBit( GPIO_Pin_22);
}
}这个方法很简单,But the downside is the delayMCU资源.尤其是这里的BLE应用,A long execution time in the interrupt service function will cause the Bluetooth connection to be interrupted,所以这里不能这么用,I actually tested that when the button is pressed faster, it is easy to cause the Bluetooth connection to be interrupted.
方法二:加定时器
Its principle and method are similar,Just don't block waiting in the interrupt service function,Instead use a timer,代码如下:
void GPIOB_IRQHandler( void )
{
if(GPIOB_ReadPortPin(GPIO_Pin_22)==0)
{
GPIOB_ClearITFlagBit( GPIO_Pin_22);
tmos_stop_task(hidEmuTaskId, START_DEBOUNCE_EVT);
tmos_start_task(hidEmuTaskId, START_DEBOUNCE_EVT,16);
}
}if(events & START_DEBOUNCE_EVT)
{
if(GPIOB_ReadPortPin(GPIO_Pin_22)==0)
{
PRINT("short press\n");
tmos_set_event( hidEmuTaskId, START_REPORT_EVT );
}
return (events ^ START_DEBOUNCE_EVT);
}Its logic is to turn it back on every falling edge of the jitter10ms定时器,Judge after the timer expiresIOLevel state to determine whether the button is pressed.
需要注意的是:10msThe timer is not a periodic timer,它是一次性的,That is, when the time is up, the timer stops.In addition, after each interrupt, let the timer restart from the beginning.If you use other code to implement, pay attention to these two points.
The benefit of this method is not as occupied as adding a delay functionMCU资源.I actually tested this method available,Does not cause interruption of the Bluetooth connection.
The above introduces the method of using the interrupt to judge the short press of the button,It can be seen that the basis for its judgment is the button press(Change from high level to low level)这个状态.Next, on the basis of method 2, we will realize the detection of long press,The basis for judging the long press is to maintain a low level for a period of time after pressing.代码如下:
if(events & START_DEBOUNCE_EVT)
{
if(GPIOB_ReadPortPin(GPIO_Pin_22)==0)
{
PRINT("short press\n");
tmos_set_event( hidEmuTaskId, START_REPORT_EVT );
tmos_start_task( hidEmuTaskId, START_LONGCHECK_TIMER,16 );
}
return (events ^ START_DEBOUNCE_EVT);
}if(events & START_LONGCHECK_TIMER)
{
static int cnt=0;
if(GPIOB_ReadPortPin(GPIO_Pin_22)==0)
{
cnt++;
if(cnt>100)
{
PRINT("long press\n");
tmos_stop_task( hidEmuTaskId, START_LONGCHECK_TIMER);
cnt =0;
}
else
tmos_start_task( hidEmuTaskId, START_LONGCHECK_TIMER,16 );
}
else
{
cnt=0;
tmos_stop_task( hidEmuTaskId, START_LONGCHECK_TIMER );
}
return (events ^ START_LONGCHECK_TIMER);
}实现的逻辑是:When a short press is detected,再开启一个10ms定时器,The level state is judged when the timer expires,如果为低电平,就让cnt变量加1,否则cnt=0,当cnt>100,That is, the low level continues1s认为是长按.I'm here when judging to long press or afterIOGoing high will stop the timer,Otherwise periodic timing,Because there is no need to keep the timer on all the time.
In addition to the above-mentioned interrupt method,还可以使用轮询的方式来实现,代码如下:
void Key_Init()
{
GPIOB_ModeCfg( GPIO_Pin_22, GPIO_ModeIN_PU );
}if(events & START_KEYSCAN_EVT)
{
KeyScan();
tmos_start_task(hidEmuTaskId, START_KEYSCAN_EVT,160);// 100ms执行一次KeyScan()
return (events ^ START_KEYSCAN_EVT);
}bool key_press_flag = false; // 按下标志
bool key_long_press_flag = false; // 长按标志
void KeyScan()
{
if(GPIOB_ReadPortPin(GPIO_Pin_22) == 0) // 低电平
{
if(key_press_flag == false)
tmos_start_task( hidEmuTaskId, START_LONGCHECK_TIMER, 1600 ); // 启动1s定时器
key_press_flag = true; // Set the pressed flag
}
else if(key_press_flag == true) // High level while the key is pressed ,Indicates that it is a pop-up after being pressed
{
key_press_flag = false; // Clear the pressed flag
if(key_long_press_flag == false)// Pop up after a short press
{
tmos_stop_task(hidEmuTaskId, START_LONGCHECK_TIMER);
PRINT("short press\n");
tmos_set_event( hidEmuTaskId, START_REPORT_EVT );
}
else // Bounce up after a long press
{
key_long_press_flag =false;
}
}
else
{
key_press_flag = false;
key_long_press_flag = false;
}
}if(events & START_LONGCHECK_TIMER)
{
key_long_press_flag =true;
PRINT("long press\n");
return (events ^ START_LONGCHECK_TIMER);
}The above code looks a bit confusing at first glance,But after you understand it, you will feel that this implementation logic is still very good,Notes are written,It will not be explained in detail here,I use it in multiple projects.It takes into account debounce and short presses/Long press detection,And the long press can determine the long press/long press to pop up.A short press is a short press when it detects a bounce.In addition, if you want to support multiple long presses at the same time,It is also easy to add.
Polling and interrupting each have their pros and cons,大家可以根据实际情况来选择,Which method do you usually use?


边栏推荐
- 基因数据平台
- Luogu P3368: 【模板】树状数组 2
- 【Excel实战】--图表联动demo_001
- 交换机端口的三种类型详解与hybrid端口实验
- seata源码解析:TM RM 客户端的初始化过程
- Thinking and summary of the efficiency of IT R&D/development process specification
- Science bosses say | Hong Kong rhubarb KaiBin teacher take you unlock the relationship between the matrix and 6 g
- flink cdc支持从oracle dg库同步吗
- express hot-reload
- openpyxl to manipulate Excel files
猜你喜欢

seata源码解析:事务状态及全局锁的存储

Creo 9.0 基准特征:基准平面

Creo 9.0 基准特征:基准轴

Code Audit - PHP

ECCV 2022 Oral 视频实例分割新SOTA:SeqFormer&IDOL及CVPR 2022 视频实例分割竞赛冠军方案...

周报2022-8-4

工程制图知识点

复现一次循环和两次循环

【LeetCode】623. Add a row to the binary tree

Comprehensively explain what is the essential difference between GET and POST requests?Turns out I always misunderstood
随机推荐
tensorflow.keras cannot introduce layers
PAT Grade B-B1020 Mooncake(25)
Pagoda measurement - building small and medium-sized homestay hotel management source code
【LeetCode】623. 在二叉树中增加一行
sql server中 两表查询 平均数 分组
Weekly Report 2022-8-4
The Secrets of the Six-Year Team Leader | The Eight Most Important Soft Skills of Programmers
轩辕实验室丨欧盟EVITA项目预研 第一章(四)
DPU — 功能特性 — 管理系统的硬件卸载
Dynamic memory development (C language)
C语言-数组
sphinx匹配指定字段
Code Audit - PHP
Creo 9.0 基准特征:基准轴
16 kinds of fragrant rice recipes
Why do I recommend using smart async?
ts/js function pass parameter with function writing
Luogu P4588: [TJOI2018]数学计算
PAT乙级-B1020 月饼(25)
手机上流行的各类谜语