STM32----DMA+ADC+单通道

DMA的基本介绍

什么是DMA (DMA的基本定义)

DMA,全称Direct Memory Access,即直接存储器访问。

DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。

配置流程(光照)(需要配置GPIO、ADC、DMA)

一、配置GPIO

1.开引脚时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); 

2.配置引脚工作模式

	//定义结构体

	GPIO_InitTypeDef GPIO_InitStruct={0};
	
	//给结构体赋值
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;//工作模式  模拟输入  (1)看官方例程  (2)参考手册8.1.11
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;  //引脚
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  //工作速度
	
	//调用XXX_init函数,将参数写入到寄存器中
	GPIO_Init(GPIOA,&GPIO_InitStruct);

二、配置ADC

1、开ADC1时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);

2、对ADC分频  

        参考手册11.1中  描述时钟不能超过14M   ADC1挂载在APB2上,时钟为72M,进行6分频  变成12M<14M

RCC_ADCCLKConfig(RCC_PCLK2_Div6);

3、配置ADC

       (1) 参数ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;         分为连续转换和不连续转换

        连续转换 给1次命令,之后自动转换  ADC_SoftwareStartConvCmd(ADC1,ENABLE);

        不连续转换,需要每次都给一个命令      ADC_SoftwareStartConvCmd(ADC1,ENABLE);

//定义结构体 
ADC_InitTypeDef ADC_InitStruct={0};	
	
//对结构体赋值
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; //连续转换 给1次命令,之后自动转换  ADC_SoftwareStartConvCmd(ADC1,ENABLE);
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;  //数据靠右对齐  16位的DR寄存器   12位的分辨率  转换结果最多12位,靠右对齐方便读取数据
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  //不外部触发  通过软件触发转换
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;  //独立模式  ADC1和ADC2独立工作  
ADC_InitStruct.ADC_NbrOfChannel = 1;   //待转换的通道数量  -- 更改
ADC_InitStruct.ADC_ScanConvMode = ENABLE;  //1个通道也可以配置扫描  

//调用xxx_init函数将参数写入到寄存器中
ADC_Init(ADC1,&ADC_InitStruct);	

三、配置DMA

1、开DMA时钟

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

2、配置DMA

        目标地址可以是数组也可以是变量,但变量只能定义一个,存放数据会被重复覆盖,因此多个通道时候只能用数组,如光照+烟雾时候。     

(1)数组用法   

//定义DMA相关的结构体
DMA_InitTypeDef DMA_InitStruct={0};

//给结构体赋值

//目标地址
DMA_InitStruct.DMA_BufferSize = sizeof(ADC_DMA_Buff)/sizeof(ADC_DMA_Buff[0]);  //能够存放的转换后数据的数量
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)ADC_DMA_Buff;  //内存的基地址
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存递增   目标地址有10个存储空间


//源地址
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;  //外设ADC作为数据的来源
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; //使用外设ADC-->内存  不使用内存到内存
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//数据宽度为半字  ADC1转换结果存放在ADC_DR低16位
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;  //循环模式   目标地址占用完之后,会重头覆盖


DMA_InitStruct.DMA_PeripheralBaseAddr = 0x4001244C;   //ADC1_DR寄存器的地址
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  //外设数据宽度
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不累加  每次ADC转换的结果都存放在ADC1_DR 会覆盖
DMA_InitStruct.DMA_Priority = DMA_Priority_High; //DMA通道的优先级
	
//调用xxx_init函数将参数写入到寄存器中
DMA_Init(DMA1_Channel1,&DMA_InitStruct);  //参考手册 10.3.7

(2)变量用法

//定义DMA相关的结构体
DMA_InitTypeDef DMA_InitStruct={0};

//给结构体赋值

//目标地址
	DMA_InitStruct.DMA_BufferSize = 1;  //能够存放的转换后数据的数量
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&ADC_DMA_Date;  //内存的基地址
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Disable;  //内存不递增  

//源地址
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;  //外设ADC作为数据的来源
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; //使用外设ADC-->内存  不使用内存到内存
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//数据宽度为半字  ADC1转换结果存放在ADC_DR低16位
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;  //循环模式   目标地址占用完之后,会重头覆盖


DMA_InitStruct.DMA_PeripheralBaseAddr = 0x4001244C;   //ADC1_DR寄存器的地址
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  //外设数据宽度
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不累加  每次ADC转换的结果都存放在ADC1_DR 会覆盖
DMA_InitStruct.DMA_Priority = DMA_Priority_High; //DMA通道的优先级
	
//调用xxx_init函数将参数写入到寄存器中
DMA_Init(DMA1_Channel1,&DMA_InitStruct);  //参考手册 10.3.7

四、开ADC的DMA功能

ADC_DMACmd(ADC1,ENABLE);

五、配置ADC规则通道

            //参数1   使用哪个ADC转换   ADC1  ADC2  ADC3
            //参数2   转换哪个通道   PA5  ADC12_IN5
            //参数3   规则组采样顺序    --  ADC_SQR
            //参数4   ADC 通道的采样时间值    周期越大:时间越长,准确率越高  
                //参考手册  11.6  TCONV = 采样时间+ 12.5个周期
                //TCONV = 采样时间+ 12.5个周期=55+12.5=67.5周期
                //分频之后 ADC1的频率位12M   周期t=1/12000000S
                //转换完成需要的时间 T =67.5周期*t=(67.5/12000000)s       -- 更改

ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_55Cycles5);   //stm32f10x_adc.h 440行

六、开使能

1.使能ADC

  ADC_Cmd(ADC1, ENABLE);

2、使能DMA

DMA_Cmd(DMA1_Channel1,ENABLE);

七、校准

         参考手册  11.4中  描述  开机必须校准1次
         重置指定的 ADC 的校准寄存器  参考手册11.12.3 位3 stm32f10x_adc.h  434行  

1、重置指定的 ADC 的校准寄存器

ADC_ResetCalibration(ADC1);

2、等待校准寄存器初始化 参考手册11.12.3 位3  stm32f10x_adc.h  435行 

while(ADC_GetResetCalibrationStatus(ADC1));

3、启动校准  参考手册11.12.3 位2    stm32f10x_adc.h   435行 

ADC_StartCalibration(ADC1);

4、等待校准结束 参考手册11.12.3 位2    stm32f10x_adc.h  436行

while(ADC_GetCalibrationStatus(ADC1));

八、启动转换

ADC_SoftwareStartConvCmd(ADC1,ENABLE); 

九、完成DMA+ADC(光照)代码

1、dma.c

#include "dma.h"

#if (ADC_DMA_USE_BUFF==1)
uint16_t ADC_DMA_Buff[10]; //用来作为DMA的目标地址   待采集通道的整数倍
#elif (ADC_DMA_USE_BUFF==0)
uint16_t ADC_DMA_Date=0;
#endif

/*
DMA  直接内存访问(Direct Memory Access)
DMA是的独立的功能,分为DMA1和DMA2
DMA可以和ADC  UART  SPI  IIC等外设一块使用
我们采样:  ADC+DMA的使用
配置ADC:
	结构体:GPIO   ADC
	实现光照:PA5   ADC12_IN5  -- 硬件原理图决定
配置DMA:
	结构体:DMA
使用ADC1采集,ADC1的通道在DMA1上,所以DMA1和ADC1一块使用 -- 参考手册 中文表59决定

源地址:  ADC_DR          ADC外设里面的寄存器
目标地址:ADC_DMA_Buff    内存的地址里面

*/
void ADC_DMA_Config(void)
{
	//1.开GPIOA时钟 
//void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);  stm3210x_rcc.h 693行
//void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); 

	//2.定义结构体
//void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);	  stm32f10x_gpio.h 351行
	GPIO_InitTypeDef GPIO_InitStruct={0};
	
	//3.给结构体赋值
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;//工作模式  模拟输入  (1)看官方例程  (2)参考手册8.1.11
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;  //引脚
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  //工作速度
	
	//4.调用XXX_init函数,将参数写入到寄存器中
	GPIO_Init(GPIOA,&GPIO_InitStruct);	

	//5.开ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	
	//6.对ADC分频   参考手册11.1中  描述时钟不能操作14M   ADC1挂载在APB2上,时钟为72M,进行6分频  变成12M<14M
//void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);	  stm32f10x_rcc.h 680行
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);

	//7.定义结构体  xxx_init
//void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);  stm32f10x_adc.h 429行
	ADC_InitTypeDef ADC_InitStruct={0};	
	
	//8.对结构体赋值
	ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; //连续转换 给1次命令,之后自动转换  ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;  //数据靠右对齐  16位的DR寄存器   12位的分辨率  转换结果最多12位,靠右对齐方便读取数据
	ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  //不外部触发  通过软件触发转换
	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;  //独立模式  ADC1和ADC2独立工作  
	ADC_InitStruct.ADC_NbrOfChannel = 1;   //待转换的通道数量  -- 更改
	ADC_InitStruct.ADC_ScanConvMode = ENABLE;  //1个通道也可以配置扫描  

	//9.调用xxx_init函数将参数写入到寄存器中
	ADC_Init(ADC1,&ADC_InitStruct);	
	
	//10.开DMA1的时钟
//void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);	  stm32f10x_rcc.h 692行
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	//11.定义DMA相关的结构体
//void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);	  stm32f10x_dma.h 411行
	DMA_InitTypeDef DMA_InitStruct={0};
	
	//12.给结构体赋值
#if (ADC_DMA_USE_BUFF==1)
	DMA_InitStruct.DMA_BufferSize = sizeof(ADC_DMA_Buff)/sizeof(ADC_DMA_Buff[0]);  //能够存放的转换后数据的数量
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)ADC_DMA_Buff;  //内存的基地址
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存递增   目标地址有10个存储空间
#elif (ADC_DMA_USE_BUFF==0)
	DMA_InitStruct.DMA_BufferSize = 1;  //能够存放的转换后数据的数量
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&ADC_DMA_Date;  //内存的基地址
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Disable;  //内存递增   
	
#endif
	
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;  //外设ADC作为数据的来源
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; //使用外设ADC-->内存  不使用内存到内存
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//数据宽度为半字  ADC1转换结果存放在ADC_DR低16位
	DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;  //循环模式   目标地址占用完之后,会重头覆盖
/*
ADC1  0x40012400 -- 0x00127FF	数据手册存储器映像
ADC_DR  相对于外设起始地址偏移  0x4C  参考手册  11.12.14的偏移地址
ADC1_DR 0x40012400+0x4C
*/
	DMA_InitStruct.DMA_PeripheralBaseAddr = 0x4001244C;   //ADC1_DR寄存器的地址
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  //外设数据宽度
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不累加  每次ADC转换的结果都存放在ADC1_DR 会覆盖
	DMA_InitStruct.DMA_Priority = DMA_Priority_High; //DMA通道的优先级
	
	//12.调用xxx_init函数将参数写入到寄存器中
	DMA_Init(DMA1_Channel1,&DMA_InitStruct);  //参考手册 10.3.7

	//13.开ADC1的DMA功能
//void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);   stm32f10x_adc.h 432行
	ADC_DMACmd(ADC1,ENABLE);
	
	//14.配置ADC规则通道
	//参数1   使用哪个ADC转换   ADC1  ADC2  ADC3
	//参数2   转换哪个通道   PA5  ADC12_IN5
	//参数3   规则组采样顺序    --  ADC_SQR
	//参数4   ADC 通道的采样时间值    周期越大:时间越长,准确率越高  
				//参考手册  11.6  TCONV = 采样时间+ 12.5个周期
				//TCONV = 采样时间+ 12.5个周期=55+12.5=67.5周期
				//分频之后 ADC1的频率位12M   周期t=1/12000000S
				//转换完成需要的时间 T =67.5周期*t=(67.5/12000000)s	   -- 更改
	ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_55Cycles5);   //stm32f10x_adc.h 440行

  //15.使能ADC    stm32f10x_adc.h  431行
  ADC_Cmd(ADC1, ENABLE);

	//16.使能DMA1
//void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);  //stm32f10x_adc.h 413行
	DMA_Cmd(DMA1_Channel1,ENABLE);

	//参考手册  11.4中  描述  开机必须校准1次
  //17.重置指定的 ADC 的校准寄存器  参考手册11.12.3 位3 stm32f10x_adc.h  434行  
  ADC_ResetCalibration(ADC1);
 
	//18.等待校准寄存器初始化 参考手册11.12.3 位3  stm32f10x_adc.h  435行 
  while(ADC_GetResetCalibrationStatus(ADC1));

  //19.启动校准  参考手册11.12.3 位2    stm32f10x_adc.h   435行 
  ADC_StartCalibration(ADC1);
  
	//20.等待校准结束 参考手册11.12.3 位2    stm32f10x_adc.h  436行   
  while(ADC_GetCalibrationStatus(ADC1));

	//21.启动转换
//	void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);	 //stm32f10x_adc.h 438行
	ADC_SoftwareStartConvCmd(ADC1,ENABLE); 	
}






2、dma.h

#ifndef __DMA_H_
#define __DMA_H_

#include "stm32f10x.h"

#define ADC_DMA_USE_BUFF  1  //0 变量接收  1数组接收

void ADC_DMA_Config(void);

#endif

  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

膽小

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值