关于stm32驱动矩阵键盘的一些问题,以及矩阵键盘的替代方法
关于矩阵键盘
对于一般的按键来说,我一般倾向于中断方式来触发。一是节省单片机的资源,不必让它来回扫描。二是中断非常高效,处理起来也很方便。但是对于矩阵键盘,我绞尽脑汁也没有想出来办法完全通过中断来实现。下面介绍一下我踩过的坑
中断+状态检测(坑)
先上图 上图是用立创eda画的,因为用的是微动开关,所以显得比较杂乱。看下图 这时我采用的方法是,P14,P15,P16,P17输入上拉,同时设为外部中断通道,下降沿触发。P10,P11,P12,P13输入下拉。这时设想的是,如果我按下按钮S1,电平被拉低,那么触发P14口中断,在中断内检测一下P10,P11,P12,P13哪个口的电平升高了,在此处是P10口,即可确定是S1按键被按下。但理想很丰满,显示很骨感。由于IO口设置的是输入上拉与输入下拉,在stm32内部上下拉电阻都为40K欧。这样按下按键后,连接按键的两个IO口的电平均为1.6V左右。在stm32内用的是TTL电平,高于2V算高电平,低于0.8V是低电平。那你说1.6V算什么嘛。
如果想要用这种方法,估计得设计电平转换电路了,但是一个矩阵键盘不值得,换个方法。
我于是想着换成外部上拉或下拉,IO口全部设成浮空输入。但是推理一下发现,这和上面哪个方法是差不多的,无非是外部上下拉。但是我可以改变外部上下拉电阻的大小,从而控制按下按钮后的状态电平,但这时问题又出现了,我只能满足一个方向,即如果我想满足在行的中断的条件,那么我将无法在列处检测到电平变化,我如果能在列处检测到电平变化,那么我的行将无法产生中断。
于是我想着将行设为推挽输出,不断的循环,但是只有一行输出为高电平,其它均为低电平。列设为下拉输入,上升沿触发中断。假如P15一行为高电平,其它均为低电平。按下S5则P10可以检测到上升沿。那么可以检测到是哪个按钮。但事实证明是不可以的,具体我也不知道为什么。总是有两行一直产生中断,导致我程序根本跑不下去。(😓)
但是在这里要注意,如果你误按下了两个开关,如S9和S13。那么相当于短路,瞬间产生大电流可能会烧掉板子,加个保护措施——在每个IO口前加个限流电阻(小一点,不要影响到下拉电阻的作用 1K左右即可)。
一个改进方法(真香)
想到之前的按键实验,一般只用到了两个按键,且加上适当延时后,按键精确性非常高。于是我想到了利用两个按键组成编码。如按键1按下为0,按键2按下为1。以不同的按键顺序组成不同的编码,既可以得到无数种状态。想法可行,试了一下还真可以(😊)
直接上程序吧。(我此次实验的编码长度为8位,可以表示256个按键(状态))
#include <GPIO.h>
#include <delay.h>
u8 row = 0;
extern u8 push_time;
extern u8 code;
void GPIO_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOG, GPIO_PinSource1);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOG, GPIO_PinSource2);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOG,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOG,&GPIO_InitStruct);
EXTI_InitStruct.EXTI_Line = EXTI_Line1|EXTI_Line2;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority= 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;
NVIC_Init(&NVIC_InitStruct);
}
void check_code()
{
if(push_time==8)
{
switch(code)
{
case 0xf0:
{
led_on();
break;
}
case 0x0f:
{
led_off();
break;
}
default:
break;
}
}
else if(push_time>8)
{
push_time=0;
led1_on();
}
}
void led_on()
{
GPIO_SetBits(GPIOG,GPIO_Pin_3);
push_time = 0;
GPIO_SetBits(GPIOG,GPIO_Pin_4);
delay_ms(500);
GPIO_ResetBits(GPIOG,GPIO_Pin_4);
}
void led_off()
{
GPIO_ResetBits(GPIOG,GPIO_Pin_3);
push_time = 0;
GPIO_SetBits(GPIOG,GPIO_Pin_4);
delay_ms(500);
GPIO_ResetBits(GPIOG,GPIO_Pin_4);
}
void led1_on()
{
push_time = 0;
GPIO_SetBits(GPIOG,GPIO_Pin_4);
delay_ms(2000);
GPIO_ResetBits(GPIOG,GPIO_Pin_4);
}
#ifndef GPIO_H
#define GPIO_H
#include <stm32f10x.h>
void GPIO_init(void);
void check_code(void);
void led1_on(void);
void led_on(void);
void led_off(void);
#endif
#include <GPIO.h>
#include <delay.h>
u8 i=0;
u8 key_push;
u8 code = 0;
u8 push_time = 0;
int main()
{
GPIO_init();
delay_init();
while(1)
{
check_code();
}
}
void EXTI1_IRQHandler(void)
{
delay_ms(10);
EXTI_ClearITPendingBit(EXTI_Line1);
if(GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_1)==0)
{
key_push = 0x00;
code = (code<<1)+key_push;
push_time++;
}
}
void EXTI2_IRQHandler(void)
{
delay_ms(10);
EXTI_ClearITPendingBit(EXTI_Line2);
if(GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_2)==0)
{
key_push = 0x01;
code = (code<<1)+key_push;
push_time++;
}
}
#ifndef __DELAY_H
#define __DELAY_H
#include <stm32f10x.h>
void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);
#endif
#include "delay.h"
static u8 fac_us=0;
static u16 fac_ms=0;
void delay_init()
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
fac_us=9;
fac_ms=9*1000;
}
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us;
SysTick->VAL=0x00;
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
SysTick->VAL =0X00;
}
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms;
SysTick->VAL =0x00;
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
SysTick->VAL =0X00;
}
完毕,以后不用再那么复杂的驱动矩阵键盘了。直接两个按键搞定
|