看到正点原子说温度传感器怎么不正确,我觉得可能是方法问题,先看看他的方法.
而手册中的计算方法:
经过查阅,除了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度,合理.
总结正点原子不合理的地方:
- VDDA不等于3.3V
- 斜率并不是恒定的,也化简不出这条公式,除了部分论坛,无官方资料标明可以这么计算.虽然有说多少mV一度,但是那个也是范围,还不如直接通过校准数值测试真实斜率.
- 内部温度传感和外部差距并不大,如果发热这么大,芯片内部几乎没什么可以散热的地方的.
*70 不是该*80吗
@小汉 不同型号的公式不太一样,具体看DS.