这里采用的八个端口为PA0-PA7。
此处先给出矩阵键盘的原理图:
一、八个端口采用开漏输出,配置上拉电阻,实现同51一样的双向IO口功能。
//按键初始化函数
void KEY_Init(void){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStructure.GPIO_Pin|= GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
//这里这么写是因为复制代码块里,太长了,所以分成两部分。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
}
以上初始化配置完成之后,精华部分如下:
/*mode =1代表连按,mode = 0代表单按*/
u8 matrixkey(int mode){
u8 row = 0;
u8 column =0;
static u8 key = 1;
GPIO_Write(GPIOA,0x0F);
if(key&&(GPIO_ReadInputData(GPIOA)&0XFF)!=0X0F){
delay_ms(10);//按下消抖
column = GPIO_ReadInputData(GPIOA)&0X0F;
GPIO_Write(GPIOA,0xF0);
delay_ms(1);//这个语句超级重要!!!
row = GPIO_ReadInputData(GPIOA)&0XF0;
LCD_ShowNum(100,20,row+column,4,12);
key = 0;
}
if(mode) key = 1;
if( (GPIO_ReadInputData(GPIOA)&0XFF) == 0X0F){
delay_ms(10); //松手消抖
key = 1;
}
return row+column;
}
因为配置成端口开漏,上拉电阻模式,
判断第几行的时候,矩阵键盘的1-4行输出逻辑1与5-8行输出的逻辑0,线与为0;
判断第几列的时候,矩阵键盘的5-8行输出逻辑1与1-4行输出的逻辑0,线与为0;
最后得到的行与列相加返回。
区别同51的代码,这里判断按键按下后,有个delay_ms(1)的语句极为重要,不一定需要1ms可以更小,让CPU有一定的反应时间后,再次读取端口电平状态。(也只是推测,具体的原因,以后要深究。)
在主函数里即
int main(void){
u8 count =1;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为115200
LCD_Init(); //初始化LCD FSMC接口
KEY_Init();
LCD_Clear(BLUE);
while(1) {
switch(matrixkey(0)){
/*第一行*/
case 0xee:count=count+1; break; case 0xde:count=count+2;break;
case 0xbe:count=count+3; break; case 0x7e:count=count+4;break;
/*第二行*/
case 0xed:count=count+5; break; case 0xdd:count=count+6;break;
case 0xbd:count=count+7; break; case 0x7d:count=count+8;break;
/*第三行*/
case 0xeb:count=count+9; break; case 0xdb:count=count+10;break;
case 0xbb:count=count+11;break; case 0x7b:count=count+12;break;
/*第四行*/
case 0xe7:count=count+13;break; case 0xd7:count=count+14;break;
case 0xb7:count=count+15;break; case 0x77:count=count+16;break;
}
LCD_ShowNum(100,100,count,4,12);
}
}
二、动态配置成4行输出,4行输入。
/*四行输出,四列输入*/
void R_Out_C_Input(void){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
}
/*四列输出,四行输入*/
void C_Out_R_Input(void){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
}
动态配置输入输出的矩阵键盘实现代码如下:
u8 matrixkey(int mode){
u8 row = 0;
u8 column = 0;
static u8 key =1;
GPIO_Write(GPIOA,0); //这里主函数里初始化后,PA0-3输出,PA4-7输入,让PA0-3输出0
if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){
delay_ms(10);
column = GPIO_ReadInputData(GPIOA)&0XF0; //获取列值
C_Out_R_Input();//PA0-3输入,PA4-7输出
delay_ms(1);
GPIO_Write(GPIOA,0);
row = GPIO_ReadInputData(GPIOA)&0X0F;//获取行值
LCD_ShowNum(100,20,row+column,4,12);
key = 0;
R_Out_C_Input();//重新配置成低四位输出,高四位输入。
}
if(mode)key = 1;
if( (GPIO_ReadInputData(GPIOA)&0XF0) == 0XF0){
delay_ms(10);//松手消抖
key = 1;
}
return row+column;
}
主函数同上,只不过将KEY_Init();这条语句改为R_Out_C_Input();
三、低四位输出,高四位输入模式固定,逐行扫描,获取1-16按钮。
主函数同二、,端口初始化配置即void R_Out_C_Input(void);函数
矩阵键盘扫描实现略长,看起来复杂,不过是依葫芦画瓢。
但最重要的是逐行扫描,必须每一行扫描,如果有按键按下,要进行松手检测,否则实现不了。
具体的心得体会,以后回来再改。
while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0); //松手检测
紧接着delay_ms(10); 松手消除抖动。
u8 matrixkey(int mode){
static u8 key =1;
GPIO_Write(GPIOA,0x0e); //第一行输出到第五-八行
if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){
delay_ms(10);
key = 0;
switch(GPIO_ReadInputData(GPIOA)&0xF0){
case 0xe0:return 0xee;
case 0xd0:return 0xde;
case 0xb0:return 0xbe;
case 0x70:return 0x7e;
}
}
while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0); //松手检测
delay_ms(10);
GPIO_Write(GPIOA,0x0d);//第二行输出到第五-八行
if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){
delay_ms(10);
key = 0;
switch(GPIO_ReadInputData(GPIOA)&0xF0){
case 0xe0:return 0xed;
case 0xd0:return 0xdd;
case 0xb0:return 0xbd;
case 0x70:return 0x7d;
}
}
while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0);
delay_ms(10);
GPIO_Write(GPIOA,0x0b);//第三行输出到第四-八行
if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){
delay_ms(10);
key = 0;
switch(GPIO_ReadInputData(GPIOA)&0xF0){
case 0xe0:return 0xeb;
case 0xd0:return 0xdb;
case 0xb0:return 0xbb;
case 0x70:return 0x7b;
}
}
while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0);
delay_ms(10);
GPIO_Write(GPIOA,0x07);//第四行输出到第五-八行
if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){
delay_ms(10);
key = 0;
switch(GPIO_ReadInputData(GPIOA)&0xF0){
case 0xe0:return 0xe7;
case 0xd0:return 0xd7;
case 0xb0:return 0xb7;
case 0x70:return 0x77;
}
}
if(mode) key =1;
if((GPIO_ReadInputData(GPIOA)&0xF0)==0xF0)
key =1;
return 0;
}
比较:
三种方案其实,第二种方案比较通用,第一种比较简单易懂,前两种都比较好,因为按键按下不松开,并不会影响CPU一直停留在while()循环里啥都不干。
最后的实验效果:
之前用的板子是STM32F107,数据手册中的GPIO口一些输出输入方式的配置与STM32F407有较大不同,这里以STM32F407的为基准。32的单片机跟51不同,51的端口不需要配置成这个端口做输出还是做输入用,双向IO意味着我端口输出XXH(八位),照样可以直接读取到,而32以及PIC系列的单片机,要想读取端口的状态,需要配置成输入模式。
但是32系列的单片机有点好处,即只要设置成开漏输出模式下,再加上拉电阻,采用GPIO_ReadInputData();
即能实现双向IO功能。
针对F407而言,内部的上拉电阻开关,无论是输入还是输出模式都可以打开,所以用第一种方案好。我看了看手册,这里配置成输出模式,并没指明开漏模式下才能端口读取电平状态,应该是两者都可以(通用或者推挽模式)。
而F103系列的,上拉电阻开关只针对输入模式下,(输出模式下,内部上、下拉电阻功能被禁止)如果要实现第一种方案,还需要额外加上拉电阻,麻烦许多,所以用二、三方案比较好。同时读取电平必须要开漏输出。
在此附上F407的输出GPIO手册截图吧:
上一篇:STM32笔记(二)(寄存器)——矩阵键盘
下一篇:基于 STM32F407 使用 4*4 矩阵键盘
推荐阅读最新更新时间:2024-11-16 21:44
设计资源 培训 开发板 精华推荐
- VN5E050J评估板
- LTC3422 的典型应用 - 具有输出断开连接的 1.5A、3MHz 同步升压型 DC/DC 转换器
- EN6360QI 8A 同步高集成 DC-DC PowerSoC 的典型应用
- XC7K420T_DDR3 X 2 64bit Sch原理图
- MAXREFDES1167:使用MAX17681A的15V / 200mA或12V / 100mA,Iso-Buck DC-DC转换器
- 便携式 3V 模拟放大
- LT6657AHMS8-2.5 具有电流限制的升压输出电流的典型应用电路
- EVAL-ADF7020DBZ3,ADF7020 ISM 频段收发器评估板
- 具有峰值电流软启动、小尺寸的 LTC3632IDD、3.3V、20mA 稳压器的典型应用电路
- 应用提示 7 使用 3.3V 低电流 LDO 稳压器