本帖最后由 华仔STM32 于 2021-11-22 22:15 编辑
1. 简单按键检测记得开始学习 单片机的时候,写的按键扫描是这样的:
- if(KEY1 == 0)
- {
- delay_ms(20);
- if(KEY1 == 0)
- {
- while(KEY1 == 0);
- // 按键按下处理代码
- }
- }
复制代码
一看,有个20ms消除抖动时间,就是说我要在这里死等20ms,还有等待按键释放,我就是不放,你能怎么样?没办法只能做超时。那我想做长按1s呢?细思极恐,对于实际项目上的应用来说是很糟糕的事情,这不仅会拖慢你整个系统,还会出现,多个按键有时检测不到的问题。有没有更好的办法来实现呢?答案是肯定的,想想,如果这个20ms的延时用定时器来做,不就可以了吗!!!
2. 状态机
首先我们得了解什么是状态机?这个当然是问度娘了!!!
状态机可归纳为4个要素,即 现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。"现态"和"条件"是 因,"动作"和"次态"是 果。详解如下:
现态:是指当前所处的状态。
条件:当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
次态:条件满足后要迁往的新状态。"次态"是相对于"现态"而言的,"次态"一旦被激活,就转变成新的"现态"了。
有了理论的支撑,有没有发现,状态机这种机制,其实可以运行到很多场景的啦,不仅仅局限于按键。
3. 程序
Key.h
- #ifndef __KEY_H
- #define __KEY_H
- #include "wm_hal.h"
-
- #define key1_val HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
- typedef struct{
- uint8_t KEY_Flag; //按键志位
- uint8_t Click; //单击
- uint8_t Double_Click; //双击
- uint8_t Press; //长按
- void (*KEY_Detcet)(void); //按键检测
- } KEY_t;
- typedef enum{
- STA1_KEY_Up = (uint8_t)0x01, //按键弹出
- STA2_KEY_DownShake = (uint8_t)0x02, //按下抖动
- STA3_KEY_Down = (uint8_t)0x03, //按键按下
- STA4_KEY_UpShake =(uint8_t)0x04, //弹起抖动
- }STA_Machine_Status_t;
- typedef struct{
- STA_Machine_Status_t ucSTA_Machine_Status; //状态机状态
- uint16_t volatile ucSTA_Machine_Scan_Timer; //状态机扫描定时器
- uint16_t volatile usKEY1_Double_Click_Timer; //KEY1双击定时器
- uint16_t volatile usKEY1_Press_Timer; //KEY1长按定时器
- } STA_Machine_t;
- #endif
复制代码
Key.c
- #include "wm_hal.h"
- #include "key.h"
- #define Timer_10mS 10
- static void KEY_Detect(void);
- KEY_t KEY1 = {false,false,false,false,KEY_Detect};
- STA_Machine_t STA_Machine = {STA1_KEY_Up,0,0,0};
- /*
- * [url=home.php?mod=space&uid=830701]@name[/url] KEY_Detect
- * [url=home.php?mod=space&uid=2666770]@Brief[/url] 按键开关检测
- * [url=home.php?mod=space&uid=3142012]@param[/url] None
- * @retval None
- *
- */
- static void KEY_Detect(void)
- {
- //间隔10ms检测状态机状态, 10ms->典型的按键消拦时间
- if(STA_Machine.ucSTA_Machine_Scan_Timer >= Timer_10mS)
- {
- //运行有限状态机进行KE1检测 ->单击检测
- switch(STA_Machine.ucSTA_Machine_Status)
- {
- case STA1_KEY_Up: //弹起状态
- {
- //按键无动作
- if(key1_val == 1)
- {
-
- }
- //检测到低电平,状态转至STA2按下抖动
- else{
- STA_Machine.ucSTA_Machine_Status = STA2_KEY_DownShake;
- printf("KEY1 message: key_Up -> key_down_shakern");
- }
- break;
- }
- case STA2_KEY_DownShake: //按键按下抖动状态
- {
- if(key1_val == 1)//如恢复高电平,说明是抖动引起的,回到初始状态
- {
- STA_Machine.ucSTA_Machine_Status = STA1_KEY_Up;
- }
- else//如果继续为低电平,刚说明已经是按下状态
- {
- STA_Machine.ucSTA_Machine_Status = STA3_KEY_Down;//转入按下确认状态
- KEY1.KEY_Flag = 1;
- KEY1.Click = 1;
- printf("KEY1 message:key_down_shake->key_down");
- }
- break;
- }
- case STA3_KEY_Down: //按键按下状态
- {
- if(key1_val == 1) //如果检测到高电压,说明按键按抬起,进入抬起消抖状态
- {
- STA_Machine.ucSTA_Machine_Status = STA4_KEY_UpShake;
- printf("KEY1 message: key_down->keyup_shakern");
- }
- else
- {
- //这里如果继续是低电平,可以进入长按状态的检测,等下期再写:)
-
- }
- break;
- }
- case STA4_KEY_UpShake: //按键抬起消抖状态
- {
- if(key1_val == 1) //如果再检测到是高电平 测确认按键已经抬起,进入抬起的初始状态
- {
- STA_Machine.ucSTA_Machine_Status = STA1_KEY_Up;
- printf("KEY1 message:key_up_shake -》 key_uprn");
- }
- else{
-
- }
- break;
- }
- default:
- STA_Machine.ucSTA_Machine_Status = STA1_KEY_Up;
- }
- STA_Machine.ucSTA_Machine_Scan_Timer = 0; //检测时间清零
- }
-
- }
-
复制代码
wm_it.c
- #include "wm_hal.h"
- extern TIM_HandleTypeDef htim0;
- extern TIM_HandleTypeDef htim1;
- #define readl(addr) ({unsigned int __v = (*(volatile unsigned int *) (addr)); __v;})
- __attribute__((isr)) void CORET_IRQHandler(void)
- {
- readl(0xE000E010);
- HAL_IncTick();
- }
- __attribute__((isr)) void TIM0_5_IRQHandler(void)
- {
- HAL_TIM_IRQHandler(&htim0);
-
- HAL_TIM_IRQHandler(&htim1);
- }
复制代码
main.c
- #include
- #include "wm_hal.h"
- #include "key.h"
- TIM_HandleTypeDef htim0;
- TIM_HandleTypeDef htim1;
- void Error_Handler(void);
- void test_key(void);
- extern KEY_t KEY1;
- extern STA_Machine_t STA_Machine;
- static void TIM0_Init(void);
- static void TIM1_Init(void);
- static void GPIO_Init(void);
- int16_t time;
- int main(void)
- {
- SystemClock_Config(CPU_CLK_160M);
- printf("enter mainrn");
- GPIO_Init();
- TIM0_Init();
- TIM1_Init();
- HAL_TIM_Base_Start_IT(&htim0);
- HAL_TIM_Base_Start_IT(&htim1);
- while(1)
- {
- if(time > 1000)
- {
- HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
- time=0;
- }
- test_key();
- HAL_Delay(5);
- }
- }
- static void TIM0_Init(void)
- {
- htim0.Instance = TIM0;
- htim0.Init.Unit = TIM_UNIT_US;
- htim0.Init.Period = 1000;//1mS
- htim0.Init.AutoReload = TIM_AUTORELOAD_PRELOAD_ENABLE;
- if (HAL_TIM_Base_Init(&htim0) != HAL_OK)
- {
- Error_Handler();
- }
- }
- static void TIM1_Init(void)
- {
- htim1.Instance = TIM1;
- htim1.Init.Unit = TIM_UNIT_US;
- htim1.Init.Period = 1001;//为1000us中断一次
- htim1.Init.AutoReload = TIM_AUTORELOAD_PRELOAD_ENABLE;
- if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
- {
- Error_Handler();
- }
- }
- void HAL_TIM_Callback(TIM_HandleTypeDef *htim)
- {
- //printf("%d ", htim->Instance);
-
- if (htim->Instance == TIM0)
- {
- time++;
- }
- if (htim->Instance == TIM1)
- {
- //printf("%d ", htim->Instance);
- //time1++;
- STA_Machine.ucSTA_Machine_Scan_Timer++;
-
- }
- }
- static void GPIO_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct = {0};
-
- __HAL_RCC_GPIO_CLK_ENABLE();
- GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2;
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
- HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2, GPIO_PIN_SET);
-
- GPIO_InitStruct.Pin = GPIO_PIN_0;
- GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
- GPIO_InitStruct.Pull = GPIO_PULLUP;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
-
- // HAL_NVIC_SetPriority(GPIOA_IRQn, 0);
- // HAL_NVIC_EnableIRQ(GPIOA_IRQn);
- }
- void test_key(void)
- {
- KEY1.KEY_Detcet();
- if(KEY1.KEY_Flag == 1)
- {
- if(KEY1.Click == 1)
- {
- HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_2);
- printf("click - >LED3 _togrn");
- }
- KEY1.KEY_Flag = 0;
- KEY1.Click = 0;
- }
- }
- void Error_Handler(void)
- {
- while (1)
- {
- }
- }
- void assert_failed(uint8_t *file, uint32_t line)
- {
- printf("Wrong parameters value: file %s on line %drn", file, line);
- }
复制代码
编译后运行效果:
附工程文件
0
|
|
|
|