STM32之按键+蜂鸣器

今天写一下按键加上蜂鸣器的相关知识,emmm,学到这里,就觉得32和51其实是很相像的,底层思想也差不多一样。51的底层再加上一个初始化函数,就构成了32的底层,而初始化函数部分的方法和步骤也是大同小异,所以大家如果学过51的话,32也会很好入门的~~~


  • 按键

老规矩,我们还是先看一下按键部分的原理图:
在这里插入图片描述
然后在原理图上找一下对应的引脚:
在这里插入图片描述
初始化按键的思想和LED配置部分差不多,而且比LED的配置简单:配置端口时钟(时钟使能GPIOA和GPIOB),设置引脚号,配置端口模式。

这里说明一点:我们知道配置LED时是需要设置引脚速率的,那么初始化按键时为什么不需要设置引脚速率了呢?我们需要知道这里的引脚速率是输出速率,但是不是IO口输出信号的速率而是IO驱动电路的响应速度这个速率是只有存在信号输出时才需要设置的,按键属于一个向单片机输入信号的模块,按下按键,将对应引脚拉低,相当于输入一个低电平的信号,所以这里是不需要设置引脚速率的。
话不多说,直接上代码:

void KeyInit(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//设置端口模式为浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

LED的配置那篇博客里已经做了详细的说明,这里就不过多陈述了,不懂的话可以去看看我的那篇博客,嘻嘻~
初始化部分写完之后,再加上51按键部分的底层就可以了(下面给出完整的代码):

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

u8 KeySta[4] = {1, 1, 1, 1};//存放按键的当前状态

extern void KeyAction(u8 keycode);

void KeyInit(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);//
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

void KeyScan()
{
    u8 i;
    static u8 keybuff[4] = {0xFF, 0xFF, 0xFF, 0xFF};//注意这里的静态变量
    
    keybuff[0] = (keybuff[0] << 1) | KEY1;
    keybuff[1] = (keybuff[1] << 1) | KEY2;
    keybuff[2] = (keybuff[2] << 1) | KEY3;
    keybuff[3] = (keybuff[3] << 1) | KEY4;  
    
    for(i = 0; i < 4; i++)
    {
        if(keybuff[i] == 0x00)
        {
            KeySta[i] = 0;
        }
        else if(keybuff[i] == 0xFF)
        {
            KeySta[i] = 1;
        }
        else
        {}
    }
}

void KeyDriver()
{
    u8 i;
    static u8 backup[4] = {1, 1, 1, 1};
    
    for(i = 0; i < 4; i++)
    {
        if(KeySta[i] != backup[i])
        {
            if(backup[i] == 1)
            {
                KeyAction(i+1);
            }
            backup[i] = KeySta[i];
        }
    }
}

按键动作函数(KeyAction):

void KeyAction(int code)
{
    if(code == 1)//按下按键
    {
        GPIOD->ODR |= (1 << 2);
        GPIOC->ODR = 0xFE00;//开启LED1
        GPIOD->ODR &= ~(1 << 2);
    }
}

按键引脚定义(相当于51的sbit管脚定义):

#define KEY1 GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);
#define KEY2 GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8);
#define KEY3 GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1);
#define KEY4 GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2);

  • 蜂鸣器

原理图:
在这里插入图片描述
从原理图上可以看出:N_Buz为高电平时,蜂鸣器关闭;N_Buz为低电平时,蜂鸣器开启
解释一下为什么是高电平关,低电平开: 这里涉及到了三极管的开关特性当b、e之间的饱和管压降大于等于0.7v时,三极管就处于导通状态(e、c之间是导通的),相反,e、c之间不导通(可以理解为这个三极管在电路当中是断开的);
然后我们再来看原理图,很明显:只有当N_BUZ输入为低电平时,b、e之间才会产生一个大于0.7v的饱和管压降,三极管才会导通,蜂鸣器才会响。

对应引脚:
在这里插入图片描述
这里需要注意!!!:PB4这个引脚是具有复用功能的引脚,也就是说它不仅可以作为普通的IO口使用,还具有别的功能,关于IO口的复用功能部分可以参照《STM32中文手册》8.3节的内容,看一下图:
在这里插入图片描述
stm32复位后引脚PB4默认配置为JTAG的RST引脚功能,所以我们在写初始化蜂鸣器函数时,要先把PB4设置成普通的IO口,这一过程也叫做端口重映射通过配置重映射寄存器(AFIO_MAPR),将一些复用功能映射到其他引脚),stm32的库函数里有专门的开启重映射的函数:
在这里插入图片描述
这里要注意一点!!:重映射必须要使能AFIO时钟!!!就是说我们应该先使能AFIO时钟,然后再开启重映射

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);//使能GPIOB和AFIO时钟

回到重映射函数,其中GPIO_Remap是GPIO端口重映射,我们要设置寄存器AFIO_MAPR的SWJ CFG[2:0]位,使PB4口重映射为普通IO口:
在这里插入图片描述
从表格上我们可以看到:当SWJ CFG[2:0]位为001时,JTAG使能,SWD使能,但是没有JNTRST,而且此时IO口是可以用的,也就是说把PB4重映射成了普通IO口。
我们来看一下库函数中对端口重映射的相关定义:
在这里插入图片描述
部分解释:

#define GPIO_Remap_SWJ_NoJTRST      ((uint32_t)0x00300100)  /*SWD使能、JTAG使能但是不包括JNRST引脚 */
#define GPIO_Remap_SWJ_JTAGDisable  ((uint32_t)0x00300200)  /*SWD使能、JTAG失能 */
#define GPIO_Remap_SWJ_Disable      ((uint32_t)0x00300400)  /*SWD与JTAG全部失能 */

配置步骤: 配置端口时钟(时钟使能),端口重映射、设置引脚号,设置引脚速率,配置端口模式,配置输出数据。
初始化代码:

void BeeInit(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_AFIO, ENABLE);
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE);//开启重映射
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;//定义管脚
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输出速率50MHZ
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//设置输出模式为强推挽式输出
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
  //  BeeOff();//设置初始化引脚状态,蜂鸣器上电时关闭
  GPIO_SetBits(GPIOB, GPIO_Pin_4);//设置初始化引脚状态,蜂鸣器上电时关闭
}

这里谈谈 GPIO_SetBits()这个函数,这个函数是在stm32的库函数中定义的,可以理解为一个引脚设置函数,我们来看看这个函数的函数体:

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  
  GPIOx->BSRR = GPIO_Pin;
}

BSRR是端口位设置/清除寄存器,功能是单独设置引脚位, 该函数实现的功能我的理解是实现对应引脚置1
相对应的还有一个GPIO_ResetBits()函数,这个函数的功能是:实现对应引脚置0, 可以看一下函数体:

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  
  GPIOx->BRR = GPIO_Pin;
}

可以看到该函数设置的寄存器是:BRR,BRR是端口位清除寄存器,功能是:单独清除引脚位

beep.h文件中我作了相应的定义:

#define BeeOff() GPIO_SetBits(GPIOB, GPIO_Pin_4);
#define BeeOn() GPIO_ResetBits(GPIOB, GPIO_Pin_4);

51底层的移植:

void BeeScan(u8 ms)//中断扫描函数,ms值与定时器定时值有关
{
    if(BeepTimer > 0)
    {
        BeepTimer -= ms;
        if(BeepTimer <= 0)
        {
            BeeOff();
            BeepTimer = 0;
        }
    }
}

void Beep(s32 time)
{
    BeepTimer = time;
    
    if(BeepTimer == 0)
    {
        BeeOff();
    }
    else
    {
        BeeOn();
    }
}
<think>嗯,用户想用正点原子的STM32F103开发板实现按键控制蜂鸣器的功能,需要示例代码或教程。首先,我得回忆一下STM32F103的硬件结构。正点原子的开发板通常有按键蜂鸣器的外设,可能连接到GPIO口。用户可能需要配置GPIO为输入和输出模式,同时处理中断或者轮询按键状态。 首先,确定硬件连接。例如,按键可能连接到某个GPIO口,比如PA0,而蜂鸣器可能连接到PB8。需要查看原理图确认引脚。然后,配置GPIO的模式,按键需要输入模式,可能带上拉或下拉电阻,而蜂鸣器需要输出模式。 接着,初始化GPIO。使用HAL库的话,需要调用HAL_GPIO_Init函数,设置相应的引脚为输入或输出。对于按键,可能需要启用中断,这样当按键按下时触发中断服务程序,改变蜂鸣器的状态。 然后,编写中断处理函数。在STM32中,外部中断服务程序需要放在对应的IRQHandler中。比如,如果按键接在PA0,对应的中断线是EXTI0,中断服务程序是EXTI0_IRQHandler。这里需要清除中断标志,并执行蜂鸣器的开关操作。 另外,主循环中可能需要轮询按键状态,如果不用中断的话。使用HAL_GPIO_ReadPin函数读取引脚电平,根据状态控制蜂鸣器。 还要注意蜂鸣器的驱动方式,有些是有源蜂鸣器,直接给电平就能响,无源的需要PWM驱动。这里假设是有源的,所以控制GPIO高低电平即可。 然后,用户可能需要示例代码结构,包括初始化部分、中断配置、主函数中的循环等。需要确保代码符合HAL库的使用规范,比如系统时钟配置、外设初始化等步骤。 最后,检查是否有正点原子提供的相关资料或例程,他们通常会有配套的例程代码,用户可以直接参考。可能需要引导用户查看开发板的资料光盘或者官方网站获取更具体的例程。 总结步骤:硬件连接确认、GPIO初始化、中断配置(如果需要)、编写控制逻辑、测试调试。确保代码中的引脚定义与实际硬件一致,避免错误。</think>### 使用STM32F103实现按键控制蜂鸣器的步骤说明 #### 1. 硬件连接确认 - **按键**:通常连接到GPIO输入引脚(例如PA0),需根据开发板原理图确认是否内置上拉电阻。若无,需软件配置上拉模式[^1]。 - **蜂鸣器**:可能通过GPIO输出驱动(例如PB8),若为有源蜂鸣器,直接控制高低电平;若为无源蜂鸣器,需PWM调制[^3]。 #### 2. GPIO初始化配置 使用STM32CubeMX或手动配置以下代码: ```c // 按键GPIO初始化(以PA0为例) GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式 GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉电阻 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 蜂鸣器GPIO初始化(以PB8为例) __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_8; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); ``` #### 3. 中断配置(可选) 若需按键中断响应,需配置外部中断: ```c // 配置PA0为外部中断 HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); // 设置中断优先级[^1] HAL_NVIC_EnableIRQ(EXTI0_IRQn); // 使能中断 // 中断服务函数 void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); // 清除中断标志 if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) { HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8); // 切换蜂鸣器状态 } } ``` #### 4. 主循环控制逻辑(轮询方式) ```c while (1) { if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) { // 检测按键按下 HAL_Delay(50); // 消抖 if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) { HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8); // 控制蜂鸣器 while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET); // 等待释放 } } } ``` #### 5. 参考资料与验证 - **正点原子例程**:可在资料光盘的`实验4 按键输入`或`实验8 蜂鸣器`中找到基础代码模板。 - **HAL库文档**:参考`STM32CubeF1`中`Drivers/STM32F1xx_HAL_Driver`的GPIO和中断相关API[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ReRrain

觉得写的不错,不妨请我喝杯~

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

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

打赏作者

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

抵扣说明:

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

余额充值