1.硬件
最近做了个数字表头小板子,上面有个蜂鸣器,闲来无事,让它唱个歌~
(有需要这个板子的可看这里,链接复制到浏览器打开):
https://item.taobao.com/item.htm?spm=a2oq0.12575281.0.0.50111debDWnlsV&ft=t&id=668157221254
首先蜂鸣器有无源和有源之分,驱动电路是一样的,用一个三极管即可。
不同的是,有源蜂鸣器通电就能响,声音频率是固定的。无源蜂鸣器需要PWM驱动,声音频率与驱动的PWM一样。因此这里只能用无源蜂鸣器。
2.STM32CubeMX配置
采用STM32G030单片机,4位数码管显示,用TIM3的CH1通道驱动蜂鸣器。主要来看程序。配置STM32CubeMX:
使能TIM3时钟,CH1配置为PWM输出模式1,预分频系数Prescaler为15。因为采用16M时钟,所以TIM3频率为16M/(15+1)=1Mhz,即计数周期为1us,方便后续计算。PWM的周期和占空比在程序中修改。
其它配置这里不详细说了。
3.编程
我们知道,do re mi每个音符都对应不同的频率(周期)。首先定义do re mi…7个音符,高中低三个音域共21个音符对应的PWM的周期:
//高中低音共21个音符对应的周期,单位us
uint16_t MusicPeriod[30]={
0,3816,3401,3030,2865,2551,2272,2024,0,0,//低音do re mi...
0,1912,1703,1517,1432,1275,1136,1012,0,0,//中音
0,955,851,758,751,637,568,508,0,0};//高音
这里为了方便,高中低三个音域不是连续的,数组索引值1-7为低音do re mi... 索引值11-17为中音do re mi... 索引值21-27为高音do re mi...其余为休止符。
然后编写一个函数,用于控制输出PWM的周期:
/***********************************************************************************************
*Function Name: void StartPWM(uint16_t Fre,uint16_t time)
*Purpose : 启动输出PWM
*params : idx--do re mi...高中低音共21个音符的索引,1-7为低音do re mi...11-17为中音do re mi...21-27为高音do re mi...其余为休止符
*return : 无
***********************************************************************************************/
void StartPWM(uint16_t idx)
{
__HAL_TIM_SetAutoreload(&htim3,MusicPeriod[idx]);//设置周期
htim3.Instance->CCR1 = MusicPeriod[idx]/2; //设置占空比为50%
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);//启动PWM
}
再然后,根据一首歌的简谱定义每一个音符和音符持续时间,这里用了两个数组:(相关的简谱音乐知识这里就不普及了,自行百度)
//《像我这样的人》简谱音符1-7低音 11-17中音 21-17高音 30结束
const uint8_t song_tone[500]={
12,13,15,13,15,13,12,13, 0,//像我这样优秀的人
12,13,15,13,15,13,12,13, 0,//本该灿烂过一生
5, 6,11, 6,11,11, 5, 6,11,
12,13,12,13, 6,11,12, 0,
12,13,15,13,15,13,12,13, 0,
12,13,15,13,12,15,13,0,
5, 6,11, 6,11, 6, 5, 6,11,
12,13,12,11,12,13, 0,
13,21,17,16,17,16,13,15,0,
13,12,12,11,11, 6,11,13,0,
13,21,17,16,17,16,17,16,13,15,15,
16,17,21,17,21,16,17,17,30};
//《像我这样的人》音符拍数 1--1/4拍 2--2/4拍
const uint16_t song_time[500]={
1,1,1,1,2,1,1,4,4,//像我这样优秀的人
1,1,1,1,1,1,2,4,4,//本该灿烂过一生
1,1,1,1,2,2,1,3,4,
1,1,1,1,3,1,4,4,
1,1,1,1,2,1,1,4,4,
1,1,1,1,1,3,4,4,
1,1,1,1,3,1,1,3,4,
1,2,1,2,2,4,4,
1,1,1,1,1,2,1,4,4,
1,1,1,1,1,2,1,4,4,
1,1,1,1,1,2,1,2,1,1,2,
1,1,1,1,1,2,1,4,4};
最后,再写个函数,一个一个音符播放即可:
#define TIME 242 //1/4拍的时间,单位ms
__IO uint16_t Music_ms = 0;//音符播放计时 在中断中1ms+1
/***********************************************************************************************
*Function Name: void PlayMusic()
*Purpose : 播放音乐函数
*params : 无
*return : 无
***********************************************************************************************/
void PlayMusic()
{
static uint8_t status = 1;
static uint16_t idx = 0;
switch(status)
{
case 1://播放音符
if(song_tone[idx] > 0 && song_tone[idx] < 30)//正常音符
{
StartPWM(song_tone[idx]);//播放
TM1650_DispNum(song_tone[idx],0);//显示音符
Music_ms = 0;
status = 2;
}
else if(song_tone[idx] == 0 )//休止符
{
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_1);//停止
Music_ms = 0;
status = 2;
}
else//大于30表示结束
{
status = 0;
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_1);//停止
}
break;
case 2://等待播放完成,下个音符
if(Music_ms >= song_time[idx]*TIME)
{
idx++;
status = 1;
}
break;
}
}
最后,请欣赏《像我这样的人》:
推荐阅读:
欢迎关注公众号"嵌入式技术开发",大家可以后台给我留言沟通交流。如果觉得该公众号对你有所帮助,也欢迎推荐分享给其他人。
版权声明:本文为CSDN博主「天外飞仙CUG」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhang062061/article/details/123059581
暂无评论