1
ADC外设是12位的逐次逼近型(SAR)模拟数字转换器,可以将模拟信号转换成数字信号。
ADC有16 个通道可测量内部或外部信号源,其中ADC有14 路外部输入通道和2路内部通道,如下图1 ADC的系统框图所示分别是通道14内部温度温度传感器通道,通道15内部电压传感器通道,即内部1.2V参考电压通道。
ADC的通道可以单次、单周期和连续进行转换。根据不同的方式又可以选择普通通道转换、任意通道转换、注入通道转换。ADC 的输入时钟不得超过16M,他是由 APB2 时钟(PCLK2) 分频产生。
图1 ADC系统框图
2
MM32F0160内部1.2V参考电压简介
MM32系列MCU出厂时,在Flash特定的内存地址存储了1.2V参考电压的校准值,即VDDA为3V3时采样1.2V电压的校准值。该校准值可以通过UM手册查阅。例如,可通过UM_MM32F0160_SC手册的ADC章节查阅到MM32F0163D7P的ADC内部1.2V参考电压的校准值存储在Flash特定的内存地址0x1FFFF7E0区域。
根据本文章节1图1的ADC系统框图可知ADC内部1.2V参考电压通道为通道15。MM32其它系列的MCU的ADC内部1.2V参考电压校准值在Flash内存的存储地址,内部1.2V参考电压通道的通道号如下表1所示(注:其它MCU系列请参考UM手册)。
表1 MM32系列ADC内部1.2V参考电压校准值的存储地址和1.2V参考电压通道号
3
ADC内部1.2V参考电压的应用场景举例
ADC的参考电压为VDD,当MCU的供电VDD不稳定或采用电池供电时ADC的参考电压会随着VDD的波动而波动,随着电池电量和电压的下降而变化,从而影响ADC的采集和测量精度,这种场景就可以考虑使用MCU出厂时VDD为3.3V时ADC采样1.2V电压得到的采样校准值,读取该采样校准值并转换成电压值,作为ADC的间接参考电压使用。
4
ADC内部1.2V参考电压采样值的读取
以MM32F0163D7P为例,查阅UM_MM32F0160_SC手册ADC章节的ADC系统框图可知ADC内部1.2V参考电压通道为通道15(VSENSOR),在读取ADC内部1.2V参考电压采样值前需配置ADC通道15并使能该通道,使用任意通道配置核心代码如下所示,本文后面8.3章节有详细的应用举例。
?
/*?Sampling?value?of?each?channel?of?ADC?*/ uint16_t?ADC_Channel_Samp_Value[4]?=?{0x00}; /*?Assign?ADC1?1.2V?Vref?channel_15?to?RANK?3?*/??? ADC_ANY_CH_Config(ADC1,3,ADC_Channel_VoltReference); /*?Enable?ADC?1.2V?channel_15?voltage?reference?*/ ADC1->ADCFG?|=??ADC_ADCFG_VSEN; /*?Get?the?sampling?value?of?ADC?1.2V?channel?15?*/???? ADC_Channel_Samp_Value[3]?=?ADC1->ADDR15;
?
5
ADC内部1.2V参考电压校准值的读取
以MM32F0163D7P为例,读取出厂VDDA为3V3时采样1.2V电压的采样校准值,并换算成电压值,核心代码如下所示。本文8.4章节有详细的读取举例。
?
/*?1.2V?sampling?calibration?value?storage?address?*/ #define?ADC_1_2V_SAMPLE_CALI_VALUE_MEM_ADDR?0x1FFFF7E0 /*?Read?ADC?1.2V?Sample?value?*/ Vref_1_2V_Cali?=?*(uint16_t*)(ADC_1_2V_SAMPLE_CALI_VALUE_MEM_ADDR); /*?Convert?to?voltage?value?*/ Vref_1_2V_Cali_V?=?(float)(Vref_1_2V_Cali?*?(3.3/4096));
?
6
ADC各通道采样电压值
与1.2V通道采样电压值换算关系
定义Calcu_ADC_Channel_V为缓存ADC各通道采样电压值,gADC_Sample_Value为缓存ADC各通道的采样值。ADC_Channel_Samp_Value[4]为缓存ADC各通道的采样值包括ADC的1.2V通道实时采样值,Vref_1_2V_Cali为VDDA为3V3时采样1.2V电压的采样校准值,Vref_1_2V_Cali由以上章节5 ADC内部1.2V参考电压校准值的读取得到,换算成电压值为Vref_1_2V_Cali_V = (float)(Vref_1_2V_Cali * (3.3/4096)) ,则存在以下关系式:
Calcu_ADC_Channel_V / gADC_Sample_Value = Vref_1_2V_Cali_V /ADC_Channel_Samp_Value[i]
其中i表示ADC各通道号包括ADC的1.2V通道。
由此,推得ADC各个通道的采样电压值为
Calcu_ADC_Channel_V = (float)(gADC_Sample_Value * (Vref_1_2V_Cali_V/ADC_Channel_Samp_Value[3]))
ADC_Channel_Samp_Value[3]缓存ADC的1.2V通道的实时采样值。即:ADC各通道的采样电压值=ADC各通道的采样值*(ADC内部采样1.2V电压的采样校准值换算得到的电压值 / ADC的1.2V通道的实时采样值)。
7
根据出厂ADC内部1.2V参考电压的校准值
反推VDDA的电压值
由于Vref_1_2V_Cali / ADC_V1_2V_Channel_Sample = VDDA / 4096
得到VDDA = ( Vref_1_2V_Cali * 4096 ) / ADC_V1_2_Channel_Sample
注意事项:读取ADC_V1_2_Channel_Sample 采样值前需配置和使能1.2V通道(MM32F0163D7P的ADC 1.2V通道为通道15)。注:本文章节4有提到配置方法。
8
ADC使用注意事项
1
ADC高达1Msps转换速率,每个通道可以独立的设置采样保持时间,但需注意的是采样保持时间最小为2.5个ADC时钟周期,最大为240.5个ADC时钟周期,在应用场景条件允许情况下,在此范围内适当加大采样保持时间可以保证ADC采样精度。条件允许情况下适当降低ADC时钟频率也可以保证采样精度,但ADC的时钟不得超过16MHz。
2
ADC的内部1.2V Vref通道转换周期比规则转换通道的转换周期稍大一些,如果对ADC的采样速率有应用场景的要求,建议使用ADC的注入通道功能。
3
ADC的采样值受到输入阻抗的影响,关于ADC输入阻抗的计算可参考对应MCU系列的DS手册,电气特性,工作条件的ADC特性。
4
使用ADC时应注意用作ADC功能的GPIO的IO类型,例如IO类型为“TC”类型即标准的IO类型,不容忍VDD电压即ADC的输入采样电压不得超过VDD电源电压。此外GPIO内部带有上下拉钳位二极管,用户应避免输入ADC的采样电压过高引起倒灌影响ADC的工作条件和采样精度(关于IO类型的说明请参考对应MCU系列的DS手册管脚定义章节)。
9
ADC内部1.2V参考电压的使用完整举例
1
以MM32F0163D7P为例,查阅DS手册得到GPIO与ADC通道对应关系如下表2所示。
表2 GPIO与ADC通道对应关系
2
以MM32F0163D7P为例,完整举例ADC内部1.2V参考电压的使用,本示例外接采样3路ADC电压值,首先初始化3路ADC通道,ADC输入的GPIO的分别为PA0对应ADC_Channel_0,PA1对应ADC_Channel_1,PA2对应ADC_Channel_2,ADC的GPIO初始化代码如下所示。
?
void?ADC_GPIO_Config(void) { ????GPIO_InitTypeDef?GPIO_InitStruct; ????/*?Enable?GPIOA?Clock?*/ ????RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA,?ENABLE); ????GPIO_StructInit(&GPIO_InitStruct); ????/*?PA0?ADC_CH0,?PA1?ADC_CH1,?PA2?ADC_CH2?*/ ????GPIO_InitStruct.GPIO_Pin???=?GPIO_Pin_0?|?GPIO_Pin_1?|?GPIO_Pin_2; ????GPIO_InitStruct.GPIO_Speed?=?GPIO_Speed_50MHz;? ????GPIO_InitStruct.GPIO_Mode??=?GPIO_Mode_AIN;???? ????GPIO_Init(GPIOA,?&GPIO_InitStruct); }
?
3
以MM32F0163D7P为例,初始化ADC为12bit精度,时钟为72M预分频为16分频,连续转换模式,数据格式右对齐,采样保持时间为240.5个ADC时钟周期,配置任意通道模式,配置并使能ADC内部1.2V通道Channel15,ADC的初始化代码如下所示。
?
void?ADC_Configure(void) { ????ADC_InitTypeDef?ADC_InitStruct; ????RCC_APB2PeriphClockCmd(RCC_APB2ENR_ADC,?ENABLE);????/*?Enable?ADC?clock?*/ ????ADC_StructInit(&ADC_InitStruct); ????ADC_InitStruct.ADC_Resolution?=?ADC_Resolution_12b; ????ADC_InitStruct.ADC_PRESCARE???=?ADC_PCLK2_PRESCARE_16; ????/*?ADC?continue?scan?convert?mode?*/ ????ADC_InitStruct.ADC_Mode?=?ADC_Mode_Continue;??? /*?AD?data?right-justified?*/??????? ????ADC_InitStruct.ADC_DataAlign?=?ADC_DataAlign_Right;? ????ADC_Init(ADC1,?&ADC_InitStruct); ????/*?Configure?ADC?ADC_Channel_x?Sample?time?*/ ????ADC_Channel_Sample_time_Configure(ADC1,ADC_Channel_0,ADC_Samctl_240_5);? ????ADC_Channel_Sample_time_Configure(ADC1,ADC_Channel_1,ADC_Samctl_240_5); ????ADC_Channel_Sample_time_Configure(ADC1,ADC_Channel_2,ADC_Samctl_240_5); ????ADC_ANY_Cmd(ADC1,?DISABLE);?/*?Disable?ADC?ANYChannel?*/??? ????ADC_ANY_NUM_Config(ADC1,3);?/*?Configure?Multi-Channel?num?*/? ????/*?Assign?PA0?ADC1?channel_0?to?RANK?0?*/????? ????ADC_ANY_CH_Config(ADC1,0,ADC_Channel_0);? ????/*?Assign?PA1?ADC1?channel_1?to?RANK?1?*/? ????ADC_ANY_CH_Config(ADC1,1,ADC_Channel_1);? ????/*?Assign?PA2?ADC1?channel_2?to?RANK?2?*/?? ????ADC_ANY_CH_Config(ADC1,2,ADC_Channel_2);? ????/*?Assign?ADC1?1.2V?Vref?channel_15?to?RANK?3?*/??? ????ADC_ANY_CH_Config(ADC1,3,ADC_Channel_VoltReference);? ????ADC_ANY_Cmd(ADC1,?ENABLE);???/*?Enable?ADC?ANYChannel?*/??? ????/*?Enable?ADC?1.2V?channel_15?voltage?reference?*/ ????ADC1->ADCFG?|=??ADC_ADCFG_VSEN;? ????ADC_Cmd(ADC1,?ENABLE);?/*?Enable?ADC?*/ }????
?
4
以MM32F0163D7P为例,读取出厂VDDA为3V3时采样1.2V电压的采样校准值,并换算成电压值,代码如下所示。
?
#define?VDDA????????3.3F #define?ADC_12BIT???4096U #define?ADC_RESOLUTION?VDDA?/?ADC_12BIT /*?1.2V?sampling?calibration?value?storage?address?*/ #define?ADC_1_2V_SAMPLE_CALI_VALUE_MEM_ADDR?0x1FFFF7E0 /*?1.2V?sampling?calibration?value?*/ uint16_t?Vref_1_2V_Cali??=?0; /*?1.2V?sampling?calibration?value?Convert?to?voltage?value?*/ float?Vref_1_2V_Cali_V?=?0.0; void?Read_Factory_1_2V_Sample_CaliValue(void) { ????/*?Read?ADC?1.2V?Sample?value?*/ ????Vref_1_2V_Cali?=?*(uint16_t*)(ADC_1_2V_SAMPLE_CALI_VALUE_MEM_ADDR); ????/*?Convert?to?voltage?value?*/ ????Vref_1_2V_Cali_V?=?(float)(Vref_1_2V_Cali?*?ADC_RESOLUTION); }
?
5
以MM32F0163D7P为例,由以上9.4章节读取出厂VDDA为3V3时采样1.2V电压的采样校准值,由以上9.3章节ADC内部1.2V参考电压通道的配置和使能,ADC的1.2V采样电压的校准值根据以上章节6表述的公式,结合ADC的1.2V通道15的实时采样值可用于反推VDDA的电压值,同时该采样校准值换算成电压值后可间接作为ADC多通道连续采样的参考电压。
ADC内部1.2V参考电压实时采样值,ADC各通道采样值的读取并换算成电压值以及在main函数初始化UART1打印功能,ADC外设的初始化,读取ADC采样1.2V的校准值,在while(1)主循环中调用获取ADC各通道采样电压值函数的详细实现的代码如下所示。本示例通过UART1每隔200ms毫秒打印输出ADC各通道采样并换算得到电压值到串口调试助手。
?
/*?Buffer?the?VDDA?voltage?value?of?the?reversed?ADC?*/ float?ADC_VDDA?=?0.0; /*?Sampling?value?of?each?channel?of?ADC?*/ uint16_t?ADC_Channel_Samp_Value[4]?=?{0x00}; /*?Buffer?the?sampling?value?of?each?channel?of?the?ADC?*/ uint16_t?gADC_Sample_Value?=?0; /*?Buffer?the?voltage?value?of?each?channel?of?ADC?*/ float?Calcu_ADC_Channel_V?=?0.0; void?Calcu_ADC_Each_Channel_Voltage_Value(void) { ????uint8_t?i?=?0; ????/*?Software?starts?the?ADC?conversion?*/ ????ADC_SoftwareStartConvCmd(ADC1,?ENABLE);? ????/*?ADC?conversion?flag?*/ ????while(ADC_GetFlagStatus(ADC1,?ADC_IT_EOC)?==?0); ????/*?Clears?the?adc's?pending?flags?*/ ????ADC_ClearFlag(ADC1,?ADC_IT_EOC); ????/*?Get?the?sampling?value?of?ADC?PA0?channel?0?*/ ????ADC_Channel_Samp_Value[0]?=?ADC1->ADDR0; ????/*?Get?the?sampling?value?of?ADC?PA1?channel?1?*/ ????ADC_Channel_Samp_Value[1]?=?ADC1->ADDR1; ????/*?Get?the?sampling?value?of?ADC?PA2?channel?2?*/ ????ADC_Channel_Samp_Value[2]?=?ADC1->ADDR2;??? ????/*?Get?the?sampling?value?of?ADC?1.2V?channel?15?*/???? ????ADC_Channel_Samp_Value[3]?=?ADC1->ADDR15;??????????????? ????/*?The?factory?1.2V?sampling?calibration?value?and?the?1.2V?channel?sampling?value?reverse?the?VDDA?voltage?value?*/ ????ADC_VDDA?=?(Vref_1_2V_Cali_V?*4096)?/?ADC_Channel_Samp_Value[3]; ????printf("ADC_VDDA?=?%0.2f ",ADC_VDDA); ????/*?Print?out?the?voltage?value?of?each?channel?calculated?by?ADC?*/ ????for(i?=?0;?i?4;?i++) ????{ ????????/*?Obtain?the?sampling?value?of?each?channel?of?the?ADC?*/ ????????gADC_Sample_Value?=?ADC_Channel_Samp_Value[i];?????? ????????/*?Calculate?the?voltage?value?of?each?channel?according?to?the?sampling?value?of?each?channel?of?the?ADC?*/ ????????Calcu_ADC_Channel_V?=?(float)(gADC_Sample_Value?*?(Vref_1_2V_Cali_V/ADC_Channel_Samp_Value[3])); ????????if(i?==?3) ????????{????????? ????????????printf("ADC1_CH15_VREF:?%0.2fV ",Calcu_ADC_Channel_V);? ????????} ????????else ????????{ ????????????printf("ADC1_CH%d:?%0.2fV ",i,Calcu_ADC_Channel_V);?? ????????}??????????????? ????}? }
?
?
int?main(void) { ????/*?SysTick?Init?*/ ????DELAY_Init(); ????/*?UART1?Init?*/ ????CONSOLE_Init(115200); ????/*?ADC?Pin?Configure?*/ ????ADC_GPIO_Configure(); ????/*?ADC?function?Configure?*/ ????ADC_Configure(); ????/*?Read?factory?1.2V?sample?calibration?value?and?convert?to?voltage?value?*/ ????Read_Factory_1_2V_Sample_CaliValue(); ????while?(1) ????{ ????????if(Tick_200ms_Flag?==?true) ????????{ ????????????Tick_200ms_Flag?=?false; ????????????/*?Get?and?Calculate?the?voltage?value?of?each?channel?of?the?ADC?*/ ????????????Calcu_ADC_Each_Channel_Voltage_Value(); ????????} ????} }
?
6
编译程序把烧录程序到核心板,如下图2所示,3路ADC分别采集外接的电压值,ADC各通道采样换算得到的电压值打印输出到串口调试助手,测试结果分别为ADC_VDDA该值是通过读取出厂VDDA为3V3时采样1.2V电压的采样校准值结合1.2V通道15的实时采样值反推得到的VDDA(即VDD的电压值)的电压值。ADC_CH0采集1.0V电压,实际采到1.03V,ADC_CH1接GND采集0V电压,实际采集到0V,ADC_CH2采集0.50V电压,实际采集到0.50V。其中ADC_CH15_VREF采集ADC内部1.2V通道实时采样得到的电压值,采集到1.18V,该值可参考以上9.4章节表述的出厂VDDA为3V3时采样1.2V电压的采样校准值换算得到的电压值接近。
图2
7
如下图3所示,换一组电压采集,ADC_CH0采集2.50V电压实际采集到2.51V,ADC_CH1采集3.32V电源电压实际采集到3.31V,ADC_CH2采集1.50V电压,实际采到1.50V电压。以上测试均在ADC允许的误差范围。
图3
审核编辑:刘清
评论