音乐播放器课程设计报告1.docx
《音乐播放器课程设计报告1.docx》由会员分享,可在线阅读,更多相关《音乐播放器课程设计报告1.docx(9页珍藏版)》请在冰豆网上搜索。
![音乐播放器课程设计报告1.docx](https://file1.bdocx.com/fileroot1/2022-12/12/379ad64c-9536-4304-99ef-c004222aa42f/379ad64c-9536-4304-99ef-c004222aa42f1.gif)
音乐播放器课程设计报告1
音乐播放器课程设计报告
课程设计报告
课程设计名称:
音乐播放器
院系:
信息工程学院
学生姓名:
班级:
学号:
成绩:
指导教师:
开课时间:
2016-2017学年2学期
音乐播放器
一、实践的内容和要求
1.1实验内容
学习WAV格式音频播放原理、数字音频接口及的相关知识。
了解模拟音频信号和数字音频信号的概念。
掌握编写音频播主要内容放器的方法。
实现音频播放,小键盘输入操作。
1.2实验要求
本系统要做一个音乐播放器,使用通用定时器定时,用比较输出功能,输出乐谱频率的方波产生声音,对3首歌曲编码,其支持WAV格式音频播放(上键:
暂停;左键:
上一首;右键:
下一首)。
利用硬件ARM嵌入式开发平台、用于ARM7TDMI的JTAG仿真器、PC机Pentium100开发实现实现音频播放。
主要实现:
上一首按键:
当点击上一首按键时,当前播放歌曲的上一首歌首被播放。
下一首按键:
当点击下一首按键时,当前播放歌曲的下一首歌曲即被播放。
当点击停止键时,当前歌曲即会被停止。
播放界面设计:
在歌曲进行播放时,界面应包含所有的按键,并且要歌曲的曲目单。
二、实践原理
WAV播放器系统要求能够对mp3、wav格式的音频文件进行解码,处理起来速度是有要求的,尤其是对320Kbps的MP3格式数据流的处理,必须非常快,才能实现MP3的流畅播放,可供选择的八位机有AVR、PIC,可供选择的32位处理器有SAMSUNG、STM32、ATMEL等。
选择MCU优先考虑于拥有丰富的RAM,八位机RAM主要靠扩展,内部RAM一般在4Kbyte以内,32位机里面,SAMSUNG一般不内带RAM,也要外扩。
本实验使用了ARM7处理器,MP3解码可以有两种方法,一是通过ARM处理器软解码,通过对MP3数据格式的解析实现MP3播放,二是通过外部解码芯片直接解码。
第一种方法在解码高码率的MP3时,ARM7处理能力吃紧,而且STM32还需要外部DAC来做音频输出,所以采用第二种方法进行解码。
MP3常见的解码芯片有VS100X系列和STA01系列,STA01需要外接DAC做音频输出,而VS1003自带DAC,且解码的文件格式包含mp3和wav,另外它还可以实现录音功能,方便以后进行功能的拓展应用。
三、实践步骤
3.1功能设计
开机后,先初始化各外设,然后检测字库文件夹是否存在,如果有问题,则提示Error,如果检测无问题,则开始循环播放SD卡内MUSIC文件夹里面的歌曲,在TFTLCD上显示歌曲名字、播放时间、歌曲总时间、歌曲数目、当前歌曲的编号等信息。
KEY0选择下一首,KEY2选择上一首,KEY_UP控制暂停/继续播放。
3.2实验步骤
1)初始化WM8978,这个过程包括软复位、DAC设置、输出设置和音量设置等。
2)初始化I2S,此过程主要设置SPI_I2SCFGR寄存器,设置I2S模式、I2S标准、时钟空闲电平和数据帧长等,最后开启I2STXDMA,使能I2S外设。
在库函数中初始化I2S调用的函数为:
voidI2S_Init(SPI_TypeDef*SPIx,I2S_InitTypeDef*I2S_InitStruct);结构体I2S_InitTypeDef的定义为:
typedefstruct
{uint16_tI2S_Mode;
uint16_tI2S_Standard;
uint16_tI2S_DataFormat;
uint16_tI2S_MCLKOutput;
uint32_tI2S_AudioFreq;
uint16_tI2S_CPOL;}I2S_InitTypeDef;
第一个参数用来设置I2S的模式,也就是设置SPI_I2SCFGR寄存器的I2SCFG相关位。
可以配置为主模式发送I2S_Mode_MasterTx,主模式接受I2S_Mode_MasterRx,从模式发送I2S_Mode_SlaveTx以及从模式接受I2S_Mode_SlaveRx四种模式。
第二个参数
I2S_Standard用来设置I2S标准,这个前面已经讲解过。
可以设置为:
飞利浦标准
I2S_Standard_Phillips,MSB对齐标准I2S_Standard_MSB,LSB对齐标准
I2S_Standard_LSB以及PCM标准I2S_Standard_PCMShort。
第三个参数I2S_DataFormat用来设置I2S的数据通信格式。
这里实际包含设置SPI_I2SCFGR寄存器的HCLEN位(通道长度)以及DATLEN位(传输的数据长度)。
当我们设置为16位标准格式
I2S_DataFormat_16b的时候,实际上传输的数据长度为16位,通道长度为16位。
当我们设置为其他值的时候,通道长度都为32位。
第四个参数I2S_MCLKOutput用来设置是否使能主时钟输出。
我们实验会使能主时钟输出。
第五个参数I2S_AudioFreq用来设置I2S
频率。
实际根据输入的频率值,会来计算SPI预分频寄存器SPI_I2SPR的预分频奇数因子以及I2S线性预分频器的值。
这里支持10中频率:
#defineI2S_AudioFreq_192k((uint32_t)192000)
#defineI2S_AudioFreq_96k((uint32_t)96000)
#defineI2S_AudioFreq_48k((uint32_t)48000)
#defineI2S_AudioFreq_44k((uint32_t)44100)
#defineI2S_AudioFreq_32k((uint32_t)32000)
#defineI2S_AudioFreq_22k((uint32_t)22050)
#defineI2S_AudioFreq_16k((uint32_t)16000)
#defineI2S_AudioFreq_11k((uint32_t)11025)
#defineI2S_AudioFreq_8k((uint32_t)8000)
#defineI2S_AudioFreq_Default((uint32_t)2)
第六个参数I2S_CPOL用来设置空闲状态时钟电平,取值为高电平I2S_CPOL_High以及低电平I2S_CPOL_Low。
3)解析WAV文件,获取音频信号采样率和位数并设置I2S时钟分频器这里,要先解析WAV文件,取得音频信号的采样率(fs)和位数(16位或32位),根据这两个参数,来设置I2S的时钟分频,这里我们用前面介绍的查表法来设置即可。
这是我们单独写了一个设置频率的函数为I2S2_SampleRate_Set。
4)设置DMAI2S播放音频的时候,一般都是通过DMA来传输数据的,所以必须配置DMA,本章我们用I2S2,其TX是使用的DMA1数据流4的通道0来传输的。
并且STM32F4的DMA具有双缓冲机制,这样可以提高效率,大大方便了我们的数据传输,本章将DMA1数据流4设置为:
双缓冲循环模式,外设和存储器都是16位宽,并开启DMA传输完成中断(方便填充数据)。
5)编写DMA传输完成中断服务函数为了方便填充音频数据,我们使用DMA传输完成中断,每当一个缓冲数据发送完后,硬件自动切换为下一个缓冲,同时进入中断服务函数,填充数据到发送完的这个缓冲。
6)开启DMA传输,填充数据最后,我们就只需要开启DMA传输,然后及时填充WAV数据到DMA的两个缓存区即可。
此时,就可以在WM8978的耳机和喇叭通道听到所播放音乐了。
操作方法为:
DMA_Cmd(DMA1_Stream4,ENABLE);//开启DMATX传输,开始播放。
3.3流程图
3.4关键代码
I2s.c代码:
#include"i2s.h"
#include"usart.h"
voidI2S2_Init(u16I2S_Standard,u16I2S_Mode,u16I2S_Clock_Polarity,u16I2S_DataFormat)
{
I2S_InitTypeDefI2S_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);//使能SPI2时钟
RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2,ENABLE);//复位SPI2
RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2,DISABLE);//结束复位
I2S_InitStructure.I2S_Mode=I2S_Mode;//IIS模式
I2S_InitStructure.I2S_Standard=I2S_Standard;//IIS标准
I2S_InitStructure.I2S_DataFormat=I2S_DataFormat;//IIS数据长度
I2S_InitStructure.I2S_MCLKOutput=I2S_MCLKOutput_Disable;//主时钟输出禁止
I2S_InitStructure.I2S_AudioFreq=I2S_AudioFreq_Default;//IIS频率设置
I2S_InitStructure.I2S_CPOL=I2S_Clock_Polarity;//空闲状态时钟电平
I2S_Init(SPI2,&I2S_InitStructure);//初始化IIS
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);//SPI2TXDMA请求使能.
I2S_Cmd(SPI2,ENABLE);//SPI2I2SEN使能.
}
//采样率计算公式:
Fs=I2SxCLK/[256*(2*I2SDIV+ODD)]
//I2SxCLK=(HSE/pllm)*PLLI2SN/PLLI2SR
//一般HSE=8Mhz
//pllm:
在Sys_Clock_Set设置的时候确定,一般是8
//PLLI2SN:
一般是192~432
//PLLI2SR:
2~7
//I2SDIV:
2~255
//ODD:
0/1
//I2S分频系数表@pllm=8,HSE=8Mhz,即vco输入频率为1Mhz
//表格式:
采样率/10,PLLI2SN,PLLI2SR,I2SDIV,ODD
constu16I2S_PSC_TBL[][5]=
{
{800,256,5,12,1},//8Khz采样率
{1102,429,4,19,0},//11.025Khz采样率
{1600,213,2,13,0},//16Khz采样率
{2205,429,4,9,1},//22.05Khz采样率
{3200,213,2,6,1},//32Khz采样率
{4410,271,2,6,0},//44.1Khz采样率
{4800,258,3,3,1},//48Khz采样率
{8820,316,2,3,1},//88.2Khz采样率
{9600,344,2,3,1},//96Khz采样率
{17640,361,2,2,0},//176.4Khz采样率
{19200,393,2,2,0},//192Khz采样率
};
//设置IIS的采样率(@MCKEN)
//samplerate:
采样率,单位:
Hz
//返回值:
0,设置成功;1,无法设置.
u8I2S2_SampleRate_Set(u32samplerate)
{
u8i=0;
u32tempreg=0;
samplerate/=10;//缩小10倍
for(i=0;i{
if(samplerate==I2S_PSC_TBL[i][0])break;
}
RCC_PLLI2SCmd(DISABLE);//先关闭PLLI2S
if(i==(sizeof(I2S_PSC_TBL)/10))return1;//搜遍了也找不到
RCC_PLLI2SConfig((u32)I2S_PSC_TBL[i][1],(u32)I2S_PSC_TBL[i][2]);//设置I2SxCLK的频率(x=2)设置PLLI2SNPLLI2SR
RCC->CR|=1while((RCC->CR&1tempreg=I2S_PSC_TBL[i][3]tempreg|=I2S_PSC_TBL[i][4]tempreg|=1SPI2->I2SPR=tempreg;//设置I2SPR寄存器
return0;
}
//I2S2TXDMA配置
//设置为双缓冲模式,并开启DMA传输完成中断
//buf0:
M0AR地址.
//buf1:
M1AR地址.
//num:
每次传输数据量
voidI2S2_TX_DMA_Init(u8*buf0,u8*buf1,u16num)
{
NVIC_InitTypeDefNVIC_InitStructure;
DMA_InitTypeDefDMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能
DMA_DeInit(DMA1_Stream4);
while(DMA_GetCmdStatus(DMA1_Stream4)!
=DISABLE){}//等待DMA1_Stream1可配置
/*配置DMAStream*/
DMA_InitStructure.DMA_Channel=DMA_Channel_0;//通道0SPI2_TX通道
DMA_InitStructure.DMA_PeripheralBaseAddr=(u32)&SPI2->DR;//外设地址为:
(u32)&SPI2->DR
DMA_InitStructure.DMA_Memory0BaseAddr=(u32)buf0;//DMA存储器0地址
DMA_InitStructure.DMA_DIR=DMA_DIR_MemoryToPeripheral;//存储器到外设模式
DMA_InitStructure.DMA_BufferSize=num;//数据传输量
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设非增量模式
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//存储器增量模式
DMA_InitStructure.DMA_PeripheralDataSize=
DMA_PeripheralDataSize_HalfWord;//外设数据长度:
16位
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//存储器数据长度:
16位
DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;//使用循环模式
DMA_InitStructure.DMA_Priority=DMA_Priority_High;//高优先级
DMA_InitStructure.DMA_FIFOMode=DMA_FIFOMode_Disable;//不使用FIFO模式
DMA_InitStructure.DMA_FIFOThreshold=DMA_FIFOThreshold_1QuarterFull;
DMA_InitStructure.DMA_MemoryBurst=DMA_MemoryBurst_Single;//外设突发单次传输
DMA_InitStructure.DMA_PeripheralBurst=DMA_PeripheralBurst_Single;//存储器突发单次传输
DMA_Init(DMA1_Stream4,&DMA_InitStructure);//初始化DMAStream
DMA_DoubleBufferModeConfig(DMA1_Stream4,(u32)buf1,DMA_Memory_0);//双缓冲模式配置
DMA_DoubleBufferModeCmd(DMA1_Stream4,ENABLE);//双缓冲模式开启
DMA_ITConfig(DMA1_Stream4,DMA_IT_TC,ENABLE);//开启传输完成中断
NVIC_InitStructure.NVIC_IRQChannel=DMA1_Stream4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x00;//抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x00;//子优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);//配置
}
//I2SDMA回调函数指针
void(*i2s_tx_callback)(void);//TX回调函数
//DMA1_Stream4中断服务函数
voidDMA1_Stream4_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_Stream4,DMA_IT_TCIF4)==SET)////DMA1_Stream4,传输完成标志
{
DMA_ClearITPendingBit(DMA1_Stream4,DMA_IT_TCIF4);
i2s_tx_callback();//执行回调函数,读取数据等操作在这里面处理
}
}
//I2S开始播放
voidI2S_Play_Start(void)
{
DMA_Cmd(DMA1_Stream4,ENABLE);//开启DMATX传输,开始播放
}
//关闭I2S播放
voidI2S_Play_Stop(void)
{
DMA_Cmd(DMA1_Stream4,DISABLE);//关闭DMA,结束播放
}
wm8978代码:
#include"wm8978.h"
#include"myiic.h"
#include"delay.h"
staticu16WM8978_REGVAL_TBL[58]=
{
0X0000,0X0000,0X0000,0X0000,0X0050,0X0000,0X0140,0X0000,0X0000,0X0000,0X0000,0X00FF,0X00FF,0X0000,0X0100,0X00FF,0X00FF,0X0000,0X012C,0X002C,0X002C,0X002C,0X002C,0X0000,0X0032,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0038,0X000B,0X0032,0X0000,0X0008,0X000C,0X0093,0X00E9,0X0000,0X0000,0X0000,0X0000,0X0003,0X0010,0X0010,0X0100,0X0100,0X0002,0X0001,0X0001,0X0039,0X0039,0X0039,0X0039,0X0001,0X0001
};
//WM8978初始化
//返回值:
0,初始化正常
//其他,错误代码
u8WM8978_Init(void)
{
u8res;
GPIO_InitTypeDefGPIO_InitStructure;
-全文完-