<STM32学习>江协非阻塞按键<HAL库>
STM32学习一、江协基础版非阻塞按键二、江协全功能非阻塞按键一、江协基础版非阻塞按键** Key_GetState ()读 PB1 电平低电平 → 返回 1读 PB11 电平低电平 → 返回 2无按键 → 返回 0****Key_GetNum ()有键值读取 → 清零键值 → 返回数值无键值直接返回 **核心扫描Key_Tick ()计数器 Count 自增 1未到 20ms直接结束等待下一次 1ms 调用达到 20ms清零计数器保存上一次按键状态调用函数读取当前按键状态判断当前松开0 上次按下1 → 赋值键值给 Key_Numstatic 局部变量变量不会销毁值一直保留在内存里下次调用函数用的是上一次留下的值变量值永久保留记忆功能#includestm32f1xx_hal.huint8_tKey_Num0;// 按键键值// 获取按键值读取后自动清零uint8_tKey_GetNum(void){uint8_tTemp;if(Key_Num){TempKey_Num;Key_Num0;returnTemp;}return0;}// 读取按键状态uint8_tKey_GetState(void){if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)GPIO_PIN_RESET){return1;}if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_11)GPIO_PIN_RESET){return2;}return0;}// 按键扫描 20ms消抖 松手触发voidKey_Tick(void){staticuint8_tCount;staticuint8_tCurrState,PrevState;Count;if(Count20){Count0;PrevStateCurrState;CurrStateKey_GetState();if(CurrState0PrevState!0){Key_NumPrevState;}}}回调函数// 定时器溢出中断回调函数1ms定时器voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef*htim){if(htim-InstanceTIM2){Key_Tick();// 1ms调用一次按键扫描}}uint8_tkey;while(1){keyKey_GetNum();// 读取按键if(key1){// PB1按键触发}if(key2){// PB11按键触发}}二、江协全功能非阻塞按键#ifndef__KEY_H#define__KEY_H// 包含STM32标准库核心头文件#includestm32f10x.h/************************* 按键数量与编号定义 *************************/#defineKEY_COUNT4// 总按键数量4个#defineKEY_10// 按键1 编号0#defineKEY_21// 按键2 编号1#defineKEY_32// 按键3 编号2#defineKEY_43// 按键4 编号3/************************* 按键事件标志位定义 *************************/// 位标志一个字节的不同位代表不同事件互不干扰#defineKEY_DOWN0x01// 按键 按下瞬间 标志#defineKEY_UP0x02// 按键 松开瞬间 标志#defineKEY_HOLD0x04// 按键 按住不放 标志#defineKEY_SINGLE0x08// 按键 单击 标志#defineKEY_DOUBLE0x10// 按键 双击 标志#defineKEY_LONG0x20// 按键 长按触发 标志#defineKEY_REPEAT0x40// 按键 长按连发 标志/************************* 函数声明 *************************/voidKey_Init(void);// 按键事件检查函数// 参数n按键编号(KEY_1~KEY_4) 参数Flag要检查的事件标志// 返回值1事件触发 0未触发uint8_tKey_Check(uint8_tn,uint8_tFlag);// 按键扫描核心函数1ms调用一次处理消抖、状态机、事件判断voidKey_Tick(void);#endif// 按键状态宏定义#defineKEY_PRESSED1// 按键按下#defineKEY_UNPRESSED0// 按键松开// 按键计时参数1ms调用一次Key_Tick#defineKEY_TIME_DOUBLE200// 双击判断间隔时间#defineKEY_TIME_LONG2000// 长按触发阈值时间#defineKEY_TIME_REPEAT100// 长按连发间隔时间// 按键事件标志数组存储4个按键的所有触发事件uint8_tKey_Flag[KEY_COUNT];/** * brief 读取指定按键的硬件状态 * param n: 按键编号(KEY_1~KEY_4) * retval 按键状态(KEY_PRESSED/KEY_UNPRESSED) */uint8_tKey_GetState(uint8_tn){if(nKEY_1)// 按键1PB1{if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)0){returnKEY_PRESSED;}}elseif(nKEY_2)// 按键2PB11{if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)0){returnKEY_PRESSED;}}elseif(nKEY_3)// 按键3PB13{if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)0){returnKEY_PRESSED;}}elseif(nKEY_4)// 按键4PB15{if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15)0){returnKEY_PRESSED;}}// 无按键按下/无效按键返回松开returnKEY_UNPRESSED;}/** * brief 检查按键是否触发指定事件 * param n: 按键编号 Flag: 要检查的事件标志 * retval 1:触发 0:未触发 * note 除HOLD事件外其余事件读取后自动清零防止重复触发 */uint8_tKey_Check(uint8_tn,uint8_tFlag){// 判断事件是否触发if(Key_Flag[n]Flag){// 非按住事件读取后清零标志位if(Flag!KEY_HOLD){Key_Flag[n]~Flag;}return1;}return0;}/** * brief 按键核心扫描函数 * note 必须1ms调用一次实现消抖、单击/双击/长按/连发 */voidKey_Tick(void){// 静态变量函数退出后值不丢失用于记忆状态和计时staticuint8_tCount,i;// Count:消抖计时 i:循环变量staticuint8_tCurrState[KEY_COUNT];// 当前按键状态staticuint8_tPrevState[KEY_COUNT];// 上一次按键状态staticuint8_tS[KEY_COUNT];// 按键状态机变量staticuint16_tTime[KEY_COUNT];// 按键计时变量// 所有按键计时递减1ms减1for(i0;iKEY_COUNT;i){if(Time[i]0){Time[i]--;}}// 20ms软件消抖每1ms计数满20ms扫描一次按键Count;if(Count20){Count0;// 清零计数器重新计时// 循环扫描4个按键for(i0;iKEY_COUNT;i){PrevState[i]CurrState[i];// 保存上一次状态CurrState[i]Key_GetState(i);// 读取当前按键状态// 基础按键事件处理 if(CurrState[i]KEY_PRESSED){Key_Flag[i]|KEY_HOLD;// 按住不放标志}else{Key_Flag[i]~KEY_HOLD;// 松开清除按住标志}// 按下瞬间当前按下 上一次松开if(CurrState[i]KEY_PRESSEDPrevState[i]KEY_UNPRESSED){Key_Flag[i]|KEY_DOWN;}// 松开瞬间当前松开 上一次按下if(CurrState[i]KEY_UNPRESSEDPrevState[i]KEY_PRESSED){Key_Flag[i]|KEY_UP;}// 按键状态机处理单击/双击/长按/连发 if(S[i]0)// 状态0空闲状态等待按键按下{if(CurrState[i]KEY_PRESSED){Time[i]KEY_TIME_LONG;// 加载长按计时S[i]1;// 切换到按下等待状态}}elseif(S[i]1)// 状态1按下等待判断长按/短按{if(CurrState[i]KEY_UNPRESSED){Time[i]KEY_TIME_DOUBLE;// 加载双击间隔计时S[i]2;// 切换到双击等待状态}elseif(Time[i]0)// 长按计时到未松开{Time[i]KEY_TIME_REPEAT;// 加载连发计时Key_Flag[i]|KEY_LONG;// 触发长按事件S[i]4;// 切换到长按连发状态}}elseif(S[i]2)// 状态2双击等待判断是否双击{if(CurrState[i]KEY_PRESSED)// 短时间内再次按下{Key_Flag[i]|KEY_DOUBLE;// 触发双击事件S[i]3;// 切换到双击完成状态}elseif(Time[i]0)// 超时未按下{Key_Flag[i]|KEY_SINGLE;// 触发单击事件S[i]0;// 回到空闲状态}}elseif(S[i]3)// 状态3双击完成等待松开复位{if(CurrState[i]KEY_UNPRESSED){S[i]0;// 松开后回到空闲状态}}elseif(S[i]4)// 状态4长按连发状态{if(CurrState[i]KEY_UNPRESSED){S[i]0;// 松开后回到空闲状态}elseif(Time[i]0)// 连发计时到{Time[i]KEY_TIME_REPEAT;// 重新加载连发计时Key_Flag[i]|KEY_REPEAT;// 触发连发事件S[i]4;// 保持连发状态}}}}}