你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32H7B0,I2S和DMA问题(音频中伴随有破音)

[复制链接]
苦逼程序员 提问时间:2023-9-4 11:22 / 未解决

功能描述:利用STM32H7B0的I2S(用DMA)接口发送数据给DAC(每秒发一次,一次持续发送100毫秒),发完数据后就停止I2S和DMA,等下一秒再启动发送数据。

存在的问题:每次启动I2S发送数据,就出现一次破音,请问这是什么问题,如何解决?

1、DMA配置如下:

// 初始化dma
uint8 dma_i2s_init_dma()
{
    HAL_StatusTypeDef halFLag = HAL_ERROR;

    // 使能dma时钟
    __HAL_RCC_DMA1_CLK_ENABLE();

    // 失能DMA-TX
    HAL_DMA_DeInit(&g_dmaTxHandle);

    // 初始化DMA发送通道
    g_dmaTxHandle.Instance = DMA1_Stream1;
    g_dmaTxHandle.Init.Request = DMA_REQUEST_SPI1_TX;    // 请求源
    g_dmaTxHandle.Init.Direction = DMA_MEMORY_TO_PERIPH; // 存储器到外设
    g_dmaTxHandle.Init.PeriphInc = DMA_PINC_DISABLE;
    g_dmaTxHandle.Init.MemInc = DMA_MINC_ENABLE;
    g_dmaTxHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    g_dmaTxHandle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    g_dmaTxHandle.Init.Mode = DMA_NORMAL; // 普通模式
    g_dmaTxHandle.Init.Priority = DMA_PRIORITY_MEDIUM;
    g_dmaTxHandle.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    g_dmaTxHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; // 16字节
    g_dmaTxHandle.Init.MemBurst = DMA_MBURST_INC4;              // 一次取16字节
    g_dmaTxHandle.Init.PeriphBurst = DMA_PBURST_SINGLE;

    halFLag = HAL_DMA_Init(&g_dmaTxHandle);
    if (halFLag != HAL_OK)
    {
        return 0;
    }

    // 链接I2S句柄和Dma句柄
    __HAL_LINKDMA(&g_i2sHandle, hdmatx, g_dmaTxHandle);

    // DMA中断使能
    HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);

    return 1;
}

2、I2S配置如下(MCLK:12.288MHz,LRCK:48KHz):

// 初始化i2s外设
uint8 dma_i2s_init_i2s()
{
    HAL_StatusTypeDef halFLag = HAL_ERROR;

    dma_i2s_init_clock(); // 初始化时钟
    dma_i2s_init_gpio();  // 初始化i2s的管脚
    dma_i2s_init_dma();   // 初始化DMA

    // 失能I2S
    HAL_I2S_DeInit(&g_i2sHandle);

    // 组织I2S发送通信参数
    g_i2sHandle.Instance = SPI1;                            // 复用SPI1
    g_i2sHandle.Init.Mode = I2S_MODE_MASTER_TX;             // 主控制器
    g_i2sHandle.Init.Standard = I2S_STANDARD_PHILIPS;       // 飞利浦协议
    g_i2sHandle.Init.DataFormat = I2S_DATAFORMAT_24B;       // 24bit
    g_i2sHandle.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;    // 主时钟输出
    g_i2sHandle.Init.AudioFreq = I2S_AUDIOFREQ_48K;         // 采样率
    g_i2sHandle.Init.CPOL = I2S_CPOL_LOW;                   // 空闲状态,时钟为低电平
    g_i2sHandle.Init.FirstBit = I2S_FIRSTBIT_MSB;           // 大端模式,高字节在前
    g_i2sHandle.Init.WSInversion = I2S_WS_INVERSION_ENABLE; // LRCK翻转,左低右高
    g_i2sHandle.Init.Data24BitAlignment = I2S_DATA_24BIT_ALIGNMENT_LEFT;
    g_i2sHandle.Init.MasterKeepIOState = I2S_MASTER_KEEP_IO_STATE_DISABLE;

    // 初始化I2S句柄
    halFLag = HAL_I2S_Init(&g_i2sHandle);
    if (halFLag != HAL_OK)
    {
        return 0;
    }

    return 1;
}

3、停止DMA和I2S

// I2S发送完成的回调函数
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
{
    __HAL_DMA_CLEAR_FLAG(hi2s, DMA_FLAG_TCIF1_5); // 清除中断标识

    uint32 dwFactor = 4096;
    uint32 dwFlag = 0;                                 // I2S发送完成标识符,非0标识发送完成
    SPI_TypeDef *pSpi = (SPI_TypeDef *)hi2s->Instance; // SPI外设

    // 等待i2s移位寄存器发送完成
    while (1)
    {
        dwFlag = pSpi->SR & dwFactor;
        if (dwFlag)
        {
            HAL_I2S_DMAStop(hi2s); // 停止I2S发送
            g_byI2sCpltFlag = 0;   // 信号已发完

            break;
        }
    }

    return;
}
收藏 评论11 发布时间:2023-9-4 11:22

举报

11个回答
小白云 回答时间:2023-9-4 14:00:09

签到

butterflyspring 回答时间:2023-9-4 17:02:22
通常启动时产生的一次破音,可能有下面比较常见的两种情况:
1. 音源中存在有破音的信号, 可以跳过MCU来验证是否有破音产生。(换一个接口设备连接验证一下信号)
2. 功率转换时产生的,那么给功放延迟几十毫秒。


苦逼程序员 回答时间:2023-9-4 19:54:58

butterflyspring 发表于 2023-9-4 17:02
通常启动时产生的一次破音,可能有下面比较常见的两种情况:</p>
<ol>
<li>音源中存在有破音的信号, 可以跳过MCU来验 ...

音源确认过没有问题,DAC的输入数据和时钟也确认过没有问题;另外,目前还没有接PA,但在DAC后面观察到了破音。

我的应用是发送100毫秒数据,之后就停止dma和i2s,等900毫秒后再启动dma和i2s发送数据,一直这样循环往复下去,这个破音每秒就出现一次。

butterflyspring 回答时间:2023-9-5 11:06:48
建议先量测一下音源过来的信号连续性。
同常来说,左右声道的信号应该是连续传输的。
苦逼程序员 回答时间:2023-9-5 17:28:35

butterflyspring 发表于 2023-9-5 11:06
建议先量测一下音源过来的信号连续性。
同常来说,左右声道的信号应该是连续传输的。
...

我把信号做成全0也有这个破音。单步跟踪,发现在HAL_I2S_Transmit_DMA()这个函数中执行完以下这行代码,破音就出来了。有没有可能是I2S“使能发送”后产生一个冲击噪声。

  /* Start the transfer */
  SET_BIT(hi2s->Instance->CR1, SPI_CR1_CSTART);
butterflyspring 回答时间:2023-9-6 15:21:26
苦逼程序员 发表于 2023-9-5 17:28
[md]我把信号做成全0也有这个破音。单步跟踪,发现在`HAL_I2S_Transmit_DMA`()这个函数中执行完以下这 ...

执行这个有可能就是恢复了时钟。通常时钟应该保持连续,突然间开启时钟容易造成破音出来。
可以反向验证定位一下,不做停止,时钟保持所有时钟,这样破音是不是就不存在了。
苦逼程序员 回答时间:2023-9-6 20:52:56

butterflyspring 发表于 2023-9-6 15:21
执行这个有可能就是恢复了时钟。通常时钟应该保持连续,突然间开启时钟容易造成破音出来。
可以反向验证 ...

若不关闭I2S(I2S时钟保持,I2S的TX使能保持),只通过`__HAL_DMA_ENABLE(&g_dmaTxHandle)来使能DMA,这时数据就不会发送(分析原因应该是没有来自I2S的DMA请求),也不会进DMA发送完成中断。

butterflyspring 回答时间:2023-9-8 11:09:36
小伙伴们的经验是突变的时钟和数据会引起爆破音。
时钟保持,没有数据(数据线上拉)可以验证。
否则就只有MUTE了
xmshao 回答时间:2023-9-10 18:25:53

可能话,不妨跟硬件工程师 尤其对音响有经验的兄弟聊聊。

jyun 回答时间:2023-9-10 21:59:47

应该排查一下,定义的I2S的格式和DMA突发大小是否相同,然后DMA传输的I2S音频数据的 buff缓存大小数量是否正确,然后地址是否 4字节对齐

苦逼程序员 回答时间:2023-9-20 20:47:25

butterflyspring 发表于 2023-9-8 11:09
小伙伴们的经验是突变的时钟和数据会引起爆破音。
时钟保持,没有数据(数据线上拉)可以验证。
否则就只 ...

MCLK、LRCK和BCK三个时钟都是MCU的I2S接口控制的,软件拿不到控制权,没法保持呀。

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版