本节通过定时器的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 { __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); } 下载程序,应当可以听到“两只老虎,两只老虎”循环播放。 程序只要稍加修改,就能演奏音乐了。
上一篇:HAL库教程11:定时器的缓冲功能与影子寄存器
下一篇:HAL库教程9:串口接收不定长数据
推荐阅读最新更新时间:2024-11-06 16:09