在調(diào)試裝置時(shí)需要一個(gè)穩(wěn)定的聲音源,想到之前買的有不少stc的MCU芯片,可以用它來產(chǎn)生幾個(gè)聲音信號。于是寫了這個(gè)程序。 八音盒能產(chǎn)生七個(gè)不同的樂音,而音樂就是由這些基本樂音組合構(gòu)成(有點(diǎn)臉紅)。所以本例程就是個(gè)基于MCU的樂音產(chǎn)生程序。 用MCU產(chǎn)生一個(gè)樂音本身很簡單,比如要產(chǎn)生一個(gè)110HZ的樂音,只要用MCU生成一個(gè)頻率為110HZ的PWM方波,然后輸出電路上加個(gè)一階低通濾波器,驅(qū)動(dòng)喇叭就聽到聲音了。 但當(dāng)我們需要產(chǎn)生多個(gè)樂音時(shí),就遇到一個(gè)問題,不同樂音的頻率不同,需要的濾波器的參數(shù)就不一樣,只用一個(gè)濾波器時(shí),頻率高的樂音會(huì)受到較大的衰減。結(jié)果低音很強(qiáng),高音聽不見了。 當(dāng)然可以用七個(gè)不同的低通濾波器,應(yīng)對七個(gè)頻率,用一個(gè)cd4051進(jìn)行選擇,mcu輸出不同頻度時(shí),控制cd4051接通不同的濾波器。不過這樣硬件就多了幾樣。而且無法適應(yīng)更多的頻率。 常用解決辦法是采用一個(gè)高頻率的載波,然后把需要產(chǎn)生的聲音頻率信號調(diào)制到載波上。這樣就節(jié)省了濾波器。這就是spwm波。它是一個(gè)脈沖寬度按正弦規(guī)律變化的PWM波。下面給出一個(gè)實(shí)現(xiàn)方法。 stc32g12k128具有硬件pwm功能。只要做好設(shè)置,它能自動(dòng)產(chǎn)生符合設(shè)置頻率和脈寬的PWM波,不需要占用MCU時(shí)間。我們只要合理控制它的脈寬變化,就能得到需要的SPWM波了。所以程序簡單穩(wěn)定。精度也很高。 我選擇系統(tǒng)主頻用24MHZ,載波頻率為55*512=28160HZ。55為參考頻率,512為對應(yīng)該頻率時(shí)正弦波表的點(diǎn)數(shù)。因?yàn)槿硕陕劼曨l率上限20KHZ,所以這個(gè)頻率不會(huì)對樂音產(chǎn)生干擾,同時(shí)它又能保證每個(gè)周期對應(yīng)一個(gè)波點(diǎn)數(shù)據(jù)。最大限度的保證產(chǎn)生的聲音的精度。對其它頻率的樂音,載波頻率不變,只改變正弦波表的點(diǎn)數(shù)。始終保證系統(tǒng)在最大精度上運(yùn)行。該載波頻率對應(yīng)的周期設(shè)置值為:24000000/28161=852=0x0354 STC官方技術(shù)手冊上有現(xiàn)成的產(chǎn)生spwm波的例程。就直接套用了,包括pwm設(shè)置和pwm中斷服務(wù)。本例程主要是解決正統(tǒng)波表的生成,管理,使用。樂譜表的建立和使用。樂曲的播放管理。 對spwm波來講,每個(gè)頻率的正弦波都需要一組正統(tǒng)波表數(shù)據(jù),建立正統(tǒng)波表時(shí),我使用了軟件 Spwm_calc.exe。 它的界面如圖所示。中值采用420,幅值415,調(diào)制度0.98,在55HZ時(shí)的點(diǎn)數(shù)512.其它頻率的點(diǎn)數(shù)值通過計(jì)算獲得。產(chǎn)生了七組數(shù)據(jù)存放在頭文件sin_table1.h里,每組數(shù)據(jù)的第一個(gè)數(shù)放的是本組數(shù)據(jù)的個(gè)數(shù)(也就是使用的點(diǎn)數(shù))。第二個(gè)開始才是正弦波表的數(shù)據(jù)。也就是說。讀波表數(shù)據(jù)要從第二個(gè)開始讀。這是為了編程序簡單方便。 系統(tǒng)設(shè)置了三個(gè)數(shù)組,一個(gè)是正弦波表,放在頭文件里。第二個(gè)是波表索引數(shù)組u16 *sin_table_index[],存放正弦波表的數(shù)據(jù)的地址,它是樂譜與正弦波表的過渡。第三個(gè)是樂譜 u8 music_score[33][3]。 樂譜表中一組數(shù)據(jù)有三個(gè),第一個(gè)是音符名,程序根據(jù)它通過索引數(shù)組讀取對應(yīng)正弦波表。第二個(gè)是音組名。1是最低音,2是低音,3是中音4是高音,5是最高音。這個(gè)數(shù)據(jù)決定從波表中讀取數(shù)據(jù)的數(shù)量,數(shù)據(jù)越少音頻越高。比如讀512個(gè)是最低音的6(la),那么讀256個(gè)就是低音的6(la),高了八度。頻率高一倍。以此類推。這樣七組波表數(shù)據(jù)可用以播放幾十個(gè)樂音。第三個(gè)是音長(樂音的持續(xù)時(shí)間)。本例程只設(shè)置了三個(gè)數(shù)值,1是全音,2是半音,4 是4分音。僅用來展示一下音長管理的方法。想多加幾個(gè)音長設(shè)置很容易。 例程使用了四個(gè)中斷服務(wù): PWMA中斷,基本照抄官方例程,用來產(chǎn)生spwm波。不同的是這里輸出的正弦波頻率不是固定的。所以讀波表時(shí)不能用固定首地址。而是用了一個(gè)變量*p存放正弦波表首地址值,方便切換頻率。 外中斷int0控制程序運(yùn)行,主要是把等待切換為運(yùn)行。如果不用這個(gè)中斷。可以讓程序開機(jī)后就自動(dòng)循環(huán)播放。在中斷服務(wù)程序中。控制變量CC的值,從而影響主程序里語句 While(cc);的運(yùn)行。 在調(diào)試時(shí)發(fā)現(xiàn)中斷服務(wù)里改變cc值后,主程序里的while語句沒有響應(yīng)。接上stc-link1d仿真器后觀察到,中斷發(fā)生后cc值確實(shí)發(fā)生了變化,但while語句不理會(huì),沒有如預(yù)期的那樣跳出死循環(huán)。把這個(gè)現(xiàn)象放到群里咨詢時(shí),有高人指出。這是由于keil編譯不合理所致,解決的辦法是聲名變量CC時(shí)加一個(gè)限制符volatile。試了一下,果然解決了問題。 中斷T1是避免按鍵振動(dòng)產(chǎn)生干擾的延時(shí)。這兩個(gè)都不重要,可以不用。 中斷T0是核心,它控制一個(gè)樂音的播放時(shí)間,同時(shí)設(shè)定播放所需要的所有參數(shù)。各語句的作用在程序里做了注明。 void t0_sever() interrupt 1//確定一個(gè)音符的輸出參數(shù),包括樂音的持續(xù)時(shí)間,音高數(shù)據(jù)的地址,數(shù)據(jù)量,讀取參數(shù) { read_music_long(cnt);//讀當(dāng)前音符的播放時(shí)間,并設(shè)定對應(yīng)的延時(shí) pp2=music_score[cnt][1];//讀取本音符的音組值,以確定讀波表數(shù)據(jù)時(shí)的偏移量 pp3=pow(2,pp2);//計(jì)算本音符數(shù)據(jù)偏移量,在式中給pp2加整數(shù)能成倍提高輸出頻率 pp0=*sin_table_index[music_score[cnt][0]-1];///pp2;//讀取本音符的數(shù)據(jù)量 pp1=(sin_table_index[music_score[cnt][0]-1]+1);//取本音符數(shù)據(jù)指針初值 p=pp1;//賦正弦數(shù)據(jù)指針初值 cnt++;//準(zhǔn)備讀下一個(gè)數(shù)據(jù) if(cnt>33)//樂譜播放完成,這里的33是根據(jù)樂譜數(shù)據(jù)的參數(shù)設(shè)定的。如果改變樂譜數(shù)據(jù)量,這里要做對應(yīng)變化,用小了不能完整播放,用大了會(huì)出錯(cuò) { cc=0;//播放結(jié)束 ET0=0;//關(guān)中斷 PWMA_IER = 0x00; //關(guān)中斷 PWMA_ENO = 0x00;//關(guān)閉PWM輸出
} } 音長子程序里有三個(gè)軟件定時(shí)程序,是直接使用stc官方的軟件延時(shí)工具產(chǎn)生的。全音用了一秒,半音0.5秒,四分音用0.25秒。 樂音播放所需要的控制參數(shù)都是在這個(gè)中斷服務(wù)里確定的。PWM中斷服務(wù)則負(fù)責(zé)按參數(shù)進(jìn)行輸出。 本例程的主程序很簡單,包括系統(tǒng)設(shè)置和播放管理兩項(xiàng)。直接列出來吧: void main(void) { mcu_initial();//mcu設(shè)置程序 //打開播放程序,播放完成后重新進(jìn)入等待 while(1)//這個(gè)是重入語句 {
cc=1;//等待狀態(tài),由中斷int0改變,如果屏蔽這個(gè)語句則程序自動(dòng)重復(fù)播放樂曲 while(cc); //初始化播放指針并開始播放 cnt=0;//把播放計(jì)數(shù)復(fù)位到開始位置 ET0=1; TR0 = 1; //定時(shí)器0開始計(jì)時(shí) PWMA_IER = 0x01; //使能中斷 PWMA_ENO |= 0x01; //使能輸出 PWMA_ENO |= 0x02; //使能輸出
cc=1; while(cc);//等待播放完成,由T0中斷服務(wù)程序控制這里的CC值 } } 為聽到產(chǎn)生的聲音,我使用了唯創(chuàng)的PWM功率放大模塊WT1312,它能把spwm信號變成推動(dòng)喇叭的正弦信號并直接推動(dòng)喇叭發(fā)聲。其電路如圖所示:把mcu的spwm輸出端直接連上功放芯片的PWM輸入端就行了。因電流較大。芯片電源單獨(dú)接了一個(gè)4.2/3.7V鋰電池。二者不需要共地。功放的輸入阻抗是100K,對MCU輸出要求很低。它的體積很小,又只用了一個(gè)電容,所以我直接用sop23-10/dip10轉(zhuǎn)接板當(dāng)功放板了。接好的板子如圖。MCU部分電路沒有特殊要求,使用最小系統(tǒng)板就行,我在實(shí)驗(yàn)中用了stc32g12k128的降龍棍系統(tǒng)板。音頻測試視頻體積太大沒法傳上來。因?yàn)闇y試電路和這個(gè)八音盒程序都沒使用晶振,所以會(huì)有些誤差。效果整體還是很不錯(cuò)的。完整的程序見附件,歡迎大家批評指正。
波表生成.png (32.22 KB, 下載次數(shù): 45)
下載附件
正弦波表軟件界面
2023-5-12 15:30 上傳
功率輸出.png (11.58 KB, 下載次數(shù): 50)
下載附件
功放電路圖
2023-5-12 15:30 上傳
功放.jpg (2.36 MB, 下載次數(shù): 54)
下載附件
超大功放板
2023-5-12 15:30 上傳
|