ARM技术论坛
直播中

王银喜

7年用户 2410经验值
私信 关注
[经验]

串口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);  
}

原作者:兆易创新GD32 MCU 汪阳

更多回帖

发帖
×
20
完善资料,
赚取积分