内容
最近为电子竞赛做准备,存在一个问题,需要设计一个简单的信号发生器。您需要使用矩阵键盘输入要生成的信号的类型,频率和幅度。 [代码链接足球外围 ,请单击此处]。写以下文章总结了我尝试过的两种单片机操作矩阵键盘的方法。
一、矩阵键盘的结构和原理
实际的矩阵键盘可能看起来像这样:
它也可能像这样:
无论外观如何,它们的内部结构都相似:
每个矩阵键盘具有8个信号端口,其中四个连接到行,其余四个连接到列。当按下某个按钮时,相应的行和列端口将被该按钮短路。如何使用此功能让STM32确定按下了哪个按钮?作者总结了以下两种常用方法。
二、扫描矩阵键盘的原理和实现
首先,请明确将STM32 GPIO配置为开漏输出+上拉电阻时,具有准双向IO的功能。
使用扫描方法判断键盘按下的基本思想是:首先将所有行设置为低,将所有列设置为高32 矩阵键盘,读取每一行的级别,然后将最高的行设置为被按下的行;设置为低凤凰体育 ,将所有行设置为高,读取每一列的级别,高的为下一列。代码如下:
三、中断矩阵键盘的原理和实现
类似地,首先要阐明GPIO的配置。在中断模式下,四行应配置为推挽输出,四列应配置为输入+上拉电阻+下降沿中断模式。当然,行和列的交换也是可能的。
中断矩阵键盘的基本思想是在初始化期间将四行拉低。当按下一个键时,该列的相应端口将检测到下降沿,程序将进入该列的中断服务功能。在服务功能中,将四行拉高,然后依次向下拉每行。同时,检查列的端口是否为低。如果为低亚博直播 ,则表明已按下行和列。代码如下:
//矩阵键盘端口初始化
//PF1~4接列 PF5~8接行
void MatrixKEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//通用输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIOF5678
GPIO_ResetBits(GPIOF,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8);//初始化输出置低
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIOF1234
}
*/
//第一行
void EXTI1_IRQHandler(void){
u8 i;
delay_ms(50);
if(PFin(1)==0){/第一行
EXTIX1_4_disable();//关闭矩阵键盘的中断
for(i=5;i<9;i++){
GPIO_SetBits(GPIOF,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8);//置为高电平
PFout(i)=0;
delay_ms(50);
if(PFin(1)==0){
switch(i){
case(5):
num=1;
break;
case(6):
num=2;
break;
case(7):
num=3;
break;
case(8):
num=12;
break;
default:num=16;
} }
}
GPIO_ResetBits(GPIOF,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8);//置为低电平
EXTIX1_4_enable();
}
EXTI_ClearITPendingBit(EXTI_Line1);//清除中断标志位*/
}
void EXTI2_IRQHandler(void){
u8 i;
delay_ms(50);
if(PFin(2)==0){/第二行
EXTIX1_4_disable();//关闭矩阵键盘的中断
for(i=5;i<9;i++)
{
GPIO_SetBits(GPIOF,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8);//置为高电平
PFout(i)=0;
delay_ms(50);
if(PFin(2)==0){
switch(i){
case(5):
num=4;
break;
case(6):
num=5;
break;
case(7):
num=6;
break;
case(8):
num=13;
break;
default:num=16;
}}
}
GPIO_ResetBits(GPIOF,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8);//置为低电平
EXTIX1_4_enable();
}
EXTI_ClearITPendingBit(EXTI_Line2);//清除中断标志位*/
}
void EXTI3_IRQHandler(void){
u8 i;
delay_ms(50);
if(PFin(3)==0){第三行
EXTIX1_4_disable();//关闭矩阵键盘的中断
for(i=5;i<9;i++)
{
GPIO_SetBits(GPIOF,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8);//置为高电平
PFout(i)=0;
delay_ms(50);
if(PFin(3)==0){
switch(i){
case(5):
num=7;
break;
case(6):
num=8;
break;
case(7):
num=9;
break;
case(8):
num=14;
break;
default: num=16;
}}
}
GPIO_ResetBits(GPIOF,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8);//置为低电平
EXTIX1_4_enable();
}
EXTI_ClearITPendingBit(EXTI_Line3);//清除中断标志位*/
}
void EXTI4_IRQHandler(void){
u8 i;
delay_ms(50);
if(PFin(4)==0){第四列
EXTIX1_4_disable();//关闭矩阵键盘的中断
for(i=5;i<9;i++)
{
GPIO_SetBits(GPIOF,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8);//置为高电平
PFout(i)=0;
delay_ms(50);
if(PFin(4)==0){
switch(i){
case(5):
num=10;
break;
case(6):
num=0;
break;
case(7):
num=11;
break;
case(8):
num=15;
break;
default:num=16;
}}
}
GPIO_ResetBits(GPIOF,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8);//置为低电平
EXTIX1_4_enable();
}
EXTI_ClearITPendingBit(EXTI_Line4);//清除中断标志位*/
}
//中断优先级配置
void NVICx_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//外部中断1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//中断通道使能
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;//外部中断2
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//中断通道使能
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;//外部中断3
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//中断通道使能
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;//外部中断4
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//中断通道使能
NVIC_Init(&NVIC_InitStructure);
}
//外部中断初始化程序
//初始化PF1~4为中断输入.
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVICx_Init();
MatrixKEY_Init(); //按键对应的IO口初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF, EXTI_PinSource1);//PF1 连接到中断线1
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF, EXTI_PinSource2);//PF2 连接到中断线2
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF, EXTI_PinSource3);//PF3 连接到中断线3
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF, EXTI_PinSource4);//PF4 连接到中断线4
/* 配置EXTI_Line1,2,3,4 */
EXTI_InitStructure.EXTI_Line = EXTI_Line1 | EXTI_Line2 | EXTI_Line3 | EXTI_Line4;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中断线使能
EXTI_Init(&EXTI_InitStructure);//配置
}
//关闭外部中断pF1~4
void EXTIX1_4_disable(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
/* 配置EXTI_Line1,2,3,4 */
EXTI_InitStructure.EXTI_Line = EXTI_Line1 | EXTI_Line2 | EXTI_Line3 | EXTI_Line4;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = DISABLE;//中断线失能
EXTI_Init(&EXTI_InitStructure);//配置
}
//开启外部中断pb1~4
void EXTIX1_4_enable(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
/* 配置EXTI_Line1,2,3,4 */
EXTI_InitStructure.EXTI_Line = EXTI_Line1 | EXTI_Line2 | EXTI_Line3 | EXTI_Line4;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中断线使能
EXTI_Init(&EXTI_InitStructure);//配置
}
四、两种方案的优缺点
扫描方法占用的资源较少,但是随着工程复杂度的增加,密钥变得非常不敏感;
中断方法占用了相当多的资源(四个中断行)亚博网页版 ,但是当工程复杂度增加时32 矩阵键盘,它可以很好地保持键的敏感性。
老王
最新评论