前言:UART作为一种常见的通讯方式,大部分是单独使用的场景。但是有时候MCU在需要用到UART进行大量数据传输任务的时候,为了避免占用内核CPU的太多资源,就需要使用到了DMA,这可以释放CPU的很多资源,有不影响正常的UART数据传输。本篇文章就基于瑞萨RH850F1KMS1的MCU介绍一下UART使用DMA进行数据传输的整个配置过程。


目录

1,关于DMA

1.1 DMA定义与简介

1.2 RH850F1KMS1中的DMA

2,关于UART

3,UART-DMA模式应用

3.1 参数配置

3.2 应用代码

4,UART-DMA调试效果


1,关于DMA

1.1 DMA定义与简介

先引用一下词条解释:

DMA(Direct Memory Access)控制器是一种在系统内部转移数据的独特外设,可以将其视为一种能够通过一组专用总线将内部和外部存储器与每个具有DMA能力的外设连接起来的控制器。它之所以属于外设,是因为它是在处理器的编程控制下来执行传输的。

通俗来讲呢DMA就是指内存和外设直接存取数据这种内存访问的计算机技术,它可以绕过CPU直接在内存与内存、内存与外设之间实现数据传输与通信。

当然其实在MCU当中就是两个地址之间的数据交互,因为本质来讲内存与外设都是有地址的。

下面的这个图片可以看得更加直观一点

打个比喻就是:CPU就是自己,原来需要我搬运一些东西从A到B,后来我不想自己亲力亲为了,我就雇了一个搬运工帮我去做,而我就告诉搬运工A与B的地址就行了,省下的时间精力我可以做其他的更重要的事情。

而DMA之所以可以代替CPU进行“数据搬运”的原因是:在MCU内部有一组专门的DMA控制器,它能在总线上像CPU一样“读/写”存储器和外设寄存器;CPU只是把“搬运路线”告诉DMA,之后DMA独占总线完成搬运,CPU被解放出来去做运算或控制。

1.2 RH850F1KMS1中的DMA

F1KMS1作为瑞萨的一款经典的车规级的MCU,其有16路DMA

每一路的通道优先级如下,当然F1KMS1只有通道0~15

在实际配置的时候,其有三种模式:单次传输模式、块传输模式1、块传输模式2.

2,关于UART

UART大家应该就相对比较熟悉了,在这里就不在过多的就行介绍,只不过对于RH850F1KMS1而言UART外设跟LIN外设共用一个硬件资源。

对应的UART的位序如下所示

3,UART-DMA模式应用

3.1 参数配置

这里来到最重要的实际应用环节,由于本文是基于RH850F1KMS1,你需要有CS+集成开发环境并安装smart configurator自动生成代码工具,主要的驱动代码就依赖于此工具的自动化的生成。

UART的配置基本上没有需要注意的地方

DMAC00配置,此通道映射到RLIN30UR0,作为UART的数据向外发送端,采用单次数据传输的模式,下面的源地址就是要发送的数据的RAM存储地址,而目标地址是UART的发送数据寄存器的地址。

DMAC01配置,此通道映射到RLIN30UR1,作为UART的数据向内接收端,采用单次数据传输的模式,下面的源地址就是UART的接收数据寄存器的地址,而目标地址是接收的数据的RAM存储地址。

3.2 应用代码

下面是截取的部分应用测试代码,包含了UART数据的发送与接收测试。

main.c

#define _DMAC00_SOURCE_ADDRESS                (0xFEBE0003UL)
#define _DMAC00_DESTINATION_ADDRESS           (0xFFCE2024UL)
#define _DMAC00_TRANSFER_COUNT                (0x00000008UL)


uint8_t txbuf[9] = "HELLO!!!\n";
uint8_t rxbuf[9] = {0};

/***********************************************************************************************************************
* Function Name: main
* Description  : This function implements main function.
* Arguments    : None
* Return Value : None
***********************************************************************************************************************/
void user_DMA_reload(void);
void PEG_set(void);

void main(void)
{
    r_main_userinit();
    /* Start user code for main. Do not edit comment generated here */
    R_Config_OSTM0_Start();

    PEG_set();

    R_Config_UART0_Start();
    R_Config_DMAC00_Start();
    R_Config_DMAC01_Start();

    // close UART receive interrupt in order to testing receive data
    INTC2.ICRLIN30UR1.BIT.MKRLIN30UR1 = _INT_PROCESSING_DISABLED;
    // uart send data trigger
    R_Config_UART0_Send(txbuf, 8);

    while(1)
    {
      if(cnt_10ms_flag)
      {
        cnt_10ms_flag = 0;

        #if UART_DMA_TEST
        // uart receive data
        R_Config_UART0_Receive(rxbuf,8);
        #endif
      }

      if(cnt_100ms_flag)
      {
          cnt_100ms_flag = 0;
          PORT.P11 ^= ((1 << 6) | (1 << 7));
      }
      
      if(cnt_1s_flag)
      {
        cnt_1s_flag = 0;

        // uart send data
        user_DMA_reload();
      }
    }
    /* End user code. Do not edit comment generated here */
}

void user_DMA_reload(void)
{
    PDMA0.DSA0 = _DMAC00_SOURCE_ADDRESS;                          //relode DMA00
    PDMA0.DDA0 = _DMAC00_DESTINATION_ADDRESS;
    PDMA0.DTC0 = _DMAC00_TRANSFER_COUNT;
    PDMA0.DCEN0 = _DMAC_CHANNEL_OPERATION_ENABLED;          //re enable DMA00
}

void PEG_set(void)
{
    PEG.SP.UINT32 = 0x00000001;
    PEG.G0MK.UINT32 = 0xFFFFF000;
    PEG.G0BA.UINT32 = 0xFEBE00FF; /* Base address of PE Guard protection Area (start of Local RAM) */  
}

Config_DMAC00_user.c

/***********************************************************************************************************************
* Function Name: r_Config_DMAC00_dmac00_interrupt
* Description  : This function handles the INTDMA0 interrupt.
* Arguments    : None
* Return Value : None
***********************************************************************************************************************/
#pragma interrupt r_Config_DMAC00_dmac00_interrupt(enable=false, channel=60, fpu=true, callt=false)
void r_Config_DMAC00_dmac00_interrupt(void)
{
    /* Start user code for r_Config_DMAC00_dmac00_interrupt. Do not edit comment generated here */
    if(PDMA0.DCST0&0x00000010)
    {
        PDMA0.DCSTC0 = 0x00000010;                          //clear Transfer completion flag
    }
    if(PDMA0.DCST0&0x00000080)
    {
        PDMA0.DCSTC0 = 0x00000080;                          //clear Transfer error flag
    }
    /* End user code. Do not edit comment generated here */
}

Config_DMAC01_user.c

/***********************************************************************************************************************
* Function Name: r_Config_DMAC01_dmac01_interrupt
* Description  : This function handles the INTDMA1 interrupt.
* Arguments    : None
* Return Value : None
***********************************************************************************************************************/
#pragma interrupt r_Config_DMAC01_dmac01_interrupt(enable=false, channel=61, fpu=true, callt=false)
void r_Config_DMAC01_dmac01_interrupt(void)
{
    /* Start user code for r_Config_DMAC01_dmac01_interrupt. Do not edit comment generated here */
    dma01_done_flg = 1;
    if(PDMA0.DCST1&0x00000010)
    {
        PDMA0.DCSTC1 = 0x00000010;                          //clear Transfer completion flag
    }
    if(PDMA0.DCST1&0x00000080)
    {
        PDMA0.DCSTC1 = 0x00000080;                          //clear Transfer error flag
    }

    PDMA0.DSA1 = _DMAC01_SOURCE_ADDRESS;                    //relode DMA01
    PDMA0.DDA1 = _DMAC01_DESTINATION_ADDRESS;
    PDMA0.DTC1 = _DMAC01_TRANSFER_COUNT;
    PDMA0.DCEN1 = _DMAC_CHANNEL_OPERATION_ENABLED;          //re enable DMA01
    /* End user code. Do not edit comment generated here */
}

4,UART-DMA调试效果

UART的发送的是“HELLO!!!”,串口调试工具接收到的效果如下

从串口调试工具端发送“ABCDE”到MCU端效果如下

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐