stm32 DMA

1、原理

软件触发适用于从存储器到存储器的数据转运(通道可以任意选择)。如把Flash里的数据,转运到SRAM里

硬件触发适用于从外设到存储器的数据转运,外设的数据需要处理完再转运。如转运ADC的数据,等到ADC每个通道AD转换完成后硬件触发一次DMA,之后DMA再转运

特定的硬件触发是指每个DMA通道的硬件触发源是不同的,想要使用特定的硬件触发源就要使用其对应的通道

内部结构分析

总线矩阵的左端为主动单元 ,拥有存储器的访问权,如DCode(专门访问Flash)、系统总线、 DMA总线;右端为被动单元,它们的存储器只能被左边的主动单元读写

DMA模块内部存在多个通道,可以分别设置它们转运数据的源地址和目的地址 仲裁器用于多个通道产生冲突时,根据通道的优先级决定顺序。多个通道虽然可以独立转运数据,但是所有通道只能分时复用一条DMA总线

在总线矩阵内部也存在一个仲裁器,如果DMA和CPU同时访问同一个目标,CPU的访问会被暂停(CPU仍能得到一半的总线带宽)

AHB从设备,也是DMA自身的寄存器,用于DMA配置,DMA既是总线矩阵上的主动单元,也是AHB总线上的被动单元,CPU通过AHB总线就可以对DMA进行配置了

DMA请求,DMA硬件触发源,如ADC转换完成、串口接收到数据,就会通过这条线路来向DMA发送硬件触发信号

基本结构

要进行存储器到存储器的数据转运,将其中一个存储器的地址放在外设站点。

传输计数器用于指定总共需要转运几次(DMA关闭时),自减计数器,每次转运1次计数器的数就会减1。

自动重装器,设置循环模式,传输计数器减到0之后可以选择恢复到最初的值,进行下一轮工作

M2M ,存储器到存储器,M2M位为1时,DMA选择软件触发。与ADC软件触发不同,以最快的速度,连续不断地触发DMA,将传输计数器清零,完成这一轮转换。软件触发 适合从存储器到存储器的转运,软件启动,不需要特定的时机。M2M位为0时,DMA选择硬件触发,一般与外设有关,如ADC转换完成、串口接收数据、定时器时间到,同时还要在对应外设调用XXX_DMA_Cmd

DMA进行转运条件:

1、开关控制,DMA_Cmd使能

2、传输计数器大于0

3、有触发信号

M2M软件触发和自动重装器循环模式不能一起使用,不然传输计数器会不断地清零再恢复,DMA停不下来。

2、代码

不重装,软件触发

cpp 复制代码
uint16_t MyDmasize=0;

/*
@para Addr1:外设地址
@para Addr2:存储器地址
@para size:转运数据的个数
*/
void MyDMA_Init(uint32_t Addr1, uint32_t Addr2, uint16_t size)
{
	MyDmasize=size;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	DMA_InitTypeDef DMA_InitStruct;
	//外设站点配置
	//起始地址
	DMA_InitStruct.DMA_PeripheralBaseAddr=Addr1;
	//数据宽度
	DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
	//是否自增
	DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Enable;
	
	//存储器站点配置
	DMA_InitStruct.DMA_MemoryBaseAddr=Addr2;
	DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
	DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;
	
	//传输方向
	DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC;
	//传输计数器
	DMA_InitStruct.DMA_BufferSize=size;
	//配置传输计数器是否要重装
	DMA_InitStruct.DMA_Mode=DMA_Mode_Normal;
	DMA_InitStruct.DMA_M2M=DMA_M2M_Enable;
	DMA_InitStruct.DMA_Priority=DMA_Priority_Medium;
	
	DMA_Init(DMA1_Channel1, &DMA_InitStruct);
	
	DMA_Cmd(DMA1_Channel1, ENABLE);
}
//每次转换时调用一次
void DMA_Transfer()
{
	//给传输计数器赋值时,DMA需要处于失能状态
	DMA_Cmd(DMA1_Channel1, DISABLE);
	
	DMA_SetCurrDataCounter(DMA1_Channel1, MyDmasize);
	
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	//等待数据转运完成
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);
	DMA_ClearFlag(DMA1_FLAG_TC1);
}

DMA+AD多通道,自动重装

cpp 复制代码
#include "stm32f10x.h"                  // Device header

uint16_t AD_Value[4];

void Ad_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	//配置ADCCLK时钟分频,ADC的输入时钟不得超过14MHz
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	//初始化为模拟输入引脚
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_3|GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_1Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_1Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_1Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 4, ADC_SampleTime_1Cycles5);
	
	ADC_InitTypeDef ADC_InitStruct;
	//独立模式 ADC1 和 ADC2 单独转换
	ADC_InitStruct.ADC_Mode=ADC_Mode_Independent;
	//选择数据对齐 左对齐、右对齐
	ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;
	//外部触发源选择, 这里使用软件触发,所以选择None
	ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	//连续转换模式, 选择单次转换还是连续转换
	ADC_InitStruct.ADC_ContinuousConvMode=ENABLE;
	//扫描转换模式, 选择扫描模式还是非扫描模式
	ADC_InitStruct.ADC_ScanConvMode=ENABLE;
	//通道数目, 只有在扫描模式下才能使用多个通道
	ADC_InitStruct.ADC_NbrOfChannel=4;
	
	ADC_Init(ADC1, &ADC_InitStruct);
	

	
	DMA_InitTypeDef DMA_InitStruct;
	//外设站点配置
	//起始地址
	//设置地址为ADC1数据寄存器,别忘了取地址符&
	DMA_InitStruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR;
	//数据宽度
	//DR寄存器里低16位是ADC1数据存储的地方,所以DMA转运的数据宽度为16位
	DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
	//是否自增
	//始终转运ADC1->DR上的数据
	DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
	
	//存储器站点配置
	DMA_InitStruct.DMA_MemoryBaseAddr=(uint32_t)AD_Value;
	DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
	DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;
	
	//传输方向
	DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC;
	//传输计数器
	DMA_InitStruct.DMA_BufferSize=4;
	//配置传输计数器是否要重装
	DMA_InitStruct.DMA_Mode=DMA_Mode_Circular;
	//软件触发还是硬件触发
	DMA_InitStruct.DMA_M2M=DMA_M2M_Disable;
	DMA_InitStruct.DMA_Priority=DMA_Priority_Medium;
	
	DMA_Init(DMA1_Channel1, &DMA_InitStruct);
	
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	//开启ADC的DMA输出
	ADC_DMACmd(ADC1, ENABLE);
	
	ADC_Cmd(ADC1, ENABLE);
	
	//手册中建议在每次上电后执行一次校准
	//复位校准
	ADC_ResetCalibration(ADC1);
	//等待复位校准完成, 复位校准完成后为 RESET
	while(ADC_GetResetCalibrationStatus(ADC1)==SET);
	//开始校准
	ADC_StartCalibration(ADC1);
	//等待校准完成
	while(ADC_GetCalibrationStatus(ADC1)==SET);
	//连续转换仅需要在最开始触发一次就可以,
	//软件触发转换函数可以挪到初始化最后,不需要每次取值都使用
	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
相关推荐
逼子格3 小时前
硬件工程师笔记——二极管Multisim电路仿真实验汇总
笔记·嵌入式硬件·硬件工程师·multisim·硬件工程师学习·电子器件·电路图
「QT(C++)开发工程师」3 小时前
STM32 | FreeRTOS 递归信号量
python·stm32·嵌入式硬件
芯眼4 小时前
ALIENTEK精英STM32F103开发板 实验0测试程序详解
开发语言·c++·stm32·单片机·嵌入式硬件·社交电子
百里东风4 小时前
STM32IIC协议基础及Cube配置
stm32·单片机·嵌入式硬件
一只川页9 小时前
arduino平台读取鼠标光电传感器
驱动开发·单片机·嵌入式硬件·计算机外设
qq_4017004110 小时前
电容触摸按键PCB设计
stm32
爱学习的张哥12 小时前
UDP--DDR--SFP,FPGA实现之模块梳理及AXI读写DDR读写上板测试
单片机·fpga开发·udp·axi·ddr
LaoZhangGong12312 小时前
W5500使用ioLibrary库创建TCP客户端
网络·经验分享·stm32·网络协议·tcp/ip
一月千帆12 小时前
51单片机课设基于GM65模块的二维码加条形码识别
单片机·嵌入式硬件·51单片机