打印

请教使用DMA+I2S配合国产CODEC,TX信号杂乱问题

[复制链接]
961|31
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 ko1057900143 于 2025-5-22 16:47 编辑

各位好,请教一个问题。目前是采用GD32F470的I2S和DMA,配个国产CODEC,想进行音频数据回环。目前遇到一个问题,就是TX BUFF,使用RX拷贝的数据示波器量的信号是杂乱的,可如果我给TX BUFF初始化全部为5a,可是可以量到信号。
我的实现思路如下:
先使用I2C对CODEC进行初始化,回读校验应该写入成功了。
然后我使用的GD的I2S2
预先的设想是DMA将CODEC的数据通过I2S2的RX搬用到缓存RX_BUFF中,
完成后进入中断,进入中断后clear flag,关闭DMA的收发channel,数据搬运倒TX_BUFF,然后使能TX的channel和RX的CHANNEL。让DMA将数据搬运到CODEC的RX。以此完成回环。
代码我粘贴主要部分在下面,麻烦各位帮忙想想错在哪儿?
拜谢

使用特权

评论回复
沙发
ko1057900143|  楼主 | 2025-5-22 16:38 | 只看该作者
初始化顺序是
        systick_config();   
        nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
        init_i2s1_dma();
        init_i2s2_dma();
        printf("DMA INIT DONE !!!\r\n");
        init_i2s1();
        init_i2s2();
        printf("I2S INIT DONE !!!\r\n");
        init_i2c();
        printf("I2C INIT DONE !!!\r\n");
        init_cmux();
        。。。// RTOS初始化
        tmp = nau88c10_init();
        i2s1_control(ENABLE);
        i2s2_control(ENABLE);

使用特权

评论回复
板凳
ko1057900143|  楼主 | 2025-5-22 16:40 | 只看该作者
// to CODEC
void init_i2s2_dma(void)
{
        dma_single_data_parameter_struct dma_init_struct;
       
    rcu_periph_clock_enable(RCU_DMA0);

        clear_i2s2_flags();
        // PB4 -> IN -> ADD 也就是数据从SD出 I2S1为TX
        // PC1 -> OUT -> I2S2_SD, 也就是数据从ADD口进 ADD为RX
/***************************** I2S TX ******************************/
    dma_single_data_para_struct_init(&dma_init_struct);

        dma_channel_disable(DMA0, DMA_CH5);
        dma_deinit(DMA0, DMA_CH5);
        // I2S复用SPI的寄存器
    dma_init_struct.periph_addr         = (uint32_t)&SPI_DATA(SPI2);
        // 内存地址
    dma_init_struct.memory0_addr        = (uint32_t)i2s_mtc_tx_cache;
        // 内存发外设
    dma_init_struct.direction           = DMA_MEMORY_TO_PERIPH;
    dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_16BIT;
    dma_init_struct.priority            = DMA_PRIORITY_MEDIUM;
    //dma_init_struct.number              = BUFFER_SIZE * 2;
           dma_init_struct.number              = BUFFER_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);
       
        // 数据发到SPI2_TX
        dma_channel_subperipheral_select(DMA0, DMA_CH5, DMA_SUBPERI0);
        //dma_circulation_enable(DMA0, DMA_CH5);
        // 全传输完成
        dma_interrupt_enable(DMA0, DMA_CH5, DMA_CHXCTL_FTFIE);

/***************************** I2S RX ******************************/
        dma_channel_disable(DMA0, DMA_CH0);
        dma_deinit(DMA0, DMA_CH0);
        // I2S_ADD_RX
    dma_init_struct.periph_addr         = (uint32_t)&SPI_DATA(I2S2_ADD);
        // 内存地址
    dma_init_struct.memory0_addr        = (uint32_t)i2s_ctm_rx_cache;
        // 外设发内存
    dma_init_struct.direction           = DMA_PERIPH_TO_MEMORY;
    dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_16BIT;
    dma_init_struct.priority            = DMA_PRIORITY_HIGH;
        // 初始化时数组的长度
    dma_init_struct.number              = BUFFER_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_CH0, &dma_init_struct);
       
        // 数据发到I2S2_ADD_RX
        dma_channel_subperipheral_select(DMA0, DMA_CH0, DMA_SUBPERI3);
        dma_circulation_disable(DMA0, DMA_CH0);
        dma_interrupt_enable(DMA0, DMA_CH0, DMA_CHXCTL_FTFIE);
       
}

使用特权

评论回复
地板
ko1057900143|  楼主 | 2025-5-22 16:41 | 只看该作者
void init_i2s2(void)
{
        rcu_periph_clock_enable(RCU_SPI2);

        // SPI2_MOSI/I2S2_SD tx
        gpio_af_set(GPIOC, GPIO_AF_5, GPIO_PIN_1);
        // SPI2_SCK/I2S2_CK
        gpio_af_set(GPIOB, GPIO_AF_6, GPIO_PIN_3);
        // I2S2_ADD_SD 全双工脚 rx
        gpio_af_set(GPIOB, GPIO_AF_7, GPIO_PIN_4);
        // SPI2_NSS/I2S2_WS
        gpio_af_set(GPIOA, GPIO_AF_6, GPIO_PIN_4);
       
        gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
        gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
       
        gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3|GPIO_PIN_4);
        gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3|GPIO_PIN_4);
       
        gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);
        gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);

        spi_i2s_deinit(SPI2);
        i2s_init(SPI2, I2S_MODE_SLAVETX, I2S_STD_PHILLIPS, I2S_CKPL_LOW);
        // 16位,8K采样率,不需要MCLK时钟
        i2s_psc_config(SPI2, I2S_AUDIOSAMPLE_8K, I2S_FRAMEFORMAT_DT16B_CH16B, I2S_MCKOUT_DISABLE);
        // 这里的库函数有问题,RX和TX反转了
        i2s_full_duplex_mode_config(I2S2_ADD, I2S_MODE_SLAVETX, I2S_STD_PHILLIPS, I2S_CKPL_LOW, I2S_FRAMEFORMAT_DT16B_CH16B);
}

使用特权

评论回复
5
ko1057900143|  楼主 | 2025-5-22 16:42 | 只看该作者
void i2s2_control(ControlStatus state)
{
        if(state == ENABLE) {
                i2s_disable(I2S2_ADD);
            i2s_disable(SPI2);
                // DMA通道使能CHN5 TX
                dma_channel_enable(DMA0, DMA_CH0);
            dma_channel_enable(DMA0, DMA_CH5);
                /* enable SPI DMA */
                spi_dma_enable(I2S2_ADD, SPI_DMA_RECEIVE);
            spi_dma_enable(SPI2, SPI_DMA_TRANSMIT);
                // 使能
                i2s_enable(I2S2_ADD);
            i2s_enable(SPI2);
                // CHN0 RX
                nvic_irq_enable(DMA0_Channel0_IRQn, 0, 0);
                nvic_irq_enable(DMA0_Channel5_IRQn, 0, 0);
        } else if(state == DISABLE) {
                // 使能
                i2s_disable(I2S2_ADD);
            i2s_disable(SPI2);
                // DMA通道使能
                dma_channel_disable(DMA0, DMA_CH0);
            dma_channel_disable(DMA0, DMA_CH5);
                spi_dma_disable(I2S2_ADD, SPI_DMA_RECEIVE);
            spi_dma_disable(SPI2, SPI_DMA_TRANSMIT);
                nvic_irq_disable(DMA0_Channel0_IRQn);
        }
}

使用特权

评论回复
6
ko1057900143|  楼主 | 2025-5-22 16:43 | 只看该作者
void DMA0_Channel0_IRQHandler(void)
{
        uint8_t index;
        // CHN5 TX CHN0 RX
        if (dma_interrupt_flag_get(DMA0, DMA_CH0, DMA_INT_FLAG_FTF)) {
        dma_interrupt_flag_clear(DMA0, DMA_CH0, DMA_INT_FLAG_FTF);
                dma_channel_disable(DMA0, DMA_CH0);
                dma_channel_disable(DMA0, DMA_CH5);
        // 2. 复制数据到 Modem TX 缓冲区
        memcpy((uint16_t*)i2s_mtc_tx_cache, (uint16_t*)i2s_ctm_rx_cache, BUFFER_SIZE);
                printf("\r\nDMA0_Channel0_IRQHandler222\r\n");
                //for(index = 0; index < 64; index ++)
                //        printf("0x%x, 0x%x, ", (i2s_mtc_tx_cache[index] >> 8), i2s_mtc_tx_cache[index] & 0x00ff);
        // 3. 重新启动 I2S1 TX DMA 和 I2S2 RX DMA
        //dma_memory_address_config(DMA0, DMA_CH0, DMA_MEMORY_0, (uint32_t)i2s_ctm_rx_cache);
        dma_memory_address_config(DMA0, DMA_CH5, DMA_MEMORY_0, (uint32_t)i2s_mtc_tx_cache);
                dma_transfer_number_config(DMA0, DMA_CH5, BUFFER_SIZE);
        dma_channel_enable(DMA0, DMA_CH5);
                dma_channel_enable(DMA0, DMA_CH0);
    }
       
}

使用特权

评论回复
7
dffzh| | 2025-5-23 09:10 | 只看该作者
估计得特别有耐心的朋友才能静下心来看完你的代码

使用特权

评论回复
评论
ko1057900143 2025-5-27 16:09 回复TA
是的呢,面对面几句话,网上迫不得已才贴代码,其实就是我如果TX的BUFF采用初始化的数据,可以测出来波形,但是如果是在中断中停掉TX和RX的DMA,把RX收到的东西拷贝到TX,就没数据。不知道是不是不可以这么拷贝 
8
dffzh| | 2025-5-23 09:23 | 只看该作者
没这方面实操经验,AI一下,不知道有木有参考价值?

使用特权

评论回复
评论
ko1057900143 2025-5-27 16:19 回复TA
I2S是CODEC是主,外挂独立晶振,MCU做的从,声道这些都用的默认的,倒是没细细研究,数据流怕出错就I2S_FRAMEFORMAT_DT16B_CH16B都是16位 
ko1057900143 2025-5-27 16:18 回复TA
CODEC的I2C配置我加了回读,应该问题不大,粘贴的代码很多惯例的初始化。跳着看就行了 
9
xch| | 2025-5-23 09:53 | 只看该作者
检查一下数据格式。

过去用蓝牙外挂CODEC也遇到。结果是搞错格式。

使用特权

评论回复
评论
ko1057900143 2025-5-29 14:48 回复TA
@xch :好的,谢谢答复,我改全双缓冲试一下。感谢 
xch 2025-5-29 11:56 回复TA
@ko1057900143 :是这个意思。GD 的dma 可以级联吗? 级联也可以用。 
ko1057900143 2025-5-29 11:40 回复TA
@xch :GD有这个功能的,按照我那个方式实测确实不太行,发送的数据还是初始化赋值的数据,不是RX拷贝过去的数据,我是监听的RX全完成后的中断。主要是想打印下数据看看。您意思是用双缓冲自动切换缓冲区是么 
xch 2025-5-28 13:15 回复TA
@ko1057900143 :不行。GD 的 DMA 没有自动循环传输和传输半程中断?需要这个功能 
ko1057900143 2025-5-28 09:46 回复TA
@xch :再请教您一个问题,就是DMA传输开始后,进入RX中断后,我使用 dma_channel_disable(DMA0, DMA_CH5);停止DMA TX传输 然后重新配置了传输TX的BUFF的内容,随后enable开始传输,这种方式可行吗? 
ko1057900143 2025-5-28 09:20 回复TA
@xch :好的好的,感谢您的指导 
xch 2025-5-27 17:39 回复TA
@ko1057900143 :我是人造锯齿波数据让它输出周期信号,示波器看波形。再人造直流电数据:16个 bit一个个位分别置一看输出直流电平。 这样就知道格式错在哪里了。 
ko1057900143 2025-5-27 16:11 回复TA
好的好的,我检查了一遍,按照新塘的手册,配置的是标准的I2S,也就是飞利浦,16BIT,8K速率。主要公司小批量生产,还找不到新塘代理 
10
jcky001| | 2025-5-23 16:21 | 只看该作者
I2S和DMA初始化问题?

使用特权

评论回复
11
elephant00| | 2025-5-23 16:22 | 只看该作者
I2S的时钟配置、数据格式与CODEC的要求是否一致。

使用特权

评论回复
12
flycamelaaa| | 2025-5-23 16:24 | 只看该作者
在中断服务函数中需正确清除中断标志位,避免重复进入中断。

使用特权

评论回复
13
classroom| | 2025-5-23 16:26 | 只看该作者
周围信号干扰或噪声?

使用特权

评论回复
评论
ko1057900143 2025-5-27 16:16 回复TA
没有声音出呢。 
14
cr315| | 2025-5-23 18:00 | 只看该作者
I2S的收发模式配置是否正确。

使用特权

评论回复
评论
ko1057900143 2025-5-27 16:15 回复TA
你好,GD32是不是只能配置I2S的收,我看库函数,貌似还有个全双工的RX和TX的翻转的BUG 
15
powerantone| | 2025-5-23 18:03 | 只看该作者
中断优先级配置有问题?

使用特权

评论回复
评论
ko1057900143 2025-5-27 16:15 回复TA
DMA是四个独立的channel,这个之间应该没有竞争关系吧,项目加了FreeRTOS,没啥特别高优先级的中断,也就系统时钟了 
16
stormwind123| | 2025-5-23 19:00 | 只看该作者
在拷贝数据时,DMA通道是否被正确禁用,避免数据搬运冲突。

使用特权

评论回复
评论
ko1057900143 2025-5-27 16:13 回复TA
这个有做,目前测试是在MCU做的回环测试,就是RX收完,把TX的DMA通道dis了,RX也dis了,然后memcpy数据到TX的BUFF,但是这个方式,貌似示波器测不到数据,可是如果我初始化的时候,给TX比如全部写5A,那可以测到,就好像,TX的buff,和发的数据不是一个源一样。 
17
probedog| | 2025-5-23 21:00 | 只看该作者
检查硬件连接是否问题。

使用特权

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

本版积分规则

2

主题

22

帖子

0

粉丝