硬汉嵌入式论坛

 找回密码
 立即注册
查看: 313|回复: 3
收起左侧

[有问必答] GD32 I2S DMA 只停止数据保持时钟输出

[复制链接]

18

主题

60

回帖

114

积分

初级会员

积分
114
发表于 2025-8-26 14:06:28 | 显示全部楼层 |阅读模式

遇到一个功放芯片 要求I2S时钟一直保持输出,否则报故障。。I2S_DMA_Start和I2S_DMA_Stop如何处理才能实现,求解。


[C] 纯文本查看 复制代码
void i2s_gpio_config(void)
{
    /* configure I2S1 and I2S1_ADD pins: I2S2_WS(PA15), I2S2_CK(PC10), I2S2_SD(PD6), I2S2_ADD_SD(PC11) */
        gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);//CLK
    gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO_PIN_6);
        gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_15);//WS
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO_PIN_15);

    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10 | GPIO_PIN_11);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO_PIN_10 | GPIO_PIN_11);
        
    gpio_af_set(GPIOA, GPIO_AF_6, GPIO_PIN_15);
    gpio_af_set(GPIOC, GPIO_AF_6, GPIO_PIN_10);
    gpio_af_set(GPIOC, GPIO_AF_5, GPIO_PIN_11);
    gpio_af_set(GPIOD, GPIO_AF_5, GPIO_PIN_6);

}

/*!
    \brief      configure different peripheral clocks
    \param[in]  none
    \param[out] none
    \retval     none
*/
void i2s_rcu_config(void)
{
    /* enable GPIOA clock */
    rcu_periph_clock_enable(RCU_GPIOA);
    /* enable GPIOB clock */
    rcu_periph_clock_enable(RCU_GPIOC);
    /* enable GPIOI clock */
    rcu_periph_clock_enable(RCU_GPIOD);
    /* enable SPI2/I2S2 clock */
    rcu_periph_clock_enable(RCU_SPI2);
    /* enable DMA0 clock */
    rcu_periph_clock_enable(RCU_DMA0);

}

/*!
    \brief      configure the SPI peripheral
    \param[in]  none
    \param[out] none
    \retval     none
*/
void i2s_spi_config(void)
{
    spi_i2s_deinit(SPI2);

    /* configure I2S2 and I2S2_ADD */
    i2s_init(SPI2, I2S_MODE_MASTERTX, I2S_STD_PHILLIPS, I2S_CKPL_LOW);
    i2s_psc_config(SPI2, I2S_AUDIOSAMPLE_48K, I2S_FRAMEFORMAT_DT16B_CH16B, I2S_MCKOUT_DISABLE);
    i2s_full_duplex_mode_config(I2S2_ADD, I2S_MODE_MASTERTX, I2S_STD_PHILLIPS, I2S_CKPL_LOW, I2S_FRAMEFORMAT_DT16B_CH16B);

}


void i2s_dma_config(void)
{
    dma_single_data_parameter_struct dma_init_struct;

    dma_single_data_para_struct_init(&dma_init_struct);

        dma_deinit(DMA0, DMA_CH5);
        dma_init_struct.periph_addr                   = (uint32_t)&SPI_DATA(SPI2);
        dma_init_struct.memory0_addr            = (uint32_t)i2s2_Audiobuffer1;
        dma_init_struct.direction                   = DMA_MEMORY_TO_PERIPH;
        dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_16BIT;
        dma_init_struct.priority                    = DMA_PRIORITY_LOW;
        dma_init_struct.number                           = I2S_AUDIO_SIZE;
        dma_init_struct.periph_inc                   = DMA_PERIPH_INCREASE_DISABLE;
        dma_init_struct.memory_inc                   = DMA_MEMORY_INCREASE_ENABLE;
        dma_init_struct.circular_mode           = DMA_CIRCULAR_MODE_DISABLE;
        dma_single_data_mode_init(DMA0, DMA_CH5, &dma_init_struct);
        dma_channel_subperipheral_select(DMA0, DMA_CH5, DMA_SUBPERI0);
        dma_circulation_disable(DMA0, DMA_CH5);
        dma_interrupt_enable(DMA0, DMA_CH5, DMA_INT_FTF);
        nvic_irq_enable(DMA0_Channel5_IRQn, 0, 0);  // 使能DMA中断
        //dma_interrupt_enable(DMA0, DMA_CH5, DMA_INT_FTF);


//    /* configure I2S2_ADD transmit DMA */
//    dma_deinit(DMA0, DMA_CH5);
//    dma_init_struct.periph_addr  = (uint32_t)&SPI_DATA(I2S2_ADD);
//    dma_init_struct.memory0_addr = (uint32_t)i2s2_txbuffer;
//    dma_init_struct.direction    = DMA_MEMORY_TO_PERIPH;
//    dma_init_struct.priority     = DMA_PRIORITY_LOW;
//    dma_single_data_mode_init(DMA0, DMA_CH5, &dma_init_struct);
//    dma_channel_subperipheral_select(DMA0, DMA_CH5, DMA_SUBPERI2);
//
    /* configure SPI2 receive DMA */
//    dma_deinit(DMA0, DMA_CH0);
//    dma_init_struct.periph_addr  = (uint32_t)&SPI_DATA(SPI2);
//    dma_init_struct.memory0_addr = (uint32_t)i2s2_rxbuffer;
//    dma_init_struct.direction    = DMA_PERIPH_TO_MEMORY;
//    dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;
//    dma_single_data_mode_init(DMA0, DMA_CH0, &dma_init_struct);
//    dma_channel_subperipheral_select(DMA0, DMA_CH0, DMA_SUBPERI0);
}

void DMA0_Channel5_IRQHandler(void) {
    // 检查传输完成标志
    if(dma_interrupt_flag_get(DMA0, DMA_CH5, DMA_INT_FLAG_FTF)) {
        dma_interrupt_flag_clear(DMA0, DMA_CH5, DMA_INT_FLAG_FTF);
        
        // 停止当前DMA通道
        dma_channel_disable(DMA0, DMA_CH5);
        
        // 切换缓冲区
        if(I2S_Buffer_Index == 0) 
                {
                   I2S_Buffer1_playdone = 1;
            // Buffer1传输完成,切换到Buffer2
            dma_memory_address_config(DMA0, DMA_CH5,DMA_MEMORY_0, (uint32_t)i2s2_Audiobuffer2);
         
            I2S_Buffer_Index = 1;
        } 
                else 
                {
                    I2S_Buffer2_playdone = 1;
            // Buffer2传输完成,切换到Buffer1
            dma_memory_address_config(DMA0, DMA_CH5, DMA_MEMORY_0,(uint32_t)i2s2_Audiobuffer1);
            I2S_Buffer_Index = 0;
        }
        
        // 重新设置传输数量并启动DMA
        dma_transfer_number_config(DMA0, DMA_CH5, I2S_AUDIO_SIZE);
        dma_channel_enable(DMA0, DMA_CH5);
    }
}

void I2S2_Init(void)
{
    /* enable peripheral clock */
    i2s_rcu_config();
    /* configure GPIO */
    i2s_gpio_config();
    /* configure DMA */
    i2s_dma_config();
    /* configure I2S */
    i2s_spi_config();
    /* enable I2S and I2S_ADD*/
    i2s_enable(SPI2);
    i2s_enable(I2S2_ADD);

    /* enable DMA channel */
        //spi_dma_enable(SPI2, SPI_DMA_TRANSMIT);
    //dma_channel_enable(DMA0, DMA_CH5);


}

void I2S_DMA_Start(void)
{
        
        //i2s_enable(SPI2);
        //i2s_enable(I2S2_ADD);

        //dma_memory_address_config(DMA0, DMA_CH5, DMA_MEMORY_0,(uint32_t)i2s2_txbuffer);
        //dma_transfer_number_config(DMA0, DMA_CH5, 96);
        //nvic_irq_enable(DMA0_Channel5_IRQn, 0, 0);        // 使能DMA中断
        spi_dma_enable(SPI2, SPI_DMA_TRANSMIT);
    dma_channel_enable(DMA0, DMA_CH5);
        //delay_1ms(1);
        Play_Config();

}
void I2S_DMA_Stop(void)
{
        //Exit_Play_Mode();
        //delay_1ms(1);
        spi_dma_disable(SPI2, SPI_DMA_TRANSMIT);
        dma_channel_disable(DMA0, DMA_CH5);
        //nvic_irq_disable(DMA0_Channel5_IRQn);        // 使能DMA中断
        dma_interrupt_flag_clear(DMA0, DMA_CH5, DMA_INT_FTF);
        dma_interrupt_flag_clear(DMA0, DMA_CH5, DMA_INT_HTF);



        //delay_1ms(10);
        //Enter_Sleep_Mode();
        //I2S2_Init();
        //CLD8684_Init();

        //Play_Config();


        // 等待DMA完全停止
                //while(dma_flag_get(DMA1, DMA_CH4, DMA_FLAG_CHEN));
        //while((DMA_CHCTL(DMA0,DMA_CH5) & DMA_CHXCTL_CHEN) != 0);

                // 强制保持I2S2使能(防止意外关闭)
                if((SPI_CTL1(SPI2) & SPI_I2SCTL_I2SEN) == 0) {
                        i2s_enable(SPI2);
                }


}

回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
118328
QQ
发表于 2025-8-26 15:02:37 | 显示全部楼层
可以考虑DMA循环模式一直开着,输出无效数据。
回复

使用道具 举报

18

主题

60

回帖

114

积分

初级会员

积分
114
 楼主| 发表于 2025-8-26 16:02:53 | 显示全部楼层
eric2013 发表于 2025-8-26 15:02
可以考虑DMA循环模式一直开着,输出无效数据。

想过收到停止命令把双缓冲数组清零的方式,但是软件逻辑就复杂了。大佬,有没有方法保持时钟但不输出数据
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
118328
QQ
发表于 2025-8-28 14:20:19 | 显示全部楼层
lishang4650 发表于 2025-8-26 16:02
想过收到停止命令把双缓冲数组清零的方式,但是软件逻辑就复杂了。大佬,有没有方法保持时钟但不输出数据 ...

别的好像没有好方法了,双缓冲修改挺方便的
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-9-26 14:54 , Processed in 0.041100 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表