第三个任务,实现键盘扫描,并通过串口通讯显示被按下的键位。

在上一节,了解了矩阵键盘的大致工作原理,那么应该怎样去实现显示被按下的按钮的键位呢?通过行列扫描,即拉高某一行或者某一列的电平,然后去扫描全部列或者全部行。

例如将所有列对应的针脚设置为推挽输出,剩下的全部行设置为上拉输入,然后假设我们按下了图上所示的7号按钮。程序应该怎么检测这一事件呢?先检测每一列,因为当按钮被按下后,某一列的电平肯定会变为0,那么我们可以知道是哪一列有按钮被按下了,此时我们需要知道是这一列中的哪一行的按钮被按下。那么通过下面的扫描函数可以得到返回值“1011”即0x0B,也就是第二行被按下;

uint8_t key_row[1]={0xff};//给key_row赋初值
key_row[0] = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)<<3;//第一行的结果左移3位
key_row[0] = key_row[0] | GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)<<2;//第二行的结果左移2位
key_row[0] = key_row[0] | GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)<<1;//第三行的结果左移1位
key_row[0] = key_row[0] | GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15);

然后我们可以通过一个switch语句来判断扫描函数返回的数值所代表的是哪一行被按下,此时通过这个函数将返回3;

if(key_row[0]!=0x0f){//如果返回值不为1111
	delay_ms(10);//延迟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://默认返回0
				return 0;
			}
	}else return 0;//没有被按下,只是机械抖动,返回0
}else return 0;//没有被按下,返回0

那么结合起来的思路就是,我们拉低每一列的电平,然后检查有没有哪一列的返回值不等于0,这是扫描列的思路;同理也可以通过扫描行来实现,具体代码如下:

[zhedie]

//key16.c
#include "key16.h"
#include "delay.h"

uint8_t key_row[1]={0xff};

void key_init(){

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

char key_row_scan(){//扫描列
	key_row[0] = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)<<3;
	key_row[0] = key_row[0] | GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)<<2;
	key_row[0] = key_row[0] | GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)<<1;
	key_row[0] = key_row[0] | GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15);
	
	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://默认返回0
				      return 0;
			}
		}else return 0;
	}else return 0;
	
}

char key_scan(){
	char key=0;//存放按键号
	char row_num=0;
	//第一行
	KEY_CLO0_OUT_LOW;//拉低,扫描
	if((row_num=key_row_scan())!=0){
		while(key_row_scan()!=0){
			key=0+row_num;
		}
	}
	KEY_CLO0_OUT_HIGH;//重新拉高
	//第二行
	KEY_CLO1_OUT_LOW;
	if((row_num=key_row_scan())!=0){
		while(key_row_scan()!=0){
			key=4+row_num;
		}
	}
	KEY_CLO1_OUT_HIGH;
	//第三行
	KEY_CLO2_OUT_LOW;
	if((row_num=key_row_scan())!=0){
		while(key_row_scan()!=0){
			key=8+row_num;
		}
	}
	KEY_CLO2_OUT_HIGH;
	//第四行
	KEY_CLO3_OUT_LOW;
	if((row_num=key_row_scan())!=0){
		while(key_row_scan()!=0){
			key=12+row_num;
		}
	}
	KEY_CLO3_OUT_HIGH;
	
	return key;
}

[/zhedie]

有了上面的代码后,在main.c中添加引用,调用函数进行判断,并通过串口输出结果:

具体代码:[zhedie]

//key16.h
#ifndef _KEY16_H
#define _KEY16_H

#include "sys.h"
#include "stm32f10x.h"

#include <string.h>

#define KEY_row0_Pin GPIO_Pin_8
#define KEY_row1_Pin GPIO_Pin_9
#define KEY_row2_Pin GPIO_Pin_10
#define KEY_row3_Pin GPIO_Pin_11

void key_init();
char key_scan();
char key_row_scan();

#define KEY_CLO0_OUT_LOW  GPIO_WriteBit(GPIOD,GPIO_Pin_8,Bit_RESET) 
#define KEY_CLO1_OUT_LOW  GPIO_WriteBit(GPIOD,GPIO_Pin_9,Bit_RESET)
#define KEY_CLO2_OUT_LOW  GPIO_WriteBit(GPIOD,GPIO_Pin_10,Bit_RESET)
#define KEY_CLO3_OUT_LOW  GPIO_WriteBit(GPIOD,GPIO_Pin_11,Bit_RESET)

#define KEY_CLO0_OUT_HIGH  GPIO_WriteBit(GPIOD,GPIO_Pin_8,Bit_SET) 
#define KEY_CLO1_OUT_HIGH  GPIO_WriteBit(GPIOD,GPIO_Pin_9,Bit_SET)
#define KEY_CLO2_OUT_HIGH  GPIO_WriteBit(GPIOD,GPIO_Pin_10,Bit_SET)
#define KEY_CLO3_OUT_HIGH  GPIO_WriteBit(GPIOD,GPIO_Pin_11,Bit_SET)


#endif
//led.c
#include "led.h"
#include "stm32f10x.h"
#include "delay.h"

void led_init(){
	
	GPIO_InitTypeDef GPIO_InitStruture;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //??PB???
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); //??PE???
	
	GPIO_InitStruture.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruture.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStruture.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruture);
	GPIO_SetBits(GPIOB,GPIO_Pin_9);
	
	GPIO_InitStruture.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruture.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStruture.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOE,&GPIO_InitStruture);
	GPIO_SetBits(GPIOE,GPIO_Pin_0);
	
	GPIO_InitStruture.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruture.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStruture.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOE,&GPIO_InitStruture);
	GPIO_SetBits(GPIOE,GPIO_Pin_1);

}

void Led_shine(char key_num){//定义了闪烁函数,根据key_num的值LED1闪烁
	int t;
	switch(key_num){
		case 1: 
			for(t=1;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 2: 
			for(t=2;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 3: 
			for(t=3;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 4: 
			for(t=4;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 5: 
			for(t=5;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 6: 
			for(t=6;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 7: 
			for(t=7;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 8: 
			for(t=8;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 9: 
			for(t=9;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 10: 
			for(t=10;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 11: 
			for(t=11;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 12: 
			for(t=12;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 13: 
			for(t=13;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 14: 
			for(t=14;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 15: 
			for(t=15;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
		case 16: 
			for(t=16;t>0;t--){
				LED1off;
				delay_ms(250);
				LED1on;
				delay_ms(250);
			}
			break;
			}
}
//led.h
#ifndef _LED_H
#define _LED_H

#include "sys.h"

#define LED1on GPIO_SetBits(GPIOE,GPIO_Pin_1)
#define LED1off GPIO_ResetBits(GPIOE,GPIO_Pin_1)
#define LED2 PEout(0)
#define LED3 PBout(9)

void led_init();
void Led_shine(char key_num);//声明了Led_shine函数

#endif
//main.c
#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include "key16.h"
#include "stdio.h"
#include "usart.h"

int main(void)
{
	char key_num;
	int t;
	
	key_init();
	led_init();
	delay_init();

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	uart_init(115200);	 	

	while(1)
	{
		key_num=key_scan();
		if(key_num>0&&key_num<17){
			printf("Button %d Pressed!\r\n",key_num);
			printf("LED shine %d times!\r\n",key_num);
			printf("<====This is a Line====>\r\n\r\n");
			Led_shine(key_num);
		}
	}	
}

[/zhedie]