作者:阿莫amigenius,排版整理:晓宇
微信公众号:芯片之家(ID:chiphome-dy)
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源码://BUZ-L定义IO//音乐声定义extern volatile uint8 Music_Triger; //触发发声标志extern uint8 flg_MusicPlaying; //非0表示正在发声void Music_Srv(void); //4ms调用一次void Music_InitIO(void); //初始化IOvoid Music_Init(void); //初始化Music模块。//*************************************************************************************************************************************Music.c源码:// 定义常用频率,数字多少就是多少Hz// 定义duo,rui,mi,fa,so等等//定义不同的乐曲数组,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[] ={ //x4ms10, 18, 10, 18, 10, 255, 0,};const uint16 Music3_FrqTab[]={_24, _24, 0,};const uint8 Music3_TimeTab[] ={ //x4ms18, 255, 0,};const uint16 Music4_FrqTab[] ={_28, _28, _24, _24, _21, _21, 0};const uint8 Music4_TimeTab[]={ //x4ms10, 18, 10, 18, 10, 255, 0,};const uint16 Music5_FrqTab[] ={_16, _16, _18, _18, _21, _21, 0};const uint8 Music5_TimeTab[] ={ //x4ms6, 16, 6, 16, 6, 255, 0,};const uint16 Music6_FrqTab[] ={_21, _21, _18, _18, _16, _16, 0};const uint8 Music6_TimeTab[] ={ //x4ms6, 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[]={ //x4ms15, 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){ // 设置频率并初始化TIMuint32 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管的通电效应,我就不信还有不懂的!




