STM32F10x 串口使用DMA
一、DMA简介
DMA(Direct Memory Access,直接存储器访问) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。
二、DMA的工作原理
DMA 传输将数据从一个地址空间复制到另外一个地址空间。当CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。
在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。
传输的四种情况:
- 外设到内存
- 外设到外设
- 内存到外设
- 内存到内存
- DMA主要特征
- 12个独立的可配置的通道(请求):DMA1有7个通道,DMA2有5个通道
- 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。
- 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) 。
- 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。
- 支持循环的缓冲器管理
- 每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。
- 存储器和存储器间的传输
- 外设和存储器、存储器和外设之间的传输
- 闪存、SRAM、外设的SRAM、APB1 、APB2和AHB外设均可作为访问的源和目标。
- 可编程的数据传输数目:最大为65535
DMA框图

DMA硬件优先级


各个通道的DMA


三、程序分析
- 初始化结构体
typedef struct
{
uint32_t DMA_PeripheralBaseAddr;
uint32_t DMA_MemoryBaseAddr;
uint32_t DMA_DIR;
uint32_t DMA_BufferSize;
uint32_t DMA_PeripheralInc;
uint32_t DMA_MemoryInc;
uint32_t DMA_PeripheralDataSize;
uint32_t DMA_MemoryDataSize;
uint32_t DMA_Mode;
uint32_t DMA_Priority;
uint32_t DMA_M2M;
}DMA_InitTypeDef;
- 配置DMA
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx, u32 peripheral_base)
{
DMA_CHxx = DMA_CHx;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
DMA_DeInit(DMA_CHx); //将DMA的通道1寄存器重设为缺省值
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendBuff; //DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向,从内存读取发送到外设
DMA_InitStructure.DMA_BufferSize = SEND_BUF_SIZE; //DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
DMA_Init(DMA_CHx, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Rx_DMA 所标识的寄存器
}
注意:需要注意的是以下几点
- 这里内存的地址可以理解为自己定义的变量地址,对于外设地址不了解的小伙伴可以看我之前的笔记STM32寄存器深入分析
- DMA缓存长度最大为65535
- DMA的通道1可以观察上面的DMA的硬件优先级比如USART1_TX使用的是通道4,USART1_RX使用的是通道5,通入如下图所示:

四、试验一
串口通过DMA发送数据,具体源码如下所示:
- main.c文件
#include "stm32f10x.h"
#include "usart1.h"
uint8_t buff[] = "STM32F10x DMA 串口实验";
/*************** 延时函数 *******************/
void Delay(__IO u32 nCount)
{
for(; nCount != 0; nCount--);
}
int main(void)
{
SystemInit(); //配置系统时钟为 72M
USART1_Config(); //USART1 配置
MYDMA_Config(DMA1_Channel4, (uint32_t)&USART1->DR);//DMA1通道4,外设为串口1,存储器为SendBuff,长度SEND_BUF_SIZE.
while (1)
{
MYDMA_Enable((uint8_t *)buff, sizeof(buff));//开始一次DMA传输!
Delay(1000000);
}
}
- usart.c文件
#include "usart1.h"
#include <stdarg.h>
#include "stm32f10x_dma.h"
#include "string.h"
DMA_InitTypeDef DMA_InitStructure;
DMA_Channel_TypeDef* DMA_CHxx;
#define SEND_BUF_SIZE 1024 //发送数据长度
uint8_t SendBuff[SEND_BUF_SIZE]; //发送数据缓冲区
// 初始化USART1
void USART1_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* 使能 USART1 时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
/* USART1 使用IO端口配置 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // PA.9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // PA.10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA
/* USART1 工作模式配置 */
USART_InitStructure.USART_BaudRate = 115200; //波特率设置:115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据位数设置:8位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位设置:1位
USART_InitStructure.USART_Parity = USART_Parity_No ; //是否奇偶校验:无
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制模式设置:没有使能
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//接收与发送都使能
USART_Init(USART1, &USART_InitStructure); //初始化USART1
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //使能串口1的DMA发送
USART_Cmd(USART1, ENABLE); //使能串口1
}
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx, u32 peripheral_base)
{
DMA_CHxx = DMA_CHx;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
DMA_DeInit(DMA_CHx); //将DMA的通道1寄存器重设为缺省值
DMA_InitStructure.DMA_PeripheralBaseAddr = peripheral_base; //DMA外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendBuff; //DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向,从内存读取发送到外设
DMA_InitStructure.DMA_BufferSize = SEND_BUF_SIZE; //DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
DMA_Init(DMA_CHx, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Rx_DMA 所标识的寄存器
}
uint16_t MYDMA_Enable(uint8_t* buff, uint16_t size)
{
uint16_t data_size;
if (size <= 0){
return 0;
}
data_size = (size > SEND_BUF_SIZE ? SEND_BUF_SIZE:size);
if (!buff){
return 0;
}
memcpy(SendBuff, buff, data_size);
DMA_Cmd(DMA_CHxx, DISABLE ); //关闭USART1 TX DMA1 所指示的通道
DMA_CHxx->CNDTR = data_size;// 设置发送长度
DMA_Cmd(DMA_CHxx, ENABLE); //使能USART1 TX DMA1 所指示的通道
return 0;
}
参考文献
STM32 DMA工作原理:https://blog.csdn.net/baidu_37366055/article/details/98069744
STM32之DMA原理:https://blog.csdn.net/lushoumin/article/details/78907526
STM32F10x 串口使用DMA的更多相关文章
- (三)stm32之串口通信DMA传输完成中断
一.DMA功能简介 首先唠叨一下DMA的基本概念,DMA的出现大大减轻了CPU的工作量.在硬件系统中,主要由CPU(内核).外设.内存(SRAM).总线等结构组成,数据经常要在内存和外设之间,外设和外 ...
- STM32F407的串口采用DMA收发数据
源:STM32F407的串口采用DMA收发数据
- STM32 串口USART DMA方式发送接收数据
硬件:stm32f103cbt6 软件:STM32F10x_StdPeriph_Lib_V3.5.0 文章目录 头文件 USART3_DR的地址 DMA的通道 DMA的中断 USART接收回调函数 头 ...
- 串口通信DMA中断
这是以前学32的时候写的,那时候学了32之后感觉32真是太强大了,比51强的没影.关于dma网上有许多的资料,亲们搜搜,这里只贴代码了,其实我也想详详细细地叙述一番,但是自己本身打字就慢,还有好多事情 ...
- STM32单片机串口中断+DMA使用(含CUBE配置)
最近又要重新用32做点东西,发现一两年没怎么碰的结果就是,曾经熟得不行的东西都变得极度陌生,这种重新学习记忆的过程过于痛苦,果然还是要留下一些记录给之后失忆的自己的. 1.STM32CUBE配置 1. ...
- STM32CubeMx——串口使用DMA收发
用到的是DMA发送数据,接收还是普通的串口接收. 一.代码生成 1.按以前的方法设置好时钟和调试方式,这里就不多说了. 2.设置串口1. 3.在DMA Setting里点击Add添加USART1_TX ...
- 串口配合DMA接收不定长数据(空闲中断+DMA接收)-(转载)
1.空闲中断和别的接收完成(一个字节)中断,发送完成(发送寄存器控)中断的一样是串口中断: 2.空闲中断是接收到一个数据以后,接收停顿超过一字节时间 认为桢收完,总线空闲中断是在检测到在接收数据后, ...
- STM32使用串口1配合DMA接收不定长数据,减轻CPU载荷
STM32使用串口1配合DMA接收不定长数据,减轻CPU载荷 http://www.openedv.com/thread-63849-1-1.html 实现思路:采 用STM32F103的串口1,并配 ...
- STM32之串口DMA接收不定长数据
STM32之串口DMA接收不定长数据 引言 在使用stm32或者其他单片机的时候,会经常使用到串口通讯,那么如何有效地接收数据呢?假如这段数据是不定长的有如何高效接收呢? 同学A:数据来了就会进入串口 ...
- RT-Thread中的串口DMA分析
这里分析一下RT-Thread中串口DMA方式的实现,以供做新处理器串口支持时的参考. 背景 在如今的芯片性能和外设强大功能的情况下,串口不实现DMA/中断方式操作,我认为在实际项目中基本是不可接受的 ...
随机推荐
- sklearn库主要模块功能简介
1.sklearn库简介 sklearn,全称scikit-learn,是python中的机器学习库,建立在numpy.scipy.matplotlib等数据科学包的基础之上,涵盖了机器学习中的样例数 ...
- ElasticSearch分页查询的实现
1.设置mapping PUT /t_order { "settings": { "number_of_shards": 1, "number_of_ ...
- 浅析JS原型链
目录 实例对象 原型对象 对象原型 短暂总结一下 constructor 原型链 何为原型链呢? 就是实例对象和原型对象之间的链接,每一个对象都有原型,原型本身又是对象,原型又有原型,以此类推形成一个 ...
- 【环境配置】vscode配置C C++开发和调试环境
按照本教程配置好之后,不再需要写任何tasks.json和launch.json文件,减轻记忆负担,即使是调试程序,也不用再写这些文件了,跟着做就能得到一个很好的集成开发环境 目录 msys2的安装与 ...
- OREPA:阿里提出训练也很快的重参数策略,内存减半,速度加倍 | CVPR 2022
论文提出了在线重参数方法OREPA,在训练阶段就能将复杂的结构重参数为单卷积层,从而降低大量训练的耗时.为了实现这一目标,论文用线性缩放层代替了训练时的BN层,保持了优化方向的多样性和特征表达能力.从 ...
- Android将数据导入到已有的excel表格_0
用到的jxl2.6.12 jar 包下载地址: https://mvnrepository.com/artifact/net.sourceforge.jexcelapi/jxl/2.6.12
- #线段树#洛谷 4340 [SHOI2016]随机序列
题目 分析 可以发现加号和减号会抵消掉,真正有用的答案就是第一段的乘积. 那也就是 \(\sum_{i=1}^nS_i*2*3^{n-i-1}\),其中 \(S_i\) 表示 \(a_1\) 到 \( ...
- OpenAtom OpenHarmony分论坛,今天14:00见!附大事记精彩发布
2022开放原子全球开源峰会 OpenAtom OpenHarmony分论坛 万物互联,使能千行百业 整装待发!精彩今日揭晓与您相约7月27日 14:00
- 三步就能在OpenHarmony中实现车牌识别
介绍 本车牌识别项目是基于开源项目 EasyPR(Easy to do Plate Recognition)实现.EasyPR 是一个开源的中文车牌识别系统,基于 OpenCV 开源库开发. 本项目使 ...
- Java 容器详解:使用与案例
Java容器是一套工具,用于存储数据和对象.可以与C++的STL类比.Java容器也称为Java Collection Framework (JCF).除了存储对象的容器之外,还提供了一套工具类,用于 ...