RT-Thread论坛
直播中

郑成枝

7年用户 1273经验值
私信 关注
[问答]

I2S WM8978播放速度慢,有哒哒哒噪声的原因?如何解决?

使用STM32F4的I2S接口,直接找的论坛的I2S代码 drv_i2s代码,发现能够正常播放音频,但是播放速度慢,有频率很高的哒哒哒的噪声,求助原因,代码如下:
drv_i2s:

I2S_HandleTypeDef hi2s2;
DMA_HandleTypeDef hdma_spi2_tx;
struct stm32_audio
{
    struct rt_i2c_bus_device *i2c_bus;
    struct rt_audio_device audio;
    struct rt_audio_configure replay_config;
    int replay_volume;
    rt_uint8_t *tx_fifo;
    rt_bool_t startup;
};
struct stm32_audio _stm32_audio_play = {0};
void i2s_samplerate_set(rt_uint32_t freq)
{
    if(freq == I2S_AUDIOFREQ_8K  || freq == I2S_AUDIOFREQ_11K ||
            freq == I2S_AUDIOFREQ_16K || freq == I2S_AUDIOFREQ_22K ||
            freq == I2S_AUDIOFREQ_32K || freq == I2S_AUDIOFREQ_44K ||
            freq == I2S_AUDIOFREQ_48K || freq == I2S_AUDIOFREQ_96K)
    {
        hi2s2.Init.AudioFreq = freq;
    }
    else
    {
        return;
    }
    __HAL_I2S_DISABLE(&hi2s2);
    HAL_I2S_Init(&hi2s2);
    __HAL_I2S_ENABLE(&hi2s2);
}
void i2s_channels_set(rt_uint16_t channels)
{
}
void i2s_samplebits_set(rt_uint16_t samplebits)
{
    switch (samplebits)
    {
        case 16:
            hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
            break;
        case 24:
            hi2s2.Init.DataFormat = I2S_DATAFORMAT_24B;
            break;
        case 32:
            hi2s2.Init.DataFormat = I2S_DATAFORMAT_32B;
            break;
        default:
            hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
            break;
    }
    __HAL_I2S_DISABLE(&hi2s2);
    HAL_I2S_Init(&hi2s2);
    __HAL_I2S_ENABLE(&hi2s2);
}
void i2s_config_set(struct rt_audio_configure config)
{
    i2s_channels_set(config.channels);
    i2s_samplerate_set(config.samplerate);
    i2s_samplebits_set(config.samplebits);
}
rt_err_t I2S_config_init(void)
{
    hi2s2.Instance = SPI2;
    hi2s2.Init.Mode = I2S_MODE_MASTER_TX;
    hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
    hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
    hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
    hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_8K;
    hi2s2.Init.CPOL = I2S_CPOL_LOW;
    hi2s2.Init.ClockSource = I2S_CLOCK_PLL;
    hi2s2.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_ENABLE;
    if (HAL_I2S_Init(&hi2s2) != HAL_OK)
    {
        rt_kprintf("HAL_I2S_Init error\n");
    }
    SET_BIT(hi2s2.Instance->CR2, SPI_CR2_TXDMAEN);
    __HAL_I2S_ENABLE(&hi2s2);
        return RT_EOK;
}
void I2S_DMAConvCplt(DMA_HandleTypeDef *hdma)
{
    rt_audio_tx_complete(&_stm32_audio_play.audio);
}
void I2S_DMAError(DMA_HandleTypeDef *hdma)
{
   // rt_kprintf("DMAError\n");
}
void DMA1_Stream4_IRQHandler(void)
{
    rt_interrupt_enter();
    HAL_DMA_IRQHandler(&hdma_spi2_tx);
    rt_interrupt_leave();
}
void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
    rt_kprintf("HAL_I2S_TxHalfCpltCallback pk 1\n");
    rt_audio_tx_complete(&_stm32_audio_play.audio);
}
void I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
    rt_audio_tx_complete(&_stm32_audio_play.audio);
}
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
{
    rt_kprintf("HAL_I2S_TxCpltCallback pk 2\n");
    rt_audio_tx_complete(&_stm32_audio_play.audio);
}
rt_err_t I2S_tx_dma(void)
{
    __HAL_DMA_ENABLE_IT(&hdma_spi2_tx,DMA_IT_TC);//开启DMA传输完成中断
    __HAL_DMA_ENABLE_IT(&hdma_spi2_tx, DMA_IT_HT); //开启DMA半传输传输完成中断
    __HAL_DMA_CLEAR_FLAG(&hdma_spi2_tx, DMA_FLAG_TCIF0_4);
    //注册回调函数,读取数据等操作在这里面处理
    hdma_spi2_tx.XferCpltCallback = I2S_DMAConvCplt;
    hdma_spi2_tx.XferM1CpltCallback = I2S_DMAConvCplt;
    hdma_spi2_tx.XferErrorCallback = I2S_DMAError;
    hdma_spi2_tx.XferHalfCpltCallback=I2S_TxHalfCpltCallback;
    HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
    return RT_EOK;
}
rt_err_t i2s_tx_init()
{
    /* set I2S_TX DMA */
    I2S_config_init();
    I2S_tx_dma();
    return RT_EOK;
}
static rt_err_t stm32_player_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;
    struct stm32_audio *st_audio = (struct stm32_audio *)audio->parent.user_data;
    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);
    switch (caps->main_type)
    {
        case AUDIO_TYPE_QUERY: /* query the types of hw_codec device */
        {
            switch (caps->sub_type)
            {
                case AUDIO_TYPE_QUERY:
                    caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
                    break;
                default:
                    result = -RT_ERROR;
                    break;
            }
            break;
        }
        case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
        {
            switch (caps->sub_type)
            {
                case AUDIO_DSP_PARAM:
                    caps->udata.config.channels     = st_audio->replay_config.channels;
                    caps->udata.config.samplebits   = st_audio->replay_config.samplebits;
                    caps->udata.config.samplerate   = st_audio->replay_config.samplerate;
                    break;
                case AUDIO_DSP_SAMPLERATE:
                    caps->udata.config.samplerate   = st_audio->replay_config.samplerate;
                    break;
                case AUDIO_DSP_CHANNELS:
                    caps->udata.config.channels     = st_audio->replay_config.channels;
                    break;
                case AUDIO_DSP_SAMPLEBITS:
                    caps->udata.config.samplebits     = st_audio->replay_config.samplebits;
                    break;
                default:
                    result = -RT_ERROR;
                    break;
            }
            break;
        }
        case AUDIO_TYPE_MIXER: /* report the Mixer Units */
        {
            switch (caps->sub_type)
            {
                case AUDIO_MIXER_QUERY:
                    caps->udata.mask = AUDIO_MIXER_VOLUME | AUDIO_MIXER_LINE;
                    break;
                case AUDIO_MIXER_VOLUME:
                    caps->udata.value = st_audio->replay_volume;
                    break;
                case AUDIO_MIXER_LINE:
                    break;
                default:
                    result = -RT_ERROR;
                    break;
            }
            break;
        }
        default:
            result = -RT_ERROR;
            break;
    }
    return result;
}
static rt_err_t  stm32_player_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;
    struct stm32_audio *st_audio = (struct stm32_audio *)audio->parent.user_data;
    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);
    switch (caps->main_type)
    {
        case AUDIO_TYPE_MIXER:
        {
            switch (caps->sub_type)
            {
                case AUDIO_MIXER_MUTE:
                {
                    /* set mute mode */
                    wm8978_mute_enabled(_stm32_audio_play.i2c_bus, RT_FALSE);
                    break;
                }
                case AUDIO_MIXER_VOLUME:
                {
                    int volume = caps->udata.value;
                    st_audio->replay_volume = volume;
                    /* set mixer volume */
                    wm8978_set_volume(_stm32_audio_play.i2c_bus, volume);
                    break;
                }
                default:
                    result = -RT_ERROR;
                    break;
            }
            break;
        }
        case AUDIO_TYPE_OUTPUT:
        {
            switch (caps->sub_type)
            {
                case AUDIO_DSP_PARAM:
                {
                    struct rt_audio_configure config = caps->udata.config;
                    st_audio->replay_config.samplerate = config.samplerate;
                    st_audio->replay_config.samplebits = config.samplebits;
                    st_audio->replay_config.channels = config.channels;
                    i2s_config_set(config);
                    break;
                }
                case AUDIO_DSP_SAMPLERATE:
                {
                    st_audio->replay_config.samplerate = caps->udata.config.samplerate;
                    i2s_samplerate_set(caps->udata.config.samplerate);
                    break;
                }
                case AUDIO_DSP_CHANNELS:
                {
                    st_audio->replay_config.channels = caps->udata.config.channels;
                    i2s_channels_set(caps->udata.config.channels);
                    break;
                }
                case AUDIO_DSP_SAMPLEBITS:
                {
                    st_audio->replay_config.samplebits = caps->udata.config.samplebits;
                    i2s_samplebits_set(caps->udata.config.samplebits);
                    break;
                }
                default:
                    result = -RT_ERROR;
                    break;
            }
            break;
        }
        default:
            break;
    }
    return result;
}
static rt_err_t stm32_player_init(struct rt_audio_device *audio)
{
    /* initialize wm8978 */
    _stm32_audio_play.i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(CODEC_I2C_NAME);
    i2s_tx_init();
    wm8978_init(_stm32_audio_play.i2c_bus);
    return RT_EOK;
}
static rt_err_t stm32_player_start(struct rt_audio_device *audio, int stream)
{
    if (stream == AUDIO_STREAM_REPLAY)
    {
        rt_kprintf("HAL_I2S_Transmit_DMA start\n");
        wm8978_player_start(_stm32_audio_play.i2c_bus);
        HAL_DMAEx_MultiBufferStart_IT(&hdma_spi2_tx, (uint32_t)_stm32_audio_play.tx_fifo, (uint32_t) & (SPI2->DR), (uint32_t)(_stm32_audio_play.tx_fifo + TX_DMA_FIFO_SIZE / 2), TX_DMA_FIFO_SIZE / 2);
        //HAL_I2S_Transmit_DMA(&hi2s2,(uint16_t *)_stm32_audio_play.tx_fifo,TX_DMA_FIFO_SIZE);
    }
    return RT_EOK;
}
static rt_err_t stm32_player_stop(struct rt_audio_device *audio, int stream)
{
    if (stream == AUDIO_STREAM_REPLAY)
    {
        HAL_I2S_DMAStop(&hi2s2);
        rt_kprintf("HAL_I2S_Transmit_DMA stop\n");
    }
    return RT_EOK;
}
static void stm32_player_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
{
    /**
     *               TX_FIFO
     * +----------------+----------------+
     * |     block1     |     block2     |
     * +----------------+----------------+
     *  \  block_size  /
     */
    info->buffer = _stm32_audio_play.tx_fifo;
    info->total_size = TX_DMA_FIFO_SIZE;
    info->block_size = TX_DMA_FIFO_SIZE / 2;
    info->block_count = 2;
}
static struct rt_audio_ops _p_audio_ops =
{
    .getcaps     = stm32_player_getcaps,
    .configure   = stm32_player_configure,
    .init        = stm32_player_init,
    .start       = stm32_player_start,
    .stop        = stm32_player_stop,
    .transmit    = NULL,
    .buffer_info = stm32_player_buffer_info,
};
int rt_hw_sound_init(void)
{
    rt_uint8_t *tx_fifo;
    /* player */
    tx_fifo = rt_malloc(TX_DMA_FIFO_SIZE);
    if (tx_fifo == RT_NULL)
    {
        return -RT_ENOMEM;
    }
    rt_memset(tx_fifo, 0, TX_DMA_FIFO_SIZE);
    _stm32_audio_play.tx_fifo = tx_fifo;
    _stm32_audio_play.tx_fifo = tx_fifo;
    /* init default configuration */
    {
        _stm32_audio_play.replay_config.samplerate = 48000;
        _stm32_audio_play.replay_config.channels   = 1;
        _stm32_audio_play.replay_config.samplebits = 16;
        _stm32_audio_play.replay_volume            = 15;
    }
    /* register sound device */
    _stm32_audio_play.audio.ops = &_p_audio_ops;
    rt_audio_register(&_stm32_audio_play.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &_stm32_audio_play);
    return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_sound_init);
使用的模拟i2c控制,msp初始化如下:

extern DMA_HandleTypeDef hdma_spi2_tx;
void HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s) {
     if (hi2s->Instance == SPI2) {
        RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
        PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
        PeriphClkInitStruct.PLLI2S.PLLI2SN = 271;
        PeriphClkInitStruct.PLLI2S.PLLI2SR = 2;
        if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
        {
          Error_Handler();
        }
        GPIO_InitTypeDef GPIO_InitStruct;
        /* Peripheral clock enable */
        __HAL_RCC_SPI2_CLK_ENABLE();
        __HAL_RCC_GPIOC_CLK_ENABLE();
        __HAL_RCC_GPIOB_CLK_ENABLE();
        __HAL_RCC_GPIOD_CLK_ENABLE();
        __HAL_RCC_GPIOI_CLK_ENABLE();
        /**I2S2 GPIO Configuration
        PC2     ------> I2S2_ext_SD
        PC3     ------> I2S2_SD
        PB12     ------> I2S2_WS
        PB13     ------> I2S2_CK
        PC6     ------> I2S2_MCK
        */
        GPIO_InitStruct.Pin = GPIO_PIN_2;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF6_I2S2ext;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
        GPIO_InitStruct.Pin = GPIO_PIN_6;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
        GPIO_InitStruct.Pin = GPIO_PIN_3;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);
        GPIO_InitStruct.Pin = GPIO_PIN_12;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
        GPIO_InitStruct.Pin = GPIO_PIN_3;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
                __HAL_RCC_DMA1_CLK_ENABLE();
        hdma_spi2_tx.Instance = DMA1_Stream4;                            //DMA1数据流4
        hdma_spi2_tx.Init.Channel = DMA_CHANNEL_0;                       //通道0
        hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;              //存储器到外设模式
        hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE;                  //外设非增量模式
        hdma_spi2_tx.Init.MemInc = DMA_MINC_ENABLE;                      //存储器增量模式
        /*todo 这里按照道理应该根据数据位宽来配置 ?*/
        hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位
        hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;    //存储器数据长度:16位
        hdma_spi2_tx.Init.Mode = DMA_CIRCULAR;                           //使用循环模式
        hdma_spi2_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;             //高优先级
        hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;               //不使用FIFO
        hdma_spi2_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;       // 使用FIFO阈值完全配置
        hdma_spi2_tx.Init.MemBurst = DMA_MBURST_SINGLE;                  //存储器单次突发传输
        hdma_spi2_tx.Init.PeriphBurst = DMA_MBURST_SINGLE;               //外设突发单次传输
        if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)
        {
            Error_Handler();
        }
        __HAL_LINKDMA(hi2s,hdmatx,hdma_spi2_tx);
    }
}
其实一开始的播放速度很慢很慢,后面我修改了DMAFIFO的大小,调大了,速度就快了些,但是总的来说还是慢,且噪声非常大

#define TX_DMA_FIFO_SIZE (4096)
#define I2S_SOUND_NAME "sound0"
#define CODEC_I2C_NAME  ("i2c1")
卡了很久了,求助

回帖(1)

陈秀英

2024-7-16 17:31:09
根据您提供的信息,播放速度慢和哒哒哒噪声的原因可能有以下几点:

1. 采样率设置不正确:请检查您的音频文件的采样率是否与STM32F4的I2S接口设置的采样率相匹配。如果采样率不匹配,可能导致播放速度变慢。

2. I2S时钟配置问题:请检查I2S时钟配置是否正确。错误的时钟配置可能导致播放速度慢和噪声问题。

3. DMA配置问题:请检查DMA的配置,确保DMA传输速率和数据大小设置正确。错误的DMA配置可能导致播放速度慢和噪声问题。

4. 音频文件格式问题:请检查音频文件的格式是否支持或兼容。某些音频格式可能需要特定的解码器或处理方式。

5. 代码问题:请仔细检查您的代码,确保I2S初始化、配置和数据处理部分没有错误。

解决方案:

1. 检查采样率设置:确保音频文件的采样率与STM32F4的I2S接口设置的采样率相匹配。

2. 检查I2S时钟配置:确保I2S时钟配置正确,可以参考STM32F4的参考手册进行配置。

3. 检查DMA配置:确保DMA的配置正确,包括传输速率和数据大小。

4. 检查音频文件格式:确保音频文件格式支持或兼容,如果需要,可以尝试转换音频文件格式。

5. 检查代码:仔细检查您的代码,确保I2S初始化、配置和数据处理部分没有错误。如果可能,可以参考其他成功的项目或示例代码。

6. 使用示波器或逻辑分析仪检查I2S信号:这可以帮助您确定问题是否出在硬件上,例如I2S接口或连接线。

7. 更新固件和库:确保您使用的STM32F4固件和库是最新版本,以避免已知的问题。

通过以上步骤,您应该能够找到问题的原因并解决播放速度慢和哒哒哒噪声的问题。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分