STM32F103-ADC采集(+DMA)

一、ADC原理

原理

 两个内部信号源是内部温度采集和内部电压,内部参考电压是内部1.2V的基准电压,不随外部供电的改变而改变,如果外部供电电压不准,那么可以进行内部基准电压采集校准。

模拟看门狗可以进行自动检测阈值。

远离电路

 8bit逐次逼近型ADC芯片,和STM32ADC原理相同。通过地址锁存和译码、通道选择开关进行ADC通道选择,通过比较器比较DAC和输入电压的大小,DAC通过加权网络电路实现。通过逐次逼近(二分法)SAR来不断逼近DAC和输入电压。最后通过锁存缓冲器进行输出DAC对应的模拟量数值。EOC是转换结束信号,Start转换开始信号,clock是时钟信号。Vref±是DAC参考电压,所有参考电压输入会影响转换误差。(采集模拟量/ADC最大量程*Vref,所以Vref的输入影响模拟量的反馈,若参考电压波动,那么反馈的模拟量也波动)

ADC内部电路

ADC端口输入->多路开关进行选择->最多4通道/最多16通道->注入通道(寄存器4*16)/规则通道(16bit)需要使用DMA转运,防止覆盖

触发ADC转换源(规则和注入组)

ADC预分频器 ,最大为14MHz。所以只能选择6和8分频,为12MHz和9MHz。

读取这些标志位就知道是否有对应的转换完成。这些标志位可以配置触发中断。

ADC通道和引脚复用的关系

 四种采集模式(两个参数的组合):

扫描模式需要用到DMA将数据移出,防止被覆盖。

还有一种间断模式,可以每扫描几次就停止,需要再次触发才能继续。

触发控制

数据对齐:

一般使用右对齐,数据可以直接使用。右对齐数值会大16倍,若不需要这么高的精度,可以直接使用左对齐,使用高8bit即可。

转换时间:

采样时间是采样保持(采样时对当前模拟量的存储和使用时间)花费的时间。采样时间越大越能避免一些毛刺信号。若ADCCLK设置超过14MHz则正确性无法保证。

校准

 电压采集电路:

1、RP1滑动变阻器可以输出一个0~3.3V的电压。(电阻最小千欧级别,在小容易烧毁和费点)

2、传感器输出电压电路,例如光敏热敏传感器等,其中N1等效可变电阻。使用R1进行分压,原理同电路1,N1小时,下拉作用增强,输出端电压下降,N1增大时,下拉作用减小,输出端受上拉电阻的作用,电压上升。(电路优先导通阻值小的电路,根据电阻进行分压,PA1出于中间电位,输出为R1分压后的电压)

3、当需要采集超出3.3V的电压时,借用分压原理,例如采集5V电压,上端17k电阻,下端33k电阻,经过分压,PA2处电压为总电压的17/(17+33)。PA2刚好为(3.3V,0V),建议分压电路最高采集超过10V电压,在高建议使用隔离放大器等。

二、DMA原理

若是内存到SRAM,则使用软件触发。若需要转运外设采集数据,则需要硬件触发。

存储器映像

 

 

 

DMA可以转运SRAM和APB外设的内容,不能转运flash内容,转运flash会出错。

对flash写入需要通过flash接口,进行按页擦除写入。

DMA流程框图

可以进行,外设->存储器,存储器->外设,存储器->存储器(flash-sram、sram-sram,flash为只读的)

软件触发为触发一次DMA(传输计数器降到0),将指定的次数转运完成。

起始地址表示转运的起始地址,可指定数据宽度Halfword、word等,地址是否自增表示转运完成后下次转运是否自增地址。若使用自动重装器,地址也会重装。

DMA使用条件:

1、开关打开

2、计数器>0

3、必须有触发信号

数据传输宽度说明

DMA中断

DMA1请求映像

 M2M是触发选择位,EN是DMA通道使能位。

DMA2请求映像

 

数据转运DMA

 ADC+DAM

ADC虽然每个通道采集完成后不会置相关标志位,在全部通道采集完成后才会置标志位。但是其实每个ADC通过采集完成后会触发DMA转运,可以完整的转运多个通道采集的数据。注意:ADC_DR地址不会变,DMA转运ADC地址不需要自增。 

三、实例

 

1、单通道单次非连续采集-手动触发

使用规则组,每次只采集1个通道,不连续采集,每次使用手动触发。

main.c

#include "stdio.h"
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t RP_Value = 0;
int main(void){
	OLED_Init();
	AD_Init();
	OLED_ShowString(1,1,"RP:0000");
	OLED_ShowString(2,1,"Voltage:0.0V");
	while(1){
		RP_Value = Get_RP();
		OLED_ShowNum(1,4,RP_Value,4);
		OLED_ShowNum(2,9,RP_Value*3.3/4095,1);
		OLED_ShowNum(2,11,RP_Value*33/4095%10,1);
		Delay_ms(500);
	}
	return 0;
}

AD.c

#include "stm32f10x.h"                  // Device header
/**
* @brief ADC1CH1初始化函数,单通道单次扫描非连续模式,无触发,手动开启转换
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void AD_Init(void){
	//1、RCC时钟  配置ADCCLK分频器
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK-6分频-12MHz
	//2、GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);

	//3、配置多路开关,把GPIO通道接到规则组列表
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_1Cycles5);//采样时间选择1.5个ADC周期,1.5+12.5 =14MHz,ADCCLK = 12MHz,所以采样时间 = 14/12 = 1.17us
	//4、配置ADC转换器
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//连续扫描或者单次扫描
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发扫描选择
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC模式,ADC1和ADC2独立转换
	ADC_InitStructure.ADC_NbrOfChannel = 1;//通道数量
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;//多通道或者单通道扫描
	ADC_Init(ADC1,&ADC_InitStructure);
	//需要的话配置看门狗检测阈值
	//5、需要的话开启中断,使用ITConfig开启对应的中断输出
	//6、NVIC配置中断优先级
	//7、开启ADC,然后进行校准//可以使用软件触发转换,可以手动获取当前采集值
	ADC_Cmd(ADC1,ENABLE);
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1)==SET);//0为复位完成 1为正在复位
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1)==SET);//0为校准完成 1为正在校准
}
/**
  * @brief 配合AD_Init,获取电位计采集到的0-3.3V模拟量
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
uint16_t Get_RP(void){
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);//完成后自动清除
	return ADC_GetConversionValue(ADC1);
}

AD.h

#ifndef __AD_H
#define __AD_H
#include "stm32f10x.h"                  // Device header

void AD_Init(void);
uint16_t Get_RP(void);

#endif

AD采集会有抖动,若想设置一个阈值,高于阈值高操作,低于阈值低操作,在阈值见会有操作冲突。可使用同施密特触发器的原理,设置两个阈值,低于低阈值低操作,高于高阈值高操作,这样不会操作冲突。

也可以读取10/20个值,然后取平均值。

2、单通道连续采集-软件触发一次即可

main.c

#include "stdio.h"
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t RP_Value = 0;
int main(void){
	OLED_Init();
	AD_Init();
	OLED_ShowString(1,1,"RP:0000");
	OLED_ShowString(2,1,"Voltage:0.00V");
	while(1){
		RP_Value = Get_RP();
		OLED_ShowNum(1,4,RP_Value,4);
		OLED_ShowNum(2,9,RP_Value*3.3/4095,1);
		OLED_ShowNum(2,11,RP_Value*330/4095%100,2);
		Delay_ms(500);
	}
	return 0;
}

AD.c

#include "stm32f10x.h"                  // Device header
/**
* @brief ADC1CH1初始化函数,单通道单次扫描非连续模式,无触发,手动开启转换
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void AD_Init(void){
	//1、RCC时钟  配置ADCCLK分频器
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK-6分频-12MHz
	//2、GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);

	//3、配置多路开关,把GPIO通道接到规则组列表
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_1Cycles5);//采样时间选择1.5个ADC周期,1.5+12.5 =14MHz,ADCCLK = 12MHz,所以采样时间 = 14/12 = 1.17us
	//4、配置ADC转换器
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续扫描或者单次扫描
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发扫描选择
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC模式,ADC1和ADC2独立转换
	ADC_InitStructure.ADC_NbrOfChannel = 1;//通道数量
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;//多通道或者单通道扫描
	ADC_Init(ADC1,&ADC_InitStructure);
	//需要的话配置看门狗检测阈值
	//5、需要的话开启中断,使用ITConfig开启对应的中断输出
	//6、NVIC配置中断优先级
	//7、开启ADC,然后进行校准//可以使用软件触发转换,可以手动获取当前采集值
	ADC_Cmd(ADC1,ENABLE);
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1)==SET);//0为复位完成 1为正在复位
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1)==SET);//0为校准完成 1为正在校准
	
	//连续扫描,软件触发一次即可
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
}
/**
  * @brief 配合AD_Init,获取电位计采集到的0-303V模拟量
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
uint16_t Get_RP(void){
	return ADC_GetConversionValue(ADC1);
}

AD.h

#ifndef __AD_H
#define __AD_H
#include "stm32f10x.h"                  // Device header

void AD_Init(void);
uint16_t Get_RP(void);

#endif

3、多通道单次采集非连续-因没使用DMA,每次触发前修改通道完成功能。

程序值接了A0和A1,效果相同 

main.c

#include "stdio.h"
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t RP_Value = 0;
int main(void){
	OLED_Init();
	AD_Init();
	OLED_ShowString(1,1,"RP:0000");
	OLED_ShowString(2,1,"Voltage:0.00V");
	OLED_ShowString(3,1,"Sensor:0000");
	OLED_ShowString(4,1,"Voltage:0.00V");
	while(1){
		RP_Value = Get_SensorValue(ADC_Channel_0);
		OLED_ShowNum(1,4,RP_Value,4);
		OLED_ShowNum(2,9,RP_Value*3.3/4095,1);
		OLED_ShowNum(2,11,RP_Value*330/4095%100,2);
		RP_Value = Get_SensorValue(ADC_Channel_1);
		OLED_ShowNum(3,8,RP_Value,4);
		OLED_ShowNum(4,9,RP_Value*3.3/4095,1);
		OLED_ShowNum(4,11,RP_Value*330/4095%100,2);
		Delay_ms(100);
	}
	return 0;
}

AD.c

#include "stm32f10x.h"                  // Device header
/**
* @brief ADC1CH1初始化函数,单通道单次扫描非连续模式,无触发,手动开启转换
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void AD_Init(void){
	//1、RCC时钟  配置ADCCLK分频器
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK-6分频-12MHz
	//2、GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);

	
	//4、配置ADC转换器
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//连续扫描或者单次扫描
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发扫描选择
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC模式,ADC1和ADC2独立转换
	ADC_InitStructure.ADC_NbrOfChannel = 1;//通道数量
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;//多通道或者单通道扫描
	ADC_Init(ADC1,&ADC_InitStructure);
	//需要的话配置看门狗检测阈值
	//5、需要的话开启中断,使用ITConfig开启对应的中断输出
	//6、NVIC配置中断优先级
	//7、开启ADC,然后进行校准//可以使用软件触发转换,可以手动获取当前采集值
	ADC_Cmd(ADC1,ENABLE);
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1)==SET);//0为复位完成 1为正在复位
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1)==SET);//0为校准完成 1为正在校准
	
	
	
}
/**
  * @brief 配合AD_Init,获取电位计采集到的0-3.3V模拟量
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
uint16_t Get_SensorValue(uint8_t ADC_Channel){
	//3、配置多路开关,把GPIO通道接到规则组列表
	ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_1Cycles5);//采样时间选择1.5个ADC周期,1.5+12.5 =14,ADCCLK = 12MHz,所以采样时间 = 14/12 = 1.17us
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
	return ADC_GetConversionValue(ADC1);
}

AD.h

#ifndef __AD_H
#define __AD_H
#include "stm32f10x.h"                  // Device header

void AD_Init(void);
uint16_t Get_SensorValue(uint8_t ADC_Channel);

#endif

4、 DMA数据转运

根据STM3存储器映像。

一般变量存在SRAM ,0x20000000起始。

const等常量存在Flash中,0x08000000起始后,存在程序代码的存储后。

 外设存储器地址例如ADC->DR存在,0x40000000起始的后面。

如果想查一个寄存器的地址,就需要先查到外设地址,然后看单个寄存器的偏移(使用芯片查手册基本都是这样)

main.c 

#include "stdio.h"
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
uint8_t DataB[4] = {0x00,0x00,0x00,0x00};
uint8_t DataA[4] = {0x01,0x02,0x03,0x04};
int main(void){
	OLED_Init();
	MyDMA_Init((uint32_t)&DataA[0],(uint32_t)&DataB[0],4);
	OLED_ShowString(1,1,"DataA:");
	OLED_ShowHexNum(2,1,DataA[0],2);
	OLED_ShowHexNum(2,4,DataA[1],2);
	OLED_ShowHexNum(2,7,DataA[2],2);
	OLED_ShowHexNum(2,10,DataA[3],2);
	OLED_ShowString(3,1,"DataB:");
	OLED_ShowHexNum(4,1,DataB[0],2);
	OLED_ShowHexNum(4,4,DataB[1],2);
	OLED_ShowHexNum(4,7,DataB[2],2);
	OLED_ShowHexNum(4,10,DataB[3],2);
	while(1){
		for(int i=0;i<4;i++){
			DataA[i]++;
		}
		Delay_ms(1000);
		startDMA1Channel1Software();
		OLED_ShowString(1,1,"DataA:");
		OLED_ShowHexNum(2,1,DataA[0],2);
		OLED_ShowHexNum(2,4,DataA[1],2);
		OLED_ShowHexNum(2,7,DataA[2],2);
		OLED_ShowHexNum(2,10,DataA[3],2);
		OLED_ShowString(3,1,"DataB:");
		OLED_ShowHexNum(4,1,DataB[0],2);
		OLED_ShowHexNum(4,4,DataB[1],2);
		OLED_ShowHexNum(4,7,DataB[2],2);
		OLED_ShowHexNum(4,10,DataB[3],2);
	}
	return 0;
}

MyDMA.c

#include "stm32f10x.h"                  // Device header
/**
  * @brief DMA1初始化,进行存储器到存储器的转运
	* @param  AddrA:源地址
  *     @arg 
	* @param  AddrB:目标地址
  *     @arg 
  * @param  BufferSize:计数器大小,即DMA_Byte个数
  *     @arg 
  * @retval None
*/
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t BufferSize){
	RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1EN,ENABLE);//使能DMA1时钟
	
	//DMA初始化,理论上为外设->存储器,实际上根据配置
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_BufferSize = BufferSize;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//传输方向
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//M2M软硬件触发选择,选择是否软件触发
	DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;//存储器
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器是否自增
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//传输模式,是否使用自动重装,自动重装和软件触发不能同时使用,会持续传输不能停止,软件触发DMA只要使能就会一直转运,计数清空后停止
	DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;//起始
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级
	DMA_Init(DMA1_Channel1,&DMA_InitStructure);
	
	
	DMA_Cmd(DMA1_Channel1,ENABLE);
}
/**
  * @brief 软件触发DMA当计数器清零时停止,此函数配合进行DMA1软件再次触发
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void startDMA1Channel1Software(void){
	while(DMA_GetCurrDataCounter(DMA1_Channel1) != 0 );
	DMA_Cmd(DMA1_Channel1,DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel1,4);
	DMA_Cmd(DMA1_Channel1,ENABLE);
}

 MyDMA.h

#ifndef __MYDMA_H
#define __MyDMA_H
#include "stm32f10x.h"                  // Device header
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t BufferSize);
void startDMA1Channel1Software(void);
#endif

5、多通道连续扫描多次采集,使用DMA采集ADC1通道1和通道2的数据

main.c

#include "stdio.h"
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
int main(void){
	OLED_Init();
	MyDMA_AD_Init();
	OLED_ShowString(1,1,"AD0:0000");
	OLED_ShowString(2,1,"AD1:0000");
	while(1){
		OLED_ShowNum(1,5,ADC_Value[0],4);
		OLED_ShowNum(2,5,ADC_Value[1],4);
		Delay_ms(100);							//延时100ms,手动增加一些转换的间隔时间
	}
	return 0;
}

 MyDMA.c

#include "stm32f10x.h"                  // Device header

uint16_t ADC_Value[4]={0};//ADC采集使用的数组
/**
  * @brief DMA1初始化,进行存储器到存储器的转运
	* @param  AddrA:源地址
  *     @arg 
	* @param  AddrB:目标地址
  *     @arg 
  * @param  BufferSize:计数器大小,即DMA_Byte个数
  *     @arg 
  * @retval None
*/
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t BufferSize){
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//使能DMA1时钟
	
	//DMA初始化,理论上为外设->存储器,实际上根据配置
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_BufferSize = BufferSize;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//传输方向
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//M2M软硬件触发选择,选择是否软件触发
	DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;//存储器
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器是否自增
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//传输模式,是否使用自动重装,自动重装和软件触发不能同时使用,会持续传输不能停止,软件触发DMA只要使能就会一直转运,计数清空后停止
	DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;//起始
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级
	DMA_Init(DMA1_Channel1,&DMA_InitStructure);
	
	
	DMA_Cmd(DMA1_Channel1,ENABLE);
}
/**
  * @brief 软件触发DMA当计数器清零时停止,此函数配合进行DMA1软件(MyDMA_Init函数)再次触发
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void startDMA1Channel1Software(void){
	while(DMA_GetCurrDataCounter(DMA1_Channel1) != 0 );
	DMA_Cmd(DMA1_Channel1,DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel1,4);
	DMA_Cmd(DMA1_Channel1,ENABLE);
}

/**
  * @brief DMA1_Channel1 + ADC1_Channel_0和ADC1_Channel_1,使用DMA1转运ADC1通道数据,
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void MyDMA_AD_Init(void){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);		//开启DMA1的时钟
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 ;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0、PA1、PA2和PA3引脚初始化为模拟输入
	
	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_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
	
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_BufferSize = 2;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//传输方向
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//M2M软硬件触发选择,选择是否软件触发
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_Value[0];//存储器
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器是否自增
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//传输模式,是否使用自动重装,自动重装和软件触发不能同时使用,会持续传输不能停止,软件触发DMA只要使能就会一直转运,计数清空后停止
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;//起始,获取ADC1->DR寄存器的地址
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级
	DMA_Init(DMA1_Channel1,&DMA_InitStructure);

	DMA_Cmd(DMA1_Channel1,ENABLE);//DMA开启
	ADC_DMACmd(ADC1,ENABLE);//ADC到DMA开启
	ADC_Cmd(ADC1,ENABLE);//ADC开启
	
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1)==SET);//0为复位完成 1为正在复位
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1)==SET);//0为校准完成 1为正在校准
	
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//ADC软件触发一次
}

 MyDMA.h

#ifndef __MYDMA_H
#define __MyDMA_H
#include "stm32f10x.h"                  // Device header
extern uint16_t ADC_Value[4];
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t BufferSize);
void startDMA1Channel1Software(void);
void MyDMA_AD_Init(void);
#endif

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值