STM32F4应用笔记(二)利用蜂鸣器播放天空之城
音階頻率對照表
百度就可以查到,我對照的是下面網址中的:
http://blog.csdn.net/u012266559/article/details/51512616
單片機產生音樂的原理
音樂的產生主要是通過單片機的I/O口輸出高低不同的脈沖信號來控制蜂鳴器發音,要想產生音頻脈沖信號,需要算出某音頻的周期(1/頻率),然后將此周期除以2,即為半周期的時間。利用單片機定時器計時這個半周期的時間,每當計時到后就輸出脈沖的I/O口反相,這樣就在此I/O口上得到此脈沖的頻率。
我是直接利用stm32的PWM端口輸出PWM波(設置成占空比為50%)就可以實現,關鍵是每個音階對應頻率的方波如何求。
從下圖看,只要理解原理,那么不管單片機的時鐘頻率多大,我們都可以自己求出相應的音階所需要的arr值。
程序用到的單片機引腳
我用的是stm32開發板,利用PF9端口輸出PWM波,PF8端口連著的是蜂鳴器,所以只需要用杜邦線把PF9和PF8端口連在一起就可以了。
程序中定義的的宏定義
10000的來歷上圖中有計算過程,然后音階頻率對照表中可以查到
低1 DO 的頻率是262,所以我們得到
低1 DO 對應的arr值是 (R/262)-1 ,其余音符對應arr的計算方式同理。
#define R 10000 //84MHz/(psc+1)=10000
#define L1 (R/262)-1 //低1 DO
關于音符0的處理以及改進
在我寫完的程序中,我是這樣處理的,一旦遇到音符0,讓PWM停止輸出。有想過關閉TIM14和PORTF時鐘,但是沒有用——因為用同樣的方法,在一首歌結束之后就是一段相同頻率的雜音。所以這種方法是不可以讓PWM輸出停止的。最后換了個方法,把PF9設置成普通IO口而且是輸入模式就可以了,就不會有噪聲了。
但是這種方法很麻煩,后來想到其實在遇到音符0的時候,只需要將PWM輸出頻率變大,讓蜂鳴器發出一個人耳聽不到的超聲波就可以了——程序已經寫完了,懶得改了。
主要程序代碼
#include "sys.h" #include "delay.h" #include "led.h" #include "timer.h" #include "key.h"#define ZERO 3000//#define R 10000 //F_CLOCK/(psc+1)=10000#define L1 (R/262)-1 //低1 DO#define half_L1 (R/277)-1 //#1 DO##define L2 (R/294)-1#define half_L2 (R/311)-1#define L3 (R/330)-1#define L4 (R/349)-1#define half_L4 (R/370)-1#define L5 (R/392)-1#define half_L5 (R/410)-1#define L6 (R/440)-1#define half_L6 (R/466)-1#define L7 (R/494)-1#define M1 (R/523)-1 //中1 DO#define half_M1 (R/554)-1 //#1 DO##define M2 (R/587)-1#define half_M2 (R/622)-1#define M3 (R/659)-1#define M4 (R/698)-1#define half_M4 (R/740)-1#define M5 (R/784)-1#define half_M5 (R/831)-1#define M6 (R/880)-1#define half_M6 (R/932)-1#define M7 (R/988)-1#define H1 (R/1046)-1 //高1 DO#define half_H1 (R/1109)-1 //#1 DO##define H2 (R/1175)-1#define half_H2 (R/1245)-1#define H3 (R/1318)-1#define H4 (R/1397)-1#define half_H4 (R/1480)-1#define H5 (R/1568)-1#define half_H5 (R/1661)-1#define H6 (R/1760)-1#define half_H6 (R/1865)-1#define H7 (R/1967)-1int flag=0;//標志int x;int tune[] = { M6,M7,H1,M7,H1,H3,M7,M7,M7,M3,M3, M6,M5,M6,H1,M5,M5,M5,M3,M4,M3,M4,H1,M3,M3,ZERO,H1,H1,H1,M7,half_M4,M4,M7,M7,M7,ZERO,M6,M7,H1,M7,H1,H3,M7,M7,M7,M3,M3,M6,M5,M6,H1,M5,M5,M5,M2,M3,M4,H1,M7,M7,H1,H1,H2,H2,H3,H1,H1,H1,H1,M7,M6,M6,M7,half_M5,M6,M6,M6,H1,H2,H3,H2,H3,H5,H2,H2,H2,M5,M5,H1,M7,H1,H3,H3,H3,H3,H3,M6,M7,H1,M7,H2,H2,H1,M5,M5,M5,H4,H3,H2,H1,H3,H3,H3,H3,H6,H6,H5,H5,H3,H2,H1,H1,ZERO,H1,H2,H1,H2,H2,H5,H3,H3,H3,H3,H6,H6,H5,H5,H3,H2,H1,H1,ZERO,H1,H2,H1,H2,H2,M7,M6,M6,M6,M6,M7 };float duration[]= { 0.5,0.5, 1.5,0.5,1,1, 1,1,1,0.5,0.5,1.5,0.5,1,1, 1,1,1,1, 1.5,0.5,1,1, 1,1,0.5,0.5,0.5,0.5, 1+0.5,0.5,1,1, 1,1,1,0.5,0.5,1+0.5,0.5,1,1, 1,1,1,0.5,0.5, 1+0.5,0.5,1,1,1,1,1,0.5,0.5, 1,0.5,0.25,0.25,0.25,0.5, 0.5,0.5,0.5,0.25,0.5,1,0.5,0.5,0.5,0.5,1,1, 1,1,1,0.5,0.5, 1+0.5,0.5,1,1,1,1,1,0.5,0.5, 1.5,0.5,1,1, 1,1,1,1,0.5,0.5,1,1,0.5,0.5, 1.5,0.25,0.5,1, 1,1,1,1,1,1,1,1, 1,1,1,1, 0.5,0.5,1,1,0.5,0.5,1,0.5,0.5,1,1, 1,1,1,1, 1,1,1,1,0.5,0.5,1,1,0.5,0.5, 1,0.5,0.25,0.5,1, 1,1,1,0.5,0.5 };//這部分是整首曲子的節拍部分,也定義個序列duration,浮點(數組的個數和前面音符的個數是一樣的,一一對應么)int length = sizeof(tune)/sizeof(tune[0]);//這里用了一個sizeof函數, 可以查出tone序列里有多少個音符 //int length;//這里定義一個變量,后面用來表示共有多少個音符void main(void) {Stm32_Clock_Init(336,8,2,7);//設置時鐘,168Mhz delay_init(168); //延時初始化 for(x=0;x<length;x++)//循環音符的次數{ if(flag==1) //上一個音符是0,在遇到下一個音符前重新使用IO口的復用功能{//RCC->AHB1ENR|=1<<5; //使能PORTF時鐘 //RCC->APB1ENR|=1<<8; //使能TIM14時鐘GPIO_Set(GPIOF,PIN9,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);//復用功能,上拉輸出GPIO_AF_Set(GPIOF,9,9); //PF9,AF9 flag=0;//將標志置0}if(tune[x]==ZERO){//RCC->APB1ENR|=0<<8; //關閉TIM14時鐘 //RCC->AHB1ENR|=0<<5; //關閉PORTF時鐘使PF9引腳無法輸出PWM波GPIO_Set(GPIOF,PIN9,GPIO_MODE_IN,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);//將PF9設置成普通IO口,輸入flag=1;//將關閉標志置1}GPIO_Set(GPIOF,PIN9,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);//復用功能,上拉輸出GPIO_AF_Set(GPIOF,9,9); //PF9,AF9//設置成復用模式(此處就是PWM端口),輸出TIM14_PWM_Init(tune[x],8400-1); //(arr,psc)if(flag==1)delay_ms(300*duration[x]);//否則延時會很長elsedelay_ms(400*duration[x]);//每個音符持續的時間,即節拍duration//設置成一個全拍400ms}//RCC->APB1ENR|=0<<8; //關閉TIM14時鐘 //RCC->AHB1ENR|=0<<5; //關閉PORTF時鐘GPIO_Set(GPIOF,PIN9,GPIO_MODE_IN,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);//把PF9設置成普通IO口輸入就可以了,就不會有噪聲了。//delay_ms(10000);//還是會有聲音,不知道為什么//GPIO_AF_Set(GPIOF,9,9); //PF9,AF9 while(1);//防止程序跑飛 }//TIM14 PWM部分初始化函數 //PWM輸出初始化 //arr:自動重裝值 //psc:時鐘預分頻數 void TIM14_PWM_Init(u32 arr,u32 psc) { //此部分需手動修改IO口設置RCC->APB1ENR|=1<<8; //TIM14時鐘使能 RCC->AHB1ENR|=1<<5; //使能PORTF時鐘 //GPIO_Set(GPIOF,PIN9,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);//復用功能,上拉輸出//GPIO_AF_Set(GPIOF,9,9); //PF9,AF9 TIM14->ARR=arr; //設定計數器自動重裝值 TIM14->PSC=psc; //預分頻器分頻 TIM14->CCMR1|=6<<4; //CH1 PWM1模式 TIM14->CCMR1|=1<<3; //CH1 預裝載使能 TIM14->CCER|=1<<0; //OC1 輸出使能 TIM14->CCER|=1<<1; //OC1 低電平有效 TIM14->CR1|=1<<7; //ARPE使能 TIM14->CR1|=1<<0; //使能定時器14TIM14->CCR1=arr*0.5; //占空比= TIM14->CCR1 / arr(單位:%)//設置占空比為50% }總結
以上是生活随笔為你收集整理的STM32F4应用笔记(二)利用蜂鸣器播放天空之城的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不是吧!你还不懂DHT协议?
- 下一篇: MATLAB人脸识别算法