STM32ADC实验讲解,从入门到放弃。


前言

本文参考了网上的博文,并加以归纳总结,帮助新手从入门到放弃


提示:以下是本篇文章正文内容

一、ADC


什么是ADC?

ADC:Analog-to-Digital Converter的缩写。指模/数转换器或者模拟/数字转换器是指将连续变量的模拟信号转换为离散的数字信号的器件。典型的模拟数字转换器将模拟信号转换为表示一定比例电压值的数字信号。

12 位 ADC 是逐次趋近型模数转换器。它具有多达 19 个复用通道,可测量来自 16 个外部源、两个内部源和 V BAT 通道的信号。这些通道的 A/D 转换可在单次、连续、扫描或不连续采样模式下进行。ADC 的结果存储在一个左对齐右对齐的 16 位数据寄存器中。ADC 具有模拟看门狗特性,允许应用检测输入电压是否超过了用户自定义的阈值上限或下限。

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

ADC主要特性有那些?

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

ADC输入范围:VREF- ≤ VIN ≤ VREF+。
在这里插入图片描述

ADC的功能说明

1.电压输入
我们先看一下下面这张图:
在这里插入图片描述
ADC所能测量的电压范围就是VREF- ≤ VIN ≤ VREF+,把 VSSA 和 VREF-接地,把 VREF+和 VDDA 接 3V3,得到ADC 的输入电压范围为: 0~3.3V。
2.输入通道
从上面截取下来的部分:
在这里插入图片描述ADC的信号输入就是通过通道来实现的,信号通过通道输入到单片机中,单片机经过转换后,将模拟信号输出为数字信号

扫描二维码关注公众号,回复: 13752847 查看本文章

STM32F103ZET6的ADC通道和引脚对应关系图:
在这里插入图片描述
可以看出,STM32F103ZET6带3个ADC控制器,一共支持23个通道,包括21个外部和2个内部信号源;但是每个ADC控制器最多只可以有18个通道,包括16个外部和2个内部信号源。

关于通道,在转换时我们又分为规则通道注入通道
在这里插入图片描述
规则通道: 平时的ADC转换都是用规则通道实现,最多可以安排16个通道,如果有列外处理的话就可以启用注入通道的转换。

注入通道: 注入通道可以在规则通道转换时,强行插入转换,相当于一个“中断通道”。

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

关于通道的选择
有 16 条复用通道。可以将转换分为两组:规则转换和注入转换。每个组包含一个转换序列,该序列可按任意顺序在任意通道上完成。例如,可按以下顺序对序列进行转换:ADC_IN3、ADC_IN8、ADC_IN2、ADC_IN2、ADC_IN0、ADC_IN2、ADC_IN2、ADC_IN15。

一个 规则转换组最多由 16 个转换构成。必须在 ADC_SQRx 寄存器中选择转换序列的规则通道及其顺序。规则转换组中的转换总数必须写入 ADC_SQR1 寄存器中的 L[3:0] 位。

一个 注入转换组最多由 4 个转换构成。必须在 ADC_JSQR 寄存器中选择转换序列的注入通道及其顺序。注入转换组中的转换总数必须写入 ADC_JSQR 寄存器中的 L[1:0] 位

如果在转换期间修改 ADC_SQRx 或 ADC_JSQR 寄存器,将复位当前转换并向 ADC 发送一个新的启动脉冲,以转换新选择的组。

温度传感器、V REFINT 和 V BAT 内部通道

对于 STM32F40x 和 STM32F41x 器件,温度传感器内部连接到通道 ADC1_IN16。内部参考电压 VREFINT 连接到
ADC1_IN17。 对于 STM23F42x 和 STM32F43x 器件,温度传感器内部连接到与 VBAT
共用的通道ADC1_IN18。一次只能选择一个转换(温度传感器或 VBAT)。同时设置了温度传感器和 VBAT 转换时,将只进行 VBAT转换。

内部参考电压 VREFINT 连接到 ADC1_IN17。
V BAT 通道连接到通道 ADC1_IN18。该通道也可转换为注入通道或规则通道。
注意: 温度传感器、 V REFINT 和 V BAT 通道只在主 ADC1 外设上可用。

转换模式
规则通道中的转换顺序由三个寄存器控制:SQR1、 SQR2、 SQR3
在这里插入图片描述
注入通道转换顺序

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

ADC单次转换模式

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

1.将ADC_CR2寄存器中的SWSTART位置1(仅适合于规则通道)
2.将JSWSTART位置1(适合于注入通道)
3.外部触发(适用于规则通道或注入通道)

一旦所选择的通道转换完成
如果转换了规则通道

— 转换数据存储在 16 位 ADC_DR 寄存器中
— EOC(转换结束)标志置 1
— EOCIE 位置 1 时将产生中断

如果转换了注入通道

— 转换数据存储在 16 位 ADC_JDR1 寄存器中
— JEOC(注入转换结束)标志置 1
— JEOCIE 位置 1 时将产生中断

然后,ADC 停止。

ADC连续转换模式
在连续转换模式下,ADC结束一个转换后立即启动一个新的转换,CONT位为1时,可通过外部触发或将ADC_CR2寄存器中的SWSTART位置1来启动此模式(仅适用于规则通道)
每次转换之后:
● 如果转换了规则通道组:
— 上次转换的数据存储在 16 位 ADC_DR 寄存器中
— EOC(转换结束)标志置 1
— EOCIE 位置 1 时将产生中断
无法连续转换注入通道。连续模式唯一的例外情况是,注入通道配置为在规则通道之后自动转换(使用JAUTO位)

ADC扫描模式(适用于扫描一组模拟通道)

通过将 ADC_CR1 寄存器中的 SCAN 位置 1 来选择扫描模式。将此位置 1 后,ADC 会扫描在 ADC_SQRx 寄存器(对于规则通道)或 ADC_JSQR 寄存器(对于注入通道)中选择的所有通道。为组中的每个通道都执行一次转换。每次转换结束后,会自动转换该组中的下一个通道。如果将 CONT 位置 1,规则通道转换不会在组中最后一个所选通道处停止,而是再次从第一个所选通道继续转换。

如果将 DMA 位置 1,则在每次规则通道转换之后,均使用直接存储器访问 (DMA) 控制器将转换自规则通道组的数据(存储在 ADC_DR 寄存器中)传输到 SRAM。

在以下情况下,ADC_SR 寄存器中的 EOC 位置 1:
● 如果 EOCS 位清零,在每个规则组序列转换结束时
● 如果 EOCS 位置 1,在每个规则通道转换结束时
从注入通道转换的数据始终存储在 ADC_JDRx 寄存器中。

ADC的 中断
当模拟看门狗状态位和溢出状态位分别置 1 时,规则组和注入组在转换结束时可能会产生中 断。可以使用单独的中断使能位以实现灵活性。
ADC_SR 寄存器中存在另外两个标志,但这两个标志不存在中断相关性:
● JSTRT(开始转换注入组的通道)
● STRT(开始转换规则组的通道)
在这里插入图片描述

在这里插入图片描述
ADC的转换时间

转换时间=采样时间+12.5个周期

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

ADC的每一次信号转换都要时间,这个时间就是转换时间。转换时间由输入时钟和采样周期来决定。ADC在STM32中是挂载在APB2总线上的

ADC得时钟是由PCLK2(72MHz)经过分频得到的,分频因子由 RCC 时钟配置寄存器RCC_CFGR 的位 15:14ADCPRE[1:0]设置,可以是 2/4/6/8分频,一般配置分频因子为6,即6分频得到ADC的输入时钟频率为12MHz。(一般不超过14MHz)

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

ADC的数据对齐
ADC_CR2 寄存器中的 ALIGN 位用于选择转换后存储的数据的对齐方式。可选择左对齐和右对齐两种方式。

需要注意的是ADC转换的精度是12位,而寄存器中有16个位来存放数据,所以要规定数据存放是左对齐还是右对齐。

在这里插入图片描述
ADC的时序图

在这里插入图片描述

二、实验步骤

代码部分讲解


需要的步骤有:

1. 开启 PA 口时钟和 ADC1 时钟,设置 PA1 为模拟输入。
2. 复位 ADC1,同时设置 ADC1 分频因子。
3. 初始化 ADC1 参数, 设置 ADC1 的工作模式以及规则序列的相关信息。
4. 使能 ADC 并校准。
5. 读取 ADC 值。

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

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); 
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.1

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

RCC_ADCCLKConfig(RCC_PCLK2_Div6);//设置 ADC1 分频因子为6
ADC_DeInit(ADC1);//复位 ADC1

3. 初始化 ADC1 参数, 设置 ADC1 的工作模式

void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct)/*下面是结构体里的内容
typedef struct
{
uint32_t ADC_Mode;
FunctionalState ADC_ScanConvMode;
FunctionalState ADC_ContinuousConvMode;
uint32_t ADC_ExternalTrigConv;
uint32_t ADC_DataAlign;
uint8_t ADC_NbrOfChannel;
}ADC_InitTypeDef;
*/
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC 工作模式:独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //AD 单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //AD 单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
//转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC 数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的 ADC 通道的数目 1
ADC_Init(ADC1, &ADC_InitStructure); //根据指定的参数初始化外设 ADCx

4. 使能 ADC 并校准

ADC_Cmd(ADC1, ENABLE); //使能指定的 ADC1
ADC_ResetCalibration(ADC1);//复位校准
ADC_StartCalibration(ADC1); //开始指定 ADC1 的校准状态
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
while(ADC_GetCalibrationStatus(ADC1)); //等待校 AD 准结束

5.读取 ADC 值。

void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel,
uint8_t Rank, uint8_t ADC_SampleTime)ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能指定的 ADC1 的软件转换启动功能
ADC_GetConversionValue(ADC1);//获取转换 ADC 转换结果数据
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG)//获取 AD 转换的状态信息的函数
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束


这里还需要说明一下 ADC 的参考电压,战舰 STM32 开发板使用的是 STM32F103ZET6,该芯片有外部参考电压: Vref-和 Vref+,其中 Vref-必须和 VSSA 连接在一起, 而 Vref+的输入范围为:2.4~VDDA。战舰 STM23 开发板通过 P7 端口,设置 Vref-和 Vref+设置参考电压,默认的我们是通过跳线帽将 Vref-接到 GND, Vref+接到 VDDA,参考电压就是 3.3V。如果大家想自己设置其他参考电压,将你的参考电压接在 Vref-和 Vref+上就 OK 了。本章我们的参考电压设置的是 3.3V。

代码示例

adc.c:

//初始化 ADC
//这里我们仅以规则通道为例
//我们默认将开启通道 0~3
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); //初始化 GPIOA.1
	ADC_DeInit(ADC1); //复位 ADC1,将外设 ADC1 的全部寄存器重设为缺省值
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC 独立模式
	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); //根据指定的参数初始化外设 ADCx
	ADC_Cmd(ADC1, ENABLE); //使能指定的 ADC1
	ADC_ResetCalibration(ADC1); //开启复位校准
	while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
	ADC_StartCalibration(ADC1); //开启 AD 校准
	while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
}

//获得 ADC 值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)
{
    
    
	//设置指定 ADC 的规则组通道,设置它们的转化顺序和采样时间
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );
	//通道 1,规则采样顺序值为 1,采样时间为 239.5 周期
	ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能软件转换功能
	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;
}

main.c:

int main(void)
{
    
    
	u16 adcx;
	float temp;
	delay_init(); //延时函数初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置 NVIC 中断分组 2
	uart_init(115200); //串口初始化波特率为 115200
	LED_Init(); //LED 端口初始化
	LCD_Init(); //LCD 初始化
	Adc_Init(); //ADC 初始化
	POINT_COLOR=RED; //设置字体为红色
	LCD_ShowString(60,50,200,16,16,"WarShip STM32");
	LCD_ShowString(60,70,200,16,16,"ADC TEST");
	LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(30,110,200,16,16,"2015/1/14");
	//显示提示信息
	POINT_COLOR=BLUE; //设置字体为蓝色
	LCD_ShowString(60,130,200,16,16,"ADC_CH0_VAL:");
	LCD_ShowString(60,150,200,16,16,"ADC_CH0_VOL:0.000V");
	while(1)
	{
    
    
		adcx=Get_Adc_Average(ADC_Channel_1,10);
		LCD_ShowxNum(156,130,adcx,4,16,0);//显示 ADC 的值
		temp=(float)adcx*(3.3/4096);
		adcx=temp;
		LCD_ShowxNum(156,150,adcx,1,16,0);//显示电压值
		temp-=adcx;
		temp*=1000;
		LCD_ShowxNum(172,150,temp,3,16,0X80);
		LED0=!LED0;
		delay_ms(250);
	}
}

总结

今天的文章就讲到这里,觉得写的还行的小伙伴,点赞加关注。如果还是看不懂的话,点击链接,小破站视频链接直达

猜你喜欢

转载自blog.csdn.net/qq_42866708/article/details/113448658