【中移芯昇】4. i2s播放音乐片段

1 前言

本章介绍使用i2s示例驱动max97357播放音乐。

2 前期准备

需要将wav文件中获取音频采样数据,参见文章:【python】将wav文件转为c

获取到音源的数组,注意音频位深,本文使用16bit的音源,最终生成uint16_t的数组。

PS:由于内存有限,所以只能保存1s左右的音源,后续再看看通过spi加载sd卡能不能实现整段音频数据读取。

3 电路

测试时注意i2s三个信号上拉电阻,我是上拉10k电阻到3.3v。

请添加图片描述
在这里插入图片描述

4 代码

main.c

/*******************************************************************************
*
* COPYRIGHT(c) 2020, China Mobile IOT
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*	1. Redistributions of source code must retain the above copyright notice,
*	   this list of conditions and the following disclaimer.
*	2. Redistributions in binary form must reproduce the above copyright notice,
*	   this list of conditions and the following disclaimer in the documentation
*	   and/or other materials provided with the distribution.
*	3. Neither the name of China Mobile IOT nor the names of its contributors
*	   may be used to endorse or promote products derived from this software
*	   without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*******************************************************************************/

/**
 * @file main.c
 * @author CMIOT Firmware Team
 * @version v1.0.0
 *
 * @copyright Copyright (c) 2020, CMIOT. All rights reserved.
 */

#include <main.h>
#include "log.h"
//#include "voice.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

/**
 * @brief  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 start\r\n");

	// 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 speaker\r\n");
//    printf("start receive wav data from mic\r\n");
//    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.
 * @param  pBuffer1, pBuffer2: buffers to be compared.
 * @param BufferLength buffer's length
 * @return 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......\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;
    // 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 = %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);

    // 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 uart5\r\n");
            i2s = SPI2;
            break;
    }

    uint16_t i=0;
    while(temp_len > i)
    {
//        printf("i = %d, value = %04x\r\n",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 = %d\n",i);

        i++;
        temp_value++;
    }
    return;
}

void reset_dma_tx()
{
//    print_dma();
	DMA_EnableChannel(I2S_MASTER_Tx_DMA_Channel, DISABLE);

//	printf("has send i2s3 tx\r\n");
	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=%d\r\n",wav_point);
//	printf("dma_buff_tx = %04x\r\n",dma_buff_tx[0]);

    DMA_SetCurrDataCounter(I2S_MASTER_Tx_DMA_Channel,DMA_BUFF_TX_MAX);
//	printf("dma count = %d\r\n",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_sinwav\r\n");
	SPI_I2S_TransmitData(I2S_MASTER, sin_wav[num]);
	num++;
	if(num == 32)
		num = 0;
//	printf("data = %04x\r\n",I2S_MASTER_Buffer_Tx_t[num]);
}

void test2_music()
{
	/* test 2: music1 (I2S SEND)*/
//	printf("test2_music\r\n");
	SPI_I2S_TransmitData(I2S_MASTER,  music2[num]);
	num++;
	if(num == MUSIC_VALUE_MAX)
		num = 0;
//    	Delay(500);
//    	printf("data[%d] = %04x\r\n",num,music1[num]);
//    	printf("data[%d]\r\n",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_tx\r\n");

    /* 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 后续

  1. 由于内存有限,音源wav只能设置1s左右,后续看看spi读卡是否可以支持完整播放。
  2. 播放结果来看,好像是加速了,不清楚哪里设置异常。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值