最近需要用到矩阵键盘,在网上搜了很久看见的大多数都是根据判断寄存器的值来进行矩阵键盘取值(反正我找了一天,免费的文章,大都是这样的,付费的我也不知道),因为本人是初学者,对寄存器的操作不懂,刚开始也照着写了,逻辑上没有问题,但最后返回不了值,查了很久的问题,也不知道哪里有问题,最后就放弃了。
然后就想到了用for循环写应该也可以。
思路:
矩阵键盘总共有8个引脚,可以控制16个按键。
首先将行和列分开,这里选择连续的GPIO口,方便连线
最开始是因为涉及到寄存器操作,所以需要以0,1,2,3|4,5,6,7,这种方式去选择GPIO口,方便运算
前四个是行,记为1,2,3,4。后四个是列,记为5,6,7,8。(对应KEY1......)
代码实现:
keyboard.c
按键初始化
/********按键初始化*************
行:
KEY1->GPIOF_0,
KEY2->GPIOF_1,
KEY3->GPIOF_2,
KEY4->GPIOF_3,
列:
KEY5->GPIOF_4,
KEY6->GPIOF_5,
KEY7->GPIOF_6,
KEY8->GPIOF_7,
*******************************/
void GPIOF_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE); // 使能GPIOF时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 行引脚设为推挽输出
GPIO_Init(GPIOF, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 列引脚设为上拉输入
GPIO_Init(GPIOF, &GPIO_InitStructure);
}
GPIO初始化,至于为什么一个要设置为上拉输入,本人水平有限,讲不太明白。大家可以去看其他的文章有讲这个。
按键扫描函数
uint8_t ScanKeyboard(void)
{
u16 i,j;
static uint8_t key_value = 0;
uint16_t line_pins[4] = {GPIO_Pin_0, GPIO_Pin_1, GPIO_Pin_2, GPIO_Pin_3};//行
uint16_t row_pins[4] = {GPIO_Pin_4, GPIO_Pin_5, GPIO_Pin_6, GPIO_Pin_7};//列
for ( i = 0; i < 4; ++i)
{
GPIO_ResetBits(GPIOF, line_pins[i]); // 选通一行,使其输出低电平
for ( j = 0; j < 4; ++j)
{
if (GPIO_ReadInputDataBit(GPIOF, row_pins[j]) == RESET) // 判断列引脚上是否有按键按下
{
key_value = i * 4 + j;
break;
}
}
GPIO_SetBits(GPIOF, line_pins[i]); // 取消选通,使其输出高电平
}
return key_value;
}
这里用到静态变量,防止没有按键按下的时候,返回0值 。
简单测试
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "keyboard.h"
int main(void)
{
u8 a=0;
u16 key=0;
u8 x=0;
u8 lcd_id[12]; //存放LCD ID字符串
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //LED端口初始化
LCD_Init();
POINT_COLOR=RED;
GPIOF_Init();
sprintf((char*)lcd_id,"LCD ID:%04X",lcddev.id);//将LCD ID打印到lcd_id数组。
while(1)
{
POINT_COLOR=RED;
LCD_ShowString(30,40,210,24,24,"hello world ^_^");
LCD_ShowString(30,70,200,16,16,"TFTLCD TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,lcd_id); //显示LCD ID
LCD_ShowString(30,130,200,12,12,"2023/5/14");
key=ScanKeyboard();
if(key==0)a=20;
if(key==1)a=21;
if(key==2)a=22;
if(key==3)a=23;
if(key==4)a=24;
if(key==5)a=25;
if(key==6)a=26;
if(key==7)a=27;
if(key==11)a=40;
LCD_ShowxNum(30,150,a,20,16,0);
x++;
if(x==12)x=0;
LED0=!LED0;
delay_ms(1000);
}
}
以上工程文件用的正点stm32f103zet6的例程。