samd: size-optimize microcontroller temp calc

Perform most arithmetic with scaled integer values.
For my calibration values
```
const uint32_t NVMCTRL_TEMP_LOG[]={0xfc05511e, 0xcc7ac0f7};
```
the maximum difference between the old and new calculation is 0.50°C.
The difference is smallest (0.13°) at 25.87°C in the old scale.

This reduces mcu_processor_get_temperature from 568 bytes to 348 bytes
(-220 bytes)
This commit is contained in:
Jeff Epler 2022-10-12 14:29:57 -05:00
parent e3688b4342
commit b251e78ac5
No known key found for this signature in database
GPG Key ID: D5BF15AB975AB4DE

View File

@ -73,46 +73,26 @@
#include "peripheral_clk_config.h"
#define ADC_TEMP_SAMPLE_LENGTH 4
#define INT1V_VALUE_FLOAT 1.0
#define INT1V_DIVIDER_1000 1000.0
#define ADC_12BIT_FULL_SCALE_VALUE_FLOAT 4095.0
#define INT1V_VALUE_FLOAT MICROPY_FLOAT_CONST(1.0)
#define INT1V_DIVIDER_1000 MICROPY_FLOAT_CONST(1000.0)
#define ADC_12BIT_FULL_SCALE_VALUE_FLOAT MICROPY_FLOAT_CONST(4095.0)
// channel argument (ignored in calls below)
#define IGNORED_CHANNEL 0
// Decimal to fraction conversion. (adapted from ASF sample).
STATIC float convert_dec_to_frac(uint8_t val) {
float float_val = (float)val;
if (val < 10) {
return float_val / 10.0;
} else if (val < 100) {
return float_val / 100.0;
} else {
return float_val / 1000.0;
}
}
// Extract the production calibration data information from NVM (adapted from ASF sample),
// then calculate the temperature
#ifdef SAMD21
STATIC float calculate_temperature(uint16_t raw_value) {
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*/
float tempR; // Production Room temperature
float tempH; // Production Hot temperature
float INT1VR; // Room temp 2's complement of the internal 1V reference value
float INT1VH; // Hot temp 2's complement of the internal 1V reference value
uint16_t ADCR; // Production Room temperature ADC value
uint16_t ADCH; // Production Hot temperature ADC value
float VADCR; // Room temperature ADC voltage
float VADCH; // Hot temperature ADC voltage
uint32_t val1; /* Temperature Log Row Content first 32 bits */
uint32_t val2; /* Temperature Log Row Content another 32 bits */
int room_temp_val_int; /* Integer part of room temperature in °C */
int room_temp_val_dec; /* Decimal part of room temperature in °C */
int hot_temp_val_int; /* Integer part of hot temperature in °C */
int hot_temp_val_dec; /* Decimal part of hot temperature in °C */
int room_int1v_val; /* internal 1V reference drift at room temperature */
int hot_int1v_val; /* internal 1V reference drift at hot temperature*/
uint32_t *temp_log_row_ptr = (uint32_t *)NVMCTRL_TEMP_LOG;
@ -120,32 +100,31 @@ STATIC float calculate_temperature(uint16_t raw_value) {
temp_log_row_ptr++;
val2 = *temp_log_row_ptr;
room_temp_val_int = (uint8_t)((val1 & FUSES_ROOM_TEMP_VAL_INT_Msk) >> FUSES_ROOM_TEMP_VAL_INT_Pos);
room_temp_val_dec = (uint8_t)((val1 & FUSES_ROOM_TEMP_VAL_DEC_Msk) >> FUSES_ROOM_TEMP_VAL_DEC_Pos);
room_temp_val_int = ((val1 & FUSES_ROOM_TEMP_VAL_INT_Msk) >> FUSES_ROOM_TEMP_VAL_INT_Pos);
room_temp_val_dec = ((val1 & FUSES_ROOM_TEMP_VAL_DEC_Msk) >> FUSES_ROOM_TEMP_VAL_DEC_Pos);
hot_temp_val_int = (uint8_t)((val1 & FUSES_HOT_TEMP_VAL_INT_Msk) >> FUSES_HOT_TEMP_VAL_INT_Pos);
hot_temp_val_dec = (uint8_t)((val1 & FUSES_HOT_TEMP_VAL_DEC_Msk) >> FUSES_HOT_TEMP_VAL_DEC_Pos);
hot_temp_val_int = ((val1 & FUSES_HOT_TEMP_VAL_INT_Msk) >> FUSES_HOT_TEMP_VAL_INT_Pos);
hot_temp_val_dec = ((val1 & FUSES_HOT_TEMP_VAL_DEC_Msk) >> FUSES_HOT_TEMP_VAL_DEC_Pos);
// necessary casts: must interpret 8 bits as signed
room_int1v_val = (int8_t)((val1 & FUSES_ROOM_INT1V_VAL_Msk) >> FUSES_ROOM_INT1V_VAL_Pos);
hot_int1v_val = (int8_t)((val2 & FUSES_HOT_INT1V_VAL_Msk) >> FUSES_HOT_INT1V_VAL_Pos);
ADCR = (uint16_t)((val2 & FUSES_ROOM_ADC_VAL_Msk) >> FUSES_ROOM_ADC_VAL_Pos);
ADCH = (uint16_t)((val2 & FUSES_HOT_ADC_VAL_Msk) >> FUSES_HOT_ADC_VAL_Pos);
int ADCR = ((val2 & FUSES_ROOM_ADC_VAL_Msk) >> FUSES_ROOM_ADC_VAL_Pos);
int ADCH = ((val2 & FUSES_HOT_ADC_VAL_Msk) >> FUSES_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);
int tempR = 10 * room_temp_val_int + room_temp_val_dec;
int tempH = 10 * hot_temp_val_int + hot_temp_val_dec;
INT1VR = 1 - ((float)room_int1v_val / INT1V_DIVIDER_1000);
INT1VH = 1 - ((float)hot_int1v_val / INT1V_DIVIDER_1000);
int INT1VR = 1000 - room_int1v_val;
int INT1VH = 1000 - hot_int1v_val;
VADCR = ((float)ADCR * INT1VR) / ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
VADCH = ((float)ADCH * INT1VH) / ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
int VADCR = ADCR * INT1VR;
int VADCH = ADCH * INT1VH;
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 */
int INT1VM; /* Voltage calculation for reality INT1V value during the ADC conversion */
VADC = ((float)raw_value * INT1V_VALUE_FLOAT) / ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
int VADC = raw_value * 1000;
// Hopefully compiler will remove common subepxressions here.
@ -153,21 +132,25 @@ STATIC float calculate_temperature(uint16_t raw_value) {
// 1b as mentioned in data sheet section "Temperature Sensor Characteristics"
// of Electrical Characteristics. (adapted from ASF sample code).
// Coarse Temp Calculation by assume INT1V=1V for this ADC conversion
float coarse_temp = tempR + (((tempH - tempR) / (VADCH - VADCR)) * (VADC - VADCR));
int coarse_temp = tempR + (tempH - tempR) * (VADC - VADCR) / (VADCH - VADCR);
// Calculation to find the real INT1V value during the ADC conversion
INT1VM = INT1VR + (((INT1VH - INT1VR) * (coarse_temp - tempR)) / (tempH - tempR));
VADCM = ((float)raw_value * INT1VM) / ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
int VADCM = raw_value * INT1VM;
// Fine Temp Calculation by replace INT1V=1V by INT1V = INT1Vm for ADC conversion
float fine_temp = tempR + (((tempH - tempR) / (VADCH - VADCR)) * (VADCM - VADCR));
float fine_temp = tempR + (((tempH - tempR) * (VADCM - VADCR)) / (VADCH - VADCR));
return fine_temp;
return fine_temp / 10;
}
#endif // SAMD21
#ifdef SAM_D5X_E5X
// Decimal to fraction conversion. (adapted from ASF sample).
STATIC float convert_dec_to_frac(uint8_t val) {
return val / MICROPY_FLOAT_CONST(10.);
}
STATIC float calculate_temperature(uint16_t TP, uint16_t TC) {
uint32_t TLI = (*(uint32_t *)FUSES_ROOM_TEMP_VAL_INT_ADDR & FUSES_ROOM_TEMP_VAL_INT_Msk) >> FUSES_ROOM_TEMP_VAL_INT_Pos;
uint32_t TLD = (*(uint32_t *)FUSES_ROOM_TEMP_VAL_DEC_ADDR & FUSES_ROOM_TEMP_VAL_DEC_Msk) >> FUSES_ROOM_TEMP_VAL_DEC_Pos;