STM32—ADC模数转换

ADC的基本特征
Analog-to-Digital Converter的缩写。指模/数转换器或者模拟/数字转换器。是指将连续变量的模拟信号转换为离散的数字信号的器件

典型的模拟数字转换器将模拟信号转换为表示一定比例电压值的数字信号

stm32——ADC简介

STM32 拥有 1~3 个 ADC(STM32F101/102 系列只有 1 个 ADC),这些 ADC 可以独立使用,也可以使用双重模式(提高采样率)。STM32 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 18 个通道,可测量 16 个外部和 2 个内部信号源。各通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。

STM32F103 系列最少都拥有 2 个 ADC,我们选择的 STM32F103RCT 包含有 3 个 ADC。STM32 的 ADC 最大的转换速率为 1Mhz,也就是转换时间为 1us(在 ADCCLK=14M,采样周期为 1.5 个 ADC 时钟下得到),不要让ADC 的时钟超过 14M,否则将导致结果准确度下降。

STM32 将 ADC 的转换分为 2 个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。

ADC的主要特征
1 12位逐次逼近型的模拟数字转换器;
2 最多带3个ADC控制器,可以单独使用,也可以使用双重模式提高采样率;
3 最多支持23个通道,可最多测量21个外部和2个内部信号源;
4 支持单次和连续转换模式;
5 转换结束,注入转换结束,和发生模拟看门狗事件时产生中断;
6 通道0到通道n的自动扫描模式;
7 自动校准;
8 采样间隔可以按通道编程;
9 规则通道和注入通道均有外部触发选项;
10 转换结果支持左对齐或右对齐方式存储在16位数据寄存器;
11 ADC转换时间:最大转换速率 1us(最大转换速度为1MHz,在ADCCLK=14M,采样周期为1.5个ADC时钟下得到);
12 ADC供电要求:2.4V-3.6V;
13 ADC输入范围:VREF- ≤ VIN ≤ VREF+。

STM32F10x系列芯片ADC通道和引脚对应关系

用stm32cubemx生成的adc数据读不到_寄存器


由上图中可以看出,STM32F103带3个ADC控制器,一共支持23个通道,包括21个外部和2个内部信号源;但是每个ADC控制器最多只可以有18个通道,包括16个外部和2个内部信号源。

ADC的基本原理介绍
ADC的工作框图

用stm32cubemx生成的adc数据读不到_寄存器_02


ADC模块的框图看起来比较复杂,接下来会一点一点地对它进行分析。

ADC引脚

用stm32cubemx生成的adc数据读不到_数据_03


一般情况下,VDD是3.3V,VSS接地,相对应的,VDDA是3.3V,VSSA也接地,模拟输入信号不要超过VDD(3.3V)。

ADC定时器详解

框图中标注的来自ADC预分频器的ADCCLK是ADC模块的时钟来源。通常,由时钟控制器提供的ADCCLK时钟和PCLK2(APB2时钟)同步。RCC控制器为ADC时钟提供一个专用的可编程预分频器。

用stm32cubemx生成的adc数据读不到_寄存器_04


这里需要注意一下,一般情况下:不要让ADC时钟超过14MHz,否则可能不准。
也就是说,如果按照默认设置PCLK2为72MHz,此时应为6分频或者8分频。
ADC中断

在框图中的最顶部,显示ADC的各种中断。很显然可以看出:规则和注入组转换结束时能产生中断,当模拟看门狗状态位被设置时也能产生中断。它们都有独立的中断使能位。
注: ADC1和ADC2的中断映射在同一个中断向量上,而ADC3的中断有自己的中断向量。

用stm32cubemx生成的adc数据读不到_数据_05


ADC中断事件的具体类型有三种,具体见下图:

用stm32cubemx生成的adc数据读不到_寄存器_06


ADC通道选择

STM32的ADC控制器有很多通道,所以模块通过内部的模拟多路开关,可以切换到不同的输入通道并进行转换。STM32特别地加入了多种成组转换的模式,可以由程序设置好之后,对多个模拟通道自动地进行逐个地采样转换。它们可以组织成两组:规则通道组和注入通道组。

在执行规则通道组扫描转换时,如有例外处理则可启用注入通道组的转换。也就是说,注入通道的转换可以打断规则通道的转换,在注入通道被转换完成之后,规则通道才可以继续转换。

可能单从字面上还是不是很了解?我们可以通过图形来更直观地认知:

用stm32cubemx生成的adc数据读不到_单片机_07


一个不太恰当的比喻是:规则通道组的转换好比是程序的正常执行,而注入通道组的转换则好比是程序正常执行之外的一个中断处理程序。

ADC转换方式
STM32的ADC的各通道可以组成规则通道组或注入通道组,但是在转换方式还可以有单次转换、连续转换、扫描转换模式

1 单次转换模式

单次转换模式下,ADC只执行一次转换。该模式既可通过设置ADC_CR2寄存器的ADON位(只适用于规则通道)启动也可通过外部触发启动(适用于规则通道或注入通道),这时CONT位为0。

用stm32cubemx生成的adc数据读不到_寄存器_08


2 连续转换模式

在连续转换模式中,当前面ADC转换一结束马上就启动另一次转换。此模式可通过外部触发启动或通过设置ADC_CR2寄存器上的ADON位启动,此时CONT位是1。

用stm32cubemx生成的adc数据读不到_扫描模式_09

3 扫描模式

扫描模式可通过设置ADC_CR1寄存器的SCAN位来选择。一旦这个位被设置,ADC扫描所有被ADC_SQRX寄存器(对规则通道)或ADC_JSQR(对注入通道)选中的所有通道。在每个组的每个通道上执行单次转换。在每个转换结束时,同一组的下一个通道被自动转换。如果设置了CONT位,转换不会在选择组的最后一个通道上停止,而是再次从选择组的第一个通道继续转换。

用stm32cubemx生成的adc数据读不到_寄存器_10


这里需要注意的是:如果在使用扫描模式的情况下使用中断,会在最后一个通道转换完毕后才会产生中断。而连续转换,是在每次转换后,都会产生中断。

如果设置了DMA位,在每次EOC后,DMA控制器把规则组通道的转换数据传输到SRAM中。而注入通道转换的数据总是存储在ADC_JDRx寄存器中。

外部触发转换
在框图的下方,显示了规则转换、注入转换可以由外部事件触发(比如定时器捕捉、EXTI线)。如果设置了EXTTRIG控制位,则外部事件就能够触发转换。EXTSEL[2:0]和JEXTSEL2:0]控制位允许应用程序选择8个可能的事件中的某一个,可以触发规则和注入组的采样。

注意: 当外部触发信号被选为ADC规则或注入转换时,只有它的上升沿可以启动转换。
自动校准
校准ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。在校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差。

通过设置ADC_CR2寄存器的CAL位启动校准。一旦校准结束,CAL位被硬件复位,可以开始正常转换。建议在上电时执行一次ADC校准。校准阶段结束后,校准码储存在ADC_DR中。

用stm32cubemx生成的adc数据读不到_数据_11

数据对齐

由于STM32的ADC是12位逐次逼近型的模拟数字转换器,而数据保存在16位寄存器中。所以,ADC_CR2寄存器中的ALIGN位选择转换后数据储存的对齐方式。数据可以左对齐或右对齐,如下图所示:

用stm32cubemx生成的adc数据读不到_扫描模式_12


注入组通道转换的数据值已经减去了在ADC_JOFRx寄存器中定义的偏移量,因此结果可以是一个负值。SEXT位是扩展的符号值。

对于规则组通道,不需减去偏移值,因此只有12个位有效。

通道采样时间

ADC使用若干个ADC_CLK周期对输入电压采样,采样周期数目可以通过ADC_SMPR1和ADC_SMPR2寄存器中的SMP[2:0]位更改。每个通道可以分别用不同的时间采样。

总转换时间如下计算:
TCONV = 采样时间+ 12.5个周期

例如:当ADCCLK=14MHz,采样时间为1.5周期时,TCONV =1.5+12.5=14周期=1μs。

故而,ADC的最小采样时间1us(ADC时钟=14MHz,采样周期为1.5周期下得到)。

ADC相关寄存器的配置

ADC控制寄存器1(ADC_CR1)

用stm32cubemx生成的adc数据读不到_数据_13


作用:设置扫描模式、中断允许(转换结束、注入转换结束、模拟看门狗)、双模式选择(一般选用独立模式)等。

注意:在扫描模式下,由ADC_SQRx或者ADC_JSQRx寄存器选中的通道被转换。如果设置了EOCIE或者JEOCIE,在最后一个通道转换完毕后才会产生EOC或者JEOC中断。

ADC控制寄存器2(ADC_CR2)

用stm32cubemx生成的adc数据读不到_stm32_14


作用:设置数据对齐方式、连续转换位、ADC启动位、外部触发转换(一般选用软件转换SWSTART、JSWSTART)

ADC采样时间寄存器x(ADC_SMPRx)

用stm32cubemx生成的adc数据读不到_寄存器_15


作用:设置ADC各通道的采样时间。

ADC注入通道数据偏移寄存器x (ADC_JOFRx)

用stm32cubemx生成的adc数据读不到_扫描模式_16


作用:设置ADC注入通道数据偏移。

ADC看门狗高/低阀值寄存器(ADC_HTR、ADC_LRT)
加粗样式

用stm32cubemx生成的adc数据读不到_扫描模式_17


作用:设置ADC模拟看门狗的高低阈值。

ADC规则序列寄存器x(ADC_SQRx)

用stm32cubemx生成的adc数据读不到_寄存器_18


作用:设置规则通道序列长度、对应序列中各个转换的通道编号(最多16个)。

ADC注入序列寄存器(ADC_JSQR)

用stm32cubemx生成的adc数据读不到_单片机_19


作用:设置注入通道序列长度、对应序列中各个转换的通道编号(最多4个)。

ADC注入数据寄存器x(ADC_JDRx)

用stm32cubemx生成的adc数据读不到_扫描模式_20


作用:存放ADC注入转换的数据。

ADC规则数据寄存器(ADC_DR)

用stm32cubemx生成的adc数据读不到_寄存器_21


作用:存放ADC规则转换的数据。

ADC状态寄存器(ADC_SR)

用stm32cubemx生成的adc数据读不到_数据_22


作用:存放ADC转换过程中的各种状态位。

STM32—ADC的配置过程

通过以上介绍,我们了解了 STM32 的单次转换模式下的相关设置,本章我们使用 ADC1的通道 1 来进行 AD 转换,其详细设置步骤如下:

1) ) 开启 PA 口时钟,设置 PA1 为模拟输入。
STM32F103RCT6 的 ADC 通道 1 在 PA1 上,所以,我们先要使能 PORTA 的时钟,然后设置 PA1 为模拟输入。
2) )使能 ADC1 时钟,并设置分频因子。
要使用 ADC1,第一步就是要使能 ADC1 的时钟,在使能完时钟之后,进行一次 ADC1 的复位。接着我们就可以通过 RCC_CFGR 设置 ADC1 的分频因子。分频因子要确保 ADC1 的时钟(ADCCLK)不要超过 14Mhz。
3) ) 设置 ADC1 的工作模式。
在设置完分频因子之后,我们就可以开始 ADC1 的模式配置了,设置单次转换模式、触发方式选择、数据对齐方式等都在这一步实现。
4) ) 设置 ADC1 规则序列的相关信息。
接下来我们要设置规则序列的相关信息,我们这里只有一个通道,并且是单次转换的,所以设置规则序列中通道数为 1(ADC_SQR1[23:20]=0000),然后设置通道 1 的采样周期(通过ADC_SMPR2[5:3]设置)。
5) )开启 AD 转换器,并校准。
在设置完了以上信息后,我们就开启 AD 转换器,执行复位校准和 AD 校准,注意这两步是必须的!不校准将导致结果很不准确。
6) ) 读取 ADC 值。
在上面的校准完成之后,ADC 就算准备好了。接下来我们要做的就是设置规则序列 1 里面的通道(通过 ADC_SQR3[4:0]设置),然后启动 ADC 转换。在转换结束后,读取 ADC1_DR 里面的值就是了。

这里还需要说明一下 ADC 的参考电压,该芯片没有外部参考电压引脚,ADC 的参考电压直接取自 VDDA,也就是 3.3V。

通过以上几个步骤的设置,我们就能正常的使用 STM32 的 ADC1 来执行 AD 转换操作了。

ADC采集软件设计

void  Adc_Init(void)
{
   
	ADC_InitTypeDef   ADC_InitStructure; 
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF |RCC_APB2Periph_ADC3, ENABLE );	  //使能ADC1通道时钟

	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
	//PA1 作为模拟通道输入引脚 
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;//模拟输入引脚
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;

    GPIO_Init(GPIOF,&GPIO_InitStructure);
	
	
	ADC_DeInit(ADC3);//复位ADC1 
	
	ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;	//模数转换工作在单次转换模式
	ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//ADC数据右对齐
	ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//转换由软件而不是外部触发启动
	ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//ADC工作模式:ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_NbrOfChannel=1;//顺序进行规则转换的ADC通道的数目
	ADC_InitStructure.ADC_ScanConvMode=DISABLE;//模数转换工作在单通道模式
	ADC_Init(ADC3,&ADC_InitStructure);
	
	ADC_Cmd(ADC3,ENABLE);	//使能指定的ADC1
	   
     while(ADC_GetResetCalibrationStatus(ADC3));	//等待复位校准结束
	
	   ADC_StartCalibration(ADC3);	 //开启AD校准
 
	while(ADC_GetCalibrationStatus(ADC3));	 //等待校准结束
	   
} 

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

	return ADC_GetConversionValue(ADC3);	//返回最近一次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;
}

在主函数里面

adcx=Get_Adc_Average(ADC_Channel_4,10);
		LCD_ShowxNum(156,190,adcx,4,16,0);//显示ADC的值
		temp=(float)adcx*(3.3/4096);
		adcx=temp;
		
		LCD_ShowxNum(156,210,adcx,1,16,0);//显示电压值
		temp-=adcx;
		temp*=1000;
		LCD_ShowxNum(172,210,temp,3,16,0X80);
		LED0=!LED0;
		delay_ms(250);

通过以上步骤,就可以实现通过PA1来进行电压的采集。