【详细讲解在STM32的UART通信中使用DMA机制】

详细讲解在STM32的UART通信中使用DMA机制

目录

  • 详细讲解在STM32的UART通信中使用DMA机制
    • 一、DMA机制概述
    • 二、DMA在UART中的作用
    • 三、DMA的配置步骤
    • 四、UART初始化与DMA结合
    • 五、DMA传输的中断处理
    • 六、DMA与中断的结合使用
    • 七、注意事项与常见问题
    • 八、代码示例
    • 九、总结

一、DMA机制概述

DMA(Direct Memory Access) 是一种硬件机制,允许外设不需要CPU干预的情况下直接访问内存。它能够显著提升数据传输的效率,尤其是在需要频繁数据传输的场景中。

二、DMA在UART中的作用

在STM32微控制器中, UART模块支持DMA传输,能够实现在CPU空闲的情况下,快速传输大量数据。具体来说,DMA可以将UART接收的数据直接传输到内存中的缓冲区,或者将内存中的数据缓冲区直接传输到UART发送缓冲区,从而降低CPU负载,提高系统效率。

三、DMA的配置步骤

  1. 使能DMA时钟
    在使用DMA之前,需要先使能DMA外设的时钟。不同的 DMA 通道对应不同的时钟配置。
// 使能DMA1时钟(例如,对于DMA1的信道)
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  1. 设置DMA Channel和DMA Mode 配置DMA通道和传输模式(比如正常模式、循环模式等)。
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_Channel = DMA_Channel_0;  // 选择DMA通道
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; // UART数据寄存器地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)rxBuffer; // 接收缓冲区地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // 设置传输方向,外设到内存
DMA_InitStructure.DMA_BufferSize = RX_BUFFER_SIZE; // 接收缓冲区大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 数据宽度为字节
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // DMA正常模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 设置DMA优先级为高
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; // FIFO模式关闭
DMA_Init(DMA1_Channel0, &DMA_InitStructure); // 初始化DMA通道
  1. 使能DMA传输 完成配置后,使能DMA通道。
DMA_Cmd(DMA1_Channel0, ENABLE);

四、UART初始化与DMA结合

  1. UART初始化配置 配置UART的基本参数,如波特率、数据格式、校验位等。
// 配置USART1
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &USART_InitStructure);
  1. 配置UART的DMA传输 使能 UART的DMA传输功能,通常在UART的中断配置中完成。
// 使能USART1的DMA接收功能
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);

五、DMA传输的中断处理

  1. 配置DMA传输完成中断 配置DMA传输完成后触发中断,以便进行后续处理。
    // 配置DMA中断
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 2; // 中断优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
  2. 编写DMA中断服务函数 在中断服务函数中进行数据处理和DMA重装载操作。

void DMA1_Channel0_IRQHandler(void) {
if (DMA_GetFlagStatus(DMA1_Channel0, DMA_FLAG_TCIF0)) {
// 清除传输完成标志
DMA_ClearFlag(DMA1_Channel0, DMA_FLAG_TCIF0);
// 处理接收到的数据
processData(rxBuffer, RX_BUFFER_SIZE);
// 重新装载DMA缓冲区
DMA_SetCurrDataCounter(DMA1_Channel0, RX_BUFFER_SIZE);
}
}

六、DMA与中断的结合使用

1.DMA和中断的区别
• DMA :在不占用CPU的情况下,直接完成内存和外设之间的数据传输,适合大数据量的传输。
• 中断 :当外设需要CPU处理特定事件时,通过中断机制通知CPU。

2.在UART通信中DMA的优势
• 提高了数据传输的效率,减少了CPU的负载。
• 适用于需要大量数据传输的应用场景,如长时间的串口数据采集。

3.什么时候最适合使用DMA
• 当需要实现高效的、大量数据的UART传输时。
• 当希望降低CPU使用率,让CPU专注于其他任务处理时。

七、注意事项与常见问题

1.DMA传输中的内存配置 确保DMA传输的内存区域是连续的,避免因内存碎片导致传输错误。

2.缓冲区的大小和管理 合理设置DMA传输的缓冲区大小,避免因为缓冲区过小而导致频繁的DMA中断,或因为缓冲区过大而导致内存浪费。

3.DMA传输方向的设置 根据实际需求设置正确的传输方向(外设到内存或内存到外设),否则会导致数据传输错误。

4.DMA优先级的设置 根据系统的实际工作情况,合理设置DMA通道的优先级,以确保关键任务的优先执行。

5.传输完成后DMA的重装载 在DMA传输完成后,配置DMA为循环模式或手动重新装载模式,以保证持续的数据传输需求。

八、代码示例

以下是一个完整的示例代码,展示了如何在STM32中配置和使用DMA进行UART数据接收:

#include "stm32f10x.h"

#define RX_BUFFER_SIZE 256

uint8_t rxBuffer[RX_BUFFER_SIZE];

void DMA_Configuration(void) {
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // 使能DMA1时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    // 配置DMA通道0
    DMA_InitStructure.DMA_Channel = DMA_Channel_0;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)rxBuffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
    DMA_InitStructure.DMA_BufferSize = RX_BUFFER_SIZE;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init(DMA1_Channel0, &DMA_InitStructure);

    // 使能DMA通道0
    DMA_Cmd(DMA1_Channel0, ENABLE);

    // 配置DMA中断优先级
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    // 启用DMA传输完成中断
    DMA_ITConfig(DMA1_Channel0, DMA_IT_TC, ENABLE);
}
void USART_Configuration(void) {
USART_InitTypeDef USART_InitStructure;
// 使能USART1外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // TX和RX引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &USART_InitStructure);
// 使能USART接收DMA
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
// 启动USART
USART_Cmd(USART1, ENABLE);
}
void USART_NVIC_Configuration(void) {
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void USART1_IRQHandler(void) {
// 处理 USART 中断,如有需要
//,...
}
void DMA1_Channel0_IRQHandler(void) {
if (DMA_GetFlagStatus(DMA1_Channel0, DMA_FLAG_TCIF0)) {
// 清除 DMA 传输完成标志
DMA_ClearFlag(DMA1_Channel0, DMA_FLAG_TCIF0);
// 数据处理操作
processData(rxBuffer, RX_BUFFER_SIZE);
// 重新装载 DMA 缓冲区
DMA_SetCurrDataCounter(DMA1_Channel0, RX_BUFFER_SIZE);
}
}
int main(void) {
// 系统时钟配置(假设已经完成)
// 配置DMA
DMA_Configuration();
// 配置USART1
USART_Configuration();
// 配置USART中断
USART_NVIC_Configuration();
// 进入主循环
while (1) {
// 主循环可以执行其他任务
}
}

九、总结

通过在STM32的UART通信中使用DMA机制,可以在不占用CPU的情况下实现高效的数据传输,显著提升系统的性能和响应速度。DMA的配置和使用需要仔细理解其工作原理和具体的配置步骤,确保在实际应用中的正确性和稳定性。
在实际开发中,还应结合具体的应用场景和硬件配置,进行合理的参数设置和优化,以达到最佳的传输效率和稳定

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VSCode 移除EmmyLua插件的红色波浪线提示

VSCode 中安装插件EmmyLua,然后打开lua文件的时候,如果lua代码引用了C#脚本的变量,经常出现 “undefined global variable: UnityEngineEmmyLua(undefined-global)” 的红色波浪线提示,这个提示看着比较烦人,我们可以通…

MWC 2025 | 紫光展锐联合移远通信推出全面支持R16特性的5G模组RG620UA-EU

2025年世界移动通信大会(MWC 2025)期间,紫光展锐联合移远通信,正式发布了全面支持5G R16特性的模组RG620UA-EU,以强大的灵活性和便捷性赋能产业。 展锐芯加持,关键性能优异 RG620UA-EU模组基于紫光展锐V62…

springboot425-基于SpringBoot的BUG管理系统(源码+数据库+纯前后端分离+部署讲解等)

💕💕作者: 爱笑学姐 💕💕个人简介:十年Java,Python美女程序员一枚,精通计算机专业前后端各类框架。 💕💕各类成品Java毕设 。javaweb,ssm&#xf…

机器人“照镜子”:开启智能新时代

机器人也爱 “照镜子”? 在科技飞速发展的今天,机器人的身影越来越频繁地出现在我们的生活和工作中。它们承担着各种各样的任务,从工业生产线上的精密操作,到家庭中的清洁服务,再到危险环境下的救援工作。然而&#xf…

让 LabVIEW 程序更稳定

LabVIEW 开发的系统,尤其是工业级应用,往往需要长时间稳定运行,容不得崩溃、卡顿或数据丢失。然而,许多系统在实际运行中会遭遇内存泄漏、通信中断、界面卡顿等问题,导致生产中断甚至设备损坏。如何设计一个既稳定又易…

基于CURL命令封装的JAVA通用HTTP工具

文章目录 一、简要概述二、封装过程1. 引入依赖2. 定义脚本执行类 三、单元测试四、其他资源 一、简要概述 在Linux中curl是一个利用URL规则在命令行下工作的文件传输工具,可以说是一款很强大的http命令行工具。它支持文件的上传和下载,是综合传输工具&…

npm ERR! code 128 npm ERR! An unknown git error occurred

【问题描述】 【问题解决】 管理员运行cmd(右键window --> 选择终端管理员) 执行命令 git config --global url.“https://”.insteadOf ssh://git cd 到项目目录 重新执行npm install 个人原因,这里执行npm install --registryhttps:…

汽车视频智能包装创作解决方案,让旅途记忆一键升级为影视级大片

在智能汽车时代,行车记录已不再是简单的影像留存,而是承载情感与创意的载体。美摄科技依托20余年视音频领域技术积累,推出汽车视频智能包装创作解决方案,以AI驱动影像处理与艺术创作,重新定义车载视频体验,…

Qt中txt文件输出为PDF格式

main.cpp PdfReportGenerator pdfReportGenerator;// 加载中文字体if (QFontDatabase::addApplicationFont(":/new/prefix1/simsun.ttf") -1) {QMessageBox::warning(nullptr, "警告", "无法加载中文字体");}// 解析日志文件QVector<LogEntr…

nlp进阶

1 Rnn RNN(Recurrent Neural Network),中文称作循环神经网络,它一般以序列数据为输入,通过网络内部的结构段计有效捕捉序列之间的关系特征,一般也是以序列形式进行输出. 单层网络结构 在循环 rnn处理的过程 rnn类别 n - n n - 1 使用sigmoid 或者softmax处理 应用在分类中…

2024 JAVA面试题

第一章-Java基础篇 1、你是怎样理解OOP面向对象 面向对象是利于语言对现实事物进行抽象。面向对象具有以下特征&#xff1a; 继承****&#xff1a;****继承是从已有类得到继承信息创建新类的过程 封装&#xff1a;封装是把数据和操作数据的方法绑定起来&#xff0c;对数据的…

浅色系可视化大屏看起来确实很漂亮,但用到的地方确实很少

在数字化信息飞速发展的时代&#xff0c;可视化大屏作为信息展示的重要载体&#xff0c;广泛应用于各类场景。其中&#xff0c;浅色系可视化大屏以其独特的视觉风格&#xff0c;在众多展示方案中脱颖而出&#xff0c;给人以清新、舒适的视觉感受。然而&#xff0c;尽管浅色系可…

蓝桥杯备考:动态规划线性dp之下楼梯问题进阶版

老规矩&#xff0c;按照dp题的顺序 step1 定义状态表达 f[i]表示到第i个台阶的方案数 step2:推导状态方程 step3:初始化 初始化要保证 1.数组不越界 2.推导结果正确 如图这种情况就越界了&#xff0c;我们如果把1到k的值全初始化也不现实&#xff0c;会增加程序的时间复杂度…

LLM 大模型基础认知篇

目录 1、基本概述 2、大模型工作原理 3、关键知识点 &#xff08;1&#xff09;RAG 知识库 &#xff08;2&#xff09;蒸馏 &#xff08;3&#xff09;微调 &#xff08;4&#xff09;智能体 1、基本概述 大型语言模型&#xff08;Large Language Model, LLM&#xff09…

物业管理系统源码 物业小程序源码

物业管理系统源码 物业小程序源码 一、基础信息管理 1. 房产信息管理 记录楼栋、单元、房间的详细信息&#xff08;面积、户型、产权等&#xff09;。 管理业主/租户的档案&#xff0c;包括联系方式、合同信息等。 2. 公共资源管理 管理停车场、电梯、绿化带、公…

Delphi连接MySql数据库房

在看Delpih6数据库开发实例导航这本书时&#xff0c;里面的数据库管理系统用的InterBase&#xff0c;但是Delphi11中已经没有这个东西了&#xff0c;我就想到利用MS的access但是里面有很多的SQL语句不支持&#xff0c;比如设置字段的默认值等&#xff0c;后来我想到连接到MySQL…

[51 单片机] --串口编程

1&#xff0c;通讯方式基本概念 1&#xff0c;按照 --> 数据传送方式串行通讯&#xff1a;使用一条数据线&#xff0c;将数据一位一位地依次传输&#xff0c;每一位数据占据一个固定的时间长度&#xff0c;串行通信的特点&#xff1a;传输线少&#xff0c;长距离传送时成本…

基础算法——模拟

模拟&#xff0c;顾名思义&#xff0c;就是题⽬让你做什么你就做什么&#xff0c;考察的是将思路转化成代码的代码能⼒。 这类题⼀般较为简单&#xff0c;属于竞赛⾥⾯的签到题&#xff08;但是&#xff0c;万事⽆绝对&#xff0c;也有可能会出现让⼈⾮常难受的 模拟题&#xf…

SparkStreaming之04:调优

SparkStreaming调优 一 、要点 4.1 SparkStreaming运行原理 深入理解 4.2 调优策略 4.2.1 调整BlockReceiver的数量 案例演示&#xff1a; object MultiReceiverNetworkWordCount {def main(args: Array[String]) {val sparkConf new SparkConf().setAppName("Networ…

Jenkins 删除历史构建记录

中文:系统管理 > 脚本命令行: 英文:Manage Jenkins > Script Console def jobName "Wens-Web" //删除的项目名称 def maxNumber 105 // 保留的最小编号&#xff0c;意味着小于该编号的构建都将被删除 Jenkins.instance.getItemByFullName(jobName).build…