打印
[其他]

MM32F0163D7P的ADC内部1.2V参考电压的使用

[复制链接]
700|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
1. MM32F0160的ADC简介
ADC有16 个通道可测量内部或外部信号源,其中ADC有14 路外部输入通道和2路内部通道,如下图1 ADC的系统框图所示分别是通道14内部温度温度传感器通道,通道15内部电压传感器通道,即内部1.2V参考电压通道。

图1 ADC系统框图

2. MM32F0160内部1.2V参考电压简介

表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表示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\r\n",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;      
        /* 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\r\n",Calcu_ADC_Channel_V);
        }
        else
        {
            printf("ADC1_CH%d: %0.2fV\r\n",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





使用特权

评论回复
沙发
weifeng90| | 2023-8-6 16:14 | 只看该作者
最好先校准一下

使用特权

评论回复
板凳
guijial511| | 2023-8-7 09:19 | 只看该作者
如果要求精度高,还是用外部参考源。

使用特权

评论回复
地板
wyjie| | 2023-8-15 20:55 | 只看该作者
要注意避免输入ADC的采样电压过高引起倒灌影响ADC的工作条件和采样精度

使用特权

评论回复
5
单片小菜| | 2023-8-16 16:47 | 只看该作者
是不是先校准一下比较好一些,精度会更好一些。

使用特权

评论回复
6
jflahdink09| | 2023-8-17 13:31 | 只看该作者
楼主看的资料很细,佩服,工程师就应该这样的。

使用特权

评论回复
7
udaidfa002| | 2023-8-17 13:39 | 只看该作者
为什么不进行校准呢?校准是不是更好一些呢?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:上海灵动微电子股份有限公司
简介:上海灵动微电子股份有限公司成立于 2011 年,是中国本土通用 32 位 MCU 产品及解决方案供应商。 灵动股份的 MCU 产品以 MM32 为标识,基于 Arm Cortex-M 系列内核,自主研发软硬件和生态系统。目前已量产近 300 多款型号,累计交付超 4 亿颗,在本土通用 32 位 MCU 公司中位居前列。

86

主题

104

帖子

5

粉丝