程序设计
根据接口电路配置GPIO初始化
sta
tic void I2S3_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// I2S MCLK, SD, CK and WS pins configuration
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1ENR_SPI3, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_6); //I2S WS
GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_6); //I2S CK I2S_SCK
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_6); //I2S SD I2S_DATAOUT MOSI
GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_5); //I2S MCK I2S_MCLK
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// config as the control I/O for power on or enter standby
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
I2S配置初始化 static void I2S_Mode_Config(SPI_I2S_STANDARD_TypeDef usStandard, SPI_I2S_DATAFORMAT_TypeDef usWordLen, SPI_I2S_AUDIO_FREQ_TypeDef usAudioFreq, SPI_I2S_TRANS_MODE_TypeDef usMode)
{
I2S_InitTypeDef I2S_InitStructure;
if ((usMode == I2S_Mode_SlaveTx) && (usMode == I2S_Mode_SlaveRx)) {
return;
}
RCC_APB1PeriphClockCmd(RCC_APB1ENR_SPI3, ENABLE);
SPI_DeInit(SPI3);
RCC_APB1PeriphClockCmd(RCC_APB1ENR_SPI3, ENABLE);
if (usMode == I2S_Mode_MasterTx) {
I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx;
I2S_InitStructure.I2S_Standard = usStandard;
I2S_InitStructure.I2S_DataFormat = usWordLen;
I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable;
I2S_InitStructure.I2S_AudioFreq = usAudioFreq;
I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
I2S_Init(SPI3, &I2S_InitStructure);
}
else if (usMode == I2S_Mode_MasterRx) {
I2S_InitStructure.I2S_Mode = I2S_Mode_MasterRx;
I2S_InitStructure.I2S_Standard = usStandard;
I2S_InitStructure.I2S_DataFormat = usWordLen;
I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable;
I2S_InitStructure.I2S_AudioFreq = usAudioFreq;
I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
I2S_Init(SPI3, &I2S_InitStructure);
}
SPI_DMACmd(SPI3, ENABLE);
I2S_Cmd(SPI3, ENABLE);
}
(1)I2S_Mode:I2S 模式选择,可选主机发送、主机接收、从机发送以及从机接收模式,它设定SPI_I2S_GCTL寄存器MODE位的值。一般设置 MM32 控制器为主机模式,当播放声音时选择发送模式,当录制声音时选择接收模式。
(2) I2S_Standard:通信标准格式选择,可选 I2S Philips 标准、左对齐标准、右对齐标准、 PCM 短帧标准或 PCM 长帧标准,它设定SPI_I2S_I2SCFGR 寄存器 I2SSTD位和 PCMSYNC位的值。一般设置为 I2S Philips 标准即可。
(3)I2S_DataFormat:数据格式选择,设定有效数据长度和帧长度,可选标准 16bit 格式、扩展16bit(32bit 帧长度) 格式、 24bit 格式和 32bit 格式,它设定 SPI_I2SCFGR 寄存器 DATLEN 位和CHLEN 位的值。对应 16bit 数据长度可选 16bit 或 32bit 帧长度,其他都是 32bit 帧长度。
(4)I2S_MCLKOutput:主时钟输出使能控制,可选使能输出或禁止输出,它设定 SPI_I2SPR 寄存器 MCKOE 位的值。为提高系统性能一般使能主时钟输出。
(5)I2S_AudioFreq:采样频率设置,标准库提供采样采样频率选择,分别为 4KHz、8kHz、 11kHz、12KHz、16kHz、22kHz、32kHz、44kHz、48kHz、96kHz、192kHz 以及默认 2Hz,它设定 SPI_I2S_SPBRG 寄存器的值。
(6)I2S_CPOL:空闲状态的 CK 线电平,可选高电平或低电平,它设定 SPI_I2S_CCTL 寄存器 CPOL位的值。一般设置为低电平即可。
在I2S_StartPlay()函数中调用I2S_Mode_Config()函数, void I2S_StartPlay(SPI_I2S_STANDARD_TypeDef usStandard, SPI_I2S_DATAFORMAT_TypeDef usWordLen, SPI_I2S_AUDIO_FREQ_TypeDef usAudioFreq)
{
// config I2S interface as standard, bit length, frequence ,the Master Tx mode
I2S_Mode_Config(usStandard, usWordLen, usAudioFreq, I2S_Mode_MasterTx);
SPI3->GCTL |= 0xF;
}
在PlayMP3FileDemo()函数中调用I2S_StartPlay()函数,并配置传输模式为主机发送I2S_Mode_MasterTx,选择Phillips标准,16位数据长度,采样频率配置为44KHz。 I2S_StartPlay(I2S_Standard_Phillips, I2S_DataFormat_16b, I2S_AudioFreq_44k);
PlayMP3File()函数是 MP3 播放器的实现函数,定义如下: void PlayMP3File(void)
{
DIR dirs;
FILINFO finfo;
FRESULT res;
static UINT br;
DELAY_Init();
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | RCC_AHBPeriph_GPIOC | RCC_AHBPeriph_GPIOD | RCC_AHBPeriph_GPIOE, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBENR_SDIO, ENABLE);
CONSOLE_Init(115200);
SDIO_ConfigInit();
printf("SDCARD TEST\r\n");
while(SD_Init()) {
printf("SD Card Error!\r\n");
}
MX_FATFS_Init();
f_mount(&SDFatFS, (TCHAR const*)SDPath, 0);
u8 buff[2] = {0x01, 0x02};
I2S_TX_DMA_Init(&buff[0], 1);
DMA_Cmd(DMA2_Channel2, ENABLE);
// while(1){
if (f_opendir(&dirs, "") == FR_OK) { //success to open directory
while (f_readdir(&dirs, &finfo) == FR_OK) { //if there is file in this directory
if (finfo.fattrib & AM_ARC) {
if(!finfo.fname[0])
break;
printf("\r\n Now Playing:[");
printf(finfo.fname);
printf("]\r\n");
res = f_open(&fsrc, finfo.fname, FA_OPEN_EXISTING | FA_READ);
SET_BIT(SPI3->GCR, SPI_GCR_SPIEN);
MODIFY_REG(DMA2_Channel2->CCR, DMA_CCR_EN, ENABLE << DMA_CCR_EN_Pos);
hMP3Decoder = MP3InitDecoder();
readPtr = readBuf;
res = f_read(&fsrc, readBuf, READBUF_SIZE, &br);
bytesLeft += br;
buffer_switch = 0;
while(1) {
offset = MP3FindSyncWord(readPtr, bytesLeft); //assume EOF if no sync found
if(offset < 0)break;
readPtr += offset; //data start point
bytesLeft -= offset; //in buffer
if(bytesLeft < READBUF_SIZE) {
memmove(readBuf, readPtr, bytesLeft);
res = f_read(&fsrc, readBuf + bytesLeft, READBUF_SIZE - bytesLeft, &br);
if((res) || (br == 0)) break;
if(br < READBUF_SIZE - bytesLeft)
memset(readBuf + bytesLeft + br, 0, READBUF_SIZE - bytesLeft - br);
bytesLeft = READBUF_SIZE;
readPtr = readBuf;
}
MP3GetLastFrameInfo(hMP3Decoder, &mp3FrameInfo);
if((samprate != mp3FrameInfo.samprate) && (mp3FrameInfo.samprate != 0)) {
// wm8978_CfgAudioIF(I2S_Standard_Phillips, mp3FrameInfo.bitsPerSample, SPI_Mode_Master);
// I2S_StartPlay((SPI_I2S_STANDARD_TypeDef)I2S_Standard_Phillips, (SPI_I2S_DATAFORMAT_TypeDef)mp3FrameInfo.bitsPerSample, (SPI_I2S_AUDIO_FREQ_TypeDef)mp3FrameInfo.samprate);
samprate = (SPI_I2S_AUDIO_FREQ_TypeDef) mp3FrameInfo.samprate;
}
while(1) {
if(DMA_GetITStatus(DMA2_IT_TC2) == SET) {
DMA_ClearITPendingBit(DMA2_IT_TC2);
if(buffer_switch == 0) {
Audio_MAL_Play((u32)buffer4, BUFF_SIZE);
MP3Decode(hMP3Decoder, &readPtr, &bytesLeft, buffer3, 0);
buffer_switch = 1;
break;
}
else {
Audio_MAL_Play((u32)buffer3, BUFF_SIZE);
MP3Decode(hMP3Decoder, &readPtr, &bytesLeft, buffer4, 0);
buffer_switch = 0;
break;
}
}
}
}
CLEAR_BIT(SPI3->GCR, SPI_GCR_SPIEN);
MODIFY_REG(DMA2_Channel2->CCR, DMA_CCR_EN, DISABLE << DMA_CCR_EN_Pos);
f_close(&fsrc);
bytesLeft = 0;
}
}
}
while(1);
// }
}
MP3文件是经过压缩算法压缩而存在的,为得到 PCM 信号,需要对 MP3 文件进行解码。本实验使用Helix MP3解码器,Helix MP3 解码器的源代码是开源代码,受制于源代码随附文件中描述的许可协议。该算法支持浮点和定点实现,可移植到任意32位定点处理器上运行,提供对 MPEG-1、 MPEG-2 以及 MPEG-2.5 标准的 Layer3 解码,以及支持可变位速率、恒定位速率,以及立体声和单声道音频格式。关于Helix MP3解码器的移植,在本文中不做重点讲述,更多信息可访问网站:
https://datatype.helixcommunity.org/Mp3dec
f_open 函数用于打开文件,如果文件打开失败则直接退出播放。
MP3InitDecoder 函数用于初始化Helix 解码器,分配解码器必须内存空间,如果初始化解码器失败直接退出播放。
f_read 函数从 SD 卡读取 MP3 文件数据,存放在 readBuf缓冲区中, br变量保存实际读取到的数据的字节数。如果读取数据失败则运行 MP3FreeDecoder 函数关闭解码器后退出播放器。
MP3Decode 函数开始对源数据缓冲区中帧数据进行解码,通过函数返回值可判断得到解码状态,如果发生解码错误则执行对应的代码。
读取到文件末尾就退出循环, 此时MP3文件已经完整播放。
实验演示
SD卡中存储有MP3文件,并将SD卡、耳机设备接入MB-039
开发板,运行程序,就可以听到音乐播放。
本次实验的例程可以通过MindMotion的官网下载MM32F3270 lib_Samples:
https://www.mindmotion.com.cn/pr ... instream/mm32f3270/
工程路径如下:
~MM32F327x_Samples\Demo_app\PlayWave_Demo\SPI_I2S_SDIO_FatFs\MP3_CS4344_Demo
可以看到详细的样例与功能操作。
0