前言
本次编写的是STM32F103系列,4*4矩阵按键,利用扫描方式获得按键值。编写完成检测是,发了疯一样,给孩子整怕了,还以为代码哪里跑偏了,仔细想一下不至于啊,就我这小技术,不至于发生这样的事,后面发现是烧入线坏了,接口不稳定,一直在重新接入导致串口一直在打印。后面换了根线就ok了。
原理图:本次测试的原理图如下,用到的应交分别为引:P5,PB4,PB3,PD2,PC12,PC11,PC10,PA15
原理:
一下图为参考:将行设置为上拉输入,列设置为推挽输出,扫描按键时,循环依次检测每一行的电频变化。先将第一行设置为低电平,其余均为高电平。再检测每一列的输入输入模式是否被触发。循环读取每一列的电频,当检测到某一列为低电平时则按键被按下。通过循环变量可以得到被按下的按键值。
初始化:
KEY_GROUP_Config函数
#define ROWS 4 //行
#define COLS 4 //列
GPIO_TypeDef * row_ports[ROWS] = {GPIOB, GPIOB, GPIOB, GPIOD}; //这里一一对应好每个引脚
uint16_t row_pins[ROWS] = {GPIO_Pin_5, GPIO_Pin_4, GPIO_Pin_3, GPIO_Pin_2};
GPIO_TypeDef * col_ports[COLS] = {GPIOC, GPIOC, GPIOC, GPIOA};
uint16_t col_pins[COLS] = {GPIO_Pin_12, GPIO_Pin_11, GPIO_Pin_10, GPIO_Pin_15};
void Key_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD, ENABLE);
GPIO_PinRemapConfig( GPIO_Remap_SWJ_Disable,ENABLE);//失能SWJ引脚,PA15,PB3,PB4
// 配置行引脚为输入模式,并启用内部上拉电阻
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; //将每一行设置为上拉输入模式
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;//频率为2MHz
for (int i = 0; i < ROWS; i++) { //循环初始化数组
GPIO_InitStruct.GPIO_Pin = row_pins[i];
GPIO_Init(row_ports[i], &GPIO_InitStruct);
}
// 配置列引脚为输出模式,并设置初始输出值为高电平
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//配置为推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
for (int i = 0; i < COLS; i++) {
GPIO_InitStruct.GPIO_Pin = col_pins[i];
GPIO_Init(col_ports[i], &GPIO_InitStruct);
GPIO_SetBits(col_ports[i],col_pins[i]);
}
}
按键扫描
初始化完成后,再主函数中循环扫描按键
uint16_t KEY_Scanf_Config(void),将按下得值返回
uint16_t KEY_Scanf_Config(void)
{
// unsigned char key_state[4][4] = {0};//获取按下的值
uint16_t val = 0;
for (int i = 0; i < COLS; ++i) {
// 设置当前列为低电平
GPIO_ResetBits(col_ports[i], col_pins[i]);
// 延时一段时间以稳定电平
Delay_ms(30);
// 检测列引脚的电平状态,并根据不同的状态设置按键状态
for (int j = 0; j <ROWS; ++j) {
if (GPIO_ReadInputDataBit(row_ports[j], row_pins[j])) {
// key_state[i][j] = 0; // 按键释放状态
} else if(!GPIO_ReadInputDataBit(row_ports[j], row_pins[j])){
//key_state[i][j] = 1;
LED_ON;
val = (j* ROWS) + i + 1; // 计算按键值
// printf("%d\n",val);
}
// 恢复当前行为高电平
//GPIO_SetBits(col_ports[i], col_pins[i]);
}
GPIO_SetBits(col_ports[i], col_pins[i]); //恢复高电平,一行为低电平其余行为高电平
}
if(val != 0){
printf("%d",val);//打印获得值
return val;//将只返回出去
}
return 0; //没有按下按键时候返回0,
}
key头函数:
#ifndef __KEY_GROUP_H
#define __KEY_GROUP_H
#include "stm32f10x.h"
#include "Delay.h"
#include "stdio.h"
#include "USART.h"
#include "LED.h"
uint16_t KEY_Scanf_Config(void);
void Key_Config(void);
#endif
串口部分
串口也放进来吧,毕竟需要打印出按键
#include "USART.h"
extern uint8_t flag;
void USART_Config_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//初始化GPIOA_PIN9|10号引脚,USART1,AFIO复用功能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 ;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
//初始化GPIO引脚
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA,&GPIO_InitStruct);
USART_InitStruct.USART_BaudRate = 115200; //波特率
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制
USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //发送接收模式
USART_InitStruct.USART_StopBits = USART_StopBits_1;//停止位
USART_InitStruct.USART_Parity = USART_Parity_No;//奇偶校验
USART_InitStruct.USART_WordLength = USART_WordLength_8b; //数据位
//初始化串口
USART_Init(USART1, &USART_InitStruct);
//配置接受中断源:USART_IT_RXNE
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
//配置中断优先级NVIC
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStruct);
USART_ClearFlag(USART1,USART_FLAG_TC|USART_FLAG_TXE);
//打开串口1
USART_Cmd(USART1, ENABLE );
}
void Send_Msg(uint8_t data)
{
USART_SendData(USART1,data);//发送信息
while(SET != USART_GetFlagStatus(USART1,USART_FLAG_TC));//发送标志位获取,知道发送全
}
void Rev_Msg(void)
{
if(SET==USART_GetFlagStatus(USART1,USART_FLAG_RXNE)){//接受数据
USART_ClearFlag(USART1,USART_FLAG_RXNE);//清除标志位置
USART_SendData(USART1,USART_ReceiveData(USART1));//发送信息到串口助手
}
}
extern uint8_t flag;//判断是否接受完全的标志位
extern char ptr[10]; //接受数据数组
volatile uint8_t recv_data;
volatile uint32_t strIndex = 0;
void USART1_IRQHandler(void)
{
//中断服务函数
if(SET == USART_GetFlagStatus(USART1,USART_IT_RXNE))
{
//清除标志位
USART_ClearITPendingBit(USART1,USART_IT_RXNE);//
recv_data = USART_ReceiveData(USART1);//把接受到的数据放到变量中
//USART_SendData(USART1,recv_data);//发送信息到串口助手
// while(SET != USART_GetFlagStatus(USART1,USART_FLAG_TC));
if(recv_data =='\n'){
ptr[strIndex] = '\0';
flag = 1;
strIndex = 0;
}else {
ptr[strIndex] = recv_data;
strIndex++;
}
}
}
int fputc(int ch,FILE *f)
{
USART1->DR=(uint8_t)ch; //将其强转为char类型数据放入数据寄存器中
while(0==(USART1->SR&(1<<6))); //循环发送
USART1->SR &=~(1<<6);
return ch;
}
USART的头文件
#ifndef __USART_H
#define __USART_H
#include "stm32f10x.h" // Device header
void Send_Msg(uint8_t data);
void Rev_Msg(void);
void USART_Config_Init(void);
#include "stdio.h"
#include "string.h"
int fputc(int ch,FILE*f);
#endif
main()函数:
#include "stm32f10x.h" // Device header
#include "USART.h"
#include "LED.h"
#include "stdio.h"
#include "KEY_GROUP.h"
uint8_t flag;
char ptr[20];
uint16_t val;
int main()
{
LED_Init();
//中断管理方式
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
USART_Config_Init();
Key_Config();
//Key_Group_Config();
printf("进入while循环\n");
while(1){
val = KEY_Scanf_Config();
if(val){
//LED_ON;
printf("s %d 被按下\n",val);
val = 0;
}
}
}
结果:
总体来说,干啥事情都要小心点,总是马马虎虎的这样不行的啊,干啥事情都要思考清楚呀,慎重做事,从心而为。细致,细心,细微。从小事做起。加油冲。