STM32F407VG短按长按功能异常求助:仅触发短按
STM32F407VG按键长短按功能排查与修复
我正在用STM32F407VG开发板做产品设计,需要实现按键的短按(控制亮度)和长按(开关显示,时长约2-3秒)功能。目前用板载用户按键、TIM6定时器(每20ms触发一次中断)实现逻辑,但只能触发短按,处理不了长按。已经知道还没加按键防抖,后续会补充。下面是相关代码,帮忙排查问题:
volatile uint32_t buttonPressTime = 0; volatile uint8_t buttonPressed = 0; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM6_Init(void); #define SHORT_PRESS_THRESHOLD 50 // 示例:50毫秒 #define LONG_PRESS_THRESHOLD 3000 // 3秒 int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM6_Init(); HAL_TIM_Base_Start_IT(&htim6); // 启用中断启动TIM6 while (1) { if (buttonPressed) { if (buttonPressTime < SHORT_PRESS_THRESHOLD) { // 处理短按 printf("Short Press"); } else if (buttonPressTime >= LONG_PRESS_THRESHOLD) { // 处理长按 printf("Long presss"); } buttonPressed = 0; // 重置标志位 } } }
TIM6初始化代码
static void MX_TIM6_Init(void) { TIM_MasterConfigTypeDef sMasterConfig = {0}; htim6.Instance = TIM6; htim6.Init.Prescaler = 55; htim6.Init.CounterMode = TIM_COUNTERMODE_UP; htim6.Init.Period = 59999; htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim6) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK) { Error_Handler(); } }
定时器回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM6) { if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) { buttonPressTime++; // 累加按键按下时长计数 } else { if (buttonPressTime > 0) { buttonPressed = 1; // 设置按键按下标志位 } buttonPressTime = 0; // 重置时长计数 } } }
问题根源分析
时间阈值单位不匹配
你定义的阈值是毫秒,但buttonPressTime的单位是20ms(TIM6中断周期),不是毫秒。比如长按阈值3000ms对应的计数应该是3000/20=150,当前用3000直接和计数比较,永远达不到触发条件。长按触发逻辑缺失
当前只有按键松开时才会设置buttonPressed标志,长按过程中按键一直按住,主循环永远检测不到长按事件,必须在按键持续按下时主动判断是否达到长按阈值。重复触发与逻辑冲突
长按触发后未及时重置计数,可能导致后续误判;按键松开时如果已经触发过长按,应该避免再触发短按。
修改后的代码示例
主函数调整
volatile uint32_t buttonPressTime = 0; volatile uint8_t buttonPressed = 0; volatile uint8_t longPressTriggered = 0; // 新增长按触发标志 void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM6_Init(void); // 调整阈值为20ms的倍数 #define SHORT_PRESS_COUNT 3 // 3*20ms=60ms,接近原50ms需求 #define LONG_PRESS_COUNT 150 // 150*20ms=3000ms,符合长按需求 int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM6_Init(); HAL_TIM_Base_Start_IT(&htim6); while (1) { if (buttonPressed) { // 仅在未触发过长按且计数符合短按时,触发短按逻辑 if (!longPressTriggered && buttonPressTime <= SHORT_PRESS_COUNT) { printf("短按触发\n"); } buttonPressed = 0; longPressTriggered = 0; } if (longPressTriggered) { // 长按逻辑(如需持续触发可移除标志位重置) printf("长按触发\n"); longPressTriggered = 0; } } }
回调函数调整
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM6) { if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) { buttonPressTime++; // 达到长按阈值且未触发过,设置长按标志 if (buttonPressTime >= LONG_PRESS_COUNT && !longPressTriggered) { longPressTriggered = 1; buttonPressTime = 0; // 重置计数避免重复触发 } } else { if (buttonPressTime > 0) { buttonPressed = 1; // 按键松开触发短按检测 } buttonPressTime = 0; } } }
额外说明
原TIM6配置的定时周期确实是20ms(基于168MHz系统时钟计算),这部分无需修改。后续添加防抖时,可在回调函数中增加连续多次状态检测逻辑,避免按键抖动导致误触发。
内容的提问来源于stack exchange,提问作者Muhammad Haris
相关产品推荐
相关产品推荐

