[应用相关] STM32之按键+蜂鸣器

[复制链接]
1002|18
 楼主| 雨果喝水 发表于 2021-2-25 22:38 | 显示全部楼层 |阅读模式

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

  • 按键

老规矩,我们还是先看一下按键部分的原理图:

413256037b646a03fa.png

然后在原理图上找一下对应的引脚:

695836037b658e518f.png



 楼主| 雨果喝水 发表于 2021-2-25 22:39 | 显示全部楼层
初始化按键的思想和LED配置部分差不多,而且比LED的配置简单:配置端口时钟(时钟使能GPIOA和GPIOB),设置引脚号,配置端口模式。
 楼主| 雨果喝水 发表于 2021-2-25 22:39 | 显示全部楼层
初始化按键的思想和LED配置部分差不多,而且比LED的配置简单:配置端口时钟(时钟使能GPIOA和GPIOB),设置引脚号,配置端口模式。
这里说明一点:我们知道配置LED时是需要设置引脚速率的,那么初始化按键时为什么不需要设置引脚速率了呢?我们需要知道这里的引脚速率是输出速率,但是不是IO口输出信号的速率而是IO驱动电路的响应速度,这个速率是只有存在信号输出时才需要设置的,按键属于一个向单片机输入信号的模块,按下按键,将对应引脚拉低,相当于输入一个低电平的信号,所以这里是不需要设置引脚速率的。
 楼主| 雨果喝水 发表于 2021-2-25 22:40 | 显示全部楼层
话不多说,直接上代码:
  1. void KeyInit(void)
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStructure;
  4.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
  5.    
  6.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8;
  7.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//设置端口模式为浮空输入
  8.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  9.    
  10.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
  11.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  12.     GPIO_Init(GPIOB, &GPIO_InitStructure);
  13. }
 楼主| 雨果喝水 发表于 2021-2-25 22:40 | 显示全部楼层
LED的配置那篇博客里已经做了详细的说明,这里就不过多陈述了,不懂的话可以去看看我的那篇博客,嘻嘻~
初始化部分写完之后,再加上51按键部分的底层就可以了(下面给出完整的代码):
  1. #include "stm32f10x.h"
  2. #include "key.h"

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

  4. extern void KeyAction(u8 keycode);

  5. void KeyInit(void)
  6. {
  7.     GPIO_InitTypeDef GPIO_InitStructure;
  8.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);//
  9.    
  10.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8;
  11.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  12.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  13.    
  14.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
  15.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  16.     GPIO_Init(GPIOB, &GPIO_InitStructure);
  17. }

  18. void KeyScan()
  19. {
  20.     u8 i;
  21.     static u8 keybuff[4] = {0xFF, 0xFF, 0xFF, 0xFF};//注意这里的静态变量
  22.    
  23.     keybuff[0] = (keybuff[0] << 1) | KEY1;
  24.     keybuff[1] = (keybuff[1] << 1) | KEY2;
  25.     keybuff[2] = (keybuff[2] << 1) | KEY3;
  26.     keybuff[3] = (keybuff[3] << 1) | KEY4;  
  27.    
  28.     for(i = 0; i < 4; i++)
  29.     {
  30.         if(keybuff[i] == 0x00)
  31.         {
  32.             KeySta[i] = 0;
  33.         }
  34.         else if(keybuff[i] == 0xFF)
  35.         {
  36.             KeySta[i] = 1;
  37.         }
  38.         else
  39.         {}
  40.     }
  41. }

  42. void KeyDriver()
  43. {
  44.     u8 i;
  45.     static u8 backup[4] = {1, 1, 1, 1};
  46.    
  47.     for(i = 0; i < 4; i++)
  48.     {
  49.         if(KeySta[i] != backup[i])
  50.         {
  51.             if(backup[i] == 1)
  52.             {
  53.                 KeyAction(i+1);
  54.             }
  55.             backup[i] = KeySta[i];
  56.         }
  57.     }
  58. }
 楼主| 雨果喝水 发表于 2021-2-25 22:41 | 显示全部楼层
按键动作函数(KeyAction):

  1. void KeyAction(int code)
  2. {
  3.     if(code == 1)//按下按键
  4.     {
  5.         GPIOD->ODR |= (1 << 2);
  6.         GPIOC->ODR = 0xFE00;//开启LED1
  7.         GPIOD->ODR &= ~(1 << 2);
  8.     }
  9. }
 楼主| 雨果喝水 发表于 2021-2-25 22:42 | 显示全部楼层
按键引脚定义(相当于51的sbit管脚定义):
  1. #define KEY1 GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);
  2. #define KEY2 GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8);
  3. #define KEY3 GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1);
  4. #define KEY4 GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2);
 楼主| 雨果喝水 发表于 2021-2-25 22:43 | 显示全部楼层
蜂鸣器
原理图:
447706037b76b131bb.png
从原理图上可以看出:N_Buz为高电平时,蜂鸣器关闭;N_Buz为低电平时,蜂鸣器开启
解释一下为什么是高电平关,低电平开: 这里涉及到了三极管的开关特性:当b、e之间的饱和管压降大于等于0.7v时,三极管就处于导通状态(e、c之间是导通的),相反,e、c之间不导通(可以理解为这个三极管在电路当中是断开的);
然后我们再来看原理图,很明显:只有当N_BUZ输入为低电平时,b、e之间才会产生一个大于0.7v的饱和管压降,三极管才会导通,蜂鸣器才会响。
 楼主| 雨果喝水 发表于 2021-2-25 22:44 | 显示全部楼层
对应引脚:
675086037b7a9a1132.png
这里需要注意!!!:PB4这个引脚是具有复用功能的引脚,也就是说它不仅可以作为普通的IO口使用,还具有别的功能,关于IO口的复用功能部分可以参照《STM32中文手册》8.3节的内容,看一下图:

551136037b7be223d3.png
 楼主| 雨果喝水 发表于 2021-2-25 22:45 | 显示全部楼层
stm32复位后引脚PB4默认配置为JTAG的RST引脚功能,所以我们在写初始化蜂鸣器函数时,要先把PB4设置成普通的IO口,这一过程也叫做端口重映射(通过配置重映射寄存器(AFIO_MAPR),将一些复用功能映射到其他引脚),stm32的库函数里有专门的开启重映射的函数: 35376037b7d097743.png
这里要注意一点!!:重映射必须要使能AFIO时钟!!!就是说我们应该先使能AFIO时钟,然后再开启重映射
  1. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);//使能GPIOB和AFIO时钟
 楼主| 雨果喝水 发表于 2021-2-25 22:45 | 显示全部楼层
回到重映射函数,其中GPIO_Remap是GPIO端口重映射,我们要设置寄存器AFIO_MAPR的SWJ CFG[2:0]位,使PB4口重映射为普通IO口: 907586037b801b67ee.png
 楼主| 雨果喝水 发表于 2021-2-25 22:56 | 显示全部楼层
从表格上我们可以看到:当SWJ CFG[2:0]位为001时,JTAG使能,SWD使能,但是没有JNTRST,而且此时IO口是可以用的,也就是说把PB4重映射成了普通IO口。
我们来看一下库函数中对端口重映射的相关定义:
899886037b81a3e706.png
 楼主| 雨果喝水 发表于 2021-2-25 23:00 | 显示全部楼层
部分解释:
  1. #define GPIO_Remap_SWJ_NoJTRST      ((uint32_t)0x00300100)  /*SWD使能、JTAG使能但是不包括JNRST引脚 */
  2. #define GPIO_Remap_SWJ_JTAGDisable  ((uint32_t)0x00300200)  /*SWD使能、JTAG失能 */
  3. #define GPIO_Remap_SWJ_Disable      ((uint32_t)0x00300400)  /*SWD与JTAG全部失能 */
 楼主| 雨果喝水 发表于 2021-2-25 23:02 | 显示全部楼层
配置步骤: 配置端口时钟(时钟使能),端口重映射、设置引脚号,设置引脚速率,配置端口模式,配置输出数据。
 楼主| 雨果喝水 发表于 2021-2-25 23:03 | 显示全部楼层
初始化代码:
  1. void BeeInit(void)
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStructure;
  4.    
  5.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_AFIO, ENABLE);
  6.     GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE);//开启重映射
  7.    
  8.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;//定义管脚
  9.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输出速率50MHZ
  10.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//设置输出模式为强推挽式输出
  11.     GPIO_Init(GPIOB, &GPIO_InitStructure);
  12.    
  13.   //  BeeOff();//设置初始化引脚状态,蜂鸣器上电时关闭
  14.   GPIO_SetBits(GPIOB, GPIO_Pin_4);//设置初始化引脚状态,蜂鸣器上电时关闭
  15. }
 楼主| 雨果喝水 发表于 2021-2-25 23:03 | 显示全部楼层
这里谈谈 GPIO_SetBits()这个函数,这个函数是在stm32的库函数中定义的,可以理解为一个引脚设置函数,我们来看看这个函数的函数体:
  1. void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
  2. {
  3.   /* Check the parameters */
  4.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  5.   assert_param(IS_GPIO_PIN(GPIO_Pin));
  6.   
  7.   GPIOx->BSRR = GPIO_Pin;
  8. }

 楼主| 雨果喝水 发表于 2021-2-25 23:05 | 显示全部楼层
BSRR是端口位设置/清除寄存器,功能是单独设置引脚位, 该函数实现的功能我的理解是实现对应引脚置1;
相对应的还有一个GPIO_ResetBits()函数,这个函数的功能是:实现对应引脚置0, 可以看一下函数体:
  1. void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
  2. {
  3.   /* Check the parameters */
  4.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  5.   assert_param(IS_GPIO_PIN(GPIO_Pin));
  6.   
  7.   GPIOx->BRR = GPIO_Pin;
  8. }
 楼主| 雨果喝水 发表于 2021-2-25 23:07 | 显示全部楼层
可以看到该函数设置的寄存器是:BRR,BRR是端口位清除寄存器,功能是:单独清除引脚位

beep.h文件中我作了相应的定义:
  1. #define BeeOff() GPIO_SetBits(GPIOB, GPIO_Pin_4);
  2. #define BeeOn() GPIO_ResetBits(GPIOB, GPIO_Pin_4);
 楼主| 雨果喝水 发表于 2021-2-25 23:08 | 显示全部楼层
51底层的移植:
  1. void BeeScan(u8 ms)//中断扫描函数,ms值与定时器定时值有关
  2. {
  3.     if(BeepTimer > 0)
  4.     {
  5.         BeepTimer -= ms;
  6.         if(BeepTimer <= 0)
  7.         {
  8.             BeeOff();
  9.             BeepTimer = 0;
  10.         }
  11.     }
  12. }

  13. void Beep(s32 time)
  14. {
  15.     BeepTimer = time;
  16.    
  17.     if(BeepTimer == 0)
  18.     {
  19.         BeeOff();
  20.     }
  21.     else
  22.     {
  23.         BeeOn();
  24.     }
  25. }
您需要登录后才可以回帖 登录 | 注册

本版积分规则

90

主题

1213

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部