音乐播放器课程设计报告 下载本文

课 程 设 计 报 告

课程设计名称: 音乐播放器 院 系: 信息工程学院 学生姓名: 班 级: 学 号:

成 绩: 指导教师:

开课时间: 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,另外它还可以实现录音功能,方便以后进行功能的拓展应用。

1

三、实践步骤

3.1功能设计

开机后,先初始化各外设,然后检测字库文件夹是否存在,如果有问题,则提示Error,如果检测无问题,则开始循环播放 SD卡内MUSIC 文件夹里面的歌曲,在 TFTLCD 上显示歌曲名字、播放时间、歌曲总时间、歌曲数目、当前歌曲的编号等信息。KEY0选择下一首,KEY2 选择上一首,KEY_UP 控制暂停/继续播放。 3.2实验步骤

1)初始化 WM8978,这个过程包括软复位、DAC 设置、输出设置 和音量设置等。 2)初始化 I2S,此过程主要设置 SPI_I2SCFGR 寄存器,设置 I2S 模式、I2S 标准、时钟空闲电平和数据帧长等,最后开启 I2S TX DMA,使能 I2S 外设。 在库函数中初始化 I2S 调用的函数为:void I2S_Init(SPI_TypeDef* SPIx, I2S_InitTypeDef* I2S_InitStruct); 结构体 I2S_InitTypeDef 的定义为:

typedef struct

{ uint16_t I2S_Mode; uint16_t I2S_Standard;

uint16_t I2S_DataFormat; uint16_t I2S_MCLKOutput; uint32_t I2S_AudioFreq;

uint16_t I2S_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

2

频率。实际根据输入的频率值,会来计算 SPI 预 分频寄存器 SPI_I2SPR 的预分频奇数因子以及 I2S 线性预分频器的值。这里支持 10 中频率:

#define I2S_AudioFreq_192k ((uint32_t)192000) #define I2S_AudioFreq_96k ((uint32_t)96000) #define I2S_AudioFreq_48k ((uint32_t)48000) #define I2S_AudioFreq_44k ((uint32_t)44100) #define I2S_AudioFreq_32k ((uint32_t)32000) #define I2S_AudioFreq_22k ((uint32_t)22050) #define I2S_AudioFreq_16k ((uint32_t)16000) #define I2S_AudioFreq_11k ((uint32_t)11025) #define I2S_AudioFreq_8k ((uint32_t)8000) #define I2S_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)设置 DMA I2S 播放音频的时候,一般都是通过 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);//开启 DMA TX 传输,开始播放。

3

3.3流程图

3.4关键代码

I2s.c代码: #include \#include \

void I2S2_Init(u16 I2S_Standard,u16 I2S_Mode,u16 I2S_Clock_Polarity,u16 I2S_DataFormat)

{

I2S_InitTypeDef I2S_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数据长度

4

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);//SPI2 TX DMA请求使能.

I2S_Cmd(SPI2,ENABLE);//SPI2 I2S EN使能. }

//采样率计算公式: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 const u16 I2S_PSC_TBL[][5]= {

{800 ,256,5,12,1}, {1102,429,4,19,0}, {1600,213,2,13,0}, {2205,429,4, 9,1},

//8Khz采样率 //11.025Khz采样率 //16Khz采样率 //22.05Khz采样率

5

{3200,213,2, 6,1}, {4410,271,2, 6,0}, {4800,258,3, 3,1}, {8820,316,2, 3,1}, {9600,344,2, 3,1}, {17640,361,2,2,0}, {19200,393,2,2,0},

//32Khz采样率 //44.1Khz采样率 //48Khz采样率 //88.2Khz采样率 //96Khz采样率 //176.4Khz采样率 //192Khz采样率

};

//设置IIS的采样率(@MCKEN) //samplerate:采样率,单位:Hz //返回值:0,设置成功;1,无法设置. u8 I2S2_SampleRate_Set(u32 samplerate) {

u8 i=0; u32 tempreg=0;

samplerate/=10;//缩小10倍

for(i=0;i<(sizeof(I2S_PSC_TBL)/10);i++)//看看改采样率是否可以支持 { }

RCC_PLLI2SCmd(DISABLE);//先关闭PLLI2S

if(i==(sizeof(I2S_PSC_TBL)/10))return 1;//搜遍了也找不到

if(samplerate==I2S_PSC_TBL[i][0])break;

RCC_PLLI2SConfig((u32)I2S_PSC_TBL[i][1],(u32)I2S_PSC_TBL[i][2]);//设置I2SxCLK的频率(x=2) 设置PLLI2SN PLLI2SR

6

}

RCC->CR|=1<<26; //开启I2S时钟

//等待I2S时钟开启成功.

while((RCC->CR&1<<27)==0);

tempreg=I2S_PSC_TBL[i][3]<<0; //设置I2SDIV tempreg|=I2S_PSC_TBL[i][4]<<8; //设置ODD位 tempreg|=1<<9;

//使能MCKOE位,输出MCK //设置I2SPR寄存器

SPI2->I2SPR=tempreg; return 0;

//I2S2 TX DMA配置

//设置为双缓冲模式,并开启DMA传输完成中断 //buf0:M0AR地址. //buf1:M1AR地址. //num:每次传输数据量

void I2S2_TX_DMA_Init(u8* buf0,u8 *buf1,u16 num) {

NVIC_InitTypeDef NVIC_InitStructure; DMA_InitTypeDef DMA_InitStructure;

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能

DMA_DeInit(DMA1_Stream4);

while (DMA_GetCmdStatus(DMA1_Stream4) != DISABLE){}//等待DMA1_Stream1可配置

/* 配置 DMA Stream */

DMA_InitStructure.DMA_Channel = DMA_Channel_0; //通道0 SPI2_TX通道 DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI2->DR;//外设地址为:(u32)&SPI2->DR

7

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);//初始化DMA Stream

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

8

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道 NVIC_Init(&NVIC_InitStructure);//配置 }

//I2S DMA回调函数指针

void (*i2s_tx_callback)(void); //TX回调函数 //DMA1_Stream4中断服务函数 void DMA1_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开始播放

void I2S_Play_Start(void) { }

//关闭I2S播放

void I2S_Play_Stop(void) {

DMA_Cmd(DMA1_Stream4,DISABLE);//关闭DMA,结束播放

}

DMA_Cmd(DMA1_Stream4,ENABLE);//开启DMA TX传输,开始播放

9

}

wm8978代码: #include \#include \#include \

static u16 WM8978_REGVAL_TBL[58]= { };

//WM8978初始化 //返回值:0,初始化正常 // 其他,错误代码 u8 WM8978_Init(void) {

u8 res;

GPIO_InitTypeDef GPIO_InitStructure;

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

10

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC, ENABLE); //使能外设GPIOB,GPIOC时

//PB12/13 复用功能输出

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化

//PC2/PC3/PC6复用功能输出

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3|GPIO_Pin_6;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉 GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化

GPIO_PinAFConfig(GPIOB,GPIO_PinSource12,GPIO_AF_SPI2); //PB12,AF5 I2S_LRCK

GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_SPI2); //PB13,AF5 I2S_SCLK

GPIO_PinAFConfig(GPIOC,GPIO_PinSource3,GPIO_AF_SPI2); I2S_DACDATA

GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_SPI2); I2S_MCK

//PC3 ,AF5

//PC6 ,AF5

GPIO_PinAFConfig(GPIOC,GPIO_PinSource2,GPIO_AF6_SPI2); //PC2 ,AF6 I2S_ADCDATA I2S2ext_SD是AF6!!!

11

IIC_Init();//初始化IIC接口

res=WM8978_Write_Reg(0,0); //软复位WM8978 if(res)return 1; //以下为通用设置

//发送指令失败,WM8978异常

WM8978_Write_Reg(1,0X1B); //R1,MICEN设置为1(MIC使能),BIASEN设置为1(模拟器工作),VMIDSEL[1:0]设置为:11(5K)

WM8978_Write_Reg(2,0X1B0); //R2,ROUT1,LOUT1输出使能(耳机可以工作),BOOSTENR,BOOSTENL使能

WM8978_Write_Reg(3,0X6C); //R3,LOUT2,ROUT2输出使能(喇叭工作),RMIX,LMIX使能

}

//WM8978写寄存器 //reg:寄存器地址 //val:要写入寄存器的值 //返回值:0,成功; // 其他,错误代码

u8 WM8978_Write_Reg(u8 reg,u16 val)

WM8978_Write_Reg(6,0);

//R6,MCLK由外部提供

WM8978_Write_Reg(43,1<<4); //R43,INVROUT2反向,驱动喇叭

WM8978_Write_Reg(47,1<<8); //R47设置,PGABOOSTL,左通道MIC获得20倍增益 WM8978_Write_Reg(48,1<<8); //R48设置,PGABOOSTR,右通道MIC获得20倍增益 WM8978_Write_Reg(49,1<<1); //R49,TSDEN,开启过热保护

WM8978_Write_Reg(10,1<<3); //R10,SOFTMUTE关闭,128x采样,最佳SNR WM8978_Write_Reg(14,1<<3); //R14,ADC 128x采样率 return 0;

12

{

IIC_Start();

IIC_Send_Byte((WM8978_ADDR<<1)|0);//发送器件地址+写命令 if(IIC_Wait_Ack())return 1; //等待应答(成功?/失败?)

IIC_Send_Byte((reg<<1)|((val>>8)&0X01));//写寄存器地址+数据的最高位

if(IIC_Wait_Ack())return 2; //等待应答(成功?/失败?) IIC_Send_Byte(val&0XFF);

//发送数据

if(IIC_Wait_Ack())return 3; //等待应答(成功?/失败?)

IIC_Stop(); }

//WM8978读寄存器

//就是读取本地寄存器值缓冲区内的对应值 //reg:寄存器地址 //返回值:寄存器值

u16 WM8978_Read_Reg(u8 reg) { }

//WM8978 DAC/ADC配置 //adcen:adc使能(1)/关闭(0) //dacen:dac使能(1)/关闭(0)

void WM8978_ADDA_Cfg(u8 dacen,u8 adcen)

13

WM8978_REGVAL_TBL[reg]=val; //保存寄存器值到本地 return 0;

return WM8978_REGVAL_TBL[reg];

{ }

//WM8978 输入通道配置 //micen:MIC开启(1)/关闭(0) //lineinen:Line In开启(1)/关闭(0) //auxen:aux开启(1)/关闭(0)

void WM8978_Input_Cfg(u8 micen,u8 lineinen,u8 auxen) {

u16 regval;

regval=WM8978_Read_Reg(2); //读取R2 if(micen)regval|=3<<2; else regval&=~(3<<2);

//开启INPPGAENR,INPPGAENL(MIC的PGA放大) //关闭INPPGAENR,INPPGAENL.

u16 regval;

regval=WM8978_Read_Reg(3); //读取R3 if(dacen)regval|=3<<0; else regval&=~(3<<0);

//R3最低2个位设置为1,开启DACR&DACL //R3最低2个位清零,关闭DACR&DACL.

WM8978_Write_Reg(3,regval); //设置R3 regval=WM8978_Read_Reg(2); //读取R2 if(adcen)regval|=3<<0; else regval&=~(3<<0);

//R2最低2个位设置为1,开启ADCR&ADCL //R2最低2个位清零,关闭ADCR&ADCL.

WM8978_Write_Reg(2,regval); //设置R2

WM8978_Write_Reg(2,regval); //设置R2

regval=WM8978_Read_Reg(44); //读取R44

14

if(micen)regval|=3<<4|3<<0; //开启LIN2INPPGA,LIP2INPGA,RIN2INPPGA,RIP2INPGA. else regval&=~(3<<4|3<<0); //关闭LIN2INPPGA,LIP2INPGA,RIN2INPPGA,RIP2INPGA.

}

//WM8978 输出配置

//dacen:DAC输出(放音)开启(1)/关闭(0)

//bpsen:Bypass输出(录音,包括MIC,LINE IN,AUX等)开启(1)/关闭(0) void WM8978_Output_Cfg(u8 dacen,u8 bpsen) { }

15

WM8978_Write_Reg(44,regval);//设置R44

if(lineinen)WM8978_LINEIN_Gain(5);//LINE IN 0dB增益 else WM8978_LINEIN_Gain(0); //关闭LINE IN if(auxen)WM8978_AUX_Gain(7);//AUX 6dB增益 else WM8978_AUX_Gain(0);

//关闭AUX输入

u16 regval=0;

if(dacen)regval|=1<<0; //DAC输出使能 if(bpsen) { }

WM8978_Write_Reg(50,regval);//R50设置 WM8978_Write_Reg(51,regval);//R51设置

regval|=1<<1; regval|=5<<2;

//BYPASS使能 //0dB增益

//WM8978 MIC增益设置(不包括BOOST的20dB,MIC-->ADC输入部分的增益) //gain:0~63,对应-12dB~35.25dB,0.75dB/Step void WM8978_MIC_Gain(u8 gain) { }

//WM8978 L2/R2(也就是Line In)增益设置(L2/R2-->ADC输入部分的增益) //gain:0~7,0表示通道禁止,1~7,对应-12dB~6dB,3dB/Step void WM8978_LINEIN_Gain(u8 gain) {

u16 regval; gain&=0X07;

regval=WM8978_Read_Reg(47); //读取R47 regval&=~(7<<4);

//清除原来的设置

gain&=0X3F;

WM8978_Write_Reg(45,gain);

//R45,左通道PGA设置

WM8978_Write_Reg(46,gain|1<<8); //R46,右通道PGA设置

WM8978_Write_Reg(47,regval|gain<<4);//设置R47

regval=WM8978_Read_Reg(48); //读取R48 regval&=~(7<<4);

//清除原来的设置

WM8978_Write_Reg(48,regval|gain<<4);//设置R48 }

//WM8978 AUXR,AUXL(PWM音频部分)增益设置(AUXR/L-->ADC输入部分的增益) //gain:0~7,0表示通道禁止,1~7,对应-12dB~6dB,3dB/Step void WM8978_AUX_Gain(u8 gain)

16

{

u16 regval; gain&=0X07;

regval=WM8978_Read_Reg(47); //读取R47 regval&=~(7<<0);

//清除原来的设置

WM8978_Write_Reg(47,regval|gain<<0);//设置R47

regval=WM8978_Read_Reg(48); //读取R48 regval&=~(7<<0);

//清除原来的设置

WM8978_Write_Reg(48,regval|gain<<0);//设置R48 }

//设置I2S工作模式

//fmt:0,LSB(右对齐);1,MSB(左对齐);2,飞利浦标准I2S;3,PCM/DSP; //len:0,16位;1,20位;2,24位;3,32位; void WM8978_I2S_Cfg(u8 fmt,u8 len) {

fmt&=0X03;

len&=0X03;//限定范围

WM8978_Write_Reg(4,(fmt<<3)|(len<<5)); //R4,WM8978工作模式设置

}

//设置耳机左右声道音量 //voll:左声道音量(0~63) //volr:右声道音量(0~63)

void WM8978_HPvol_Set(u8 voll,u8 volr)

17

{

voll&=0X3F;

volr&=0X3F;//限定范围

if(voll==0)voll|=1<<6;//音量为0时,直接mute if(volr==0)volr|=1<<6;//音量为0时,直接mute WM8978_Write_Reg(52,voll);

//R52,耳机左声道音量设置

WM8978_Write_Reg(53,volr|(1<<8)); //R53,耳机右声道音量设置,同步更新(HPVU=1)

}

//设置喇叭音量

//voll:左声道音量(0~63) void WM8978_SPKvol_Set(u8 volx) {

volx&=0X3F;//限定范围

if(volx==0)volx|=1<<6;//音量为0时,直接mute

//R54,喇叭左声道音量设置

WM8978_Write_Reg(54,volx);

WM8978_Write_Reg(55,volx|(1<<8)); //R55,喇叭右声道音量设置,同步更新(SPKVU=1)

}

//设置3D环绕声

//depth:0~15(3D强度,0最弱,15最强) void WM8978_3D_Set(u8 depth) {

depth&=0XF;//限定范围

WM8978_Write_Reg(41,depth); //R41,3D环绕设置

18

}

//设置EQ/3D作用方向 //dir:0,在ADC起作用 // 1,在DAC起作用(默认) void WM8978_EQ_3D_Dir(u8 dir) {

u16 regval;

regval=WM8978_Read_Reg(0X12); if(dir)regval|=1<<8; else regval&=~(1<<8);

WM8978_Write_Reg(18,regval);//R18,EQ1的第9位控制EQ/3D方向 }

//设置EQ1

//cfreq:截止频率,0~3,分别对应:80/105/135/175Hz //gain:增益,0~24,对应-12~+12dB void WM8978_EQ1_Set(u8 cfreq,u8 gain) {

u16 regval;

cfreq&=0X3;//限定范围 if(gain>24)gain=24; gain=24-gain;

regval=WM8978_Read_Reg(18); regval&=0X100;

regval|=cfreq<<5; //设置截止频率

19

regval|=gain; //设置增益

WM8978_Write_Reg(18,regval);//R18,EQ1设置 }

//设置EQ2

//cfreq:中心频率,0~3,分别对应:230/300/385/500Hz //gain:增益,0~24,对应-12~+12dB void WM8978_EQ2_Set(u8 cfreq,u8 gain) {

u16 regval=0;

cfreq&=0X3;//限定范围 if(gain>24)gain=24; gain=24-gain;

regval|=cfreq<<5; //设置截止频率 regval|=gain;

//设置增益

WM8978_Write_Reg(19,regval);//R19,EQ2设置 }

//设置EQ3

//cfreq:中心频率,0~3,分别对应:650/850/1100/1400Hz //gain:增益,0~24,对应-12~+12dB void WM8978_EQ3_Set(u8 cfreq,u8 gain) {

u16 regval=0;

cfreq&=0X3;//限定范围 if(gain>24)gain=24;

20

gain=24-gain;

regval|=cfreq<<5; //设置截止频率 regval|=gain;

//设置增益

WM8978_Write_Reg(20,regval);//R20,EQ3设置 }

//设置EQ4

//cfreq:中心频率,0~3,分别对应:1800/2400/3200/4100Hz //gain:增益,0~24,对应-12~+12dB void WM8978_EQ4_Set(u8 cfreq,u8 gain) {

u16 regval=0;

cfreq&=0X3;//限定范围 if(gain>24)gain=24; gain=24-gain;

regval|=cfreq<<5; //设置截止频率 regval|=gain;

//设置增益

WM8978_Write_Reg(21,regval);//R21,EQ4设置 }

//设置EQ5

//cfreq:中心频率,0~3,分别对应:5300/6900/9000/11700Hz //gain:增益,0~24,对应-12~+12dB void WM8978_EQ5_Set(u8 cfreq,u8 gain) {

u16 regval=0;

21

cfreq&=0X3;//限定范围 if(gain>24)gain=24; gain=24-gain;

regval|=cfreq<<5; //设置截止频率 regval|=gain;

//设置增益

WM8978_Write_Reg(22,regval);//R22,EQ5设置 }

Main.c代码 #include \#include \#include \#include \#include \#include \#include \#include \#include \#include \#include \#include \#include \#include \ #include \#include \

22

//ALIENTEK 探索者STM32F407开发板 实验43 //音乐播放器 实验 -库函数版本 //技术支持:www.openedv.com

//淘宝店铺:http://eboard.taobao.com //广州市星翼电子科技有限公司 //作者:正点原子 @ALIENTEK int main(void) { delay_init(168);

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2

//初始化延时函数 uart_init(115200); LED_Init();

//初始化串口波特率为115200

//初始化LED

//初始化USMART //LCD初始化 //按键初始化 //初始化W25Q128 //初始化WM8978 //耳机音量设置 //喇叭音量设置 //初始化内部内存池 //初始化CCM内存池

//为fatfs相关变量申请内存 //挂载SD卡

23

usmart_dev.init(84);

LCD_Init(); KEY_Init();

W25QXX_Init(); WM8978_Init();

WM8978_HPvol_Set(40,40); WM8978_SPKvol_Set(60); my_mem_init(SRAMIN);

my_mem_init(SRAMCCM); exfuns_init();

f_mount(fs[0],\

POINT_COLOR=BLACK; while(font_init()) {

LCD_ShowString(30,50,200,16,16,\delay_ms(200);

//检查字库

LCD_Fill(30,50,240,66,WHITE);//清除显示 delay_ms(200);

}

POINT_COLOR=BLACK;

Show_Str(60,50,200,16,\开发板\ }

Show_Str(60,70,200,16,\

Show_Str(60,90,200,16,\实验者:陈斌,蒋文娟\

Show_Str(60,130,200,16,\下一首 KEY2:上一首\Show_Str(60,150,200,16,\while(1) { }

audio_play();

24

3.5下载验证

四、心得体会

通过本次实验,主要学习了 WAV 格式音频播放原理、数字音频接口及 IIS 总线接 口的相关知识。了解了模拟音频信号和数字音频信号的概念。掌握了编写音频播放器的方法并且实现了音频播放。本系统能够支持触摸屏操作,显示歌词等功能。

此次的课程设计使我掌握那些自己不熟悉的硬件相关的知识点,做一个课程设计还需要注意很多方面,无论是格式,还是书写的内容和要表达的思想都得严格要求自己,所以做起来真的不算容易。但在解决问题的过程中会有成就感,也会看到自己的不足,在以后的学习中,我会不断的完善自我,不断进取,能使自己在编程这方面有一个大的发展。

25