STM32学习记录(八):DMA

news/2024/11/20 16:46:43/文章来源:https://www.cnblogs.com/qianxiaohan/p/18346157

什么是DMA?

DMA在之前的学习中已经用过了。那么,什么是DMA?

Direct memory access (DMA) is used in order to provide high-speed data transfer between peripherals and memory as well as memory to memory. Data can be quickly moved by DMA without any CPU actions. This keeps CPU resources free for other operations.

直接内存访问(Direct memory access:DMA)可用于外设和内存、内存和内存之间的高速数据传输。数据可以在没有CPU的参与下被快速移动,这使得CPU资源可用于其他操作。简而言之,DMA能减轻CPU的压力。

DMA框图

STM32F103C8T6提供了1个7通道的DMA,其结构框图如下:

每个通道都可以处理位于固定地址的外围寄存器和存储器地址之间的DMA传输,也可以实现内存到内存(Memory-to-memory)模式的DMA传输。

不同的外设使用的DMA通道可能不一样,比如,ADC1只能向DMA1的通道1发送请求、TIM1_UP(TIM1发生更新时)向DMA1的通道5发送请求,如下表所示:

DMA示例

DMA配置的流程图

下面是没有使用DMA中断的配置流程图:

flowchart TBid1["开启DMA、外设时钟"] --> id2["配置DMA、外设初始化结构体"] --> id3["开启DMA"] --> id4["外设开启DMA"]

DMA+ADC1读取内部温度传感器

见另一篇博客:STM32学习记录(七):ADC

DMA使用M2M模式实现数据搬运

DMA有内存到内存(Memory to Memory)模式,使用DMA将数据从一个内存地址搬运到另一个内存地址,传输的数据大小可以是8位(字节)、16位(半字)、32位(字)。DMA初始化结构体有几个参数要注意:

  • DMA_PeripheralBaseAddr:默认是外设地址,在M2M模式下是内存的地址
  • DMA_MemoryBaseAddr:内存的地址
  • DMA_PeripheralDataSize:外设数据大小,8位、16位或32位
  • DMA_BufferSize:缓冲区数据个数
  • DMA_DIR:数据搬运的方向。方向可为:外设--->内存、外设<----内存
  • DMA_PeripheralInc:外设地址是否递增,取决于数据的个数,如果用一个长度为8的数组,地址就需要递增
  • DMA_MemoryInc:内存地址是否递增,取决于数据的个数
  • DMA_Mode:有常规(normal)模式和循环(circular)模式,当DMA使用内存到内存模式时,只能选择常规模式

对于DMA1中断,有几种中断源:

  • DMA1_IT_GL1,DMA1通道1全局中断
  • DMA1_IT_TC1,DMA1通道1传输完成中断
  • DMA1_IT_HT1,DMA1通道1传输一半中断
  • DMA1_IT_TE1,DMA1通道1传输错误中断

中断处理函数都是先判断外设的中断标志位,再执行相应的操作,最后清除中断标志位。

下面的例子用DMA1将SRC_Buffer数组的数据搬运到DST_Buffer数组中,可以直接从DST_Buffer数组中读取DMA搬运的数据,并通过串口发送出去

#include "stm32f10x.h"                  
#include "delay.h"
#include "usart.h"
#include <stdio.h>
#define BufferSize 8void DMA_Configuration(void);			
void RCC_Configuration(void);			
void NVIC_Configuration(void);			
uint8_t BufferCompare(const uint32_t *buffer1, uint32_t *buffer2, uint32_t buffersize);const uint32_t SRC_Buffer[BufferSize] = {0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20}; //DMA要搬运的数据uint32_t DST_Buffer[BufferSize];	//DMA搬运的数据的目的内存地址uint32_t CurrDataCounterEnd = 0x01;		//DMA剩余要搬运的数据单元个数,等于0时表示DMA搬运完成int main(void)
{COM_Init();				//串口初始化,采样数据RCC_Configuration();	//配置DMANVIC_Configuration();	//配置DMA时钟DMA_Configuration();	//配置NVIC/* 等待数据传输完成 */while(CurrDataCounterEnd != 0){}/* 判断数据传输是否丢失,返回1就向串口发送数据 */if(BufferCompare(SRC_Buffer, DST_Buffer, BufferSize)){for(int i = 0; i < BufferSize; i++){printf("%lX ", DST_Buffer[i]);	//以16进制形式打印delay_ms(20);}}while (1){}
}void RCC_Configuration(void)
{/* 开启DMA时钟 */RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
}void NVIC_Configuration(void)
{NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;	//使能DMA1通道1中断通道NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_Init(&NVIC_InitStructure);
}/* 比较两个内存地址的数据是否相等 */
uint8_t BufferCompare(const uint32_t *buffer1, uint32_t *buffer2, uint32_t buffersize)
{while(buffersize--){if(*buffer1 != *buffer2) return 0;buffer1++;buffer2++;}return 1;
}void DMA_Configuration(void)
{/* 反初始化DMA1通道1 */DMA_DeInit(DMA1_Channel1);/* 配置DMA初始化结构体 */DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Buffer;		//配置外设基地址,这里是ADC1数据寄存器的地址DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;		//配置内存基地址,这里是数据存放的内存地址DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;	//外设数据大小,32位, 双ADC模式DMA_InitStructure.DMA_BufferSize = BufferSize;		//缓存大小,以数据单元为一个单位,比如数据是16位,BufferSize = 8表示有8x16位的数据DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;	//外设作为数据传输的来源 DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;	//开启DMA的内存到内存模式DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;	//内存数据大小,32位DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;	//内存地址递增DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;		//常规模式,即只读取1次数据到缓冲区DMA_InitStructure.DMA_Priority = DMA_Priority_High;		//优先级高DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;	//外设地址递增DMA_Init(DMA1_Channel1, &DMA_InitStructure);	//初始化DMA/* 使能DMA1传输完成中断 */DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);DMA_Cmd(DMA1_Channel1, ENABLE);	//使能DMA}/* DMA1通道1中断处理函数 */
void DMA1_Channel1_IRQHandler(void)
{if(DMA_GetITStatus(DMA1_IT_TC1) != RESET){CurrDataCounterEnd = DMA_GetCurrDataCounter(DMA1_Channel1); //DMA还需传输的数据单元个数DMA_ClearITPendingBit(DMA1_IT_TC1);	//清除TC标志位}
}

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

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

相关文章

CTF—Misc基础

一:文件操作与隐写 1、文件类型的识别 1、文件头完好情况: (1)file命令 使用file命令识别:识别出file.doc为jpg类型(2)winhex 通过winhex工具查看文件头类型,根据文件头部内容去判断文件的类型eg:JPG类型(3)notepad++ 下载HEXeditor插件,查看文件的头部信息,和010e…

位运算符

1.与(&)2.或(|)3.亦或(^)4.非(~)5.关于位运算的面试题 问:如何用电脑将2乘8最快算出?6.左移 右移的底层原理

雷达气象学(7)——反射率因子图分析(气象回波篇)

从本篇文章开始介绍反射率因子图(即雷达回波强度图)的分析与识别方法。 目录7.0 雷达回波的分类7.1 层状云降水回波7.2 积状云降水回波(对流性降水回波)7.3 层积混合降水回波7.4 零度层亮带7.5 晴空回波 7.0 雷达回波的分类 雷达回波可分为气象回波和非气象回波: \[雷达回…

(Jmeter新玩法)Python 调 Jmeter执行参数化jmx脚本

# Python 调 Jmeter执行参数化jmx脚本import os from os.path import join import time import re from string import Templatejmeter_Home = r"F:\softtotal\xxx\bin\jmeter.bat"# jmx文件路径 currpath = os.path.dirname(os.path.realpath(__file__)) # 要运行的…

超快速的百度网盘不限速下载技巧,建议偷偷使用!

小伙伴们,你们是否曾经为百度迅雷网盘限速而烦恼呢?动不动就要冲svip会员,不仅费钱,开通后还慢的气死,我现在来介绍一种全新的高效下载方法吧,只需要下载并安装一款神奇的软件,接着简单操作即可轻松实现快速下载,节省时间,这款神奇的软件具体信息在文章最后有说明。 第…

JVM内存组件划分梳理

1.JVM内存划分图2.线程栈每天坚持,终会抵达!

Flink实战(10)-checkpoint容错保证

0 前言 程序在 Flink 集群运行,某个算子因为某些原因出现故障,如何处理 在故障恢复后,如何保证数据状态,和故障发生之前的数据状态一致? 1 什么是 checkpoint(检查点)? Checkpoint 能生成快照(Snapshot)。 若 Flink 程序崩溃,重新运行程序时可以有选择地从这些快照进行恢…

一个蒟蒻对简单距离的简单理解

一个蒟蒻对简单距离的简单理解: 呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃,写的简单粗暴,如有不对的,欢迎纠正 神马是距离? 在数学中,距离是泛函分析中最基本的概念之一。它所定义的距离空间连接了拓扑空间与赋范线性空间等其他空间,是…