日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

stm32蜂鸣器播放音乐

發(fā)布時間:2023/12/10 编程问答 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 stm32蜂鸣器播放音乐 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

??最近學(xué)習(xí)stm32芯片,使用的是藍(lán)橋杯的f103的舊板子,看到上面有蜂鳴器,所以就想寫代碼來控制蜂鳴器播放一首音樂。
??這里我參考了這篇文章基于STM32F103,用蜂鳴器播放歌曲。同這篇文章一樣,我也遇到了蜂鳴器發(fā)出的聲音不對。參考了這篇文章,以及查找網(wǎng)上的其他資料,最終完成了蜂鳴器的調(diào)試,以及歌曲的編寫,文章最后會附上代碼。
??先對音符這類東西進(jìn)行說明吧。因為自己也不是學(xué)音樂的,一些關(guān)于音樂的知識都是網(wǎng)上搜集的,所以如果有講得不對的地方,也請各位讀者在評論指正,我會及時改正。
??首先在百度上搜索每個音符的頻率,這里我參考的是音符與頻率對照表

其實對于哪個調(diào)來說,我感覺不出來什么差別(可能我沒有什么藝術(shù)細(xì)胞吧),而且在代碼里我的音符的頻率對應(yīng)圖片里的音符的頻率是高一個八度。【如下圖】

// 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};

??也許我上面說的有些迷糊,但是我們通過上面那張表就可以得到每個音符所對應(yīng)的頻率,這個是我們需要的。
??有了每個音符的頻率還不夠,我們還需要知道每個音符持續(xù)的時間,也就是音符時值。
??這里我拿我的選的一個曲子舉例子。

??Eb\text{Eb}Eb指的是曲子的調(diào)是Eb調(diào),(每個調(diào)之前的區(qū)別,我個人感覺不出大差別,所以我的音符頻率是按C調(diào)頻率來的),后面的44\frac4444?指的是以四分音符為一拍,一小節(jié)四拍。在后面的音符=120,指的是一分鐘120拍,所以我們從這里就可以知道,一個四分音符的時值是0.5s.
那么什么是四分音符呢?

??如上圖,音符下面有三條橫線的,就是三十二分音符,有兩條橫線的就是十六分音符,一條橫線的就是八分音符,沒有橫線的就是四分音符
??這些音符對應(yīng)的時值分別為(設(shè)四分音符的時值為ttt)

  • 四分音符=ttt
  • 八分音符=t/2t/2t/2
  • 十六分音符=t/4t/4t/4
  • 三十二音符=t/8t/8t/8

??除了上面四個音符外,有時譜子里還會遇到一個音符跟著一個小點,這種稱為附點

有附點的音符的時值,與附點跟隨的音符有關(guān)。附點的時值為跟隨的音符時值的一半。所以有附點的音符的時值為 跟隨的音符的時值×1.5

例如,圖中的3˙. ̄\underline{\dot 3.}3˙.?的時值計算如下
設(shè)四分音符的時值為 ttt
3˙ ̄\underline{\dot 3}3˙?為八分音符,時值為 t/2t/2t/2
附點的時值為跟隨音符的時值的一半,在這里也就是 t/4t/4t/4
因此 3˙. ̄\underline{\dot 3.}3˙.? 的時值為t/2+t/4=3t/4t/2+t/4=3t/4t/2+t/4=3t/4
//--------------------------------------------------------------------------
舉一反三 6.6.6.的時值計算如下
設(shè)四分音符的時值為 ttt
666為四分音符,所以時值為 ttt
附點為跟隨音符的時值的一半,為 t/2t/2t/2
所以 6.6.6.的時值為 t+t/2=3t/2t+t/2=3t/2t+t/2=3t/2

??關(guān)于音符的介紹差不多就到這里了,接下來就是代碼相關(guān)的東西了。
【蜂鳴器的控制就不用我說了吧,一般原理圖上都是用三極管驅(qū)動蜂鳴器,所以引腳出高低就能控制蜂鳴器的叫與不叫,至于引腳出高還是低,蜂鳴器響,具體的還是看自己板子上的原理圖吧】
首先是定義一個音符的頻率數(shù)組,至于數(shù)組中的數(shù)從哪來,可以參考我前面發(fā)的音符頻率對照表,或者要是知道音符頻率的關(guān)系,自己算也可以。

// 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};

接下來就是寫譜子的音符,這里我就截我選的曲子

藍(lán)色線截選的音符,就是我讓單片機(jī)播放的曲子片段。
相應(yīng)的音符數(shù)組如下

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 };

數(shù)組里對應(yīng)的數(shù)字,就是tone數(shù)組的元素下標(biāo),也就是每個音符。
??然后是關(guān)于音符時值的數(shù)組【每個音符具體的時值與你選的譜子的曲速和音符種類有關(guān),我在上面都講解了一下,如果還是沒看懂的話,可以去B站看看相關(guān)知識,大概幾分鐘就可以看完】

//因為我選的曲子速度是每分鐘120拍,所以可以算出每個音符相對應(yīng)時值 //時值 全音符2s 二分音符 1s 四分音符 0.5s 八分音符 0.25s // 十六分音符 0.125s 三十二分音符 0.0625s //比例為32:1 //全音符 64 二分音符 32 四分音符 16 八分音符 8 //十六分音符 4 三十二分音符 2u8 scale = 16;//每個音符時值放大的比例,用于將時值數(shù)組轉(zhuǎn)換為實際的時間長度 //其實scale的值應(yīng)該為32,但是測試的時候,感覺播放的速度過快,所以調(diào)成16 //大家在實際操作的時候,也可以改一改,但是建議能不改scale的值就別改scale的值 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 };

??準(zhǔn)備好音符和時值數(shù)組之后,一個重要的函數(shù)就來了。

void BUZZER_Play(u16 frq){u32 time;if(frq){ // time = 500000/((u32)frq);//單個脈沖的高電平持續(xù)時間(單位為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); }

??這是用蜂鳴器播放一個脈沖的函數(shù)。函數(shù)的參數(shù),頻率frq,即單個音符對應(yīng)頻率。函數(shù)執(zhí)行的就是蜂鳴器播放該頻率下的一個脈沖。
??根據(jù),之前的參考的文章,他們的設(shè)置高電平的占空比為50%,但我具體測試的時候,發(fā)現(xiàn)修改time的值雖然會有使音色改善,但也造成了播放速度過快,后面想了想,覺得蜂鳴器的占空比為50%不太合理,根據(jù)測試,選擇了10%的占空比,大家具體測試的時候,如果發(fā)現(xiàn)音色不好,可以修改高電平的占空比。

具體方法如下:

  • 先將time= 500000/((u32)frq);寫到BUZZER_Play()函數(shù)里,然后讓蜂鳴器響和不響的延時時間都相同,為time
  • 下載代碼測試音色,根據(jù)實際情況,修改time = 500000/((u32)frq)中的500000的值,直到自己一個滿意的效果。然后比較修改后time的值與原來time = 500000/((u32)frq)的。將原來time值假設(shè)為t0t_0t0?,調(diào)試完之后的值為t1t_1t1?
  • 將蜂鳴器響的延遲時間設(shè)置為t1t_1t1?,將蜂鳴器關(guān)閉的延遲時間設(shè)置為t0?t1t_0-t_1t0??t1?
  • 修改完的代碼,樣子如下

    void BUZZER_Play(u16 frq){u32 time;if(frq){time = m/((u32)frq);//m根據(jù)實際測試結(jié)果進(jìn)行修改BUZZER_ON();delay_us(time);BUZZER_OFF();delay_us(t_0-t_1);//t_0-t_1的值根據(jù)實際測試結(jié)果計算出來的}else delay_us(1000); }

    ??寫完播放一個固定頻率的脈沖之后,就是寫在一段時間內(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]]表示的是脈沖個數(shù)BUZZER_Play((u32)tone[music[i]]); }

    對于第一個循環(huán)語句

    for(i=0;i<i<sizeof(music)/sizeof(music[0]);i++)

    sizeof(music)/sizeof(music[0])的效果等價于求music數(shù)組的長度,所以這里的循環(huán)就是讀取數(shù)組中的每個音符。

    這個函數(shù)里最重要的一句就是

    for(j=0;j<((u16)time[i])*tone[music[i]]/scale;j++)

    ??前面說過time數(shù)組是music中對應(yīng)音符的時值,scale是將音符時值放大的比例,之所以要放大音符的時值就是為了避免數(shù)組中存在小數(shù),方便書寫和儲存。因此在計算脈沖個數(shù)的時候就需要變換回實際的時值,所以除以放大的比例。【可以發(fā)現(xiàn)這是整數(shù)除法,會舍棄小數(shù),所以存在一點誤差,不過問題也不大】
    ??所以(u16)time[i])*tone[music[i]]/scale表示的就是脈沖的個數(shù),也就是執(zhí)行BUZZER_Play()的次數(shù)。

    ??好了,講完了所用的函數(shù),我們就應(yīng)該從總體上來分析,我們?yōu)槭裁催@么寫。首先蜂鳴器的發(fā)聲頻率不同就會產(chǎn)生不同的聲音,因此我們控制蜂鳴器開關(guān)時間就可以控制蜂鳴器的產(chǎn)生不同頻率的聲波。


    具體就是控制高低電平的時間,產(chǎn)生不同的聲音,產(chǎn)生脈沖的個數(shù)就是其持續(xù)的時間。

    最后貼一個我的代碼。

    // 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;//每個音符時值放大的比例,用于將時值數(shù)組轉(zhuǎn)換為實際的時間長度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);//單個脈沖的高電平持續(xù)時間(單位為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]]表示的是脈沖個數(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)封裝起來方便調(diào)用,暫時還沒測試可行性,等我測試完之后,看實際情況進(jìn)行修改,大家如果有什么代碼建議也可以發(fā)表在評論,一起討論討論

    總結(jié)

    以上是生活随笔為你收集整理的stm32蜂鸣器播放音乐的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。