尝试用MSPM0L1306芯片内部温度传感器测量温度,并和外置的热敏电阻读数对比。
内部温度传感器
阅读MSPL01306 datasheet,发现内部温度传感器有一个出厂标定的温度,为30摄氏度,温度系数TSc为-1.75e-3。
阅读MSPM0L系列Technical Reference Manual,里面详细介绍了adc码值转换为温度的公式,基本思路是器件温度和输出的电压是线性关系,温度系数是负值,因此温度越高,电压越低。
另外还需要一个参考温度点,来标定关系曲线的截距。
这个标定的温度点对应的ADC码值,就是TEMP_SENSE0。
TEMP_SENSE0存储在每个器件的内部。获取TEMP_SENSE0常量,可以查阅driverlib文档的Factory Region (FACTORYREGION)部分,找到对应的函数:
一行搞定:
volatile const uint32_t TEMP_SENSE0 = DL_FactoryRegion_getTemperatureVoltage();
我手上的板子,TEMP_SENSE0的值为377。
注意:此处似乎有坑,请看下文。
SysConfig配置
ADC12
2个通道分别采样外部热敏电阻电压和内部温度传感器电压。
注意Reference不一样,外部是3.3V左右的VDDA,内部用1.4V的VREF。
VREF设置为内部1.4V
软件部分
内部温度传感器的换算,根据前文找到的计算方法,编写代码如下:
float internal_temperature_sensor_calc(uint16_t adc_code) {
/*参考MSPM0L系列Technical Reference Manual 2.2.5节*/
float TSc = -1.75e-3; // MSPM0L1306 datasheet 7.13节
float TStrim = 30; // MSPM0L1306 datasheet 7.13节
float Vref = 1.4;
float Vsample = (Vref / 4096) * (adc_code - 0.5);
float Vtrim = (Vref / 4096) * (TEMP_SENSE0 - 0.5);
float Tsample = (1 / TSc) * (Vsample - Vtrim) + TStrim;
return Tsample;
}
其中:
TEMP_SENSE0 = DL_FactoryRegion_getTemperatureVoltage();
完整的代码见文末。
发现问题
目前外部热敏电阻的读数正常。
内部温度传感器的值有明显的问题。
室温24度左右,adc code是1896左右,而对应出厂校准的只读存储器里:30度校准值是377。
根据这一校准值得到的室温,与真实值相差甚远。
排除问题的一些尝试
1、更换ADC的参考电压,发现adc code随参考电压等比例变化,说明adc参考电压设置1.4V是正确的。
2、将手指放在芯片上加热芯片,可以看到adc code会减小,符合传感器的负温度系数特性,说明内部温度传感器应该是在工作的。
3、可能是校准值有误,需要重新校准。实际上TI在datasheet里提到了“通过用户校准可以实现更高的绝对精度”,因此手动校准是可行的。
手动校准
由于手头没有恒温箱,就用环境温度做一个简单的校准。
这里通过求解上述方程,得到30度校准值对应的adc码值“理论值”。使用python编写方程求解程序。
得到TEMP_SENSE0的“理论值”为1867。
修正后的代码,输出温度就基本正确了:
同时加热外置热敏电阻和MSPM0L1306,发现二者测量值都同步升高,截图是升到30度左右的结果。说明内置传感器是好的,只是需要手动校准。
思考
仔细看了LaunchPad上的MSPM0L1306的丝印,没有找到工程样品(ES)的痕迹,应该是正式版,不知道为何温度的出厂校准相差甚远?
完整代码
SysConfig参考代码
/**
* These arguments were used when this file was generated. They will be automatically applied on subsequent loads
* via the GUI or CLI. Run CLI with '--help' for additional information on how to override these arguments.
* @cliArgs --device "MSPM0L130X" --package "VQFN-32(RHB)" --part "Default" --product "mspm0_sdk@1.20.00.06"
* [url=home.php?mod=space&uid=304333]@versions[/url] {"tool":"1.18.0+3266"}
*/
/**
* Import the modules used in this configuration.
*/
const ADC12 = scripting.addModule("/ti/driverlib/ADC12", {}, false);
const ADC121 = ADC12.addInstance();
const Board = scripting.addModule("/ti/driverlib/Board");
const SYSCTL = scripting.addModule("/ti/driverlib/SYSCTL");
const VREF = scripting.addModule("/ti/driverlib/VREF");
/**
* Write custom configuration values to the imported modules.
*/
ADC121.$name = "ADC12_0";
ADC121.sampClkSrc = "DL_ADC12_CLOCK_ULPCLK";
ADC121.sampClkDiv = "DL_ADC12_CLOCK_DIVIDE_8";
ADC121.powerDownMode = "DL_ADC12_POWER_DOWN_MODE_MANUAL";
ADC121.samplingOperationMode = "sequence";
ADC121.adcMem2chansel = "DL_ADC12_INPUT_CHAN_7";
ADC121.adcMem3chansel = "DL_ADC12_INPUT_CHAN_3";
ADC121.sampleTime0 = "40 us";
ADC121.adcMem0chansel = "DL_ADC12_INPUT_CHAN_9";
ADC121.adcMem1chansel = "DL_ADC12_INPUT_CHAN_11";
ADC121.endAdd = 1;
ADC121.enabledInterrupts = ["DL_ADC12_INTERRUPT_MEM0_RESULT_LOADED","DL_ADC12_INTERRUPT_MEM1_RESULT_LOADED"];
ADC121.adcMem1vref = "VREF";
ADC121.peripheral.$assign = "ADC0";
ADC121.adcPin9Config.$name = "ti_driverlib_gpio_GPIOPinGeneric1";
VREF.checkVREFReady = true;
/**
* Pinmux solution for unlocked pins/peripherals. This ensures that minor changes to the automatic solver in a future
* version of the tool will not impact the pinmux you originally saw. These lines can be completely deleted in order to
* re-solve from scratch.
*/
ADC121.peripheral.adcPin9.$suggestSolution = "PA15";
Board.peripheral.$suggestSolution = "DEBUGSS";
Board.peripheral.swclkPin.$suggestSolution = "PA20";
Board.peripheral.swdioPin.$suggestSolution = "PA19";
SYSCTL.peripheral.$suggestSolution = "SYSCTL";
VREF.peripheral.$suggestSolution = "VREF";
主程序代码
#include "ti_msp_dl_config.h"
#include <math.h>
#include <stdio.h>
volatile bool gCheckADC;
volatile uint32_t TEMP_SENSE0; // 内部温度传感器校准值
volatile uint16_t adc_thermistor;
volatile uint16_t adc_internal_temp_sensor;
volatile float t_thermistor;
volatile float t_internal_temp_sensor;
/*热电阻温度换算*/
float VBias = 3.30; // set the VBIAS voltage
// set the number of bits based on you ADC (2^# of ADC Bit Value)
unsigned int ADC_BITS = 4096;
float VTEMP = 0; // set up the variable for the measured voltage
float THRM_TEMP = 0; // setup the variable for the calculated temperature
float thermistor_calc_temperature(uint16_t raw_ADC) {
// THRM calculations via regression
// Copied from TI Thermistor Design Tool Excel Doc
float VTEMP = 0.0;
float THRM_ADC = raw_ADC;
float THRM_A0 = -4.232811E+02;
float THRM_A1 = 4.728797E+02;
float THRM_A2 = -1.988841E+02;
float THRM_A3 = 4.869521E+01;
float THRM_A4 = -1.158754E+00;
VTEMP = (VBias / ADC_BITS) * THRM_ADC; // calculate volts per bit then
// multiply that times the ADV value
THRM_TEMP = (THRM_A4 * powf(VTEMP, 4)) + (THRM_A3 * powf(VTEMP, 3)) +
(THRM_A2 * powf(VTEMP, 2)) + (THRM_A1 * VTEMP) +
THRM_A0; // 4th order regression to get temperature
return THRM_TEMP;
}
float internal_temperature_sensor_calc(uint16_t adc_code) {
/*参考MSPM0L系列Technical Reference Manual 2.2.5节*/
float TSc = -1.75e-3; // MSPM0L1306 datasheet 7.13节
float TStrim = 30; // MSPM0L1306 datasheet 7.13节
float Vref = 1.4;
float Vsample = (Vref / 4096) * (adc_code - 0.5);
float Vtrim = (Vref / 4096) * (TEMP_SENSE0 - 0.5);
float Tsample = (1 / TSc) * (Vsample - Vtrim) + TStrim;
return Tsample;
}
int main(void) {
/* Initialize peripherals and enable interrupts */
SYSCFG_DL_init();
NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);
TEMP_SENSE0 = DL_FactoryRegion_getTemperatureVoltage();
printf("TEMP_SENSE0=%d\n", TEMP_SENSE0);
TEMP_SENSE0 = 1867; //使用自定义的校准值覆盖出厂校准值
gCheckADC = false;
while (1) {
DL_ADC12_startConversion(ADC12_0_INST);
/* Wait until all data channels have been loaded. */
while (gCheckADC == false) {
__WFE();
}
/* ch0:热电阻adc读数,
ch1:内部温度传感器adc读数 */
adc_thermistor = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_0);
adc_internal_temp_sensor =
DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_1);
gCheckADC = false;
DL_ADC12_enableConversions(ADC12_0_INST);
printf("adc_thermistor=%d, adc_internal_temp_sensor=%d\n", adc_thermistor,
adc_internal_temp_sensor);
t_thermistor = thermistor_calc_temperature(adc_thermistor);
t_internal_temp_sensor =
internal_temperature_sensor_calc(adc_internal_temp_sensor);
printf("t_thermistor=%g, t_internal_temp_sensor=%g\n", t_thermistor, t_internal_temp_sensor);
printf("\n");
}
}
/* Check for the last result to be loaded then change boolean */
void ADC12_0_INST_IRQHandler(void) {
switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) {
case DL_ADC12_IIDX_MEM1_RESULT_LOADED:
gCheckADC = true;
break;
default:
break;
}
}
Python用户校准程序代码
from sympy import *
# 已知环境温度为Tsample,ADC采样值为adc_code,求校准值TEMP_SENSE0
Tsample = 24.6
adc_code = 1895
TSc = -1.75e-3
TStrim = 30
Vref = 1.4
TEMP_SENSE0 = Symbol('TEMP_SENSE0')
Vsample = (Vref / 4096) * (adc_code - 0.5)
Vtrim = (Vref / 4096) * (TEMP_SENSE0 - 0.5)
eq = (1 / TSc) * (Vsample - Vtrim) + TStrim - Tsample
print(solve(eq, TEMP_SENSE0))