ADC简介

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

典型的模拟数字转换器将模拟信号转换为表示一定比例电压值的数字信号,简单地说就是将模拟电压值,转换成对应的肉眼可读数值。

STM32f103系列有3个ADC,精度为12位,每个ADC最多有16个外部通道。其中ADC1和ADC2都有16个外部通道,ADC3一般有8个外部通道,各通道的A/D转换可以单次、连续、扫描或间断执行,ADC转换的结果可以左对齐或右对齐储存在16位数据寄存器中。ADC的输入时钟不得超过14MHz(否则将导致结果准度的降低),其时钟频率由PCLK2分频产生。

ADC特点

● 12位分辨率
● 转换结束、注入转换结束和发生模拟看门狗事件时产生中断
● 单次和连续转换模式
● 从通道0到通道n的自动扫描模式
● 自校准
● 带内嵌数据一致性的数据对齐
● 采样间隔可以按通道分别编程
● 规则转换和注入转换均有外部触发选项
● 间断模式
● 双重模式(带2个或以上ADC的器件)
● ADC转换时间:
─ STM32F103xx增强型产品:时钟为56MHz时为1μs(时钟为72MHz为1.17μs)
─ STM32F101xx基本型产品:时钟为28MHz时为1μs(时钟为36MHz为1.55μs)
─ STM32F102xxUSB型产品:时钟为48MHz时为1.2μs
─ STM32F105xx和STM32F107xx产品:时钟为56MHz时为1μs(时钟为72MHz为1.17μs)
● ADC供电要求:2.4V到3.6V
● ADC输入范围:VREF- ≤ VIN ≤ VREF+
● 规则通道转换期间有DMA请求产生。

ADC原理

ADC框图
在这里插入图片描述

1.电压输入范围
ADC所能测量的电压范围就是VREF- ≤ VIN ≤ VREF+,把 VSSA 和 VREF-接地,把 VREF+和 VDDA 接 3V3,得到ADC 的输入电压范围为: 0~3.3V。(这里注意PA1用杜邦线要连接到3.3V,不要连5v,否则板子会烧)
2.输入通道
ADC的信号输入就是通过通道来实现的,信号通过通道输入到单片机中,单片机经过转换后,将模拟信号输出为数字信号。STM32中的ADC有着18个通道,16个外部通道,2个内部通道。

在这里插入图片描述
这里注意,空白格都是连接内部VSS,这里的PF管脚,只有144脚芯片才有,因此144脚芯片多五个通道,为21个外部通道,小于144脚芯片只有16个外部通道。

外部的16个通道在转换时又分为规则通道注入通道,其中规则通道最多有16路,注入通道最多有4路(注入通道类似中断事件),这里简单介绍:
规则通道
最平常的通道、也是最常用的通道,平时的ADC转换都是用规则通道实现的。
注入通道
注入通道是相对于规则通道的,注入通道可以在规则通道转换时,强行插入转换,相当于一个“中断通道”吧。当有注入通道需要转换时,规则通道的转换会停止,优先执行注入通道的转换,当注入通道的转换执行完毕后,再回到之前规则通道进行转换。

3.转换顺序
理解ADC的转换通道之后,会发现使用ADC只用一个通道来转换,使用比较简单,但现实生活中经常要用到多通道,这里就涉及先后转换顺序了。

规则通道转换顺序
规则通道中的转换顺序由三个寄存器控制:SQR1、SQR2、SQR3,它们都是32位寄存器。SQR寄存器控制着转换通道的数目和转换顺序,只要在对应的寄存器位SQx中写入相应的通道,这个通道就是第x个转换。

在这里插入图片描述
注入通道转换顺序
和规则通道转换顺序的控制一样,注入通道的转换也是通过注入寄存器来控制,只不过只有一个JSQR寄存器来控制,控制关系如下:

在这里插入图片描述
注意:只有当JL=4的时候,注入通道的转换顺序才会按照JSQ1、JSQ2、JSQ3、JSQ4的顺序执行。当JL<4时,注入通道的转换顺序恰恰相反,也就是执行顺序为:JSQ4、JSQ3、JSQ2、JSQ1。

4.触发源
ADC需要触发信号来进行模/数转换,主要有两种:
其一就是通过直接配置寄存器触发,通过配置控制寄存器CR2的ADON位,写1时开始转换,写0时停止转换。在程序运行过程中只要调用库函数,将CR2寄存器的ADON位置1就可以进行转换,比较好理解。
另外,还可以通过内部定时器或者外部IO触发转换,也就是说可以利用内部时钟让ADC进行周期性的转换,也可以利用外部IO使ADC在需要时转换,具体的触发由控制寄存器CR2决定。

5.转换时间
ADC每次信号转换都需要转换时间,转换时间由输入时钟采样周期来决定。

输入时钟
由于ADC在STM32中是挂载在APB2总线上的,所以ADC得时钟是由PCLK2(72MHz)经过分频得到的,分频因子由 RCC 时钟配置寄存器RCC_CFGR 的位 15:14 ADCPRE[1:0]设置,可以是 2/4/6/8 分频,一般配置分频因子为8,即8分频得到ADC的输入时钟频率为9MHz

采样周期
采样周期是确立在输入时钟上的,配置采样周期可以确定使用多少个ADC时钟周期来对电压进行采样,采样的周期数可通过 ADC采样时间寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0]位设置,ADC_SMPR2 控制的是通道 0~9, ADC_SMPR1 控制的是通道 10~17。每个通道可以配置不同的采样周期,但最小的采样周期是1.5个周期,也就是说如果想最快时间采样就设置采样周期为1.5

转换时间
转换时间=采样时间+12.5个周期
12.5个周期是固定的,一般我们设置 PCLK2=72M,经过 ADC 预分频器能分频到最大的时钟只能是 12M,采样周期设置为 1.5 个周期,算出最短的转换时间为 1.17us

6.数据寄存器
转换完成的数据放在数据寄存器里,转换完成的数据也分规则通道转换数据和注入通道转换数据。

规则数据寄存器
在这里插入图片描述
当使用ADC独立模式(也就是只使用一个ADC,可以使用多个通道)时,数据存放在低16位中,
当使用ADC多模式时高16位存放ADC2的数据。需要注意的是ADC转换的精度是12位,而寄存器中有16个位来存放数据,所以要规定数据存放是左对齐还是右对齐。

这里注意:ADC经常与DMA一起使用,是因为当使用ADC多通道时,会有多组转换数据,但数据寄存器只有一个,会导致数据相互覆盖,传输错误。所以一般要开启DMA模式将转换的数据,传输在一个数组中,程序对数组读操作就可以得到转换的结果。

注入数据寄存器
在这里插入图片描述
注入通道寄存器四个,注入数据通道最多四个,因此不会产生数据覆盖问题。数据存放也分左对齐和右对齐。

7.中断
三个中断情况:
规则通道转换完成中断
规则通道数据转换完成之后,可以产生一个中断,可以在中断函数中读取规则数据寄存器的值。这也是单通道时读取数据的一种方法。
注入通道转换完成中断
注入通道数据转换完成之后,可以产生一个中断,并且也可以在中断中读取注入数据寄存器的值。
模拟看门狗事件
当输入的模拟量(电压)不再阈值范围内就会产生看门狗事件,就是用来监视输入的模拟量是否正常。
在这里插入图片描述
具体哪种情况由ADC_SR寄存器配置决定。

8.电压转换

y = 3.3*x/4096

x:十二位二进制数
y:真实电压
因为十二位二进制数对应2的十二次方是4096,而最大电压是3.3V,对应相除即可得y

PS:这里提一下为什么要分规则数据组和注入数据组,官方是这么举例的:

假如你在家里的院子内放了 5 个温度探头,室内放了 3 个温度探头;你需要时刻监视室外温度即可,但偶尔你想看看室内的温度;因此你可以使用规则通道组循环扫描室外的 5 个探头并显示 AD转换结果,
当你想看室内温度时,通过一个按钮启动注入转换组(3个室内探头)并暂时显示室内温度,
当你放开这个按钮后,系统又会回到规则通道组继续检测室外温度。从系统设计上,测量并显示室内温度的过程中断了测量并显示室外温度的过程,
但程序设计上可以在初始化阶段分别设置好不同的转换组,系统运行中不必再变更循环转换的配置,从而达到两个任务互不干扰和快速切换的结果。可以设想一下,如果没有规则组和注入组的划分,当你按下按钮后,需要从新配置 AD 循环扫描的通道,然后在释放按钮后需再次配置 AD 循环扫描的通道。

这么理解来说,就是应用领域中检测和检测探头需要较快处理,这样对AD转换的分组可以简化事件的处理程序,提高事件处理速度,更方便快捷。

ADC模式

STM32F的ADC的各通道可以单次,连续,扫描或者间断模式执行。这里手册里都有较详细的讲解,就不具体说了。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

ADC常用函数

常用库函数
在这里插入图片描述

ADC例程

主要是正点原子的例程:

在这里插入图片描述
ADC初始化结构体

typedef struct __ADC_HandleTypeDef
{

  ADC_TypeDef                   *Instance;              
  ADC_InitTypeDef               Init;                  
  DMA_HandleTypeDef             *DMA_Handle;           
  HAL_LockTypeDef               Lock;                   
  __IO uint32_t                 State;               
  __IO uint32_t                 ErrorCode;         

}ADC_HandleTypeDef;

ADC初始化

void MY_ADC_Init(void)
{ 
	RCC_PeriphCLKInitTypeDef ADC_CLKInit;
	
	ADC_CLKInit.PeriphClockSelection=RCC_PERIPHCLK_ADC;			//ADC外设时钟
	ADC_CLKInit.AdcClockSelection=RCC_ADCPCLK2_DIV6;			//分频因子6时钟为72M/6=12MHz
	HAL_RCCEx_PeriphCLKConfig(&ADC_CLKInit);					//设置ADC时钟
	
    ADC1_Handler.Instance=ADC1;
    ADC1_Handler.Init.DataAlign=ADC_DATAALIGN_RIGHT;             //右对齐
    ADC1_Handler.Init.ScanConvMode=DISABLE;                      //非扫描模式
    ADC1_Handler.Init.ContinuousConvMode=DISABLE;                //关闭连续转换
    ADC1_Handler.Init.NbrOfConversion=1;                         //1个转换在规则序列中 也就是只转换规则序列1 
    ADC1_Handler.Init.DiscontinuousConvMode=DISABLE;             //禁止不连续采样模式
    ADC1_Handler.Init.NbrOfDiscConversion=0;                     //不连续采样通道数为0
    ADC1_Handler.Init.ExternalTrigConv=ADC_SOFTWARE_START;       //软件触发
    HAL_ADC_Init(&ADC1_Handler);                                 //初始化 
	
	HAL_ADCEx_Calibration_Start(&ADC1_Handler);					 //校准ADC
}

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "usmart.h"
#include "adc.h"

int main(void)
{ 
	u16 adcx;
	float temp;
	
    HAL_Init();                    	 	//初始化HAL库    
    Stm32_Clock_Init(RCC_PLL_MUL9);   	//设置时钟,72M
	delay_init(72);               		//初始化延时函数
	uart_init(115200);					//初始化串口
	usmart_dev.init(84); 		  	  	//初始化USMART	
	LED_Init();							//初始化LED	
 	LCD_Init();							//初始化LCD
	MY_ADC_Init();                  	//初始化ADC1通道1
	
	POINT_COLOR=RED; 
	LCD_ShowString(30,50,200,16,16,"Mini STM32");	
	LCD_ShowString(30,70,200,16,16,"ADC TEST");	
	LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(30,110,200,16,16,"2019/11/15");	  
	POINT_COLOR=BLUE;//设置字体为蓝色
	LCD_ShowString(30,130,200,16,16,"ADC1_CH1_VAL:");	      
	LCD_ShowString(30,150,200,16,16,"ADC1_CH1_VOL:0.000V");	//先在固定位置显示小数点  	
    while(1)
	{
        adcx=Get_Adc_Average(ADC_CHANNEL_1,20);//获取通道1的转换值,20次取平均
		LCD_ShowxNum(134,130,adcx,4,16,0);    //显示ADCC采样后的原始值
		temp=(float)adcx*(3.3/4096);          //获取计算后的带小数的实际电压值,比如3.1111
		adcx=temp;                            //赋值整数部分给adcx变量,因为adcx为u16整形
		LCD_ShowxNum(134,150,adcx,1,16,0);    //显示电压值的整数部分,3.1111的话,这里就是显示3
		temp-=adcx;                           //把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111
		temp*=1000;                           //小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。
		LCD_ShowxNum(150,150,temp,3,16,0X80); //显示小数部分(前面转换为了整形显示),这里显示的就是111.
		LED0=!LED0;
		delay_ms(250);	
	}
}

这里用的是ADC1的通道1(PA1)进行单次转化,注意硬件连接将PA1连到3.3V(拔掉跳线帽)

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐