STM32学习22--DMA
一、DMA简介1、DMADirect Memory Accss直接存储器访问。数据搬运不占用CPU由DMA控制器完成内存↔外设、内存↔内存数据搬运无需CPU干预。2、12个独立可配置的通道DMA17个通道DMA25个通道3、每个通道都支持软件触发和特定的硬件触发一般存储器之间用软件外设和存储器之间用硬件。4、STM32F103C8T6只有DMA17个通道5、DMA属于AHB外设时钟是RCC_AHBPeriphClockCmd不是APB1/APB2。二、核心硬件1、寄存器DMA每个通道都有一组寄存器以DMA1_Channelx为例寄存器作用对应结构体成员CCR通道配置寄存器使能、方向、循环、地址递增、位宽、优先级、M2M模式DMA_InitTypeDef大部分参数CPAR外设地址寄存器DMA_PeripheralBaseAddrCMAR内存地址寄存器DMA_MemoryBaseAddrCNDTR传输计数寄存器0~65535,剩余要传输的次数DMA_BufferSizeCIFR/CCRIFR中断标志、中断清除寄存器DMA_GetFlagStatus/DMA_ClearFlag注CNDTR总传输次数每搬完1个数据硬件自动-1,。标准库函数DMA_SetCurrDataCounter通道开启时也不生效先关DMA写入DMA_SetCurrDataCounter该函数再打开DMA。即2、3种传输方向1外设→存储器ADC采样、串口接收数组硬件触发2存储器→外设数组发送串口、SPI输出硬件触发以上2种需要外设输出DMA请求信号比如串口TX为空、RX收到字节、ADC转换结束否则DMA只是待命不会主动搬运等外设发出请求才搬运。所以除了配置DMA必须打开外设的DMA请求使能USART_DMACmd()ADC_Cmd()3存储器→存储器M2MSRAM复制软件触发禁止循环模式3、2种工作模式1DMA_Mode_Normal正常模式计数器CNDTR(DMA通道x传输数量寄存器(DMA_CNDTRx)(x 1…7)自减到0通道自动停止再次传输必须先关闭DMA重写BufferSize再使能。即DMA_Cmd(DISABLE)→修改BufferSize→DMA_CmdENABLE。2DMA_Mode_Circular循环模式计数到0硬件自动重装初始计数、初始地址持续循环。适合ADC连续采样、环形缓冲区注M2M内存拷贝不能开启Circular会无限搬运死机3地址递增PerpheralInc外设地址递增外设寄存器 DR固定一般关闭DISABLE;MemoryInc内存地址递增数组缓存一般开启Enable注自增步长数据宽度Byte1HalfWord2Word44数据宽度PerpheralDataSize/MemoryDataSizeByte8bitHalfWord16bitWord32bit源、目标最好宽度一致避免错位5通道优先级4档LOW/Medium/High/VeryHigh多个DMA同时请求时需要仲裁DMA1和2之间没有优先级比较6通道映射每个DMA通道绑定固定外设请求不能随便选通道比如CHI1ADC1CH4USART1_TXCH5USART1_RXCH6USART2_RXCH7USART2_TX同一个通道同一时刻只能服务一个外设。4、标志位函数DMA每个通道有3个事件标志TCTransfer Complete传输完成CNTDR计数到0HTHalf Transfer半传输CNTDR降到一半TETransfer Error传输错误总线访问出错三、DMA结构体uint32_t DMA_PeripheralBaseAddr 外设基地址uint32_t DMA_MemoryBaseAddr存储器基地址uint32_t DMA_DIR传输方向uint32_t DMA_BufferSize 传输次数最大65535CNDTR寄存器uint32_t DMA_PeripheralInc外设地址自增uint32_t DMA_MemoryInc内存地址自增uint32_t DMA_PeripheralDataSize外设位宽uint32_t DMA_MemoryDataSize;内存位宽uint32_t DMA_Mode 模式normal/circularuint32_t DMA_Priority通道优先级uint32_t DMA_M2M内存到内存使能四、代码部分练习1内存到内存拷贝M2MDMA1_Channel1实现每秒11、MyDMA.c文件#include stm32f10x.h // Device header uint16_t MyDMA_Size; void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size) { MyDMA_Size Size; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); //DMA初始化 DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_BufferSize Size;//CNDTR初始值 DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralSRC;//外设作为源外设→内存//DMA_DIR_PeripheralDST//外设作为目标内存→外设 DMA_InitStructure.DMA_M2M DMA_M2M_Enable ;//软件触发disable是硬件触发 DMA_InitStructure.DMA_PeripheralBaseAddr AddrA;//外设基地址 DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Enable; DMA_InitStructure.DMA_MemoryBaseAddr AddrB ;//存储器基地址 DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_Priority DMA_Priority_Medium; DMA_InitStructure.DMA_Mode DMA_Mode_Normal; DMA_Init(DMA1_Channel1,DMA_InitStructure); //先关闭 DMA_Cmd(DMA1_Channel1,DISABLE); } void MyDMA_Transfer() { DMA_Cmd(DMA1_Channel1,DISABLE); DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size); DMA_Cmd(DMA1_Channel1,ENABLE); while(DMA_GetFlagStatus(DMA1_FLAG_TC1) RESET); DMA_ClearFlag(DMA1_FLAG_TC1); }2、MyDMA.h文件#ifndef __MYDMA_H #define __MYDMA_H void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size); void MyDMA_Transfer(); #endif3、main函数#include stm32f10x.h // Device header #include Delay.h #include MyDMA.h #include OLED.h uint8_t DataA[]{0x01,0x02,0x03,0x04}; uint8_t DataB[]{0,0,0,0}; int main() { OLED_Init(); MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,4); OLED_ShowString(1,1,DataA); OLED_ShowString(3,1,DataB); OLED_ShowHexNum(1,8,(uint32_t)DataA,8); OLED_ShowHexNum(3,8,(uint32_t)DataB,8); OLED_ShowHexNum(2,1,DataA[0] ,2); OLED_ShowHexNum(2,4,DataA[1] ,2); OLED_ShowHexNum(2,7,DataA[2] ,2); OLED_ShowHexNum(2,10,DataA[3] ,2); OLED_ShowHexNum(4,1,DataB[0] ,2); OLED_ShowHexNum(4,4,DataB[1] ,2); OLED_ShowHexNum(4,7,DataB[2] ,2); OLED_ShowHexNum(4,10,DataB[3] ,2); while(1) { DataA[0]; DataA[1]; DataA[2]; DataA[3]; OLED_ShowHexNum(2,1,DataA[0] ,2); OLED_ShowHexNum(2,4,DataA[1] ,2); OLED_ShowHexNum(2,7,DataA[2] ,2); OLED_ShowHexNum(2,10,DataA[3] ,2); OLED_ShowHexNum(4,1,DataB[0] ,2); OLED_ShowHexNum(4,4,DataB[1] ,2); OLED_ShowHexNum(4,7,DataB[2] ,2); OLED_ShowHexNum(4,10,DataB[3] ,2); Delay_ms(1000); MyDMA_Transfer(); OLED_ShowHexNum(2,1,DataA[0] ,2); OLED_ShowHexNum(2,4,DataA[1] ,2); OLED_ShowHexNum(2,7,DataA[2] ,2); OLED_ShowHexNum(2,10,DataA[3] ,2); OLED_ShowHexNum(4,1,DataB[0] ,2); OLED_ShowHexNum(4,4,DataB[1] ,2); OLED_ShowHexNum(4,7,DataB[2] ,2); OLED_ShowHexNum(4,10,DataB[3] ,2); Delay_ms(1000); } }4、注意事项1时钟是AHB不是APB。2通道选错每个通道绑定固定外设不能随便选一个通道就用ADC/USART看参考手册3开启DMA通道后修改BufferSize必须先DMA_CmdDISABLE否则CNDTR无效4M2M模式开始Circular循环直接死循环5Normal模式传输完成CNDTR变成0通道不再响应硬件请求想要再次传输必须先关闭DMA再重写BufferSize然后使能DMA_Cmd6内存地址对齐Word32位传输内存数组尽量4字节对齐否则会异常练习2ADC1配合DMA循环采样外设→内存循环模式DMA不停把ADC转换结果写入数组CNDTR到0后自动重装CPU随时读取数组的值拿到采样值不需要中断1、AD.c文件#include stm32f10x.h // Device header uint16_t AD_Value[4]; void AD_Init() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); //ADC分频最大14MHz RCC_ADCCLKConfig(RCC_PCLK2_Div6); //GPIOA配置模拟输入 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 |GPIO_Pin_1|GPIO_Pin_2 |GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5); //初始化ADC ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_ContinuousConvMode ENABLE; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right ; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None ;//不使用外部触发使用软件触发 ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_NbrOfChannel 4; ADC_InitStructure.ADC_ScanConvMode ENABLE; ADC_Init(ADC1,ADC_InitStructure); //DMA初始化 DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_BufferSize 4;// DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralSRC;/// DMA_InitStructure.DMA_M2M DMA_M2M_Disable ;// DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)ADC1-DR;// DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_HalfWord;// DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable;// DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)AD_Value ;// DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_HalfWord;// DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable;// DMA_InitStructure.DMA_Priority DMA_Priority_Medium;// DMA_InitStructure.DMA_Mode DMA_Mode_Circular;// DMA_Init(DMA1_Channel1,DMA_InitStructure);//ADC1只能通道1 DMA_Cmd(DMA1_Channel1,ENABLE); ADC_DMACmd(ADC1,ENABLE); ADC_Cmd(ADC1,ENABLE); ADC_ResetCalibration(ADC1);//复位标准 while(ADC_GetResetCalibrationStatus(ADC1) SET);//等待复位校准完成 ADC_StartCalibration(ADC1);//开始校准 while(ADC_GetCalibrationStatus(ADC1) SET);//等待校准完成 ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发转换 }2、AD.h文件#ifndef __AD_H #define __AD_H void AD_Init(); extern uint16_t AD_Value[4]; #endif3、main函数#include stm32f10x.h #include Delay.h #include AD.h #include OLED.h int main() { OLED_Init(); AD_Init(); OLED_ShowString(1,1,AD0:); OLED_ShowString(2,1,AD1:); OLED_ShowString(3,1,AD2:); while(1) { OLED_ShowNum(1,5,AD_Value[0],4); OLED_ShowNum(2,5,AD_Value[1],4); OLED_ShowNum(3,5,AD_Value[2],4); Delay_ms(100);//防止转换快数值闪烁 } }4、注意事项1循环模式下不要轮询TC完成标志TC会反复置1。2开启外设DMA使能DMA配置完还要调用ADC_DMACmd打开外设DMA请求信号。五、中断1、DMA没有独立的状态寄存器状态全部在中断状态寄存器ISR分两类1状态标志硬件状态位只读不管开不开中断都会置1就算关闭DMA所有中断DMA硬件运行事件发生标志位同样会变2中断使能位CCR寄存器控制是否产生中断输出开中断CCR寄存器里对应中断使能位要置1才会输出到NVIC产生中断如果不开中断可以轮询读取ISR的标志位。3清除中断只能IFCR不能直接写ISR2、轮询VS中断轮询查询硬件状态标志不开启DMA中断不开NVIC不开CCR里的TCIE允许传输完成中断只读ISR的状态标志ISR.TCIFx读取通道x传输完成状态标志TCIF返回SET/RESET库函数FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);清除状态标志函数void DMA_ClearFlag(uint32_t DMAy_FLAG);中断允许中断配置NVIC开DMA写中断服务函数在中断服务函数里依然是读取ISR标志清除IFCRvoid DMA1_Channel1_IRQHandler() { if(DMA_GetITStatus(DMA1_IT_TC1)SET) { //其他代码 DMA_ClearITPendingBit(DMA1_IT_TC1); } }二者区别DMA_GetFlagStatus()只读取ISR硬件状态不看CCR中断使能位适合轮询模式DMA_GetITStatus()读ISR状态标志并且同时判断CCR寄存器对应中断使能位是否打开适合中断模式本文完

相关新闻

十分钟精通《三步擒龙》策略:全套指标解析

十分钟精通《三步擒龙》策略:全套指标解析

** 台上一分钟台下十年功**,虽然《三步擒龙》策略战法集成了多年的私募机构投资经验,但是如果没有彻底搞懂策略战法的使用,到最后也会竹篮打水一场空,只有彻底精通掌握《三步擒龙》策略战法才能对待主力量化交易知己知彼&#xff…

2026/10/27 14:46:00 阅读更多 →
Java入门零基础指南:手把手教你搭建第一个可运行的Java项目

Java入门零基础指南:手把手教你搭建第一个可运行的Java项目

很多刚接触编程的零基础小伙伴,首选的入门语言基本都是Java。作为目前就业生态最完善、岗位需求最多的编程语言,Java稳定性强、应用场景广,不管是后端开发、软件开发还是企业项目搭建,都占据着举足轻重的地位。但我发现绝大多数新…

2026/10/27 14:04:53 阅读更多 →
Java微服务实战:基于Spring Cloud Alibaba的全链路架构搭建

Java微服务实战:基于Spring Cloud Alibaba的全链路架构搭建

接触Java开发的朋友都清楚,现在企业后端开发早已告别传统单体架构。早些年我们做项目,所有业务代码、逻辑处理、接口服务全部堆砌在一个工程里,开发简单、初期迭代快,但项目体量变大后,各种问题接踵而至。代码臃肿混乱…

2026/10/28 18:11:40 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/27 17:33:32 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/28 7:28:00 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/28 9:02:11 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/27 18:20:50 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/28 9:00:54 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/28 7:29:02 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/28 16:58:40 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/28 9:01:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/28 19:25:09 阅读更多 →