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

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位分辨率

聊聊室内定位技术

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

版权声明:本文为CSDN博主「天外飞仙CUG」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhang062061/article/details/123059581

生成海报
点赞 0

天外飞仙CUG

我还没有学会写个人说明!

暂无评论

相关推荐

AD9528芯片介绍及配置详解

AD9528是ADI(亚德诺半导体技术有限公司, Analog Devices, Inc. 简称ADI )出品的一款双级PLL,集成JESD204B SYSREF发生器,可用于多器件同步。第一级锁相环(PLL) (P

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

1.什么是过采样过采样技术是一种以牺牲采样速度来提高ADC分辨率的技术。部分STM32单片机是支持硬件过采样的,如STM32G0系列。通过过采样,可以将12位的ADC提升到16位,非常实用。根据过采样技

单次事件触发DMA读取多字节SPI数据

有人想使用定时器事件触发DMA读取SPI数据,希望每一次更新事件仅触发一轮DMA传输,借助DMA实现N字节的SPI数据收发。要完成这个功能,这里推荐两种做法供参考。第一种,先做好SPI的D

定时器触发STM32 ADC的采样转换示例

开发板:STM32F446 Nucleo开发板IDE:  keil MDK初始化配置工具:stm32cubeMx例程内容:通过定时器触发ADC规则通道及注入通道的模数转换工作。下面基于STM32CubeMx进行些必要