打印

GD32E230一上电,就进入DMA中断,求高人指导

[复制链接]
550|22
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 huaimengzi 于 2025-6-10 23:12 编辑

单片机为GD32E230C8T6, 利用IDLE空闲中断+DMA接收,利用DMA发送及DMA发送完成后产生中断,在中断中将RS485切换方向。原本是单片机收到数据后,将数据原样发回给串口助手,但程序一上电,就进入到了DMA中断去了,求各位大神帮忙指导一下原因。

/************************************************************************************************
**文  件  名:Uart0_GPIO_Config
**说      明:串口的GPIO配置
************************************************************************************************/
static void Uart0_GPIO_Config(void)
{
    /*1.开启Usart0对应的GPIO的时钟*/
    rcu_periph_clock_enable(Uart0_Info.RcuGpio);

    /*2.连接端口到USART的引脚*/
    gpio_af_set(Uart0_Info.gpio, BSP_USART0_AF, Uart0_Info.TxPin);
    gpio_af_set(Uart0_Info.gpio, BSP_USART0_AF, Uart0_Info.RxPin);

    /*3.配置TX,RX两个引脚为复用模式,配置为上拉模式*/
    gpio_mode_set(Uart0_Info.gpio, GPIO_MODE_AF, GPIO_PUPD_PULLUP, Uart0_Info.TxPin);
    gpio_mode_set(Uart0_Info.gpio, GPIO_MODE_AF, GPIO_PUPD_PULLUP, Uart0_Info.RxPin);

    /*4.配置TX,RX为推挽输出,速度最大为10MHZ*/
    gpio_output_options_set(Uart0_Info.gpio, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, Uart0_Info.TxPin);
    gpio_output_options_set(Uart0_Info.gpio, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, Uart0_Info.RxPin);

    /*5.配置RS485的Enable引脚*/
    rcu_periph_clock_enable(BSP_RS485EN0_RCU);                                                     //开启时钟
    gpio_mode_set(BSP_RS485EN0_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, BSP_RS485EN0_PIN);          //输出,浮空
    gpio_output_options_set(BSP_RS485EN0_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, BSP_RS485EN0_PIN);//配置为推挽输出
}

/************************************************************************************************
**文  件  名:Uart0_Comm_Config
**说      明:串口配置
************************************************************************************************/
static void Uart0_Comm_Config(uint32_t BaudRate)
{
    /*1.开启USART的时钟*/
    rcu_periph_clock_enable(Uart0_Info.RcuUart);

    /*2.配置串口参数*/
    usart_deinit(Uart0_Info.UartNumber);                                                                //复位串口
    usart_baudrate_set(Uart0_Info.UartNumber, BaudRate);                                        //设置波特率
    usart_word_length_set(Uart0_Info.UartNumber, USART_WL_8BIT);                        //配置数据位的长度8
    usart_stop_bit_set(Uart0_Info.UartNumber, USART_STB_1BIT);                        //停止位1位
    usart_parity_config(Uart0_Info.UartNumber, USART_PM_NONE);                        //设置无校验
    usart_transmit_config(Uart0_Info.UartNumber, USART_TRANSMIT_ENABLE);        //使能发送
    usart_receive_config(Uart0_Info.UartNumber, USART_RECEIVE_ENABLE);        //使能接收

    /*3.使能串口*/
    usart_enable(Uart0_Info.UartNumber);

    /*4.配置为UART0接收空闲中断*/
    //4.1 开启内核串口中断
    nvic_irq_enable(Uart0_Info.irq, 0);
    //4.2 开启外设接收空闲中断
    usart_interrupt_enable(Uart0_Info.UartNumber, USART_INT_IDLE);
}

/*********************************************************************************************************
* 函数名称:static void DMA_USART0_Init(void)
* 函数功能:采用空闲中断+DMA接收,采用DMA发送
*********************************************************************************************************/
static void DMA_USART0_Init(void)
{
    /*0.定义DMA结构体*/
    dma_parameter_struct dmaStruct;

    /*1.使能DMA时钟;*/
    rcu_periph_clock_enable(Uart0_Info.Rcu_DMA);

    /******************************以下DMA_USART0_TX配置******************************/
    /*2.复位DMA的TX发送通道和复位结构体;*/
    dma_deinit(Uart0_Info.DMA_CH_TX);
    dma_struct_para_init(&dmaStruct);

    /*3. 为结构体赋值*/
    dmaStruct.direction = DMA_MEMORY_TO_PERIPHERAL;
    dmaStruct.memory_addr = (uint32_t)Uart0_Comm.SendBuff;
    dmaStruct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dmaStruct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dmaStruct.number = TXD0_BUFF_SIZE;
    dmaStruct.periph_addr = USART0_TDATA_ADDRESS;
    dmaStruct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dmaStruct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dmaStruct.priority = DMA_PRIORITY_HIGH;
    /*4. 把结构体变量的值注入DMA的TX通道参数中*/
    dma_init(Uart0_Info.DMA_CH_TX, &dmaStruct);

    /*5. configure DMA mode */
    dma_circulation_disable(Uart0_Info.DMA_CH_TX);
    dma_memory_to_memory_disable(Uart0_Info.DMA_CH_TX);
   /*开启内部中断*/
    nvic_irq_enable(DMA_Channel1_2_IRQn ,0);
    /*6. USART DMA enable for transmission */
    usart_dma_transmit_config(Uart0_Info.UartNumber, USART_DENT_ENABLE);
    /* enable DMA channel1 transfer complete interrupt */ //开启DMA发送完成中断
    dma_interrupt_enable(Uart0_Info.DMA_CH_TX, DMA_INT_FTF);
    /*7. enable DMA channel1 */
    dma_channel_enable(Uart0_Info.DMA_CH_TX);

    /******************************以下DMA_USART0_RX配置******************************/
    /*2.复位DMA的RX接收通道;*/
    dma_deinit(Uart0_Info.DMA_CH_RX);
    /*3.0 将DMA结构体中所有参数初始化为默认值*/
    dma_struct_para_init(&dmaStruct);


    /*3.1配置传输方向;*/
    dmaStruct.direction = DMA_PERIPHERAL_TO_MEMORY;
    /*3.2配置数据源地址;*/
    dmaStruct.periph_addr = USART0_RDATA_ADDRESS;
    /*3.3配置源地址是固定的还是增长的;*/
    dmaStruct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    /*3.4配置源数据传输位宽;*/
    dmaStruct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    /*3.5配置数据目的首地址;*/
    dmaStruct.memory_addr = (uint32_t)Uart0_Comm.RecBuff_A;
    /*3.6配置目的地址是固定的还是增长的;*/
    dmaStruct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    /*3.7配置目的数据传输位宽;*/
    dmaStruct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    /*3.8配置数据传输最大次数;*/
    dmaStruct.number = RXD0_BUFF_SIZE;
    /*3.9配置DMA通道优先级;DMA_PRIORITY_HIGH 改为最高ultra*/
    dmaStruct.priority = DMA_PRIORITY_ULTRA_HIGH;

    /*3.10 配置完后进行初始化*/
    dma_init(Uart0_Info.DMA_CH_RX, &dmaStruct);


    /*4.禁止DMA接收循环模式*/
    dma_circulation_disable(Uart0_Info.DMA_CH_RX);
    /*5.禁止DMA 内存到内存模式*/
    dma_memory_to_memory_disable(Uart0_Info.DMA_CH_RX);

    /*6.使能DMA的RX通道;*/
    dma_channel_enable(Uart0_Info.DMA_CH_RX);
    /*7. 使能UART接收数据使用DMA;*/
    usart_dma_receive_config(Uart0_Info.UartNumber, USART_DENR_ENABLE);
}


/************************************************************************************************
**文  件  名:void Uart0_Init(uint32_t BaudRate)
**说      明:初始化串口
************************************************************************************************/
void Uart0_Init(uint32_t BaudRate)
{
    Uart0_GPIO_Config();
    Uart0_Comm_Config(BaudRate);
    DMA_USART0_Init();
    DIR_RX0();                  //初始状态为接收
}


/************************************************************************************************
**文  件  名:DMA_Channel1_2_IRQHandler
**说      明:DMA发送完成中断服务函数
**作      用:主要用来给RS485切换方向:发送完成后,切换成接收模式
************************************************************************************************/
void DMA_Channel1_2_IRQHandler(void)
{
    if (dma_interrupt_flag_get(Uart0_Info.DMA_CH_TX, DMA_INT_FLAG_FTF))          /*如果DMA发送完成标志置位*/
    {
        while (!usart_flag_get(Uart0_Info.UartNumber, USART_FLAG_TC) );                // 等待USART发送移位寄存器空(确保最后一字节已发出)
        DIR_RX0();                                                                                                       /* 发送完成后,切换RS485为接收模式*/
        dma_interrupt_flag_clear(Uart0_Info.DMA_CH_TX, DMA_INT_FLAG_FTF);       // 清除DMA中断标志
    }
}

/************************************************************************************************
**文  件  名:void USART0_IRQHandler(void)
**说      明:UART0接收空闲中断 即IDLE
************************************************************************************************/
void USART0_IRQHandler(void)
{
    /*1.判断是不是IDLE产生的中断:如果空闲中断标志位置1*/
    if(RESET != usart_interrupt_flag_get(Uart0_Info.UartNumber, USART_INT_FLAG_IDLE))
    {
        /*2. 清除中断标志位*/
        usart_interrupt_flag_clear(Uart0_Info.UartNumber, USART_INT_FLAG_IDLE);
        /*3. 读取收到的数据长度: 实际接收到的数据长度 = 接收缓冲区大小 - DMA没有传输的数量*/
        Uart0_Comm.RecvBytes =  RXD0_BUFF_SIZE - dma_transfer_number_get(Uart0_Info.DMA_CH_RX);
        /*4.切换缓冲区*/
        if(Uart0_Comm.Current_Rec_Buff == Uart0_Comm.RecBuff_A)      //数据当前在RXD0_Buff_A中
        {
            Uart0_Comm.Current_Rec_Buff = Uart0_Comm.RecBuff_B;         //切换成B缓冲区
        }
        else
        {
            Uart0_Comm.Current_Rec_Buff = Uart0_Comm.RecBuff_A;         //如果为B就切换成A
        }

        /*5. 重启DMA指向另一缓冲区,对DMA搬运次数重新赋值,防止搬运次数用完,就不搬运了*/
        dma_channel_disable(Uart0_Info.DMA_CH_RX);                                          //设置之前需要关闭通道,否则不生效
        dma_interrupt_flag_clear(Uart0_Info.DMA_CH_RX,DMA_INT_FLAG_FTF);     //新增清除DMA接收完成标志
        dma_memory_address_config(Uart0_Info.DMA_CH_RX, (uint32_t)Uart0_Comm.Current_Rec_Buff);
        dma_transfer_number_config(Uart0_Info.DMA_CH_RX, RXD0_BUFF_SIZE);  //重新设置搬运次数
        dma_channel_enable(Uart0_Info.DMA_CH_RX);                                          //设置完成后再打开
        /*6.一帧标志位置位*/
        Uart0_Comm.Frame_Rec_Flag = true;
        //此时数据在非Current_Buff 中,数据长度为data_len,通过这个标志在主程序中处理即可
    }
}

/***********************************************************/
int main(void)
{
    /*串口0初始化*/
    Uart0_Init(BSP_BAUD0);                //波特率为115200u
    while(1)
    {


     /*判断是否收到一帧数据,否则直接返回*/
    if(Uart0_Comm.Frame_Rec_Flag == true)
    {
           Uar0_Process_Data();
           Uart0_Comm.Frame_Rec_Flag =false;
    }

    }
    return 0;
}

使用特权

评论回复
评论
xch 2025-6-11 11:13 回复TA
串口初始化时不允许使能 : usart_transmit_config(Uart0_Info.UartNumber, USART_TRANSMIT_ENABLE); //使能发送 。允许了发送就会申请DMA 喂它数据 
沙发
jobszheng| | 2025-6-10 22:48 | 只看该作者
我看着代码挺像是 就是发送完成了。人家进入的! 在init函数里面,只配置。等到在要发送的时候再enable DMA

使用特权

评论回复
板凳
guijial511| | 2025-6-11 08:07 | 只看该作者
接收空闲中断开启情况下,就是一运行就会进入DMA中断把。

使用特权

评论回复
地板
zjsx8192| | 2025-6-11 08:07 | 只看该作者
要不就是芯片挂了

使用特权

评论回复
5
huaimengzi|  楼主 | 2025-6-11 09:41 | 只看该作者
guijial511 发表于 2025-6-11 08:07
接收空闲中断开启情况下,就是一运行就会进入DMA中断把。

接受空闲中断指的是UART中断,跟DMA没关系吧。

使用特权

评论回复
6
huaimengzi|  楼主 | 2025-6-11 09:45 | 只看该作者
zjsx8192 发表于 2025-6-11 08:07
要不就是芯片挂了

没有挂,不开启DMA发送完成NIVC中断,程序只能原路回传一次数据给串口助手,我猜测进入不了中断,485没切换方向,导致串口助手再发送,单片机无法接收。

使用特权

评论回复
7
huaimengzi|  楼主 | 2025-6-11 09:46 | 只看该作者
jobszheng 发表于 2025-6-10 22:48
我看着代码挺像是 就是发送完成了。人家进入的! 在init函数里面,只配置。等到在要发送的时候再enable DMA ...

串口助手收不到单片机数据

使用特权

评论回复
8
qinlu123| | 2025-6-11 11:09 | 只看该作者
guijial511 发表于 2025-6-11 08:07
接收空闲中断开启情况下,就是一运行就会进入DMA中断把。

接收空闲中断只有在串口收着收着突然收不到了才会触发,如果一开始就收不到是不会触发的。

使用特权

评论回复
9
qinlu123| | 2025-6-11 11:11 | 只看该作者
本帖最后由 qinlu123 于 2025-6-11 11:18 编辑

你的485切换方向要放到串口发送完成中断里去,而不是放到DMA中断里去。DMA中断里什么东西都不放。

使用特权

评论回复
10
qinlu123| | 2025-6-11 11:17 | 只看该作者

使用特权

评论回复
11
qinlu123| | 2025-6-11 11:26 | 只看该作者
看stm32的代码,串口空闲中断、发送完成中断、DMA发送Harf完成中断、接收中断都是在USARTx_IRQHandler()里处理的,和DMA中断没关系。

使用特权

评论回复
评论
qinlu123 2025-6-11 13:11 回复TA
是我搞错了,HAL库是在DMA发送完成后在DMA中断里操作了串口寄存器继而触发串口中断。 
12
dffzh| | 2025-6-11 15:19 | 只看该作者
本帖最后由 dffzh 于 2025-6-11 15:21 编辑

1、软件仿真,在main初始化代码位置打个断点后,单步调试,同时打开寄存器查看窗口,看DMA的中断标志寄存器的FTF位是什么时候变成1的。因为标志位缺省值是0,肯定是运行了哪里的代码后标志位变成1了,就进入DMA发送完成中断了,尝试往回分析的方法。


2、与你提的问题倒是无关;
全局标志位Uart0_Comm.Frame_Rec_Flag别忘记加volatile关键字声明一下,否则后面可能会出现偶发性Bug。

使用特权

评论回复
评论
huaimengzi 2025-6-12 20:20 回复TA
@dffzh :好无奈,网上也找不到相关资料 
huaimengzi 2025-6-12 20:15 回复TA
@dffzh :仍没有找到原因 
dffzh 2025-6-12 13:24 回复TA
@huaimengzi :怎么样了?找到原因没? 
dffzh 2025-6-11 16:35 回复TA
@huaimengzi :不客气,有进展或者还需要帮忙,直接回复。 
huaimengzi 2025-6-11 16:29 回复TA
好的,我试一下,非常感谢 
13
怀揣少年梦| | 2025-6-11 16:50 | 只看该作者
中断使能放在所有外设初始化之后

使用特权

评论回复
14
丙丁先生| | 2025-6-12 13:29 | 只看该作者
‘中断使能放在所有外设初始化之后’ 感觉也是这样,

使用特权

评论回复
15
丙丁先生| | 2025-6-12 13:36 | 只看该作者
[研电赛技术支持] 感觉很有道理,按这个排除吧,GD32E230一上电,就进入DMA中断,求高人指导  https://bbs.21ic.com/icview-3461130-1-1.html

使用特权

评论回复
16
丙丁先生| | 2025-6-12 13:43 | 只看该作者
dma_interrupt_flag_clear(Uart0_Info.DMA_CH_TX, DMA_INT_FLAG_FTF);安排在哪里?  https://bbs.21ic.com/icview-3461132-1-1.html

使用特权

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

本版积分规则

6

主题

23

帖子

0

粉丝