HAL库教程10:定时器的PWM模式应用

  本节通过定时器的PWM模式驱动无源蜂鸣器,来演奏一段音乐。本博客在掌机的系列教程中介绍过蜂鸣器的驱动原理,感兴趣的可以参考电子琴

无源蜂鸣器驱动电路

  蜂鸣器按照有无震荡源(不是电源),可以分为有源蜂鸣器和无源蜂鸣器。有源蜂鸣器上电就能工作,控制简单,但是只有一个音调。无源蜂鸣器需要单片机提供震荡源,虽然控制稍微复杂一点,但是可以发出不同频率的声音。
在这里插入图片描述

PWM原理

  根据我们的电路,引脚输出高电平时,驱动电路为蜂鸣器提供了闭合回路,则引脚给高电平,蜂鸣器就能响。然而,只给高电平,无源蜂鸣器不能持续发出声音,只有一瞬间有声音;需要马上给低电平,然后再给一个高电平。即在一个很短的周期内,无源蜂鸣器在高电平持续期间工作,在低电平持续期间休息。周期的倒数就是频率。
  无源蜂鸣器可以用高电平持续的时间调整音量,在一个周期中,高电平持续的时间越长,蜂鸣器声音越大;高电平持续的时间越短,蜂鸣器的声音越小。

  STM32的定时器自带PWM输出模式。为了方便理解PWM,我们想象一个场景:语文老师、数学老师和体育老师带着小白和小黑两个小朋友在玩游戏。语文老师说一个数字x;数学老师负责从0数到x,数到x以后再从0开始,周而复始;体育老师负责说出一个数字y,如果数学老师报的数字比y小,则小白要举手,否则小白把手放下;直到数学老师报数跟y一样大,小白才把手放下,同时小黑举起手来。但是小黑要时刻注意,如果数到最大的数字时,就要做好准备放下手,因为接下来的数字是0。
  例如语文老师说的数字是9,数学老师说的数字是6,即x=9,y=6,那么在一个游戏周期内,小白和小黑举手的情况如下:

数学老师 0 1 2 3 4 5 6 7 8 9
小白
小黑

  通过这个例子,我们来重新理解定时器输出PWM的一些概念:
  语文老师报的数字是9,9就是自动重装值(AutoReload),代表计数周期(Counter Period)。
  数学老师从0数到9,所以称数学老师报的数字是计数值(counter)。9就是最大周期,由于从0开始计数,故一共数了10个数字。数学老师数数的速度,由时钟(84Mhz)和分频(Prescaler)决定。
  体育老师说的数字是6,6就叫做比较值(compare),比较值除以自动重装值得到占空比,6÷10=60%就是占空比。
  在PWM模式1下,小白举手代表引脚输出高电平,小白举手时间占总时间的比例,就是占空比。小黑举手就是引脚输出低电平。PWM模式2与1逻辑相反。
在PWM驱动蜂鸣器的案例中,语文老师报的数字,由音调的频率决定;体育老师报的数字,可以控制音量。小白与小黑是不是需要举手,无需写判断语句,
STM32定时器PWM模式可以自动判断计数值与比较值什么时候相等。

使用CubeMX配置定时器为PWM模式

在这里插入图片描述
在这里插入图片描述
在定时器2的初始化代码中,增加开启定时器PWM模式的代码

static void MX_TIM2_Init(void)
{
  /* USER CODE BEGIN TIM2_Init 2 */
    HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
  /* USER CODE END TIM2_Init 2 */
}

从音调与音量到定时器的设置

  音调与频率是对应的。
在这里插入图片描述
我们先写一个函数,用于根据音调与音量,设置定时器。思路如下:
在这里插入图片描述
  溢出时间 = (自动重装值+1)/12000000,频率是时间的倒数,音调与频率有关,所以知道音调(频率)以后,可用以下方法计算自动重装值:
  Autoreload=(12000000/usFraq)-1; //频率变为自动重装值
  在无声时,让counter与0比较,counter始终大于0,则引脚电平始终为低,蜂鸣器不会响,即可以达到静音的目的。
  比较值决定音量,比较值要比自动重装值小,可以用后者乘以小于1的系数得到。实际上把自动重装值右移也是让比较值小于自动重装值的一个方法,效果更好一点。因为即便只有10%的占空比,蜂鸣器还是很响。
  新建Beep.h用于存放宏定义,新建Beep.c编写演奏函数。

//Beep.c
/**
  * @brief 根据频率让蜂鸣器发出声音
  * @param 频率,音量
  * @note  音量建议范围2~10,2是最大,10几乎听不清了
  * @retval None
  */    
void Beep_Sound(unsigned short usFraq,unsigned char volume_level)   //usFraq是发声频率,即真实世界一个音调的频率。
{
    u16 Autoreload;
    if((usFraq<CL1)||(usFraq>20000))//太低与太高的频率都当做无声
    {
        __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,0);//音量
        __HAL_TIM_SET_COUNTER(&htim2,0);
    }
    else
    {
        Autoreload=(12000000/usFraq)-1;  //频率变为自动重装值,设置的定时器时钟源为12Mhz
        __HAL_TIM_SET_AUTORELOAD(&htim2,Autoreload);
        __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Autoreload>>volume_level);//音量
        __HAL_TIM_SET_COUNTER(&htim2,0);//在不使用缓冲的情况下,必须把计数值清零,否则可能出现计数值大于自动重装值以后,必须数到最大值的情况
    }
}

注意,修改完ARR以后,必须手动清零计数器!
  然后主函数响几个音符:

//main()  
  while (1)
  {
    Beep_Sound(CM1,6);
    HAL_Delay(200);
    Beep_Sound(CM2,6);
    HAL_Delay(200);
    Beep_Sound(CM3,6);
    HAL_Delay(200);
    Beep_Sound(CM1,6);
    HAL_Delay(200);
    Beep_Sound(0,6);
    HAL_Delay(200);
  }

  下载程序,应当可以听到“两只老虎,两只老虎”循环播放。
  程序只要稍加修改,就能演奏音乐了。可以参考演奏音乐背景音乐

发布了127 篇原创文章 · 获赞 312 · 访问量 56万+

猜你喜欢

转载自blog.csdn.net/geek_monkey/article/details/89326363
今日推荐