STM32按键程序讲解

本文介绍了嵌入式系统中按键的两种操作模式——连按和不连按,并通过示例代码展示了如何实现。在不连按模式下,长时间按住按键只被视为一次输入。此外,还探讨了按键的复用技巧,如长按短按的区分、按键次数复用以及两个按键组合使用的场景,展示了如何通过中断和定时器优化按键功能,实现更高效的操作控制。

一、连按、不连按

连按:手一直按按钮保持不松的状态,小灯的状态发生改变

不连按模式:手一直按按钮保持不松的状态相当于只按了一次

三个按键都处于上拉输入状态:按下GPIO口为0

下面程序中用到了static关键字,下面一起来回顾一下:

  静态局部变量使用static修饰符定义,即使在声明时未赋初值,编译器也会把它初始化为0。且静态局部变量存储于进程的全局数据区,即使函数返回,它的值也会保持不变。

//static出现在类中
class Person
{
public:
	static int i;  //类内声明
};
int Person::i;   //类外定义
int main()
{
	Person::i = 10; //其他地方可以改变其值
}

u8 keyScan(u8 mode)
{
	static u8 f=1;
	if(mode)
	{
		f=1;
	}
	if(f&&(key0==0||key1==0||key2==0))
	{
		delay_ms(10);//延时去抖
		f=0;
		if(key0==0)
		{
			return 0;
		}
		else if(key1==0)
		{
			return 1;
		}
		else if(key2==0)
		{
			return 2;
		}
	}
	else if(key0==1&&key1==1&&key2==1)
	{
		f=1;
	}
	return -1;  //返回-1表示无按键按下
	
}
void main()
{
	u8 key;
	delay_init();   //延时函数初始化
	Led_init();    //led小灯初始化
	Key_init();    //按键初始化
	LED0=0;        //刚开始使led0小灯处于亮的状态
	while(1)
	{
		key=keyScan(1);
		switch(key)
		{
			case 0:
			{
				LED0=!LED0;   //改变led小灯状态
				delay_ms(1000); //延时1s
				break;
			}
			case 1:
			{
				LED1=!LED1;
				delay(1000);
				break;
			}
			case 2
			{
				LED0=!LED0;
				LED1=!LED1;
				delay(1000);
				break;
			}
			default:
			{
				delay_ms(10);
			}
		}
	}
	
}

二、按键复用

  按键复用的应用在我们日常生活中非常常见,我们的智能手机的按键很少,只有开关键和音量键,但却能应用复用实现其他功能。

  1、长按、短按

 下面这个例子中我们来实现只用一个按键来控制两个led小灯的控制。

  3s以内:控制LED0

  3s以上:控制LED1

u8 key_Scan(u16 time)
{
  u16 count=0;
  if(key0==0)
  {
    delay_ms(10); 
    if(key0==0)
    {
      while(key0==0)
      {
        delay_ms(10);
        count++; 
      }
    }
    if(count==0)
      return 0;
    else if(count*10<time)
      return 1;
    else
      return 2;
  }
  return 0;
}
int main(void)
 {	
	u8 t=0;	  
	delay_init();	    	 	  
	LED_Init();		  	 	
	KEY_Init();          	
	LED0=0;					
	while(1)
	{		
		t=key_Scan(3000);
		switch(t)
		{
		  case 0:
		  {
			break;
		  }
		  case 1:   
		  {
			LED0=!LED0;
			break;
		  }
		  case 2:
		  {
			LED1=!LED1;
			break;
		  }
		}
	}		 
}

 2、在一定时间内次数复用

    1s内按一次:控制LED0

    1s内按两次:控制LED1

会使用中断后再写(这都过了三四个月了才想起写)

思路:按键为下降沿中断,触发后打开定时器,定时1s后判断按键次数。

main主函数:

 u8 count=0;  //记录1s内按下KEY0的次数
 int main(void)
 {	 
	delay_init();	    	 //延时函数初始化	  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
    TIM3_Int_Init(4999,7199);//10Khz的计数频率,计数到5000为500ms
    EXTIX_Init();    //KEY0外部中断中断初始化
	LED_Init();		  	 	//初始化与LED连接的硬件接口
	while(1)
	{
    
    }	 
 }

中断线5(KEY0)的中断服务程序:

 void EXTI9_5_IRQHandler(void)
{			
  delay_ms(10);   //消抖		
  
  if(KEY0==0)	{
    count++;
    if(count==1)
    {
      //打开定时器
      TIM_Cmd(TIM3, ENABLE);  //开始计时
    }   
    
  }
  EXTI_ClearITPendingBit(EXTI_Line5);    //清除LINE5上的中断标志位  
}

定时器3中断服务程序:
 

void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
	{
    if(count==1)   //1s内按1次KEY0,led0亮
    {
      LED0=!LED0;
    }
    else           //1s内按1次KEY0,led1亮
    {
      LED1=!LED1;
    }
    count=0;
    TIM_Cmd(TIM3,DISABLE);  //停止计时
	  TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
	}
}

 3、两个按键共同按下

单独按下key0,控制ed0    

key0和key1一起按下,控制led1

u8 key_Scan2()   //一定要不支持连按
{
  static u8 f=1;
  if(f&&(KEY0==0||(KEY0==0&&KEY1==0)))
  {
    delay_ms(10);
    if(KEY0==0)
    {
      f=0;
      if(KEY0==0&&KEY1)
      {
        return 1;
      }
      else if(KEY0==0&&KEY1==0)
      {
        return 2;
      }
      else
        return 0;
    }
  }
  else if(KEY0==1)
    f=1;
  return 0;
}
void main(void)
 {	
	u8 t=0;	  
	delay_init();	    	   
	LED_Init();		  	 	
	KEY_Init();          	
	LED0=0;					
	while(1)
    {
        t=key_Scan2();
		switch(t)
		{
			case 0:
			{
				break;
			}
			case 1:
			{
				LED0=!LED0;
				break;
			}
			case 2:
			{
				LED1=!LED1;
				break;
			}
		}
	}		 
}
    }

<think>好的,我现在需要处理用户关于STM32按键程序设计的请求。用户提到他们想查找相关的资料或示例代码,特别是关于按键长短按的设计。我需要结合之前提供的引用内容,以及系统级指令来生成合适的回答。 首先,我得回顾用户提供的引用内容。引用[1]详细说明了按键长短按的设计,包括状态机、时间戳和消抖处理。引用[2]则介绍了STM32的GPIO特性和HAL库的使用。这些信息是关键,特别是状态机的设计和HAL库的函数调用。 接下来,用户的需求是示例代码和教程。我需要确保回答结构清晰,分步骤讲解,同时提供代码示例。根据系统指令,代码需要用正确的格式,行内数学表达式用$...$,但这里可能不太需要数学公式,主要关注代码结构和逻辑。 用户之前的提问可能涉及按键处理,所以需要覆盖消抖、状态检测、长短按判断。引用[1]中的状态机设计是一个重点,应该详细说明状态转移,比如检测按下、消抖、等待释放、判断时间等步骤。 另外,需要确保使用HAL库,因为引用[2]提到了HAL库的使用。代码中应该包括GPIO的初始化和HAL库函数,如HAL_GPIO_ReadPin和HAL_GetTick。 还要注意用户提到的消抖处理(20ms)和长短按时间阈值(1秒)。代码中需要实现时间戳记录,计算按下时间,并根据阈值触发不同事件。 需要检查是否有遗漏的部分,比如事件触发后的处理,是否需要在主循环中处理标志位,或者使用回调函数。引用[1]提到通过标志位分离检测与处理,所以代码中应该有标志变量,如key_short_press和key_long_press。 可能还需要考虑按键扫描函数的调用频率,是否应该放在定时器中断中,或者主循环中定期调用。这里可能需要说明建议将扫描函数放在主循环或定时器回调中,例如每10ms执行一次。 最后,生成相关问题,引导用户深入探讨,比如如何扩展多个按键、调整时间阈值、低功耗处理等。这些问题应该与用户的需求相关,并基于现有内容扩展。 现在需要组织这些点,确保回答结构清晰,分步骤说明,代码正确,并引用提供的参考文献。同时,避免使用Markdown格式,但根据用户层的要求,可能允许代码块。需要确认用户层的指示,但示例中使用了代码块,所以应该遵循。</think>基于STM32按键程序设计需要结合GPIO控制、消抖处理和状态机设计,以下为基于HAL库的完整实现方案: ### 一、硬件设计要点 1. **硬件连接**:按键连接GPIO引脚(如PA0),配置为上拉输入模式(默认高电平,按下时拉低) 2. **消抖参数**:机械按键抖动时间通常为$5ms-20ms$,建议取$20ms$滤波 3. **时间阈值**:短按<1s,长按≥1s(可通过宏定义调整) ### 二、软件实现步骤 #### 1. GPIO初始化 ```c // cubemx配置 void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式 GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉电阻 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } ``` #### 2. 按键状态机实现 ```c #define KEY_SHORT_PRESS 1 #define KEY_LONG_PRESS 2 #define DEBOUNCE_TIME 20 // 消抖时间(ms) #define LONG_PRESS_TIME 1000 // 长按时间(ms) uint8_t key_scan(void) { static uint8_t key_state = 0; static uint32_t press_time = 0; uint8_t key_val = 0; switch(key_state) { case 0: // 等待按下 if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) { press_time = HAL_GetTick(); key_state = 1; } break; case 1: // 消抖确认 if(HAL_GetTick() - press_time > DEBOUNCE_TIME) { if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) { key_state = 2; } else { key_state = 0; } } break; case 2: // 等待释放 if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET) { uint32_t duration = HAL_GetTick() - press_time; key_val = (duration < LONG_PRESS_TIME) ? KEY_SHORT_PRESS : KEY_LONG_PRESS; key_state = 0; } break; } return key_val; } ``` #### 3. 主循环调用 ```c while(1) { uint8_t key = key_scan(); if(key == KEY_SHORT_PRESS) { // 处理短按事件 HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); } else if(key == KEY_LONG_PRESS) { // 处理长按事件 SystemReset(); } HAL_Delay(10); // 建议扫描周期10ms } ``` ### 三、关键设计原理 1. **状态机设计**:通过三个状态实现按键检测流程[^1] - 状态0:检测初始按下 - 状态1:消抖确认 - 状态2:等待释放并判断时长 2. **时间戳应用**:使用`HAL_GetTick()`记录精确时间差 $$ t_{press} = t_{release} - t_{press} $$ 3. **事件分离**:扫描函数返回事件类型,主程序处理具体逻辑 ### 四、优化建议 1. 多按键支持:扩展为二维数组管理多个按键状态 2. 参数可配置:通过宏定义调整时间阈值 3. 低功耗处理:在等待状态启用中断唤醒
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

small_planet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值