【STM32】控制LED灯和蜂鸣器的按键实验

🐱作者:一只大喵咪1201
🐱专栏:《STM32学习》
🔥格言:你只管努力,剩下的交给时间!
图

描述

本喵这次介绍的实验是按键实验,同样使用STM32F103ZE型号的芯片,在开发板上有4个按键,实验目的是每按一个按键就会让LED灯或者是蜂鸣器的工作状态发生反转,下面本喵来详细介绍。

用到的相关寄存器

GPIO

IO口在单片机的使用中是最多的,此次实验用到了LED灯,蜂鸣器,以及3个按键,所以也使用到了IO口。

IDR

叫做端口数据输入寄存器(GPIOX_IDR),读取它的信息就能获得IO口上的状态,是作输入使用的。
图
这是它每一位的对应关系,可以看到,IDR寄存器是一个32位的寄存器,但是只有低16位在使用,而且是只读类型的,高16位保留,如果读的话结果就是0。低16位从0到15对应的该组IO口标号从0到15引脚上的数据信息,而且只能以16位的形式读取数据。

ODR

LED灯和蜂鸣器的使用会用到端口数据输出寄存器(GPIOX_ODR),在跑马灯实验中曾详细介绍过GPIO里一些寄存器的使用,有兴趣的小伙伴可以移步到跑马灯实验这篇文章中参考一下。

RCC

时钟的配置是使用任何一个外设都要设置的,这里也不例外,同样在跑马灯实验这篇文章中讲述过时钟使能的配置。

确定位置

LED灯

LED0与PB5相连,LED1与PE5相连,详情参考跑马灯实验这篇文章。

蜂鸣器

我们来看开发板原理图。
图
可以将BEEP看作是蜂鸣器,它与PB8相连。
图
这是它的具体电路,当BEEP是低电平时,蜂鸣器不会响,当BEEP是高电平时,通过三极管的放大作用,就会使蜂鸣器发出响声。

按键

图
可以看到,按键KEY0与PE4相连,按键KEY1与PE3相连,按键KEY2与PE2相连。
图
按键WK_UP与PA0相连。
图
根据按键的电路图可以发现,KEY0,KEY1,KEY2这三个按键的另一端是和地相连接的,也就是当按键按下去时是低电平,未按下时是高电平,所以这3个键采用上拉输入模式。
WK_UP键的另一端连接的是3.3V的高电平,当键按下去时是高电平,未按下时是低电平,所以采用下拉输入模式。

程序及分析

不支持连续按

当有键按下并且没有松开,只作一次键输入,不当作连续输入。
led.h中的代码

#ifndef __LED_H
#define __LED_H

void LED_Init(void);

#endif


led.c中的代码

#include "stm32f10x.h"//顶级头文件引用
#include "led.h"

void LED_Init()
{
	GPIO_InitTypeDef GPIO_InitStruct;//创建GPIO结构体变量
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE,ENABLE);//PB组和PE组IO口时钟使能
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//引脚5
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//输出最大速度是50MHZ
	
	GPIO_Init(GPIOB,&GPIO_InitStruct);//PB5初始化
	GPIO_SetBits(GPIOB,GPIO_Pin_5);//PB5初始化为高电平,LED0灭
	
	GPIO_Init(GPIOE,&GPIO_InitStruct);//PE5初始化
	GPIO_SetBits(GPIOE,GPIO_Pin_5);//PE5初始化为高电平,LED1灭
}

beep.h中的代码

#ifndef __BEEP_H
#define __BEEP_H

void BEEP_Init(void);

#endif

beep.c中的代码

#include "stm32f10x.h"//顶级头文件引用
#include "beep.h"

void BEEP_Init()
{
	GPIO_InitTypeDef GPIO_InitStruct;//创建GPIO结构体变量
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//在LED初始化函数中已经将PB组IO口使能过了,这里可以不使能
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;//引脚5
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//输出最大速度是50MHZ
	
	GPIO_Init(GPIOB,&GPIO_InitStruct);//PB8初始化
	GPIO_ResetBits(GPIOB,GPIO_Pin_8);//PB5初始化为低电平,蜂鸣器不响
}

key.h中的代码

#ifndef __KEY_H
#define __KEY_H
#include "stm32f10x.h"//引用顶级头文件
#include "sys.h"//使用位带操作的头文件
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//宏定义按键名就是IO的值

#define KEY0_PRES 1
#define KEY1_PRES 2
#define KEY2_PRES 3
#define WK_UP_PRES 4//定义键按下后返回的值

#define LED0 PBout(5)
#define LED1 PEout(5)
#define beep PBout(8)//使用位带操作加宏定义,直接对硬件名字进行操作

void KEY_Init(void);

uint8_t KEY_Scan(void);//键盘扫描函数
#endif

这里充分使用了宏定义,将每个硬件的名字都赋予了意义,在主程序中直接对硬件名字操作以及使用就可以。
为了方便改变LED灯以及蜂鸣器的状态,我们使用位带操作的方法,这样就可以直接对LED灯以及蜂鸣器的状态进行取反。
其中unit8_t类型的变量是STM32固件库中定义的变量类型。

typedef unsigned          char uint8_t;

可以看到,它的本质是一个无符号的char类型,大小只有一个字节。因为STM32的空间比较小,尽量使用占内存较小的变量。

key.c中的代码

#include "stm32f10x.h"
#include "delay.h"
#include "key.h"

void KEY_Init()
{
	GPIO_InitTypeDef GPIO_InitStruct;//创建GPIO结构体变量
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOA,ENABLE);//使能PE和PA组IO口
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//设置成上拉输入
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_3|GPIO_Pin_2;//PE4,PE3,PE2引脚,也就是KEY0,KEY1,KEY2按键
	
	GPIO_Init(GPIOE,&GPIO_InitStruct);//按键初始化为上拉输入
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPD;//设置为下拉输入
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;//PA引脚,也就是WK_UP按键
	
	GPIO_Init(GPIOA,&GPIO_InitStruct);//按键初始化为下拉输入
}

uint8_t KEY_Scan()
{
	static uint8_t key = 1;//储存键的状态,没有键按下时是1
	if(key&&(KEY0==0|KEY1==0|KEY2==0|WK_UP==1))//是否有键按下
	{
		delay_ms(10);//延时消抖
		if(key&&(KEY0==0|KEY1==0|KEY2==0|WK_UP==1))//是否真的有键按下
		{
			key=0;//有键按下,储存为0
			if(KEY0==0)
				return KEY0_PRES;//KEY0按下
			else if(KEY1==0)
				return KEY1_PRES;//KEY1按下
			else if(KEY2==0)
				return KEY2_PRES;//KEY2按下
			else if(WK_UP==1)//最好加上if,如果只有else的话可除了前面三个键的情况还可能有其他情况干扰WK_UP键的状态
				return WK_UP_PRES;//WK_UP按下
		}
	}
	else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)//为了会更加灵敏
		key=1;//没有键按下,储存1
	return 0;//没有键按下,返回0
}

键盘扫描函数的功能就是扫描每个与硬件相连按键的IO口的状况,然后通过这个状况来判断是否有键按下,不同的键按下对应着不同的处理。
注意:
判断按键是否按下时,需要进行消抖处理,通常消抖都是通过延时消抖,还可以采用消抖电路来消抖。
消抖的原因:
由于外界干扰,以及按键上的弹簧产生的机械作用,按键上的电平会在没有人按下的时候发生微小的变化,如果不作消抖处理,CPU就会将这些由其他因素引起的电平变化当作是键输入来处理,这样就产生了BUG,所以我们要进行消抖。
当检测到按键上有电平变化时,进行延时处理,一般10ms就够。如果是其他因素引起的电平变化,10ms之内就电平就会恢复,如果是用户按键导致的电平变化,那么在这10ms内就不会恢复到原来的电平状态,就可以认为是有键输入,并且进行相应的处理。
是不是有个疑问?
如果我在10ms内按下键并松开,是不是CPU就不会当作键输入处理?
是这样的,只要你手速够快,就可以骗过CPU,但是我们平常按键时,按一下并松开的时长肯定会超过10ms的。
让按键更加灵敏

else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)

这是判断没有键输入的语句,这种情况是与有键输入的情况对立的。如果写成只有一个else就笼统的概括没有键输入的情况,这样就会导致按键不灵敏,有时候需要按好几次才会出现对应的反应。

main.c中的代码

#include "led.h"
#include "beep.h"

int main()
{
	delay_init();//延时初始化
	
	LED_Init();//led初始化
	
	BEEP_Init();//蜂鸣器初始化
	
	KEY_Init();//按键初始化
	
	while(1)
	{
		uint8_t ret=KEY_Scan();
		if(ret)
		{
			switch(ret)
			{
				case KEY0_PRES:
					LED0=!LED0;
					LED1=!LED1;//同时反转俩个LED灯
					break;
				case KEY1_PRES:
					LED0=!LED0;//反转LED0
					break;
				case KEY2_PRES:
					LED1=!LED1;//反转LED1
					break;
				case WK_UP_PRES:
					beep=!beep;//蜂鸣器状态反转
					break;
			}
		}
		else
			delay_ms(10);//每隔10ms扫描一次键盘
	}
}

当按下右键时,同时控制俩个LED灯的状态发生反转,当按下下键和左键则分别只控制一个LED灯的状态发生反转,当按下上键时,蜂鸣器的状态发生反转。

视频展示

不支持连续按键

上电后,初始化状态是LED灯不亮,蜂鸣器不响,当有键输入后LED以及蜂鸣器的状态会发生改变。
连续按键也就是按着一个键不动的时候,LED以及蜂鸣器的状态不会发生改变,仅作一次键输入处理。

支持连续按

uint8_t KEY_Scan()
{
	static uint8_t key = 1;//储存键的状态,没有键按下时是1
	if(key&&(KEY0==0|KEY1==0|KEY2==0|WK_UP==1))//是否有键按下
	{
		delay_ms(10);//延时消抖
		if(key&&(KEY0==0|KEY1==0|KEY2==0|WK_UP==1))//是否真的有键按下
		{
			//key=0;//有键按下,储存为0
			if(KEY0==0)
				return KEY0_PRES;//KEY0按下
			else if(KEY1==0)
				return KEY1_PRES;//KEY1按下
			else if(KEY2==0)
				return KEY2_PRES;//KEY2按下
			else if(WK_UP==1)//最好加上if,如果只有else的话可除了前面三个键的情况还可能有其他情况干扰WK_UP键的状态
				return WK_UP_PRES;//WK_UP按下
		}
	}
	else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)
		key=1;//没有键按下,储存1
	return 0;//没有键按下,返回0
}

支持连续按只需要将key=0注释掉就可以,此时,键是否按下就不会参照前一次按键的状态,一只按照不放就是一只都有键输入处理,就像家里的遥控器调音量一样,按照音量键不放音量就会不停的发生变化。

视频展示

支持连续按键

当连续按键,也就是按着一个键不放的时候,LED灯会有轻微的闪烁,其实LED灯的状态是在不停的连续变化的,由于变化的太快,只能看到轻微的闪烁。当按着控制蜂鸣器的按键不放时,可以听到蜂鸣器的响声不是连续的,而是有一些断续,这也说明蜂鸣器的状态在不停的发生改变,由于改变的太快,只能听到就轻微的间隔。

总结

该实验使用的外设主要就是GPIO,LED灯和蜂鸣器是IO口的输出,按键是IO口的输入,掌握好IO口相关的知识就很容易做出来。

  • 57
    点赞
  • 182
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
您好!要实现STM32按键控制蜂鸣器,可以按照以下步骤进行: 1. 首先需要在STM32上配置GPIO口,将按键蜂鸣器连接到相应的GPIO口。 2. 在程序中配置GPIO口的输入输出模式和上下拉电阻。 3. 在程序中开启定时器中断,并且通过定时器中断控制蜂鸣器的响声。 4. 在程序中通过GPIO口读取按键的状态,并且根据按键的状态控制蜂鸣器的响声。 下面是一段示例代码,可以供您参考: ```c #include "stm32f10x.h" #define BEEP_GPIO_PORT GPIOA #define BEEP_GPIO_PIN GPIO_Pin_8 #define KEY_GPIO_PORT GPIOC #define KEY_GPIO_PIN GPIO_Pin_13 void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; // 配置蜂鸣器GPIO口为推挽输出模式 GPIO_InitStructure.GPIO_Pin = BEEP_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(BEEP_GPIO_PORT, &GPIO_InitStructure); // 配置按键GPIO口为上拉输入模式 GPIO_InitStructure.GPIO_Pin = KEY_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(KEY_GPIO_PORT, &GPIO_InitStructure); } void TIM_Configuration(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 配置定时器2为1KHz的频率 TIM_TimeBaseStructure.TIM_Period = 7199; TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 开启定时器2中断 TIM_ClearFlag(TIM2, TIM_FLAG_Update); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); } void TIM2_IRQHandler(void) { static uint16_t beep_count = 0; if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); if (beep_count > 0) { GPIO_WriteBit(BEEP_GPIO_PORT, BEEP_GPIO_PIN, (BitAction)(1 - GPIO_ReadOutputDataBit(BEEP_GPIO_PORT, BEEP_GPIO_PIN))); beep_count--; } } } int main(void) { GPIO_Configuration(); TIM_Configuration(); while (1) { if (GPIO_ReadInputDataBit(KEY_GPIO_PORT, KEY_GPIO_PIN) == RESET) { beep_count = 100; // 按键按下时,蜂鸣器鸣叫100ms } } } ``` 以上代码中,通过配置定时器中断和GPIO口的读写状态,实现了按键控制蜂鸣器的功能。具体实现方法可能因为不同的STM32芯片型号和开发板而有所不同,需要根据具体的情况进行调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一只大喵咪1201

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

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

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

打赏作者

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

抵扣说明:

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

余额充值