本帖最后由 Libby@ 于 2024-6-5 11:28 编辑
#技术资源#
32位单片机SAMD21,采用内部温度传感器监测环境温度,通过MCC配置省时省力
SAMD21单片机内置了温度传感器,可以用来监测环境温度。通过配置Timer定时采样,并利用事件系统触发ADC转换,避免占用MCU资源。温度数据通过轮询的方式读取,每次ADC有新数据时,读取12位数据并转换为温度值。需要注意的是,每个芯片都有自己的温度校准值,存储在NVM User Row中,需提前读取这些校准值代入计算公式。以SAM D21 Curiosity Nano评估板(32 位 SAM 系列微控制器 SAMD21G17D)为例,借助事件系统和Timer,触发ADC转换,通过Microchip Code Configurator(MCC)进行快速配置,以最大限度地减少代码编写的工作量。
新建工程,进入MCC配置界面,添加组件 ADC/TC3/SERCOM5/EVSYS(新建项目,可参考我之前的贴子)
(1) 添加ADC模块,设置ADC为单端模式,并配置温度传感器为输入通道,参考电压选择1.0V, 采用硬件触发转换。
(2) 配置Timer,选择合适的定时周期,当溢出时,触发ADC转换
(3) 设置EVSYS,将 Timer 的溢出事件连接到 ADC 。TC3溢出产生事件,ADC为使用者,当设置正确,Event Status和User Ready显示绿色
(4) 配置 USART 进行调试打印,SAM D21 Curiosity Nano板子,SERCOM5作为串口输出,设置波特率及TX和RX。MCC自带工具STDIO,直连到SERCOM5。
(5) 单击“Generate”按钮,MCC可以生成相应的初始化代码,回到Projects可查看初始化的代码。
(6) 添加代码,可查看芯片手册Temperature Sensor的公式,根据得到的ADC值计算得到摄氏度。在代码中添加读取NVM User Row中温度校准值的函数。在主循环中添加轮询代码,当ADC转换完成时,读取数据并根据校准值进行温度计算。
以下是main.c的片段:温度参数及公式可参见数据手册--温度传感器特性,由于参数过多,未贴出。有小伙伴需要完整的计算过程,如下
#define INT1V_VALUE_FLOAT 1.0
#define INT1V_DIVIDER_1000 1000.0
#define ADC_12BIT_FULL_SCALE_VALUE_FLOAT 4095.0
float input_voltage;
static uint16_t Adc_value;
float coarse_temp; /* Coarse value of the temperature - tempC */
float fine_temp; /* Finer value of the temperature - tempF */
float tempR; /* Production Room Temperature value read from NVM memory - tempR */
float tempH; /* Production Hot Temperature value read from NVM memory - tempH */
float INT1VR; /* Room temp 2's complement of the internal 1V reference value - INT1VR */
float INT1VH; /* Hot temp 2's complement of the internal 1V reference value - INT1VR */
float VADCR; /* Room Temperature ADC voltage - VADCR */
float VADCH; /* Hot Temperature ADC voltage - VADCH */
uint16_t ADCR; /* Production Room Temperature ADC Value read from NVM memory - ADCR */
uint16_t ADCH; /* Production Hot Temperature ADC Value read from NVM memory - ADCH */
/* This function helps to convert the Decimal value into fractional value
for decimal values from Temperature Log Row Content */
float convert_dec_to_frac(uint8_t val) {
if (val < 10) {
return ((float) val / 10.0);
} else if (val < 100) {
return ((float) val / 100.0);
} else {
return ((float) val / 1000.0);
}
}
/* This function helps us to read and store the production calibration data to variables for temperature calculation */
void load_temperature_log_row_data(void) {
volatile uint32_t val1; /* Temperature Log Row Content first 32 bits */
volatile uint32_t val2; /* Temperature Log Row Content another 32 bits */
uint8_t room_temp_val_int; /* Integer part of room temperature in °C */
uint8_t room_temp_val_dec; /* Decimal part of room temperature in °C */
uint8_t hot_temp_val_int; /* Integer part of hot temperature in °C */
uint8_t hot_temp_val_dec; /* Decimal part of hot temperature in °C */
int8_t room_int1v_val; /* internal 1V reference drift at room temperature */
int8_t hot_int1v_val; /* internal 1V reference drift at hot temperature*/
uint32_t *temp_log_row_ptr = (uint32_t *) TEMP_LOG_ADDR;
val1 = *temp_log_row_ptr;
temp_log_row_ptr++;
val2 = *temp_log_row_ptr;
room_temp_val_int = (uint8_t) ((val1 & FUSES_TEMP_LOG_WORD_0_ROOM_TEMP_VAL_INT_Msk) >> FUSES_TEMP_LOG_WORD_0_ROOM_TEMP_VAL_INT_Pos);
room_temp_val_dec = (uint8_t) ((val1 & FUSES_TEMP_LOG_WORD_0_ROOM_TEMP_VAL_DEC_Msk) >> FUSES_TEMP_LOG_WORD_0_ROOM_TEMP_VAL_DEC_Pos);
hot_temp_val_int = (uint8_t) ((val1 & FUSES_TEMP_LOG_WORD_0_HOT_TEMP_VAL_INT_Msk) >> FUSES_TEMP_LOG_WORD_0_HOT_TEMP_VAL_INT_Pos);
hot_temp_val_dec = (uint8_t) ((val1 & FUSES_TEMP_LOG_WORD_0_HOT_TEMP_VAL_DEC_Msk) >> FUSES_TEMP_LOG_WORD_0_HOT_TEMP_VAL_DEC_Pos);
room_int1v_val = (int8_t) ((val1 & FUSES_TEMP_LOG_WORD_0_ROOM_INT1V_VAL_Msk) >> FUSES_TEMP_LOG_WORD_0_ROOM_INT1V_VAL_Pos);
hot_int1v_val = (int8_t) ((val2 & FUSES_TEMP_LOG_WORD_1_HOT_INT1V_VAL_Msk) >> FUSES_TEMP_LOG_WORD_1_HOT_INT1V_VAL_Pos);
ADCR = (uint16_t) ((val2 & FUSES_TEMP_LOG_WORD_1_ROOM_ADC_VAL_Msk) >> FUSES_TEMP_LOG_WORD_1_ROOM_ADC_VAL_Pos);
ADCH = (uint16_t) ((val2 & FUSES_TEMP_LOG_WORD_1_HOT_ADC_VAL_Msk) >> FUSES_TEMP_LOG_WORD_1_HOT_ADC_VAL_Pos);
tempR = room_temp_val_int + convert_dec_to_frac(room_temp_val_dec);
tempH = hot_temp_val_int + convert_dec_to_frac(hot_temp_val_dec);
INT1VR = 1 - ((float) room_int1v_val / INT1V_DIVIDER_1000);
INT1VH = 1 - ((float) hot_int1v_val / INT1V_DIVIDER_1000);
VADCR = ((float) ADCR * INT1VR) / ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
VADCH = ((float) ADCH * INT1VH) / ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
}
/* This function return the Fine temperature value after done with calculation using Equation1 and Equation 1b as mentioned in data sheet on 37.11.8 Temperature Sensor Characteristics */
float calculate_temperature(uint16_t raw_code) {
float VADC; /* Voltage calculation using ADC result for Coarse Temp calculation */
float VADCM; /* Voltage calculation using ADC result for Fine Temp calculation. */
float INT1VM; /* Voltage calculation for reality INT1V value during the ADC conversion */
VADC = ((float) raw_code * INT1V_VALUE_FLOAT) / ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
/* Coarse Temp Calculation by assume INT1V=1V for this ADC conversion */
coarse_temp = tempR + (((tempH - tempR) / (VADCH - VADCR)) * (VADC - VADCR));
/* Calculation to find the real INT1V value during the ADC conversion */
INT1VM = INT1VR + (((INT1VH - INT1VR) * (coarse_temp - tempR)) / (tempH - tempR));
VADCM = ((float) raw_code * INT1VM) / ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
/* Fine Temp Calculation by replace INT1V=1V by INT1V = INT1Vm for ADC conversion */
fine_temp = tempR + (((tempH - tempR) / (VADCH - VADCR)) * (VADCM - VADCR));
return fine_temp;
}
int main(void) {
float temperature;
/* Initialize all modules */
SYS_Initialize(NULL); //初始化
load_temperature_log_row_data(); // 读取校准值
printf("\r\n *********************************************** \r\n");
printf("\r\n Temperature \r\n");
printf("\r\n *********************************************** \r\n");
ADC_Enable(); //使能ADC
TC3_TimerStart();// 启动TC3
while (true) {
if (ADC_ConversionStatusGet() == 1) {
Adc_value = ADC_ConversionResultGet(); // 读取 ADC 结果
input_voltage = Adc_value / ADC_12BIT_FULL_SCALE_VALUE_FLOAT; //转化成电压值
temperature = calculate_temperature(Adc_value); // 温度转换(根据数据手册和校准值)
printf("\r\n:ADC Voltage : %4d, Voltage :%2.3fV, Temperature in room:%2.1fC\r\n\r\n", Adc_value, input_voltage, temperature);
}
}
/* Execution should not come here during normal operation */
return ( EXIT_FAILURE);
}
/*******************************************************************************
End of File
*/
通过上述步骤,快速配置 SAMD21 的内部温度传感器、定时器和事件系统,从而减少代码工作量,以最小化占用 MCU ,监测环境温度。
欢迎大家留言讨论
|