查看: 1363|回复: 0
收起左侧

教你玩转[18]_RVSTAR—DMA数据传输篇

[复制链接]

  离线 

  • TA的每日心情
    慵懒
    2021-7-23 17:16
  • 签到天数: 17 天

    [LV.4]

    发表于 2021-6-9 19:06:18 | 显示全部楼层 |阅读模式

    有人预言,RISC-V或将是继Intel和Arm之后的第三大主流处理器体系。欢迎访问全球首家只专注于RISC-V单片机行业应用的中文网站

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    本帖最后由 草帽王子 于 2021-6-9 19:30 编辑

    在之前的内容里,我们使用过UART、SPI、I2C等接口进行过数据传输,在处理通信数据时,几乎都是在主程序或中断服务程序中进行数据的转存,这样耗费了大量的CPU时间。幸运的是,微控制器的设计者也考虑到这个问题,设计出了DMA(Direct Memory Access,直接存储器访问)传输功能,使得数据可以从一个地址空间复制到另一个地址空间,而不经过CPU,从而让CPU专注在其他功能上。本期内容我们以UART的DMA传输为例,简单介绍DMA的应用方法。

    • 系统环境: Windows 10-64bit
    • 软件平台: NucleiStudio IDE 202102版 或 PlatformIO IDE 、CoolTer
    • 硬件需求:RV-STAR开发板 、TTL-USB串口转换

    一、DMA(直接内存访问)原理

    GD32VF 单片机芯片及应用-教你玩转[18]_RVSTAR—DMA数据传输篇risc-v单片机中文社区(1)

    DMA,全称Direct Memory Access,即直接存储器访问。DMA传输将数据从一个地址空间复制到另外一个地址空间。传输动作的初始化由CPU完成,而传输动作本身由DMA控制器来实行。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。DMA的方式并没有让处理器工作拖延,反而可以去处理其他的工作。DMA对于高效能嵌入式系统算法和网络传输是很重要的。

    在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。


    二、GD32VF103的DMA控制器

    GD32VF103的DMA控制器有12个通道(DMA0有7个通道,DMA1有5个通道)。每个通道都是专门用来处理一个或多个外设的存储器访问请求的。DMA控制器内部实现了一个仲裁器,用来仲裁多个DMA请求的优先级。

    DMA控制器和RISC-V内核共享系统总线,当DMA和CPU访问同样的地址空间时,DMA访问可能会阻挡CPU访问系统总线几个总线周期。总线矩阵中实现了循环仲裁算法来分配DMA与CPU的访问权,它可以确保CPU得到至少一半的系统总线带宽。
    GD32VF 单片机芯片及应用-教你玩转[18]_RVSTAR—DMA数据传输篇risc-v单片机中文社区(2)

    主要特征如下
    • 传输数据长度可编程配置,最大到65536
    • 12个通道,并且每个通道都可配置(DMA0有7个通道,DMA1有5个通道)
    • AHB和APB外设,片上闪存和SRAM都可以作为访问的源端和目的端
    • 每个通道连接固定的硬件DMA请求
    • 支持软件优先级(低、中、高、极高)和硬件优先级(通道号越低,优先级越高)
    • 存储器和外设的数据传输宽度可配置:字节,半字,字
    • 存储器和外设的数据传输支持固定寻址和增量式寻址
    • 支持循环传输模式
    • 支持外设到存储器,存储器到外设,存储器到存储器的数据传输
    • 每个通道有3种类型的事件标志和独立的中断
    • 支持中断的使能和清除


    三、实验部分

    原理部分已经介绍过,DMA的工作过程比较简单,开发起来也比较容易,用户只需要结合数据手册,明确微控制器DMA通道和外设间对应关系,明确DMA的源地址、目的地址、数据宽度等信息,在开发时对DMA控制器和外设进行相应的使能和配置即可。

    由于RV-STAR的USB串口(UART4)不支持DMA功能,本次的实验使用UART3进行:首先使用串口的DMA发送功能,让数据不经CPU直接从内存(txbuffer)传输到串口的发送端,然后使用串口的DMA接收功能接收10个字节的数据,保存到rxbuffer中,最后在主循环中(使用CPU)将rxbuffer接收到的数据再通过串口发送出去。
    1. #include "nuclei_sdk_hal.h"

    2. uint8_t rxbuffer[10];
    3. uint8_t txbuffer[] = "\nUART DMA receive and transmit example, please input 10 bytes:\n";
    4. #define ARRAYNUM(arr_name) (uint32_t)(sizeof(arr_name) / sizeof(*(arr_name)))

    5. void uart_init();
    6. void uart_send(int ch);

    7. int main()
    8. {
    9.     dma_parameter_struct dma_init_struct;
    10.     /* enable DMA1 */
    11.     rcu_periph_clock_enable(RCU_DMA1);
    12.     /* initialize UART3 */
    13.     uart_init();

    14.     /* deinitialize DMA Channel4(UART3_TX) */
    15.     dma_deinit(DMA1, DMA_CH4);
    16.     dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
    17.     dma_init_struct.memory_addr = (uint32_t)txbuffer;
    18.     dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    19.     dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    20.     dma_init_struct.number = ARRAYNUM(txbuffer) - 1;
    21.     dma_init_struct.periph_addr = (uint32_t)&USART_DATA(UART3);
    22.     dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    23.     dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    24.     dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    25.     dma_init(DMA1, DMA_CH4, &dma_init_struct);
    26.     /* configure DMA mode */
    27.     dma_circulation_disable(DMA1, DMA_CH4);
    28.     /* enable DMA channel3 */
    29.     dma_channel_enable(DMA1, DMA_CH4);

    30.     /* USART DMA enable for transmission and reception */
    31.     usart_dma_transmit_config(UART3, USART_DENT_ENABLE);
    32.     usart_dma_receive_config(UART3, USART_DENR_ENABLE);

    33.     /* wait DMA Channel transfer complete */
    34.     while (RESET == dma_flag_get(DMA1, DMA_CH4, DMA_FLAG_FTF));
    35.     while (1) {
    36.         /* deinitialize DMA1 Channel2 (UART3_RX) */
    37.         dma_deinit(DMA1, DMA_CH2);
    38.         dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
    39.         dma_init_struct.memory_addr = (uint32_t)rxbuffer;
    40.         dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    41.         dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    42.         dma_init_struct.number = 10;
    43.         dma_init_struct.periph_addr = (uint32_t)&USART_DATA(UART3);
    44.         dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    45.         dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    46.         dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    47.         dma_init(DMA1, DMA_CH2, &dma_init_struct);
    48.         /* configure DMA mode */
    49.         dma_circulation_disable(DMA1, DMA_CH2);
    50.         /* enable DMA channel4 */
    51.         dma_channel_enable(DMA1, DMA_CH2);

    52.         /* wait DMA channel transfer complete */
    53.         while (RESET == dma_flag_get(DMA1, DMA_CH2, DMA_FLAG_FTF));

    54.         for (int i = 0; i < 10; i++) {
    55.             uart_send(rxbuffer[i]);
    56.         }
    57.         uart_send('\n');
    58.     }
    59. }
    复制代码

    代码结构比较清晰,读者们可以自行阅读消化(包含uart_init()和uart_send()实现的完整代码工程,请参考文末附带链接)。

    代码编写完成后,需要编译并上传到开发板,然后需要使用一个类似下图所示的TTL-USB串口转换器,将RV-STAR的UART3串口通过它连接到电脑上,其中:UART3_TX(PC10)连接到转换器的RX引脚上,UART3_RX(PC11)连接到转换器的TX引脚上,然后连接两个开发板的GND地线即可。
    GD32VF 单片机芯片及应用-教你玩转[18]_RVSTAR—DMA数据传输篇risc-v单片机中文社区(3)

    连接完成后,发现设备管理器上除了RV-STAR的默认串口(UART4)外,增加了一个端口,然后使用CoolTerm串口工具连接这个串口,按下RV-STAR上的复位键,发现终端中打印出了从txbuffer经DMA传输到UART发送端的数据。
    GD32VF 单片机芯片及应用-教你玩转[18]_RVSTAR—DMA数据传输篇risc-v单片机中文社区(4)

    然后使用CoolTerm的发送字符串功能,一次性发送10个字节的数据,发现终端上返回了刚刚发送的10字节数据,说明数据从串口输入经DMA存入到内存(rxbuffer)又被正常发送到了串口,实验完成。


    GD32VF 单片机芯片及应用-教你玩转[18]_RVSTAR—DMA数据传输篇risc-v单片机中文社区(5)

    本次实验源码:https://github.com/Nuclei-Softwa ... ar/dma/dma_ram_uart






    上一篇:教你玩转[17]_RVSTAR—正交编码器接口篇
    下一篇:WAIC—RIOS执行主任谭章熹:RISC-V从开源芯片制造、EDA到处理器
    RISCV作者优文
    全球首家只专注于RISC-V单片机行业应用的中文网站
    回复

    使用道具 举报

    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    关闭

    RISC-V单片机中文网上一条 /2 下一条



    版权及免责声明|RISC-V单片机中文网 |网站地图

    GMT+8, 2024-4-19 09:43 , Processed in 0.751338 second(s), 48 queries .

    快速回复 返回顶部 返回列表