第2-3讲:按键检测
-
- 学习目的
- 学习轻触按键和触摸按键硬件电路原理。
- 学习STC8A8K64D4用作输入时相关寄存器的配置。
- 掌握如何读取GPIO状态。
- 掌握编写轻触按键和触摸按键检测程序。
- 硬件电路设计
IK-64D4开发板上设计了4个轻触按键和一个触摸按键,提供给用户作为按键开关使用。
-
-
- 轻触按键
-
轻触按键又称轻触开关,是电路中常用的一种开关元器件,也是一种常用的人机接口。广泛用于家电、数码产品、便携仪产品、电脑产品等电子设备中。
轻触按键,顾名思义我们只需要施加很小的力量即可改变开关连接的状态。轻触按键在所需外力作用下(按下按键)触点导通,无外力作用时(释放按键)触点断开,如下图所示:
图1:轻触开关原理
由此,我们可以通过将连接到轻触按键的GPIO配置为输入模式,之后读取该GPIO的状态来判断轻触按键是否按下。
IK-64D4开发板上设计了4个轻触按键Key1、Key2、Key3和Key4,分别连接到STC8A8K64D4的GPIO P3.7、P3.6、P0.7和P0.5。轻触按键电路如下图所示,当按键处于释放状态时(按键没有按下),按键断开,由于上拉电阻的作用GPIO的输入为高电平。当按键按下时,按键导通,按键连接的GPIO短接到GND,这时,该GPIO的输入为低电平。由此,程序中我们可以根据读取的GPIO的状态是低电平还是高电平来判断对应按键是按下还是释放。
图2:轻触按键电路
4个轻触按键占用的单片机的引脚如下表:
表1:轻触按键检测引脚分配
名称 | 引脚 | 说明 |
Key1 | P3.7 | 非独立GPIO,和以太网模块W5500接口复用。 |
Key2 | P3.6 | 非独立GPIO,和以太网模块W5500接口复用。 |
Key3 | P0.7 | 独立GPIO。 |
Key4 | P0.5 | 独立GPIO。 |
- 注:独立GPIO表示开发板没有其他的电路使用这个GPIO,非独立GPIO说明开发板有其他电路用到了该GPIO。读者在使用非独立GPIO使用时需要注意电路的连接,以避免多个电路使用了同一个GPIO。
-
- 触摸按键
-
IK-64D4开发板上设计了一路基于TTP223触摸检测芯片的触摸按键,TTP223是电容式单键触摸按键IC,电压输入范围为2.0V~5.5V。TTP223利用操作者的手指与触摸按键焊盘之间产生电荷电平来进行检测,通过监测电荷的微小变化来确定手指接近或者触摸到感应表面。没有任何机械部件,不会磨损,其感测部分可以放置到任何绝缘层(通常为玻璃或塑料材料)的后面,很容易制成与周围环境相密封的键盘。
触摸按键电路如下图所示。
图3:触摸按键电路
TTP223的检测灵敏度可通过外部电容值(上图中的C15)来调整。SLH引脚用于设置TTP223的输出方式。
1) SLH = 0:触摸时,TTP223的OUT引脚输出高电平。
2) SLH = 1:触摸时,TTP223的OUT引脚输出低电平。
本电路中,SLH = 0,所以触摸时OUT引脚输出高电平,无触摸时OUT引脚输出低电平。注意一下,这和轻触按键的电路输出刚好是反的,轻触按键电路是按键按下时,电路输出低电平,无按键按下时电路输出高电平。
- 为什么触摸按键输出的信号不做成和轻触按键一样,也是按键时输出低电平,无按键时输出高电平?
这是因为:作为开发板,要方便用户测试,2种不同类型的输出方式更方便我们使用,另外在后续的章节中我们还会用到按键信号的上升沿和下降沿,这时就可以通过轻触键和触摸按键来获取,而不需要另外接线。
触摸按键占用的STC8A8K64D4的引脚如下表:
表2:触摸按键引脚分配
名称 | 引脚 | 说明 |
触摸按键 | P5.4 | 和外部复位电路共用IO |
-
-
- 按键检测电路需要考虑的因素
-
按键检测电路设计的时候,需要我们考虑两个方面:按键释放时GPIO口状态的确定和按键检测电路的保护以及按键消抖。
- 按键释放时GPIO口状态的确定
按键检测电路中,当按键释放后要能保证GPIO口电平是确定的,即按键释放时GPIO口固定为高电平或低电平。所以一般按键电路中,都会通过上拉电阻或下拉电阻来保证当按键释放时GPIO处于固定的电平。如果单片机的GPIO有片内可配置的上/下拉电阻,也可以使用片内上/下拉电阻而不用在单片机外部增加上/下拉电阻,使用GPIO检测按键时只需通过软件打开上拉/下拉电阻就可以了。
- 按键检测电路保护
设计电路时,可以考虑在按键和STC8A8K64D4单片机的GPIO之间串接了一个小阻值的电阻,这个电阻的作用如下:
- 保护IO,若IO口不小心被配置成了推挽输出,按下按键可能会损坏IO,串接电阻后,即使出现这种情况,也不会损坏IO。
- 降低按键动作时产生的抖动峰值电压,防止抖动电压对单片机产生影响。虽然很多单片机在IO上都有电压钳位设计,可以承受一定范围内的电压抖动,但是通过串接一个小阻值电阻既能让抖动峰值电压更低而又不影响按键检测电路的性能,而且花费的代价也很小(仅串接一个电阻),无疑是比较合算的。
- 按键硬件消抖
对于按键硬件上的消抖,一般常用的方式是在按键上并接一个容值约0.1uF左右电容,利用电容两端的电压不能突变的特性,消除抖动时产生的毛刺电压。虽然电容可以起到消除抖动的作用,但是在考虑按键灵敏度的情况下,电容是无法完全消除抖动的,消除抖动还需要软件的配合。
本电路中,没有采用电容消抖的方式,在整体设计上,我们是使用软件完成消抖的。
-
- 软件设计
- GPIO配置
- 软件设计
STC8A8K64D4提供的7个用于操作GPIO的寄存器中,和输入相关的寄存器有端口模式配置寄存器、端口上拉电阻控制寄存器、端口施密特触发控制寄存器、端口数字信号输入使能控制寄存器和端口数据寄存器。使用GPIO进入输入检测的时候,首先需要配置GPIO输入相关的寄存器,包含下面4个配置:
- 配置GPIO的工作模式为:本例中配置为准双向口。
- 配置上拉电阻:是否开启GPIO的上拉电阻要根据实际情况确定,如硬件电路上已经设计了外部上拉电阻,则无需开启。本例中需要开启外部上拉电阻(硬件电路上没有外部上拉)。
- 端口施密特触发控制寄存器:需要使能,端口施密特触上电复位后默认使能,因此,通常代码里面没有配置这个。
- 端口数字信号输入使能控制寄存器:需要使能,端口施密特触上电复位后默认使能。
配置完成后,即可通过读取端口数据寄存器来获取GPIO当前的状态是高电平还是低电平。
这几个配置项中,GPIO工作模式配置在前文已经描述过,这里不再赘述,端口施密特触发控制寄存器和端口数字信号输入使能控制寄存器通常无需配置,使用默认值即可,这里,我们主要看一下如下使能GPIO的上拉电阻。
配置GPIO上拉电阻的寄存器如下表所示,STC8A8K64D4共有8个端口P0~P7,因此,下表中只有P0PU~P7PU寄存器是STC8A8K64D4拥有的。
表3:端口上拉电阻控制寄存器
端口上拉电阻控制寄存器中的各个位的值对应端口中的GPIO的上拉电阻的开启或关闭(值为1:开启上拉电阻,值为0:关闭上拉电阻),如P0PU中的位0的值对应P0.0的上拉电阻的开启或关闭。端口上拉电阻控制寄存器是不支持位寻址的,也就是只能使用寄存器P0PU~P7PU访问,另外,需要特别注意的是端口上拉电阻控制寄存器为扩展 RAM 区特殊功能寄存器,访问前需先将P_SW2寄存器的最高位(EAXFR)置1,否则无法访问。
- 开启上拉电阻示例:使能P3.6的上拉电阻
代码清单:使能P3.6的上拉电阻
- P_SW2 |= 0x80; //将EAXFR位置1,使能访问XFR(扩展RAM区特殊功能寄存器)
- P3PU |= 0x40; //将P3PU的位6设置为1,使能P3.6的上拉电阻
- P_SW2 &= 0x7F; //将EAXFR位置0,关闭访问XFR
-
-
- 轻触按键检测实验
-
- 注:本节的实验是在“实验2-2-1:有源蜂鸣器鸣响控制”的基础上修改,本节对应的实验源码是:“实验2-3-1:轻触按键检测”。
-
-
- 实验内容
-
-
- 配置连接按键Key1、Key2、Key3和Key4的GPIO P3.7、P3.6、P0.7和P0.5为准双向口。
- 主循环中检测按键状态并使用软件消抖,当检测到按键按下后,翻转对应的LED指示灯的状态,即检测到按键Key1~ Key4按下后,分别翻转指示灯D1~D4的状态。
-
-
- 代码编写
-
-
- 新建一个名称为“button.c”的文件及其头文件“button.h”并保存到工程的“Source”文件夹,并将“button.c”加入到Keil工程中的“SOURCE”组。
- 引用头文件
因为在“main.c”文件中使用了“button.c”文件中的函数,所以需要引用下面的头文件“button.h”。
代码清单:引用头文件
- //引用头文件
- #include " button.h"
- 引脚定义和配置
4个轻触按键Key1、Key2、Key3和Key4的GPIO P3.7、P3.6、P0.7和P0.5,这里,我们使用“#define”定义如下宏。
代码清单:定义连接按键的引脚
- #define BUTTON1_P37 P37 //用户按键Key1用GPIO口P3.7
- #define BUTTON2_P36 P36 //用户按键Key2用GPIO口P3.6
- #define BUTTON3_P07 P07 //用户按键Key3用GPIO口P0.7
- #define BUTTON3_P05 P05 //用户按键Key4用GPIO口P0.5
- 按键扫描函数
在编写按键检测函数之前,为了方便判断按键的状态,我们定义了一些常量用来表示按键的状态和有效按键(按键按下)的标号,代码如下。
代码清单:按键检测相关常量定义
- //定义按键按下和释放状态
- #define BUTTON_PRESSED 0
- #define BUTTON_RELEASED 1
- //定义按键有效状态编号(按键按下)
- #define BUTTONS_RELEASED 0 //没有按键按下
- #define BUTTON1_PRESSED 1 //按键KEY1按下
- #define BUTTON2_PRESSED 2 //按键KEY2按下
- #define BUTTON3_PRESSED 3 //按键KEY3按下
- #define BUTTON4_PRESSED 4 //按键KEY4按下
按键扫描函数中,对连接4个按键的GPIO的状态进行读取,如果为低电平,则延时10ms后再次读取(软件消抖),如仍为低电平,则认为按键有效,并返回对应的按键编号。
代码清单:按键扫描函数
- /**************************************************************************
- 功能描述:读取开发板上的4个用户按键(KEY1、KEY2、KEY3和KEY4)的状态
- 参 数:mode [in]: 是否支持连按,=true:支持,=false:不支持
- 返 回 值:有按键按下,返回对应的按键编号,否则返回BUTTONS_RELEASED(没有按键按下)
- *************************************************************************/
- u8 buttons_scan(u8 mode)
- {
- static u8 btn_up=1; //标志变量
- if(mode==1) //支持连按
- {
- btn_up=1; //变量 Key_up会被重新赋值为1
- }
- //读取按键KEY1、KEY2、KEY3和KEY4连接的IO口电平是否为低电平
- if(btn_up&&((BUTTON1_P37 == BUTTON_PRESSED ) || (BUTTON2_P36 == BUTTON_PRESSED ) ||
- (BUTTON3_P07 == BUTTON_PRESSED ) || (BUTTON4_P05 == BUTTON_PRESSED )))
- {
- delay_ms(10); //软件延时10ms,消抖
- btn_up=0; //变量 btn_up清零
- //KEY1按键按下(P3.7为低电平),返回按键有效(BUTTON3_PRESSED)
- if(BUTTON1_P37 == BUTTON_PRESSED)
- {
- return BUTTON1_PRESSED;
- }
- //KEY2按键按下(P3.6为低电平),返回按键有效(BUTTON4_PRESSED)
- else if(BUTTON2_P36 == BUTTON_PRESSED)
- {
- return BUTTON2_PRESSED;
- }
- //KEY3按键按下(P0.7为低电平),返回按键有效(BUTTON5_PRESSED)
- else if(BUTTON3_P07 == BUTTON_PRESSED)
- {
- return BUTTON3_PRESSED;
- }
- //KEY4按键按下(P0.5为低电平),返回按键有效(BUTTON5_PRESSED)
- else if(BUTTON4_P05 == BUTTON_PRESSED)
- {
- return BUTTON4_PRESSED;
- }
- }
- else if((BUTTON1_P37 == BUTTON_RELEASED )&&(BUTTON2_P36 == BUTTON_RELEASED ) &&
- (BUTTON3_P07 == BUTTON_RELEASED )&&(BUTTON4_P05 == BUTTON_RELEASED ))
- {
- btn_up=1; //变量 key_up置位
- }
- return BUTTONS_RELEASED; //返回无按键按下
- }
主函数中配置连接按键的GPIO为准双向口,因为开发板轻触按键检测硬件电路上设计有外部上拉电阻,因此,无需要打开GPIO的片内上拉电阻。之后在主循环里面调用按键扫描函数buttons_scan()查询是否有按键按下,如果有按键按下则翻转对应编号的指示灯的状态。
代码清单:主函数
- /**************************************************************************
- 功能描述:主函数
- 入口参数:无
- 返回值:int类型
- *************************************************************************/
- int main(void)
- {
- u8 temp;
- P2M1 &= 0x3F; P2M0 &= 0x3F; //设置P2.6~P2.7为准双向口(指示灯D1和D2)
- P7M1 &= 0xF9; P7M0 &= 0xF9; //设置P7.1~P7.2为准双向口(指示灯D4和D3)
- P3M1 &= 0x3F; P3M0 &= 0x3F; //设置P3.6~P3.7为准双向口(按键KEY2和KEY1)
- P0M1 &= 0x5F; P0M0 &= 0x5F; //设置P0.5,P0.7为准双向口(按键KEY4和KEY3)
- //如果按键电路上没有外部上拉电阻,需要开启GPIO的片内上拉。
- //开发板的按键电路设计了上拉电阻,因此,无需开启片内上拉
- // P_SW2 |= 0x80; //将EAXFR位置1,以访问在XDATA区域的扩展SFR
- // P0PU |= 0xA0; //开启P0.5、P0.7的上拉电阻
- // P3PU |= 0xC0; //开启P3.6、P3.7的上拉电阻
- // P_SW2 &= 0x7F; //将EAXFR位置0,恢复访问XRAM
- while(1)
- {
- temp = buttons_scan(0); //获取开发板用户按键检测值,不支持连按
- if(temp == BUTTON1_PRESSED) //按键KEY1按下
- {
- led_toggle(LED_1); //用户指示灯D1状态翻转
- }
- else if(temp == BUTTON2_PRESSED) //按键KEY2按下
- {
- led_toggle(LED_2); //用户指示灯D2状态翻转
- }
- else if(temp == BUTTON3_PRESSED) //按键KEY3按下
- {
- led_toggle(LED_3); //用户指示灯D3状态翻转
- }
- else if(temp == BUTTON4_PRESSED) //按键KEY4按下
- {
- led_toggle(LED_4); //用户指示灯D3状态翻转
- }
- }
- }
-
-
-
- 硬件连接
-
-
本实验需要使用LED指示灯和按键,因此需要用跳线帽短接复用引脚的指示灯(D1和D2)和按键(KEY1和KEY2),而指示灯D3和D4以及按键KEY3和KEY4是独立引脚,没有和其他电路复用引脚,是没有短接跳线帽的操作的。
图4:跳线帽短接
-
-
-
- 实验步骤
-
-
- 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-3-1:轻触按键检测”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
- 双击“…\button\Project”目录下的工程文件“button.uvproj”。
- 点击编译按钮编译工程,编译成功后生成的HEX文件“button.hex”位于工程的“…\button\project\Objects”目录下。
- 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
- 程序运行后,依次按下用户按键KEY1、KEY2、KEY3和KEY4,可以观察到每按一次按键,对应编号的用户指示灯的状态翻转。
-
-
- 触摸按键检测实验
-
- 注:本节的实验是在“实验2-3-1:轻触按键实验”的基础上修改,本节对应的实验源码是:“实验2-3-2:触摸按键检测”。
-
-
- 实验内容
-
-
在“实验2-3-1:轻触按键实验”的基础上增加触摸按键的检测代码,主要包含下面两个方面。
- 配置连接触摸按键的GPIO P5.4为准双向口。
- 主循环中检测触摸按键状态并使用软件消抖,当检测到触摸按键按下后,点亮4个LED指示灯。
-
-
- 代码编写
-
-
- 引脚定义和配置
在“button.h”文件中按键引脚定义的地方加入触摸按键的引脚定义,代码清单如下。
代码清单:定义连接按键的引脚
- //省略无关的代码
- #define TOUCH_BUTTON_P54 P54 //触摸按键用GPIO口P5.4
- 触摸按键扫描函数
触摸按键的检测方式和轻触按键一样,不同的是轻触按键检测到低电平时认为按键按下,触摸按键检测到高电平时认为按键按下,代码如下。
代码清单:按键检测相关常量定义
- /**************************************************************************
- 功能描述:读取开发板上的触摸按键的状态
- 参 数:无
- 返 回 值:有按键按下,返回BUTTON_PRESSED,否则返回BUTTONS_RELEASED(没有按键按下)
- *************************************************************************/
- u8 touch_button_scan(void)
- {
- //读取触摸按键用引脚P5.4是否是高电平 (用手指触摸按键感应区域,引脚为高电平)
- if(TOUCH_BUTTON_P54 == 1)
- {
- delay_ms(10); //软件延时10ms,软件消抖
- if(TOUCH_BUTTON_P54== 1) //检测触摸按键用引脚P5.4是否依然是高电平
- {
- return BUTTON_PRESSED; //返回按键按下
- }
- }
- return BUTTON_RELEASED; //返回无按键按下
- }
主函数中配置连接触摸按键的GPIO P5.4为准双向口,之后在主循环里面调用触摸按键扫描函数touch_button_scan()查询是否有按键按下,如果有按键按下则点亮4个LED指示灯。
代码清单:主函数
- /**************************************************************************
- 功能描述:主函数
- 入口参数:无
- 返 回 值:int类型
- *************************************************************************/
- int main(void)
- {
- u8 temp;
- //省略了无关的代码
- P5M1 &= 0xEF; P5M0 &= 0xEF; //设置P5.4为准双向口(触摸按键)
- while(1)
- {
- //省略了无关的代码
- temp = touch_button_scan(); //读取开发板触摸按键检测值
- if(temp == BUTTON_PRESSED) //如果触摸按键按下
- {
- leds_on(); //点亮4个用户指示灯
- }
- }
- }
-
-
-
- 硬件连接
-
-
本实验需要使用LED指示灯、4个轻触按键和触摸按键,因此需要用跳线帽短接复用引脚的指示灯(D1和D2)、按键(KEY1和KEY2)和触摸按键(TOUCH),而指示灯D3和D4以及按键KEY3和KEY4是独立引脚,没有和其他电路复用引脚,是没有短接跳线帽的操作的。
图5:跳线帽短接
-
-
-
- 实验步骤
-
-
- 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-3-2:触摸按键检测”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
- 双击“…\touch_button\Project”目录下的工程文件“touch_button.uvproj”。
- 点击编译按钮编译工程,编译成功后生成的HEX文件“touch_button.hex”位于工程的“…\button\project\Objects”目录下。
- 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
- 程序运行后,按下用户按键KEY1~KEY4可以翻转对应编号的指示灯状态,用手指去接触触摸按键,4个LED指示灯D1~D4全部点亮。