让蜂鸣器唱个歌~《像我这样的人》

1.硬件

最近做了个数字表头小板子,上面有个蜂鸣器,闲来无事,让它唱个歌~

2b70fb913eb1515f8935d72440f96b46.png

(有需要这个板子的可看这里,链接复制到浏览器打开):

https://item.taobao.com/item.htm?spm=a2oq0.12575281.0.0.50111debDWnlsV&ft=t&id=668157221254

首先蜂鸣器有无源和有源之分,驱动电路是一样的,用一个三极管即可。

ec146baf74c4755a1287ae80af0d5762.png

不同的是,有源蜂鸣器通电就能响,声音频率是固定的。无源蜂鸣器需要PWM驱动,声音频率与驱动的PWM一样。因此这里只能用无源蜂鸣器。

2.STM32CubeMX配置

采用STM32G030单片机,4位数码管显示,用TIM3的CH1通道驱动蜂鸣器。主要来看程序。配置STM32CubeMX:

3900a01470c51cb921dbb841e5983030.png

使能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;
  }
}

最后,请欣赏《像我这样的人》:

推荐阅读:

避坑速看!STM32G0系列多通道ADC的问题

STM32定时器应用合集

STM32的‍12位ADC过采样实现16位分辨率

聊聊室内定位技术

   欢迎关注公众号"嵌入式技术开发",大家可以后台给我留言沟通交流。如果觉得该公众号对你有所帮助,也欢迎推荐分享给其他人。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值