最近进行N32WB031 mcu的项目开发,实现按键处理这块进行了测试验证:
1. 程序使用了systick来计数
2. 使用ns_timer 作为软件定时器。
3. 使用按键中断来进行sleep唤醒。
key.c 代码:
#include "stdio.h"
#include "n32wb03x.h"
#include "app_key.h"
#include "ns_delay.h"
#include "ns_timer.h"
#include "hx3918_software_i2c.h"
#include "app_lcd_ui.h"
#include "app_printf.h"
#include "rwip.h"
#include "ns_ble.h"
#include "ns_sleep.h"
#include "ns_log.h"
#include "hx3918.h"
//#include "key.h"
extern void delay_n_ms(uint32_t count);
timer_hnd_t timer1s_id = NS_TIMER_INVALID_HANDLER;
timer_hnd_t timer3s_id = NS_TIMER_INVALID_HANDLER;
timer_hnd_t timer10ms_id = NS_TIMER_INVALID_HANDLER;
timer_hnd_t timer5s_id = NS_TIMER_INVALID_HANDLER;
static uint8_t button_just_released = 0;
// 按键状态变量
static ButtonState_t button_state = BUTTON_RELEASED;
uint8_t key_flag = false;
// 读取按键状态
uint8_t Read_Button(void) {
return GPIO_ReadInputDataBit(KEY_INPUT_PORT, KEY_INPUT_PIN) == RESET; // 假设按键按下时引脚为低电平
}
//按键事件回调函数
void KeyEventHandler(ButtonState_t state)
{
switch (state) {
case BUTTON_SHORT_PRESSED:
// 单击
// printf("One_click,\n");
lpuart_release_debug("One_click,\n",12);
app_lcd_shortkey_handle();
break;
case BUTTON_DOUBLE_CLICKED:
// printf("double_click,\n");
lpuart_release_debug("double_click,\n",12);
app_lcd_doublekey_handle();
break;
case BUTTON_LONG_PRESSED:
lpuart_release_debug("long_click,\n",12);
// printf("long_click,\n");
app_longkey_handle();
break;
default:
break;
}
}
void key_timeout_handle()
{
// 如果timer未取消则认为是短按
ns_timer_cancel(timer1s_id);
timer1s_id = NS_TIMER_INVALID_HANDLER;
button_state = BUTTON_SHORT_PRESSED;
KeyEventHandler(button_state);
}
void key5s_timeout_handle()
{
ns_timer_cancel(timer5s_id);
timer5s_id = NS_TIMER_INVALID_HANDLER;
key_flag = false;
}
void longkey_timeout_handle()
{
ns_timer_cancel(timer3s_id);
timer3s_id = NS_TIMER_INVALID_HANDLER;
if(Read_Button()) //长按
{
KeyEventHandler(BUTTON_LONG_PRESSED);
button_just_released = 0; //清除双击flag
}
}
uint8_t KeyCallHandle()
{
return key_flag;
}
// 按键处理函数
void ProcessKey(void) {
static uint32_t pressCount = 0;
uint32_t current_time = GetTick(); // 获取当前时间(需要配置HAL_Tick)
if (Read_Button())
{ // 按键被按下
if (button_state == BUTTON_RELEASED)
{ // 如果之前是释放状态
button_state = BUTTON_PRESSED; // 更新按键状态为按下
last_press_time = current_time; // 记录按键按下的时间
ns_timer_cancel(timer1s_id);
ns_timer_cancel(timer3s_id);
ns_timer_cancel(timer5s_id);
timer1s_id = NS_TIMER_INVALID_HANDLER;
timer5s_id = ns_timer_create(5000, (timer_callback_t)key5s_timeout_handle);
timer3s_id = ns_timer_create(long_press_time, (timer_callback_t)longkey_timeout_handle);
}
}
else
{ // 按键被释放
if (button_state == BUTTON_PRESSED)
{ // 如果之前是按下状态
button_state = BUTTON_RELEASED; // 更新按键状态为释放
ns_timer_cancel(timer3s_id); //清除long_key timer
timer3s_id = NS_TIMER_INVALID_HANDLER;
uint32_t press_duration = current_time - last_press_time; // 计算按键按下的持续时间
printf("pre:%d, cur:%d, last:%d",press_duration,current_time ,last_press_time);
timer1s_id = ns_timer_create(short_press_time+10, (timer_callback_t)key_timeout_handle);
if (press_duration < short_press_time && button_just_released)
{
// 如果在双击间隔内再次释放,并且之前已经释放过,则认为是双击
ns_timer_cancel(timer1s_id);
timer1s_id = NS_TIMER_INVALID_HANDLER;
button_state = BUTTON_DOUBLE_CLICKED;
KeyEventHandler(button_state);
button_just_released = 0; //
}
else if (press_duration >= long_press_time)
{
// 如果按下时间超过长按时间,则认为是长按
ns_timer_cancel(timer1s_id);
timer1s_id = NS_TIMER_INVALID_HANDLER;
// button_state = BUTTON_LONG_PRESSED;
button_just_released = 0; //
}
else
{
button_just_released = 1; // 标记按键刚刚被释放
}
// 等待一段时间以确保不是误触或抖动
delay_n_ms(10); // 延迟10ms
if (Read_Button())
{
// 如果在延迟后按键仍然被按下,则认为是长按的一部分,不处理双击和短按
button_just_released = 0;
}
}
}
// 根据需要重置状态或执行其他逻辑
if (button_state == BUTTON_SHORT_PRESSED ||
button_state == BUTTON_LONG_PRESSED ||
button_state == BUTTON_DOUBLE_CLICKED) {
// 处理完短按、长按或双击后,重置状态
button_state = BUTTON_RELEASED;
button_just_released = 0;
}
}
void EXTI0_1_IRQ_Handler(void);
/**
* @brief Configures key port.
* @param GPIOx x can be A to G to select the GPIO port.
* @param Pin This parameter can be GPIO_PIN_0~GPIO_PIN_15.
*/
void KeyInputExtiInit(GPIO_Module* GPIOx, uint16_t Pin)
{
GPIO_InitType GPIO_InitStructure;
EXTI_InitType EXTI_InitStructure;
NVIC_InitType NVIC_InitStructure;
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
/* Enable the GPIO Clock */
if (GPIOx == GPIOA)
{
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA | RCC_APB2_PERIPH_AFIO, ENABLE);
}
else if (GPIOx == GPIOB)
{
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB | RCC_APB2_PERIPH_AFIO, ENABLE);
}
else
{
return;
}
/*Configure the GPIO pin as input floating*/
if (Pin <= GPIO_PIN_ALL)
{
GPIO_InitStruct(&GPIO_InitStructure);
GPIO_InitStructure.Pin = Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.GPIO_Pull = GPIO_PULL_UP;
GPIO_InitPeripheral(GPIOx, &GPIO_InitStructure);
}
/*Configure key EXTI Line to key input Pin*/
GPIO_ConfigEXTILine(KEY_INPUT_PORT_SOURCE, KEY_INPUT_PIN_SOURCE);
ModuleIrqRemoval(EXTI0_1_IRQn);
ModuleIrqRegister(EXTI0_1_IRQn,EXTI0_1_IRQ_Handler);
/*Configure key EXTI line*/
EXTI_InitStructure.EXTI_Line = KEY_INPUT_EXTI_LINE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitPeripheral(&EXTI_InitStructure);
/*Set key input interrupt priority*/
NVIC_InitStructure.NVIC_IRQChannel = KEY_INPUT_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
uint32_t key10ms_timeout_handle()
{
ns_timer_cancel(timer10ms_id);
if (Read_Button())
{
key_flag = true;
lpuart_release_debug("\nIRQ1,",6);
}
}
// 中断服务程序(ISR)
void EXTI0_1_IRQ_Handler(void) // EXTIx 是与您的按键GPIO引脚相关的中断处理函数
{
if (EXTI_GetITStatus(KEY_INPUT_EXTI_LINE) != RESET) // KEY_EXTI_LINE 是您的按键GPIO引脚的中断线
{
ns_timer_cancel(timer10ms_id);
timer10ms_id = ns_timer_create(10, (timer_callback_t)key10ms_timeout_handle);
EXTI_ClrITPendBit(KEY_INPUT_EXTI_LINE); // 清除中断标志
lpuart_release_debug("\nIRQ,",5);
// 检查按键状态
}
}
uint32_t tick;
//void HAL_InitTick(uint32_t TickPriority)
//uint32_t key1ms_timeout_handle();
void HAL_InitTick()
{
// timer1ms_id = ns_timer_create(1, (timer_callback_t)key1ms_timeout_handle);
//启动systick_config定时器,并将其定时中断时间设置为1ms
if(SysTick_Config(SystemCoreClock/1000) == 0)
{
//设置systick中断优先级
// NVIC_SetPriority(SysTick_IRQn, 0);
lpuart_release_debug("systick is On\n", 14);
}
else
{
lpuart_release_debug("systick is Fail\n", 16);
}
}
uint32_t GetTick(void)
{
return tick;
}
/* 系统中断处理函数 */
void SysTick_Handler(void)
{
/* 定义时钟中断处理函数 */
tick++;
}
void version_print()
{
char ver_buf[26] ;
snprintf(ver_buf, sizeof(ver_buf), "hx3918_test_ver: %s\r\n",HX3918_TEST_VER);
lpuart_release_debug(ver_buf,strlen(ver_buf));
}
void key_init()
{
HAL_InitTick();
app_hx3918_io_init();
KeyInputExtiInit(KEY_INPUT_PORT,KEY_INPUT_PIN);
// version_print();
}
.h代码:
#ifndef __APP_KEY_H
#define __APP_KEY_H
#include "n32wb03x.h"
#define KEY_N32_STB
#ifdef KEY_N32_STB
#define KEY_INPUT_PORT GPIOA
#define KEY_INPUT_PIN GPIO_PIN_0
#define KEY_INPUT_EXTI_LINE EXTI_LINE0
#define KEY_INPUT_PORT_SOURCE GPIOA_PORT_SOURCE
#define KEY_INPUT_PIN_SOURCE GPIO_PIN_SOURCE0
#define KEY_INPUT_IRQn EXTI0_1_IRQn
#else
#define KEY_INPUT_PORT GPIOB
#define KEY_INPUT_PIN GPIO_PIN_1
#define KEY_INPUT_EXTI_LINE EXTI_LINE1
#define KEY_INPUT_PORT_SOURCE GPIOB_PORT_SOURCE
#define KEY_INPUT_PIN_SOURCE GPIO_PIN_SOURCE1
#define KEY_INPUT_IRQn EXTI0_1_IRQn
#endif
// 按键状态变量
typedef enum {
BUTTON_RELEASED = 0,
BUTTON_PRESSED,
BUTTON_SHORT_PRESSED,
BUTTON_LONG_PRESSED,
BUTTON_DOUBLE_CLICKED
} ButtonState_t;
static uint32_t last_press_time = 0;
static uint32_t short_press_time = 400; // 短按时间阈值,单位ms
static uint32_t double_click_interval = 1000; // 双击间隔,单位ms
static uint32_t long_press_time = 3000; // 长按时间,单位ms
#if 1
void KeyInputExtiInit(GPIO_Module* GPIOx, uint16_t Pin);
void KeyEventHandler(ButtonState_t state) ;
//void HAL_InitTick(void);
uint32_t GetTick(void);
void key_init(void);
void ProcessKey(void);
uint8_t KeyCallHandle(void);
#endif
#endif