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

目录

概述:

端口设置:

代码分析:

运行现象:


概述:

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.mzph.cn/news/603662.shtml

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

相关文章

Vue2:修改默认配置的方法

一、前情概要 之前我们说到,用vue-cli创建vue项目之后,项目结构大概是这样的。其中,标红部分的文件是非常重要的结构文件,不建议修改文件名。 但是,实际上了,vue是允许修改的。 准备配置文件:v…

软件测试|使用PyMySQL访问MySQL数据库的详细指南

简介 PyMySQL是Python中流行的MySQL数据库驱动程序,它提供了便捷的方法来连接、查询和更新MySQL数据库。本文将为您提供使用PyMySQL访问MySQL数据库的详细指南,包括安装PyMySQL、连接数据库、执行查询和更新操作等。 环境准备 在开始之前,…

编译原理笔记(三)

一、词法分析程序的设计 1、词法分析程序的输出 在识别出下一个单词同时验证其词法正确性之后,词法分析程序将结果以单词符号的形式发送至语法分析程序以回应其请求。 单词符号一般分下列5类: 关键字:如:begin、end、if、whil…

双变量probit模型

1. Probit模型 1.1 模型含义 假设个体只有两种选择,y1或y0。影响选择的变量都包括在向量x中。即线性概率模型为 y值服从两点分布 被认为是连接函数,函数选择具有一定的灵活性。如果为标准正态的累积分布函数,则模型成为Probit模型&#xff…

NACHI机器人模拟示教器如何切换中文

前言 现在开始学习机器人的编程语言,那么要学习会用首先得用模拟示教器来学习,但是全是英文确实比较难受一些些,没有中文来的直观。所以摸透一下如何给示教器更换语言。 具体步骤 步骤一:将中文的汉化包下载下来。具体的下载链…

Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)

目录 一、前言 二、AOP—快速入门 1.动态代理优化 : 2.问题分析 : 3.AOP—基本介绍 : 4.AOP—使用说明 : 5.AOP—入门案例 : 三、AOP—切入点表达式 1.基本说明 : 2.语法格式 : 3.注意事项 : 4.代码演示 : 四、AOP—切入点表达式的更多细节 1.JoinPoint : 1.1 简…

华为HCIE课堂笔记第十三章 IPv6地址配置

目录 第十三章 IPv6地址配置 13.1 IPv6地址无状态自动配置 13.1.1 RS和RA报文格式 13.1.2 RA的Flags字段 13.1.3 地址的生存周期 13.1.4 RA报文中前缀中的Flags 13.2 DHCPv6 13.2.1 DHCPV6的概念 13.2.2 DCHPv6的报文 第十三章 IPv6地址配置 13.1 IPv6地址无状态自动…

ENVI无法打开Landsat8的头文件问题和解决

问题 解决方案 双击打开该xxx_MTL.txt的头文件 第一行GROUP LANDSAT_METADATA_FILE 改为 GROUP L1_METADATA_FILE 按住CTRLF,查找GROUP LEVEL1_PROCESSING_RECORD 会查到两个包含的句子,把这两个中间的部分全部删掉 删除空行后,应该为…

基于Python+Django,开发一款房屋租赁系统

学习文档 学习过程中,遇到问题可以咨询作者 功能介绍 平台采用B/S结构,后端采用主流的PythonDjango进行开发,前端采用主流的Vue.js进行开发。 整个平台包括前台和后台两个部分。 前台功能包括:首页、房屋详情页、用户中心模块。…

学习使用js/jquery获取指定class名称的三种方式

学习使用js/jquery获取指定class名称的三种方式 简介一、获取元素的class名称1、通过原生JS获取元素的class名称2、通过Jquery获取元素的class名称 二、应用1、样式修改2、动画效果实现 简介 在开发网页时,我们经常需要通过JS获取元素的class名称进行一些操作&…

IMU用于无人机故障诊断

最近,来自韩国的研究团队通过开发以IMU为中心的数据驱动诊断方法,旨在多旋翼飞行器可以自我评估其性能,即时识别和解决推进故障。该方法从单纯的常规目视检查跃升为复杂的诊断细微差别,标志着无人机维护的范式转变。 与依赖额外传…

Unity中Shader面片一直面向摄像机(个性化修改及适配BRP)

文章目录 前言一、个性化修改面向摄像机效果1、把上一篇文章中求的 Z轴基向量 投影到 XoZ平面上2、其余步骤和之前的一致3、在属性面板定义一个变量,控制面片面向摄像机的类型4、效果 二、适配BRP三、最终代码 前言 在上一篇文章中,我们用Shader实现了面…

Vim 快速指南:高效删除文本行

🧙‍♂️ 诸位好,吾乃诸葛妙计,编程界之翘楚,代码之大师。算法如流水,逻辑如棋局。 📜 吾之笔记,内含诸般技术之秘诀。吾欲以此笔记,传授编程之道,助汝解技术难题。 &…

阿里巴巴中国站按图搜索1688商品(拍立淘) API(1688.item_search_img)在电商中的前景

一、引言 随着移动互联网的快速发展和普及,越来越多的消费者开始通过手机等移动设备进行购物。在这个背景下,电商企业需要不断创新和改进,以满足消费者的需求和提供更好的购物体验。其中,按图搜索商品的技术成为了电商领域的一个…

C++ 求一个数是否是丑数。

#include<string.h> #include <iostream> using namespace std; int isChou(int num) { if (num < 0) { return 0; } while (num % 2 0) { // 不断除以2&#xff0c;直到不能整除为止 num / 2; } while (num % 3 0) { // 不断除…

操作系统期末复习笔记(持续更新..)

一、操作系统的基本概念 1.1 操作系统概念 控制和管理整个计算机系统的硬件与软件资源。合理地组织、调度计算机的工作与资源。为用户和其他软件提供方便接口与环境的程序集合。 1.2 操作系统的特征 特征&#xff1a;并发&#xff0c;共享&#xff0c;虚拟&#xff0c;异步…

数据结构复习部分机考题-自用

A.二叉树孩子链表法之找家人 题目描述 给出二叉树的孩子链表表示法&#xff0c;根据输入要求&#xff0c;找指定结点的双亲或孩子 输入 第一行输入两个参数&#xff0c;第一个参数n表示树有n个结点&#xff0c;第二个参数r表示根结点的数组下标 接着n行&#xff0c;每行先…

kotlin chunked 和 windowed

kotlin chunked的作用 将集合按照指定的数量分割成多个结合 val numbers listOf(0,1,2,3,4,5,6,7,8,9) //把集合按照一个结合3个元素分割 Log.d("chunked", numbers.chunked(3).toString()) // 打印结果 [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] kotlin windowed…

带大家做一个,易上手的家常香干炒腊肉

从冰箱那一块腊肉 套个食品级的袋子 然后用冷水化冰 准备两块香干 香干切成片 不要太薄 当然也别厚了 一把青蒜 青蒜切成段 干和叶子分开装 腊肉去掉下面的肉皮 然后切小块 锅中加入清水 下入少量油和盐 开小火 水起泡泡后下入香干 过水 半分钟左右 香干捞出备用 将腊…

条件竞争之文件上传

一、条件竞争介绍 条件竞争,在程序员日常的Web应用开发中&#xff0c;通常不如其他漏洞受到的关注度高。因为普遍的共识是&#xff0c;条件竞争是不可靠的&#xff0c;大多数时候只能靠代码审计来识别发现&#xff0c;而依赖现有的工具或技术很难在黑盒灰盒中识别并进行攻击。…