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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

stm32蜂鸣器播放音乐

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

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

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

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

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

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

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

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

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

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

例如,圖中的3˙. ̄\underline{\dot 3.}3˙.?的時值計算如下
設四分音符的時值為 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.的時值計算如下
設四分音符的時值為 ttt
666為四分音符,所以時值為 ttt
附點為跟隨音符的時值的一半,為 t/2t/2t/2
所以 6.6.6.的時值為 t+t/2=3t/2t+t/2=3t/2t+t/2=3t/2

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

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

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

//因為我選的曲子速度是每分鐘120拍,所以可以算出每個音符相對應時值 //時值 全音符2s 二分音符 1s 四分音符 0.5s 八分音符 0.25s // 十六分音符 0.125s 三十二分音符 0.0625s //比例為32:1 //全音符 64 二分音符 32 四分音符 16 八分音符 8 //十六分音符 4 三十二分音符 2u8 scale = 16;//每個音符時值放大的比例,用于將時值數組轉換為實際的時間長度 //其實scale的值應該為32,但是測試的時候,感覺播放的速度過快,所以調成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 };

??準備好音符和時值數組之后,一個重要的函數就來了。

void BUZZER_Play(u16 frq){u32 time;if(frq){ // time = 500000/((u32)frq);//單個脈沖的高電平持續時間(單位為us)為1*10^6/2f=500000/f f為音符的頻率time = 50000/((u32)frq);BUZZER_ON();delay_us(time);BUZZER_OFF();delay_us(time*9);//調節占空比}else delay_us(1000); }

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

具體方法如下:

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

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

    ??寫完播放一個固定頻率的脈沖之后,就是寫在一段時間內播放固定頻率的脈沖的函數。

    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]]表示的是脈沖個數BUZZER_Play((u32)tone[music[i]]); }

    對于第一個循環語句

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

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

    這個函數里最重要的一句就是

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

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

    ??好了,講完了所用的函數,我們就應該從總體上來分析,我們為什么這么寫。首先蜂鳴器的發聲頻率不同就會產生不同的聲音,因此我們控制蜂鳴器開關時間就可以控制蜂鳴器的產生不同頻率的聲波。


    具體就是控制高低電平的時間,產生不同的聲音,產生脈沖的個數就是其持續的時間。

    最后貼一個我的代碼。

    // 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;//每個音符時值放大的比例,用于將時值數組轉換為實際的時間長度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);//單個脈沖的高電平持續時間(單位為us)為1*10^6/2f=500000/f f為音符的頻率time = 50000/((u32)frq);BUZZER_ON();delay_us(time);BUZZER_OFF();delay_us(time*9);//調節占空比}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]]表示的是脈沖個數BUZZER_Play((u32)tone[music[i]]); }void MUSIC1_Play(void){MUSIC_Play(BrokenMoon,BrokenMoonTime,sizeof(BrokenMoon)/sizeof(BrokenMoon[0])); }

    這里我想修改代碼方便調用,就將MUSIC_Play(u8 *music,u8 *time,u16 length)封裝起來方便調用,暫時還沒測試可行性,等我測試完之后,看實際情況進行修改,大家如果有什么代碼建議也可以發表在評論,一起討論討論

    總結

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

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。