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