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