【STM32】RTT-Studio中HAL库开发教程五:UART的DMA应用

文章目录

  • 一、简介
    • 1.关于DMA
    • 2.DMA使用场景
    • 3.DMA控制结构
    • 4.IDLE空闲中断
    • 5.实现方法
  • 二、RTT配置
  • 三、串口收发流程
  • 四、完整代码
  • 五、测试验证

一、简介

1.关于DMA

   DMA(Direct Memory Access,直接存储器访问) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于CPU的大量中断负载。否则,CPU需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU对于其他的工作来说就无法使用。
  简单的来说,能控制主存内部读写,这样有利于减轻CPU负担,加快读取速度。
  在没有DMA之前,串口每次发送数据时都要由CPU将源地址上的数据拷贝到串口发送的相关寄存器上;串口每次接收数据时都要由CPU将发送来的数据拷贝到主存上。而加了DMA后,只需要告诉DMA源地址和目标地址,DMA通道就能够自动进行数据的转移,即CPU只需要告诉DMA:串口需要发送的数据在哪里,串口接收到的数据应该存在哪里,运输的工作则交由DMA去做,运输期间CPU就可以去处理别的事情,这就大大提高了CPU的运行效率。


2.DMA使用场景

DMA用在只需要传输数据,不需要处理数据的地方,有三种传输方式:

  • 外设→存储器 (例如:将串口RDR寄存器写入某数据buf)
  • 存储器→外设 (例如:将某数据buf写入串口TDR寄存器)
  • 存储器→存储器(例如:复制某特别大的数据buf)

3.DMA控制结构

Stm32F4最多有:2个DMA控制器,各8个数据流,每个数据流有8个通道(或请求),每个通道有一个仲裁器,用于处理请求的优先级。
在这里插入图片描述


4.IDLE空闲中断

(1)STM32 IDLE 接收空闲中断—接受完一帧数据,触发中断
  在使用串口接受字符串时,可以使用空闲中断(IDLEIE置1,即可使能空闲中断),这样在接收完一个字符串,进入空闲状态时,即将IDLE置1,便会激发一个空闲中断。在中断处理函数,我们可以解析这个字符串。

(2)STM32的IDLE的中断产生条件
  在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断

(3)STM32 RXNE接收数据中断—接受到一个字节的数据,触发中断
  当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断


5.实现方法

利用串口IDLE空闲中断的方式接收一帧数据,方法如下:

  • 选择一个串口,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。
  • 初始化完成之后,当外部给单片机发送数据的时候,假设这次接受的数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区数组里面。
  • 当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用__HAL_DMA_GET_COUNTER(&hdma_usart1_rx)。
  • 函数计算出当前DMA接收存储空间剩余字节:本次的数据接受长度=预先定义的接收总字节-接收存储空间剩余字节。

例如:本次串口接受200个字节,HAL_UART_Receive_DMA(&huart1,rx_buffer,200);//打开DMA接收。然后我发送了HelloWord9个字节的数据长度,那么此时 GET_COUNTER函数读出来 接收存储空间剩余字节 就是191个字节,实际接受的字节(9) = 预先定义的接收总字节(200) - __HAL_DMA_GET_COUNTER()(191):9 = 200-191


二、RTT配置

(1)配置串口:主要是配置使用的串口号,以及打开RTT所需要的驱动函数。

/** After configuring corresponding UART or UART DMA, you can use it.** STEP 1, define macro define related to the serial port opening based on the serial port number*                 such as     #define BSP_USING_UART1** STEP 2, according to the corresponding pin of serial port, define the related serial port information macro*                 such as     #define BSP_UART1_TX_PIN       "PA9"*                             #define BSP_UART1_RX_PIN       "PA10"** STEP 3, if you want using SERIAL DMA, you must open it in the RT-Thread Settings.*                 RT-Thread Setting -> Components -> Device Drivers -> Serial Device Drivers -> Enable Serial DMA Mode** STEP 4, according to serial port number to define serial port tx/rx DMA function in the board.h file*                 such as     #define BSP_UART1_RX_USING_DMA**/#define BSP_USING_UART1
#define BSP_UART1_TX_PIN       "PA9"
#define BSP_UART1_RX_PIN       "PA10"#define BSP_USING_UART3
#define BSP_UART3_TX_PIN       "PD8"
#define BSP_UART3_RX_PIN       "PD9"

(2)配置初始化函数:主要是使用CubeMx配置生成串口的初始化代码,并将代码复制到board.c中。

extern DMA_HandleTypeDef hdma_usart3_rx;
extern DMA_HandleTypeDef hdma_usart3_tx;/**
* @brief USART MSP Initialization
* This function configures the hardware resources used in this example
* @param husart: USART handle pointer
* @retval None
*/
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if (huart->Instance == USART3){/* Peripheral clock enable */__HAL_RCC_USART3_CLK_ENABLE();__HAL_RCC_GPIOD_CLK_ENABLE();/**USART3 GPIO ConfigurationPD8     ------> USART3_TXPD9     ------> USART3_RX*/GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_PULLUP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF7_USART3;HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);/* USART3 DMA Init *//* USART3_RX Init */hdma_usart3_rx.Instance = DMA1_Stream1;hdma_usart3_rx.Init.Channel = DMA_CHANNEL_4;hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE;hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;hdma_usart3_rx.Init.Mode = DMA_NORMAL;hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW;hdma_usart3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;if (HAL_DMA_Init(&hdma_usart3_rx) != HAL_OK){Error_Handler();}__HAL_LINKDMA(huart, hdmarx, hdma_usart3_rx);/* USART3_TX Init */hdma_usart3_tx.Instance = DMA1_Stream3;hdma_usart3_tx.Init.Channel = DMA_CHANNEL_4;hdma_usart3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;hdma_usart3_tx.Init.PeriphInc = DMA_PINC_DISABLE;hdma_usart3_tx.Init.MemInc = DMA_MINC_ENABLE;hdma_usart3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;hdma_usart3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;hdma_usart3_tx.Init.Mode = DMA_NORMAL;hdma_usart3_tx.Init.Priority = DMA_PRIORITY_LOW;hdma_usart3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;if (HAL_DMA_Init(&hdma_usart3_tx) != HAL_OK){Error_Handler();}__HAL_LINKDMA(huart, hdmatx, hdma_usart3_tx);/* USART3 interrupt Init */HAL_NVIC_SetPriority(USART3_IRQn, 0, 0);HAL_NVIC_EnableIRQ(USART3_IRQn);}
}

三、串口收发流程

(1)接收数据的流程:
在这里插入图片描述

  • 首先在初始化的时候打开DMA接收
  • 当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组
  • 程序此时也不会进入接收中断,在软件上无需做任何事情,要在初始化配置的时候设置好配置就可以了。

(2)数据接收完成的流程:

  • 当数据接收完成之后产生接收空闲中断④
  • 在中断服务函数中:
      a:判断是否为IDLE接受空闲中断
      b:在中断服务函数中将接收完成标志位置1
      c:关闭DMA防止在处理数据时候接收数据,产生干扰。
      d:计算出接收缓存中的数据长度,清除中断位,
  • while循环 主程序流程:
      a:主程序中检测到接收完成标志被置1
      b:进入数据处理程序,现将接收完成标志位置0,
      c:将接收到的数据重新发送到上位机
      d:重新设置DMA下次要接收的数据字节数,使能DMA进入接收数据状态。

四、完整代码

1.uart.c文件

#include "uart.h"volatile uint8_t rx_len = 0;            // 接收一帧数据的长度
volatile uint8_t recv_end_flag = 0;     // 一帧数据接收完成标志
uint8_t rx_buffer[100]={0};             // 接收数据缓存数组void MX_USART3_UART_Init(void)
{/* DMA controller clock enable */__HAL_RCC_DMA1_CLK_ENABLE();huart3.Instance = USART3;huart3.Init.BaudRate = 115200;huart3.Init.WordLength = UART_WORDLENGTH_8B;huart3.Init.StopBits = UART_STOPBITS_1;huart3.Init.Parity = UART_PARITY_NONE;huart3.Init.Mode = UART_MODE_TX_RX;huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart3.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart3) != HAL_OK){Error_Handler();}/* DMA interrupt init *//* DMA1_Stream1_IRQn interrupt configuration */HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);/* DMA1_Stream3_IRQn interrupt configuration */HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE); //使能IDLE中断//DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度HAL_UART_Receive_DMA(&huart3, rx_buffer, BUFFER_SIZE);
}/*** @brief 串口发送功能函数* @param buf:发送数据* @param len:数据长度*/
void DMA_Usart_Send(uint8_t *buf,uint8_t len)
{// 判断是否发送正常,如果出现异常则进入异常中断函数if (HAL_UART_Transmit_DMA(&huart3, buf, len) != HAL_OK){Error_Handler();}
}/*** @brief 串口接收功能函数* @param Data:接收数据* @param len:接收长度*/
void DMA_Usart1_Read(uint8_t *Data,uint8_t len)
{// 重新打开DMA接收HAL_UART_Receive_DMA(&huart3, Data, len);
}/*** @brief This function handles DMA1 stream1 global interrupt.*/
void DMA1_Stream1_IRQHandler(void)
{HAL_DMA_IRQHandler(&hdma_usart3_rx);
}/*** @brief This function handles DMA1 stream3 global interrupt.*/
void DMA1_Stream3_IRQHandler(void)
{HAL_DMA_IRQHandler(&hdma_usart3_tx);
}/*** @brief This function handles USART3 global interrupt.*/
void USART3_IRQHandler(void)
{uint32_t tmp_flag = 0;uint32_t temp;tmp_flag = __HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE);    // 获取IDLE标志位if ((tmp_flag != RESET))                                    // idle标志被置位{
#if 1__HAL_UART_CLEAR_IDLEFLAG(&huart3);                     // 清除标志位
#elsetemp = huart3.Instance->SR;                             // 清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能temp = huart3.Instance->DR;                             // 读取数据寄存器中的数据
#endifHAL_UART_DMAStop(&huart3);                              // 停止DMA传输,防止干扰#if 1temp = __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);          // 获取DMA中未传输的数据个数
#elsetemp  = hdma_usart3_rx.Instance->NDTR;                  // 读取NDTR寄存器,获取DMA中未传输的数据个数,
#endifrx_len = BUFFER_SIZE - temp;                            // 总计数减去未传输的数据个数,得到已经接收的数据个数recv_end_flag = 1;                                      // 接受完成标志位置1}HAL_UART_Receive_DMA(&huart3, rx_buffer, BUFFER_SIZE);      // 重新打开DMA接收HAL_UART_IRQHandler(&huart3);
}

2.uart.h文件

#ifndef APPLICATIONS_UART_H_
#define APPLICATIONS_UART_H_#include <rtthread.h>
#include <drv_common.h>UART_HandleTypeDef huart3;
DMA_HandleTypeDef hdma_usart3_rx;
DMA_HandleTypeDef hdma_usart3_tx;#define BUFFER_SIZE  100extern  volatile uint8_t rx_len ;       // 接收一帧数据的长度
extern volatile uint8_t recv_end_flag;  // 一帧数据接收完成标志
extern uint8_t rx_buffer[100];          // 接收数据缓存数组extern void MX_USART3_UART_Init(void);
extern void DMA_Usart_Send(uint8_t *buf,uint8_t len);
extern void DMA_Usart1_Read(uint8_t *Data,uint8_t len);#endif /* APPLICATIONS_UART_H_ */

3.main.c文件

#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <string.h>
#include "uart.h"int main(void)
{int count = 1;MX_USART3_UART_Init();while (count){if (recv_end_flag == 1)     // 接收完成标志{DMA_Usart_Send(rx_buffer, rx_len);rx_len = 0;             // 清除计数recv_end_flag = 0;      // 清除接收结束标志位memset(rx_buffer, 0, rx_len);}}return RT_EOK;
}

五、测试验证

  通过将程序下载到单片机中,可以使用串口助手来进行数据的发送,如下可以看到可以进行正常数据的收发,并且使用uart的DMA功能,可以更好的改善CPU的运行效率,并且也不用暂用CPU,加快器性能。
在这里插入图片描述


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

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

相关文章

python办公自动化:PPT工具之`Python-PPTX`简介

第1章 简介 1.1 什么是Python-PPTX库&#xff1f; Python-PPTX库是一个用于创建和修改Microsoft PowerPoint (.pptx) 文件的Python库。它提供了一个易于使用的API&#xff0c;使得开发者能够以编程方式操纵PowerPoint文件&#xff0c;包括创建新文件、修改现有文件、添加文本…

2024最新最全面的JMeter 做接口加密测试

JMeter 怎么做接口的加密&#xff1f; JMeter如果需要做加密测试&#xff0c;是需要加密类型对应的jar包的。本文以MD5,加密作为教程。 1、在Test Plan 引用jar包&#xff1b; 2、添加BeanShell Sampler取样器&#xff0c;并输入调用代码 import md5.mymd5;//调用jar包 String…

Excel技巧(二)

函数 SUMIFS函数 用于计算其满足多个条件的全部参数的总量 语法&#xff1a;SUMIFS(sum_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...) COUNTIFS函数 计算多个区域中满足给定条件的单元格的个数 语法&#xff1a;countifs(criteria_range1,crit…

翻译软件 Fastrans 开发日志 #2

就过了几天&#xff0c;我的 Fastrans 项目&#xff08; https://github.com/YaoqxCN/Fastrans &#xff09;又更新了两个版本&#xff0c;现在是 v1.1.1。&#xff08;求个 star 谢谢&#xff01;&#xff09; 上次我初步实现了 Fastrans 的翻译功能以及 UI&#xff0c;可以看…

平衡膳食与健康 第二篇

健康,不止于不生病. 不良的生活方式 瞎折腾 影响身体健康 实际上健康还包括在其他方面 精神方面 社会接触能力大夫问题复盘 慢性疾病&#xff0c;都是生活方式病 慢性病与生活方式密切相关 还有心理问题**:焦虑症,抑郁症** 健康医学和疾病医学差距 压健康状态比例 肚…

如何打造基于Java SpringBoot和Vue的医院门诊智能预约平台?四步实现高效就医流程,整合MySQL数据库,优化用户体验。

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…

开发中如何在运行/调试时将项目热部署到Tomcat

这里有一篇不错的博客&#xff0c;可以参考 http://t.csdnimg.cn/oWcgm 正常情况下&#xff0c;我们将web项目打包成war包后&#xff0c;需要放到tomcat的webapps路径下&#xff0c;然后启动tomcat&#xff0c;才能正常访问。但是这在开发阶段是极为不便的。因此可以使用两种方…

电商库存API:商家数字化转型的加速器

在电商行业&#xff0c;库存管理是运营的核心之一。随着业务的扩展和消费者需求的多样化&#xff0c;传统的库存管理方法已经难以满足现代电商的需求。这时&#xff0c;电商库存API应运而生&#xff0c;为商家提供了一种高效、灵活的库存管理手段。 一、对接电商库存API的重要…

猫头虎分享:Python库 TensorFlow 的简介、安装、用法详解入门教程

猫头虎分享&#xff1a;Python库 TensorFlow 的简介、安装、用法详解入门教程 &#x1f42f; 摘要 今天猫头虎带大家走进 人工智能 的核心领域&#xff0c;深入探讨 TensorFlow 这个强大的 Python库。从 TensorFlow 的基础简介到详细的安装和用法&#xff0c;这篇教程将带你从…

cubeide Target is not responding, retrying... 或基于vscode方式等 无法调试

点击调试输出如图&#xff1a; 基于cubeidet开发环境&#xff0c;debug后输出&#xff1a; 基于vscode开发环境&#xff1a; OpenOCD: GDB Server Quit Unexpectedly. See gdb-server output in TERMINAL tab for more details. 解决方法&#xff1a; 这里的调试选择一个&…

AI大模型编写多线程并发框架(六十三):监听器优化·上

系列文章目录 文章目录 系列文章目录前言一、项目背景二、第七轮对话-补充异步执行代码三、第八轮对话-增加累计完成等字眼四、第九轮对话-线程安全优化五、第十轮对话-增加运行时数据七、参考文章 前言 在这个充满技术创新的时代&#xff0c;AI大模型正成为开发者们的新宠。它…

MDK 复制hex文件到根目录并加上日期 bat 脚本

工程目录示例 copy.bat ::关闭命令行显示 echo off :: GBK chcp 936 >nul setlocal EnableDelayedExpansion:设置文件夹路径 set "FolderName.\Objects":: set "FolderName.\Objects" :: #;copy %FolderName%\*.hex ..\*.hex //复制到根目录 :: c…

uniapp小程序实现横屏手写签名

<template><view class"signBox column-me"><!-- 这个是自定义的title-可根据自己封装的title的作为调整 --><status-bar title"电子签名" :bgColor"null"></status-bar><view class"topHint">请…

滚雪球学MyBatis-Plus(02):环境准备

环境准备 本地开发环境参考如下&#xff1a; 开发工具&#xff1a;IntelliJ IDEA 2021.3.2JDK版本&#xff1a; JDK 1.8Spring Boot版本&#xff1a;2.3.1.RELEASEMaven版本&#xff1a;Apache Maven 3.8.2MySQL&#xff1a;5.6 前言 在上期内容中&#xff0c;我们系统地介绍了…

python实战三-提取Word数据到Excel

视频源码&#xff1a; 链接&#xff1a;https://pan.quark.cn/s/83db5bb15383 一个文件夹下有大量会议通知文件&#xff0c;为word文件&#xff0c;文件格式都是一致的&#xff0c;现在要将文件中的一些字段提取出来汇总到Excel文件中。 会议通知文件格式如下&#xff1a; 要提…

电路笔记(PCB): 椭圆形板框绘制

使用 QCAD绘制并导出dxf文件 https://www.qcad.org/en/&#xff0c;QCAD 是一个免费的开源应用程序&#xff0c;用于二维 &#xff08;2D&#xff09; 计算机辅助绘图 &#xff08;CAD&#xff09;。 导入dxf文件

【Apache Doris】数据均衡问题排查指南

原文阅读&#xff1a;【巨人肩膀社区博客分享】【Apache Doris】数据均衡问题排查指南 一、前提概要 当集群处于以下几种情况时&#xff0c;可参考本文进行问题排查。 •BE节点之间的数据不均 •单个BE节点上的多个磁盘之间的数据不均 •BE节点的上线和下线进度卡死&#…

路由策略工具

1.产生背景 a.保证数据访问的安全性 b.提高链路带宽利用率 c.流量路径不优 2.解决方案: 解决方案一:路由策略:可通过修改路由条目(即对接收和发布的路由进行过滤)来控制流量可达性 解决方案二:流量过滤:可使用Traffic-Filter工具对数据直接进行过滤 3.路由策略工具 a.可利…

基于jstat 分析垃圾回收情况,进行JVM调优

文章目录 引言I jstat 统计信息工具JVM 堆内存布局命令格式元数据空间统计堆内存统计JVM编译方法统计编译统计类加载统计II JVM调优基本概念: 应用程序的响应时间(RT)和吞吐量(QPS)JVM调优原理调优思路调优方法JVM调优技巧建议III 基于jstat 分析垃圾回收情况,进行JVM调优…

gitee 简单使用

目录 1. github 介绍 2. gitee 介绍 2.1 下载工具 2.2 创建仓库 2.3 clone 远程仓库到本地 2.4 上传代码 2.4.1 add:预添加到本地仓库 2.4.2 commit:提交代码到仓库 2.4.3 push:推送添加操作到远程仓库 3. 问题解决 3.1 文件推送成功&#xff0c;gitee没有绿点 3.2…