发布时间:2026/8/16 23:17:54
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寄存器对应中断使能位是否打开适合中断模式本文完

相关新闻

2026/8/16 23:17:54

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

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

2026/8/16 23:17:54

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

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

2026/8/16 23:17:54

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

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

2026/8/17 0:02:57

nslookup命令使用说明

个人建站,域名备案完成后,往往还要做域名解析服务,技术人员怎么能知道自己配置的DNS正确与否呢?NSLOOKUP查询域名信息的一个非常有用的命令,可以指定查询的类型,可以查到DNS记录的生存时间还可以指定使用哪…

2026/8/17 0:02:57

【原创唯一】基于SpringBoot+Vue的在线书店商城系统 课程设计/大作业/期末作业(源码+MySQL数据库+实验报告+PPT+远程部署)

摘要 电子商务与移动支付的普及,线上购书已成为高校师生及社会公众获取图书的重要方式。传统线下书店在图书检索、库存查询、订单跟踪等方面存在信息分散、效率较低等问题。本文设计并实现了一套基于 B/S 架构的网上书店系统,采用前后端分离模式&#xf…

2026/8/17 0:02:57

飞书局域网文件传输实战:3种方案实现高速点对点传输

1. 项目概述:为什么要在局域网内用飞书传文件? 飞书作为一款主流的协同办公套件,其核心功能是围绕云端协作设计的。无论是文档、表格还是文件,通常的分享逻辑都是“上传到云端 -> 生成链接 -> 分享给同事”。这个流程在互联…

2026/8/17 0:02:57

LabVIEW异步调用实战:解决界面卡顿与并行处理难题

1. 项目概述:为什么异步调用是LabVIEW进阶的必经之路如果你在LabVIEW里写过稍微复杂点的程序,尤其是涉及到界面响应、多任务并行或者硬件IO等待,大概率会遇到一个头疼的问题:程序“卡”住了。前面板点不动,进度条不更新…

2026/8/16 23:57:56

开发者如何高效利用GitHub日报:从信息筛选到工程实践

1. 项目日报的价值:为什么开发者需要关注每日精选 每天打开GitHub,面对海量的新项目、新提交和趋势榜单,你是不是也常常感到信息过载,无从下手?作为一个在开源社区摸爬滚打了十多年的老码农,我深知这种“选…

2026/8/16 0:00:35

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/16 0:00:36

工业传感器与变送器详解:序章 从物理世界到工业数据

序章 从物理世界到工业数据 ——重新认识工业传感器与变送器 工业自动化系统正变得日益复杂。今天的工业现场早已不是简单的控制回路,而是由多层技术共同构成的立体体系:PLC、DCS、SCADA、MES、工业互联网、边缘计算与人工智能。控制系统可以执行复杂算法,工业网络可以实现…

2026/8/17 0:02:57

LabVIEW异步调用实战:解决界面卡顿与并行处理难题

1. 项目概述:为什么异步调用是LabVIEW进阶的必经之路如果你在LabVIEW里写过稍微复杂点的程序,尤其是涉及到界面响应、多任务并行或者硬件IO等待,大概率会遇到一个头疼的问题:程序“卡”住了。前面板点不动,进度条不更新…

2026/8/17 0:02:57

飞书局域网文件传输实战:3种方案实现高速点对点传输

1. 项目概述:为什么要在局域网内用飞书传文件? 飞书作为一款主流的协同办公套件,其核心功能是围绕云端协作设计的。无论是文档、表格还是文件,通常的分享逻辑都是“上传到云端 -> 生成链接 -> 分享给同事”。这个流程在互联…

2026/8/15 9:46:39

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/16 16:53:03

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/15 9:46:30

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…