c - 在HAL for STM32中实现单按、长按和双按功能
问题描述
我正在尝试实现单按、双按和长按功能来执行不同的功能。到目前为止,我已经了解了单按和长按的逻辑,但我不知道如何检测双按。至于代码,我已经使用计数器实现了单按和长按,但代码只停留在第一个 if 条件下。
bool single_press = false;
bool long_press = false;
if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13))
{
HAL_TIM_Base_Start(&htim2);
if ((TIM2->CNT == 20) && (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)))
{
single_press = true;
long_press = false;
}
else if ((TIM2->CNT == 799) && (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)))
{
single_press = true;
long_press = true;
}
HAL_TIM_Base_Stop(&htim2);
}
if (single_press == true && long_press == false)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, 1);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, 0);
}
else if (single_press == true && long_press == true)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 0);
}
}
我正在尝试实现一种情况,如果我按下键 20 毫秒(单次按下)PB0 会变高一秒钟,如果我按下键 800 毫秒 PB7 会变高一秒钟。但是,在运行程序时,当我按下按钮时,无论我按住按钮多长时间,PB0 都会变为高电平,而 PB7 则保持低电平。所以我想我有两个问题:
- 如何编辑我的代码,以便单按 PB0 变高,长按 PB7 变高?
- 如何实现双按功能?
谢谢!
解决方案
不要使用延迟作为开始。一直处于延迟状态时,您看不到按钮在做什么(或做任何其他有用的事情)。相反,您需要不断地轮询(或使用中断)按钮状态,并在状态发生变化时为其添加时间戳,并根据时间做出行动决策。
首先,您需要一个强大的按钮状态检测和去抖动功能。有多种方法。一个例子:
bool buttonState()
{
static const uint32_t DEBOUNCE_MILLIS = 20 ;
static bool buttonstate = HAL_GPIO_ReadPin( GPIOC, GPIO_PIN_13 ) == GPIO_PIN_SET ;
static uint32_t buttonstate_ts = HAL_GetTick() ;
uint32_t now = HAL_GetTick() ;
if( now - buttonstate_ts > DEBOUNCE_MILLIS )
{
if( buttonstate != HAL_GPIO_ReadPin( GPIOC, GPIO_PIN_13 ) == GPIO_PIN_SET )
{
buttonstate = !buttonstate ;
buttonstate_ts = now ;
}
}
return buttonstate ;
}
所以buttonState()
总是立即返回 - 没有延迟,但在状态更改后重新读取按钮会保持 20 毫秒,以防止将开关弹跳误解为多个状态更改。
然后你需要一个按钮状态轮询函数来检测按钮按下和按钮向上事件的时间。这样:
____________________________
____| |_____________
<----long-press min-->
^
|_Long press detected
______ _____
____| |___| |_________________________
^
|_Double press detected
______
____| |___________________________________
<------->
^ ^
| |_Single press detected
|_ Double press gap max.
请注意,单次按下是在按下按钮后经过太长时间后才检测到的,因为它是双击。以下可能需要一些调试(未经测试)作为说明性处理:
typedef enum
{
NO_PRESS,
SINGLE_PRESS,
LONG_PRESS,
DOUBLE_PRESS
} eButtonEvent ;
eButtonEvent getButtonEvent()
{
static const uint32_t DOUBLE_GAP_MILLIS_MAX = 250 ;
static const uint32_t LONG_MILLIS_MIN = 800 ;
static uint32_t button_down_ts = 0 ;
static uint32_t button_up_ts = 0 ;
static bool double_pending = false ;
static bool long_press_pending = false ;
static bool button_down = false ; ;
eButtonEvent button_event = NO_PRESS ;
uint32_t now = HAL_GetTick() ;
// If state changed...
if( button_down != buttonState() )
{
button_down = !button_down ;
if( button_down )
{
// Timestamp button-down
button_down_ts = now ;
}
else
{
// Timestamp button-up
button_up_ts = now ;
// If double decision pending...
if( double_pending )
{
button_event = DOUBLE_PRESS ;
double_pending = false ;
}
else
{
double_pending = true ;
}
// Cancel any long press pending
long_press_pending = false ;
}
}
// If button-up and double-press gap time expired, it was a single press
if( !button_down && double_pending && now - button_up_ts > DOUBLE_GAP_MILLIS_MAX )
{
double_pending = false ;
button_event = SINGLE_PRESS ;
}
// else if button-down for long-press...
else if( !long_press_pending && button_down && now - button_down_ts > LONG_MILLIS_MIN )
{
button_event = LONG_PRESS ;
long_press_pending = false ;
double_pending = false ;
}
return button_event ;
}
最后你需要经常轮询按钮事件:
int main()
{
for(;;)
{
// Check for button events
switch( getButtonEvent() )
{
case NO_PRESS : { ... } break ;
case SINGLE_PRESS : { ... } break ;
case LONG_PRESS : { ... } break ;
case DOUBLE_PRESS : { ... } break ;
}
// Do other work...
}
}
查看如何没有延迟,让您可以实时检查按钮事件并进行其他工作。显然,“其他工作”也必须在没有过多延迟的情况下执行,否则会弄乱您的按钮事件时间。因此,例如,要在单次按下时实现 1 秒输出,您可能需要:
case SINGLE_PRESS :
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1);
single_press_ts = now ;
} break ;
然后在开关/案例之后:
if( now - single_press_ts > 1000 )
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 0);
}
如果这是一个问题,那么您需要考虑使用中断来处理按钮事件——将其与去抖动处理结合起来,或者使用 RTOS 调度程序并轮询任务中的按钮事件。
推荐阅读
- mysql - Sequelize 在查询中包含多个表
- python-3.x - 我们如何在 python 中使用 selenium 和 webdriver 来获取所有产品名称、价格和评级?假设我们在畅销书的亚马逊页面上
- node.js - Pupateer page.evaluate 解析网站时随机停止工作
- python - 我不明白为什么我的 Port Scanner Python 程序出错
- c# - File.Create c# 中的 System.UnauthorizedAccessException
- php - 记录在 40 秒后显示,有时在查询期间丢失与 MySQL 服务器的连接
- r - 删除具有相同值但在 R 中的不同列中的重复行
- scala - 为什么quill使用IDEA找不到driverClassName和jdbcUrl?
- php - 下面常量的PHP抽象类访问
- ios - 布局更改时如何正确调整 PKDrawing 的大小?