• STM32物联网项目-PWM驱动蜂鸣器


    PWM驱动无源蜂鸣器

    之前是使用标准库函数配置引脚输出PWM控制呼吸灯,无源蜂鸣器也类似,这次使用的是HAL库,用CubeMX软件初始化PWM功能

    蜂鸣器电路图

    Buz接到了32单片机的PA8引脚,所以使用PA8引脚的定时器生成PWM信号,驱动蜂鸣器,使其发出声音

    在这里插入图片描述

    PWM输出原理

    Period:周期,单位是秒

    Duty:占空比

    在这里插入图片描述

    CubeMX配置

    因为PA8引脚是定时器1的通道1,定时器1是高级定时器,也有PWM输出功能,所以在软件中对TIM1进行初始化

    主要就是要设定定时器时钟的分频值PSC,自动重载值ARR,因为定时器时钟确定后,每计数一次的时间也定了,从0向上计数到ARR的值,就是一个PWM周期

    在PA8引脚选择TIM1_CH1

    在这里插入图片描述

    开启高级定时器1的时钟源,选择内部时钟,通道1的PWM功能

    在这里插入图片描述

    配置时基单元和PWM输出通道

    在这里插入图片描述

    预分频系数PSC设置为71,也可以写为72000000/1000000-1,设置定时器的时钟为1MHz,因为72MHz的晶振振72次是1us,1/1us = 1/0.000001s = 1000000Hz = 1Mz

    自动重载值ARR为999,也就是从0计数到999,每计数一次是1us,共1000次,所以1000 * 1us = 1ms,所以PWM信号的周期就是1ms,换算成频率就是1KHz

    配置PWM输出通道时,Pulse设置的就是CCR的值,当CNT计数值小于CRR时,会输出一个有效电平,是高电平有效还是低电平有效要看CH Polarity(CH通道极性)选择,如果选择为高电平,则该有效电平就是高电平,如果选择低电平则该有效电平就是低电平;

    Pulse的值也就是占空比的值,因为PWM周期已经在设置自动重载值ARR时确定了,为1000,所以Pulse设置为500的话,表示PWM信号的占空比就为50%,如果Pulse为250,则占空比就是25%

    ARR的值控制的是PWM信号的周期,CCR的值控制的是PWM信号的占空比;ARR不变,改变CCR的值,则会改变占空比;CCR不变,改变ARR,则会改变PWM的周期

    注意:在大多数情况下,选择开启CCR寄存器的预装载功能,让占空比的变化在下一个PWM信号周期才生效

    在这里插入图片描述

    打开定时器6中断和触摸按键1的外部中断,定时器回调函数中可以改变定时器1输出PWM的频率,达到输出不同的声音效果,触摸按键可以开启和关闭蜂鸣器

    在这里插入图片描述

    代码1

    实现功能:触摸按键1可以打开或关闭蜂鸣器,开发板上电后蜂鸣器会发出不同音调的声音

    Buzzer.c

    主要是编写蜂鸣器的开启和关闭函数

    /* Includes ------------------------------------------------------------------*/
    #include "MyApplication.h"
    
    /* Private define-------------------------------------------------------------*/
    
    /* Private variables----------------------------------------------------------*/
    static void Buzzer_ON(void);
    static void Buzzer_OFF(void);
    /* Public variables-----------------------------------------------------------*/
    Buzzer_t Buzzer = 
    {
        OFF_Status,
        Buzzer_ON,
        Buzzer_OFF
    };
    /* Private function prototypes------------------------------------------------*/
    
    /*
    * @name   Buzzer_ON
    * @brief  打开蜂鸣器
    * @param  None
    * @retval None   
    */
    static void Buzzer_ON()
    {
        //只需开启PWM输出功能,即可让蜂鸣器响
        HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
        Buzzer.Status = ON_Status;
    }
    
    /*
    * @name   Buzzer_OFF
    * @brief  关闭蜂鸣器
    * @param  None
    * @retval None   
    */
    static void Buzzer_OFF()
    {
        //关闭PWM输出,即关闭蜂鸣器
        HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_1);
        Buzzer.Status = OFF_Status;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    MyInit.c

    要在初始化函数中打开蜂鸣器,上电后才会响

    /*
    * @name   Peripheral_Set
    * @brief  外设设置
    * @param  None
    * @retval None   
    */
    static void Peripheral_Set()
    {
      Timer6.Timer6_Start_IT();   //启动定时器6
      Buzzer.ON();                //打开蜂鸣器
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    CallBack.c

    外部中断回调函数中,触摸按键1按下翻转LED灯电平,同时控制蜂鸣器的开关

    定时器6中断回调函数中,用LED2显示定时器运行状态,改变定时器1ARR的值,不断改变PWM信号的频率,发出不同的声音

    /*
    * @name   HAL_GPIO_EXTI_Callback
    * @brief  外部中断回调函数
    * @param  GPIO_Pin:触发中断的GPIO引脚
    * @retval None   
    */
    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    {
      if(GPIO_Pin == KEY1_Pin)    //如果触摸按键1被按下
      {
        LED.LED_Fun(LED1,LED_Flip);
        //控制蜂鸣器开关
        if(Buzzer.Status == ON_Status)
        {
          Buzzer.Buzzer_OFF();  //关闭蜂鸣器
        }
        else
        {
          Buzzer.Buzzer_ON();   //打开蜂鸣器
        }
      }
    }
    /*
    * @name   HAL_TIM_PeriodElapsedCallback
    * @brief  定时器中断回调函数
    * @param  *htim:处理定时器的结构体指针
    * @retval None   
    */
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
      static uint8_t Fre_Cnt = 0;
      if(htim->Instance == htim6.Instance)
      {
        //定时器6每隔5ms进入一次中断,usMCU_Run_Timer就加1,当计时时间达到1s时,翻转LED灯的状态
        if(++Timer6.usMCU_Run_Timer >= TIMER_1s)
        {
          Timer6.usMCU_Run_Timer = 0;
          LED.LED_Fun(LED2,LED_Flip);
        }
    
        //控制PWM的频率长度与大小
        if(Fre_Cnt++ >= 3)  //0,1,2不执行
        {
          Fre_Cnt = 0;
    
          //定时器1时钟:1MHz
          //PWM周期:(1/1000000Hz) * ARR
          //PWM频率:1/(1/1000000Hz)*ARR = 1000000/ARR
          //ARR = 250,PWM周期为:250us = 0.25ms = 0.00025s,PWM频率为:1/0.00025s = 4000Hz = 4KHz
          //ARR = 500,PWM周期为:500us = 0.5ms = 0.0005s,PWM频率为:1/0.0005s = 2000Hz = 2KHz
          //ARR = 1000,PWM周期为:1000us = 1ms = 0.001s,PWM频率为:1/0.001s = 1000Hz = 1KHz
          //ARR = 2000,PWM周期为:2000us = 2ms = 0.002s,PWM频率为:1/0.002s = 500Hz = 0.5KHz
          
          /*定时器6每隔5ms执行一次回调函数,在Fre_Cnt为0,1,2时,不改变ARR的值,所以PWM周期不会变,维持15ms
          当Fre_Cnt为3时,进行一次改变ARR值的操作,把ARR的值减少,当减到500时再让ARR的值为2000,
          如此反复改变ARR的值,改变PWM的频率,但占空比始终是50%*/
          TIM1->ARR -= 10;
          if(TIM1->ARR < 500)
          {
            TIM1->ARR = 2000;
          }
          //设置占空比为50%
          TIM1->CCR1 = TIM1->ARR/2; 
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    代码2

    实现功能:触摸KEY1,KEY2,KEY3三个触摸按键蜂鸣器分别发出不同音调的声音,触摸KEY4,使用定时器每隔1秒发出前三个按键的声音

    Buzzer.c

    因为蜂鸣器发出不同音调是通过改变频率来实现的,改变频率就相当于改变了PWM信号的周期,所以是对ARR寄存器里的值进行修改,因为后续会多次改变频率,所以写成函数形式,给到按键外部中断回调函数调用

    /*
    * @name   Frequency_1KHz
    * @brief  蜂鸣器发出1kHz声音
    * @param  None
    * @retval None   
    */
    static void Frequency_1KHz()
    {
        Buzzer.ON();
        TIM1->ARR = 1000;
        TIM1->CCR1 = TIM1->ARR/2;
        HAL_Delay(50);
        Buzzer.OFF();
    }
    
    /*
    * @name   Frequency_2KHz
    * @brief  蜂鸣器发出2kHz声音
    * @param  None
    * @retval None   
    */
    static void Frequency_2KHz()
    {
        Buzzer.ON();
        TIM1->ARR = 500;
        TIM1->CCR1 = TIM1->ARR/2;
        HAL_Delay(50);
        Buzzer.OFF();
    }
    
    /*
    * @name   Frequency_4KHz
    * @brief  蜂鸣器发出4kHz声音
    * @param  None
    * @retval None   
    */
    static void Frequency_4KHz()
    {
        Buzzer.ON();
        TIM1->ARR = 250;
        TIM1->CCR1 = TIM1->ARR/2;
        HAL_Delay(50);
        Buzzer.OFF();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    CallBack.c

    外部中断回调函数实现三个触摸按键发出不同音调的功能,定时器中断回调函数不断检测蜂鸣器状态位,如果触摸按键4被按下,则该状态位被置位,定时器中就每隔1秒中按顺序发出4KHz,2KHz和1KHz的声音

    /*
    * @name   HAL_GPIO_EXTI_Callback
    * @brief  外部中断回调函数
    * @param  GPIO_Pin:触发外部中断的引脚
    * @retval None   
    */
    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    {
      //触摸按键KEY1按下,蜂鸣器发出1KHz的声音
      if(GPIO_Pin == KEY1_Pin)
      {
        LED.LED_Fun(LED1,LED_Flip);
        Buzzer.Frequency_1KHz();
      }
      //触摸按键KEY2按下,蜂鸣器发出2KHz的声音
      if(GPIO_Pin == KEY2_Pin)
      {
        LED.LED_Fun(LED2,LED_Flip);
        Buzzer.Frequency_2KHz();
      }
      //触摸按键KEY3按下,蜂鸣器发出4KHz的声音
      if(GPIO_Pin == KEY3_Pin)
      {
        LED.LED_Fun(LED3,LED_Flip);
        Buzzer.Frequency_4KHz();
      }
      //触摸按键KEY4按下,则蜂鸣器标志位置1
      if(GPIO_Pin == KEY4_Pin)
      {
        Buzzer.Buzzer_Flag = TRUE;
      }
    }
    
    /*
    * @name   HAL_TIM_PeriodElapsedCallback
    * @brief  定时器中断回调函数
    * @param  *htim:处理定时器的结构体指针
    * @retval None   
    */
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
      if(htim->Instance == htim6.Instance)
      {
        //定时器6每隔5ms进入一次中断,usMCU_Run_Timer就加1,当计时时间达到1s时,执行相应功能
        if(++Timer6.usMCU_Run_Timer >= TIMER_1s)
        {
          Timer6.usMCU_Run_Timer = 0;
          if(Buzzer.Buzzer_Flag == TRUE)	//如果蜂鸣器标志位被置位
          {
            Buzzer.Frequency_4KHz();
    
            HAL_Delay(200);
            Buzzer.Frequency_2KHz();
    
            HAL_Delay(200);
            Buzzer.Frequency_1KHz();
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    注意——HAL_Delay函数中断优先级的问题

    在蜂鸣器发出不同频率声音的函数中可以看到,使用了HAL库的延时函数HAL_Delay(),而频率的函数又在外部中断的回调函数中被调用,同时在定时器回调函数也有调用HAL_Delay函数;

    因为HAL_Delay延时函数是使用到了System tick time(系统滴答定时器),也是通过中断定时,所以这三个中断就要考虑中断优先级的关系了

    在这里插入图片描述

    在这里插入图片描述

    因为外部中断或者定时器中断都是在中断处理过程中被HAL_Delay的中断打断的,说明HAL_Delay的中断优先级是比这两者高的,不然HAL_Delay的延时中断打断不了外部中断或者定时器中断,就没有延时的作用

    打开CubeMX的NVIC配置界面可以看到,System tick timer的默认抢占优先级是0,自己配置的外部中断是1,定时器6中断是2,数字越小则优先级越大,所以HAL_Delay延时函数在外部中断或定时器中断中能起作用
    在这里插入图片描述

    拓展

    在这里插入图片描述

  • 相关阅读:
    JavaScript-DOM节点的相关操作
    终止Promise的执行
    Java内存模型之happens-before
    Java计算机毕业设计铜仁学院毕业就业管理系统源码+系统+数据库+lw文档
    uniapp读取(获取)缓存中的对象值(微信小程序)
    C#生成anb文件
    CentOS 安装HTTP代理服务器 Squid
    新闻资讯|基于微信小程序的经济新闻资讯系统设计与实现(源码+数据库+文档)
    多用户登录(列表)
    Python实现深度森林(Deep Forest)分类模型(deepforest分类算法)项目实战
  • 原文地址:https://blog.csdn.net/weixin_46251230/article/details/126689144