如何基于STM32F407 Discovery计算按键按压时长?
STM32F407按键按压时长检测实现方案
核心思路
用32位定时器实现高精度计时,主循环采用非阻塞逻辑处理LED闪烁,通过检测按键的按下/松开状态变化记录并计算按压时长,松开按键后根据时长阈值执行对应LED闪烁周期调整。
硬件配置
- 按键:PA0(开发板USER按键)、PB0,配置为上拉输入(按键按下时接地)
- LED:PD13(开发板LD2),配置为推挽输出
- 定时器:TIM2,配置为1ms步进计数(适配168MHz系统时钟)
代码实现
1. 定时器初始化(TIM2)
#include "stm32f4xx.h" void TIM2_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 168MHz时钟 → 预分频167 + 自动重装载999 = 1ms计数一次 TIM_TimeBaseStructure.TIM_Prescaler = 167; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = 999; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_Cmd(TIM2, ENABLE); // 启动定时器 }
2. GPIO初始化(按键+LED)
void GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOD, ENABLE); // 按键PA0、PB0:上拉输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_Init(GPIOB, &GPIO_InitStructure); // LED PD13:推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOD, &GPIO_InitStructure); }
3. 主循环逻辑(非阻塞处理)
uint8_t key1_pressed = 0; // PA0按键按下标记 uint32_t key1_start_time = 0; uint8_t key2_pressed = 0; // PB0按键按下标记 uint32_t key2_start_time = 0; uint32_t led_blink_period = 500; // 默认LED闪烁周期500ms uint32_t last_led_toggle_time = 0; int main(void) { SystemInit(); // 初始化系统为168MHz TIM2_Init(); GPIO_Init(); while(1) { // 非阻塞LED闪烁 if(TIM_GetCounter(TIM2) - last_led_toggle_time >= led_blink_period) { last_led_toggle_time = TIM_GetCounter(TIM2); GPIO_ToggleBits(GPIOD, GPIO_Pin_13); } // PA0按键检测(含消抖) if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == RESET && !key1_pressed) { // 10ms消抖,确认按键稳定按下 uint32_t debounce_start = TIM_GetCounter(TIM2); while(TIM_GetCounter(TIM2) - debounce_start < 10); if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == RESET) { key1_pressed = 1; key1_start_time = TIM_GetCounter(TIM2); } } else if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == SET && key1_pressed) { key1_pressed = 0; uint32_t press_duration = TIM_GetCounter(TIM2) - key1_start_time; // 短按(800-1200ms)、长按(3800-4200ms)阈值 if(press_duration >= 800 && press_duration <= 1200) { led_blink_period = 500; } else if(press_duration >= 3800 && press_duration <= 4200) { led_blink_period = 2000; } } // PB0按键检测(逻辑同PA0,对应不同操作) if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == RESET && !key2_pressed) { uint32_t debounce_start = TIM_GetCounter(TIM2); while(TIM_GetCounter(TIM2) - debounce_start < 10); if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == RESET) { key2_pressed = 1; key2_start_time = TIM_GetCounter(TIM2); } } else if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == SET && key2_pressed) { key2_pressed = 0; uint32_t press_duration = TIM_GetCounter(TIM2) - key2_start_time; if(press_duration >= 800 && press_duration <= 1200) { led_blink_period = 100; } else if(press_duration >= 3800 && press_duration <= 4200) { led_blink_period = 3000; } } } }
关键注意事项
- 按键消抖:添加10ms延迟确认按键状态,避免机械抖动导致的误触发
- 时钟匹配:若系统时钟不是168MHz,需调整TIM2的预分频和自动重装载值,保证1ms计时步进
- 非阻塞设计:所有操作均通过定时器计数差值判断,主循环无阻塞,确保按压期间LED持续工作
- 阈值容错:设置时长容错范围(如短按±200ms),避免按键操作的微小误差导致功能失效
内容的提问来源于stack exchange,提问作者namlı
相关产品推荐
相关产品推荐

