【stm32】 ADC

本文围绕STM32 ADC展开,介绍了ADC基本概念,它能将模拟电压转换为数字变量。阐述了逐次逼近型ADC原理,以ADC0809为例说明输入通道选择和AD转换过程。还详细讲解了STM32 ADC的框图、规则组与注入组、转换模式等结构,最后给出了ADC配置实例及相关函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

STM32ADC简介:

• ADC(Analog-Digital Converter)模拟-数字转换器
• ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。[^1]

• ADC(模拟-数字转换器)是模拟到数字的桥梁。
• DAC(数字 — 模拟转换器):可以将数字变量转换为模拟电压,适用在波形生成等领域。
• PWM:PWM也可以将数字变量转换为模拟电压,在某些情况下可以代替DAC,但同时PWM只有完全导通和完全断开两种状态,在两种状态下没有功率损耗,所以直流电机调速等大功率场合下,适用PWM。

• 12位逐次逼近型ADC,1us转换时间。

• 分辨率:一般用多少位来表示,12位AD值,它的表示范围就是0一2^12-1,量化结果的范围是0一4095。位数越高,量化结果越精细,对应分辨率越高

• 输入电压范围:0一3.3V,转换结果范围:0一4095。

ADC的输入电压,一般是要求在芯片供电的正负极之间变化的

• 18个输入通道,可测量16个外部和2个内部信号源

• 16个外部信号源:16个GPIO口,在引脚上直接接模拟信号,不需要任何外部电路,引脚直接测量电压。
• 2个内部信号源:内部温度传感器和内部参考电压

• 内部温度传感器:温度传感器可以测量CPU的温度。
• 内部参考电压: 内部参考电压是一个1.2v的基准电压,这个基准电压是不随外部供电电压变化而变化的。所以>>如果你的芯片供电电压不是标准的3.3v,那外部电压的测量的电压可能不准。这时就可以读取这个基准电压进>>行校准,就可以得到正确的电压值。

• 规则组和注入组两个转换单元
• 模拟看门狗自动监测输入电压范围

• 模拟看门狗可以检测指定的某些通道,当AD值高于它设定的上阈值或低于下阈值时,它就会申请中断,就可以直接在中断函数里执行操作,无需手动取值,进行判断。

• STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道

逐次逼近型ADC原理(以ADC0809为例):

请添加图片描述

输入通道选择:

• IN0~IN7:八路选择通道,通过通道选择开关,输入到AD转换部分进行转换。
• 地址锁存和译码:把通道号放在这ADDA~ADDC三个引脚上,再锁存,就可以打开对应通道。

ADC转换是一个很快的过程,给一个开始信号,几us就可以转换完成。所以,如果想转换多个信号,不必设计多个AD转换器,只要设计一个AD转换器加一个多路选择开关就行了。

STM32有18个输入通道,对应到这里,就是一个18路输入的多路开关。

AD转换:

• 比较器:它可以判断两个输入电压的大小关系,输出高低电平指示谁大谁小。它的两个输入端,一个是待测的电压,另一个是DAC的电压输出端。
• DAC: 给它一个数据,它就可以输出数据对应的电压。DAC内部是使用加权电阻网络实现的转换,详见(待续)。
• 逐次逼近寄存器SAR:负责电压调节(也就是DAC的输入)。

逐次逼近转换过程:

请添加图片描述


为了最快找到未知电压的编码,通常我们会使用二分法来寻找:比如这里是8位为的ADC,那编码就是0~255,第一次比较的时候,我们给DAC输入255的一半,进行比较,那就是128,然后再看谁大谁下,如果DAC电压大了,第二次比较的时候,就再给128的一半64,如果还大,第三次比较的时候,给32,如果这次DAC电压小了,那四次就给64-32中间的值,这样依次继续下去,就可以得到电压的编码。把这个过程用二进制表示,就可以发现,这些数是二进制数的每一位的位权,这个判断过程,相当于是对二进制数从高位到低位依次判断是一还是零的过程。
• 对于8位的ADC,从高位到低位依次判断八次就可以找到未知电压的编码了,对于12位的ADC,就需要判断12次。

输出:

• 八位三态锁存缓冲器: 在AD转换结束后,通过这里输出,八位有八个输出,十二位有十二个输出。

其他:

• EOC:转换结束信号。
• START:开始转换,给一个输入脉冲,开始转换。
• CLOCK: ADC时钟,因为ADC内部是一位一位判断的,需要时钟的驱动。
• VREF+和VREF-: DAC和ADC参考电压,决定DAC最大值的对应电压,DAC的参考电压也决定了ADC的输入范围,所以它也是ADC的参考电压。
• VCC和GND:整个芯片的供电,一般VCC和VREF+接在一起,GND和VREF-接在一起。所以一般情况下,ADC输入电压的范围和ADC的供电是一样的。

STM32ADC:

框图:

在这里插入图片描述

• VREF+在内部已接VDDA,VREF-在内部已接VSSA

ADCCLK来源:在这里插入图片描述

规则组与注入组:

• 有16个多路通道。可以把转换组织成两组:规则组和注入组。在任意多个通道上以任意顺序进
行的一系列转换构成成组转换。例如,可以如下顺序完成转换:通道3、通道8、通道2、通道
2、通道0、通道2、通道2、通道15。
● 规则组由多达16个转换组成。规则通道和它们的转换顺序在ADC_SQRx寄存器中选择。规
则组中转换的总数应写入ADC_SQR1寄存器的L[3:0]位中。
● 注入组由多达4个转换组成。注入通道和它们的转换顺序在ADC_JSQR寄存器中选择。注入
组里的转换总数目应写入ADC_JSQR寄存器的L[1:0]位中。
如果ADC_SQRx或ADC_JSQR寄存器在转换期间被更改,当前的转换被清除,一个新的启动脉
冲将发送到ADC以转换新选择的组

规则组:

最多可以选中16个通道,通道的转换结果会被覆盖,在一个组转换完成后,只有序列中最后一个通道的转换完成可被读取,所以通常与DMA搭配使用。
示意图:
请添加图片描述

注入组:

最多可以选中4个通道,通道的转换结果会存放在对应的寄存器里,在一个组转换完成后,每一个通道的转换结果都可以被读取。
示意图:请添加图片描述

模拟看门狗:

里面可以存一个阈值高限和一个阈值低限,如果启动了模拟看门狗,那看门狗就会看住它看门的通道,一旦超过了阈值范围,它就会乱叫,申请一个模拟看门狗中断。

如果被ADC转换的模拟电压低于低阀值或高于高阀值,AWD模拟看门狗状态位被设置。阀值位
于ADC_HTR和ADC_LTR寄存器的最低12个有效位中。通过设置ADC_CR1寄存器的AWDIE位
以允许产生相应中断。
阀值独立于由ADC_CR2寄存器上的ALIGN位选择的数据对齐模式。比较是在对齐之前完成的

请添加图片描述

ADC基本结构:

在这里插入图片描述

• 输入通道: 16个GPIO口外加两个内部通道。
• AD转换器:里面有两个组,一个规则组,一个注入组,规则组最多可以选中16个通道,注入组最多可以选中4个通道。
• AD数据寄存器:AD转换器转换后的结果存放在这里,其中规则组只有一个寄存器,注入组有四个。
• 触发控制:这个提供了START信号,可以选择软件触发和硬件触发。
• RCC: ADC逐次比较就是由这个时钟驱动的。
• 模拟看门狗:用于检测转换结果的范围。

输入通道:

在这里插入图片描述

• c8t6的外部通道只有前十个。(详见这里
• 只有ADC1有通道16和17。

双ADC(待续):

转换模式:

扫描模式:

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

• 扫描模式主要用于简化对多个模拟通道的连续采样操作。在扫描模式下,ADC能够按照预设的通道顺序自动进行一系列的转换,而无需在每次转换后重新配置通道。

示意图(以规则组为例,注入组同理):
请添加图片描述

间断模式模式(待续):
单次转换模式:

单次转换模式下,ADC只执行一次转换。该模式既可通过设置ADC_CR2寄存器的ADON位(只
适用于规则通道)启动也可通过外部触发启动(适用于规则通道或注入通道),这时CONT位为0。
一旦选择通道的转换完成:
● 如果一个规则通道被转换:
─ 转换数据被储存在16位ADC_DR寄存器中
─ EOC(转换结束)标志被设置
─ 如果设置了EOCIE,则产生中断。
● 如果一个注入通道被转换:
─ 转换数据被储存在16位的ADC_DRJ1寄存器中
─ JEOC(注入转换结束)标志被设置
─ 如果设置了JEOCIE位,则产生中断。然后ADC停止。

• 单次转换模式是一种专用于执行单次ADC转换操作的工作模式,尤其适用于只需从特定模拟通道获取单次数据值的应用场景。
示意图(以规则组为例):
请添加图片描述

连续转换模式:

在连续转换模式中,当前面ADC转换一结束马上就启动另一次转换。此模式可通过外部触发启
动或通过设置ADC_CR2寄存器上的ADON位启动,此时CONT位是1。
每个转换后:
● 如果一个规则通道被转换:
─ 转换数据被储存在16位的ADC_DR寄存器中
─ EOC(转换结束)标志被设置
─ 如果设置了EOCIE,则产生中断。
● 如果一个注入通道被转换:
─ 转换数据被储存在16位的ADC_DRJ1寄存器中
─ JEOC(注入转换结束)标志被设置
─ 如果设置了JEOCIE位,则产生中断。

• 连续转换模式允许ADC在无需外部触发的情况下连续不断地进行数据转换。
请添加图片描述

实例

以上的模式可以两两组合使用(扫描模式+连续转换或单次转换模式)下面是一些实例。
在这里插入图片描述

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

触发控制:

在这里插入图片描述

数据对齐:

在这里插入图片描述

转换时间:

在这里插入图片描述

校准:

在这里插入图片描述

ADC配置实例:

ADC配置基本步骤:

第一步:开启GPIO和ADC的RCC时钟并将ADCCLK分配器配置一下。
第二步:配置GPIO,将需要用到的GPIO配置为模拟输入模式。
第三步:配置多路开关,将通道接入到规则组列表里。
第四步:配置ADC转换器。
第五步:开启ADC。

ADC时钟配置函数:

/**
  * @brief  配置ADC时钟(ADCCLK)
  * @param  RCC_PCLK2: 配置ADC的时钟预分频器。 被分频的时钟是APB2时钟(PCLK2)。
  *   该参数可以是下列值之一:
  *     @arg RCC_PCLK2_Div2: ADC时钟 = PCLK2/2
  *     @arg RCC_PCLK2_Div4: ADC时钟 = PCLK2/4
  *     @arg RCC_PCLK2_Div6: ADC时钟= PCLK2/6
  *     @arg RCC_PCLK2_Div8: ADC时钟 = PCLK2/8
  * @retval None
  */
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2)

ADC规则组通道配置函数:

/**
  * @brief  设置指定 ADC 的规则组通道,设置它们的转化顺序和采样时间
  * @param  ADCx: x 可以是 1 或者 2 来选择 ADC 外设 ADC1 或 ADC2
  * @param  ADC_Channel: 被设置的 ADC 通道
  *   这个参数可以是下列值之一:
  *     @arg ADC_Channel_0: 选择 ADC 通道 0
  *     @arg ADC_Channel_1: 选择 ADC 通道 1
  *     @arg ADC_Channel_2: 选择 ADC 通道 2
  *     @arg ADC_Channel_3: 选择 ADC 通道 3 
  *     @arg ADC_Channel_4: 选择 ADC 通道 4 
  *     @arg ADC_Channel_5: 选择 ADC 通道 5
  *     @arg ADC_Channel_6: 选择 ADC 通道 6
  *     @arg ADC_Channel_7: 选择 ADC 通道 7
  *     @arg ADC_Channel_8: 选择 ADC 通道 8
  *     @arg ADC_Channel_9: 选择 ADC 通道 9
  *     @arg ADC_Channel_10: 选择 ADC 通道 10
  *     @arg ADC_Channel_11: 选择 ADC 通道 11
  *     @arg ADC_Channel_12: 选择 ADC 通道 12
  *     @arg ADC_Channel_13: 选择 ADC 通道 13
  *     @arg ADC_Channel_14: 选择 ADC 通道 14
  *     @arg ADC_Channel_15: 选择 ADC 通道 15
  *     @arg ADC_Channel_16: 选择 ADC 通道 16
  *     @arg ADC_Channel_17: ADC 选择 ADC 通道
  * @param  Rank: 规则组采样顺序。取值范围 1 到 16。
  * @param  ADC_SampleTime: 指定 ADC 通道的采样时间值(时间越长越稳定)
  *   这个参数可以是下列值之一:
  *     @arg ADC_SampleTime_1Cycles5: 采样时间为 1.5 周期
  *     @arg ADC_SampleTime_7Cycles5: 采样时间为 7.5 周期
  *     @arg ADC_SampleTime_13Cycles5: 采样时间为 13.5 周期
  *     @arg ADC_SampleTime_28Cycles5: 采样时间为 28.5 周期	
  *     @arg ADC_SampleTime_41Cycles5: 采样时间为 41.5 周期	
  *     @arg ADC_SampleTime_55Cycles5: 采样时间为 55.5 周期	
  *     @arg ADC_SampleTime_71Cycles5: 采样时间为 71.5 周期	
  *     @arg ADC_SampleTime_239Cycles5: 采样时间为 239.5 周期	
  * @retval None
  */
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)

ADC初始化函数:

/**
  * @brief  根据 ADC_InitStruct 中指定的参数初始化外设 ADCx 的寄存器。
  * @param  ADCx: x 可以是 1 或者 2 来选择 ADC 外设 ADC1 或 ADC2。
  * @param  ADC_InitStruct: 指向结构 ADC_InitTypeDef 的指针,包含了指定外设 ADC 的配置信息
  * @retval None
  */
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct)

ADC初始化结构体:

typedef struct
{
  uint32_t ADC_Mode;//设置 ADC 工作在独立或者双 ADC 模式。
  FunctionalState ADC_ScanConvMode;//规定了模数转换工作在扫描模式(多通道)还是单次(单通道)模式。可以设置这个参数为 ENABLE 或者 DISABLE。
  FunctionalState ADC_ContinuousConvMode; //规定了模数转换工作在连续还是单次模式。可以设置这个参数为 ENABLE或者 DISABLE
  uint32_t ADC_ExternalTrigConv;//定义了使用外部触发来启动规则通道的模数转换
  uint32_t ADC_DataAlign;//规定了 ADC 数据向左边对齐还是向右边对齐               
  uint8_t ADC_NbrOfChannel;//规定了顺序进行规则转换的 ADC 通道的数目。           
}ADC_InitTypeDef;

复位校准函数

/**
  * @brief  重置指定的 ADC 的校准寄存器
  * @param  ADCx: x 可以是 1 或者 2 来选择 ADC 外设 ADC1 或 ADC2。
  * @retval None
  */
void ADC_ResetCalibration(ADC_TypeDef* ADCx)
/**
  * @brief  获取 ADC 重置校准寄存器的状态
  * @param  ADCx: x 可以是 1 或者 2 来选择 ADC 外设 ADC1 或 ADC2。
  * @retval ADC 重置校准寄存器的新状态(SET 或者 RESET)。
  */
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx)

校准函数

/**
  * @brief  开始指定 ADC 的校准
  * @param  ADCx: x 可以是 1 或者 2 来选择 ADC 外设 ADC1 或 ADC2
  * @retval None
  */
void ADC_StartCalibration(ADC_TypeDef* ADCx)
/**
  * @brief  获取指定 ADC 的校准状态
  * @param  ADCx: x 可以是 1 或者 2 来选择 ADC 外设 ADC1 或 ADC2
  * @retval ADC 校准的新状态(SET 或者 RESET)
  */
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx)

转换函数

/**
  * @brief  使能或者失能指定的 ADC 的软件转换启动功能
  * @param  ADCx:x 可以是 1 或者 2 来选择 ADC 外设 ADC1 或 ADC2
  * @param  NewState: 指定 ADC 的软件转换启动新状态这个参数可以取:ENABLE 或者 DISABLE
  * @retval None
  */
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState)

标志位读取:

/**
  * @brief  检查制定 ADC 标志位置 1 与否
  * @param  ADCx: x 可以是 1 或者 2 来选择 ADC 外设 ADC1 或 ADC2
  * @param  ADC_FLAG: 指定需检查的标志位 
  *   This parameter can be one of the following values:
  *     @arg ADC_FLAG_AWD: 模拟看门狗标志位
  *     @arg ADC_FLAG_EOC: 转换结束标志位
  *     @arg ADC_FLAG_JEOC: 注入组转换结束标志位
  *     @arg ADC_FLAG_JSTRT: 注入组转换开始标志位
  *     @arg ADC_FLAG_STRT: 规则组转换开始标志位
  * @retval 指定ADC标志位的最新状态 (SET 或 RESET).
  */
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG)

转换结果读取

/**
  * @brief  返回常规通道的最后一次ADCx转换结果数据。
  * @param  ADCx: 其中x可以是1、2或3来选择ADC外设。
  * @retval 转换结果。
  */
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx)
实例:
void AD_Init()
{
开启GPIO和ADC的RCC时钟并将ADCCLK分配器配置一下。//
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6); //72M/6=12M
///配置GPIO,将需要用到的GPIO配置为模拟输入模式。//	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AIN;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
///配置多路开关,将通道接入到规则组列表里。//		
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);//将通道1放在规则组列表第一个位置里,采样时间为55.5个周期。
配置ADC转换器。///	
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//让ADC1工作在独立模式
	ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//ADC 数据右对齐
	ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//由软件触发启动
	ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//让ADC工作在单次模式
	ADC_InitStructure.ADC_ScanConvMode=DISABLE;//不使用扫描模式
	ADC_InitStructure.ADC_NbrOfChannel=1;//在非扫描模式下无效
	ADC_Init(ADC1,&ADC_InitStructure);//使用参数初始化
/开启ADC/
	ADC_Cmd(ADC1,ENABLE);
/校准
	ADC_ResetCalibration(ADC1);//开始ADC1复位校准
	while(ADC_GetResetCalibrationStatus(ADC1) == SET){}//确定ADC1复位校准是否结束
	ADC_StartCalibration(ADC1);//开始ADC1校准
	while(ADC_GetCalibrationStatus(ADC1) == SET){}//确定ADC1校准是否结束
}
/读取
uint16_t AD_GetValue()
{
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//开始转换
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)== RESET){}//确定转换是否结束
	return ADC_GetConversionValue(ADC1);//读取转换结果
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值