這種聽起來比較悅耳的聲音產生原理也非常簡單。就是利用電容的放電產生一個漸弱的尾音,模擬出擊打風鈴的音色。
電路如下:
image.png (27.09 KB)
下載附件
2020-1-6 00:03 上傳
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); //初始化IO
- void 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();
- }
- }
復制代碼
【必讀】版權免責聲明
1、本主題所有言論和內容純屬會員個人意見,與本論壇立場無關。2、本站對所發內容真實性、客觀性、可用性不做任何保證也不負任何責任,網友之間僅出于學習目的進行交流。3、對提供的數字內容不擁有任何權利,其版權歸原著者擁有。請勿將該數字內容進行商業交易、轉載等行為,該內容只為學習所提供,使用后發生的一切問題與本站無關。 4、本網站不保證本站提供的下載資源的準確性、安全性和完整性;同時本網站也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的損失或傷害。 5、本網站所有軟件和資料均為網友推薦收集整理而來,僅供學習用途使用,請務必下載后兩小時內刪除,禁止商用。6、如有侵犯你版權的,請及時聯系我們(電子郵箱1370723259@qq.com)指出,本站將立即改正。
|