1 /*
2 * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdint.h>
8 #include "esp_types.h"
9 #include "esp_err.h"
10 #include "esp_check.h"
11 #include "assert.h"
12 #include "hal/efuse_ll.h"
13 #include "hal/adc_types.h"
14 #include "driver/adc_types_legacy.h"
15 #include "esp_adc_cal_types_legacy.h"
16
17 /* ----------------------------- Configuration ------------------------------ */
18 #ifdef CONFIG_ADC_CAL_EFUSE_TP_ENABLE
19 #define EFUSE_TP_ENABLED 1
20 #else
21 #define EFUSE_TP_ENABLED 0
22 #endif
23
24 #ifdef CONFIG_ADC_CAL_EFUSE_VREF_ENABLE
25 #define EFUSE_VREF_ENABLED 1
26 #else
27 #define EFUSE_VREF_ENABLED 0
28 #endif
29
30 #ifdef CONFIG_ADC_CAL_LUT_ENABLE
31 #define LUT_ENABLED 1
32 #else
33 #define LUT_ENABLED 0
34 #endif
35
36 /* ESP32s with both Two Point Values and Vref burned into eFuse are required to
37 * also also burn the EFUSE_BLK3_PART_RESERVE flag. A limited set of ESP32s
38 * (not available through regular sales channel) DO NOT have the
39 * EFUSE_BLK3_PART_RESERVE burned. Moreover, this set of ESP32s represents Vref
40 * in Two's Complement format. If this is the case, modify the preprocessor
41 * definitions below as follows...
42 * #define CHECK_BLK3_FLAG 0 //Do not check BLK3 flag as it is not burned
43 * #define VREF_FORMAT 1 //eFuse Vref is in Two's Complement format
44 */
45 #define CHECK_BLK3_FLAG 1
46 #define VREF_FORMAT 0
47
48 /* ------------------------------ eFuse Access ----------------------------- */
49 #define VREF_MASK 0x1F
50 #define VREF_STEP_SIZE 7
51 #define VREF_OFFSET 1100
52
53 #define TP_LOW1_OFFSET 278
54 #define TP_LOW2_OFFSET 421
55 #define TP_LOW_MASK 0x7F
56 #define TP_LOW_VOLTAGE 150
57 #define TP_HIGH1_OFFSET 3265
58 #define TP_HIGH2_OFFSET 3406
59 #define TP_HIGH_MASK 0x1FF
60 #define TP_HIGH_VOLTAGE 850
61 #define TP_STEP_SIZE 4
62
63 /* ----------------------- Raw to Voltage Constants ------------------------- */
64 #define LIN_COEFF_A_SCALE 65536
65 #define LIN_COEFF_A_ROUND (LIN_COEFF_A_SCALE/2)
66
67 #define LUT_VREF_LOW 1000
68 #define LUT_VREF_HIGH 1200
69 #define LUT_ADC_STEP_SIZE 64
70 #define LUT_POINTS 20
71 #define LUT_LOW_THRESH 2880
72 #define LUT_HIGH_THRESH (LUT_LOW_THRESH + LUT_ADC_STEP_SIZE)
73 #define ADC_12_BIT_RES 4096
74
75 /* ------------------------ Characterization Constants ---------------------- */
76 static const uint32_t adc1_tp_atten_scale[4] = {65504, 86975, 120389, 224310};
77 static const uint32_t adc2_tp_atten_scale[4] = {65467, 86861, 120416, 224708};
78 static const uint32_t adc1_tp_atten_offset[4] = {0, 1, 27, 54};
79 static const uint32_t adc2_tp_atten_offset[4] = {0, 9, 26, 66};
80
81 static const uint32_t adc1_vref_atten_scale[4] = {57431, 76236, 105481, 196602};
82 static const uint32_t adc2_vref_atten_scale[4] = {57236, 76175, 105678, 197170};
83 static const uint32_t adc1_vref_atten_offset[4] = {75, 78, 107, 142};
84 static const uint32_t adc2_vref_atten_offset[4] = {63, 66, 89, 128};
85
86 //20 Point lookup tables, covering ADC readings from 2880 to 4096, step size of 64
87 static const uint32_t lut_adc1_low[LUT_POINTS] = {2240, 2297, 2352, 2405, 2457, 2512, 2564, 2616, 2664, 2709,
88 2754, 2795, 2832, 2868, 2903, 2937, 2969, 3000, 3030, 3060};
89 static const uint32_t lut_adc1_high[LUT_POINTS] = {2667, 2706, 2745, 2780, 2813, 2844, 2873, 2901, 2928, 2956,
90 2982, 3006, 3032, 3059, 3084, 3110, 3135, 3160, 3184, 3209};
91 static const uint32_t lut_adc2_low[LUT_POINTS] = {2238, 2293, 2347, 2399, 2451, 2507, 2561, 2613, 2662, 2710,
92 2754, 2792, 2831, 2869, 2904, 2937, 2968, 2999, 3029, 3059};
93 static const uint32_t lut_adc2_high[LUT_POINTS] = {2657, 2698, 2738, 2774, 2807, 2838, 2867, 2894, 2921, 2946,
94 2971, 2996, 3020, 3043, 3067, 3092, 3116, 3139, 3162, 3185};
95
96 /* ----------------------- EFuse Access Functions --------------------------- */
check_efuse_vref(void)97 static bool check_efuse_vref(void)
98 {
99 //Check if Vref is burned in eFuse
100 return (efuse_ll_get_adc_vref() != 0) ? true : false;
101 }
102
check_efuse_tp(void)103 static bool check_efuse_tp(void)
104 {
105 //Check if Two Point values are burned in eFuse
106 if (CHECK_BLK3_FLAG && (efuse_ll_get_blk3_part_reserve() == 0)) {
107 return false;
108 }
109 //All TP cal values must be non zero
110 return efuse_ll_get_adc1_tp_low() &&
111 efuse_ll_get_adc2_tp_low() &&
112 efuse_ll_get_adc1_tp_high() &&
113 efuse_ll_get_adc2_tp_high();
114 }
115
decode_bits(uint32_t bits,uint32_t mask,bool is_twos_compl)116 static inline int decode_bits(uint32_t bits, uint32_t mask, bool is_twos_compl)
117 {
118 int ret;
119 if (bits & (~(mask >> 1) & mask)) { //Check sign bit (MSB of mask)
120 //Negative
121 if (is_twos_compl) {
122 ret = -(((~bits) + 1) & (mask >> 1)); //2's complement
123 } else {
124 ret = -(bits & (mask >> 1)); //Sign-magnitude
125 }
126 } else {
127 //Positive
128 ret = bits & (mask >> 1);
129 }
130 return ret;
131 }
132
read_efuse_vref(void)133 static uint32_t read_efuse_vref(void)
134 {
135 //eFuse stores deviation from ideal reference voltage
136 uint32_t ret = VREF_OFFSET; //Ideal vref
137 uint32_t bits = efuse_ll_get_adc_vref();
138 ret += decode_bits(bits, VREF_MASK, VREF_FORMAT) * VREF_STEP_SIZE;
139 return ret; //ADC Vref in mV
140 }
141
read_efuse_tp_low(adc_unit_t adc_num)142 static uint32_t read_efuse_tp_low(adc_unit_t adc_num)
143 {
144 //ADC reading at 150mV stored in two's complement format
145 uint32_t ret;
146 uint32_t bits;
147
148 if (adc_num == ADC_UNIT_1) {
149 ret = TP_LOW1_OFFSET;
150 bits = efuse_ll_get_adc1_tp_low();
151 } else {
152 ret = TP_LOW2_OFFSET;
153 bits = efuse_ll_get_adc2_tp_low();
154 }
155 ret += decode_bits(bits, TP_LOW_MASK, true) * TP_STEP_SIZE;
156 return ret; //Reading of ADC at 150mV
157 }
158
read_efuse_tp_high(adc_unit_t adc_num)159 static uint32_t read_efuse_tp_high(adc_unit_t adc_num)
160 {
161 //ADC reading at 850mV stored in two's complement format
162 uint32_t ret;
163 uint32_t bits;
164
165 if (adc_num == ADC_UNIT_1) {
166 ret = TP_HIGH1_OFFSET;
167 bits = efuse_ll_get_adc1_tp_high();
168 } else {
169 ret = TP_HIGH2_OFFSET;
170 bits = efuse_ll_get_adc2_tp_high();
171 }
172 ret += decode_bits(bits, TP_HIGH_MASK, true) * TP_STEP_SIZE;
173 return ret; //Reading of ADC at 850mV
174 }
175
176 /* ----------------------- Characterization Functions ----------------------- */
characterize_using_two_point(adc_unit_t adc_num,adc_atten_t atten,uint32_t high,uint32_t low,uint32_t * coeff_a,uint32_t * coeff_b)177 static void characterize_using_two_point(adc_unit_t adc_num,
178 adc_atten_t atten,
179 uint32_t high,
180 uint32_t low,
181 uint32_t *coeff_a,
182 uint32_t *coeff_b)
183 {
184 const uint32_t *atten_scales;
185 const uint32_t *atten_offsets;
186
187 if (adc_num == ADC_UNIT_1) { //Using ADC 1
188 atten_scales = adc1_tp_atten_scale;
189 atten_offsets = adc1_tp_atten_offset;
190 } else { //Using ADC 2
191 atten_scales = adc2_tp_atten_scale;
192 atten_offsets = adc2_tp_atten_offset;
193 }
194 //Characterize ADC-Voltage curve as y = (coeff_a * x) + coeff_b
195 uint32_t delta_x = high - low;
196 uint32_t delta_v = TP_HIGH_VOLTAGE - TP_LOW_VOLTAGE;
197 //Where coeff_a = (delta_v/delta_x) * atten_scale
198 *coeff_a = (delta_v * atten_scales[atten] + (delta_x / 2)) / delta_x; //+(delta_x/2) for rounding
199 //Where coeff_b = high_v - ((delta_v/delta_x) * high_x) + atten_offset
200 *coeff_b = TP_HIGH_VOLTAGE - ((delta_v * high + (delta_x / 2)) / delta_x) + atten_offsets[atten];
201 }
202
characterize_using_vref(adc_unit_t adc_num,adc_atten_t atten,uint32_t vref,uint32_t * coeff_a,uint32_t * coeff_b)203 static void characterize_using_vref(adc_unit_t adc_num,
204 adc_atten_t atten,
205 uint32_t vref,
206 uint32_t *coeff_a,
207 uint32_t *coeff_b)
208 {
209 const uint32_t *atten_scales;
210 const uint32_t *atten_offsets;
211
212 if (adc_num == ADC_UNIT_1) { //Using ADC 1
213 atten_scales = adc1_vref_atten_scale;
214 atten_offsets = adc1_vref_atten_offset;
215 } else { //Using ADC 2
216 atten_scales = adc2_vref_atten_scale;
217 atten_offsets = adc2_vref_atten_offset;
218 }
219 //Characterize ADC-Voltage curve as y = (coeff_a * x) + coeff_b
220 //Where coeff_a = (vref/4096) * atten_scale
221 *coeff_a = (vref * atten_scales[atten]) / (ADC_12_BIT_RES);
222 *coeff_b = atten_offsets[atten];
223 }
224
225 /* ------------------------ Conversion Functions --------------------------- */
calculate_voltage_linear(uint32_t adc_reading,uint32_t coeff_a,uint32_t coeff_b)226 static uint32_t calculate_voltage_linear(uint32_t adc_reading, uint32_t coeff_a, uint32_t coeff_b)
227 {
228 //Where voltage = coeff_a * adc_reading + coeff_b
229 return (((coeff_a * adc_reading) + LIN_COEFF_A_ROUND) / LIN_COEFF_A_SCALE) + coeff_b;
230 }
231
232 //Only call when ADC reading is above threshold
calculate_voltage_lut(uint32_t adc,uint32_t vref,const uint32_t * low_vref_curve,const uint32_t * high_vref_curve)233 static uint32_t calculate_voltage_lut(uint32_t adc, uint32_t vref, const uint32_t *low_vref_curve, const uint32_t *high_vref_curve)
234 {
235 //Get index of lower bound points of LUT
236 uint32_t i = (adc - LUT_LOW_THRESH) / LUT_ADC_STEP_SIZE;
237
238 //Let the X Axis be Vref, Y axis be ADC reading, and Z be voltage
239 int x2dist = LUT_VREF_HIGH - vref; //(x2 - x)
240 int x1dist = vref - LUT_VREF_LOW; //(x - x1)
241 int y2dist = ((i + 1) * LUT_ADC_STEP_SIZE) + LUT_LOW_THRESH - adc; //(y2 - y)
242 int y1dist = adc - ((i * LUT_ADC_STEP_SIZE) + LUT_LOW_THRESH); //(y - y1)
243
244 //For points for bilinear interpolation
245 int q11 = low_vref_curve[i]; //Lower bound point of low_vref_curve
246 int q12 = low_vref_curve[i + 1]; //Upper bound point of low_vref_curve
247 int q21 = high_vref_curve[i]; //Lower bound point of high_vref_curve
248 int q22 = high_vref_curve[i + 1]; //Upper bound point of high_vref_curve
249
250 //Bilinear interpolation
251 //Where z = 1/((x2-x1)*(y2-y1)) * ( (q11*x2dist*y2dist) + (q21*x1dist*y2dist) + (q12*x2dist*y1dist) + (q22*x1dist*y1dist) )
252 int voltage = (q11 * x2dist * y2dist) + (q21 * x1dist * y2dist) + (q12 * x2dist * y1dist) + (q22 * x1dist * y1dist);
253 voltage += ((LUT_VREF_HIGH - LUT_VREF_LOW) * LUT_ADC_STEP_SIZE) / 2; //Integer division rounding
254 voltage /= ((LUT_VREF_HIGH - LUT_VREF_LOW) * LUT_ADC_STEP_SIZE); //Divide by ((x2-x1)*(y2-y1))
255 return (uint32_t)voltage;
256 }
257
interpolate_two_points(uint32_t y1,uint32_t y2,uint32_t x_step,uint32_t x)258 static inline uint32_t interpolate_two_points(uint32_t y1, uint32_t y2, uint32_t x_step, uint32_t x)
259 {
260 //Interpolate between two points (x1,y1) (x2,y2) between 'lower' and 'upper' separated by 'step'
261 return ((y1 * x_step) + (y2 * x) - (y1 * x) + (x_step / 2)) / x_step;
262 }
263
264 /* ------------------------- Public API ------------------------------------- */
esp_adc_cal_check_efuse(esp_adc_cal_value_t source)265 esp_err_t esp_adc_cal_check_efuse(esp_adc_cal_value_t source)
266 {
267 if (source == ESP_ADC_CAL_VAL_EFUSE_TP) {
268 return (check_efuse_tp()) ? ESP_OK : ESP_ERR_NOT_SUPPORTED;
269 } else if (source == ESP_ADC_CAL_VAL_EFUSE_VREF) {
270 return (check_efuse_vref()) ? ESP_OK : ESP_ERR_NOT_SUPPORTED;
271 } else {
272 return ESP_ERR_INVALID_ARG;
273 }
274 }
275
esp_adc_cal_characterize(adc_unit_t adc_num,adc_atten_t atten,adc_bits_width_t bit_width,uint32_t default_vref,esp_adc_cal_characteristics_t * chars)276 esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num,
277 adc_atten_t atten,
278 adc_bits_width_t bit_width,
279 uint32_t default_vref,
280 esp_adc_cal_characteristics_t *chars)
281 {
282 //Check parameters
283 assert((adc_num == ADC_UNIT_1) || (adc_num == ADC_UNIT_2));
284 assert(chars != NULL);
285 assert(bit_width < ADC_WIDTH_MAX);
286
287 //Check eFuse if enabled to do so
288 bool efuse_tp_present = check_efuse_tp();
289 bool efuse_vref_present = check_efuse_vref();
290 esp_adc_cal_value_t ret;
291
292 if (efuse_tp_present && EFUSE_TP_ENABLED) {
293 //Characterize based on Two Point values
294 uint32_t high = read_efuse_tp_high(adc_num);
295 uint32_t low = read_efuse_tp_low(adc_num);
296 characterize_using_two_point(adc_num, atten, high, low, &chars->coeff_a, &chars->coeff_b);
297 ret = ESP_ADC_CAL_VAL_EFUSE_TP;
298 } else if (efuse_vref_present && EFUSE_VREF_ENABLED) {
299 //Characterize based on eFuse Vref
300 uint32_t vref = read_efuse_vref();
301 characterize_using_vref(adc_num, atten, vref, &chars->coeff_a, &chars->coeff_b);
302 ret = ESP_ADC_CAL_VAL_EFUSE_VREF;
303 } else {
304 //Characterized based on default Vref
305 characterize_using_vref(adc_num, atten, default_vref, &chars->coeff_a, &chars->coeff_b);
306 ret = ESP_ADC_CAL_VAL_DEFAULT_VREF;
307 }
308
309 //Initialized remaining fields
310 chars->adc_num = adc_num;
311 chars->atten = atten;
312 chars->bit_width = bit_width;
313 chars->vref = (EFUSE_VREF_ENABLED && efuse_vref_present) ? read_efuse_vref() : default_vref;
314 //Initialize fields for lookup table if necessary
315 if (LUT_ENABLED && atten == ADC_ATTEN_DB_11) {
316 chars->low_curve = (adc_num == ADC_UNIT_1) ? lut_adc1_low : lut_adc2_low;
317 chars->high_curve = (adc_num == ADC_UNIT_1) ? lut_adc1_high : lut_adc2_high;
318 } else {
319 chars->low_curve = NULL;
320 chars->high_curve = NULL;
321 }
322 return ret;
323 }
324
esp_adc_cal_raw_to_voltage(uint32_t adc_reading,const esp_adc_cal_characteristics_t * chars)325 uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_characteristics_t *chars)
326 {
327 assert(chars != NULL);
328
329 //Scale adc_rading if not 12 bits wide
330 adc_reading = (adc_reading << (ADC_WIDTH_BIT_12 - chars->bit_width));
331 if (adc_reading > ADC_12_BIT_RES - 1) {
332 adc_reading = ADC_12_BIT_RES - 1; //Set to 12bit res max
333 }
334
335 if (LUT_ENABLED && (chars->atten == ADC_ATTEN_DB_11) && (adc_reading >= LUT_LOW_THRESH)) { //Check if in non-linear region
336 //Use lookup table to get voltage in non linear portion of ADC_ATTEN_DB_11
337 uint32_t lut_voltage = calculate_voltage_lut(adc_reading, chars->vref, chars->low_curve, chars->high_curve);
338 if (adc_reading <= LUT_HIGH_THRESH) { //If ADC is transitioning from linear region to non-linear region
339 //Linearly interpolate between linear voltage and lut voltage
340 uint32_t linear_voltage = calculate_voltage_linear(adc_reading, chars->coeff_a, chars->coeff_b);
341 return interpolate_two_points(linear_voltage, lut_voltage, LUT_ADC_STEP_SIZE, (adc_reading - LUT_LOW_THRESH));
342 } else {
343 return lut_voltage;
344 }
345 } else {
346 return calculate_voltage_linear(adc_reading, chars->coeff_a, chars->coeff_b);
347 }
348 }
349