前情提示: 我们之前简要做了LED和蜂鸣器的学习笔记。因此,本文程序涉及到LED和蜂鸣器的程序段不再重复出现。
零、按键基本认识
1、防抖
按键机械触点断开、闭合的时候,由于触点的弹性作用,按键开关不会马上稳定接通或一下子断开,而是会产生一些波纹信号,这些波纹信号会干扰高低电平的判断。如下图所示,在按键按下的前后均有信号抖动:
为了解决这个问题,有一些电路自带消抖功能,利用电容充放电的延时,消除了干扰波纹,从而简化了软件的处理,软件只需检测引脚的高低电平即可。这叫硬件消抖。
然而,我们的实验板却没有硬件消抖功能,因此,当用户按下按键时,软件需要延时一会儿(一般为10ms左右),待引脚的输入电平稳定后再判断高低电平。这叫软件消抖。
大致的思路如下:
1 2 3 4 5 6 7 8 9 10 11 12
| uint8_t KEY_Scan(void) { if(KEY按下) { delay_ms(10); if(KEY确实按下) { return KEY_Value; } return 无效值; } }
|
返回的结果只有两种:要么按键确实被按下了,要么按键确实没被按下。
2、支持连续按
支持连续按的意思是,用户一直按着按键,相应的外设就会一直工作,直到用户松手,相应的外设才不工作。简单来说就是用户按一次不松手算很多次按下。其实,上面的程序就实现了这个功能,思路跟上面是一样的。
我们重复写一遍。大致的思路如下:
1 2 3 4 5 6 7 8 9 10 11 12
| uint8_t KEY_Scan(void) { if(KEY按下) { delay_ms(10); if(KEY确实按下) { return KEY_Value; } return 无效值; } }
|
3、不支持连续按
不支持连续按的意思是,即使用户一直按着按键,但相应的外设不会一直工作,响一下就不响了。简单来说就是用户按一次不松手只能算一次按下。
大致的思路如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| uint8_t KEY_Scan(void) { static uint8_t iskey = 0; if(!iskey && KEY按下) { delay_ms(10); iskey = 1; if(KEY确实按下) { return KEY_VALUE; } }else if(KEY没有按下) iskey = 0; return 没有按下 }
|
4、STM32F103精英上按键的电路图
电路图如下如所示:
从电路图我们可以看到,精英版为我们提供了两种按键,WK_UP接VCC电源,而KEY0和KEY1接地。因此,WK_UP连接的端口为下拉输入模式,KEY0和KEY1连接的端口为上拉输入模式。
当单片机读到的电平为低电平时,说明KEY0和KEY1被按下,而WK_UP没有被按下;
当单片机读到的电平为高电平时,说明KEY0和KEY1没有被按下,而WK_UP被按下。
最后,WK_UP接的是PA0端口,KEY0接的是PE4端口,KEY1接的是PE3端口。
一、按键实验初体验
1.支持连续按
本程序实现功能:按下按键,相应外设会工作,若按键不松手则外设一直工作。换言之,外设工作与否取决于按键是否被按下。
程序如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| #ifndef __KEY_H #define __KEY_H #include "sys.h"
#define KEY0 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) #define KEY1 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3) #define KEY_UP GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)
#define IPU_ON 0 #define IPD_ON 1
#define ALL_KEY_UNPRS 0 #define KEY0_PRS 1 #define KEY1_PRS 2 #define KEY_UP_PRS 3
void KEY_Init(void); u8 KEY_Scan(void);
#endif
#include "stm32f10x.h" #include "key.h" #include "delay.h"
void KEY_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOE, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; GPIO_Init(GPIOA, &GPIO_InitStructure); }
u8 KEY_Scan(void) { if(KEY0 == IPU_ON || KEY1 == IPU_ON || KEY_UP == IPD_ON) { delay_ms(10); if(KEY0 == IPU_ON) return KEY0_PRS; else if(KEY1 == IPU_ON) return KEY1_PRS; else if(KEY_UP == IPD_ON) return KEY_UP_PRS; } return ALL_KEY_UNPRS; }
#include "stm32f10x.h" #include "sys.h" #include "beep.h" #include "key.h" #include "led.h" #include "delay.h"
int main() { u8 key = 0; delay_init(); KEY_Init(); LED_Init(); Beep_Init(); while(1) { key = KEY_Scan(); switch(key) { case KEY0_PRS: LED0_ON; while(KEY_Scan() != ALL_KEY_UNPRS);
break; case KEY1_PRS: LED1_ON; while(KEY_Scan() != ALL_KEY_UNPRS); break; case KEY_UP_PRS: BEEP_ON; while(KEY_Scan() != ALL_KEY_UNPRS); break; default: LED0_OFF; LED1_OFF; BEEP_OFF; break; } } }
|
2.不支持连续按
本程序实现功能:按下按键,相应外设会工作,且经过300ms后不工作。这就是说若按键不松手,外设不会一直工作。
将以上部分程序修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
|
u8 KEY_Scan(void) { static int isPrsBefore = 0; if((isPrsBefore == 0) && (KEY0 == IPU_ON || KEY1 == IPU_ON || KEY_UP == IPD_ON)) { delay_ms(10); isPrsBefore = 1; if(KEY0 == IPU_ON) return KEY0_PRS; else if(KEY1 == IPU_ON) return KEY1_PRS; else if(KEY_UP == IPD_ON) return KEY_UP_PRS; }else if (KEY0 == !IPU_ON && KEY1 == !IPU_ON && KEY_UP == !IPD_ON) isPrsBefore = 0; return ALL_KEY_UNPRS; }
#include "stm32f10x.h" #include "sys.h" #include "beep.h" #include "key.h" #include "led.h" #include "delay.h"
int main() { u8 key = 0; delay_init(); KEY_Init(); LED_Init(); Beep_Init(); while(1) { key = KEY_Scan(); switch(key) { case KEY0_PRS: LED0_ON; delay_ms(300); break; case KEY1_PRS: LED1_ON; delay_ms(300); break; case KEY_UP_PRS: BEEP_ON; delay_ms(300); break; default: LED0_OFF; LED1_OFF; BEEP_OFF; break; } } }
|
二、综合实验
以下我们实现一个功能:按下按键,相应外设工作状态将反转。按键不松手,工作状态不会改变(即不支持连续按)。
为实现工作状态反转功能,本程序使用位带操作读取IO口输入电平和输出电平。这种办法实现了与51类似的IO口控制功能,比如51里是这样用的:sbit LED0 = P2^6;
在STM32里是这样用的:#define LED0 PEin(5),意思是读取GPIOE.5口的输入电平;#define LED0 PEout(5),意思是读取GPIOE.5口的输出电平。
完整代码如下(头文件sys.h为正点原子资料盘自带文件):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
| #ifndef __LED_H #define __LED_H
#define LED0_OFF GPIO_SetBits(GPIOB, GPIO_Pin_5) #define LED0_ON GPIO_ResetBits(GPIOB, GPIO_Pin_5) #define LED1_OFF GPIO_SetBits(GPIOE, GPIO_Pin_5) #define LED1_ON GPIO_ResetBits(GPIOE, GPIO_Pin_5)
#define LED0 PBout(5) #define LED1 PEout(5)
void LED_Init(void);
#endif
#include "stm32f10x.h" #include "led.h"
void LED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_Init(GPIOE, &GPIO_InitStructure); LED0_OFF; LED1_OFF; }
#ifndef __BEEP_H #define __BEEP_H
#define BEEP_ON GPIO_SetBits(GPIOB, GPIO_Pin_8) #define BEEP_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_8)
#define BEEP PBout(8)
void Beep_Init(void);
#endif
#include "stm32f10x.h" #include "beep.h"
void Beep_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); BEEP_OFF; }
#ifndef __KEY_H #define __KEY_H #include "sys.h"
#define KEY0 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) #define KEY1 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3) #define KEY_UP GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)
#define IPU_ON 0 #define IPD_ON 1
#define ALL_KEY_UNPRS 0 #define KEY0_PRS 1 #define KEY1_PRS 2 #define KEY_UP_PRS 3
void KEY_Init(void); u8 KEY_Scan(void);
#endif
#include "stm32f10x.h" #include "key.h" #include "delay.h"
void KEY_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOE, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; GPIO_Init(GPIOA, &GPIO_InitStructure); }
u8 KEY_Scan(void) { static int isPrsBefore = 0; if((isPrsBefore == 0) && (KEY0 == IPU_ON || KEY1 == IPU_ON || KEY_UP == IPD_ON)) { delay_ms(10); isPrsBefore = 1; if(KEY0 == IPU_ON) return KEY0_PRS; else if(KEY1 == IPU_ON) return KEY1_PRS; else if(KEY_UP == IPD_ON) return KEY_UP_PRS; }else if (KEY0 == !IPU_ON && KEY1 == !IPU_ON && KEY_UP == !IPD_ON) isPrsBefore = 0; return ALL_KEY_UNPRS; }
#include "stm32f10x.h" #include "sys.h" #include "beep.h" #include "key.h" #include "led.h" #include "delay.h"
int main() { u8 key = 0; delay_init(); KEY_Init(); LED_Init(); Beep_Init(); while(1) { key = KEY_Scan(); switch(key) { case KEY0_PRS: LED0 = !LED0; break; case KEY1_PRS: LED1 = !LED1; break; case KEY_UP_PRS: BEEP = !BEEP; break; default: break; } } }
|