查看原文
其他

【开源】蜂鸣器怎样实现类似高级冰箱上很清脆“叮叮”声...

芯片之家 2023-04-09


作者:阿莫amigenius,排版整理:晓宇

微信公众号:芯片之家(ID:chiphome-dy)

阿莫上有位兄弟发帖问蜂鸣器如何产生清脆的“叮叮”的声音,帖子不小心🔥了。
很快就盖了100多层楼,大家普遍都很感兴趣,ID为amigenius的大神手贱回了一帖,引来了很多兄弟的兴趣,这位兄台很无私,在此奉上电路和源码。
其实,这种听起来比较悦耳的声音产生原理也非常简单。就是利用电容的放电产生一个渐弱的尾音,模拟出击打风铃的音色。
电路如下:

BUZ-H负责输出音频频率,BUZ-L负责对C12的充电作控制。

例如,要发出“叮~~~~”的一声,在BUZ-H输出2400Hz的方波,BUZ-L输出72ms的高电平,此段时间内,蜂鸣器将由幅值12V的频率驱动,发出类似击打瞬间的声音,同时对C12充电。72ms后,BUZ-L输出低电平,这个时候,蜂鸣器将由C12来提供驱动电流,并且随著电容放电,声音逐渐减弱,产生类似风铃尾音的效果。1秒后,关闭BUZ-H的输出。

这种发声方式,其实还是单音频发声,只是在单音频的基础上,用电容的充放电模拟了击打和尾音,产生比较丰满悦耳的听感,所以称之为“伪和弦”。

各位有兴趣的话,可以用多个蜂鸣器同时发出不同音调,组成多音的真和弦,一个音调输出占用一个定时器。俺以前10多年前蛋疼,搞过几十个蜂鸣器的(5和弦,蜂鸣器多个并联),并且并增加一个IO用作电容的放电回路来产生短音,听感还是不错的。真有空时回老家翻出来拍个视频给大家看看。

源码如下,带音量控制,带家电常用的音调组合,还附上一个超级玛丽的1UP声音,玩过的朋友肯定会觉得很亲切,哈哈!

源码不是整个工程,我也懒得整理了,生活艰苦,搬砖不易。

用法,在您的工程里面增加两个文件,一个名为Music.c,一个名为Music.h(头文件)。现在源码,使用的是STM32F1xx,主频72M,使用的定时器是TIM2(任意定时器均可,自己修改),使用硬件PWM输出。所以您单片机有N个定时器,则可以最多N-1个真和弦(留一个做主时基),做产品,通常一个伪和弦就够了。Music_Srv()这个函数,没4ms调用一次,您放中断也行,放主循环也行(前提是您主循环没有阻塞的占用)。需要发声的时候,任意地方放一句 Music_Triger=MUSIC_XXX;  即可,不阻塞,也不用等它播完再调用,随传随叫。

BUZ-L现在接的是PA1,这个任意IO即可,请自行修改。BUZ-H接的是PA0,这个随您用不同的TIM而不同,自己修改,例程用的是TIM2。

源码如下:
//*************************************************************************************************************************************Music.h源码:
#ifndef __MUSIC_H_#define __MUSIC_H_
//BUZ-L定义IO#define BEEPL_1 GPIOA_DSRR=BIT1#define BEEPL_0 GPIOA_DRR=BIT1#define BEEPL_XOR {if(BitTst(GPIOA_ODR,BIT1)) BEEPL_0; else BEEPL_1;}
//音乐声定义#define MUSIC_PWR_UP 1#define MUSIC_TURN_ON 2#define MUSIC_TURN_OFF 4#define MUSIC_DING 3#define MUSIC_UP 5#define MUSIC_DOWN 6#define MUSIC_1UP 7
extern volatile uint8 Music_Triger; //触发发声标志extern uint8 flg_MusicPlaying; //非0表示正在发声
void Music_Srv(void); //4ms调用一次void Music_InitIO(void); //初始化IOvoid Music_Init(void); //初始化Music模块。
#endif
//*************************************************************************************************************************************Music.c源码:#include "music.h"
#define TIM2_ARR (*((volatile uint16 *)(TIM2_BASE + 0x002c)))#define TIM2_CR1 (*((volatile uint16 *)(TIM2_BASE + 0)))#define TIM2_CCR1 (*((volatile uint16 *)(TIM2_BASE + 0x0034)))#define TIM2_CCER (*((volatile uint16 *)(TIM2_BASE + 0x0020)))#define TIM2_CNT (*((volatile uint16 *)(TIM2_BASE + 0x0024)))#define TIM2_CCR2 (*((volatile uint16 *)(TIM2_BASE + 0x0038)))// 定义常用频率,数字多少就是多少Hz#define _28 2850 #define _24 2400#define _22 2250#define _21 2100#define _18 1850#define _16 1650// 定义duo,rui,mi,fa,so等等#define _l1 130#define _l2 146#define _l3 164#define _l4 174#define _l5 196#define _l6 220#define _l7 246
#define _1 261#define _2 293#define _3 329#define _4 349#define _5 392#define _6 440#define _7 494
#define _1d1 523#define _2d1 587#define _3d1 659#define _4d1 698#define _5d1 784#define _6d1 880#define _7d1 987
#define _1d2 1046#define _2d2 1175#define _3d2 1318#define _4d2 1397#define _5d2 1568#define _6d2 1760#define _7d2 1976
#define _1d3 (_1d2*2)#define _2d3 (_2d2*2)#define _3d3 (_3d2*2)#define _4d3 (_4d2*2)#define _5d3 (_5d2*2)#define _6d3 (_6d2*2)#define _7d3 (_7d2*2)

//定义不同的乐曲数组,0 结束const uint16 Music1_FrqTab[]={ //频率表,对应BUZ-H的输出频率,0 结束 _18, _18, _21, _21, _24, _24, _28, _28, 0,};const uint8 Music1_TimeTab[]={ //BUZ-L输出时间,偶数高电平,奇数为低电平尾音,x4ms,0 结束 20, 30, 20, 30, 20, 30, 20, 255, 0,};
const uint16 Music2_FrqTab[]={ _21, _21, _22, _22, _24, _24, 0};const uint8 Music2_TimeTab[] ={ //x4ms 10, 18, 10, 18, 10, 255, 0,};
const uint16 Music3_FrqTab[]={ _24, _24, 0,};const uint8 Music3_TimeTab[] ={ //x4ms 18, 255, 0,};
const uint16 Music4_FrqTab[] ={ _28, _28, _24, _24, _21, _21, 0};const uint8 Music4_TimeTab[]={ //x4ms 10, 18, 10, 18, 10, 255, 0,};
const uint16 Music5_FrqTab[] ={ _16, _16, _18, _18, _21, _21, 0};const uint8 Music5_TimeTab[] ={ //x4ms 6, 16, 6, 16, 6, 255, 0,};
const uint16 Music6_FrqTab[] ={ _21, _21, _18, _18, _16, _16, 0};const uint8 Music6_TimeTab[] ={ //x4ms 6, 16, 6, 16, 6, 255, 0,};
const uint16 Music7_FrqTab[]={ 1324,1324,1574,1574,2645,2645, 2114,2114,2347,2347,3154,3154, 0,};const uint8 Music7_TimeTab[]={ //x4ms 15, 25, 15, 25, 15, 25, 15, 25 , 15 , 25 , 15, 255, 0,};
uint8 Music_Vol=137; //音量volatile uint8 Music_Triger=0; //触发播放哪一支,例如播放“叮~~~”,则任意地方调用 Music_Triger=MUSIC_DING;uint8 flg_MusicPlaying=0; //非零播放中。
//乐曲表指针uint16 const * music_frq_tab; uint8 const * music_interval_tab;
TIM_TimeBaseInitTypeDef T2_TimeBaseStruct;TIM_OCInitTypeDef T2_OCInitStruct;
void Music_SetVolumn(uint8 vol){ //设置音量,并且初始化TIM输出 uint32 lTmp; lTmp=T2_TimeBaseStruct.TIM_Period; lTmp*=vol; lTmp/=255; T2_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; T2_OCInitStruct.TIM_Pulse = lTmp; T2_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; T2_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; TIM_OC1Init(TIM2, &T2_OCInitStruct);}/* --------------------------------------------------------------- TIM2 Configuration: Output Compare Toggle Mode: TIM2CLK = 72 MHz, Prescaler = 0x9, TIM2 counter clock = 7.2 MHz--------------------------------------------------------------- */void Music_SetFrq(uint16 frq){ // 设置频率并初始化TIM uint32 clk; clk=7200000; clk/=frq; clk--; /* Time base configuration */ T2_TimeBaseStruct.TIM_Period = clk; T2_TimeBaseStruct.TIM_Prescaler = 0x9; T2_TimeBaseStruct.TIM_ClockDivision = 0x0; T2_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &T2_TimeBaseStruct);
        Music_SetVolumn(Music_Vol);    }
void Music_ChangeFrq(uint16 frq){ //改变频率。 uint32 clk; clk=7200000; clk/=frq; clk--; /* Time base configuration */ TIM2_ARR = clk; clk*=Music_Vol; clk/=255; TIM2_CCR1=clk;}
void Music_Frq_Enable(void){ TIM2_CCER|=BIT0;}
void Music_Frq_Diable(void){ TIM2_CCER&=(~BIT0);}
void Music_Init(void){ Music_SetFrq(4000);
TIM_ARRPreloadConfig(TIM2, ENABLE); // 这个记得要开
//启用CCR1寄存器的影子寄存器(直到产生更新事件才更改设置) TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
Music_Frq_Diable(); TIM_Cmd(TIM2, ENABLE);}
void Music_InitIO(void){ GPIO_InitTypeDef tmpGPIO; tmpGPIO.GPIO_Mode=GPIO_Mode_Out_PP; tmpGPIO.GPIO_Speed=GPIO_Speed_50MHz;
tmpGPIO.GPIO_Pin=GPIO_Pin_1; GPIO_Init(GPIOA,&tmpGPIO);
tmpGPIO.GPIO_Pin=GPIO_Pin_0; tmpGPIO.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_Init(GPIOA,&tmpGPIO); BEEPL_1;}
void Music_Select(void){ switch(Music_Triger){ default: flg_MusicPlaying=0; return; case MUSIC_PWR_UP: music_frq_tab=Music1_FrqTab; music_interval_tab=Music1_TimeTab; break; case MUSIC_TURN_ON: music_frq_tab=Music2_FrqTab; music_interval_tab=Music2_TimeTab; break; case MUSIC_DING: music_frq_tab=Music3_FrqTab; music_interval_tab=Music3_TimeTab; break; case MUSIC_TURN_OFF: music_frq_tab=Music4_FrqTab; music_interval_tab=Music4_TimeTab; break; case MUSIC_UP: music_frq_tab=Music5_FrqTab; music_interval_tab=Music5_TimeTab; break; case MUSIC_DOWN: music_frq_tab=Music6_FrqTab; music_interval_tab=Music6_TimeTab; break; case MUSIC_1UP: music_frq_tab=Music7_FrqTab; music_interval_tab=Music7_TimeTab; break; } flg_MusicPlaying=1;}


void Music_Srv(void){ static uint8 PlayStep=0; static uint8 PlayCt=0; uint8 cTmp;
if(Music_Triger){ Music_Select(); Music_Triger=0; PlayStep=0; PlayCt=0; Music_ChangeFrq(music_frq_tab[PlayStep]); Music_Frq_Enable();
BEEPL_1; } if(flg_MusicPlaying){ cTmp=music_interval_tab[PlayStep]; PlayCt++; if(PlayCt>=cTmp){ PlayCt=0; PlayStep++; Music_ChangeFrq(music_frq_tab[PlayStep]); if(music_interval_tab[PlayStep]==0){ flg_MusicPlaying=0; } else{ BEEPL_XOR; } } } else{ BEEPL_0; Music_Frq_Diable(); }}

推荐阅读


  电子漫画系列,更新九张图片。

 【最强干货】618个3D封装免费分享

 【干货第二波】一大波3D封装免费分享

  贴片电容国内外品牌

  DC-DC电源芯片国内外品牌大汇总

  几毛钱的32768晶振,这也能写出一篇干货?

  车牌识别控制板原理图+PCB+3D齐全无私分享

 【视觉盛宴】你没见过的电子元器件的另一面!

  完全由C编写,高度可移植,超级牛逼的菜单架构!

 【视频】老外教你三极管和MOS管的通电效应,我就不信还有不懂的!

请戳右下角,给我一点在看!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存