1 /*
2 * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdint.h>
8 #include <stdbool.h>
9 #include <string.h>
10 #include "esp_types.h"
11 #include "esp_err.h"
12 #include "esp_log.h"
13 #include "esp_check.h"
14 #include "esp_heap_caps.h"
15 #include "soc/soc_caps.h"
16 #include "esp_adc/adc_cali_scheme.h"
17 #include "adc_cali_interface.h"
18 #include "esp_private/adc_share_hw_ctrl.h"
19
20 #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
21 #include "esp_efuse_rtc_calib.h"
22 #include "curve_fitting_coefficients.h"
23
24 const __attribute__((unused)) static char *TAG = "adc_cali";
25
26 // coeff_a is actually a float number
27 // it is scaled to put them into uint32_t so that the headers do not have to be changed
28 static const int coeff_a_scaling = 65536;
29
30 /* -------------------- Characterization Helper Data Types ------------------ */
31 typedef struct {
32 uint32_t voltage;
33 uint32_t digi;
34 } adc_calib_data_ver1_t;
35
36 typedef struct {
37 char version_num;
38 adc_unit_t unit_id;
39 adc_atten_t atten;
40 union {
41 adc_calib_data_ver1_t ver1;
42 } ref_data;
43 } adc_calib_info_t;
44
45 /* ------------------------ Context Structure--------------------------- */
46 typedef struct {
47 uint32_t coeff_a; ///< Gradient of ADC-Voltage curve
48 uint32_t coeff_b; ///< Offset of ADC-Voltage curve
49 } cali_chars_first_step_t;
50
51 typedef struct {
52 adc_unit_t unit_id; ///< ADC unit
53 adc_channel_t chan; ///< ADC channel
54 adc_atten_t atten; ///< ADC attenuation
55 cali_chars_first_step_t chars_first_step; ///< Calibration first step characteristics
56 cali_chars_second_step_t chars_second_step; ///< Calibration second step characteristics
57 } cali_chars_curve_fitting_t;
58
59 /* ----------------------- Characterization Functions ----------------------- */
60 static void get_first_step_reference_point(int version_num, adc_unit_t unit_id, adc_atten_t atten, adc_calib_info_t *calib_info);
61 static void calc_first_step_coefficients(const adc_calib_info_t *parsed_data, cali_chars_curve_fitting_t *chars);
62 static int32_t get_reading_error(uint64_t v_cali_1, const cali_chars_second_step_t *param, adc_atten_t atten);
63 static esp_err_t check_valid(const adc_cali_curve_fitting_config_t *config);
64
65 /* ------------------------ Interface Functions --------------------------- */
66 static esp_err_t cali_raw_to_voltage(void *arg, int raw, int *voltage);
67
68 /* ------------------------- Public API ------------------------------------- */
adc_cali_create_scheme_curve_fitting(const adc_cali_curve_fitting_config_t * config,adc_cali_handle_t * ret_handle)69 esp_err_t adc_cali_create_scheme_curve_fitting(const adc_cali_curve_fitting_config_t *config, adc_cali_handle_t *ret_handle)
70 {
71 esp_err_t ret = ESP_OK;
72 ESP_RETURN_ON_FALSE(config && ret_handle, ESP_ERR_INVALID_ARG, TAG, "invalid arg: null pointer");
73 ret = check_valid(config);
74 if (ret != ESP_OK) {
75 return ret;
76 }
77 // current version only accepts encoding version: ESP_EFUSE_ADC_CALIB_VER_MIN <= adc_encoding_version <= ESP_EFUSE_ADC_CALIB_VER_MAX.
78 uint32_t adc_encoding_version = esp_efuse_rtc_calib_get_ver();
79 ESP_RETURN_ON_FALSE((adc_encoding_version >= ESP_EFUSE_ADC_CALIB_VER_MIN) &&
80 (adc_encoding_version <= ESP_EFUSE_ADC_CALIB_VER_MAX), ESP_ERR_NOT_SUPPORTED, TAG, "Calibration required eFuse bits not burnt");
81
82 adc_cali_scheme_t *scheme = (adc_cali_scheme_t *)heap_caps_calloc(1, sizeof(adc_cali_scheme_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
83 ESP_RETURN_ON_FALSE(scheme, ESP_ERR_NO_MEM, TAG, "no mem for adc calibration scheme");
84
85 cali_chars_curve_fitting_t *chars = (cali_chars_curve_fitting_t *)heap_caps_calloc(1, sizeof(cali_chars_curve_fitting_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
86 ESP_GOTO_ON_FALSE(chars, ESP_ERR_NO_MEM, err, TAG, "no memory for the calibration characteristics");
87
88 scheme->raw_to_voltage = cali_raw_to_voltage;
89 scheme->ctx = chars;
90
91 //Prepare calibration characteristics
92 adc_calib_info_t calib_info = {0};
93 //Set first step calibration context
94 get_first_step_reference_point(adc_encoding_version, config->unit_id, config->atten, &calib_info);
95 calc_first_step_coefficients(&calib_info, chars);
96 //Set second step calibration context
97 curve_fitting_get_second_step_coeff(config, &(chars->chars_second_step));
98 chars->unit_id = config->unit_id;
99 chars->chan = config->chan;
100 chars->atten = config->atten;
101
102 *ret_handle = scheme;
103
104 return ESP_OK;
105
106 err:
107 if (scheme) {
108 heap_caps_free(scheme);
109 }
110 return ret;
111 }
112
adc_cali_delete_scheme_curve_fitting(adc_cali_handle_t handle)113 esp_err_t adc_cali_delete_scheme_curve_fitting(adc_cali_handle_t handle)
114 {
115 ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
116
117 heap_caps_free(handle->ctx);
118 handle->ctx = NULL;
119
120 heap_caps_free(handle);
121 handle = NULL;
122
123 return ESP_OK;
124 }
125
126 /* ------------------------ Interface Functions --------------------------- */
cali_raw_to_voltage(void * arg,int raw,int * voltage)127 static esp_err_t cali_raw_to_voltage(void *arg, int raw, int *voltage)
128 {
129 //pointers are checked in the upper layer
130
131 cali_chars_curve_fitting_t *ctx = arg;
132
133 #if SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
134 int chan_compensation = adc_get_hw_calibration_chan_compens(ctx->unit_id, ctx->chan, ctx->atten);
135 raw -= chan_compensation;
136 /* Limit the range */
137 int max_val = (1L << SOC_ADC_RTC_MAX_BITWIDTH) - 1;
138 raw = raw <= 0 ? 0 :
139 raw > max_val ? max_val : raw;
140 #endif // SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
141
142 uint64_t v_cali_1 = (uint64_t)raw * ctx->chars_first_step.coeff_a / coeff_a_scaling + ctx->chars_first_step.coeff_b;
143 int32_t error = get_reading_error(v_cali_1, &(ctx->chars_second_step), ctx->atten);
144
145 *voltage = (int32_t)v_cali_1 - error;
146
147 return ESP_OK;
148 }
149
150 /* ----------------------- Characterization Functions ----------------------- */
151 //To get the reference point (Dout, Vin)
get_first_step_reference_point(int version_num,adc_unit_t unit_id,adc_atten_t atten,adc_calib_info_t * calib_info)152 static void get_first_step_reference_point(int version_num, adc_unit_t unit_id, adc_atten_t atten, adc_calib_info_t *calib_info)
153 {
154 assert((version_num >= ESP_EFUSE_ADC_CALIB_VER_MIN) &&
155 (version_num <= ESP_EFUSE_ADC_CALIB_VER_MAX));
156 esp_err_t ret;
157
158 calib_info->version_num = version_num;
159 calib_info->unit_id = unit_id;
160 calib_info->atten = atten;
161
162 uint32_t voltage = 0;
163 uint32_t digi = 0;
164 ret = esp_efuse_rtc_calib_get_cal_voltage(version_num, unit_id, (int)atten, &digi, &voltage);
165 assert(ret == ESP_OK);
166 calib_info->ref_data.ver1.voltage = voltage;
167 calib_info->ref_data.ver1.digi = digi;
168 }
169
170 /*
171 * Estimate the (assumed) linear relationship btwn the measured raw value and the voltage
172 * with the previously done measurement when the chip was manufactured.
173 */
calc_first_step_coefficients(const adc_calib_info_t * parsed_data,cali_chars_curve_fitting_t * ctx)174 static void calc_first_step_coefficients(const adc_calib_info_t *parsed_data, cali_chars_curve_fitting_t *ctx)
175 {
176 ctx->chars_first_step.coeff_a = coeff_a_scaling * parsed_data->ref_data.ver1.voltage / parsed_data->ref_data.ver1.digi;
177 ctx->chars_first_step.coeff_b = 0;
178 ESP_LOGV(TAG, "Calib V1, Cal Voltage = %"PRId32", Digi out = %"PRId32", Coef_a = %"PRId32"\n", parsed_data->ref_data.ver1.voltage, parsed_data->ref_data.ver1.digi, ctx->chars_first_step.coeff_a);
179 }
180
181
get_reading_error(uint64_t v_cali_1,const cali_chars_second_step_t * param,adc_atten_t atten)182 static int32_t get_reading_error(uint64_t v_cali_1, const cali_chars_second_step_t *param, adc_atten_t atten)
183 {
184 if (v_cali_1 == 0) {
185 return 0;
186 }
187 uint8_t term_num = param->term_num;
188 int32_t error = 0;
189 uint64_t coeff = 0;
190
191 uint64_t *term = (uint64_t *)heap_caps_calloc(term_num, sizeof(uint64_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
192 if (term == NULL) {
193 ESP_LOGE(TAG, "Memory allocation failed for 'term'");
194 return -1;
195 }
196
197 memset(term, 0, term_num * sizeof(uint64_t));
198
199 uint64_t variable_prev = 1;
200 coeff = (*param->coeff)[atten][0][0];
201 term[0] = variable_prev * coeff / (*param->coeff)[atten][0][1];
202 error = (int32_t)(term[0] * (*param->sign)[atten][0]);
203
204 for (int i = 1; i < term_num; i++) {
205 uint64_t variable_current = variable_prev * v_cali_1;
206 coeff = (*param->coeff)[atten][i][0];
207 term[i] = variable_current * coeff;
208 ESP_LOGV(TAG, "big coef is %llu, big term%d is %llu, coef_id is %d", coeff, i, term[i], i);
209 term[i] = term[i] / (*param->coeff)[atten][i][1];
210 error += (int32_t)(term[i] * (*param->sign)[atten][i]);
211
212 ESP_LOGV(TAG, "term%d is %llu, error is %"PRId32, i, term[i], error);
213 variable_prev = variable_current;
214 }
215
216 heap_caps_free(term);
217
218 return error;
219 }
220
check_valid(const adc_cali_curve_fitting_config_t * config)221 static esp_err_t check_valid(const adc_cali_curve_fitting_config_t *config)
222 {
223 ESP_RETURN_ON_FALSE(config->unit_id < SOC_ADC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid ADC unit");
224 ESP_RETURN_ON_FALSE(config->atten < SOC_ADC_ATTEN_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid ADC attenuation");
225
226 bool available_oneshot_bitwidth = (config->bitwidth >= SOC_ADC_RTC_MIN_BITWIDTH && config->bitwidth <= SOC_ADC_RTC_MAX_BITWIDTH);
227 bool available_dma_bitwidth = (config->bitwidth >= SOC_ADC_DIGI_MIN_BITWIDTH && config->bitwidth <= SOC_ADC_DIGI_MAX_BITWIDTH);
228 bool default_bitwidth_mark = (config->bitwidth == ADC_BITWIDTH_DEFAULT);
229 bool available_bitwidth = (available_oneshot_bitwidth || available_dma_bitwidth || default_bitwidth_mark);
230 ESP_RETURN_ON_FALSE(available_bitwidth, ESP_ERR_INVALID_ARG, TAG, "invalid bitwidth");
231
232 return ESP_OK;
233 }
234
235 #endif //#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
236