打印
[研电赛技术支持]

gd32开发板I2S音频功能分析

[复制链接]
495|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2023-7-19 13:06 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
开发板的基本信息:gd32f450zkt6;使用的音频D/A转换器芯片:CS4344。

查看CS4344芯片的数据手册,该音频转换芯片引脚介绍如下:



查看mcu的数据手册,使用I2S1接口,具体使用的引脚是PC1,PC7,PB9,PC6。引脚介绍如下:








官方设计的电路原理图如下:



实际的电路如下:



程序设计基本流程:

I2S的引脚功能配置(配置I2S外设功能)

开启中断配置

读取音频文件

发送音频数据

官方代码分析:

配置中断优先级分组,允许中断请求

nvic_priority_group_set(NVIC_PRIGROUP_PRE0_SUB4);
    nvic_irq_enable(SPI1_IRQn, 0, 1);
音频文件播放功能函数

i2s_audio_play()
具体分析音频文件播放功能,步骤如下:

1.  判断是否是wave audio文件,同时得到数据的起始位置datastartaddr 。

/*!
    \brief      wave audio file parsing function
    \param[in]  none
    \param[out] none
    \retval     errorcode_enum
*/
errorcode_enum codec_wave_parsing(void)
{
    uint32_t temp = 0;
    uint32_t extraformatbytes = 0;
    /* initialize the headertab index variable */
    headertab_index = 0;
    /* read chunkid, must be 'riff' */
    if(CHUNKID != read_unit(4, bigendian)) {
        return(UNVALID_RIFF_ID);
    }
    /* read the file length */
    wave_struct.riffchunksize = read_unit(4, littleendian);
    /* read the file format, must be 'wave' */
    if(FILEFORMAT != read_unit(4, bigendian)) {
        return(UNVALID_WAVE_FORMAT);
    }
    /* read the format chunk, must be 'fmt' */
    if(FORMATID != read_unit(4, bigendian)) {
        return(UNVALID_FORMATCHUNK_ID);
    }
    /* read the size of the 'fmt' data, must be 0x10 */
    if(FORMATCHUNKSIZE != read_unit(4, littleendian)) {
        extraformatbytes = 1;
    }
    /* read the audio format, must be 0x01 (pcm) */
    wave_struct.formattag = read_unit(2, littleendian);
    if(WAVE_FORMAT_PCM != wave_struct.formattag) {
        return(UNSUPPORETD_FORMATTAG);
    }
    /* read the number of channels: 0x02->stereo 0x01->mono */
    wave_struct.numchannels = read_unit(2, littleendian);
    /* read the sample rate */
    wave_struct.samplerate = read_unit(4, littleendian);
    /* update the i2s_audiofreq value according to the .wav file sample rate */
    if((wave_struct.samplerate < 8000) || (wave_struct.samplerate > 192000)) {
        return(UNSUPPORETD_SAMPLE_RATE);
    } else {
        i2saudiofreq = wave_struct.samplerate;
    }
    /* read the byte rate */
    wave_struct.byterate = read_unit(4, littleendian);
    /* read the block alignment */
    wave_struct.blockalign = read_unit(2, littleendian);
    /* read the number of bits per sample */
    wave_struct.bitspersample = read_unit(2, littleendian);
    if(BITS_PER_SAMPLE_16 != wave_struct.bitspersample) {
        return(UNSUPPORETD_BITS_PER_SAMPLE);
    }
    /* if there are extra format bytes, these bytes will be defined in "fact chunk" */
    if(1 == extraformatbytes) {
        /* read th extra format bytes, must be 0x00 */
        if(0x00 != read_unit(2, littleendian)) {
            return(UNSUPPORETD_EXTRAFORMATBYTES);
        }
        /* read the fact chunk, must be 'fact' */
        if(FACTID != read_unit(4, bigendian)) {
            return(UNVALID_FACTCHUNK_ID);
        }
        /* read fact chunk data size */
        temp = read_unit(4, littleendian);
        /* set the index to start reading just after the header end */
        headertab_index += temp;
    }
    /* read the data chunk, must be 'data' */
    if(DATAID != read_unit(4, bigendian)) {
        return(UNVALID_DATACHUNK_ID);
    }
    /* read the number of sample data */
    wave_struct.datasize = read_unit(4, littleendian);
    /* set the data pointer at the beginning of the effective audio data */
    datastartaddr += headertab_index;


    return(VALID_WAVE_FILE);
}

2. 文件格式正确,进行I2S配置

/*!
    \brief      configure the I2S peripheral
    \param[in]  none
    \param[out] none
    \retval     none
*/
void i2s_config()
{
    /* enable the GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_GPIOC);
    /* enable I2S1 clock */
    rcu_periph_clock_enable(RCU_SPI1);


    /* I2S1_MCK(PC6), I2S1_CK(PC7), I2S1_WS(PB9), I2S1_SD(PC1) GPIO pin configuration */
    gpio_af_set(GPIOC, GPIO_AF_5, GPIO_PIN_6 | GPIO_PIN_7);
    gpio_af_set(GPIOC, GPIO_AF_7, GPIO_PIN_1);
    gpio_af_set(GPIOB, GPIO_AF_5, GPIO_PIN_9);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1 | GPIO_PIN_6 | GPIO_PIN_7);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1 | GPIO_PIN_6 | GPIO_PIN_7);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);


    spi_i2s_deinit(SPI1);


    /* I2S1 peripheral configuration */
    i2s_psc_config(SPI1, i2saudiofreq, I2S_FRAMEFORMAT_DT16B_CH16B, I2S_MCLKOUTPUT);
    i2s_init(SPI1, I2S_MODE_MASTERTX, I2S_STANDARD, I2S_CKPL_HIGH);
    /* enable the I2S1 peripheral */
    i2s_enable(SPI1);
}

3. 中断使能

spi_i2s_interrupt_enable(SPI1, SPI_I2S_INT_TBE);
接下来看下中断函数中的处理,进行音频数据发送

/*!
    \brief      this function handles SPI1 exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void SPI1_IRQHandler(void)
{
    if(SET == spi_i2s_interrupt_flag_get(SPI1, SPI_I2S_INT_TBE))
        /* send data */
    {
        i2s_audio_data_send();
    }
}
音频发送函数的具体实现,如下:

/*!
    \brief      send audio data
    \param[in]  none
    \param[out] none
    \retval     none
*/
void i2s_audio_data_send(void)
{
    /* send the data read from the memory */
    spi_i2s_data_transmit(SPI1, read_half_word(audiodataindex + datastartaddr));
    /* increment the index */
    audiodataindex += (uint32_t)wave_struct.numchannels ;
}
audiodataindex 每次加1,(因为wave_struct.numchannels的值是1),也就是每次读取音频数据的地址会加1。

注意read_half_word()函数,对read_half_word()函数进行查看,如下:

/*!
    \brief      read half word
    \param[in]  offset : audio data index
    \param[out] none
    \retval     audio data
*/
uint16_t read_half_word(uint32_t offset)
{
    static  uint32_t monovar = 0, tmpvar = 0;
    if((AUDIOFILEADDRESS + offset) >= AUDIOFILEADDRESSEND) {
        spi_i2s_interrupt_disable(SPI1, SPI_I2S_INT_TBE);
        audiodataindex = 0;
    }
    /* test if the left channel is to be sent */
    if(0 == monovar) {
        tmpvar = (*(__IO uint16_t *)(AUDIOFILEADDRESS + offset));
        /* increment the mono variable only if the file is in mono format */
        if(CHANNEL_MONO == wave_struct.numchannels)
            /* increment the monovar variable */
        {
            monovar++;
        }
        /* return the read value */
        return tmpvar;
        /* right channel to be sent in mono format */
    } else {
        /* reset the monovar variable */
        monovar = 0;
        /* return the previous read data in mono format */
        return tmpvar;
    }
}

整个的流程到此结束。

将程序下载到开发板,插上耳机即可收听到播放的音频。
————————————————
版权声明:本文为CSDN博主「嵌入式学习和实践」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_46158019/article/details/130377276

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2028

主题

15904

帖子

14

粉丝