使用STM32的内部温度传感器

/ 2评 / 0

看到正点原子说温度传感器怎么不正确,我觉得可能是方法问题,先看看他的方法.

而手册中的计算方法:

经过查阅,除了F1,其他只要有温度传感器,都符合这条公式.附录中提供了一个公式,如下.

其中VDD_CALIB可以先搞不懂,但是有一个例程叫stsw-stm32072,例程中是这么写的.

void processTempData(void)
{
  uint32_t index,dataSum;
  dataSum = 0;
  /* sort received data in */
  insertionSort(ADC_ConvertedValueBuff, MAX_TEMP_CHNL);
  /* Calculate the Interquartile mean */
  tempAVG = interquartileMean(ADC_ConvertedValueBuff, MAX_TEMP_CHNL);
  /* Sum up all mesured data for reference voltage average calculation */
  for (index = MAX_TEMP_CHNL; index < ADC_CONV_BUFF_SIZE; index++){
    dataSum += ADC_ConvertedValueBuff[index];
  }
  /* Devide sum up result by 4 for the temperature average calculation*/
  refAVG = dataSum / 4 ;
#if VDD_CORRECTION == 1
	/* estimation of VDD=VDDA=Vref+ from ADC measurement of Vrefint reference voltage in mV */
  vdd_ref = calibdata.VREF * 3000 / refAVG;
	/* correction factor if VDD <> 3V */
	tempAVG = tempAVG * vdd_ref / 3000;
#endif
  /* Calculate temperature in C from Interquartile mean */
  temperature_C = ( (int32_t) tempAVG - (int32_t) calibdata.TS_CAL_1 ) ;
  temperature_C = temperature_C * (int32_t)(HOT_CAL_TEMP - COLD_CAL_TEMP);
  temperature_C = temperature_C /
                  (int32_t)(calibdata.TS_CAL_2 - calibdata.TS_CAL_1);
  temperature_C = temperature_C + COLD_CAL_TEMP;
}

tempAVG是一个平均值,关于温度ADC的,这是ADC数值,而不是电压数值.再想一想,拿一个电压减一个AD数值多不合理啊.经过替换,这个其实是如此的.

  /* Calculate temperature in C from Interquartile mean */
  temperature_C = ( (int32_t) tempAVG - (int32_t) DEFAULT_COLD_VAL ) ;
  temperature_C = temperature_C * (int32_t)(HOT_CAL_TEMP - COLD_CAL_TEMP);
  temperature_C = temperature_C /
                  (int32_t)(DEFAULT_HOT_VAL - DEFAULT_COLD_VAL);
  temperature_C = temperature_C + COLD_CAL_TEMP;

但是DEFAULT_COLD_VAL和DEFAULT_HOT_VAL,都是在VDDA=3.3V下测试的.而目标板电压一般达不到标准3.3V的.

所以更准确方法是先求出目前VDDA,还好,带隙基准的AD数值也测量了一下,也是3.3V下的,那么,我们就可以列一个公式求了.

公式是这样的.

我VERFINT_CAL数值是1542,而VERFINT_DATA是1850,所以,大概2.75V,但是我要这个中间变量没用的.我们目标是把低数值变高数值.如何把低变高呢?
通过等比例缩放公式推导如下.
ADC_SCALE_DR = ADC_DR / (3.3V / (3.3 V * VREFINT_CAL / VREFINT_DATA)) 
有点冗余,先不化简了.反正调整过的ADC数值有了,不知道是否还记得这段代码.

#define TEMP110_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7C2))
#define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7B8))
#define VDD_CALIB ((uint16_t) (330))
#define VDD_APPLI ((uint16_t) (300))
int32_t temperature; /* will contain the temperature in degree Celsius */
temperature = (((int32_t) ADC1->DR * VDD_APPLI / VDD_CALIB)
- (int32_t) *TEMP30_CAL_ADDR );
temperature = temperature * (int32_t)(110 - 30);
temperature = temperature / (int32_t)(*TEMP110_CAL_ADDR
- *TEMP30_CAL_ADDR);
temperature = temperature + 30;

只要愿意,他可以写的非常长,非常难看.

temperature = (((((int32_t) ADC_SCALE_DR ) - (int32_t) *TEMP30_CAL_ADDR ) * (int32_t)(110 - 30)) / (int32_t)(*TEMP110_CAL_ADDR - *TEMP30_CAL_ADDR)) + 30;

如果还要展开,其实是这样.

#define TEMP110_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7C2))
#define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7B8))
#define VREFINT_CAL_ADDR ((uint16_t*) ((uint32_t) 0X1FFFF7BA))
int32_t temperature; /* 计算出来的温度 */
uint16_t vref_value = 0; /* 校准通道下的ADC测量数值 */
/* ADC-DR 是温度通道下的采样值 */
temperature = (((((int32_t) (ADC->DR / (3.3V / (3.3 V * VREFINT_CAL_ADDR / vref_value))) ) - (int32_t) *TEMP30_CAL_ADDR ) * (int32_t)(110 - 30)) / (int32_t)(*TEMP110_CAL_ADDR - *TEMP30_CAL_ADDR)) + 30;

其实你应该已经看得一脸懵逼了.当然,有这么多括号的,肯定是不合理的. 看看下面有效化简.

	#define TEMP110_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7C2))
	#define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7B8))
	#define VREFINT_CAL_ADDR ((uint16_t*) ((uint32_t) 0X1FFFF7BA))
	temperature = (ADC->DR * (float)*VREFINT_CAL_ADDR / VREFINT_DATA-  *TEMP30_CAL_ADDR ) * 70 / (*TEMP110_CAL_ADDR - *TEMP30_CAL_ADDR) + 30;

实际上我测试到的温度传感器AD数值是2132,而校准数值是1850,通过模拟计算为31度,合理.

总结正点原子不合理的地方:

  1. VDDA不等于3.3V
  2. 斜率并不是恒定的,也化简不出这条公式,除了部分论坛,无官方资料标明可以这么计算.虽然有说多少mV一度,但是那个也是范围,还不如直接通过校准数值测试真实斜率.
  3. 内部温度传感和外部差距并不大,如果发热这么大,芯片内部几乎没什么可以散热的地方的.
  1. 小汉说道:

    *70 不是该*80吗

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注