2021-04-29 18:49:04 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2021 Eug Krashtan
|
2022-01-10 17:53:09 +01:00
|
|
|
* Copyright (c) 2022 Wouter Cappelle
|
2021-04-29 18:49:04 +03:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
2022-05-06 10:25:46 +02:00
|
|
|
#include <zephyr/device.h>
|
2022-07-11 23:31:06 +02:00
|
|
|
#include <zephyr/devicetree.h>
|
2022-05-06 10:25:46 +02:00
|
|
|
#include <zephyr/drivers/sensor.h>
|
|
|
|
#include <zephyr/drivers/adc.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
2023-09-06 15:06:04 +02:00
|
|
|
#include <stm32_ll_adc.h>
|
2023-04-24 12:22:10 +02:00
|
|
|
#if defined(CONFIG_SOC_SERIES_STM32H5X)
|
|
|
|
#include <stm32_ll_icache.h>
|
|
|
|
#endif /* CONFIG_SOC_SERIES_STM32H5X */
|
2021-04-29 18:49:04 +03:00
|
|
|
|
|
|
|
LOG_MODULE_REGISTER(stm32_temp, CONFIG_SENSOR_LOG_LEVEL);
|
2022-06-17 14:11:56 +02:00
|
|
|
#define CAL_RES 12
|
2022-01-10 17:53:09 +01:00
|
|
|
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_temp)
|
2021-04-29 18:49:04 +03:00
|
|
|
#define DT_DRV_COMPAT st_stm32_temp
|
2022-01-10 17:53:09 +01:00
|
|
|
#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_temp_cal)
|
|
|
|
#define DT_DRV_COMPAT st_stm32_temp_cal
|
2023-04-01 18:13:33 +02:00
|
|
|
#define HAS_DUAL_CALIBRATION 1
|
|
|
|
#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32c0_temp_cal)
|
|
|
|
#define DT_DRV_COMPAT st_stm32c0_temp_cal
|
|
|
|
#define HAS_SINGLE_CALIBRATION 1
|
2022-01-10 17:53:09 +01:00
|
|
|
#else
|
|
|
|
#error "No compatible devicetree node found"
|
|
|
|
#endif
|
2021-04-29 18:49:04 +03:00
|
|
|
|
2023-04-01 18:13:33 +02:00
|
|
|
#if defined(HAS_SINGLE_CALIBRATION) || defined(HAS_DUAL_CALIBRATION)
|
|
|
|
#define HAS_CALIBRATION 1
|
|
|
|
#endif
|
|
|
|
|
2021-04-29 18:49:04 +03:00
|
|
|
struct stm32_temp_data {
|
|
|
|
const struct device *adc;
|
2022-03-22 12:27:45 +08:00
|
|
|
const struct adc_channel_cfg adc_cfg;
|
2023-09-06 15:06:04 +02:00
|
|
|
ADC_TypeDef *adc_base;
|
2021-04-29 18:49:04 +03:00
|
|
|
struct adc_sequence adc_seq;
|
|
|
|
struct k_mutex mutex;
|
|
|
|
int16_t sample_buffer;
|
2022-01-10 17:38:20 +01:00
|
|
|
int16_t raw; /* raw adc Sensor value */
|
2021-04-29 18:49:04 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
struct stm32_temp_config {
|
2022-01-10 17:53:09 +01:00
|
|
|
#if HAS_CALIBRATION
|
|
|
|
uint16_t *cal1_addr;
|
|
|
|
int cal1_temp;
|
2023-04-01 18:13:33 +02:00
|
|
|
#if HAS_DUAL_CALIBRATION
|
|
|
|
uint16_t *cal2_addr;
|
2022-01-10 17:53:09 +01:00
|
|
|
int cal2_temp;
|
2023-04-01 18:13:33 +02:00
|
|
|
#else
|
|
|
|
int avgslope;
|
|
|
|
#endif
|
2022-01-10 17:53:09 +01:00
|
|
|
int cal_vrefanalog;
|
2022-06-17 14:11:56 +02:00
|
|
|
int ts_cal_shift;
|
2022-01-10 17:53:09 +01:00
|
|
|
#else
|
2021-04-29 18:49:04 +03:00
|
|
|
int avgslope;
|
|
|
|
int v25_mv;
|
2022-01-10 17:53:09 +01:00
|
|
|
#endif
|
2023-04-13 10:55:24 +02:00
|
|
|
bool is_ntc;
|
2021-04-29 18:49:04 +03:00
|
|
|
};
|
|
|
|
|
2022-01-10 17:42:31 +01:00
|
|
|
static int stm32_temp_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
2021-04-29 18:49:04 +03:00
|
|
|
{
|
|
|
|
struct stm32_temp_data *data = dev->data;
|
|
|
|
struct adc_sequence *sp = &data->adc_seq;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
k_mutex_lock(&data->mutex, K_FOREVER);
|
|
|
|
|
2022-03-22 12:27:45 +08:00
|
|
|
rc = adc_channel_setup(data->adc, &data->adc_cfg);
|
|
|
|
if (rc) {
|
|
|
|
LOG_DBG("Setup AIN%u got %d", data->adc_cfg.channel_id, rc);
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
2023-09-06 15:06:04 +02:00
|
|
|
LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(data->adc_base),
|
|
|
|
LL_ADC_PATH_INTERNAL_TEMPSENSOR);
|
|
|
|
k_usleep(LL_ADC_DELAY_TEMPSENSOR_STAB_US);
|
|
|
|
|
2021-04-29 18:49:04 +03:00
|
|
|
rc = adc_read(data->adc, sp);
|
|
|
|
if (rc == 0) {
|
2022-01-10 17:38:20 +01:00
|
|
|
data->raw = data->sample_buffer;
|
2021-04-29 18:49:04 +03:00
|
|
|
}
|
|
|
|
|
2022-03-22 12:27:45 +08:00
|
|
|
unlock:
|
2021-04-29 18:49:04 +03:00
|
|
|
k_mutex_unlock(&data->mutex);
|
|
|
|
|
2022-03-22 12:27:45 +08:00
|
|
|
return rc;
|
2021-04-29 18:49:04 +03:00
|
|
|
}
|
|
|
|
|
2022-01-10 17:42:31 +01:00
|
|
|
static int stm32_temp_channel_get(const struct device *dev, enum sensor_channel chan,
|
|
|
|
struct sensor_value *val)
|
2021-04-29 18:49:04 +03:00
|
|
|
{
|
|
|
|
struct stm32_temp_data *data = dev->data;
|
|
|
|
const struct stm32_temp_config *cfg = dev->config;
|
|
|
|
float temp;
|
|
|
|
|
|
|
|
if (chan != SENSOR_CHAN_DIE_TEMP) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2022-01-10 17:53:09 +01:00
|
|
|
#if HAS_CALIBRATION
|
2023-04-24 12:22:10 +02:00
|
|
|
|
|
|
|
#if defined(CONFIG_SOC_SERIES_STM32H5X)
|
|
|
|
LL_ICACHE_Disable();
|
|
|
|
#endif /* CONFIG_SOC_SERIES_STM32H5X */
|
|
|
|
|
2022-06-30 21:25:22 +02:00
|
|
|
temp = ((float)data->raw * adc_ref_internal(data->adc)) / cfg->cal_vrefanalog;
|
2022-06-17 14:11:56 +02:00
|
|
|
temp -= (*cfg->cal1_addr >> cfg->ts_cal_shift);
|
2023-04-01 18:13:33 +02:00
|
|
|
#if HAS_SINGLE_CALIBRATION
|
2023-04-13 10:55:24 +02:00
|
|
|
if (cfg->is_ntc) {
|
|
|
|
temp = -temp;
|
|
|
|
}
|
2023-04-01 18:13:33 +02:00
|
|
|
temp /= (cfg->avgslope * 4096) / (cfg->cal_vrefanalog * 1000);
|
|
|
|
#else
|
2022-01-10 17:53:09 +01:00
|
|
|
temp *= (cfg->cal2_temp - cfg->cal1_temp);
|
2022-06-17 14:11:56 +02:00
|
|
|
temp /= ((*cfg->cal2_addr - *cfg->cal1_addr) >> cfg->ts_cal_shift);
|
2023-04-01 18:13:33 +02:00
|
|
|
#endif
|
drivers: sensor: stm32_temp: drop ts-cal-offset property
According to the formulas found in the reference manuals of the SoC
families using the "st,stm32-temp-cal" version of the temperature sensor
(i.e. G0, G4, H7, L0, L1, L4, L5, U5, WB, WL), the temperature is
computed with the following formula:
T = ((TS_CAL2_TEMP - TS_CAL1_TEMP) / (TS_CAL2 - TS_CAL1))
* (TS_DATA - TS_CAL1) + TS_CAL1_TEMP
What is called ts-cal-offset in the stm32_temp driver is therefore the
same value as TS_CAL1_TEMP1. Use it directly instead of defining another
property.
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
2022-06-30 21:40:50 +02:00
|
|
|
temp += cfg->cal1_temp;
|
2023-04-24 12:22:10 +02:00
|
|
|
|
|
|
|
#if defined(CONFIG_SOC_SERIES_STM32H5X)
|
|
|
|
LL_ICACHE_Enable();
|
|
|
|
#endif /* CONFIG_SOC_SERIES_STM32H5X */
|
|
|
|
|
2022-01-10 17:53:09 +01:00
|
|
|
#else
|
2022-06-30 21:25:22 +02:00
|
|
|
/* Sensor value in millivolts */
|
|
|
|
int32_t mv = data->raw * adc_ref_internal(data->adc) / 0x0FFF;
|
2022-01-10 17:38:20 +01:00
|
|
|
|
2021-04-29 18:49:04 +03:00
|
|
|
if (cfg->is_ntc) {
|
2022-01-10 17:38:20 +01:00
|
|
|
temp = (float)(cfg->v25_mv - mv);
|
2021-04-29 18:49:04 +03:00
|
|
|
} else {
|
2022-01-10 17:38:20 +01:00
|
|
|
temp = (float)(mv - cfg->v25_mv);
|
2021-04-29 18:49:04 +03:00
|
|
|
}
|
2022-01-10 17:42:31 +01:00
|
|
|
temp = (temp / cfg->avgslope) * 10;
|
2021-04-29 18:49:04 +03:00
|
|
|
temp += 25;
|
2022-01-10 17:53:09 +01:00
|
|
|
#endif
|
|
|
|
|
2021-10-11 11:32:40 +02:00
|
|
|
return sensor_value_from_double(val, temp);
|
2021-04-29 18:49:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct sensor_driver_api stm32_temp_driver_api = {
|
|
|
|
.sample_fetch = stm32_temp_sample_fetch,
|
|
|
|
.channel_get = stm32_temp_channel_get,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int stm32_temp_init(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct stm32_temp_data *data = dev->data;
|
|
|
|
struct adc_sequence *asp = &data->adc_seq;
|
|
|
|
|
|
|
|
k_mutex_init(&data->mutex);
|
|
|
|
|
|
|
|
if (!device_is_ready(data->adc)) {
|
|
|
|
LOG_ERR("Device %s is not ready", data->adc->name);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
*asp = (struct adc_sequence){
|
2022-03-22 12:27:45 +08:00
|
|
|
.channels = BIT(data->adc_cfg.channel_id),
|
2021-04-29 18:49:04 +03:00
|
|
|
.buffer = &data->sample_buffer,
|
|
|
|
.buffer_size = sizeof(data->sample_buffer),
|
2022-03-22 12:27:45 +08:00
|
|
|
.resolution = 12U,
|
2021-04-29 18:49:04 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-04-01 17:30:48 +02:00
|
|
|
static struct stm32_temp_data stm32_temp_dev_data = {
|
|
|
|
.adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(0)),
|
2023-09-06 15:06:04 +02:00
|
|
|
.adc_base = (ADC_TypeDef *)DT_REG_ADDR(DT_INST_IO_CHANNELS_CTLR(0)),
|
2023-04-01 17:30:48 +02:00
|
|
|
.adc_cfg = {
|
|
|
|
.gain = ADC_GAIN_1,
|
|
|
|
.reference = ADC_REF_INTERNAL,
|
|
|
|
.acquisition_time = ADC_ACQ_TIME_MAX,
|
|
|
|
.channel_id = DT_INST_IO_CHANNELS_INPUT(0),
|
|
|
|
.differential = 0
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct stm32_temp_config stm32_temp_dev_config = {
|
|
|
|
#if HAS_CALIBRATION
|
|
|
|
.cal1_addr = (uint16_t *)DT_INST_PROP(0, ts_cal1_addr),
|
|
|
|
.cal1_temp = DT_INST_PROP(0, ts_cal1_temp),
|
2023-04-01 18:13:33 +02:00
|
|
|
#if HAS_DUAL_CALIBRATION
|
|
|
|
.cal2_addr = (uint16_t *)DT_INST_PROP(0, ts_cal2_addr),
|
2023-04-01 17:30:48 +02:00
|
|
|
.cal2_temp = DT_INST_PROP(0, ts_cal2_temp),
|
2023-04-01 18:13:33 +02:00
|
|
|
#else
|
|
|
|
.avgslope = DT_INST_PROP(0, avgslope),
|
|
|
|
#endif
|
2023-04-01 17:30:48 +02:00
|
|
|
.ts_cal_shift = (DT_INST_PROP(0, ts_cal_resolution) - CAL_RES),
|
|
|
|
.cal_vrefanalog = DT_INST_PROP(0, ts_cal_vrefanalog),
|
|
|
|
#else
|
|
|
|
.avgslope = DT_INST_PROP(0, avgslope),
|
|
|
|
.v25_mv = DT_INST_PROP(0, v25),
|
|
|
|
#endif
|
2023-04-13 10:55:24 +02:00
|
|
|
.is_ntc = DT_INST_PROP_OR(0, ntc, false)
|
2023-04-01 17:30:48 +02:00
|
|
|
};
|
2022-06-17 14:11:56 +02:00
|
|
|
|
2023-04-01 17:30:48 +02:00
|
|
|
SENSOR_DEVICE_DT_INST_DEFINE(0, stm32_temp_init, NULL,
|
|
|
|
&stm32_temp_dev_data, &stm32_temp_dev_config,
|
|
|
|
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
|
|
|
|
&stm32_temp_driver_api);
|