校准STM32 ADC(VREFINT)

校准STM32 ADC(VREFINT),stm32,adc,stm32f0,Stm32,Adc,Stm32f0,我试图在STM32F042微控制器上读取VDDA。我在3.29V的VDD上得到了意想不到的结果。我一定错过了一些基本的东西 输出: adc_测试c: #包括 #包括“stm32f0xx.h” #定义VREFINT_CAL_ADDR 0x1FFF7BA/*数据表p。19 */ #定义VREFINT\u CAL((uint16\u t*)VREFINT\u CAL\u ADDR) 外部无效初始化\u监视器\u句柄(无效); 内部主(空) { RCC->APB2ENR |=RCC_APB2ENR_AD

我试图在STM32F042微控制器上读取VDDA。我在3.29V的VDD上得到了意想不到的结果。我一定错过了一些基本的东西

输出: adc_测试c:
#包括
#包括“stm32f0xx.h”
#定义VREFINT_CAL_ADDR 0x1FFF7BA/*数据表p。19 */
#定义VREFINT\u CAL((uint16\u t*)VREFINT\u CAL\u ADDR)
外部无效初始化\u监视器\u句柄(无效);
内部主(空)
{
RCC->APB2ENR |=RCC_APB2ENR_ADC1EN;/*启用ADC外围时钟*/
RCC->CR2 |=RCC_CR2_HSI14ON;/*启动ADC HSI*/
而(!(RCC->CR2和RCC_CR2_HSI14RDY));/*等待完成*/
/*校准*/
ADC1->CR |=ADC_CR_ADCAL;/*开始ADC校准*/
而(ADC1->CR&ADC_CR_ADCAL);/*等待完成*/
ADC1->CR |=ADC_CR_ADEN;/*ADC启用*/
而(!(ADC1->ISR&ADC_ISR_ADRDY));/*等待完成*/
ADC1->SMPR |=ADC_SMPR1_SMPR_0 |/*采样模式:最长*/
ADC_SMPR1_SMPR_1|
ADC_SMPR1_SMPR_2;
/*VDD参考*/
ADC->CCR |=ADC_CCR_VREFEN;/*VREF启用*/
ADC1->CHSELR=ADC\U CHSELR\U CHSEL17;/*CH17=VREFINT*/
初始化_monitor_handles();/*启用半托管*/
而(1){
ADC1->CR |=ADC_CR_ADSTART;/*开始ADC转换*/
同时(!(ADC1->ISR&ADC_ISR_EOC));/*等待完成*/
uint32\u t vdda=3300UL**VREFINT\u-CAL/ADC1->DR;/*参考手册第252页;恒定值,以毫伏为单位*/
printf(“VREFINT=%lu;VREFINT\u CAL=%lu;VDDA=%lu mV\n”,
(无符号长)ADC1->DR,
(未签名长)*V初始值,
(无符号长)vdda);
}
}
数据表中的屏幕截图:

参考手册截图 注:这指的是.3V,但我认为这是一个输入错误,因为上面的数据表和下面较长的公式指的是3.3V,并且.3V低于该部件的最低工作电压

我目前正在为STM32L4开发一个ADC驱动程序。在实现过程中,我遇到了几乎相同的问题。在我看来,第一个公式

不是计算VDDA,而是计算VREF+。它是ADC评估ADC-IN通道时所依据的电压。 此外,VREFINT_数据不是测量的VREF+电压,而是取决于控制器的内部参考电压。在我的情况下,它在控制器数据表中定义:

以下是我如何使用张贴公式的图片:

一些评论: ln 102:计算VREF+非VDDA

ln 105-110:计算所有列组/配置序列

ln 108:计算ADCpin_x测量的电压

LN109:乘以增益得到实际值


在我看来,通过计算每个转换序列的VREF+,我会得到更好的结果,因为VREF+上的一些涟漪会得到补偿。

实际上它是在计算Vdda,因为VREF计算非常简单,您必须读取ADC的相应通道,采样时间比数据表中标记的要长(通常为10 us)。如果Vdda为2.0 V,则4095的值对应于2.0 V(或更高)的绝对值(相关GND)。以线性方式,Vref的值将远高于Vdda=3.30 V时读取的值。因此,值的补偿​​必须使用2.0 V的电压读数才能知道绝对值​​ADC正在测量的电压。如果未对其进行补偿,则它们将为数值​​相对于Vdda当时的电压水平。
此外,还实现了电源值,这将有助于不超出微控制器的规格。

正如@Artur所说,Vref+不是Vdda,但通常(我在硬件设计中就是这样)Vref+连接到Vdda(根据数据表使用相应的过滤器),因此计算Vdda与计算Vref+相同

我将向您展示如何计算vdda,因为我基于STM32L431

。必须首先配置ADC以测量VREFINT:

void MX_ADC1_Init(void)
{

    /* USER CODE BEGIN ADC1_Init 0 */

    /* USER CODE END ADC1_Init 0 */

    ADC_ChannelConfTypeDef sConfig = {0};

    /* USER CODE BEGIN ADC1_Init 1 */

    /* USER CODE END ADC1_Init 1 */
    /** Common config
     */
    hadc1.Instance = ADC1;
    hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
    hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
    hadc1.Init.LowPowerAutoWait = DISABLE;
    hadc1.Init.ContinuousConvMode = DISABLE;
    hadc1.Init.NbrOfConversion = 1;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    hadc1.Init.DMAContinuousRequests = DISABLE;
    hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
    hadc1.Init.OversamplingMode = DISABLE;
    if (HAL_ADC_Init(&hadc1) != HAL_OK)
    {
        Error_Handler();
    }
    /** Configure Regular Channel
     */
    sConfig.Channel = ADC_CHANNEL_VREFINT;
    sConfig.Rank = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5;
    sConfig.SingleDiff = ADC_SINGLE_ENDED;
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        Error_Handler();
    }
    /* USER CODE BEGIN ADC1_Init 2 */

    /* USER CODE END ADC1_Init 2 */
}
.现在我将向您展示执行方程式的代码:

vdda = 3.0 * (VREFINT_CAL /average);
vch = VREF * (average / ADC_RESOLUTION);

log("vdd = %.5f - ", vdda);
log("vchn = %.5f", vch);
其中:

#define ADC_RESOLUTION 4095.0           // adc resolution 12 bits
#define VREFINT_CAL 1655.00             // Raw data acquired at a temperature of 30 °C (± 5 °C), VDDA = VREF+ = 3.0 V (± 10 mV)
#define VREF 3.3                        // voltage reference 3.3V
注:

“average”是adc采集的256个样本的平均值(它只是一个简单的过滤器)

“log”是我创建的一个函数,类似于uart的printf

“VREFINT_CAL”因型号而异

结果:

vdd = 3.28035 - vchn = 1.21343
如我们所见,VREFINT与数据表(1.212V典型值)匹配:


我看不出您的代码有任何明显的错误,我可以确认参考手册中的
.3
而不是
3.3
确实是一个打字错误(我在网上找到的副本没有这个错误)。关于这个问题,我有一个粗略的猜测——您是否让Vssa引脚浮动,而不是接地?(假设您使用的STM32F042变型实际上有一个单独的Vssa引脚。)计算Vdda时的错误可疑地接近一个二极管压降,如果负参考电压浮动,这似乎是一个合理的结果。这是一个有趣的想法,但引脚(引脚32)连接到GND:有趣的是,热敏垫没有连接到任何东西。该示意图非常错误-它显示了该零件的UFQFNP32变体的零件号,但根据LQFP32变体(甚至没有热敏垫)标记了引脚.在UFQFPN32上,引脚16和32是额外的端口B I/O引脚,热焊盘是您唯一的接地连接,是正确操作所必需的。基本上,您的芯片只能通过一些I/O引脚上的ESD保护二极管看到接地,2.62V是芯片接收到的电源的准确测量值。Owwww…您你完全正确。之前版本的原理图要求使用LQFP32,然后改为UFQFNP32,我猜硬件人员没有仔细阅读数据表。这看起来很糟糕……我很惊讶芯片能正常工作,而且非常好
vdd = 3.28035 - vchn = 1.21343