STM32学习之ADC

一、ADC简介

1.1 基础简介

STM32其ADC的规则通道组最多包含 16 个转换,而注入通道组最多包含 4 个通道。STM32 的ADC可以进行很多种不同的转换模式,我们这里就不在一一列举了。

1.2 使用规则通道的单次转换模式

STM32 的 ADC 在单次转换模式下,只执行一次转换,该模式可以通过 ADC_CR2 寄存器的ADON位(只适用于规则通道)启动,也可以通过外部触发启动(适用于规则通道和注入通道),这时CONT 位为 0。 以规则通道为例,一旦所选择的通道转换完成,转换结果将被存ADC_DR 寄存器中,EOC(转换结束)标志将被置位,如果设置了 EOCIE,则会产生中断。然后 ADC 将停止,直到下次启动。我们介绍一下我们执行规则通道的单次转换,需要用到的ADC寄存器。

1.21 需要用到的ADC寄存器

ADC控制寄存器(ADC_CR1和ADC_CR2)

ADC_CR1:

在这里插入图片描述
ADC_CR1 的 SCAN位,该位用于设置扫描模式,由软件设置和清除,如果设置为 1,则使用扫描模式,如果为 0,则关闭扫描模式。在扫描模式下,由ADC_SQRx 或ADC_JSQRx 寄存器选中的通道被转换。如果设置了 EOCIE 或 JEOCIE,只在最后一个通道转换完毕后才会产生 EOC或 JEOC 中断。

ADC_CR1[19:16]用于设置ADC的操作模式,详细的对应关系如图所示:

在这里插入图片描述
本章我们要使用的是独立模式,所以设置这几位为 0 就可以了

ADC_CR2:

在这里插入图片描述
该寄存器我们也只针对性的介绍一些位:ADON位用于开关AD转换器。而CONT 位用于设置是否进行连续转换,我们使用单次转换,所以 CONT 位必须为 0。CAL 和 RSTCAL 用于 AD校准。ALIGN用于设置数据对齐,我们使用右对齐,该位设置为 0。EXTSEL[2:0]用于选择启动规则转换组转换的外部事件,详细的设置关系如图所示

在这里插入图片描述
我们这里使用的是软件触发(SWSTART),所以设置这 3 个位为 111。

ADC_CR2 的SWSTART 位用于开始规则通道的转换,我们每次转换(单次转换模式下)都需要向该位写 1。 AWDEN为用于使能温度传感器和Vrefint。

ADC采样事件寄存器(ADC_SMPR1 和ADC_SMPR2)

这两个寄存器用于设置通道 0~17 的采样时间,每个通道占用 3 个位。ADC_SMPR1 的各位描述如图所 示:

在这里插入图片描述

ADC_SMPR2 的各位描述如下图所示:

在这里插入图片描述
对于每个要转换的通道,采样时间建议尽量长一点,以获得较高的准确度,但是这样会降低ADC的转换速率。ADC 的转换时间可以由以下公式计算: Tcovn=采样时间+12.5 个周期。其中:Tcovn 为总转换时间,采样时间是根据每个通道的 SMP 位的设置来决定的。
例如,当ADCCLK=14Mhz 的时候,并设置 1.5 个周期的采样时间,则得到:Tcovn=1.5+12.5=14 个周 期=1us。

ADC 规则序列寄存器(ADC_SQR1~3)

该寄存器总共有 3 个,这几个寄存器的功能都差不多,这里我们仅介绍一下ADC_SQR1,该寄存器的各位描述如图所示:

在这里插入图片描述

L[3:0]用于存储规则序列的长度,我们这里只用了 1 个,所以设置这几个位的值为 0。其他的 SQ13~16 则存储了规则序列中第 13~16 个通道的编号(0~17)。另外两个规则序列寄存器同ADC_SQR1 大同小异,我们这里就不再介绍了,要说明一点的是:我们选择的是单次转换, 所以只有一个通道在规则序列里面,这个序列就是 SQ1,通过ADC_SQR3 的最低 5 位(也就是 SQ1)设置。 第四个要介绍的是ADC规则数据寄存器(ADC_DR)。规则序列中的AD转化结果都将被存在这个寄存器里面,而注入通道的转换结果被保存在 ADC_JDRx 里面。ADC_DR 的各位描述 如图 :

在这里插入图片描述
这里要提醒一点的是,该寄存器的数据可以通过 ADC_CR2 的 ALIGN 位设置左对齐还是右对齐。在读取数据的时候要注意。 最后一个要介绍的ADC寄存器为ADC状态寄存器(ADC_SR),该寄存器保存了ADC转换时的各种状态。该寄存器的各位描述如图所示:

在这里插入图片描述

这里我们要用到的是 EOC 位,我们通过判断该位来决定是否此次规则通道的 AD 转换已经完成,如果完成我们就从ADC_DR中读取转换结果,否则等待转换完成。

1.22 详细设置步骤

1)开启 PA口时钟和ADC1 时钟,设置 PA1 为模拟输入

STM32F103ZET6 的ADC通道 1 在PA1 上,所以,我们先要使能PORTA 的时钟和 ADC1时钟,然后设置 PA1 为模拟输入。使能 GPIOA 和 ADC 时钟用 RCC_APB2PeriphClockCmd 函 数,设置 PA1 的输入方式,使用 GPIO_Init 函数即可。这里我们列出 STM32 的 ADC 通道与 GPIO对应表:

在这里插入图片描述

2)复位ADC1,同时设置ADC1 分频因子

开启ADC1 时钟之后,我们要复位ADC1,将ADC1 的全部寄存器重设为缺省值之后我们就可以通过RCC_CFGR 设置 ADC1 的分频因子。分频因子要确保ADC1 的时钟(ADCCLK) 不要超过 14Mhz。 这个我们设置分频因子位 6,时钟为 72/6=12MHz。
库函数的实现方法是: RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC时钟复位的方法是: ADC_DeInit(ADC1);
这个函数非常容易理解,就是复位指定的ADC。

3)初始化 ADC1参数,设置ADC1 的工作模式以及规则序列的相关信息

在设置完分频因子之后,我们就可以开始ADC1 的模式配置了,设置单次转换模式、触发方式选择、数据对齐方式等都在这一步实现。同时,我们还要设置ADC1 规则序列的相关信息, 我们这里只有一个通道,并且是单次转换的,所以设置规则序列中通道数为 1 。这些在库函数 中是通过函数ADC_Init 实现的,下面我们看看其定义:

void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef*ADC_InitStruct)//第一个参数是指定ADC号

typedef struct {
   uint32_t ADC_Mode;    //设置ADC的模式
   FunctionalState ADC_ScanConvMode;   //设置是否开启扫描模式
   FunctionalState ADC_ContinuousConvMode;  //是否开启连续转换模式
   uint32_t ADC_ExternalTrigConv;   //用来设置启动规则转换组转换的外部事件
   uint32_t ADC_DataAlign;    //用来设置 ADC 数据对齐方式是左对齐还是右对齐
   uint8_t ADC_NbrOfChannel;   //用来设置规则序列的长度
}ADC_InitTypeDef;

如果是多通道 ,则设置uint8_t ADC_NbrOfChannel 为对应的通道数量,且在后面配置ADC_RegularChannelConfig时,也要配置相应个数(每个通道的序号)

4)使能ADC并校准

在设置完了以上信息后,我们就使能 AD 转换器,执行复位校准和 AD 校准
使能指定的ADC的方法是: ADC_Cmd(ADC1, ENABLE);
执行复位校准的方法是: ADC_ResetCalibration(ADC1);
执行ADC校准的方法是: ADC_StartCalibration(ADC1); //开始指定ADC1 的校准状态记住,每次进行校准之后要等待校准结束。这里是通过获取校准状态来判断是否校准是否结束。 下面我们一一列出复位校准和AD校准的等待结束方法:
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束 while(ADC_GetCalibrationStatus(ADC1)); //等待校AD准结束

以上四个步骤写为:

void  Adc_Init(void)
{ 	
	ADC_InitTypeDef ADC_InitStructure; 
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE );	  //使能ADC1通道时钟
 

	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M

	//PA1 作为模拟通道输入引脚                         
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
	GPIO_Init(GPIOA, &GPIO_InitStructure);	

	ADC_DeInit(ADC1);  //复位ADC1 

	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	//ADC工作模式:ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;	//模数转换工作在单通道模式
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;	//模数转换工作在单次转换模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//转换由软件而不是外部触发启动
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//ADC数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 1;	//顺序进行规则转换的ADC通道的数目
	ADC_Init(ADC1, &ADC_InitStructure);	//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   

  
	ADC_Cmd(ADC1, ENABLE);	//使能指定的ADC1
	
	ADC_ResetCalibration(ADC1);	//使能复位校准  
	 
	while(ADC_GetResetCalibrationStatus(ADC1));	//等待复位校准结束
	
	ADC_StartCalibration(ADC1);	 //开启AD校准
 
	while(ADC_GetCalibrationStatus(ADC1));	 //等待校准结束
 
//	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能

}				  
5)读取ADC值

接下来我们要做的就是设置规则序列 1 里面的通道,采样顺序,以及通道的采样周期,然后启动ADC转换。在转换结束后,读取ADC转换结果值就是了。这里设置规则序列通道以及采样周期的函数是:
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
我们这里是规则序列中的第 1 个转换,同时采样周期为 239.5,所以设置为:
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );
软件开启ADC转换的方法是:
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能指定的ADC1 的软件
转换启动功能,开启转换之后,就可以获取转换ADC转换结果数据,方法是:
ADC_GetConversionValue(ADC1);
同时在 AD 转换中,我们还要根据状态寄存器的标志位来获取 AD 转换的各个状态信息。库函数获取AD转换的状态信息的函数是:
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG)
比如我们要判断ADC1d 的转换是否结束,方法是:
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束

这里还需要说明一下 ADC 的参考电压,STM32F103 最小系统板使用的是STM32F103ZET6,该芯片有外部参考电压:Vref-和 Vref+,其中 Vref-必须和 VSSA 连接在一 起,而Vref+的输入范围为:2.4~VDDA。如果大家想自己设置其他参考电压,将你的参考电压接在 Vref-和 Vref+上就 OK 了。本章我们的参考电压设置的是 3.3V。
通过以上几个步骤的设置,我们就能正常的使用 STM32 的ADC1 来执行AD转换操作了。

代码为:

u16 Get_Adc(u8 ch)   
{
  	//设置指定ADC的规则组通道,一个序列,采样时间
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );	//ADC1,ADC通道,采样时间为239.5周期	  			    
  
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能	
	 
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束

	return ADC_GetConversionValue(ADC1);	//返回最近一次ADC1规则组的转换结果
}

u16 Get_Adc_Average(u8 ch,u8 times)
{
	u32 temp_val=0;
	u8 t;
	for(t=0;t<times;t++)
	{
		temp_val+=Get_Adc(ch);
		delay_ms(5);
	}
	return temp_val/times;
} 	 

主函数为:

int main(void)
 {	 
    u16 adcx;
	float temp;
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 	//串口初始化为115200		 	
 	Adc_Init();		  		//ADC初始化
 	
	while(1)
	{
		adcx=Get_Adc_Average(ADC_Channel_1,10);  //得到ADC的值
		temp=(float)adcx*(3.3/4096);      //得到电压值
		delay_ms(250);	
	}
 }

1.3 多通道采集

只需修改三个部分

  1. void Adc_Init(void)函数中添加初始化需要的引脚
	//PA1 、PA2作为模拟通道输入引脚                         
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
	GPIO_Init(GPIOA, &GPIO_InitStructure);	
  1. 添加相应通道数量
	ADC_RegularChannelConfig(ADC1,ADC_Channel_1, 1, ADC_SampleTime_239Cycles5 );	//ADC1,ADC通道,采样时间为239.5周期	  		
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_239Cycles5 );	//ADC1,ADC通道,采样时间为239.5周期	  		
  1. 在主函数添加相应数量的通道来采集数据
int main(void)
 {	 
    u16 adc1,adc2;
	float temp;
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 	//串口初始化为115200		 	
 	Adc_Init();		  		//ADC初始化
 	
	while(1)
	{
		 /*先采集通道1数据*/
    	adc1=Get_Adc_Average(ADC_Channel_1,10);  //得到ADC的值
		temp=(float)adcx*(3.3/4096);      //得到电压值
	  
	  /*再采集通道2数据*/
	    adc2=Get_Adc_Average(ADC_Channel_2,10);  //得到ADC的值
		temp=(float)adcx*(3.3/4096);      //得到电压值
		delay_ms(250);
	}
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值