1 /*
2  * SPDX-FileCopyrightText: 2019-2021 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 "soc/efuse_periph.h"
10 #include "esp_err.h"
11 #include "esp_check.h"
12 #include "assert.h"
13 #include "esp_efuse.h"
14 #include "esp_efuse_table.h"
15 #include "esp_efuse_rtc_table.h"
16 #include "hal/adc_hal.h"
17 #include "hal/adc_types.h"
18 #include "driver/adc_types_legacy.h"
19 #include "esp_adc_cal_types_legacy.h"
20 
21 const static char LOG_TAG[] = "adc_calib";
22 
23 /* ------------------------ Characterization Constants ---------------------- */
24 
25 // coeff_a and coeff_b are actually floats
26 // they are scaled to put them into uint32_t so that the headers do not have to be changed
27 static const int coeff_a_scaling = 65536;
28 static const int coeff_b_scaling = 1024;
29 /* -------------------- Characterization Helper Data Types ------------------ */
30 typedef struct {
31     int adc_calib_high;
32     int adc_calib_low;
33 } adc_calib_data_ver1;
34 
35 typedef struct {
36     int adc_calib_high;         // the reading of adc ...
37     int adc_calib_high_voltage; // ... at this voltage (mV)
38 } adc_calib_data_ver2;
39 
40 typedef struct {
41     char version_num;
42     adc_unit_t adc_num;
43     adc_atten_t atten_level;
44     union {
45         adc_calib_data_ver1 ver1;
46         adc_calib_data_ver2 ver2;
47     } efuse_data;
48 } adc_calib_parsed_info;
49 
prepare_calib_data_for(adc_unit_t adc_num,adc_atten_t atten,adc_calib_parsed_info * parsed_data_storage)50 static bool prepare_calib_data_for(adc_unit_t adc_num, adc_atten_t atten, adc_calib_parsed_info *parsed_data_storage)
51 {
52     int version_num = esp_efuse_rtc_table_read_calib_version();
53     int tag;
54     parsed_data_storage->version_num = version_num;
55     parsed_data_storage->adc_num = adc_num;
56     parsed_data_storage->atten_level = atten;
57     switch (version_num) {
58     case 1:
59         // note: use the adc_num as in hal, which start from 0.
60         tag = esp_efuse_rtc_table_get_tag(version_num, adc_num, atten, RTCCALIB_V1_PARAM_VLOW);
61         parsed_data_storage->efuse_data.ver1.adc_calib_low = esp_efuse_rtc_table_get_parsed_efuse_value(tag, false);
62         tag = esp_efuse_rtc_table_get_tag(version_num, adc_num, atten, RTCCALIB_V1_PARAM_VHIGH);
63         parsed_data_storage->efuse_data.ver1.adc_calib_high = esp_efuse_rtc_table_get_parsed_efuse_value(tag, false);
64         break;
65     case 2:
66         tag = esp_efuse_rtc_table_get_tag(version_num, adc_num, atten, RTCCALIB_V2_PARAM_VHIGH);
67         parsed_data_storage->efuse_data.ver2.adc_calib_high = esp_efuse_rtc_table_get_parsed_efuse_value(tag, false);
68         switch (parsed_data_storage->atten_level) {
69         case ADC_ATTEN_DB_0:
70             parsed_data_storage->efuse_data.ver2.adc_calib_high_voltage = 600;
71             break;
72         case ADC_ATTEN_DB_2_5:
73             parsed_data_storage->efuse_data.ver2.adc_calib_high_voltage = 800;
74             break;
75         case ADC_ATTEN_DB_6:
76             parsed_data_storage->efuse_data.ver2.adc_calib_high_voltage = 1000;
77             break;
78         case ADC_ATTEN_DB_11:
79             parsed_data_storage->efuse_data.ver2.adc_calib_high_voltage = 2000;
80             break;
81         default:
82             break;
83         }
84         break;
85     default:
86         // fall back to case 1 with zeros as params.
87         parsed_data_storage->version_num = 1;
88         tag = esp_efuse_rtc_table_get_tag(version_num, adc_num, atten, RTCCALIB_V1_PARAM_VLOW);
89         parsed_data_storage->efuse_data.ver1.adc_calib_high = esp_efuse_rtc_table_get_parsed_efuse_value(tag, true);
90         tag = esp_efuse_rtc_table_get_tag(version_num, adc_num, atten, RTCCALIB_V1_PARAM_VHIGH);
91         parsed_data_storage->efuse_data.ver1.adc_calib_low = esp_efuse_rtc_table_get_parsed_efuse_value(tag, true);
92         break;
93     }
94     return true;
95 }
96 
97 /* ----------------------- Characterization Functions ----------------------- */
98 /**
99  *  (Used in V1 of calibration scheme)
100  *  The Two Point calibration measures the reading at two specific input voltages, and calculates the (assumed linear) relation
101  *  between input voltage and ADC response. (Response = A * Vinput + B)
102  *  A and B are scaled ints.
103  *  @param high The ADC response at the higher voltage of the corresponding attenuation (600mV, 800mV, 1000mV, 2000mV).
104  *  @param low The ADC response at the lower voltage of the corresponding attenuation (all 250mV).
105  *
106  */
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)107 static void characterize_using_two_point(adc_unit_t adc_num,
108         adc_atten_t atten,
109         uint32_t high,
110         uint32_t low,
111         uint32_t *coeff_a,
112         uint32_t *coeff_b)
113 {
114     // once we have recovered the reference high(Dhigh) and low(Dlow) readings, we can calculate a and b from
115     // the measured high and low readings
116     static const uint32_t v_high[] = {600, 800, 1000, 2000};
117     static const uint32_t v_low = 250;
118     *coeff_a = coeff_a_scaling * (v_high[atten] - v_low) / (high - low);
119     *coeff_b = coeff_b_scaling * (v_low * high - v_high[atten] * low) / (high - low);
120 }
121 
122 /*
123  * Estimate the (assumed) linear relationship btwn the measured raw value and the voltage
124  * with the previously done measurement when the chip was manufactured.
125  * */
calculate_characterization_coefficients(const adc_calib_parsed_info * parsed_data,esp_adc_cal_characteristics_t * chars)126 static bool calculate_characterization_coefficients(const adc_calib_parsed_info *parsed_data, esp_adc_cal_characteristics_t *chars)
127 {
128     switch (parsed_data->version_num) {
129     case 1:
130         ESP_LOGD(LOG_TAG, "Calib V1, low%dmV, high%dmV\n", parsed_data->efuse_data.ver1.adc_calib_low, parsed_data->efuse_data.ver1.adc_calib_high);
131 
132         characterize_using_two_point(parsed_data->adc_num, parsed_data->atten_level,
133                                      parsed_data->efuse_data.ver1.adc_calib_high, parsed_data->efuse_data.ver1.adc_calib_low,
134                                      &(chars->coeff_a), &(chars->coeff_b));
135         break;
136     case 2:
137         ESP_LOGD(LOG_TAG, "Calib V2, volt%dmV\n", parsed_data->efuse_data.ver2.adc_calib_high);
138         chars->coeff_a = coeff_a_scaling * parsed_data->efuse_data.ver2.adc_calib_high_voltage /
139                          parsed_data->efuse_data.ver2.adc_calib_high;
140         chars->coeff_b = 0;
141         break;
142     default:
143         return false;
144         break;
145     }
146     return true;
147 }
148 
149 /* ------------------------- Public API ------------------------------------- */
esp_adc_cal_check_efuse(esp_adc_cal_value_t source)150 esp_err_t esp_adc_cal_check_efuse(esp_adc_cal_value_t source)
151 {
152     if (source != ESP_ADC_CAL_VAL_EFUSE_TP) {
153         return ESP_ERR_NOT_SUPPORTED;
154     }
155     uint8_t adc_encoding_version = esp_efuse_rtc_table_read_calib_version();
156     if (adc_encoding_version != 1 && adc_encoding_version != 2) {
157         // current version only accepts encoding ver 1 and ver 2.
158         return ESP_ERR_INVALID_VERSION;
159     }
160     return ESP_OK;
161 }
162 
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)163 esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num,
164         adc_atten_t atten,
165         adc_bits_width_t bit_width,
166         uint32_t default_vref,
167         esp_adc_cal_characteristics_t *chars)
168 {
169     bool res __attribute__((unused));
170     adc_calib_parsed_info efuse_parsed_data = {0};
171     // Check parameters
172     assert((adc_num == ADC_UNIT_1) || (adc_num == ADC_UNIT_2));
173     assert(chars != NULL);
174     assert(bit_width == ADC_WIDTH_BIT_13);
175 
176     // make sure adc is calibrated.
177     res = prepare_calib_data_for(adc_num, atten, &efuse_parsed_data);
178     assert(res);
179     res = calculate_characterization_coefficients(&efuse_parsed_data, chars);
180     assert(res);
181     ESP_LOGD(LOG_TAG, "adc%d (atten leven %d) calibration done: A:%"PRId32" B:%"PRId32"\n", adc_num, atten, chars->coeff_a, chars->coeff_b);
182 
183     // Initialize remaining fields
184     chars->adc_num = adc_num;
185     chars->atten = atten;
186     chars->bit_width = bit_width;
187 
188     // these values are not used as the corresponding calibration themes are deprecated.
189     chars->vref = 0;
190     chars->low_curve = NULL;
191     chars->high_curve = NULL;
192 
193     // in esp32s2 we only use the two point method to calibrate the adc.
194     return ESP_ADC_CAL_VAL_EFUSE_TP;
195 }
196 
esp_adc_cal_raw_to_voltage(uint32_t adc_reading,const esp_adc_cal_characteristics_t * chars)197 uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_characteristics_t *chars)
198 {
199     assert(chars != NULL);
200     return adc_reading * chars->coeff_a / coeff_a_scaling + chars->coeff_b / coeff_b_scaling;
201 }
202