HPM6750开发笔记《DMA接收和发送数据UART例程深度解析》

news2024/10/2 12:26:07

目录

概述:

端口设置:

代码分析:

运行现象:


概述:

DMA(Direct Memory Access)是一种计算机系统中的数据传输技术,它允许数据在不经过中央处理器(CPU)的直接控制下在内存和外设之间传输。UART(Universal Asynchronous Receiver/Transmitter)是一种串行通信协议,用于在设备之间传输数据。

在DMA接收和发送数据的情况下,DMA可以用于管理UART通信中的数据传输。具体来说:

  1. DMA接收数据:

    • 当UART接收到数据时,通常会触发中断来通知CPU。
    • 使用DMA时,DMA控制器可以直接从UART接收缓冲区中读取数据,并将其存储到内存中,而无需CPU的干预。
    • 这允许在数据到达时实现高效的数据传输,减轻了CPU的负担。
  2. DMA发送数据:

    • 当需要通过UART发送数据时,通常需要将数据写入UART的发送缓冲区,并等待发送完成。
    • 使用DMA时,DMA控制器可以直接从内存中获取要发送的数据,并将其传输到UART发送缓冲区,而无需CPU的干预。
    • 这提高了数据传输的效率,因为CPU可以继续执行其他任务而无需等待数据发送完成。

总的来说,DMA在UART通信中的应用可以提高数据传输的效率,减少对CPU的依赖,使系统能够更有效地处理数据。

端口设置:

波特率115200        1位停止位        无奇偶校验

代码分析:

包含了一些头文件,包含了与底层硬件和外设驱动相关的定义和函数声明

义了一些与UART相关的宏,如UART的基地址、时钟名、DMA请求等

声明了两个全局变量 uart_tx_dma_doneuart_rx_dma_done,用于表示UART的发送和接收DMA是否完成

#include "board.h"
#include "hpm_clock_drv.h"
#include "hpm_uart_drv.h"
#ifdef CONFIG_HAS_HPMSDK_DMAV2
#include "hpm_dmav2_drv.h"
#else
#include "hpm_dma_drv.h"
#endif
#include "hpm_dmamux_drv.h"
#include "hpm_l1c_drv.h"
#include "hpm_common.h"

#define TEST_UART                   BOARD_APP_UART_BASE
#define TEST_UART_CLK_NAME          BOARD_APP_UART_CLK_NAME
#define TEST_UART_TX_DMA_REQ        BOARD_APP_UART_TX_DMA_REQ
#define TEST_UART_RX_DMA_REQ        BOARD_APP_UART_RX_DMA_REQ

#define TEST_UART_DMA_CONTROLLER    BOARD_APP_HDMA
#define TEST_UART_DMAMUX_CONTROLLER BOARD_APP_DMAMUX
#define TEST_UART_TX_DMA_CHN        (0U)
#define TEST_UART_RX_DMA_CHN        (1U)
#define TEST_UART_TX_DMAMUX_CHN     DMA_SOC_CHN_TO_DMAMUX_CHN(TEST_UART_DMA_CONTROLLER, TEST_UART_TX_DMA_CHN)
#define TEST_UART_RX_DMAMUX_CHN     DMA_SOC_CHN_TO_DMAMUX_CHN(TEST_UART_DMA_CONTROLLER, TEST_UART_RX_DMA_CHN)
#define TEST_UART_DMA_IRQ           BOARD_APP_HDMA_IRQ

#define TEST_BUFFER_SIZE   (16U)
ATTR_PLACE_AT_NONCACHEABLE uint8_t uart_buff[TEST_BUFFER_SIZE];

volatile bool uart_tx_dma_done;
volatile bool uart_rx_dma_done;

hpm_stat_t uart_tx_trigger_dma(DMA_Type *dma_ptr,
                    uint8_t ch_num,
                    UART_Type *uart_ptr,
                    uint32_t src,
                    uint32_t size)
{
    dma_handshake_config_t config;

    dma_default_handshake_config(dma_ptr, &config);
    config.ch_index = ch_num;
    config.dst = (uint32_t)&uart_ptr->THR;
    config.dst_fixed = true;
    config.src = src;
    config.src_fixed = false;
    config.data_width = DMA_TRANSFER_WIDTH_BYTE;
    config.size_in_byte = size;

    return dma_setup_handshake(dma_ptr, &config, true);
}
  • 参数说明:

    • dma_ptr: DMA控制器的指针,指向用于配置和控制DMA的硬件寄存器。
    • ch_num: DMA通道号,表示要配置的DMA通道。
    • uart_ptr: UART控制器的指针,指向用于配置和控制UART的硬件寄存器。
    • src: 数据源的地址,这是UART发送数据的来源。
    • size: 要传输的数据大小,以字节为单位。
  • 函数逻辑:

    1. 创建一个 dma_handshake_config_t 类型的结构体变量 config,用于配置DMA的握手参数。
    2. 调用 dma_default_handshake_config 函数初始化 config 结构体,设置了一些默认的DMA握手参数。
    3. 设置 config 结构体的各个成员:
      • ch_index: DMA通道号。
      • dst: 目的地地址,这里是UART的传输保持寄存器(THR - Transmitter Holding Register)的地址。
      • dst_fixed: 目的地地址是否固定,这里设置为 true,表示目的地地址不变。
      • src: 数据源地址,即要发送的数据的地址。
      • src_fixed: 数据源地址是否固定,这里设置为 false,表示数据源地址可能变化。
      • data_width: 数据传输宽度,这里设置为字节宽度。
      • size_in_byte: 要传输的数据大小。
  • 返回值:

    • 调用 dma_setup_handshake 函数,根据配置好的参数设置DMA握手,并返回相应的状态。
  • 注意事项:

    • 该函数通过设置DMA的握手参数,将UART的发送数据配置到DMA通道中,并返回相应的状态,用于后续判断是否配置成功。

这个函数的作用是通过DMA实现UART的发送数据,配置了DMA握手参数,确保数据正确地传输到UART传输保持寄存器中。


hpm_stat_t uart_rx_trigger_dma(DMA_Type *dma_ptr,
                    uint8_t ch_num,
                    UART_Type *uart_ptr,
                    uint32_t dst,
                    uint32_t size)
{
    dma_handshake_config_t config;

    dma_default_handshake_config(dma_ptr, &config);
    config.ch_index = ch_num;
    config.dst = dst;
    config.dst_fixed = false;
    config.src = (uint32_t)&uart_ptr->RBR;
    config.src_fixed = true;
    config.data_width = DMA_TRANSFER_WIDTH_BYTE;
    config.size_in_byte = size;

    return dma_setup_handshake(dma_ptr, &config, true);
}
  • 参数说明:

    • dma_ptr: DMA控制器的指针,指向用于配置和控制DMA的硬件寄存器。
    • ch_num: DMA通道号,表示要配置的DMA通道。
    • uart_ptr: UART控制器的指针,指向用于配置和控制UART的硬件寄存器。
    • dst: 数据目的地的地址,这是用于存储UART接收数据的缓冲区的地址。
    • size: 要传输的数据大小,以字节为单位。
  • 函数逻辑:

    1. 创建一个 dma_handshake_config_t 类型的结构体变量 config,用于配置DMA的握手参数。
    2. 调用 dma_default_handshake_config 函数初始化 config 结构体,设置了一些默认的DMA握手参数。
    3. 设置 config 结构体的各个成员:
      • ch_index: DMA通道号。
      • dst: 目的地地址,即UART接收数据的缓冲区地址。
      • dst_fixed: 目的地地址是否固定,这里设置为 false,表示目的地地址可能变化。
      • src: 数据源地址,这里是UART的接收保持寄存器(RBR - Receiver Buffer Register)的地址。
      • src_fixed: 数据源地址是否固定,这里设置为 true,表示数据源地址不变。
      • data_width: 数据传输宽度,这里设置为字节宽度。
      • size_in_byte: 要传输的数据大小。
  • 返回值:

    • 调用 dma_setup_handshake 函数,根据配置好的参数设置DMA握手,并返回相应的状态。
  • 注意事项:

    • 该函数通过设置DMA的握手参数,将UART的接收数据配置到DMA通道中,并返回相应的状态,用于后续判断是否配置成功。

这个函数的作用是通过DMA实现UART的接收数据,配置了DMA握手参数,确保UART接收到的数据传输到指定的缓冲区中。


void dma_isr(void)
{
    volatile hpm_stat_t stat_rx_chn, stat_tx_chn;
    stat_rx_chn = dma_check_transfer_status(TEST_UART_DMA_CONTROLLER, TEST_UART_RX_DMA_CHN);
    if (stat_rx_chn & DMA_CHANNEL_STATUS_TC) {
        uart_rx_dma_done = true;
    }
    stat_tx_chn = dma_check_transfer_status(TEST_UART_DMA_CONTROLLER, TEST_UART_TX_DMA_CHN);
    if (stat_tx_chn & DMA_CHANNEL_STATUS_TC) {
        uart_tx_dma_done = true;
    }
}
SDK_DECLARE_EXT_ISR_M(TEST_UART_DMA_IRQ, dma_isr)
  • 函数逻辑:

    1. 定义两个局部变量 stat_rx_chnstat_tx_chn,用于保存DMA通道的传输状态。
    2. 调用 dma_check_transfer_status 函数检查UART接收和发送的DMA通道的传输状态。
    3. 如果接收通道的传输状态中包含 DMA_CHANNEL_STATUS_TC(传输完成),则将 uart_rx_dma_done 置为 true,表示UART接收DMA完成。
    4. 如果发送通道的传输状态中包含 DMA_CHANNEL_STATUS_TC,则将 uart_tx_dma_done 置为 true,表示UART发送DMA完成。
  • 注意事项:

    • volatile 修饰 stat_rx_chnstat_tx_chn,表示这两个变量可能在中断服务例程之外被修改,确保编译器不会对它们进行优化。
    • 中断处理函数通过检查DMA通道的传输状态来确定DMA是否完成,从而设置相应的标志位。
  • SDK_DECLARE_EXT_ISR_M

    • 该宏用于声明外部中断服务例程。在这里,使用该宏声明了中断服务例程 dma_isr 并关联到 TEST_UART_DMA_IRQ 所指定的中断。

这个中断服务例程的作用是在DMA传输完成时被调用,检查相应的DMA通道状态,并设置标志位以通知主程序相应的DMA传输已完成。


int main(void)
{
    hpm_stat_t stat;
    uart_config_t config = {0};

    board_init();
    printf("UART DMA \n");
    printf("UART will send back received characters, echo every %d bytes\n", TEST_BUFFER_SIZE);

    /* if TEST_UART is same as BOARD_CONSOLE_BASE, it has been initialized in board_init(); */
    board_init_uart(TEST_UART);
    uart_default_config(TEST_UART, &config);
    config.fifo_enable = true;
    config.dma_enable = true;
    config.src_freq_in_hz = clock_get_frequency(TEST_UART_CLK_NAME);
    config.tx_fifo_level = uart_tx_fifo_trg_not_full;
    config.rx_fifo_level = uart_rx_fifo_trg_not_empty;
    stat = uart_init(TEST_UART, &config);
    if (stat != status_success) {
        printf("failed to initialize uart\n");
        while (1) {
        }
    }

    intc_m_enable_irq_with_priority(TEST_UART_DMA_IRQ, 1);
    dmamux_config(TEST_UART_DMAMUX_CONTROLLER, TEST_UART_RX_DMAMUX_CHN, TEST_UART_RX_DMA_REQ, true);
    dmamux_config(TEST_UART_DMAMUX_CONTROLLER, TEST_UART_TX_DMAMUX_CHN, TEST_UART_TX_DMA_REQ, true);

    while (1) {
        /* config rx dma transfer */
        stat = uart_rx_trigger_dma(TEST_UART_DMA_CONTROLLER,
                                    TEST_UART_RX_DMA_CHN,
                                    TEST_UART,
                                    core_local_mem_to_sys_address(BOARD_RUNNING_CORE, (uint32_t)uart_buff),
                                    TEST_BUFFER_SIZE);
        if (stat != status_success) {
            printf("uart rx trigger dma failed\n");
            break;
        }

        while (!uart_rx_dma_done) {
            __asm("nop");
        }
        uart_rx_dma_done = false;

        /* config tx dma transfer */
        stat = uart_tx_trigger_dma(TEST_UART_DMA_CONTROLLER,
                                    TEST_UART_TX_DMA_CHN,
                                    TEST_UART,
                                    core_local_mem_to_sys_address(BOARD_RUNNING_CORE, (uint32_t)uart_buff),
                                    TEST_BUFFER_SIZE);
        if (stat != status_success) {
            printf("uart tx trigger dma failed\n");
            break;
        }

        while (!uart_tx_dma_done) {
            __asm("nop");
        }
        uart_tx_dma_done = false;
    }

    while (1) {
        __asm("nop");
    }

    return 0;
}

  • 代码逻辑:
    1. 调用 board_init 初始化板子。
    2. 输出一些提示信息,包括 "UART DMA" 和 UART 将返回接收到的字符,每次回显的字节数。
    3. 如果 TEST_UARTBOARD_CONSOLE_BASE 相同,说明UART已在 board_init 中初始化。
    4. 调用 board_init_uart 初始化UART。
    5. 初始化UART配置,包括使能FIFO、DMA,设置传输频率等。
    6. 调用 uart_init 初始化UART,检查初始化是否成功。
    7. 启用DMA中断,并配置UART的DMA传输请求。
    8. 进入主循环,循环中:
      • 配置UART接收的DMA传输。
      • 等待UART接收DMA完成。
      • 配置UART发送的DMA传输。
      • 等待UART发送DMA完成。
    9. 如果在DMA配置过程中发生错误,输出相应的错误信息,并跳出主循环。
    10. 主循环最后有一个空操作,用于保持程序运行。

该主函数的主要任务是配置并执行UART的DMA传输,实现了UART接收到的数据的回显。


以下是上述代码的主要运行流程:

  1. 初始化:

    • 初始化嵌入式系统板。
    • 打印一些信息,包括 "UART DMA" 和一条关于将接收到的字符发送回去的消息。
    • 初始化 UART 模块,包括配置 UART 的基本参数和启用 DMA。
  2. 设置中断和DMA多路复用:

    • 启用 UART DMA 中断并设置中断优先级。
    • 配置 DMA 多路复用,将 RX 和 TX DMA 通道与 UART 的相应请求关联。
  3. 主循环:

    • 进入一个无限循环,该循环执行以下操作:
      • 配置 RX DMA 传输:设置 UART 接收的 DMA 传输,将接收到的数据存储在 uart_buff 缓冲区中。
      • 等待 RX DMA 传输完成:通过轮询等待 uart_rx_dma_done 标志。
      • 在数据前添加 "send" 前缀:将 "send" 字符串复制到 uart_buff 的开头。
      • 配置 TX DMA 传输:设置 UART 发送的 DMA 传输,发送整个 uart_buff 缓冲区的数据。
      • 等待 TX DMA 传输完成:通过轮询等待 uart_tx_dma_done 标志。
  4. 结束:

    • 由于主循环是一个无限循环,因此在实际应用中可能需要添加适当的终止条件。
    • 在实际应用中,可能还需要在主循环中添加对接收到的数据的处理逻辑。

总体而言,该代码通过 DMA 实现了 UART 数据的异步传输。接收到的数据被存储在 uart_buff 缓冲区中,并在发送之前添加了 "send" 前缀。此过程一直在一个无限循环中进行,确保持续接收和发送数据。


运行现象:

当工程正确运行后,通过串口手动输入字符串,如 ‘1234567887654321’,则串口终端会收到如下信息:

UART DMA example
UART will send back received characters, echo every 16 bytes
1234567887654321

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1362353.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虾皮长尾词工具:如何使用关键词工具优化Shopee产品的长尾关键词

在Shopee(虾皮)平台上,卖家们都希望能够吸引更多的潜在买家,提高产品的曝光率和转化率。而要实现这一目标,了解和使用长尾关键词是非常重要的。本文将介绍长尾关键词的定义、重要性以及如何使用关键词工具来优化Shopee…

【LeetCode:11. 盛最多水的容器 | 双指针】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

数字孪生在增强现实(AR)中的应用

数字孪生在增强现实(Augmented Reality,AR)中的应用可以提供更丰富、交互性更强的现实世界增强体验。以下是数字孪生在AR中的一些应用,希望对大家有所帮助。北京木奇移动技术有限公司,专业的软件外包开发公司&#xff…

【C++】STL 算法 ④ ( 函数对象与谓词 | 一元函数对象 | “ 谓词 “ 概念 | 一元谓词 | find_if 查找算法 | 一元谓词示例 )

文章目录 一、函数对象与谓词1、一元函数对象2、" 谓词 " 概念3、find_if 查找算法 二、一元谓词示例1、代码示例 - 一元谓词示例2、执行结果 一、函数对象与谓词 1、一元函数对象 " 函数对象 " 是通过 重载 函数调用操作符 () 实现的 operator() , 函数对…

VMware 安装 macOS虚拟机(附工具包)

VMware 安装 macOS虚拟机,在Windows上体验苹果macOS系统! 安装教程:VMware 安装 macOS虚拟机VMware Workstation Pro 是一款强大的虚拟机软件,可让您在 Windows 电脑上运行 macOS 系统。只需简单几步操作,即可轻松安装…

什么是Alibaba Cloud Linux?完全兼容CentOS,详细介绍

Alibaba Cloud Linux是基于龙蜥社区OpenAnolis龙蜥操作系统Anolis OS的阿里云发行版,针对阿里云服务器ECS做了大量深度优化,Alibaba Cloud Linux由阿里云官方免费提供长期支持和维护LTS,Alibaba Cloud Linux完全兼容CentOS/RHEL生态和操作方式…

企业档案集中式管理什么意思?企业档案集中式管理的特点

企业档案集中式管理是指将企业所有的档案资料集中存放、管理和维护的一种方式。在集中式管理中,企业将所有的档案资料集中存放在一个统一的档案中心或档案馆中,通过专门的档案管理人员负责对档案资料进行分类、整理、存储和检索,确保档案资料…

【Java】实验七

实验要求: 1、编写有复制文本文件功能的记事本程序,界面参考下图,窗口中放置文本区(JTextArea)组件: 当点击“复制文件”菜单项后,出现下面的文件对话框,选择要复制的文件。 点击“打开”按钮后,将选中的文件显示在记事本的文本区,并将该文件复制到同一目录下的“cop…

刚学C/C++,使用的是CLion,想要在同一个项目里面运行多个相互独立脚本?

前言: 正常来说,一般一个项目只会有一个程序入口点。C和C程序的入口点是main函数。在一个项目中,只能有一个main函数,否则编译器会不知道从哪个main函数开始执行。 但是,作为初学者,我就是想用CLio…

Apple M2 Pro芯片 + docker-compose up + mysql、elasticsearch pull失败问题的解法

背景 (1)从github上git clone了一个基于Spring Boot的Java项目,查看readme,发现要在项目的根目录下,执行“docker-compose up”。(2)执行“docker-compose up”的前提是,在macos上要…

vue实现代码编辑器,无坑使用CodeMirror

vue实现代码编辑器,无坑使用CodeMirror vue实现代码编辑器,使用codemirror5 坑:本打算cv一下网上的,结果发现网上的博客教程都是错的,而且博客已经是几年前的了,我重新看了github上的,发现安装的命令都已经不一样了。我…

sublime text 3 分屏和关闭分屏

有时候需要编辑多个地方的代码,开多个编辑器又太麻烦,那么Sublime自带的分屏快捷键可以解决烦恼。 Altshift2 分为2列 Altshift3 分为3列 Altshift4 分为4列 Altshift5 分为2行2列 Altshift8 分为2行 Altshift9 分为3行 取消分屏:Alts…

【JAVA】OPENGL+TIFF格式图片,不同阈值旋转效果

有些科学研究领域会用到一些TIFF格式图片,由于是多张图片相互渐变,看起来比较有意思: import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.logging.*;/*** 可以自已定义日志打印格式…

C++多态性——(5)运算符重载(第二节)

归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍 收藏⭐ 留言​📝 身先才能率人,律己才能服人…

外包干了1个月,技术退步一大半。。。

先说一下自己的情况,本科生,19年通过校招进入广州某软件公司,干了接近4年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

YOLOv5+混合注意力机制再涨4.3%,Transformer混合设计依旧可以卷

在工业生产过程中,由于低效率、不统一的评估、高成本以及缺乏实时数据,传统的手动检测焊接缺陷不再被应用。 为了解决表面贴装技术中焊接缺陷检测的低准确率、高误检率和计算成本问题,提出了一种新方法。该方法是一种专门针对焊接缺陷检测算法…

node:全局对象事件环buffer

node:全局对象&事件环&buffer 全局对象 exports/module/require/__dirname/__filename:这些是参数 global全局对象,挂载global上的 process process 进程,代码node服务都是跑在一个进程里面。进程和集群 process上常用属性…

适配最新微信小程序隐私协议开发指南

准备工作 小程序后台设置用户隐私保护指引,需要等待审核通过:设置-基本设置-服务内容声明-用户隐私保护指引 小程序的基础库版本从 2.32.3 开始支持,所以要选这之后的版本 在 app.json 中加上这个设置 “usePrivacyCheck”: true 具体步骤可以…

【方法】PPT设置密码后如何修改?

PowerPoint是我们日常和工作中经常用到的办公软件,有时候为了保护文件,还会设置密码,那设置密码后又想要修改密码,怎么操作呢?下面来看看PPT常用的两种密码是如何修改的。 1. “打开密码” 想要修改PPT的“打开密码”…

C++性能优化- perf 和火焰图的安装使用

工欲善其事必先利其器,要想做Linux下的程序性能优化,就得先知道当前性能的瓶颈在哪里。 这里主要介绍一下常用的工具:perf工具和火焰图的使用方法 本文中的命令都是自己在Ubuntu18.04系统上测试可用的,在其他系统可能会需要不同的…