#include
#include "delay.h"
#include "sys.h"
/********************************
硬件连接:
PA0 ----------> C4
PA1 ----------> C3
PA2 ----------> C2
PA3 ----------> C1
PA4 ----------> R1
PA5 ----------> R2
PA6 ----------> R3
PA7 ----------> R4
********************************/
/********************************
矩阵的对应关系:
A3 A2 A1 A0
| | | |
A7--> S1 S2 S3 S4
A6--> S5 S6 S7 S8
A5--> S9 S10 S11 S12
A4--> S13 S14 S15 S16
********************************/
#define KEY_IN() (GPIO_ReadInputData(GPIOA)&0x00f0) //读取按键的输入管脚GPIOA4-7 读出行号
void KEY_Init(){
GPIO_InitTypeDef a;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //配置时钟给GPIOA
// 上拉输入模式默认情况下是高电平,用于检测低电平的输入(就是变化结果只有0,要么不变1)
a.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
a.GPIO_Speed = GPIO_Speed_10MHz;
a.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 必须要配置输出寄存器为1
GPIO_Init(GPIOA, &a);
GPIO_SetBits(GPIOA,GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7); //还必须需要配置输出寄存器的值
//key的输出管脚配置
a.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_0;
a.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
a.GPIO_Speed = GPIO_Speed_50MHz; //高速响应
GPIO_Init(GPIOA, &a); //没有配置输出寄存器,配置模式推挽输出默认未配置输出寄存器情况下为0,故不配置
}
int key_scan_lie(){
u8 i=0;
u16 pin=0x0001; //根据GPIO_Pin_x找的规律
//大体是先送“1” ,本着“1碰1”上拉输入不变的方式进行扫描;每扫描一次需要重新复原,防止影响别的扫描操作;
for(;i<4;i++,pin<<=1){
GPIO_SetBits(GPIOA,pin); //置为1
//这样写的原因是:当高电平3.3V遇到高电平3.3V电平变化为高电平;输入管脚为上拉输入模式
if(KEY_IN() == 0xf0){
GPIO_ResetBits(GPIOA,pin); //复原 变0
return 4-i; //返回对应的列号
}
GPIO_ResetBits(GPIOA,pin); //复原 变0
}
return (-16); //未扫描出列号的返回值
}
/*****************************
直接调用这个函数即可,返回对应按键的值
*****************************/
u8 key4x4_scan(){
u8 key_value = 0;
if(KEY_IN()!=0xf0){
delay_ms(20); //消抖,这么没事可说的
if( (key_value=KEY_IN()) != 0xf0){ //赋值并判断
// 先读出数据对应行号 0xe0 0111,0000 即PA7的管脚被变化成0
switch(key_value){
case 0xe0: key_value= 0*4 + key_scan_lie() ; break;
case 0xd0: key_value= 1*4 + key_scan_lie() ; break;
case 0xb0: key_value= 2*4 + key_scan_lie() ; break;
case 0x70: key_value= 3*4 + key_scan_lie() ; break;
}
while(KEY_IN() != 0xf0); //等待按键松开
}
}
return key_value; //返回对应的按键值:0表示未按下,负数表示扫描了行但列没扫描成功,正数表示按键按下返回正确的值
}
#include
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include
#define KEY_IN() (GPIO_ReadInputData(GPIOA)&0x00f0) //读取按键的输入管脚GPIOA4-7 读出行号
#define KEY_IN_0() GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4) //该函数读出的结果只有0和1
#define KEY_IN_1() GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)
#define KEY_IN_2() GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)
#define KEY_IN_3() GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7)
#define KEY_OUT_0(x) do{ if(x){GPIO_SetBits(GPIOA,GPIO_Pin_0);} \
else{GPIO_ResetBits(GPIOA,GPIO_Pin_0);} \
}while(0)
#define KEY_OUT_1(x) do{ if(x){GPIO_SetBits(GPIOA,GPIO_Pin_1);} \
else{GPIO_ResetBits(GPIOA,GPIO_Pin_1);} \
}while(0)
#define KEY_OUT_2(x) do{ if(x){GPIO_SetBits(GPIOA,GPIO_Pin_2);} \
else{GPIO_ResetBits(GPIOA,GPIO_Pin_2);} \
}while(0)
#define KEY_OUT_3(x) do{ if(x){GPIO_SetBits(GPIOA,GPIO_Pin_3);} \
else{GPIO_ResetBits(GPIOA,GPIO_Pin_3);} \
}while(0)
/********************************
矩阵的对应关系: OUT
A3 A2 A1 A0 1
| | | |
IN:A4--> S1 S2 S3 S4
A5--> S5 S6 S7 S8
A6--> S9 S10 S11 S12
A7--> S13 S14 S15 S16
硬件连接输入上图:没变,但可以使矩阵管脚不连续
********************************/
void KEY_Init(){
GPIO_InitTypeDef a;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //配置时钟给GPIOA
// 上拉输入模式默认情况下是高电平,用于检测低电平的输入(就是变化结果只有0,要么不变1)
a.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
a.GPIO_Speed = GPIO_Speed_10MHz;
a.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 必须要配置输出寄存器为1
GPIO_Init(GPIOA, &a);
GPIO_SetBits(GPIOA,GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7); //还必须需要配置输出寄存器的值
//key的输出管脚配置
a.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_0;
a.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
a.GPIO_Speed = GPIO_Speed_50MHz; //高速响应
GPIO_Init(GPIOA, &a); //没有配置输出寄存器,配置模式推挽输出默认未配置输出寄存器情况下为0,故不配置
}
判断的按键输入管脚的状态,-1表示无输入,有输入返回对应的输入管脚口
int key_scan(const int rs){ //行扫描
int rt=-1; //返回的结果
rt= KEY_IN_0()==rs ? 0 : \
KEY_IN_1()==rs ? 1 : \
KEY_IN_2()==rs ? 2 : \
KEY_IN_3()==rs ? 3 : -1;
return rt;
}
/*****************************
直接调用这个函数即可,返回对应按键的值
*****************************/
u8 key4x4_scan(){
u8 key_value=0;
int key_x=-1,key_y=-1;
if(key_scan(0)!= -1){//按键变动
delay_ms(20);
if( (key_x=key_scan(0)) != -1){ //先行扫描结果并赋值, “变”判断条件输入“变” ,将扫描的结果赋值给key_x
/*** 列扫描 ,将扫描的结果赋值给key_y ****/
//扫描第4列的
KEY_OUT_0(1);
key_y = key_scan(0) == -1 ? 4 : -1;
KEY_OUT_0(0); //还原
if(key_y != -1) goto done;
//扫描第3列的
KEY_OUT_1(1);
key_y = key_scan(0) == -1 ? 3 : -1;
KEY_OUT_1(0); //还原
if(key_y != -1) goto done;
//扫描第2列的
KEY_OUT_2(1);
key_y = key_scan(0) == -1 ? 2 : -1;
KEY_OUT_2(0); //还原
if(key_y != -1) goto done;
//扫描第1列的
KEY_OUT_3(1);
key_y = key_scan(0) == -1 ? 1 : -1;
KEY_OUT_3(0); //还原
// if(key_y != -1) goto done;
}
done: while(key_scan(0)!= -1); //等待按键松开
key_value= (key_x== -1 || key_y== -1) ? 0 : key_x*4 + key_y;
}
return key_value; //返回对应的键值 0表示未按下,正数表示得到对应的值
}
输出寄存器IDR的使用说明:在未配置任何模式下读取Pin是1状态(感觉是高阻态)
在STM32F103C8T6核心板使用时读出管脚不对劲,PA14的管脚为0;PA14原理是被SWD下载口占用,所有才不一样;参考链接
矩阵开关的思想:4个输入和4个输出:2x2的4种方式扫描;
我采用的是:上拉输入推挽输出0,3输出0,1输出0的无变化找位置;还有等待尝试…