二、音頻播放程序設計
1、程序實(shí)現的方案
(1)目的
添加一個(gè)命令msplay,后帶一個(gè)參數,就是要播放的WAV文件的名稱(chēng)。
支持播放PCM格式的文件,其它編碼方式暫不支持。
(2)實(shí)現方案
參照
利用TIM4通道1定期產(chǎn)生的中斷,從緩沖期中讀取聲音數據。這個(gè)中斷頻率就是WAV文件的采樣頻率。
聲音數據通過(guò)TIM3的通道3產(chǎn)生不同頻率方波輸出。我這個(gè)板上的PB0接著(zhù)揚聲器,可以復用為TIM3_CH3。
為了聲音播放的連續性,采用兩個(gè)數據緩沖區。在中斷處理程序中,當一個(gè)緩沖區的數據讀完后,自動(dòng)切換到下一個(gè)緩沖區開(kāi)頭。而在主程序中,當發(fā)現一個(gè)緩沖區空以后,又很快讀入聲音數據。
(3)程序執行流程分析
在串口輸入命令“msplay ***.wav”,進(jìn)入音頻播放命令處理程序。
首先讀入文件頭的44個(gè)字節,獲取wav文件頭信息,對比標志“RIFF”,“WAVE”“fmt ”,獲取編碼方式、聲道、采樣頻率、位數,每次采樣的字節數、每秒采樣的字節數,根據“fmt ”后面的size字段定位 “DATA CHUNK”,獲得數據區開(kāi)始偏移。暫時(shí)不考慮有“fact CHUCK”的情況。
數據的讀取通過(guò)設置幾個(gè)結構體,定義一個(gè)指針讓它指向緩沖區頭部,將該指針前者類(lèi)型轉換成結構指針,取出緩沖區對應的屬性數據。
若對應位置的數據不符合要求,則程序退出。
將重要的數據都保存到WavInfo結構體中。
根據采樣率的要求,初始化TIM4。然后將TIM3進(jìn)行初始化設置,并設置CH3輸出通道。填充0號緩沖區,使能TIM4中斷。置位播放標志。播放實(shí)際上就開(kāi)始了。
2、程序代碼設計
(1)重點(diǎn)是先要把定時(shí)器的配置弄清楚
首先TIM2、TIM3和TIM4都位于APB1,最高頻率36MHz,TIM2已經(jīng)被我使用,利用通道3產(chǎn)生方波,為液晶提供穩壓電源驅動(dòng)脈沖。它的輸出是采用了輸出比較模式。下面先回顧一下一個(gè)重要寄存器和工作模式。
預分頻PSC:控制計數的實(shí)際頻率,定時(shí)器并不直接對36MHz的脈沖計數,而要先分頻。計數頻率 = 36MHz 除以 (PSC +1)。
自動(dòng)重裝載ARR:實(shí)際控制計數周期,相當于定時(shí)器的提前溢出值,比如設置成0x100,則當從0計數到0x100時(shí),定時(shí)器自動(dòng)復位到初始值。(我的理解不知道對不對)?;蛘咴谙蛳掠嫈的J綍r(shí),每次都從自動(dòng)裝載值開(kāi)始計數,向下計數到0,然后又重新裝載ARR的值。
其它的再看一下參考資料:
時(shí)鐘選擇:我現在是采用內部時(shí)鐘源,所以要禁止從模式。CR1中CEN使能內部時(shí)鐘,開(kāi)啟定時(shí)器,DIR控制計數方向。
比較通道的預裝載含義:如果不使能預裝載,比較值總是立即寫(xiě)入當前比較寄存器。如果使能,則設定的比較值現在不起作用,等到一個(gè)更新事件發(fā)生時(shí),才寫(xiě)入當前比較寄存器。
自動(dòng)重裝載寄存器也有同樣的預裝載的含義。
向上計數PWM模式1:初始化時(shí),計數值為0,比較輸出參考電平為高。當計數值大于比較值時(shí),輸出電平為低。所以寫(xiě)ARR是控制PWM的頻率,寫(xiě)CCR是控制脈沖的占空比。但是要特別注意OCREF與OC輸出極性之間的關(guān)系,要不然有可能你想增加占空比的時(shí)候反而減小了。
比較模式是在匹配時(shí)產(chǎn)生動(dòng)作,如果利用比較匹配、輸出翻轉模式產(chǎn)生波形,只能產(chǎn)生方波。而且輸出頻率在除以周期值以后還得除以2。
而PWM才是真正的像是在比較大小,計數值小于比較值時(shí)輸出高電平,大于比較值時(shí)輸出低電平(OCREF引腳)。PWM中心對稱(chēng)模式是指所有輸出引腳的波形中心對稱(chēng),它內部實(shí)現是通過(guò)先向上計數、再向下計數實(shí)現的。其輸出頻率在相同周期值的情況下,為向上計數模式的一半
(2)添加命令msplay,先設計出框架
(1)要使用定時(shí)器4,先要使能時(shí)鐘。
(2)要使用定時(shí)器4的更新中斷,必須設置NVIC。
(3)讓定時(shí)器3工作于PWM1模式下,其頻率設置為72000 000/256大概為280kHz,這就是PWM頻率(要工作于高頻下,我試著(zhù)將頻率降低,結果就只能聽(tīng)到噪音)。
因為測試文件是單聲道、8位采樣的,所以采樣值小的時(shí)候,PWM占空比就小,PWM輸出平均幅度就與波形文件中的數據大小成正比,因此揚聲器可以將聲音還原。
void Speaker_Start( WAVINFO *WavInfo )
{
GPIO_InitTypeDef GPIO_InitStructure; //進(jìn)行GPIO端口設置的數據結構
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //時(shí)基單元配置數據結構
TIM_OCInitTypeDef TIM_OCInitStructure; //輸出模式設置數據結構
/* PB0腳接著(zhù)定時(shí)器3 CH3,PWM脈沖輸出,該輸出控制揚聲器 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* 時(shí)基單元配置:TIM3計數頻率 =72MHz除以分頻系數。*/
TIM_TimeBaseStructure.TIM_Period = 255; //寫(xiě)入自動(dòng)裝載寄存器,PWM頻率大約36000000/256。
TIM_TimeBaseStructure.TIM_Prescaler = 0; //
TIM_TimeBaseStructure.TIM_ClockDivision = 0;//與什么采樣相關(guān),這里用不到
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* TIM3 配置在PWM1模式, */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //輸出使能
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OCInitStructure.TIM_Pulse = 0; /* 占空比初始化為0*/
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//高電平有效,因為PWM輸出接三極管基極。
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
/* TIM4 配置為定時(shí)中斷,其頻率為WAV文件采樣頻率 */
TIM_TimeBaseStructure.TIM_Period = 72000000/WavInfo->SampFreq;
TIM_TimeBaseStructure.TIM_Prescaler = 0x0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
/* Enable the TIM4 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); //剛開(kāi)始時(shí)忘了這個(gè)。
/* Start TIM3 */
TIM_Cmd(TIM3, ENABLE);
/* Start TIM4 */
TIM_Cmd(TIM4, ENABLE);
/* Enable TIM4 update interrupt */
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
}
聯(lián)系客服