零基础STM32入门教程--07-按键

第11课:STM32按键输入实验(标准库)

专栏:STM32F103入门教程
环境:Keil‑MDK + STM32F103 标准库3.5版本

在单片机项目开发中,按键是非常基础、高频使用的人机交互方式。复杂产品会采用触摸屏、APP、小程序,而入门阶段我们先学习普通物理按键读取。本节课使用 PB12 作为按键输入引脚。

一、硬件接线

按键原理

在这里插入图片描述

轻触按键特性:未按下时两端断开;按下之后两端导通。可以把万用表拨到蜂鸣档,自行实测验证通断。

本实验接线方案:

按键一端接开发板3.3V,另一端接单片机PB12引脚。

在这里插入图片描述

GPIO总共有4种输入模式:上拉输入、下拉输入、浮空输入、模拟输入,我们分析该选用哪一种:

  1. 模拟输入GPIO_Mode_AIN:专门用于ADC电压采集,直接排除。
  2. 浮空输入GPIO_Mode_IN_FLOATING:芯片内部上下拉电阻全部断开。引脚电平极易受外界电磁干扰产生抖动,只适合外部电路已经自带上下拉电阻的场景,本实验不使用。
  3. 上拉输入GPIO_Mode_IPU:空闲默认高电平;按键接3.3V,按下后依旧是高电平,电平不会发生变化,无法识别按键动作,不可用。
  4. 下拉输入GPIO_Mode_IPD:引脚空闲默认低电平;按键按下接通3.3V,引脚变为高电平,可以准确识别按键按下,本节课选用该模式。

💡学习小建议:上面4种模式大家都可以修改代码实际测试一遍,实践出真知。

面包板实物接线参考:
在这里插入图片描述

  • 3.3V电源从开发板3.3V引脚引出
  • 按键的其中一个引脚接3.3V
  • 按键另外一个引脚飞线连接到PB12

二、工程与程序编写

2.1 工程复制

复制上节课LED_FLASH整个工程文件夹,重命名为KEY,同时把工程文件LED_FLASH.uvprojx重命名为KEY.uvprojx,双击打开工程。

本节课我们加入串口printf打印功能,按键按下时把信息输出到电脑串口助手,方便调试。串口原理后续课程详解,本节课先直接使用,做到会调用即可。

2.2 添加串口驱动文件

  1. 在工程目录 KEY\Hardware 文件夹下新建子文件夹 Usart
  2. Keil工程,右键Hardware分组 → Add New Item to Group 'Hardware',新建usart.c源文件。
    在这里插入图片描述

继续新建头文件usart.h
在这里插入图片描述

usart.h
#ifndef __USART_H   //防止头文件重复包含,避免编译报错
#define __USART_H
#include "stm32f10x.h"
void usart1_init(uint32_t baud); //串口1初始化函数
#endif
usart.c
#include "stdio.h"
#include "usart.h"

// MicroLIB 环境下printf重定向,仅需实现fputc
int fputc(int ch, FILE *f)
{
    USART_SendData(USART1, (uint8_t)ch);
    //等待发送数据寄存器为空,避免丢字符
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    return ch;
}

void usart1_init(uint32_t baud)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	//开启USART1、GPIOA外设时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);

	//PA9 USART1_TX 复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	//串口参数初始化
	USART_InitStructure.USART_BaudRate = baud;                     //波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;    //8位数据
	USART_InitStructure.USART_StopBits = USART_StopBits_1;        //1位停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;            //无校验
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控
	USART_InitStructure.USART_Mode = USART_Mode_Tx;                //仅发送模式

	USART_Init(USART1, &USART_InitStructure);
	USART_Cmd(USART1, ENABLE); //使能串口1
}
  1. 添加标准库外设文件stm32f10x_usart.c到工程,用到哪个外设就添加对应的库文件。在这里插入图片描述

  2. 添加头文件搜索路径,把Hardware\Usart加入包含路径。
    在这里插入图片描述

2.3 添加按键驱动文件

KEY\Hardware\Gpio目录新建key.ckey.h

key.h
#ifndef __KEY_H
#define __KEY_H
#include "stm32f10x.h"

void key_init(void);                 //按键GPIO初始化
uint8_t key_press_get(void);         //读取按键状态:返回1代表按键按下

#endif
key.c
#include "key.h"

void key_init(void)
{
	GPIO_InitTypeDef gpio_init_struct;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启GPIOB时钟

	gpio_init_struct.GPIO_Pin = GPIO_Pin_12;
	gpio_init_struct.GPIO_Mode = GPIO_Mode_IPD;         //下拉输入模式
	gpio_init_struct.GPIO_Speed = GPIO_Speed_2MHz;       //输入模式低速即可
	GPIO_Init(GPIOB,&gpio_init_struct);
}

//读取PB12引脚电平
uint8_t key_press_get(void)
{
	return GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12);
}

📖怎么知道API函数、模式宏定义?
可以查阅《STM32F10x_StdPeriph_Driver_3.5.0中文版.chm》固件库手册,也可以直接打开库源码stm32f10x_gpio.cstm32f10x_gpio.h查看函数声明。
在这里插入图片描述

GPIO库部分函数声明参考:

void GPIO_DeInit(GPIO_TypeDef* GPIOx);
void GPIO_AFIODeInit(void);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource);
//......省略其余函数

🔔初学阶段优先学会调用库函数;后续进阶再去研究底层寄存器。

2.4 main.c主函数

#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "usart.h"
#include <stdio.h>

int main(void)
{
	led_init();
	delay_init();
	usart1_init(115200);  //串口波特率115200,串口助手需要保持一致
	key_init();

	while(1)
	{
		led_on();
		delay_ms(1000);
		led_off();
		delay_ms(1000);

		if(key_press_get() == 1){
			printf("检测到按键按下 \r\n");
		}
	}
}

硬件接线提示:PA9(TX)接到WCH-LINK的RX引脚。

2.5 Keil工程配置MicroLIB

魔术棒 → Target,勾选 Use MicroLIB,printf重定向依赖该配置。
在这里插入图片描述

⚠️注意:MicroLIB不需要写半主机相关桩代码,只需要实现fputc函数。

三、现象与问题分析

下载程序,打开串口助手,波特率设置115200。

现象1:LED周期闪烁,按键很难触发打印

大部分时候按下按键串口没有输出,需要一直按住按键,大约等待2秒左右才会打印一次。

原因分析
主循环里面大部分时间卡在delay_ms(1000)延时,按键检测代码执行时机极少,只有LED熄灭后的一瞬间读取按键电平。

时序说明:0~2000ms执行LED亮灭延时,仅很短的窗口执行按键判断。
在这里插入图片描述
这是时间简化示意图,实际检测时间不到1ms.led_on这类函数执行时间很短,没有算在里面。

现象2:去掉LED延时,循环只检测按键

while(1)
{
	if(key_press_get() == 1){
		printf("检测到按键按下 \r\n");
	}
}

实际效果:按一次按键,串口疯狂输出大量打印信息
在这里插入图片描述

原因:单片机运行速度极快;人手按下按键持续几百毫秒,在这段时间内程序会循环成千上万次,就会重复多次检测到高电平。

现象3:简单加上延时消抖

while(1)
{
	delay_ms(200);
	if(key_press_get() == 1){
		printf("检测到按键按下 \r\n");
	}
}

简单测试下可以做到按一次打印一次。

❗该写法只适合简单测试项目,实际产品中不推荐。delay_ms是阻塞延时,会卡住整个系统,其他任务无法运行。

✨下一节课我们学习外部中断方式实现按键检测,解决轮询阻塞的痛点。

实物效果图

在这里插入图片描述

串口助手接收效果:
在这里插入图片描述

资料下载

参考资料网盘:
链接: https://pan.baidu.com/s/1BrH0JbUY6z7j7gAihTa4Dw
提取码: qnws

本节课完整工程例程网盘:
链接: https://pan.baidu.com/s/1rl6EIqi9m6kQ1JFjuSqINg
提取码: bumc


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

oshan2012

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

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

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

打赏作者

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

抵扣说明:

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

余额充值