stm32蜂鸣器播放音乐
??最近學(xué)習(xí)stm32芯片,使用的是藍(lán)橋杯的f103的舊板子,看到上面有蜂鳴器,所以就想寫代碼來(lái)控制蜂鳴器播放一首音樂(lè)。
??這里我參考了這篇文章基于STM32F103,用蜂鳴器播放歌曲。同這篇文章一樣,我也遇到了蜂鳴器發(fā)出的聲音不對(duì)。參考了這篇文章,以及查找網(wǎng)上的其他資料,最終完成了蜂鳴器的調(diào)試,以及歌曲的編寫,文章最后會(huì)附上代碼。
??先對(duì)音符這類東西進(jìn)行說(shuō)明吧。因?yàn)樽约阂膊皇菍W(xué)音樂(lè)的,一些關(guān)于音樂(lè)的知識(shí)都是網(wǎng)上搜集的,所以如果有講得不對(duì)的地方,也請(qǐng)各位讀者在評(píng)論指正,我會(huì)及時(shí)改正。
??首先在百度上搜索每個(gè)音符的頻率,這里我參考的是音符與頻率對(duì)照表
其實(shí)對(duì)于哪個(gè)調(diào)來(lái)說(shuō),我感覺(jué)不出來(lái)什么差別(可能我沒(méi)有什么藝術(shù)細(xì)胞吧),而且在代碼里我的音符的頻率對(duì)應(yīng)圖片里的音符的頻率是高一個(gè)八度。【如下圖】
??也許我上面說(shuō)的有些迷糊,但是我們通過(guò)上面那張表就可以得到每個(gè)音符所對(duì)應(yīng)的頻率,這個(gè)是我們需要的。
??有了每個(gè)音符的頻率還不夠,我們還需要知道每個(gè)音符持續(xù)的時(shí)間,也就是音符時(shí)值。
??這里我拿我的選的一個(gè)曲子舉例子。
??Eb\text{Eb}Eb指的是曲子的調(diào)是Eb調(diào),(每個(gè)調(diào)之前的區(qū)別,我個(gè)人感覺(jué)不出大差別,所以我的音符頻率是按C調(diào)頻率來(lái)的),后面的44\frac4444?指的是以四分音符為一拍,一小節(jié)四拍。在后面的音符=120,指的是一分鐘120拍,所以我們從這里就可以知道,一個(gè)四分音符的時(shí)值是0.5s.
那么什么是四分音符呢?
??如上圖,音符下面有三條橫線的,就是三十二分音符,有兩條橫線的就是十六分音符,一條橫線的就是八分音符,沒(méi)有橫線的就是四分音符。
??這些音符對(duì)應(yīng)的時(shí)值分別為(設(shè)四分音符的時(shí)值為ttt)
- 四分音符=ttt
- 八分音符=t/2t/2t/2
- 十六分音符=t/4t/4t/4
- 三十二音符=t/8t/8t/8
??除了上面四個(gè)音符外,有時(shí)譜子里還會(huì)遇到一個(gè)音符跟著一個(gè)小點(diǎn),這種稱為附點(diǎn)。
有附點(diǎn)的音符的時(shí)值,與附點(diǎn)跟隨的音符有關(guān)。附點(diǎn)的時(shí)值為跟隨的音符時(shí)值的一半。所以有附點(diǎn)的音符的時(shí)值為 跟隨的音符的時(shí)值×1.5
例如,圖中的3˙. ̄\underline{\dot 3.}3˙.?的時(shí)值計(jì)算如下
設(shè)四分音符的時(shí)值為 ttt
3˙ ̄\underline{\dot 3}3˙?為八分音符,時(shí)值為 t/2t/2t/2
附點(diǎn)的時(shí)值為跟隨音符的時(shí)值的一半,在這里也就是 t/4t/4t/4
因此 3˙. ̄\underline{\dot 3.}3˙.? 的時(shí)值為t/2+t/4=3t/4t/2+t/4=3t/4t/2+t/4=3t/4
//--------------------------------------------------------------------------
舉一反三 6.6.6.的時(shí)值計(jì)算如下
設(shè)四分音符的時(shí)值為 ttt
666為四分音符,所以時(shí)值為 ttt
附點(diǎn)為跟隨音符的時(shí)值的一半,為 t/2t/2t/2
所以 6.6.6.的時(shí)值為 t+t/2=3t/2t+t/2=3t/2t+t/2=3t/2
??關(guān)于音符的介紹差不多就到這里了,接下來(lái)就是代碼相關(guān)的東西了。
【蜂鳴器的控制就不用我說(shuō)了吧,一般原理圖上都是用三極管驅(qū)動(dòng)蜂鳴器,所以引腳出高低就能控制蜂鳴器的叫與不叫,至于引腳出高還是低,蜂鳴器響,具體的還是看自己板子上的原理圖吧】
首先是定義一個(gè)音符的頻率數(shù)組,至于數(shù)組中的數(shù)從哪來(lái),可以參考我前面發(fā)的音符頻率對(duì)照表,或者要是知道音符頻率的關(guān)系,自己算也可以。
接下來(lái)就是寫譜子的音符,這里我就截我選的曲子
藍(lán)色線截選的音符,就是我讓單片機(jī)播放的曲子片段。
相應(yīng)的音符數(shù)組如下
數(shù)組里對(duì)應(yīng)的數(shù)字,就是tone數(shù)組的元素下標(biāo),也就是每個(gè)音符。
??然后是關(guān)于音符時(shí)值的數(shù)組【每個(gè)音符具體的時(shí)值與你選的譜子的曲速和音符種類有關(guān),我在上面都講解了一下,如果還是沒(méi)看懂的話,可以去B站看看相關(guān)知識(shí),大概幾分鐘就可以看完】
??準(zhǔn)備好音符和時(shí)值數(shù)組之后,一個(gè)重要的函數(shù)就來(lái)了。
void BUZZER_Play(u16 frq){u32 time;if(frq){ // time = 500000/((u32)frq);//單個(gè)脈沖的高電平持續(xù)時(shí)間(單位為us)為1*10^6/2f=500000/f f為音符的頻率time = 50000/((u32)frq);BUZZER_ON();delay_us(time);BUZZER_OFF();delay_us(time*9);//調(diào)節(jié)占空比}else delay_us(1000); }??這是用蜂鳴器播放一個(gè)脈沖的函數(shù)。函數(shù)的參數(shù),頻率frq,即單個(gè)音符對(duì)應(yīng)頻率。函數(shù)執(zhí)行的就是蜂鳴器播放該頻率下的一個(gè)脈沖。
??根據(jù),之前的參考的文章,他們的設(shè)置高電平的占空比為50%,但我具體測(cè)試的時(shí)候,發(fā)現(xiàn)修改time的值雖然會(huì)有使音色改善,但也造成了播放速度過(guò)快,后面想了想,覺(jué)得蜂鳴器的占空比為50%不太合理,根據(jù)測(cè)試,選擇了10%的占空比,大家具體測(cè)試的時(shí)候,如果發(fā)現(xiàn)音色不好,可以修改高電平的占空比。
具體方法如下:
修改完的代碼,樣子如下
void BUZZER_Play(u16 frq){u32 time;if(frq){time = m/((u32)frq);//m根據(jù)實(shí)際測(cè)試結(jié)果進(jìn)行修改BUZZER_ON();delay_us(time);BUZZER_OFF();delay_us(t_0-t_1);//t_0-t_1的值根據(jù)實(shí)際測(cè)試結(jié)果計(jì)算出來(lái)的}else delay_us(1000); }??寫完播放一個(gè)固定頻率的脈沖之后,就是寫在一段時(shí)間內(nèi)播放固定頻率的脈沖的函數(shù)。
void MUSIC_Play(void){u16 i,j;for(i=0;i<i<sizeof(music)/sizeof(music[0]);i++)for(j=0;j<((u16)time[i])*tone[music[i]]/scale;j++) //time[i]*tone[music[i]]表示的是脈沖個(gè)數(shù)BUZZER_Play((u32)tone[music[i]]); }對(duì)于第一個(gè)循環(huán)語(yǔ)句
for(i=0;i<i<sizeof(music)/sizeof(music[0]);i++)sizeof(music)/sizeof(music[0])的效果等價(jià)于求music數(shù)組的長(zhǎng)度,所以這里的循環(huán)就是讀取數(shù)組中的每個(gè)音符。
這個(gè)函數(shù)里最重要的一句就是
for(j=0;j<((u16)time[i])*tone[music[i]]/scale;j++)??前面說(shuō)過(guò)time數(shù)組是music中對(duì)應(yīng)音符的時(shí)值,scale是將音符時(shí)值放大的比例,之所以要放大音符的時(shí)值就是為了避免數(shù)組中存在小數(shù),方便書寫和儲(chǔ)存。因此在計(jì)算脈沖個(gè)數(shù)的時(shí)候就需要變換回實(shí)際的時(shí)值,所以除以放大的比例。【可以發(fā)現(xiàn)這是整數(shù)除法,會(huì)舍棄小數(shù),所以存在一點(diǎn)誤差,不過(guò)問(wèn)題也不大】
??所以(u16)time[i])*tone[music[i]]/scale表示的就是脈沖的個(gè)數(shù),也就是執(zhí)行BUZZER_Play()的次數(shù)。
??好了,講完了所用的函數(shù),我們就應(yīng)該從總體上來(lái)分析,我們?yōu)槭裁催@么寫。首先蜂鳴器的發(fā)聲頻率不同就會(huì)產(chǎn)生不同的聲音,因此我們控制蜂鳴器開關(guān)時(shí)間就可以控制蜂鳴器的產(chǎn)生不同頻率的聲波。
具體就是控制高低電平的時(shí)間,產(chǎn)生不同的聲音,產(chǎn)生脈沖的個(gè)數(shù)就是其持續(xù)的時(shí)間。
最后貼一個(gè)我的代碼。
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // 低7 1 2 3 4 5 6 7 高1 高2 高3 高4 高5 高6 高7 高高1 uc16 tone[] = {247,262,294,330,349,392,440,494,523,587,659,698,784,880,988,1046,0};u8 scale = 16;//每個(gè)音符時(shí)值放大的比例,用于將時(shí)值數(shù)組轉(zhuǎn)換為實(shí)際的時(shí)間長(zhǎng)度u8 BrokenMoon[]={3,5,6,8,9,8,9,10,8,6,5,3,8,9,6,6,8,9,8,9,10,12,13,15,14,13,14,13,12,13,12,10,9,10,8,9,8,9,10,6,8,9,8,6,6,5,6,5,6,8,9,10,9,5,6 }; u8 BrokenMoonTime[]={8,8,8,8,16,8,8,16,8,8,8,8,8,8,16,8,8,16,8,8,16,8,8,8,8,2,2,4,8,16,8,8,16,8,8,16,8,8,12,4,4,4,8,16,8,8,12,2,2,8,8,8,8,16,32 };void BUZZER_Play(u16 frq){u32 time;if(frq){ // time = 500000/((u32)frq);//單個(gè)脈沖的高電平持續(xù)時(shí)間(單位為us)為1*10^6/2f=500000/f f為音符的頻率time = 50000/((u32)frq);BUZZER_ON();delay_us(time);BUZZER_OFF();delay_us(time*9);//調(diào)節(jié)占空比}else delay_us(1000); }void MUSIC_Play(u8 *music,u8 *time,u16 length){u16 i,j;for(i=0;i<length;i++)for(j=0;j<((u16)time[i])*tone[music[i]]/scale;j++) //time[i]*tone[music[i]]表示的是脈沖個(gè)數(shù)BUZZER_Play((u32)tone[music[i]]); }void MUSIC1_Play(void){MUSIC_Play(BrokenMoon,BrokenMoonTime,sizeof(BrokenMoon)/sizeof(BrokenMoon[0])); }這里我想修改代碼方便調(diào)用,就將MUSIC_Play(u8 *music,u8 *time,u16 length)封裝起來(lái)方便調(diào)用,暫時(shí)還沒(méi)測(cè)試可行性,等我測(cè)試完之后,看實(shí)際情況進(jìn)行修改,大家如果有什么代碼建議也可以發(fā)表在評(píng)論,一起討論討論
總結(jié)
以上是生活随笔為你收集整理的stm32蜂鸣器播放音乐的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 操作系统:提升电脑开机速度的15个小技巧
- 下一篇: xyz坐标图_“色觉地图”的建立(二):