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