1 前言
本章介绍使用i2s示例驱动max97357播放音乐。
2 前期准备
需要将wav文件中获取音频采样数据,参见文章:【python】将wav文件转为c 获取到音源的数组,注意音频位深,本文使用16bit的音源,最终生成uint16_t的数组。
PS:由于内存有限,所以只能保存1s左右的音源,后续再看看通过spi加载sd卡能不能实现整段音频数据读取。
测试时注意i2s三个信号上拉电阻,我是上拉10k电阻到3.3v。
4 代码
main.c
- #include
- #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
- };
- // dma buffer num bigger, noise obvious
- #define DMA_BUFF_TX_MAX 16 //16 is a suitable value
- 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);
- /* add*/
- uint32_t wav_point=0; // dma get voice sample num
- uint32_t num = 0; // i2s send num
- #define TEST_NUM 1
- // 1. sinwav; 2. music; 3. dma tx
- /**
- * [url=home.php?mod=space&uid=2666770]@Brief[/url] Main function.
- */
- int main(void)
- {
- /*!< At this stage the microcontroller clock setting is already configured,
- this is done through SystemInit() function which is called from startup
- file (startup_cm32m4xxr.s) before to branch to application main.
- To reconfigure the default setting of SystemInit() function, refer to
- system_cm32m4xxr.c file
- */
- log_init();
- log_info("I2S_DMA test startrn");
- // init dma tx buffer
- memset(dma_buff_tx,0x0000,DMA_BUFF_TX_MAX);
- memcpy(dma_buff_tx,music2,DMA_BUFF_TX_MAX);
- /* System clocks configuration ---------------------------------------------*/
- RCC_Configuration();
- /* GPIO configuration ------------------------------------------------------*/
- GPIO_Configuration();
- /* Deinitializes the SPI2 and SPI3 peripheral registers --------------------*/
- SPI_I2S_DeInit(I2S_SLAVE);
- SPI_I2S_DeInit(I2S_MASTER);
- /* I2S_SLAVE_Rx_DMA_Channel configuration ---------------------------------------------*/
- 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);
- /* I2S_MASTER_Tx_DMA_Channel configuration ---------------------------------------------*/
- 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 peripheral configuration */
- I2S_InitStructure.Standard = I2S_STD_PHILLIPS;
- I2S_InitStructure.DataFormat = I2S_DATA_FMT_16BITS;//_EXTENDED;
- I2S_InitStructure.MCLKEnable = I2S_MCLK_DISABLE;
- I2S_InitStructure.AudioFrequency = I2S_AUDIO_FREQ_48K;
- // I2S_InitStructure.AudioFrequency = I2S_AUDIO_FREQ_48K*16*2;
- I2S_InitStructure.CLKPOL = I2S_CLKPOL_LOW;
- /* I2S3 Master Transmitter to I2S2 Slave Receiver communication ------------*/
- /* I2S3 configuration */
- I2S_InitStructure.I2sMode = I2S_MODE_MASTER_TX;
- I2S_Init(I2S_MASTER, &I2S_InitStructure);
- /* I2S2 configuration */
- I2S_InitStructure.I2sMode = I2S_MODE_SlAVE_RX;
- I2S_Init(I2S_SLAVE, &I2S_InitStructure);
- /* Enable I2S_SLAVE Rx request */
- SPI_I2S_EnableDma(I2S_SLAVE, SPI_I2S_DMA_RX, ENABLE);
- /* Enable I2S_MASTER Tx request */
- SPI_I2S_EnableDma(I2S_MASTER, SPI_I2S_DMA_TX, ENABLE);
- /* Enable the I2S2 */
- I2S_Enable(I2S_SLAVE, ENABLE);
- /* Enable the I2S3 */
- I2S_Enable(I2S_MASTER, ENABLE);
- /* Enable DMA2 Channel2 */
- DMA_EnableChannel(I2S_MASTER_Tx_DMA_Channel, ENABLE);
- /* Enable DMA1 Channel4 */
- DMA_EnableChannel(I2S_SLAVE_Rx_DMA_Channel, ENABLE);
- // test start
- while(1)
- {
- switch_test();
- }
- printf("end send wav data to speakerrn");
- // printf("start receive wav data from micrn");
- // while (!DMA_GetFlagStatus(I2S_SLAVE_Rx_DMA_FLAG, DMA1))
- // ;
- while (1)
- {
- }
- }
- /**
- * @brief Configures the different system clocks.
- */
- void RCC_Configuration(void)
- {
- /* Enable peripheral clocks --------------------------------------------------*/
- /* Enable I2S_SLAVE DMA clock */
- RCC_EnableAHBPeriphClk(I2S_SLAVE_DMA_CLK, ENABLE);
- RCC_EnableAHBPeriphClk(I2S_MASTER_DMA_CLK, ENABLE);
- /* GPIOB, GPIOC, GPIOD and AFIO clocks enable */
- RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB | RCC_APB2_PERIPH_GPIOC | RCC_APB2_PERIPH_GPIOD | RCC_APB2_PERIPH_AFIO, ENABLE);
- /* SPI2 and SPI3 clocks enable */
- RCC_EnableAPB1PeriphClk(I2S_SLAVE_CLK | I2S_MASTER_CLK, ENABLE);
- }
- /**
- * @brief Configures the different GPIO ports.
- */
- void GPIO_Configuration(void)
- {
- GPIO_InitType GPIO_InitStructure;
- /* Disable the JTAG interface and enable the SWJ interface
- This operation is not necessary for Connectivity Line devices since
- SPI3 I/Os can be remapped on other GPIO pins */
- // GPIO_ConfigPinRemap(GPIO_RMP_SW_JTAG_SW_ENABLE, ENABLE);
- GPIO_ConfigPinRemap(GPIO_RMP1_SPI3, ENABLE);
- /* Configure SPI2 pins: WS, CK and SD ---------------------------------*/
- 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);
- /* Configure SPI3 pins: CK and SD ------------------------------------*/
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_InitStructure.Pin = I2S_MASTER_PIN_CK | I2S_MASTER_PIN_SD;
- GPIO_Init(GPIOC, &GPIO_InitStructure);
- /* Configure SPI3 pins: WS -------------------------------------------*/
- GPIO_InitStructure.Pin = I2S_MASTER_PIN_WS;
- GPIO_Init(GPIOD, &GPIO_InitStructure);
- }
- /**
- * @brief Compares two buffers.
- * [url=home.php?mod=space&uid=3142012]@param[/url] pBuffer1, pBuffer2: buffers to be compared.
- * @param BufferLength buffer's length
- * [url=home.php?mod=space&uid=1141835]@Return[/url] PASSED: pBuffer1 identical to pBuffer2
- * FAILED: pBuffer1 differs from pBuffer2
- */
- Status Buffercmp(uint16_t* pBuffer1, uint16_t* pBuffer2, uint16_t BufferLength)
- {
- while (BufferLength--)
- {
- if (*pBuffer1 != *pBuffer2)
- {
- return FAILED;
- }
- pBuffer1++;
- pBuffer2++;
- }
- return PASSED;
- }
- /**
- * @brief Inserts a delay time.
- * @param nCount specifies the delay time length.
- */
- void Delay(__IO uint32_t nCount)
- {
- /* Decrement nCount value */
- for (; nCount != 0; nCount--)
- ;
- }
- /* ---------------------- add ------------------------*/
- void print_uart_wait()
- {
- if(status_wait_uart == true)
- {
- status_wait_uart = false;
- printf("i2s transfer waiting......rn");
- }
- }
- void print_uart_wait_end()
- {
- status_wait_uart = true;
- printf("end i2s wait.rn");
- }
- 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) = %drn",sizeof(data));
- bool str_data = len_set>=len_data;
- // if send overside strlen, just send strlen
- uint16_t len = str_data ? len_data:len_set;
- printf("strlen = %d, send len = %d, send>strlen = %s, len = %drn",len_data,len_set,
- str_data?"true":"false",len);
- if(str_data == true)
- {
- printf("send data len overside str_len.rn");
- }
- else
- {
- printf("send data len = %d.rn",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]rn",temp_len,(char * )data);
- // choose i2s
- 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 uart5rn");
- i2s = SPI2;
- break;
- }
- uint16_t i=0;
- while(temp_len > i)
- {
- // printf("i = %d, value = %04xrn",i,*temp_value);
- /* Wait the Tx buffer to be empty */
- // while (SPI_I2S_GetStatus(i2s, flag) == RESET)
- // {
- // print_uart_wait();
- // }
- // print_uart_wait_end();
- /* Send a data from SPI3 */
- SPI_I2S_TransmitData(i2s, *temp_value);
- printf("i = %dn",i);
- i++;
- temp_value++;
- }
- return;
- }
- void reset_dma_tx()
- {
- // print_dma();
- DMA_EnableChannel(I2S_MASTER_Tx_DMA_Channel, DISABLE);
- // printf("has send i2s3 txrn");
- 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;
- // printf("wav_point=%drn",wav_point);
- // printf("dma_buff_tx = %04xrn",dma_buff_tx[0]);
- DMA_SetCurrDataCounter(I2S_MASTER_Tx_DMA_Channel,DMA_BUFF_TX_MAX);
- // printf("dma count = %drn",DMA_GetCurrDataCounter(I2S_MASTER_Tx_DMA_Channel));
- 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()
- {
- /* test 1: sinwav*/
- // printf("test1_sinwavrn");
- SPI_I2S_TransmitData(I2S_MASTER, sin_wav[num]);
- num++;
- if(num == 32)
- num = 0;
- // printf("data = %04xrn",I2S_MASTER_Buffer_Tx_t[num]);
- }
- void test2_music()
- {
- /* test 2: music1 (I2S SEND)*/
- // printf("test2_musicrn");
- SPI_I2S_TransmitData(I2S_MASTER, music2[num]);
- num++;
- if(num == MUSIC_VALUE_MAX)
- num = 0;
- // Delay(500);
- // printf("data[%d] = %04xrn",num,music1[num]);
- // printf("data[%d]rn",num);
- /* Wait for I2S TX buffer empty */
- while (SPI_I2S_GetStatus(I2S_MASTER, SPI_I2S_TE_FLAG) == RESET)
- ;
- }
- void test3_dma_tx()
- {
- /* test 3: music1 (DMA SEND)*/
- // printf("test3_dma_txrn");
- /* Wait for DMA2 channel2 transfer complete */
- 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读卡是否可以支持完整播放。 播放结果来看,好像是加速了,不清楚哪里设置异常。
|