串口DMA发送--双BUFF和DMA完成中断--适用于低波特率

一、DMA的配置

这里的一些配置介绍在前两篇介绍串口 DMA发送的时候写过了,可以去看,配置是一样的,这里不多介绍;

static INT  __dmaTxConfig (UART_SIOCHAN  *pUartSioChan)
{
    UINT32  uiCtlReVal;
    UINT32  uiDmaM0Addr;
    UINT32  uiDmaM1Addr;
    UINT32  iDmaTxChan;
    UINT32  uiBase;
    UINT32  uiDmaX       = pUartSioChan->SIOCHAN_pDmaBase;
    UINT32  uiDmaTxPeri  = pUartSioChan->SIOCHAN_iDmaTxPeri;

    uiBase      = pUartSioChan->SIOCHAN_pBase;
    iDmaTxChan  = pUartSioChan->SIOCHAN_iDmaTxChan;

    rcu_periph_clock_enable(RCU_DMA1);
    dma_single_data_parameter_struct  DmaInitStruct;

    dma_deinit(uiDmaX, iDmaTxChan);
    DmaInitStruct.direction    = DMA_MEMORY_TO_PERIPH;
    DmaInitStruct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;            /*  内存自增                    */
    DmaInitStruct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;          /*  存储和外设的传输宽度        */
    DmaInitStruct.number       = DMA_TXBUFF_SIZE;
    DmaInitStruct.periph_addr  = (UINT32)(uiBase + 0x4);
    DmaInitStruct.periph_inc   = DMA_MEMORY_INCREASE_DISABLE;           /*  外设地址固定                */
    DmaInitStruct.priority     = DMA_PRIORITY_ULTRA_HIGH;
    dma_single_data_mode_init(uiDmaX, iDmaTxChan, DmaInitStruct);
    dma_channel_subperipheral_select(uiDmaX, iDmaTxChan, uiDmaTxPeri); /*  外设选择                    */
    dma_flow_controller_config(uiDmaX, iDmaTxChan, DMA_FLOW_CONTROLLER_DMA);

    uiDmaM0Addr = (UINT32)_G_ucTxbuffer1;
    DMA_CHM0ADDR(uiDmaX, iDmaTxChan) = uiDmaM0Addr;                     /*  设置存储器0基地址           */
    uiDmaM1Addr = (UINT32)_G_ucTxbuffer2;
    DMA_CHM1ADDR(uiDmaX, iDmaTxChan) = uiDmaM1Addr;                     /*  设置存储器1基地址           */

    uiCtlReVal = DMA_CHCTL(uiDmaX, iDmaTxChan);
    uiCtlReVal &= ~(1 << 18);                                           /*  不设置存储切换模式          */
    uiCtlReVal &= ~(1 << 19);                                           /*  首先设置存储器0作为存储区域 */
    DMA_CHCTL(uiDmaX, iDmaTxChan) = uiCtlReVal;

    dma_channel_disable(uiDmaX, iDmaTxChan);                            /* disable DMA channel          */

    return  (ERROR_NONE);
}

二、串口发送数据

应用程序调用write函数去主动发送数据时,在驱动中首先会调用txStartUp()函数,此函数是开始通过串口发送数据,使用了DMA传输,首先这个数据就会被转移到DMA BUFF中;等开启DMA 传输,DMA会主动搬运数据到外设,而不经过CPU;

从TTY设备中获取数据放到DMA BUFF,代码如下:
DMA_TXBUFF_SIZE 表示DMA BUFF最多可以放置的字节数;

     while(usart_flag_get(uiBase, USART_FLAG_TC) == RESET);
         {
         uiBuffUsed = dma_using_memory_get(uiDmaX, iDmaTxChan);         /*  双BUFF时判断                */
         while (uiCount < DMA_TXBUFF_SIZE) {
               if (GETTXCHAR(&cChar)) {                                 /*  没有数据可以取出            */
                   break;
               }
               if (uiBuffUsed == DMA_MEMORY_0) {             /*  确定使用的是哪一个BUFF   */
                   _G_ucTxbuffer1[uiCount] = cChar;
                   uiBuffNext = DMA_MEMORY_1;

               } else {
                   _G_ucTxbuffer2[uiCount] = cChar;
                   uiBuffNext = DMA_MEMORY_0;
               }
               ++uiCount;
         }

上面将数据放置到DMA BUFF中结束或者DMA BUFF满了,需要配置DMA 计数器的值和要配置用于DMA传输的BUFF(因为这里是双BUFF):

 dma_interrupt_disable(uiDmaX, iDmaTxChan, DMA_CHXCTL_FTFIE);        /* full transfer finish interrupt enable*/
 dma_channel_disable(uiDmaX, iDmaTxChan);                           /*  通道失能                    */
  _G_TxNextDate = uiCount;
  DMA_CHCNT(uiDmaX, iDmaTxChan) = _G_TxNextDate;//dma要发送的数据多少
  dmaSwitchBuffer(uiDmaX, iDmaTxChan,_G_iTxNextBuff);
  _G_iTxNextBuff = !(_G_iTxNextBuff);

为了提高效率,如果填充完第一个DMA BUFF之后,仍然有数据需要传输,可以继续往第二个DMA BUFF中填充数据;

uiBuffNext 是使用的哪一个DMA BUFF标志;
uiCount 记录了传输到DMA BUFF中的数据大小

       if (uiCount == DMA_TXBUFF_SIZE) {           /*  上一个BUFF被填满          */
             uiCount = 0;

              while (uiCount < DMA_TXBUFF_SIZE) {
                _PrintFormat("func= %s, line = %d\r\n", __func__, __LINE__);
                   if (GETTXCHAR(&cChar)) {
                       break;
                   }
                   if (uiBuffNext == DMA_MEMORY_0) {
                       _PrintFormat("1func= %s, line = %d\r\n", __func__, __LINE__);
                       _G_ucTxbuffer1[uiCount] = cChar;

                   } else {
                       _G_ucTxbuffer2[uiCount] = cChar;
                   }
                   ++uiCount;
             }
             _G_TxNextDate = uiCount;
         }

下面就是开启DMA传输,完成中断使能,应该先使能完成中断,再开启传输;

      dma_interrupt_enable(uiDmaX, iDmaTxChan, DMA_CHXCTL_FTFIE);        /* full transfer finish interrupt enable*/
      dma_channel_enable(uiDmaX, iDmaTxChan);

DMA传输完成:指的是第一个DMA BUFF传输完成,传输完成指的是DMA CNT的值减到0,每通过DMA 传输一个字节,那么CNT就会减一;

三、完成中断的服务函数
第一个BUFF传输完成进入传输完成中断服务函数,在这个函数里面需要开启第二个DAM BUFF的传输;
并完成修改下一个使用DMA BUFF的标志;

    if((RESET != dma_interrupt_flag_get(uiDmaX, iDmaTxChan, DMA_INTF_FTFIF))) {
        UINT32 cnt = REG32(uiDmaX + (0x14 + 0x18 *iDmaTxChan));
        _PrintFormat("func= %s, line = %d,cnt = %d\r\n", __func__, __LINE__, cnt);
        dma_interrupt_flag_clear(uiDmaX, iDmaTxChan, DMA_INTC_FTFIFC);

        if (_G_TxNextDate == 0) {
            dma_channel_disable(uiDmaX, iDmaTxChan);
        }
         uiCount = 0;
         uiBuffUsed = dma_using_memory_get(uiDmaX, iDmaTxChan);          /*  传输完成自动切换            */
        _PrintFormat("func= %s, line = %d, uiBuffUsed = %d\r\n", __func__, __LINE__, uiBuffUsed);
      if (_G_TxNextDate != 0) {
        dma_channel_disable(uiDmaX, iDmaTxChan);                        /*  通道失能传输数据            */
        DMA_CHCNT(uiDmaX, iDmaTxChan) = _G_TxNextDate;                  /*  设置传输长度                */
        dmaSwitchBuffer(uiDmaX, iDmaTxChan,_G_iTxNextBuff);
        _G_iTxNextBuff = !(_G_iTxNextBuff);
        _PrintFormat("func= %s, line = %d,_G_TxNextDate = %d\r\n", __func__, __LINE__, _G_TxNextDate);
         dma_channel_enable(uiDmaX, iDmaTxChan);                         /*  中断开始传输数据            */
        uiBuffUsed = dma_using_memory_get(uiDmaX, iDmaTxChan);          /*  传输完成自动切换            */
       }
      if (_G_TxNextDate == DMA_TXBUFF_SIZE) {
          if (uiBuffUsed == DMA_MEMORY_0) {
              uiBuffSelect = DMA_MEMORY_1;
          } else {
              uiBuffSelect = DMA_MEMORY_0;
          }

          while (uiCount < DMA_TXBUFF_SIZE) {
                if (GETTXCHAR(&cTx)) {
                    _PrintFormat("func= %s, line = %d\r\n", __func__, __LINE__);
                    break;
                }
                if (uiBuffSelect == DMA_MEMORY_0) {
                     _G_ucTxbuffer1[uiCount] = cTx;
                } else {
                     _G_ucTxbuffer2[uiCount] = cTx;
                }
                ++uiCount;
          }

          _G_TxNextDate = uiCount;
      } else {
          _G_TxNextDate = 0;
          dma_interrupt_flag_clear(uiDmaX, iDmaTxChan, DMA_INTC_FTFIFC);
          _PrintFormat("func= %s, line = %d, _G_TxNextDate = %d\r\n", __func__, __LINE__, _G_TxNextDate);
      }
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: gd32f405是一款具有DMA功能的微控制器,可以通过串口与外部设备进行数据通信。串口通信是一种常用的数据传输方式,它通过数据线来传输数据,并且具有较好的适应性和广泛的应用场景。 gd32f405支持使用DMA(直接内存访问)来处理串口数据传输。DMA是一种硬件模块,可以在不需要CPU干预的情况下,直接从内存读取数据并将其传输到外部设备,或将外部设备的数据传输到内存。这样可以减轻CPU的负担,提高数据传输效率。 使用DMA进行串口数据传输有以下几个步骤: 1. 配置DMA通道:首先需要配置DMA通道用于串口数据传输的接口。在gd32f405中,可以使用多个DMA通道进行不同功能的数据传输。 2. 配置串口:需要配置串口相关参数,包括波特率、数据位数、校验位等。gd32f405的串口模块具有多个发送和接收缓冲区,可以通过调用相应的函数对串口进行配置。 3. 配置DMA传输方向和长度:根据具体需求,需要设置DMA的传输方向,即是从内存到外设还是从外设到内存。同时需要设置数据传输的长度。 4. 启动DMA传输:配置完成后,通过调用相应的函数启动DMA传输。DMA将自动从内存读取数据并进行串口传输,或从串口接收数据并写入内存,无需CPU干预。 通过使用DMA进行串口数据传输,可大大提高数据传输效率,减少CPU的负担。gd32f405的DMA功能使得串口通信更加可靠和高效。 ### 回答2: gd32f405是一款单片机芯片,而串口DMA是其提供的两种功能。 串口是一种用于数据传输的接口,通过串行通信的方式可以实现将数据从一个设备传输到另一个设备。gd32f405内置了多个串口通道,可以通过配置相关寄存器和使用相应的引脚来实现与其他设备的串口通信。 而DMA(Direct Memory Access)是一种直接内存访问的技术,可以在不经过CPU的情况下,直接将数据从外设读取到内存或从内存写入到外设。在gd32f405中,通过配置DMA通道的相关寄存器,可以实现串口数据的DMA传输。 具体实现串口-DMA的步骤如下: 1. 配置串口:包括波特率、数据位、停止位、校验位等参数。 2. 配置DMA通道:包括传输方向、数据宽度、传输大小等参数。 3. 配置内存地址和外设地址:分别指定DMA传输的起始地址和目标地址。 4. 使能串口DMA模式和DMA通道。 5. 当需要进行数据传输时,触发DMA传输请求。 6. 当DMA传输完成后,通过相应的中断或标志位进行处理。 通过串口-DMA的方式,可以实现高效的数据传输,减轻CPU负担。此外,gd32f405还支持多个DMA通道,可以同时进行多路数据传输,提高系统的数据处理效率。 总结来说,gd32f405芯片提供了串口DMA功能,可以通过配置相关寄存器和引脚,实现串口数据的高效传输,并且支持多通道的DMA操作,提高系统的性能和效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值