STM32笔记—DMA

目录

一、DMA简介

二、DMA主要特性

 三、DMA框图

3.1 DMA处理

 3.2 仲裁器

 3.3 DMA通道

扩展:

 断言:

枚举:

 3.4 可编程的数据传输宽度、对齐方式和数据大小端

 3.5 DMA请求映像

四、DMA基本结构

4.1 DMA_Init配置

4.2 实现DMA+ADC扫描模式

实现要求

存储器映射

  ADC_DR寄存器

 代码

        4.2.1 配置时钟

        4.2.2 GPIO初始化

        4.2.3 ADC配置(ADC连续模式)

         4.2.4 DMA初始化(DMA循环模式)

         4.2.5 使能与校准

         4.2.6 ADC触发

有一定的区别:


一、DMA简介

        直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。 两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。

二、DMA主要特性

        12个独立的可配置的通道(请求):DMA1有7个通道,DMA2有5个通道;每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置;在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低)优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) ; 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程;源和目标地址必须按数据传输宽度对齐;每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。

 三、DMA框图

        没有DMA

        1.如果没有DMA,CPU传输数据还要以内核作为中转站,比如要将ADC采集的数据转移到到SRAM中,这个过程是这样的:

        内核通过DCode经过总线矩阵协调,从获取AHB存储的外设ADC采集的数据,

        然后内核再通过DCode经过总线矩阵协调把数据存放到内存SRAM中。   

STM32笔记—DMA_第1张图片

        有DMA的话,

        DMA传输时外设对DMA控制器发出请求。

        DMA控制器收到请求,触发DMA工作。

        DMA控制器从AHB外设获取ADC采集的数据,存储到DMA通道中

        DMA控制器的DMA总线与总线矩阵协调,使用AHB把外设ADC采集的数据经由DMA通道存放到SRAM中,这个数据的传输过程中,完全不需要内核的参与,也就是不需要CPU的参与。

STM32笔记—DMA_第2张图片

        在发生一个事件后,外设向DMA控制器发送一个请求信号。DMA控制器根据通道的优先权处理请求。当DMA控制器开始访问发出请求的外设时,DMA控制器立即发送给它一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。如果有更多的请求时,外设可以启动下一个周期。

STM32笔记—DMA_第3张图片

3.1 DMA处理

        每次DMA传送由3个操作组成: ● 从外设数据寄存器或者从当前外设/存储器地址寄存器指示的存储器地址取数据,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元。 ● 存数据到外设数据寄存器或者当前外设/存储器地址寄存器指示的存储器地址,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元。 ● 执行一次DMA_CNDTRx寄存器的递减操作,该寄存器包含未完成的操作数目。

        DMA通道x外设地址寄存器,DMA_CPARx---外设数据寄存器的基地址,作为数据传输的源或目标。DMA通道x存储器地址寄存器,DMA_CMARx---存储器地址存储器地址作为数据传输的源或目标。DMA通道x传输数量寄存器,DMA_CNDTRx---数据传输数量 (Number of data to transfer)

STM32笔记—DMA_第4张图片

STM32笔记—DMA_第5张图片

 3.2 仲裁器

        仲裁器根据通道请求的优先级来启动外设/存储器的访问。 优先权管理分2个阶段: ● 软件:每个通道的优先权可以在DMA_CCRx寄存器中设置,有4个等级: ─ 最高优先级 ─ 高优先级 ─ 中等优先级 ─ 低优先级 ● 硬件:如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优先权。举个例子,通道2优先于通道4。 注意: 在大容量产品和互联型产品中,DMA1控制器拥有高于DMA2控制器的优先级

 3.3 DMA通道

        每个通道都可以在有固定地址的外设寄存器和存储器地址之间执行DMA传输。DMA传输的数据量是可编程的,最大达到65535。包含要传输的数据项数量的寄存器,在每次传输后递减。通道配置过程下面是配置DMA通道x的过程(x代表通道号):

         1. 在DMA_CPARx寄存器中设置外设寄存器的地址。发生外设数据传输请求时,这个地址将是数据传输的源或目标。

        2. 在DMA_CMARx寄存器中设置数据存储器的地址。发生外设数据传输请求时,传输的数据将从这个地址读出或写入这个地址。

        3. 在DMA_CNDTRx寄存器中设置要传输的数据量。在每个数据传输后,这个数值递减。

         4. 在DMA_CCRx寄存器的PL[1:0]位中设置通道的优先级。

         5. 在DMA_CCRx寄存器中设置数据传输的方向、循环模式、外设和存储器的增量模式、外设和存储器的数据宽度、传输一半产生中断或传输完成产生中断。

        循环模式循环模式用于处理循环缓冲区和连续的数据传输(如ADC的扫描模式)。在DMA_CCRx寄存器中的CIRC位用于开启这一功能。当启动了循环模式,数据传输的数目变为0时,将会自动地被恢复成配置通道时设置的初值,DMA操作将会继续进行。

下文详细介绍了两者区别:

    【精选】STM32 DMA 循环模式DMA_Mode_Circular详解_dma循环模式和正常模式-CSDN博客

        DMA通道的操作可以在没有外设请求的情况下进行,这种操作就是存储器到存储器模式。 当设置了DMA_CCRx寄存器中的MEM2MEM位之后,在软件设置了DMA_CCRx寄存器中的EN位启动DMA通道时,DMA传输将马上开始。当DMA_CNDTRx寄存器变为0时,DMA传输结束。存储器到存储器模式不能与循环模式同时使用。初始化代码如下所示:


	/*DMA初始化*/
	DMA_InitTypeDef DMA_InitStructure;										//定义结构体变量
	DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;						//外设基地址,给定形参AddrA
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据宽度,选择字节
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;			//外设地址自增,选择使能
	DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;							//存储器基地址,给定形参AddrB
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据宽度,选择字节
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器地址自增,选择使能
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;						//数据传输方向,选择由外设到存储器
	DMA_InitStructure.DMA_BufferSize = Size;								//转运的数据大小(转运次数)
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//模式,选择正常模式
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;								//存储器到存储器,选择使能
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//优先级,选择中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);							//将结构体变量交给DMA_Init,配置DMA1的通道1

        方法1:DMA_Mode_Normal,正常模式,当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次,当通道配置为非循环模式时,传输结束后(即传输计数变为0)将不再产生DMA操作。要开始新的DMA传输,需要在关闭DMA通道的情况下,在DMA_CNDTRx寄存器中重新写入传输数目。配置代码如下: 

/**
  * 函    数:启动DMA数据转运
  * 参    数:无
  * 返 回 值:无
  */
void MyDMA_Transfer(void)
{
	DMA_Cmd(DMA1_Channel1, DISABLE);					//DMA失能,在写入传输计数器之前,需要DMA暂停工作
	DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);	//写入传输计数器,指定将要转运的次数
	DMA_Cmd(DMA1_Channel1, ENABLE);						//DMA使能,开始工作
	
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);	//等待DMA工作完成
	DMA_ClearFlag(DMA1_FLAG_TC1);						//清除工作完成标志位
}
扩展:

DMA_GetFlagStatus()函数详解:

1. 这一段就是说DMA1还是DMA2空闲。

//注意: #define FLAG_Mask       ((uint32_t)0x10000000)
//相与:同1为1,其余为0

DMA1和DMA2这一位有区别,如下:这样可以简单的判断执行哪一句。

STM32笔记—DMA_第6张图片

STM32笔记—DMA_第7张图片

2.  这一段就是枚举与断言

STM32笔记—DMA_第8张图片

 断言:

  函数作用:

STM32笔记—DMA_第9张图片

枚举:
typedef enum //此处的day可以省略
{
    saturday,
    sunday = 0,
    monday,
    tuesday,
    wednesday,
    thursday,
    friday
} workday; // 此处的 workday 为枚举型 enum day 的别名

workday today, tomorrow; // 变量 today 和 tomorrow 的类型为枚举型 workday,即 enum day

typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus; 
这句话意思就是给enum {RESET = 0, SET = !RESET}起了别名:FlagStatus和ITStatus 
STM32笔记—DMA_第10张图片

 3. 底层源码

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
{
  FlagStatus bitstatus = RESET;
  uint32_t tmpreg = 0;
  
  /* Check the parameters */
  assert_param(IS_DMA_GET_FLAG(DMAy_FLAG));

  /* Calculate the used DMAy */
  if ((DMAy_FLAG & FLAG_Mask) != (uint32_t)RESET)
  {
    /* Get DMA2 ISR register value */
    tmpreg = DMA2->ISR ;
  }
  else
  {
    /* Get DMA1 ISR register value */
    tmpreg = DMA1->ISR ;
  }

  /* Check the status of the specified DMAy flag */
  if ((tmpreg & DMAy_FLAG) != (uint32_t)RESET)
  {
    /* DMAy_FLAG is set */
    bitstatus = SET;
  }
  else
  {
    /* DMAy_FLAG is reset */
    bitstatus = RESET;
  }
  
  /* Return the DMAy_FLAG status */
  return  bitstatus;
}

        方法2:DMA_Mode_Circular ,循环传输模式,当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。也就是多次传输模式。在循环模式下,最后一次传输结束时,DMA_CNDTRx寄存器的内容会自动地被重新加载为其初始数值,内部的当前外设/存储器地址寄存器也被重新加载为DMA_CPARx/DMA_CMARx寄存器设定的初始基地址。  

        6. 设置DMA_CCRx寄存器的ENABLE位,启动该通道。 一旦启动了DMA通道,它既可响应连到该通道上的外设的DMA请求。

STM32笔记—DMA_第11张图片

STM32笔记—DMA_第12张图片

         一旦启动了DMA通道,它既可响应连到该通道上的外设的DMA请求。

         当传输一半的数据后,半传输标志(HTIF)被置1,当设置了允许半传输中断位(HTIE)时,将产生一个中断请求。

        在数据传输结束后,传输完成标志(TCIF)被置1,当设置了允许传输完成中断位(TCIE)时,将产生一个中断请求。

STM32笔记—DMA_第13张图片

STM32笔记—DMA_第14张图片

 3.4 可编程的数据传输宽度、对齐方式和数据大小端

        每个通道都可以在有固定地址的外设寄存器和存储器地址之间执行DMA传输。DMA传输的数据量是可编程的,最大达到65535。包含要传输的数据项数量的寄存器,在每次传输后递减。

STM32笔记—DMA_第15张图片

 3.5 DMA请求映像

        从外设(TIMx[x=1、2、3、4]、ADC1、SPI1、SPI/I2S2、I2Cx[x=1、2]和USARTx[x=1、2、3])产生的7个请求,通过逻辑或输入到DMA1控制器,这意味着同时只能有一个请求有效。参见下图的DMA1请求映像。 外设的DMA请求,可以通过设置相应外设寄存器中的控制位,被独立地开启或关闭。

STM32笔记—DMA_第16张图片

四、DMA基本结构

STM32笔记—DMA_第17张图片

4.1 DMA_Init配置

#include "stm32f10x.h"                  // Device header

uint16_t MyDMA_Size;					//定义全局变量,用于记住Init函数的Size,供Transfer函数使用

/**
  * 函    数:DMA初始化
  * 参    数:AddrA 原数组的首地址
  * 参    数:AddrB 目的数组的首地址
  * 参    数:Size 转运的数据大小(转运次数)
  * 返 回 值:无
  */
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
	MyDMA_Size = Size;					//将Size写入到全局变量,记住参数Size
	
	/*开启时钟*/
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);						//开启DMA的时钟
	
	/*DMA初始化*/
	DMA_InitTypeDef DMA_InitStructure;										//定义结构体变量
	DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;						//外设基地址,给定形参AddrA
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据宽度,选择字节
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;			//外设地址自增,选择使能
	DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;							//存储器基地址,给定形参AddrB
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据宽度,选择字节
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器地址自增,选择使能
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;						//数据传输方向,选择由外设到存储器
	DMA_InitStructure.DMA_BufferSize = Size;								//转运的数据大小(转运次数)
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//模式,选择正常模式
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;								//存储器到存储器,选择使能
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//优先级,选择中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);							//将结构体变量交给DMA_Init,配置DMA1的通道1
	
	/*DMA使能*/
	DMA_Cmd(DMA1_Channel1, DISABLE);	//这里先不给使能,初始化后不会立刻工作,等后续调用Transfer后,再开始
}

/**
  * 函    数:启动DMA数据转运
  * 参    数:无
  * 返 回 值:无
  */
void MyDMA_Transfer(void)
{
	DMA_Cmd(DMA1_Channel1, DISABLE);					//DMA失能,在写入传输计数器之前,需要DMA暂停工作
	DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);	//写入传输计数器,指定将要转运的次数
	DMA_Cmd(DMA1_Channel1, ENABLE);						//DMA使能,开始工作
	
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);	//等待DMA工作完成
	DMA_ClearFlag(DMA1_FLAG_TC1);						//清除工作完成标志位
}

4.2 实现DMA+ADC扫描模式

实现要求

STM32笔记—DMA_第18张图片

存储器映射

类型

起始地址

存储器

用途

ROM

0x0800 0000

程序存储器Flash

存储C语言编译后的程序代码,常量数据

0x1FFF F000

系统存储器

存储BootLoader,用于串口下载

0x1FFF F800

选项字节

存储一些独立于程序代码的配置参数

RAM

0x2000 0000

运行内存SRAM

存储运行过程中的临时变量

0x4000 0000

外设寄存器

存储各个外设的配置参数

0xE000 0000

内核外设寄存器

存储内核各个外设的配置参数(NVIC、systick)

        ADC将菜做好了,DMA负责将菜及时的交给顾客,厨师与服务员的关系!!!那服务员从哪里端菜了?查数据手册:第一句话就是在规则组下一次只能上一个菜,需要使用服务员及时的端菜。第二句话就是告诉我们服务员端菜的地方在ADC_DR寄存器。

        那么菜最终端给的顾客是谁?根据程序要求,我们这里的顾客是SRAM。

STM32笔记—DMA_第19张图片

  ADC_DR寄存器
#define ADC1                ((ADC_TypeDef *) ADC1_BASE)
#define ADC1_BASE             (APB2PERIPH_BASE + 0x2400)

STM32笔记—DMA_第20张图片

#define  ADC_DR_DATA                         ((uint32_t)0x0000FFFF)        /*!< Regular data */
#define  ADC_DR_ADC2DATA                     ((uint32_t)0xFFFF0000)        /*!< ADC2 data */

 STM32笔记—DMA_第21张图片

STM32笔记—DMA_第22张图片

 代码
        4.2.1 配置时钟

                DMA1\ADC1\GPIOA

/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);		//开启DMA1的时钟
	
	/*设置ADC时钟*/
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
        4.2.2 GPIO初始化
/*GPIO初始化*/
	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);					//将PA0、PA1、PA2和PA3引脚初始化为模拟输入
        4.2.3 ADC配置(ADC连续模式)

            ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                        

              //连续转换,使能,每转换一次规则组序列后立刻开始下一次转换

/*规则组通道配置*/
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);	//规则组序列1的位置,配置为通道0
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);	//规则组序列2的位置,配置为通道1
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);	//规则组序列3的位置,配置为通道2
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);	//规则组序列4的位置,配置为通道3
	
	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;											//定义结构体变量
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;							//模式,选择独立模式,即单独使用ADC1
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;						//数据对齐,选择右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;			//外部触发,使用软件触发,不需要外部触发
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;							//连续转换,使能,每转换一次规则组序列后立刻开始下一次转换
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;								//扫描模式,使能,扫描规则组的序列,扫描数量由ADC_NbrOfChannel确定
	ADC_InitStructure.ADC_NbrOfChannel = 4;										//通道数,为4,扫描规则组的前4个通道
	ADC_Init(ADC1, &ADC_InitStructure);											//将结构体变量交给ADC_Init,配置ADC1
         4.2.4 DMA初始化(DMA循环模式)

        循环模式用于处理循环缓冲区和连续的数据传输(如ADC的扫描模式)。

            DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                
                //模式,选择循环模式,与ADC的连续转换一致,这样ADC每次上菜,DMA马上拿走,一直循环

	/*DMA初始化*/
	DMA_InitTypeDef DMA_InitStructure;
											定义结构体变量
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;	
			外设基地址,给定形参AddrA
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
外设数据宽度,选择半字,对应16为的ADC数据寄存器
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;	
		外设地址自增,选择失能,始终以ADC数据寄存器为源
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;	
				存储器基地址,给定存放AD转换结果的全局数组AD_Value
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;	
		存储器数据宽度,选择半字,与源数据宽度对应
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;				
		存储器地址自增,选择使能,每次转运后,数组移到下一个位置
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;					
		数据传输方向,选择由外设到存储器,ADC数据寄存器转到数组
	DMA_InitStructure.DMA_BufferSize = 4;							
			转运的数据大小(转运次数),与ADC通道数一致
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;				
				模式,选择循环模式,与ADC的连续转换一致
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;				
				存储器到存储器,选择失能,数据由ADC外设触发转运到存储器
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;			
			优先级,选择中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);					
			将结构体变量交给DMA_Init,配置DMA1的通道1

 注意:

        DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;  //外设基地址,给定形参AddrA,如果不进行强制类型转换会报错-----类型为“volatile uint32_t *”的值不能赋值给类型为“uint32_t”的实体。   

        #define MAXTIME 1000,一个简单的MAXTIME就定义好了,它代表1000,如果在程序里面写

if(i

        #define ADC1       ((ADC_TypeDef *) ADC1_BASE),一个ADC_TypeDef结构体指针类型的变量ADC1_BASE。

STM32笔记—DMA_第23张图片

 // 访问数据操作如下:
data.age = 24;          // 结构体变量通过点运算符( . )访问
pdata->age = 24;        // 指向结构体的指针通过箭头运算符( -> )访问

typedef struct          // 定义一个结构体类型:DATA
{
    char key[10];       // 结构体成员:key
    char name[20];      // 结构体成员:name
    int age;            // 结构体成员:age
}DATA;
    
DATA data;              // 声明一个结构体变量
DATA *pdata;            // 声明一个指向结构体的指针
    
// 访问数据操作如下:
data.age = 24;          // 结构体变量通过点运算符( . )访问
pdata->age = 24;        // 指向结构体的指针通过箭头运算符( -> )访问

结构体指针的访问变量方法
1)p->结构体成员;
2)(*p).结构体成员;

p->num 的含义是:指针变量 p 所指向的结构体变量中的 num 成员。p->num 最终代表的就是 num 这个成员中的内容。

STM32笔记—DMA_第24张图片

STM32笔记—DMA_第25张图片 

         4.2.5 使能与校准

  因为:    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                
               // 存储器到存储器,选择失能,数据由ADC外设触发转运到存储器,硬件触发

        由于DMA转运有三个条件:开关控制DMA_CMD必须使能,传输计数器必须大于0(当寄存器的内容为0时,无论通道是否开启,都不会发生任何数据传输。),触发源必须有触发信号。

        前文提到对于DMA正常模式,当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次,当通道配置为非循环模式时,传输结束后(即传输计数变为0)将不再产生DMA操作。且ADC单次转换下,ADC只执行一次转换。相当于没有开启ADC到DMA的通道

所以这里需要开启ADC到DMA的输出,用到下图函数:

STM32笔记—DMA_第26张图片

/*DMA和ADC使能*/
	DMA_Cmd(DMA1_Channel1, ENABLE);							//DMA1的通道1使能
	ADC_DMACmd(ADC1, ENABLE);								//ADC1触发DMA1的信号使能
	ADC_Cmd(ADC1, ENABLE);									//ADC1使能
	
	/*ADC校准*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
	
         4.2.6 ADC触发
/*ADC触发*/
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	//软件触发ADC开始工作,由于ADC处于连续转换模式,故触发一次后ADC就可以一直连续不断地工作

(当不是ADC连续模式的时候,DMA也不是循环模式时)

有一定的区别:

        加上下代码即可:最大的区别就是需要不断的开启 ADC

        ADC_SoftwareStartConvCmd(ADC1,ENABLE);

        上文连续模式下,由于ADC处于连续转换模式,故触发一次后ADC就可以一直连续不断地工作。

        1. 告诉DMA我做了ADC做了4个菜;2.ADC开始做菜;3.DMA取菜(一个个拿)DMA1_FLAG_TC1: DMA1 Channel1 transfer complete flag.

void AD_GetValue(void)
{
	DMA_Cmd(DMA1_Channel1,DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel1,4);
	DMA_Cmd(DMA1_Channel1,ENABLE);
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
	DMA_ClearFlag(DMA1_FLAG_TC1);
}

你可能感兴趣的:(51单片机,stm32,笔记,嵌入式硬件)