汪阳 · 2023年01月06日 · 山东

【GD32F427开发板试用】03串口DMA接收发送+接收超时中断实现不定长字节接收

使用DMA收发串口数据能提高MCU的处理效率,本来打算使用DMA接收+IDLE中断的,但初步实验了下IDLE中断在实际使用中的问题:1.idle中断空闲只要有1个字节空闲时间就中断,貌似很多M内核芯片都是这样设置的。2.实验GD32F427R发现idle中断只要空闲1个字符就会进入,正常是IDLE中断发生后清除只有接收到一个字符后芯片再自动重新开启idle中断,找了GD32F4的手册也没有发现相关的描述,虽然能通过接收字符判断但中断太频繁了。GD32作为国内M核大厂,希望在软件和手册上还是需要加强。最后不断查找发现有一个接收超时中断,但是接收超时中断只能usart使用。GD32F4为usart\_periph: USARTx(x=0,1,2,5)。并且超时的字节数可以配置,这样使用起来还是挺爽的,基本处理都在中断中,实际使用可以使用接收完成标志在程序中轮训处理收到的数据并进行DMA发送。我使用的是USART2,关键代码如下:

1.串口初始化:
   

 rcu\_periph\_clock\_enable(GD32\_COM2\_CLK);  
    /\* enable USART clock \*/  
    rcu\_periph\_clock\_enable(GD32\_COM2\_GPIO\_CLK);  
     
    /\* connect port to USARTx\_Tx \*/  
    gpio\_af\_set(GD32\_COM2\_GPIO\_PORT, GD32\_COM2\_AF, GD32\_COM2\_TX\_PIN);  
     
    /\* connect port to USARTx\_Rx \*/  
    gpio\_af\_set(GD32\_COM2\_GPIO\_PORT, GD32\_COM2\_AF, GD32\_COM2\_RX\_PIN);  
     
    /\* configure USART Tx as alternate function push-pull \*/  
    gpio\_mode\_set(GD32\_COM2\_GPIO\_PORT, GPIO\_MODE\_AF, GPIO\_PUPD\_PULLUP,GD32\_COM2\_TX\_PIN);  
    gpio\_output\_options\_set(GD32\_COM2\_GPIO\_PORT, GPIO\_OTYPE\_PP, GPIO\_OSPEED\_50MHZ,GD32\_COM2\_TX\_PIN);  
    /\* configure USART Rx as alternate function push-pull \*/  
    gpio\_mode\_set(GD32\_COM2\_GPIO\_PORT, GPIO\_MODE\_AF, GPIO\_PUPD\_PULLUP,GD32\_COM2\_RX\_PIN);  
    gpio\_output\_options\_set(GD32\_COM2\_GPIO\_PORT, GPIO\_OTYPE\_PP, GPIO\_OSPEED\_50MHZ,GD32\_COM2\_RX\_PIN);  
    /\* USART configure \*/  
    usart\_deinit(com);  
    usart\_oversample\_config(com, USART\_OVSMOD\_8);  
    usart\_baudrate\_set(com,baudrate); //波特率  
    usart\_parity\_config(com, USART\_PM\_NONE); //校验位:NONE  
    usart\_word\_length\_set(com, USART\_WL\_8BIT); //数据位:8  
    usart\_stop\_bit\_set(com, USART\_STB\_1BIT); //停止位:1  
    usart\_receive\_config(com, USART\_RECEIVE\_ENABLE);  
    usart\_transmit\_config(com, USART\_TRANSMIT\_ENABLE);  
    //--接收超时设置  
    usart\_receiver\_timeout\_threshold\_config(com,10);//字节数,与波特率无关  
    usart\_interrupt\_enable(com,USART\_INT\_RT);  
    usart\_receiver\_timeout\_enable(com);  
  
    usart\_enable(com);  
  
   usart\_dma\_transmit\_config(com, USART\_DENT\_ENABLE);  // 使能串口DMA发送  
   /\* USART interrupt configuration \*/  
  nvic\_irq\_enable(USART2\_IRQn, 1, 1);  
  
  /\*uart dma rx set\*/  
  dma\_single\_data\_parameter\_struct dma\_init\_struct;  
 /\* enable DMA0 \*/  
  rcu\_periph\_clock\_enable(RCU\_DMA0);  
   /\* deinitialize DMA channel \*/  
  dma\_deinit(DMA0, DMA\_CH1);   
  dma\_init\_struct.direction = DMA\_PERIPH\_TO\_MEMORY;  
  //存储器地址  
   dma\_init\_struct.memory0\_addr = (uint32\_t)rxbuffer;  
    dma\_init\_struct.memory\_inc = DMA\_MEMORY\_INCREASE\_ENABLE;  
    dma\_init\_struct.periph\_memory\_width = DMA\_PERIPH\_WIDTH\_8BIT;  
    dma\_init\_struct.number = 300;  
    dma\_init\_struct.periph\_addr = (uint32\_t)&USART\_DATA(GD32\_COM2);  
    dma\_init\_struct.periph\_inc = DMA\_PERIPH\_INCREASE\_DISABLE;  
    dma\_init\_struct.priority = DMA\_PRIORITY\_MEDIUM;  
    dma\_single\_data\_mode\_init(DMA0, DMA\_CH1, &dma\_init\_struct);  
     
    /\* configure DMA mode \*/  
    // dma\_circulation\_disable(DMA0, DMA\_CH1);  
    dma\_circulation\_enable(DMA0, DMA\_CH1); //循环模式  
    dma\_channel\_subperipheral\_select(DMA0, DMA\_CH1, DMA\_SUBPERI4);  
     
    //放在使能通道前,使能通道后不可写  
    dma\_transfer\_number\_config(DMA0, DMA\_CH1, 300);  
    //使能通道  
    dma\_channel\_enable(DMA0, DMA\_CH1);  
     
    usart\_dma\_receive\_config(USART2, USART\_DENR\_ENABLE);  
  /\* enable DMA1 channel2 transfer complete interrupt \*/  
   dma\_interrupt\_enable(DMA0, DMA\_CH1, DMA\_CHXCTL\_FTFIE);  
   nvic\_irq\_enable(DMA0\_Channel1\_IRQn, 0, 1);  

2.DMA发送
 

void USART\_SendString\_DMA(uint8\_t \* str, uint32\_t len)  
{  
    dma\_single\_data\_parameter\_struct dma\_struct = {0};  
     /\* enable DMA1 \*/  
    rcu\_periph\_clock\_enable(RCU\_DMA0);  
/\* deinitialize DMA channel7(USART2 TX) \*/  
    dma\_deinit(DMA0, DMA\_CH3);  
    dma\_struct.direction = DMA\_MEMORY\_TO\_PERIPH;  // 内存到外设  
    dma\_struct.memory0\_addr = (uint32\_t)str;  // 内存基地址  
    dma\_struct.memory\_inc = DMA\_MEMORY\_INCREASE\_ENABLE;  // 开启内存自增  
    dma\_struct.periph\_memory\_width = DMA\_MEMORY\_WIDTH\_8BIT;  // 内存数据宽度8bit  
    dma\_struct.number = len;  // len个数据  
    dma\_struct.periph\_addr = (uint32\_t)&USART\_DATA(GD32\_COM2);//(uint32\_t)0x40013804;  // 串口缓冲区基地址  
    dma\_struct.periph\_inc = DMA\_PERIPH\_INCREASE\_DISABLE;  // 关闭外设地址自增  
    //dma\_struct.periph\_width = DMA\_PERIPHERAL\_WIDTH\_8BIT;  // 外设数据宽度8bit  
    dma\_struct.priority = DMA\_PRIORITY\_HIGH;  // 优先级高  
    dma\_single\_data\_mode\_init(DMA0, DMA\_CH3, &dma\_struct);  
    dma\_circulation\_disable(DMA0, DMA\_CH3);  // 关闭循环模式  
    dma\_flag\_clear(DMA0, DMA\_CH3, DMA\_FLAG\_FTF);  // 清除DMA传输完成标志位  
    dma\_channel\_subperipheral\_select(DMA0, DMA\_CH3, DMA\_SUBPERI4);  
/\* enable DMA1 channel2 transfer complete interrupt \*/  
    dma\_interrupt\_enable(DMA0, DMA\_CH3, DMA\_CHXCTL\_FTFIE);  
    uart\_state = 0;  
    dma\_channel\_enable(DMA0, DMA\_CH3);  // 使能DMA传输  
}  

 3.DMA发送完成中断

void DMA0\_Channel3\_IRQHandler(void)  
{  
    if(dma\_interrupt\_flag\_get(DMA0, DMA\_CH3, DMA\_INT\_FLAG\_FTF)) {  
        dma\_interrupt\_flag\_clear(DMA0, DMA\_CH3, DMA\_INT\_FLAG\_FTF);  
    uart\_state = 1;  
   dma\_channel\_disable(DMA0, DMA\_CH3);  // 关闭DMA发送传输  
  /\*打开DMA接收传输\*/  
 Receive\_DataStart(GD32\_COM4);  
  
    }  
}  

4.USART接收超时中断

void USART2\_IRQHandler(void)  
{  
    //接收超时中断(也可以使用IDLE中断实现)  
    if(usart\_interrupt\_flag\_get(GD32\_COM4,USART\_INT\_FLAG\_RT))  
    {  
        //清除中断标志  
        usart\_interrupt\_flag\_clear(GD32\_COM4,USART\_INT\_FLAG\_RT);  
        EventStopB(0);//测试中断间隔时间  
        Receive\_DataPack(GD32\_COM4);  
        EventStartB(0);  
    }  
}  

 5.DMA接收完成进行DMA发送

void Receive\_DataPack(uint32\_t com){  
//    dma\_single\_data\_parameter\_struct dma\_init\_struct;  
    /\* 关闭DMA,防止干扰 \*/  
    dma\_channel\_disable(DMA0, DMA\_CH1);     
    /\* 清DMA标志位 \*/  
    dma\_flag\_clear(DMA0, DMA\_CH1, DMA\_INTF\_FTFIF);  
     
    /\* 获取接收到的数据长度,单位:字节 \*/  
    int len = 300 - dma\_transfer\_number\_get(DMA0, DMA\_CH1);  
    if(len > 0)  
    {  
        memcpy(txbuffer,rxbuffer,len);  
        USART\_SendString\_DMA(txbuffer,len);  
    }  
    Receive\_DataStart(GD32\_COM4);  
}  

6.重新开启接收DMA

void Receive\_DataStart(uint32\_t com){  
    dma\_single\_data\_parameter\_struct dma\_init\_struct;  
/\* deinitialize DMA channel \*/  
    dma\_deinit(DMA0, DMA\_CH1);  
    dma\_init\_struct.direction = DMA\_PERIPH\_TO\_MEMORY;  
    //设置存储器地址  
    dma\_init\_struct.memory0\_addr = (uint32\_t)rxbuffer;  
    dma\_init\_struct.memory\_inc = DMA\_MEMORY\_INCREASE\_ENABLE;  
    dma\_init\_struct.periph\_memory\_width = DMA\_PERIPH\_WIDTH\_8BIT;  
    dma\_init\_struct.number = 300;  
    dma\_init\_struct.periph\_addr = (uint32\_t)&USART\_DATA(GD32\_COM2);  
    dma\_init\_struct.periph\_inc = DMA\_PERIPH\_INCREASE\_DISABLE;  
    dma\_init\_struct.priority = DMA\_PRIORITY\_MEDIUM;  
    dma\_single\_data\_mode\_init(DMA0, DMA\_CH1, &dma\_init\_struct);  
     
/\* configure DMA mode \*/  
    // dma\_circulation\_disable(DMA0, DMA\_CH1);  
    dma\_circulation\_enable(DMA0, DMA\_CH1); //循环模式  
    dma\_channel\_subperipheral\_select(DMA0, DMA\_CH1, DMA\_SUBPERI4);  
     
    /\* 重新赋值计数值 \*/  
    dma\_transfer\_number\_config(DMA0, DMA\_CH1, 300);  
     
// dma\_interrupt\_flag\_clear(DMA0, DMA\_CH2, DMA\_INT\_FLAG\_FTF);  
  
    /\* 重新打开DMA \*/  
    dma\_channel\_enable(DMA0, DMA\_CH1);  
   
    usart\_dma\_receive\_config(USART2, USART\_DENR\_ENABLE);  
/\* enable DMA1 channel2 transfer complete interrupt \*/  
    dma\_interrupt\_enable(DMA0, DMA\_CH1, DMA\_CHXCTL\_FTFIE);  
nvic\_irq\_enable(DMA0\_Channel1\_IRQn, 0, 1);  
}
推荐阅读
关注数
10693
内容数
187
中国高性能通用微控制器领域的领跑者兆易创新GD系列芯片技术专栏。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息