单片机STM32入门——(3)矩阵按键

1.理论分析

1.1键盘扫描方式

我们所用到的键盘为4×4矩阵键盘,先分析矩阵键盘的电路连接方式及其扫描方式。根据电路可知

  • 第1 ~ 4行按键的一端分别为P30 ~ P33引脚
  • 第1 ~ 4列按键的另一端分别为P34 ~ P37引脚

4×4矩阵键盘电路
我们将矩阵键盘的接口分别定义为GPIO端口

  • P30-P33(行)引脚分别定义为PD8-PD11(GPIOD端口),且模式为上拉输入(即有按键按下时为0,没有按键按下时为1)
  • P34-P37(列)引脚分别定义为PB12-PB15(GPIOB端口),且模式为推挽输出

我们这里采用将列置0,然后扫描行,当哪一行被按下后,对应的电平置0,此时该按键导通,即可读取到按键值。

1.2行扫描逻辑

首先我们需要定义一个数组用来存放行扫描的结果。
分别存放PD8~PD11的电平值

  • 从GPIOD端口获取行扫描值
  • 判断行扫描值的二进制数
  • 返回对应是第几行按键按下

行扫描程序执行流程

Created with Raphaël 2.2.0 Start 行扫描值 0111 ? return 1 end 1011 ? return 2 1101 ? return 3 1110 ? return 4 return 0 yes no yes no yes no yes no

其中该程序在进行按键扫描时,同样需要进行按键消抖,已经在前一篇中说明,这里就不再赘述。
行扫描结果对应的按键值

PD 8PD 9PD10PD11按键结果
0111第一行被按下
1011第二行被按下
1101第三行被按下
1110第四行被按下

1.3列扫描逻辑

列扫描的执行过程

  • 将每一列依次置0
  • 将行扫描函数返回值赋值给定义的变量key_row_num
  • 判断该变量是否为0
  • 得出按键值

列扫描程序执行流程

Created with Raphaël 2.2.0 Start key_row_num=key_row_scan key_row_num = 0 ? End key_num= X +key_row_num yes no

2.程序编写

2.1按键扫描程序

2.1.1按键初始化

void key_init(){

	GPIO_InitTypeDef GPIO_InitStruture;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//打开PB时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);//打开PD时钟
	
	//定义PB12、PB13、PB14、PB15为推挽输出、分别定义为列
	GPIO_InitStruture.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruture.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
	GPIO_InitStruture.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruture);
	
	//定义PD8、PD9、PD10、PD11为上拉输入、分别定义为四行
	GPIO_InitStruture.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruture.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11;
	GPIO_Init(GPIOD,&GPIO_InitStruture);
}

2.1.2按键扫描程序头文件

头文件中我们编写了按键的行引脚分别对应P8 ~ P11端口,以及按键列引脚的高低电平,分别对应PB12 ~ PB15端口。

#ifndef _KEY16_H
#define _KEY16_H

#include "sys.h"
#include "stm32f10x.h"
#include <string.h>

//定义行按键的引脚
#define key_row0_Pin GPIO_Pin_8//定义P8为行1
#define key_row1_Pin GPIO_Pin_9//定义P9为行2
#define key_row2_Pin GPIO_Pin_10//定义P10为行3
#define key_row3_Pin GPIO_Pin_11//定义P11为行4

//行扫描函数、列扫描函数、初始化函数声明
void key_init();
char key_row_scan(void);
char key_scan(void);

//定义列的低电平输出
#define KEY_CLO0_OUT_LOW  GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_RESET) 
#define KEY_CLO1_OUT_LOW  GPIO_WriteBit(GPIOB,GPIO_Pin_13,Bit_RESET)
#define KEY_CLO2_OUT_LOW  GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_RESET)
#define KEY_CLO3_OUT_LOW  GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_RESET)

//定义列的高电平输出
#define KEY_CLO0_OUT_HIGH  GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_SET) 
#define KEY_CLO1_OUT_HIGH  GPIO_WriteBit(GPIOB,GPIO_Pin_13,Bit_SET)
#define KEY_CLO2_OUT_HIGH  GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_SET)
#define KEY_CLO3_OUT_HIGH  GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_SET)

#endif

2.1.3行扫描函数

char key_row_scan(void){
	
	key_row[0] = GPIO_ReadInputDataBit(GPIOD, key_row0_Pin)<<3;//读取PD8/第1行
	key_row[0] = key_row[0] | (GPIO_ReadInputDataBit(GPIOD, key_row1_Pin)<<2);//读取PD9/第2行
	key_row[0] = key_row[0] | (GPIO_ReadInputDataBit(GPIOD, key_row2_Pin)<<1);//读取PD10/第3行
	key_row[0] = key_row[0] | (GPIO_ReadInputDataBit(GPIOD, key_row3_Pin));//读取PD11/第4行
	
	if(key_row[0] != 0x0f)
	{
		delay_ms(10);
			if(key_row[0] != 0x0f)
			{
				switch(key_row[0])
				{
					case 0x07:	//0111 第1行被按下
						return 1;	
					case 0x0b:	//1011 第2行被按下
						return 2;	
					case 0x0d:	//1101 第3行被按下
						return 3;
					case 0x0e:	//1110 第4行被按下
						return 4;
					default	:
						return 0; //没有按键被按下
				}		
			}else return 0;	
	}else return 0;	
} 

2.1.3列扫描函数

char key_scan(void){
	char key_num=0;       //1-16对应的按键数
    char key_row_num=0;        //行扫描结果记录
    
    KEY_CLO0_OUT_LOW;        
    if( (key_row_num=key_row_scan()) != 0 )
    { 
        while(key_row_scan() != 0);  //消抖
        key_num = 0 + key_row_num;
    }
    KEY_CLO0_OUT_HIGH;
    
    KEY_CLO1_OUT_LOW;        
    if( (key_row_num=key_row_scan()) != 0 )
    { 
        while(key_row_scan() != 0);
        key_num = 4 + key_row_num;
        //printf("Key_Clo_2\r\n");
    }
    KEY_CLO1_OUT_HIGH;
    
    KEY_CLO2_OUT_LOW;    
    if( (key_row_num=key_row_scan()) != 0 )
    { 
        while(key_row_scan() != 0);
    key_num = 8 + key_row_num;
        //printf("Key_Clo_3\r\n");
    }
    KEY_CLO2_OUT_HIGH;
    
    KEY_CLO3_OUT_LOW;    
    if( (key_row_num=key_row_scan()) != 0 )
    {
        while(key_row_scan() != 0);
        key_num = 12 + key_row_num;
    }
    KEY_CLO3_OUT_HIGH;
    
    return key_num;
}

2.2主程序

主程序通过调用行扫描函数和列扫描函数来输出按键值。

#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include "key16.h"
#include "stdio.h"
#include "usart.h"

int main(void)
{
	char key_num_end;

	delay_init();
	key_init();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	uart_init(115200);//波特率

	while(1)
	{
		key_num_end = key_scan();
		if(key_num_end>0&&key_num_end<17){
			printf("Key_NUM = %d \r\n",key_num_end); //按下1-16个按键的操作
      printf("= = = = = = = = = = = \r\n");		
		}	
	}	
}
  • 28
    点赞
  • 132
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
STM32单片机是一种高性能、低功耗的32位微控制器,广泛应用于嵌入式系统的开发中。其原理是基于ARM Cortex-M系列的核心,具有丰富的外设和高性能的计算能力,可以实现多种功能。 STM32单片机的应用非常广泛。其可以应用于智能家居、医疗仪器、工业自动化等领域。它可以用来控制各种传感器,如温度传感器、湿度传感器等,实现对环境的监测和控制。同时,它也可以用于控制各种执行机构,如电机和执行器,实现对设备的控制和运动。此外,STM32单片机还可以应用于通信领域,如无线模块和网络模块的控制,实现设备之间的数据传输和通信。 基于Proteus的虚拟仿真可以帮助开发者在进行STM32单片机的开发过程中,不需要实际搭建硬件电路,即可进行软件开发和调试。Proteus是一款功能强大的虚拟仿真软件,可以模拟STM32单片机和外设的工作过程。它可以提供丰富的模型库和仿真环境,方便开发者进行程序的编写、调试和验证。 通过Proteus的虚拟仿真,开发者可以在电脑上实现对STM32单片机的全面测试,包括外设的连接和数据交互。开发者可以使用Proteus提供的虚拟示波器、虚拟显示器等工具,模拟实际硬件的工作状态,及时查看和调试程序。 总而言之,STM32单片机的原理和应用非常广泛,通过Proteus的虚拟仿真可以帮助开发者在进行STM32单片机的开发过程中,提高开发效率和减少成本,并且可以更好地进行软件开发和调试。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

with钦捷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值