/* * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include "esp_adc/adc_cali_scheme.h" #include "ntc_driver.h" #define NTC_DRIVER_CHECK(a, str, ret_val) \ if (!(a)) { \ ESP_LOGW(TAG, "%s:(%d): %s", __FUNCTION__, __LINE__, str);\ return (ret_val); \ } #define NTC_DRIVER_CHECK_GOTO(a, str, label) \ if (!(a)) { \ ESP_LOGW(TAG, "%s:(%d): %s", __FUNCTION__, __LINE__, str);\ goto label; \ } const static char *TAG = "ntc driver"; typedef struct ntc_driver { ntc_config_t s_ntc_config; adc_oneshot_unit_handle_t adc_handle; adc_cali_handle_t adc_cali_handle; } ntc_driver_dev_t; static bool if_init_adc_unit = false; static esp_err_t adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out_handle) { esp_err_t ret = ESP_FAIL; #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting"); adc_cali_curve_fitting_config_t cali_config = { .unit_id = unit, .atten = atten, .bitwidth = ADC_BITWIDTH_DEFAULT, }; ret = adc_cali_create_scheme_curve_fitting(&cali_config, out_handle); #endif #if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting"); adc_cali_line_fitting_config_t cali_config = { .unit_id = unit, .atten = atten, .bitwidth = ADC_BITWIDTH_DEFAULT, }; ret = adc_cali_create_scheme_line_fitting(&cali_config, out_handle); #endif if (ret == ESP_OK) { ESP_LOGI(TAG, "Calibration Success"); } else if (ret == ESP_ERR_NOT_SUPPORTED) { ESP_LOGW(TAG, "eFuse not burnt, skip software calibration"); } else { ESP_LOGE(TAG, "Invalid arg or no memory"); } return ret; } esp_err_t ntc_dev_create(ntc_config_t *config, ntc_device_handle_t *ntc_handle, adc_oneshot_unit_handle_t *adc_handle) { ESP_LOGI(TAG, "IoT Ntc Driver Version: %d.%d.%d", NTC_DRIVER_VER_MAJOR, NTC_DRIVER_VER_MINOR, NTC_DRIVER_VER_PATCH); esp_err_t ret = ESP_OK; if (ntc_handle == NULL || config == NULL) { ESP_LOGW(TAG, "arg is invalid"); return ESP_ERR_INVALID_ARG; } ntc_driver_dev_t *ndd = (ntc_driver_dev_t *) calloc(1, sizeof(ntc_driver_dev_t)); if (ndd == NULL) { ESP_LOGW(TAG, "Calloc device failed"); return ESP_ERR_NO_MEM; } ndd->s_ntc_config = *config; if (adc_handle != NULL && *adc_handle != NULL) { ndd->adc_handle = *adc_handle; } else { adc_oneshot_unit_init_cfg_t init_config1 = { .unit_id = config->unit, }; ret = adc_oneshot_new_unit(&init_config1, &ndd->adc_handle); NTC_DRIVER_CHECK_GOTO(ret == ESP_OK, "adc oneshot new unit fail!", init_fail); if_init_adc_unit = true; if (adc_handle != NULL) { *adc_handle = ndd->adc_handle; } } adc_oneshot_chan_cfg_t channel_config = { .bitwidth = ADC_BITWIDTH_DEFAULT, .atten = config->atten, }; ret = adc_oneshot_config_channel(ndd->adc_handle, config->channel, &channel_config); NTC_DRIVER_CHECK_GOTO(ret == ESP_OK, "adc oneshot config channel fail!", init_fail); ret = adc_calibration_init(config->unit, config->atten, &ndd->adc_cali_handle); NTC_DRIVER_CHECK_GOTO(ret == ESP_OK, "adc calibration init fail!", init_fail); *ntc_handle = (ntc_device_handle_t)ndd; return ret; init_fail: if (if_init_adc_unit) { adc_oneshot_del_unit(ndd->adc_handle); } free(ndd); ndd = NULL; return ret; } esp_err_t ntc_dev_get_adc_handle(ntc_device_handle_t ntc_handle, adc_oneshot_unit_handle_t *adc_handle) { esp_err_t ret = ESP_OK; if (ntc_handle == NULL) { ESP_LOGW(TAG, "Pointer of handle is invalid"); return ESP_ERR_INVALID_ARG; } ntc_driver_dev_t *ndd = (ntc_driver_dev_t *)ntc_handle; *adc_handle = ndd->adc_handle; return ret; } esp_err_t ntc_dev_delete(ntc_device_handle_t ntc_handle) { esp_err_t ret = ESP_OK; if (ntc_handle == NULL) { ESP_LOGW(TAG, "Pointer of handle is invalid"); return ESP_ERR_INVALID_ARG; } ntc_driver_dev_t *ndd = (ntc_driver_dev_t *)ntc_handle; #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED ret = adc_cali_delete_scheme_curve_fitting(ndd->adc_cali_handle); NTC_DRIVER_CHECK(ret == ESP_OK, "adc curve calibration deinit fail", ESP_FAIL); #endif #if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED ret = adc_cali_delete_scheme_line_fitting(ndd->adc_cali_handle); NTC_DRIVER_CHECK(ret == ESP_OK, "adc line calibration deinit fail", ESP_FAIL); #endif if (if_init_adc_unit) { ret = adc_oneshot_del_unit(ndd->adc_handle); NTC_DRIVER_CHECK(ret == ESP_OK, "adc oneshot deinit fail", ESP_FAIL); } free(ndd); ndd = NULL; return ret; } static esp_err_t ntc_voltage_to_temperature(ntc_device_handle_t ntc_handle, uint32_t voltage_mv, float *temperature) { ntc_driver_dev_t *ndd = (ntc_driver_dev_t *)ntc_handle; if (ndd->s_ntc_config.b_value && voltage_mv) { uint32_t r_ntc_ohm = 0; if (ndd->s_ntc_config.circuit_mode == CIRCUIT_MODE_NTC_VCC) { r_ntc_ohm = (ndd->s_ntc_config.vdd_mv - voltage_mv) * ndd->s_ntc_config.fixed_ohm / voltage_mv; } else if (ndd->s_ntc_config.circuit_mode == CIRCUIT_MODE_NTC_GND) { r_ntc_ohm = voltage_mv * ndd->s_ntc_config.fixed_ohm / (ndd->s_ntc_config.vdd_mv - voltage_mv); } *temperature = 1.0 / (log(1.0 * r_ntc_ohm / ndd->s_ntc_config.r25_ohm) * 1.0 / ndd->s_ntc_config.b_value + 1.0 / 298.15) - 273.0; return ESP_OK; } return ESP_FAIL; } esp_err_t ntc_dev_get_temperature(ntc_device_handle_t ntc_handle, float *temperature) { int voltage, adc_raw = 0; if (ntc_handle == NULL) { ESP_LOGW(TAG, "Pointer of handle is invalid"); return ESP_ERR_INVALID_ARG; } ntc_driver_dev_t *ndd = (ntc_driver_dev_t *)ntc_handle; esp_err_t ret = adc_oneshot_read(ndd->adc_handle, ndd->s_ntc_config.channel, &adc_raw); NTC_DRIVER_CHECK(ret == ESP_OK, "adc oneshot read fail", ESP_FAIL); NTC_DRIVER_CHECK(adc_raw != 0, "adc raw data equal to 0", ESP_FAIL); ret = adc_cali_raw_to_voltage(ndd->adc_cali_handle, adc_raw, &voltage); NTC_DRIVER_CHECK(ret == ESP_OK, "adc calibration raw to voltage fail", ESP_FAIL); NTC_DRIVER_CHECK(voltage != 0, "voltage equal to 0", ESP_FAIL); ntc_voltage_to_temperature(ndd, voltage, temperature); return ESP_OK; }