使用定時器來計算時間
??在電子琴這節中,我們已經講述了蜂鳴器的原理,知道如何用蜂鳴器演示不同音調的音樂,本節改進根據頻率計算周期的方法,改為定時器,精確度更高,且不再阻塞CPU。
??首先,我們不再把蜂鳴器的控制引腳PB1作為普通IO,而是作為定時器的通道。在IO的初始化中,不應當繼續操作PB1。通過查看數據手冊,可以知道,PB1可以作為定時器3的通道4。(當然也可以作為定時器1和定時器8的通道,只不過定時器1和8是高級定時器,用起來稍微復雜一點點)。
通道的概念類似于道路。
??然后編寫初始化函數。這段初始化函數可能比較復雜,我們暫時無需深究,只需要知道,這個定時器做了這么一件事情:
把原先這樣的代碼延時,交給了定時器自帶的功能來實現:
time_ON = F_us>>tvolum;
time_OFF = F_us - time_ON;
BEEP = 1;
delay_us(time_ON);
BEEP = 0;
delay_us(time_OFF);
??定時器的用法是單片機里的重點和難點,不用想得太復雜,他就是個表嘛,只不過,他不能直接告訴你過了多少時間,他只知道數了多少個數字,也知道數一個數字用多長時間,兩者結合,能算出過了多長時間。
??定時器3的初始化代碼
//beep.c
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM_CH4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置用來作為TIMx時鐘頻率除數的預分頻值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設置時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位
//初始化TIM3 Channel2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //選擇定時器模式:TIM脈沖寬度調制模式2 每路產生
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
TIM_OCInitStructure.TIM_Pulse=0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性,高低沒有區別
TIM_OC4Init(TIM3, &TIM_OCInitStructure); //根據T指定的參數初始化外設TIM3 OC4
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR4上的預裝載寄存器
TIM_ARRPreloadConfig(TIM3, ENABLE);
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
??我們在主函數中調用這個中斷服務函數:
TIM3_PWM_Init(0xfffe,8); //蜂鳴器頻率定時器初始化
??自動重裝值決定什么時候定時器溢出,而分頻系數決定定時器自加的頻率,舉例來說,定時器就像捉迷藏游戲中,負責捉人的小朋友,他要計數,好讓大家躲起來。分頻系數決定數數字的快慢,自動重裝值決定數到幾。
關于定時器溢出的時間計算,有個公式:
Tout = ((arr+1)*(psc+1))/Tclk
Tout 是溢出時間
arr是自動重裝值,也可以稱為周期或計數值,捉迷藏要數到10,那么代表arr就是9。默認情況下arr要+1,代表即便參數是0,也不會誤操作。
psc就是分頻系數,決定捉迷藏數多快。這個數字越大,代表數的越慢。
Tclk代表輸入時鐘,我們采用的是72Mhz
??定時器本質上是一個不斷自加的計數器,只不過在自加的時候,能夠自動比較計數值跟某個設定值而已。定時器+1用時多少?
??1/72000000,單位是秒。
??我想讓數的慢一點,感覺72Mhz的時鐘太快了,想用36Mhz可以嗎?可以,2分頻就行,這是+1的操作用時
??2/72000000,
??數100個數字用時多少?
??100 * 2/72000000
??在初始化的時候,我們傳入的第一個參數是數多少個數字。第二個參數是數數字的速度。此處設置的頻率是72/9=8Mhz。兩個參數共同決定周期,由于第一個參數要根據頻率改變,所以初始化的時候并不關心。
根據頻率計算自動重裝值
??接下來需要一個函數,把頻率變為自動重裝值。溢出時間 = 自動重裝值+1/8000000,頻率是時間的倒數,音調與頻率有關,所以知道音調(頻率)以后,可用以下方法計算自動重裝值:
Autoreload=(8000000/usFraq)-1; //頻率變為自動重裝值
??特別的,當音調是0的時候,不發出任何聲音。我們可以采取關閉定時器的方法來靜音,但是不方便。比較方便的方法就是設置比較值,當自動重裝值小于比較值的時候,引腳輸出高電平,否則,引腳輸出低電平。按理說,把比較值設置為0xffff,則無論如何自動重裝值都小于比較值就可以保持靜音了,不過實測,此時會有噪音,嘗試后發現,設置比較值為0的時候,可以靜音,且沒有噪音。可以調用庫函數設置比較值。在頻率很小或者大于20000時,人類都聽不到,此時可以靜音。
//beep.c
//蜂鳴器停止發聲
void buzzerQuiet(void)
{
TIM_SetCompare4(TIM3,0);
}
??在頻率小于122的時候,我們也認為應當靜音,是因為一方面,各音調的頻率都大于122;另一方面,自動重裝值最大是65535,此時對應的頻率就是122。
??我們已經知道,音量由占空比決定,根據頻率算出自動重裝值以后,把它右移(相當于除以2的倍數)若干位,可以調整音量。
//beep.c
//蜂鳴器發出聲音
//usFreq即發聲頻率,volume_level是音量等級,1最高,到9幾乎就聽不到聲音了。
void buzzerSound(unsigned short usFraq,unsigned char volume_level) //usFraq是發聲頻率,即真實世界一個音調的頻率。
{
unsigned long Autoreload;
if((usFraq<=122)||(usFraq>20000))
{
buzzerQuiet();
}
else
{
Autoreload=(8000000/usFraq)-1; //頻率變為自動重裝值
TIM_SetAutoreload(TIM3,Autoreload);
TIM_SetCompare4(TIM3,Autoreload>>volume_level); //音量
}
}
??特別強調一點,比較值與自動重裝值比較大小,這個功能是定時器自帶的,不需要在中斷服務里比較。
主函數循環
??調用函數來演奏某個音樂是很簡單的。我們先在主函數中演奏兩只老虎的開頭。
//main.c
int main(void)
{
LED_Init();
KEY_Init();
delay_init();
initIIC();
initOLED();
TIM3_PWM_Init(0xfffe,8); //蜂鳴器頻率定時器初始化
while(1)
{
buzzerSound(CM1,volum);
delay_ms(250);
buzzerSound(CM2,volum);
delay_ms(250);
buzzerSound(CM3,volum);
delay_ms(250);
buzzerSound(CM1,volum);
delay_ms(250);
buzzerSound(0,volum);
delay_ms(250);
}
}
從簡譜到數組
??接下來我們嘗試用數組來儲存樂譜。我們要知道每個音符的音調和持續的時間,所以可以定義一個新的結構體:
typedef struct
{
short mName; //音名
short mTime; //時值,全音符,二分音符,四分音符
}tNote;
??然后我把兩只老虎的樂譜改寫如下:
const tNote AllBGM[]=
{
//兩只老虎 36 BEGIN_BGM
{CM1,TT/4},{CM2,TT/4},{CM3,TT/4},{CM1,TT/4},
{CM1,TT/4},{CM2,TT/4},{CM3,TT/4},{CM1,TT/4},
{CM3,TT/4},{CM4,TT/4},{CM5,TT/4},{0,TT/4},
{CM3,TT/4},{CM4,TT/4},{CM5,TT/4},{0,TT/4},
{CM5,TT/8},{CM6,TT/8},{CM5,TT/8},{CM4,TT/8},{CM3,TT/4},{CM1,TT/4},
{CM5,TT/8},{CM6,TT/8},{CM5,TT/8},{CM4,TT/8},{CM3,TT/4},{CM1,TT/4},
{CM1,TT/4},{CL5,TT/4},{CM1,TT/4},{0,TT/4},
{CM1,TT/4},{CL5,TT/4},{CM1,TT/4},{0,TT/4},
};
??我們采用C調,中間的那個音階,所有的音調都是CMx。每一小節都分4個音符,所以每個音符持續的時間都是某個常數的四分之一。這個常數我定義為TT,其實它與現實世界的沒有直接的對應關系,數字小一點,唱的就快一點。如果音符有個下劃線,那么持續的時間就是八分之一,再有個下劃線,時間就是十六分之一。
??用于演奏音樂的函數如下:
void musicPlay(int length,unsigned char volume_level)
{
u8 i=0;
while(i buzzerSound(AllBGM[i].mName,volume_level); delay_ms(AllBGM[i].mTime); i++; } } ??我傳入的參數是數組的長度與音量,其實應當傳入某個樂譜的指針,只是擔心指針與結構體一起用,容易懵逼,因此樂譜作為了全局的變量。然后主函數值調用這個播放函數就可以了。 while(1) { musicPlay(36,volum); }
上一篇:STM32掌機教程8,背景音樂
下一篇:STM32掌機教程6,電子琴
推薦閱讀
史海拾趣
CCS公司深知人才是企業發展的核心競爭力。因此,公司高度重視人才培養和團隊建設。公司建立了完善的人才培養和激勵機制,通過定期的培訓、學習和實踐鍛煉,不斷提升員工的專業技能和綜合素質。同時,CCS公司還注重團隊建設,通過團隊建設活動和文化建設,增強員工的凝聚力和歸屬感,為公司的發展提供有力的人才保障。
為了提高產品質量和客戶滿意度,磁聯達(CND-tek)公司引入了一套嚴格的質量管理體系。公司從原材料采購、生產過程到成品檢驗等各個環節都進行了嚴格把關,確保每一件產品都符合高標準的質量要求。此外,公司還建立了完善的售后服務體系,為客戶提供及時、專業的技術支持和解決方案。這些舉措使得磁聯達(CND-tek)的產品質量得到了客戶的高度認可。
Conxall公司的創始人李華,是一位在電子行業摸爬滾打多年的工程師。他深感市場上電子產品同質化嚴重,缺乏真正的創新。于是,他毅然決定創立Conxall公司,致力于研發具有獨特功能和高性能的電子產品。李華帶領團隊日夜奮戰,終于研發出了第一款具有自主知識產權的智能手機芯片,憑借其出色的性能和穩定性,迅速在市場上獲得了認可。
要求是在傳送帶的2端分別放置光電傳感器,中間用4個“7劃數碼管”(就是電子顯示的那個8)顯示傳送帶的速度 需要自行安裝排布所有的電路并分析 我們已完成光電傳感器的電路 問題在于如何使光電傳感器的輸出電壓作為測量速度的開始和結束?(需自 ...… 查看全部問答∨ |
|
我最近在做一塊ARM板上的藍牙底層初始化驅動,環境如下 板子:ARM11板 Linux內核版本:2.6.18 藍牙模塊:采用CSR的Bluecore5核心,和CPU通過UART相連 藍牙驅動:1.自己根據藍牙的datasheet在系統啟動時給藍牙模塊發送了一條Reset信號,并初始化 ...… 查看全部問答∨ |
求購windows CE下智能輸入法,最好是T9輸入法,至少含拼音和筆畫,windows CE操作系統,鍵盤4X5。有意聯系xiao615@126.com.… 查看全部問答∨ |
我們計算機組成原理考試,老師讓我們自己上網搜索具體運用了計算機組成原理知識的一些程序優化技術,比如沒優化時是怎樣的,運用了相關計算機組成知識優化后現在時間周期提高了多少, 望前輩們隨便舉個相關案例, 指個大方向也好, 我自己去查查, 急求, ...… 查看全部問答∨ |
|
我想打開png圖片,上網查到要用SHLoadImageFile函數可以,但是使用SHLoadImageFile函數需要aygshell.lib,我在網上找不到aygshell.lib,卻下載了AYGSHELL.DLL,可是光有AYGSHELL.DLL卻不知道應該怎么用它。 希望高人指點一下怎么用AYGSHELL.DLL來 ...… 查看全部問答∨ |
用單臺處理機順序計算表達式:f=a+be+ce^2+de^3,需幾級?若用三臺處理機計算此表達式,則只需幾級? 用單臺處理機順序計算表達式:f=a+be+ce^2+de^3,需幾級?若用三臺處理機計算此表達式,則只需幾級? … 查看全部問答∨ |
ST在業界第一個推出了基于Cortex-M3內核的無傳感器電機控制方 相關新聞STM32電機控制開發套件意法半導體(ST)推出首款在基于ARM Cortex的微控制器上運行的無傳感器磁場定向電機控制解決方案STM32無傳感器永磁同步電機矢量控制驅動器的CPU占用率低于30%中國,2008年2月5日 — 意法半導體( ...… 查看全部問答∨ |