1 前言
本章介绍使用i2s示例驱动max97357播放音乐。
2 前期准备
需要将wav文件中获取音频采样数据,参见文章:【python】将wav文件转为c
获取到音源的数组,注意音频位深,本文使用16bit的音源,最终生成uint16_t的数组。
PS:由于内存有限,所以只能保存1s左右的音源,后续再看看通过spi加载sd卡能不能实现整段音频数据读取。
3 电路
测试时注意i2s三个信号上拉电阻,我是上拉10k电阻到3.3v。
4 代码
main.c
#include <main.h>
#include "log.h"
#include "music2.h"
#define BufferSize (32)
DMA_InitType DMA_InitStructure;
GPIO_InitType GPIO_InitStructure;
I2S_InitType I2S_InitStructure;
bool status_wait_uart = false;
const uint16_t I2S_MASTER_Buffer_Tx[BufferSize] = {0x0102, 0x0304, 0x0506, 0x0708, 0x090A, 0x0B0C, 0x0D0E, 0x0F10,
0x1112, 0x1314, 0x1516, 0x1718, 0x191A, 0x1B1C, 0x1D1E, 0x1F20,
0x2122, 0x2324, 0x2526, 0x2728, 0x292A, 0x2B2C, 0x2D2E, 0x2F30,
0x3132, 0x3334, 0x3536, 0x3738, 0x393A, 0x3B3C, 0x3D3E, 0x3F40};
const uint16_t sin_wav[BufferSize] = {
0x0000, 0x1869, 0x2fec, 0x45ae,
0x5a78, 0x6a13, 0x75ca, 0x7d2e,
0x7ffe, 0x7daa, 0x76b9, 0x6b6c,
0x5a9d, 0x47b6, 0x322c, 0x1acc,
0x0034, 0xe7cb, 0xd044, 0xba7e,
0xa5ad, 0x960a, 0x8a4a, 0x82dd,
0x8002, 0x824c, 0x8934, 0x9478,
0xa53e, 0xb81f, 0xcda4, 0xe501
};
#define DMA_BUFF_TX_MAX 16
uint16_t dma_buff_tx[DMA_BUFF_TX_MAX]={};
uint16_t I2S_SLAVE_Buffer_Rx[BufferSize];
volatile Status TransferStatus = FAILED;
ErrorStatus HSEStartUpStatus;
void RCC_Configuration(void);
void GPIO_Configuration(void);
Status Buffercmp(uint16_t* pBuffer1, uint16_t* pBuffer2, uint16_t BufferLength);
void Delay(__IO uint32_t nCount);
uint32_t wav_point=0;
uint32_t num = 0;
#define TEST_NUM 1
int main(void)
{
log_init();
log_info("I2S_DMA test start\r\n");
memset(dma_buff_tx,0x0000,DMA_BUFF_TX_MAX);
memcpy(dma_buff_tx,music2,DMA_BUFF_TX_MAX);
RCC_Configuration();
GPIO_Configuration();
SPI_I2S_DeInit(I2S_SLAVE);
SPI_I2S_DeInit(I2S_MASTER);
DMA_DeInit(I2S_SLAVE_Rx_DMA_Channel);
DMA_InitStructure.PeriphAddr = (uint32_t)I2S_SLAVE_DR_Base;
DMA_InitStructure.MemAddr = (uint32_t)I2S_SLAVE_Buffer_Rx;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
DMA_InitStructure.BufSize = BufferSize;
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE;
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_HALFWORD;
DMA_InitStructure.MemDataSize = DMA_MEMORY_DATA_SIZE_HALFWORD;
DMA_InitStructure.CircularMode = DMA_MODE_NORMAL;
DMA_InitStructure.Priority = DMA_PRIORITY_VERY_HIGH;
DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE;
DMA_Init(I2S_SLAVE_Rx_DMA_Channel, &DMA_InitStructure);
DMA_DeInit(I2S_MASTER_Tx_DMA_Channel);
DMA_InitStructure.PeriphAddr = (uint32_t)I2S_MASTER_DR_Base;
DMA_InitStructure.MemAddr = (uint32_t)dma_buff_tx;
DMA_InitStructure.BufSize = DMA_BUFF_TX_MAX;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_DST;
DMA_Init(I2S_MASTER_Tx_DMA_Channel, &DMA_InitStructure);
I2S_InitStructure.Standard = I2S_STD_PHILLIPS;
I2S_InitStructure.DataFormat = I2S_DATA_FMT_16BITS;
I2S_InitStructure.MCLKEnable = I2S_MCLK_DISABLE;
I2S_InitStructure.AudioFrequency = I2S_AUDIO_FREQ_48K;
I2S_InitStructure.CLKPOL = I2S_CLKPOL_LOW;
I2S_InitStructure.I2sMode = I2S_MODE_MASTER_TX;
I2S_Init(I2S_MASTER, &I2S_InitStructure);
I2S_InitStructure.I2sMode = I2S_MODE_SlAVE_RX;
I2S_Init(I2S_SLAVE, &I2S_InitStructure);
SPI_I2S_EnableDma(I2S_SLAVE, SPI_I2S_DMA_RX, ENABLE);
SPI_I2S_EnableDma(I2S_MASTER, SPI_I2S_DMA_TX, ENABLE);
I2S_Enable(I2S_SLAVE, ENABLE);
I2S_Enable(I2S_MASTER, ENABLE);
DMA_EnableChannel(I2S_MASTER_Tx_DMA_Channel, ENABLE);
DMA_EnableChannel(I2S_SLAVE_Rx_DMA_Channel, ENABLE);
while(1)
{
switch_test();
}
printf("end send wav data to speaker\r\n");
while (1)
{
}
}
void RCC_Configuration(void)
{
RCC_EnableAHBPeriphClk(I2S_SLAVE_DMA_CLK, ENABLE);
RCC_EnableAHBPeriphClk(I2S_MASTER_DMA_CLK, ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB | RCC_APB2_PERIPH_GPIOC | RCC_APB2_PERIPH_GPIOD | RCC_APB2_PERIPH_AFIO, ENABLE);
RCC_EnableAPB1PeriphClk(I2S_SLAVE_CLK | I2S_MASTER_CLK, ENABLE);
}
void GPIO_Configuration(void)
{
GPIO_InitType GPIO_InitStructure;
GPIO_ConfigPinRemap(GPIO_RMP1_SPI3, ENABLE);
GPIO_InitStructure.Pin = I2S_SLAVE_PIN_WS | I2S_SLAVE_PIN_CK | I2S_SLAVE_PIN_SD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.Pin = I2S_MASTER_PIN_CK | I2S_MASTER_PIN_SD;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.Pin = I2S_MASTER_PIN_WS;
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
Status Buffercmp(uint16_t* pBuffer1, uint16_t* pBuffer2, uint16_t BufferLength)
{
while (BufferLength--)
{
if (*pBuffer1 != *pBuffer2)
{
return FAILED;
}
pBuffer1++;
pBuffer2++;
}
return PASSED;
}
void Delay(__IO uint32_t nCount)
{
for (; nCount != 0; nCount--)
;
}
void print_uart_wait()
{
if(status_wait_uart == true)
{
status_wait_uart = false;
printf("i2s transfer waiting......\r\n");
}
}
void print_uart_wait_end()
{
status_wait_uart = true;
printf("end i2s wait.\r\n");
}
void myI2s_tx(uint8_t i2s_id, uint16_t * data, uint16_t len_set)
{
uint16_t len_data = sizeof(data)/sizeof(uint16_t);
printf("sizeof(data) = %d\r\n",sizeof(data));
bool str_data = len_set>=len_data;
uint16_t len = str_data ? len_data:len_set;
printf("strlen = %d, send len = %d, send>strlen = %s, len = %d\r\n",len_data,len_set,
str_data?"true":"false",len);
if(str_data == true)
{
printf("send data len overside str_len.\r\n");
}
else
{
printf("send data len = %d.\r\n",len_set);
}
myI2s_senddata(i2s_id,data,len);
}
void myI2s_senddata(uint8_t i2s_id, uint16_t* data, uint16_t len_set)
{
uint16_t * temp_value = data;
uint16_t temp_len = len_set;
printf("len_set = %d, strvalue = [%s]\r\n",temp_len,(char * )data);
SPI_Module* i2s = SPI2;
uint16_t flag = SPI_I2S_TE_FLAG;
switch(i2s_id)
{
case 2:
i2s = SPI2;
break;
case 3:
i2s = SPI3;
break;
default:
printf("use default uart5\r\n");
i2s = SPI2;
break;
}
uint16_t i=0;
while(temp_len > i)
{
SPI_I2S_TransmitData(i2s, *temp_value);
printf("i = %d\n",i);
i++;
temp_value++;
}
return;
}
void reset_dma_tx()
{
DMA_EnableChannel(I2S_MASTER_Tx_DMA_Channel, DISABLE);
memset(dma_buff_tx,0x0000,DMA_BUFF_TX_MAX);
memcpy(dma_buff_tx,music2 + wav_point,DMA_BUFF_TX_MAX);
wav_point+=DMA_BUFF_TX_MAX;
if(wav_point >= MUSIC_VALUE_MAX)
wav_point=0;
DMA_SetCurrDataCounter(I2S_MASTER_Tx_DMA_Channel,DMA_BUFF_TX_MAX);
DMA_ClearFlag(I2S_MASTER_Tx_DMA_FLAG, DMA2);
DMA_EnableChannel(I2S_MASTER_Tx_DMA_Channel, ENABLE);
}
void switch_test()
{
switch(TEST_NUM)
{
case 1:
test1_sinwav();
break;
case 2:
test2_music();
break;
case 3:
test3_dma_tx();
break;
default:
break;
}
}
void test1_sinwav()
{
SPI_I2S_TransmitData(I2S_MASTER, sin_wav[num]);
num++;
if(num == 32)
num = 0;
}
void test2_music()
{
SPI_I2S_TransmitData(I2S_MASTER, music2[num]);
num++;
if(num == MUSIC_VALUE_MAX)
num = 0;
while (SPI_I2S_GetStatus(I2S_MASTER, SPI_I2S_TE_FLAG) == RESET)
;
}
void test3_dma_tx()
{
while (!DMA_GetFlagStatus(I2S_MASTER_Tx_DMA_FLAG, DMA2))
;
if(DMA_GetFlagStatus(I2S_MASTER_Tx_DMA_FLAG, DMA2) == SET)
reset_dma_tx();
}
music2.h
#define MUSIC_VALUE_MAX 77715
const uint16_t music2[] = {
0x06AB,0x052B,0x03E6,0x0353,0x0631,0x0966,0x0874,0x0C2A,
0x1615,0x16D6,0x1360,0x18C3,0x1D52,0x18C8,0x11B8,0x0ED2,
0x0E65,0x0E30,0x1073,0x14AA,0x1767,0x1CE3,0x284B,0x30F5,
0x31D9,0x2FBF,0x317E,0x3488,0x31AC,0x2A56,0x22A7,0x22AB,
};
5 结果
代码中有test_num,1为播放正弦波,2为音乐,3为通过dma播放。 1,2都是调用SPI_I2S_TransmitData实现播放,3是通过写入dma数组播放。
注意:播放代码中不要添加printf函数,否则会无法出声。
6 后续
- 由于内存有限,音源wav只能设置1s左右,后续看看spi读卡是否可以支持完整播放。
- 播放结果来看,好像是加速了,不清楚哪里设置异常。
|