【详细讲解在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 256uint8_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通道0DMA_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通道0DMA_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的配置和使用需要仔细理解其工作原理和具体的配置步骤,确保在实际应用中的正确性和稳定性。
在实际开发中,还应结合具体的应用场景和硬件配置,进行合理的参数设置和优化,以达到最佳的传输效率和稳定

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

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

相关文章

M系列芯片 MacOS 在 Conda 环境中安装 TensorFlow 2 和 Keras 3 完整指南

目录 1. 引言2. 环境准备3. 安装 TensorFlow 和必要依赖4. 结语Reference 1. 引言 Keras 是搞深度学习很可爱的工具,其友好的接口让我总是将其作为搭建模型原型的首选。然而,当我希望在 M 系列芯片的MacBook Pro上使用 Keras时,使用Conda和P…

清华北大DeepSeek六册

「清华北大-Deepseek使用手册」 链接:https://pan.quark.cn/s/98782f7d61dc 「清华大学Deepseek整理) 1-6版本链接:https://pan.quark.cn/s/72194e32428a AI学术工具公测链接:https://pan.baidu.com/s/104w_uBB2F42Da0qnk78_ew …

paddlehub hub TypeError 错误

pip install paddlehub hub install chinese_ocr_db_crnn_mobile 提示错误: TypeError: Descriptors cannot be created directly. If this call came from a _pb2.py file, your generated code is out of date and must be regenerated with protoc > 3.19.0…

零信任沙箱:为网络安全筑牢“隔离墙”

在数字化浪潮汹涌澎湃的今天,网络安全如同一艘船在波涛汹涌的大海中航行,面临着重重挑战。数据泄露、恶意软件攻击、网络钓鱼等安全威胁层出不穷,让企业和个人用户防不胜防。而零信任沙箱,就像是一座坚固的“隔离墙”,…

【String】917. 仅仅反转字母

917. 仅仅反转字母 - 力扣(LeetCode) 使用双指针,一个指针指向s的开始,一个指向s的末尾,同时遍历即可。

大语言模型学习

大语言模型发展历程 当前国内外主流LLM模型 ‌一、国外主流LLM‌ ‌LLaMA2‌ Meta推出的开源模型,参数规模涵盖70亿至700亿,支持代码生成和多领域任务适配‌57。衍生版本包括Code Llama(代码生成优化)和Llama Chat(对…

3dsmax烘焙光照贴图然后在unity中使用

效果预览 看不清[完蛋!] 实现步骤 使用 软件 软体名称地址photoshophttps://www.adobe.com/products/photoshop.htmlunity3Dhttps://unity.com/3dsmaxhttps://www.autodesk.com.cn/products/3ds-max/free-trialpacker-iohttps://www.uv-packer.com/HDR 贴图地址…

P8651 [蓝桥杯 2017 省 B] 日期问题--注意日期问题中2月的天数 / if是否应该连用

P8651 [P8651 [蓝桥杯 2017 省 B] 日期问题--注意日期问题中2月的天数 / if是否应该连用 题目 分析代码 题目 分析 代码中巧妙的用到3重循环,完美的解决了输出的顺序问题【题目要求从小到大】 需要注意的是2月的值,在不同的年份中应该更新2月的值 还有…

android 横竖屏适配工作总结

1、创建一个横屏文件夹,复制一份竖屏的布局。然后修改适配横屏。只要布局id都有,其他想怎么改就怎么修改。 2、最好使用kotlin语言编写和使用viewBinding绑定控件,可以使用?.判空控件是否存在,不至于缺少这个控件时候直接崩溃。 …

VS2022远程调试Ubuntu中的C++程序

前言 最近想基于星火大模型的SDK开发第一些应用。但是,发现星火的SDK当中Linux版本的比较丰富,Windows 版本支持的比较少。但是,从调试的IDE而言,Visual Studio又是最方便的。所以,考虑采用Visual Studio Ubuntu的形式…

VS Code(Cursor)远程开发调试教程(超详细)

前言 📢 声明:本文配置及开发方法同样适合Cursor !! 在开始之前,你需要准备以下东西: 本地电脑: 安装好 VS Code(Windows、Mac 或 Linux 都可以)。 官网下载&#xff0c…

【C++】类与对象:深入理解默认成员函数

类与对象:深入理解默认成员函数 引言1、默认成员函数概述2、构造函数与析构函数2.1 默认构造函数2.2 析构函数 3、拷贝控制成员3.1 拷贝构造函数3.2 赋值运算符重载 4、移动语义(C11)4.1 移动构造函数4.2 移动赋值运算符 5、三五法则与最佳实…

QT实现计算器

1:在注册登录的练习里面, 追加一个QListWidget 项目列表 要求:点击注册之后,将账号显示到 listWidget上面去 以及,在listWidget中双击某个账号的时候,将该账号删除 Widget.h #ifndef WIDGET_H #define…

算法进阶——二分

二分法: 一种高效查找方法,将问题搜索范围一分为二,迭代地缩小范围,直到找到目标。 二分法适用于有序的数据集合。 常见的二分类型有: 整数二分 浮点二分 二分答案 二分解题步骤: 1.研究并发现数据…

Kotlin函数式编程与Lambda表达式

Kotlin函数式编程与Lambda表达式 一、函数式编程基础 1.1 什么是函数式编程 函数式编程是一种编程范式,它将计算过程视为数学函数的求值,强调使用不可变数据和纯函数。在Kotlin中,函数式编程的特性让我们能够写出更简洁、更易维护的代码。…

Java 并行流(parallelStream)详解

目录 1. 什么是 parallelStream?2. parallelStream 的优势3. parallelStream 的使用3.1 基本使用3.2 计算总和示例3.3 结合groupingByConcurrent实现线程安全的分组操作 4. parallelStream 的注意事项4.1 适用场景4.2 并行流的局限性 5. 控制并行流线程数6. 总结 1.…

Ubuntu 20.04下配置VSCode以支持OpenCV库开发

Ubuntu 20.04下配置VSCode以支持OpenCV库开发 1. 安装OpenCV库安装OpenCV(推荐使用APT安装)或者从源码安装OpenCV(可选) 2. 安装VSCode的C扩展3. 配置c_cpp_properties.json4. 编写代码并测试5. 配置tasks.json(编译Op…

io学习----->标准io

思维导图: 一.io的作用 io是实现对文件的操作,把运行结果存到文件中,读取文件的数据,方便后期查询。 二.io的概念 io是指系统 和外部设备或用户之间的数据交互 I:input 表示数据从外部设备输入到内存中; O:output…

使用消息队列怎样防止消息重复?

大家好,我是君哥。 使用消息队列时,我们经常会遇到一个可能对业务产生影响的问题,消息重复。在订单、扣款、对账等对幂等有要求的场景,消息重复的问题必须解决。 那怎样应对重复消息呢?今天来聊一聊这个话题。 1.三…

005 公网访问 docker rocketmq

文章目录 创建自定义网络创建NameServer容器创建Broker容器正式开始启动 Nameserver 容器启动 Broker 容器并关联 Nameserverdocker exec -it rmqbroker vi /etc/rocketmq/broker.conf检查 namesrv 解析检查 Broker 注册状态Nameserver 日志Broker 日志检查容器日志手动指定 Br…