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
本文来自博客园,作者:浇筑菜鸟,转载请注明原文链接:https://www.cnblogs.com/jzcn/p/15814823.html
如本博客的内容侵犯了你的权益,请与以下地址联系,本人获知后,马上删除。同时本人深表歉意,并致以崇高的谢意! cn_jiaozhu@qq.com
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!