微控制器外接按键按压时长检测及对应LED点亮功能实现问询
按键按压时长检测实现方案(PIC/STM32)
实现思路
- 硬件连接:按键一端接GPIO,另一端接地,GPIO配置为内部上拉(省去额外下拉电阻);LED通过限流电阻接GPIO(GPIO设为推挽输出,可选择低/高电平点亮,根据接线调整)。
- 消抖处理:机械按键按压/释放时会产生10-20ms的抖动,必须做消抖——新手可以用定时器连续采样(多次状态一致才判定有效),也可以用短延时快速实现,定时器采样更规范。
- 计时逻辑:用定时器生成1ms固定时间基准,检测到按键按下(消抖后)开始累计计时;按键释放时根据累计时长触发对应LED动作,也可以在按压过程中实时判断时长并点亮LED。
- 状态管理:用变量记录按键当前状态(按下/释放)、计时起始标志、累计时长,避免重复触发逻辑。
代码示例
STM32(HAL库)
假设使用STM32F103,CubeMX配置:
- PA0:按键输入,开启内部上拉
- PA1、PA2:LED输出,推挽模式
- TIM2:1ms定时中断(预分频71,自动重装载值999,基于72MHz系统时钟)
#include "stm32f1xx_hal.h" /* 全局变量 */ uint8_t key_state = 0; // 0:释放,1:按下(已消抖) uint32_t press_duration = 0; // 按压时长(ms) /* TIM2中断回调函数 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM2) { if(key_state == 1) // 按键处于按下状态,累计时长 { press_duration++; } } } /* 按键扫描与消抖 */ void Key_Scan(void) { static uint8_t key_buf = 0; key_buf = (key_buf << 1) | HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0); // 连续8次采样为低电平(上拉模式下按键按下为低),判定有效按下 if(key_buf == 0x00 && key_state == 0) { key_state = 1; press_duration = 0; // 重置计时 } // 连续8次采样为高电平,判定有效释放 else if(key_buf == 0xFF && key_state == 1) { key_state = 0; // 根据时长控制LED if(press_duration >= 1000 && press_duration < 2000) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); } else if(press_duration >= 2000) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); } else // 短按,熄灭所有LED { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); } } } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM2_Init(); HAL_TIM_Base_Start_IT(&htim2); // 启动定时器中断 while (1) { Key_Scan(); HAL_Delay(10); // 扫描间隔,配合消抖采样 } }
PIC(XC8编译器,PIC16F877A)
硬件配置:
- RB0:按键输入,开启内部上拉(通过OPTION_REG配置)
- RB1、RB2:LED输出(推挽模式)
- TMR0定时器:1ms定时中断(预分频64,基于4MHz系统时钟,TMR0初始值设为6)
#include <xc.h> // 配置位(根据PIC型号调整) __CONFIG(FOSC_HS & WDTE_OFF & PWRTE_ON & BOREN_ON & LVP_OFF & CPD_OFF & WRT_OFF & CP_OFF); #define _XTAL_FREQ 4000000 /* 全局变量 */ bit key_state = 0; // 0:释放,1:按下 unsigned int press_duration = 0; // 按压时长(ms) /* 定时器0中断服务程序 */ void __interrupt() isr(void) { if(T0IF) { TMR0 = 6; // 重置定时器值,保证1ms中断周期 T0IF = 0; // 清除中断标志 if(key_state == 1) { press_duration++; } } } /* 按键扫描与消抖 */ void Key_Scan(void) { static unsigned char key_buf = 0; key_buf = (key_buf << 1) | !RB0; // RB0上拉,按下为低,取反后1表示按下 if(key_buf == 0xFF && key_state == 0) // 连续8次采样为按下状态 { key_state = 1; press_duration = 0; } else if(key_buf == 0x00 && key_state == 1) // 连续8次采样为释放状态 { key_state = 0; // 根据时长控制LED if(press_duration >= 1000 && press_duration < 2000) { RB1 = 1; RB2 = 0; } else if(press_duration >= 2000) { RB1 = 1; RB2 = 1; } else { RB1 = 0; RB2 = 0; } } } void main(void) { TRISB0 = 1; // RB0设为输入 TRISB1 = 0; // RB1设为输出 TRISB2 = 0; // RB2设为输出 // 配置TMR0定时器 OPTION_REG = 0b00000110; // 预分频64,TMR0时钟来自Fosc/4 TMR0 = 6; T0IE = 1; // 使能TMR0中断 GIE = 1; // 使能全局中断 RB1 = 0; RB2 = 0; while(1) { Key_Scan(); __delay_ms(10); } }
内容的提问来源于stack exchange,提问作者Bhaskar Jha
相关产品推荐
相关产品推荐

