Skip to content

Commit 1569c7e

Browse files
authored
Merge pull request #7045 from jepler/smaller-microcontroller-temperature-samd
samd: Size-optimize the temperature code with an 0.5 to 2.0 degree relative additional error
2 parents dce4c9d + ead03cd commit 1569c7e

File tree

2 files changed

+55
-53
lines changed

2 files changed

+55
-53
lines changed

ports/atmel-samd/common-hal/microcontroller/Processor.c

Lines changed: 50 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -73,101 +73,99 @@
7373
#include "peripheral_clk_config.h"
7474

7575
#define ADC_TEMP_SAMPLE_LENGTH 4
76-
#define INT1V_VALUE_FLOAT 1.0
77-
#define INT1V_DIVIDER_1000 1000.0
78-
#define ADC_12BIT_FULL_SCALE_VALUE_FLOAT 4095.0
76+
#define INT1V_VALUE_FLOAT MICROPY_FLOAT_CONST(1.0)
77+
#define INT1V_DIVIDER_1000 MICROPY_FLOAT_CONST(1000.0)
78+
#define ADC_12BIT_FULL_SCALE_VALUE_FLOAT MICROPY_FLOAT_CONST(4095.0)
7979

8080
// channel argument (ignored in calls below)
8181
#define IGNORED_CHANNEL 0
8282

83-
// Decimal to fraction conversion. (adapted from ASF sample).
84-
STATIC float convert_dec_to_frac(uint8_t val) {
85-
float float_val = (float)val;
86-
if (val < 10) {
87-
return float_val / 10.0;
88-
} else if (val < 100) {
89-
return float_val / 100.0;
90-
} else {
91-
return float_val / 1000.0;
92-
}
93-
}
9483

9584
// Extract the production calibration data information from NVM (adapted from ASF sample),
9685
// then calculate the temperature
86+
//
87+
// This code performs almost all operations with scaled integers. For
88+
// instance, tempR is in units of 1/10°C, INT1VR is in units of 1mV, etc,
89+
// This is important to reduce the code size of the function. The effect on
90+
// precision is a ~.9°C difference vs the floating point algorithm on an
91+
// approximate 0..60°C range with a difference of ~.5°C at 25°C. When the fine
92+
// calculation step is skipped, the additional error approximately doubles.
93+
//
94+
// To save code size, rounding is neglected. However, trying to add back rounding
95+
// (by computing (a + b/2) / b instead of just a / b) actually didn't help
96+
// accuracy anyway.
9797
#ifdef SAMD21
9898
STATIC float calculate_temperature(uint16_t raw_value) {
99-
volatile uint32_t val1; /* Temperature Log Row Content first 32 bits */
100-
volatile uint32_t val2; /* Temperature Log Row Content another 32 bits */
101-
uint8_t room_temp_val_int; /* Integer part of room temperature in °C */
102-
uint8_t room_temp_val_dec; /* Decimal part of room temperature in °C */
103-
uint8_t hot_temp_val_int; /* Integer part of hot temperature in °C */
104-
uint8_t hot_temp_val_dec; /* Decimal part of hot temperature in °C */
105-
int8_t room_int1v_val; /* internal 1V reference drift at room temperature */
106-
int8_t hot_int1v_val; /* internal 1V reference drift at hot temperature*/
107-
108-
float tempR; // Production Room temperature
109-
float tempH; // Production Hot temperature
110-
float INT1VR; // Room temp 2's complement of the internal 1V reference value
111-
float INT1VH; // Hot temp 2's complement of the internal 1V reference value
112-
uint16_t ADCR; // Production Room temperature ADC value
113-
uint16_t ADCH; // Production Hot temperature ADC value
114-
float VADCR; // Room temperature ADC voltage
115-
float VADCH; // Hot temperature ADC voltage
99+
uint32_t val1; /* Temperature Log Row Content first 32 bits */
100+
uint32_t val2; /* Temperature Log Row Content another 32 bits */
101+
int room_temp_val_int; /* Integer part of room temperature in °C */
102+
int room_temp_val_dec; /* Decimal part of room temperature in °C */
103+
int hot_temp_val_int; /* Integer part of hot temperature in °C */
104+
int hot_temp_val_dec; /* Decimal part of hot temperature in °C */
105+
int room_int1v_val; /* internal 1V reference drift at room temperature */
106+
int hot_int1v_val; /* internal 1V reference drift at hot temperature*/
116107

117108
uint32_t *temp_log_row_ptr = (uint32_t *)NVMCTRL_TEMP_LOG;
118109

119110
val1 = *temp_log_row_ptr;
120111
temp_log_row_ptr++;
121112
val2 = *temp_log_row_ptr;
122113

123-
room_temp_val_int = (uint8_t)((val1 & FUSES_ROOM_TEMP_VAL_INT_Msk) >> FUSES_ROOM_TEMP_VAL_INT_Pos);
124-
room_temp_val_dec = (uint8_t)((val1 & FUSES_ROOM_TEMP_VAL_DEC_Msk) >> FUSES_ROOM_TEMP_VAL_DEC_Pos);
114+
room_temp_val_int = ((val1 & FUSES_ROOM_TEMP_VAL_INT_Msk) >> FUSES_ROOM_TEMP_VAL_INT_Pos);
115+
room_temp_val_dec = ((val1 & FUSES_ROOM_TEMP_VAL_DEC_Msk) >> FUSES_ROOM_TEMP_VAL_DEC_Pos);
125116

126-
hot_temp_val_int = (uint8_t)((val1 & FUSES_HOT_TEMP_VAL_INT_Msk) >> FUSES_HOT_TEMP_VAL_INT_Pos);
127-
hot_temp_val_dec = (uint8_t)((val1 & FUSES_HOT_TEMP_VAL_DEC_Msk) >> FUSES_HOT_TEMP_VAL_DEC_Pos);
117+
hot_temp_val_int = ((val1 & FUSES_HOT_TEMP_VAL_INT_Msk) >> FUSES_HOT_TEMP_VAL_INT_Pos);
118+
hot_temp_val_dec = ((val1 & FUSES_HOT_TEMP_VAL_DEC_Msk) >> FUSES_HOT_TEMP_VAL_DEC_Pos);
128119

120+
// necessary casts: must interpret 8 bits as signed
129121
room_int1v_val = (int8_t)((val1 & FUSES_ROOM_INT1V_VAL_Msk) >> FUSES_ROOM_INT1V_VAL_Pos);
130122
hot_int1v_val = (int8_t)((val2 & FUSES_HOT_INT1V_VAL_Msk) >> FUSES_HOT_INT1V_VAL_Pos);
131123

132-
ADCR = (uint16_t)((val2 & FUSES_ROOM_ADC_VAL_Msk) >> FUSES_ROOM_ADC_VAL_Pos);
133-
ADCH = (uint16_t)((val2 & FUSES_HOT_ADC_VAL_Msk) >> FUSES_HOT_ADC_VAL_Pos);
134-
135-
tempR = room_temp_val_int + convert_dec_to_frac(room_temp_val_dec);
136-
tempH = hot_temp_val_int + convert_dec_to_frac(hot_temp_val_dec);
124+
int ADCR = ((val2 & FUSES_ROOM_ADC_VAL_Msk) >> FUSES_ROOM_ADC_VAL_Pos);
125+
int ADCH = ((val2 & FUSES_HOT_ADC_VAL_Msk) >> FUSES_HOT_ADC_VAL_Pos);
137126

138-
INT1VR = 1 - ((float)room_int1v_val / INT1V_DIVIDER_1000);
139-
INT1VH = 1 - ((float)hot_int1v_val / INT1V_DIVIDER_1000);
127+
int tempR = 10 * room_temp_val_int + room_temp_val_dec;
128+
int tempH = 10 * hot_temp_val_int + hot_temp_val_dec;
140129

141-
VADCR = ((float)ADCR * INT1VR) / ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
142-
VADCH = ((float)ADCH * INT1VH) / ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
130+
int INT1VR = 1000 - room_int1v_val;
131+
int INT1VH = 1000 - hot_int1v_val;
143132

144-
float VADC; /* Voltage calculation using ADC result for Coarse Temp calculation */
145-
float VADCM; /* Voltage calculation using ADC result for Fine Temp calculation. */
146-
float INT1VM; /* Voltage calculation for reality INT1V value during the ADC conversion */
133+
int VADCR = ADCR * INT1VR;
134+
int VADCH = ADCH * INT1VH;
147135

148-
VADC = ((float)raw_value * INT1V_VALUE_FLOAT) / ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
136+
int VADC = raw_value * 1000;
149137

150138
// Hopefully compiler will remove common subepxressions here.
151139

152140
// calculate fine temperature using Equation1 and Equation
153141
// 1b as mentioned in data sheet section "Temperature Sensor Characteristics"
154142
// of Electrical Characteristics. (adapted from ASF sample code).
155143
// Coarse Temp Calculation by assume INT1V=1V for this ADC conversion
156-
float coarse_temp = tempR + (((tempH - tempR) / (VADCH - VADCR)) * (VADC - VADCR));
144+
int coarse_temp = tempR + (tempH - tempR) * (VADC - VADCR) / (VADCH - VADCR);
157145

146+
#if CIRCUITPY_FULL_BUILD
158147
// Calculation to find the real INT1V value during the ADC conversion
148+
int INT1VM; /* Voltage calculation for reality INT1V value during the ADC conversion */
149+
159150
INT1VM = INT1VR + (((INT1VH - INT1VR) * (coarse_temp - tempR)) / (tempH - tempR));
160151

161-
VADCM = ((float)raw_value * INT1VM) / ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
152+
int VADCM = raw_value * INT1VM;
162153

163154
// Fine Temp Calculation by replace INT1V=1V by INT1V = INT1Vm for ADC conversion
164-
float fine_temp = tempR + (((tempH - tempR) / (VADCH - VADCR)) * (VADCM - VADCR));
155+
float fine_temp = tempR + (((tempH - tempR) * (VADCM - VADCR)) / (VADCH - VADCR));
165156

166-
return fine_temp;
157+
return fine_temp / 10;
158+
#else
159+
return coarse_temp / 10.;
160+
#endif
167161
}
168162
#endif // SAMD21
169163

170164
#ifdef SAM_D5X_E5X
165+
// Decimal to fraction conversion. (adapted from ASF sample).
166+
STATIC float convert_dec_to_frac(uint8_t val) {
167+
return val / MICROPY_FLOAT_CONST(10.);
168+
}
171169
STATIC float calculate_temperature(uint16_t TP, uint16_t TC) {
172170
uint32_t TLI = (*(uint32_t *)FUSES_ROOM_TEMP_VAL_INT_ADDR & FUSES_ROOM_TEMP_VAL_INT_Msk) >> FUSES_ROOM_TEMP_VAL_INT_Pos;
173171
uint32_t TLD = (*(uint32_t *)FUSES_ROOM_TEMP_VAL_DEC_ADDR & FUSES_ROOM_TEMP_VAL_DEC_Msk) >> FUSES_ROOM_TEMP_VAL_DEC_Pos;

shared-bindings/microcontroller/Processor.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,11 @@ MP_PROPERTY_GETTER(mcu_processor_reset_reason_obj,
104104
//| temperature: Optional[float]
105105
//| """The on-chip temperature, in Celsius, as a float. (read-only)
106106
//|
107-
//| Is `None` if the temperature is not available."""
107+
//| Is `None` if the temperature is not available.
108+
//|
109+
//| .. note :: On small SAMD21 builds without external flash,
110+
//| the reported temperature has reduced accuracy and precision, to save code space.
111+
//| """
108112
STATIC mp_obj_t mcu_processor_get_temperature(mp_obj_t self) {
109113
float temperature = common_hal_mcu_processor_get_temperature();
110114
return isnan(temperature) ? mp_const_none : mp_obj_new_float(temperature);

0 commit comments

Comments
 (0)