STM32嵌入式一:通过定时器实现按键消抖、长按、短按和双击

一、通过CubeMX配置并创建项目

1、首先在STM32CubeMX主界面通过菜单栏"File"-"New Project"进入项目芯片选型界面;
2、输入开发板对应芯片型号并选择芯片,开始项目;(本实验使用的开发板芯片型号是STM32F103ZET6,你需要根据自己开发板的芯片选择对应型号进行项目创建)

在这里插入图片描述

3、进行系统Debug调试方式配置:Debug方式选择SWD(Serial wire debug);

在这里插入图片描述

4、进行RCC时钟树的配置:高速外部时钟(HSE)时钟源选择外部晶振;低速外部时钟(LSE)一般在项目需要使用RTC实时时钟时开启,本项目中不需要开启;本实验选择的STM32F103ZET6芯片CPU所需时钟频率是72MHz,在RCC时钟树配置时需要通过锁相环倍频器(PLLCLK)将系统时钟(SYSCLK)配置成72MHz,你需要根据自己芯片CPU所需频率进行配置。

在这里插入图片描述
在这里插入图片描述

5、进行项目输出管理配置:设置项目名称、存放位置和开发环境;进行项目代码生成的个性化配置,最后输出项目。

在这里插入图片描述
在这里插入图片描述

输出项目时可能会提示没有下载此项目依赖的资源包,点击下载即可;生成项目之后先拒绝打开,在CubeMX中继续进行按键功能的配置。

二、配置按键所需的定时器

本实验选择基本定时器TIM7来完成按键功能;按键功能采用10ms消抖,所以定时器TIM7周期设置为10ms,即频率为100Hz;STM32F103ZET6的8个定时器原始时钟频率都是72MHz,所以需要进行分频操作使得定时器频率变为100Hz;同时需要开启TIM7的中断。

在这里插入图片描述
在这里插入图片描述

三、配置按键引脚

本实验使用两个按键key0和key1,分别对应芯片PE4和PA0引脚;同时由于开发板的这两个按键“共阳极”,按下时传给引脚的是高电平,所以需要配置PE4、PA0引脚为“下拉输入”。

在这里插入图片描述
在这里插入图片描述

再次点击"generate code"按钮更新图形化配置的初始化代码。

四、按键功能代码编写

1、创建按键功能文件

1、在项目文件夹中新建一个"USER"文件夹用来存放用户自己编写的代码,这里用来存放按键功能代码;并在USER文件夹中创建两个文件"key_user.h"、“key_user.c”;

2、使用Keil软件打开项目文件夹中"MDK-ARM"文件夹下的".uvprojx"文件,在keil中创建一个同名的"USER"文件夹,并将上一步创建的.c文件导入;

在这里插入图片描述

3、同时需要给项目配置C/C++文件包含路径,将之前在项目文件夹中创建的"USER"文件夹包含到此项目中;

2、编写按键功能文件

按键头文件编写

1、在keil中打开USER文件夹中的"key_user.c"文件,在此文件界面右键打开"key_user.h"文件,开始编写按键头文件;
2、头文件需包含cubeMX图形化配置自动生成的main.h文件;

#include "main.h"

3、在头文件中定义一个按键结构体Key_Struct,其成员变量代表一个按键的若干属性标志;

typedef struct{      
		uint8_t	  key_status;  //按键状态————共阳极,高电平1表示被按下,0表示松开;共阴极,低电平0表示被按下,1表示松开
		uint8_t	  key_step;     //按键判断步骤变量
	    uint8_t   key_step2;
		uint8_t   key_count;    //长按短按的判断时间变量,按键持续按下时间计时器
	    uint8_t   key_count2;
		uint8_t   key_shortFlag;  //短按标志变量,默认为0,表示没触发;
		uint8_t	  key_longFlag;    //长按标志变量
		uint8_t   key_doubleFlag;  //双击标志变量  
}Key_Struct;

4、在头文件中需要将源文件中定义的按键结构体对象(后续会在源文件编写时定义)声明为外部变量;(当源文件里的变量在头文件中声明为外部变量时,其他文件只要包括含此头文件即可直接使用这些变量,不需要在其他文件中再次声明这个外部变量)

extern Key_Struct key[2];  //这里以数组形式创建两个Key_Struct结构体对象/变量,根据用户需要选择要创建的对象个数,即结构体数组大小 
按键源文件编写

1、源文件需要包含按键头文件;
2、在源文件中定义一个按键结构体对象数组Key_Struct key[],数组大小根据你实际需要使用的按键数决定,本实验使用key0、key1两个按键,结构体数组大小为2;

#include "key.h"
Key_Struct key[2]; 

3、重定义定时器中断回调函数void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)这个"_weak"函数;由于此中断回调函数会被所有定时器中断服务程序调用,所以在此函数内部必须判断是哪个定时器产生的中断,当检测到是TIM7产生的中断时,开始进行按键检测。按键功能检测判断的功能代码如下:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
  //此回调函数会被所有TIM定时器中断服务程序调用,即所有tim中断源都会调用它,
  //所以要先判断是哪一个tim定时器中断源触发的
  if(htim->Instance == TIM7){   //基本定时器TIM7周期为10ms,频率为100Hz
  //每隔10ms读取一遍所有按键状态————是否按下
    key[0].key_status = HAL_GPIO_ReadPin(key0_GPIO_Port,key0_Pin);   
	key[1].key_status = HAL_GPIO_ReadPin(key1_GPIO_Port,key1_Pin);
    // key[2].key_status = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
    // key[3].key_status = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);//.......根据按键个数添加	
      //开始for循环轮询判断每个按键情况
      for(uint8_t i = 0; i < 2; i++){
        switch(key[i].key_step){//根据每个按键的step进行对应步骤判断		
          case 0: //step0:判断按键是否被按下——————核心点:按键是共阳极还是共阴极!!!!这里是共阳极,按下检测到的是高电平
            if(key[i].key_status == GPIO_PIN_SET){
			  key[i].key_step = 1; //如果被按下,step=1,下一个10ms中断执行下一步,此步相当于10ms消抖
			}else{
			  key[i].key_step = 0; //如果没被按下,step=0,下一个10ms中断依旧执行第一步case0
			}
			break;		
          case 1:  //step1:按键消抖————进一步判断按键是否被按下以·进行下一步判断:误触还是确实被按下——长按还是短按
            if(key[i].key_status == GPIO_PIN_SET){
			  key[i].key_count = 0; //依旧被按下,开始预备进行长按计时
			  key[i].key_step = 2;
			}else{
			  key[i].key_step = 0;  //否则,10ms间隔后未检测到按下,说明之前的按下可能是误触,
			}
			break;		
          case 2:
		  	if(key[i].key_status == GPIO_PIN_SET){
		      key[i].key_count++;   //按键持续按下时间计时器开始计时
			}else if(key[i].key_status == GPIO_PIN_RESET && key[i].key_count > 150){//按键被松开,且被松开前已持续按下超过150个10ms,即超过1.5秒,判断为长按
    		  key[i].key_longFlag = 1;
			  key[i].key_step = 0;
			}else if(key[i].key_status == GPIO_PIN_RESET && key[i].key_count <= 150){//按键被松开,且被松开前已持续按下不超过150个10ms,即不超过1.5秒,即为一次短按  
			  switch(key[i].key_step2){//判断短按还是双击,因为能进到这个条件语句里的一定是一次短按,要判断在一次短按后规定时间内是否又有短按						
     		    case 0: //这是一次短按,但不能认为一定是,要通过count2定时器检测是否在此次短按后规定count2个10ms时间内再无短按按下才可认定是短按
				  key[i].key_count2 = 0;
				  key[i].key_step2 = 1;
				  break;					
				case 1://能进入此步,说明case0执行过了,即完成一次短按;再进入此步说明又是一次短按,判断为双击
			      key[i].key_doubleFlag = 1;
				  key[i].key_step2 = 0;
				  break;			
                default:break; 
              }
			  key[i].key_step = 0;
			}
			break;
		default:break;
	  }
			
      if (key[i].key_step2 == 1){
	  //对单击短按判断,这里count2的比较数值根据用户自己定义,比如两次短按间隔不超过0.5秒记为双击,
	  //则超过0.5秒,即count2超过50个10ms后按键没有按下,记为单击;		
	    key[i].key_count2++;
        if(key[i].key_count2 > 50){
          key[i].key_shortFlag = 1;
		  key[i].key_step2 = 0;
        }
      }
	}//for循环结束
  } 
}

至此,按键功能代码的头文件和源文件编写完成,只需要在main.c文件中包含按键头文件,即可通过按键结构体对象使用对应按键的长、短、双击标志变量来进行后续操作。main.c文件中使用示例:

### 如何使用 ECharts 创建纵向柱状图 为了创建个纵向柱状图,在ECharts中需要调整`xAxis``yAxis`的设置,使横轴成为类目轴而纵轴成为数值轴。具体来说,可以通过交换原本用于定义横向柱状图中的`xAxis`与`yAxis`属性来实现点[^2]。 下面是个简单的例子,展示了如何通过JavaScript代码构建这样的图表: ```javascript var chartDom = document.getElementById('main'); var myChart = echarts.init(chartDom); var option; option = { title: { text: '示例纵向柱状图' }, tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, legend: {}, grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, xAxis: { type: 'value' // 设置为数值型 }, yAxis: { type: 'category', // 类目型 data: ['类别', '类别二', '类别三', '类别四', '类别五'] }, series: [ { name: '销量', type: 'bar', data: [24210, 32524, 50434, 52421, 98742], label: { show: true, position: 'insideRight' } } ] }; if (option && typeof option === 'object') { myChart.setOption(option); } ``` 这段脚本初始化了个名为`myChart`的对象,并设置了其选项参数以显示个基本的纵向条形图。其中最关键的部分在于`xAxis`被设为数值类型(`type:'value'`),而`yAxis`则作为分类轴并指定了具体的类别数据。 此外,还可以进步自定义样式其他功能特性,比如添加提示框、网格线以及标签等元素,从而让图表更加美观易读。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

绿色蓝胖子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值