STM32 ADC模块详解及应用指南
stm32ADC:
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);//读取转换结果
}
作者:冯雪娟_00