一、业务需求与分析
业务需求:当按键按下的时候,串口调试助手会打印出对应的按键
业务分析:1、原理:矩阵按键通常为低电平有效,那么我们需要根据行和列来确定是哪一个按键
按下了,只有当行和列都是低电平的时候,检测按键的函数(uint16_t KEY_Scanf_Config)才会检测到低电平,从而确定该按键;
2、首先,需要四行输入高电平,四列输出高电平做准备工作,初始化行引脚置为上拉输入(PB5-PD2),列引脚置为复用推挽输出
3、然后,将一列置为输出低电平。例如“1”按键,我将第一列置为低电平输出,因为我初始化了第一行为上拉输入,所以它默认输入高电平,当我“1”按下后,它就会变成低电平输入。行和列都是低电平就能确定是“1”按键被按下
4、准备工作都做好后,我们需要写一个检测按键的函数,一直处于检测状态(while(1)),我们需要将哪个按键按下打印到串口调试助手,就需要该函数返回值打印出来
二、实现步骤
1、查看原理图,找8个连续的GPIO的引脚
2、查看参考手册,查找这些引脚能否使用
注意!!!在我们做引脚初始化之前,我们第一步要做的是先去参考手册查看哪些引脚已经被使用,如果我们需要去复用这些引脚,那么就需要重映射(先大概知道是哪些引脚被使用)
对比我们需要使用的引脚(原理图)和该表格看出,在复位状态也就是正常状态下,调试接口信号被映射到GPIO端口上,我们所使用的PB3、PB4、PA15是不可用的,只有在JTAG-DP和SW-DP关闭下,我们才能使用,所以我们需要去固件库手册找到重映射的函数将它关闭
3、完整代码
//主函数代码
#include "stm32f10x.h" // Device header
#include "USART.h"
#include "stdio.h"
#include "KEY_GROUP.h"
uint8_t flag;
char ptr[20];
uint16_t val;
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
USART_Config_Init();
Key_Config();
printf("进入while循环\n");
while(1){
val = KEY_Scanf_Config();
if(val){
printf("s %d 被按下\n",val);
val = 0;
}
}
}
#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(USART_GetITStatus(USART1, USART_IT_IDLE)!=RESET){
if(SET == USART_GetFlagStatus(USART1,USART_IT_RXNE))
{
//清除标志位
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
recv_data = USART_ReceiveData(USART1); //把接受到的数据放到变量中
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;
}
#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
//key_group.c文件
#include "KEY_GROUP.h"
#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 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])) {
} else if(!GPIO_ReadInputDataBit(row_ports[j], row_pins[j])){
val = (j* ROWS) + i + 1; // 计算按键值
}
}
//恢复高电平,一行为低电平其余行为高电平
GPIO_SetBits(col_ports[i], col_pins[i]);
}
if(val != 0){
printf("%d",val); //打印获得值
return val; //将只返回出去
}
return 0; //没有按下按键时候返回0,
}
void KEY_Work(void)
{
uint16_t val;
val = KEY_Scanf_Config();
if(val){
printf("s %d 被按下\n",val);
val = 0;
}
}
#ifndef __KEY_GROUP_H
#define __KEY_GROUP_H
#include "stm32f10x.h"
#include "Delay.h"
#include "stdio.h"
#include "USART.h"
uint16_t KEY_Scanf_Config(void);
void Key_Config(void);
void KEY_Work(void);
#endif
//延时函数
#include "Delay.h"
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}
#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f10x.h"
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);
#endif