关于stm32驱动矩阵键盘的一些问题,以及矩阵键盘的替代方法(真香)

关于stm32驱动矩阵键盘的一些问题,以及矩阵键盘的替代方法

关于矩阵键盘

对于一般的按键来说,我一般倾向于中断方式来触发。一是节省单片机的资源,不必让它来回扫描。二是中断非常高效,处理起来也很方便。但是对于矩阵键盘,我绞尽脑汁也没有想出来办法完全通过中断来实现。下面介绍一下我踩过的坑

中断+状态检测(坑)

先上图
立创eda画的,因为我用的是微动开关,所以有四个引脚,只用到了两个
上图是用立创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个按键(状态))

                                GPIO.c文件
#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;        //PG1,PG2  用来接按键的,产生中断
	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);
	
	//************EXIT_Init***************//
	
	EXTI_InitStruct.EXTI_Line = EXTI_Line1|EXTI_Line2;   //PG1  PG2 的中断通道
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;   //中断
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;   //本来上拉,输入的是高电平。然后按下按钮变为低电平
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStruct);
	
	//************NVIC_init***************//
	NVIC_InitStruct.NVIC_IRQChannel =  EXTI1_IRQn;           //中断通道1   中断优先级配置完成
	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;           //中断通道2
	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();          //  按了超过8次  ,主要是防止程序出意外
	}
}

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);
}
//             GPIO.h文件
#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
//                 main.c  文件
#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)        //输入是PG1
	{
		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)        //输入是PG2
	{
		key_push = 0x01;
		code = (code<<1)+key_push;
		push_time++;
	}
}


//             delay.h       文件
#ifndef __DELAY_H
#define __DELAY_H 			   
#include <stm32f10x.h>
	 
void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);

#endif
        //delay.c文件
#include "delay.h"

static u8  fac_us=0;  //us延时倍乘数     
static u16 fac_ms=0;  //ms延时倍乘数




//注意:使用时需要添加misc.c文件到项目中
//初始化延迟函数
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void delay_init()
{
 SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
 fac_us=9;  
 fac_ms=9*1000;     
}      


/*******************************************************************************
* 函 数 名: delay_us
* 函数功能  : us延时,
* 输    入: nus:要延时的us数
注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)
* 输    出: 无
*******************************************************************************/           
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;         //清空计数器   
}


/*******************************************************************************
* 函 数 名: delay_ms
* 函数功能  : ms延时,
* 输    入: nms:要延时的ms数
注意:nms的值,SysTick->LOAD为24位寄存器,不要大于0xffffff*8*1000/SYSCLK
对72M条件下,nms<=1864ms 
* 输    出         : 无
*******************************************************************************/
void delay_ms(u16 nms)
{          
 u32 temp;     
 SysTick->LOAD=(u32)nms*fac_ms;  //时间加载(SysTick->LOAD为24bit)
 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;        //清空计数器          
} 

完毕,以后不用再那么复杂的驱动矩阵键盘了。直接两个按键搞定

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值