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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

编程问答

stm32蜂鸣器播放音乐

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

??最近學(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è)八度。【如下圖】

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

??也許我上面說(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)系,自己算也可以。

// 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ái)就是寫譜子的音符,這里我就截我選的曲子

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

//因?yàn)槲疫x的曲子速度是每分鐘120拍,所以可以算出每個(gè)音符相對(duì)應(yīng)時(shí)值 //時(shí)值 全音符2s 二分音符 1s 四分音符 0.5s 八分音符 0.25s // 十六分音符 0.125s 三十二分音符 0.0625s //比例為32:1 //全音符 64 二分音符 32 四分音符 16 八分音符 8 //十六分音符 4 三十二分音符 2u8 scale = 16;//每個(gè)音符時(shí)值放大的比例,用于將時(shí)值數(shù)組轉(zhuǎn)換為實(shí)際的時(shí)間長(zhǎng)度 //其實(shí)scale的值應(yīng)該為32,但是測(cè)試的時(shí)候,感覺(jué)播放的速度過(guò)快,所以調(diào)成16 //大家在實(shí)際操作的時(shí)候,也可以改一改,但是建議能不改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ù)組之后,一個(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)音色不好,可以修改高電平的占空比。

具體方法如下:

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

    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)題。

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