首页 > 其他分享 >STM32学习记录(八):DMA

STM32学习记录(八):DMA

时间:2024-08-06 23:16:43浏览次数:14  
标签:DMA 记录 NVIC void STM32 DMA1 InitStructure 外设

什么是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 TB id1["开启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 8

void 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();	//配置DMA
	NVIC_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标志位
	}
}

标签:DMA,记录,NVIC,void,STM32,DMA1,InitStructure,外设
From: https://www.cnblogs.com/qianxiaohan/p/18346157

相关文章

  • Python-记录一次迭代求和
    importitertoolsdefget_result(hope,list_input):""":paramhope:#期望相加所得参数:paramlist_input:#所有数值:return:"""defgenerate_combination(items,length):forcombinationinitertools.co......
  • 基于STM32的智能灌溉系统
    目录引言环境准备工作硬件准备软件安装与配置系统设计系统架构硬件连接代码实现初始化代码传感器读取和控制代码应用场景农业灌溉花园自动灌溉常见问题及解决方案常见问题解决方案结论1.引言智能灌溉系统通过实时监测土壤湿度和环境温度,自动控制灌溉设......
  • Java使用POI导出excel记录
    eg:@OverridepublicvoidexportExcel(HttpServletResponseresponse)throwsException{//创建Excel文档XSSFWorkbookworkbook=newXSSFWorkbook();XSSFSheetsheet=workbook.createSheet("设备厂商");//创建表头XSSFRow......
  • (CubeIDE/CubeMX STM32引脚布局)将配置的引脚转移到其它引脚
            新建了项目,配置好引脚,但是想将原先的配置换到别的引脚上,我教大家一个方法。    这里以STM32F103芯片为例,其它芯片也同样适用,先打开工程,如下图所示。     假设我现在想使用PA7点灯,但是现在PA7被SPI1占用了,那我们需要知道还有没有其它引脚可......
  • STM32启动文件,在启动文件中理解IAP
    ;********************(C)COPYRIGHT2017STMicroelectronics********************;*FileName:startup_stm32f103xe.s;*Author:MCDApplicationTeam;*Description:STM32F103xEDevicesvectortableforMDK-ARMtoolchain.;*......
  • Mybatis 记录
    1.根据列表批量修改voidsecretKeySequence(@Param("list")List<IndustrialShareDto>list);<updateid="secretKeySequence">updatecloud_industrial_setset`index`=case<foreachcollection="list"it......
  • [记录]跟老客户签了保密协议,不给打广告怎么办?
    其实我们在做销售的过程中,经常碰到一种很头痛的问题,就是某个客户问你,在我们国家有没有跟别的客户甚至大客户合作过?这种问题呢,虽然有些时候,是一些客人为了避免同质化竞争,决定绕开一些供应商;但是大部分客人问这个问题的目的,是希望了解你的产品是否适合他的市场。如果你已经给他的同......
  • STM32F1移植FREERTOS DEMO
    简介STM32F1太常用了,因为官网已经把移植的工作做的很完善了,只要文件放到相应工程里就可以使用,这里只做一个简单的DEMO,记录下FreeRTOS正常启动的流程CUBEMX配置1.新建CUBEMX工程,使用SWD的情况先配置SWD设置,防止第一次烧录后,后续无法使用2.由于FreeRTOS有重新使用到SYSTICK(滴搭......
  • 为什么 xgboost.QuantileDMatrix 使用自定义数据迭代器对数据进行四次传递?
    我正在尝试使用自定义数据迭代器,如下所示此处,因为我的数据集太大。只是为了测试它是如何工作的,我正在使用示例的子集并运行以下代码。X是我的数据的numpy数组。我的迭代器如下所示classIterForQDMatrix(xgb.core.DataIter):def__init__(self,d......
  • STM32学习记录(二):GPIO
    何为高低电平?高电平电压(highlevelvoltage),输出的电压值相对较高低电平电压(lowlevelvoltage),输出的电压值相对较低高低电平的电压具体值是多少需要参考数据手册,举个例子,如下表所示,VDD的典型值是3.3V,根据数据手册可以知道低电平下GPIO输出的最大电压是0.4V;高电平下GPIO输......