From e2c0e220fdcba426f99943ea7ab438e4e4de67ca Mon Sep 17 00:00:00 2001 From: "Kenneth J. Miller" Date: Thu, 11 May 2023 00:00:42 +0200 Subject: [PATCH] drivers: sensor: Add STM32 VREF+ sensor Add VREF+ sensor driver and DT node definition. This driver allows determining the actual voltage applied to an SoC's VREF+ pin, by comparing the VREFINT internal bandgap voltage reference with its factory calibration data. In packages where VREF+ is bonded to VDDA, this permits direct measurement of VDDA voltage. Signed-off-by: Kenneth J. Miller --- drivers/sensor/CMakeLists.txt | 1 + drivers/sensor/Kconfig | 2 + drivers/sensor/stm32_vref/CMakeLists.txt | 5 + drivers/sensor/stm32_vref/Kconfig | 12 ++ drivers/sensor/stm32_vref/stm32_vref.c | 142 +++++++++++++++++++++++ dts/bindings/sensor/st,stm32-vref.yaml | 23 ++++ 6 files changed, 185 insertions(+) create mode 100644 drivers/sensor/stm32_vref/CMakeLists.txt create mode 100644 drivers/sensor/stm32_vref/Kconfig create mode 100644 drivers/sensor/stm32_vref/stm32_vref.c create mode 100644 dts/bindings/sensor/st,stm32-vref.yaml diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index c3e5bc55eb3..2e8d7985fdb 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -108,6 +108,7 @@ add_subdirectory_ifdef(CONFIG_SM351LT sm351lt) add_subdirectory_ifdef(CONFIG_HAS_STMEMSC stmemsc) add_subdirectory_ifdef(CONFIG_STM32_TEMP stm32_temp) add_subdirectory_ifdef(CONFIG_STM32_VBAT stm32_vbat) +add_subdirectory_ifdef(CONFIG_STM32_VREF stm32_vref) add_subdirectory_ifdef(CONFIG_STTS751 stts751) add_subdirectory_ifdef(CONFIG_SX9500 sx9500) add_subdirectory_ifdef(CONFIG_TH02 th02) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index ce71c66dab6..2e0ccc7d6c3 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -257,6 +257,8 @@ source "drivers/sensor/stm32_temp/Kconfig" source "drivers/sensor/stm32_vbat/Kconfig" +source "drivers/sensor/stm32_vref/Kconfig" + source "drivers/sensor/stts751/Kconfig" source "drivers/sensor/sx9500/Kconfig" diff --git a/drivers/sensor/stm32_vref/CMakeLists.txt b/drivers/sensor/stm32_vref/CMakeLists.txt new file mode 100644 index 00000000000..ed2bd5b186d --- /dev/null +++ b/drivers/sensor/stm32_vref/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(stm32_vref.c) diff --git a/drivers/sensor/stm32_vref/Kconfig b/drivers/sensor/stm32_vref/Kconfig new file mode 100644 index 00000000000..375c0b4bc3d --- /dev/null +++ b/drivers/sensor/stm32_vref/Kconfig @@ -0,0 +1,12 @@ +# STM32 vref sensor configuration options + +# Copyright (c) 2023 Kenneth J. Miller +# SPDX-License-Identifier: Apache-2.0 + +config STM32_VREF + bool "STM32 VREF Sensor" + default y + depends on DT_HAS_ST_STM32_VREF_ENABLED + select ADC + help + Enable driver for STM32 Vref sensor. diff --git a/drivers/sensor/stm32_vref/stm32_vref.c b/drivers/sensor/stm32_vref/stm32_vref.c new file mode 100644 index 00000000000..8b85d0e4c9b --- /dev/null +++ b/drivers/sensor/stm32_vref/stm32_vref.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2023 Kenneth J. Miller + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT st_stm32_vref + +#include +#include +#include +#include +#include +#if defined(CONFIG_SOC_SERIES_STM32H5X) +#include +#endif /* CONFIG_SOC_SERIES_STM32H5X */ + +LOG_MODULE_REGISTER(stm32_vref, CONFIG_SENSOR_LOG_LEVEL); + +struct stm32_vref_data { + const struct device *adc; + const struct adc_channel_cfg adc_cfg; + struct adc_sequence adc_seq; + struct k_mutex mutex; + int16_t sample_buffer; + int16_t raw; /* raw adc Sensor value */ +}; + +struct stm32_vref_config { + uint16_t *cal_addr; + int cal_mv; +}; + +static int stm32_vref_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + struct stm32_vref_data *data = dev->data; + struct adc_sequence *sp = &data->adc_seq; + int rc; + + if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_VOLTAGE) { + return -ENOTSUP; + } + + k_mutex_lock(&data->mutex, K_FOREVER); + + 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; + } + + rc = adc_read(data->adc, sp); + if (rc == 0) { + data->raw = data->sample_buffer; + } + +unlock: + k_mutex_unlock(&data->mutex); + + return rc; +} + +static int stm32_vref_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct stm32_vref_data *data = dev->data; + const struct stm32_vref_config *cfg = dev->config; + float vref; + + if (chan != SENSOR_CHAN_VOLTAGE) { + return -ENOTSUP; + } + + if (data->raw == 0) { + LOG_ERR("Raw ADC value is zero"); + return -ENODATA; + } + +/* + * ERRATA: STM32H5X: bus fault errors occur when reading engineering bytes with + * icache enabled. + * See https://github.com/zephyrproject-rtos/zephyr/commit/065a8f2 + */ +#if defined(CONFIG_SOC_SERIES_STM32H5X) + LL_ICACHE_Disable(); +#endif /* CONFIG_SOC_SERIES_STM32H5X */ + + /* Calculate VREF+ using VREFINT bandgap voltage and calibration data */ + vref = cfg->cal_mv * (*cfg->cal_addr) / data->raw; + /* millivolt to volt */ + vref /= 1000; + +#if defined(CONFIG_SOC_SERIES_STM32H5X) + LL_ICACHE_Enable(); +#endif /* CONFIG_SOC_SERIES_STM32H5X */ + + return sensor_value_from_double(val, vref); +} + +static const struct sensor_driver_api stm32_vref_driver_api = { + .sample_fetch = stm32_vref_sample_fetch, + .channel_get = stm32_vref_channel_get, +}; + +static int stm32_vref_init(const struct device *dev) +{ + struct stm32_vref_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){ + .channels = BIT(data->adc_cfg.channel_id), + .buffer = &data->sample_buffer, + .buffer_size = sizeof(data->sample_buffer), + .resolution = 12U, + }; + + return 0; +} + +static struct stm32_vref_data stm32_vref_dev_data = { + .adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(0)), + .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_vref_config stm32_vref_dev_config = { + .cal_addr = (uint16_t *)DT_INST_PROP(0, vrefint_cal_addr), + .cal_mv = DT_INST_PROP(0, vrefint_cal_mv), +}; + +SENSOR_DEVICE_DT_INST_DEFINE(0, stm32_vref_init, NULL, &stm32_vref_dev_data, &stm32_vref_dev_config, + POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &stm32_vref_driver_api); diff --git a/dts/bindings/sensor/st,stm32-vref.yaml b/dts/bindings/sensor/st,stm32-vref.yaml new file mode 100644 index 00000000000..59d6d760705 --- /dev/null +++ b/dts/bindings/sensor/st,stm32-vref.yaml @@ -0,0 +1,23 @@ +# Copyright (c) 2023 Kenneth J. Miller +# SPDX-License-Identifier: Apache-2.0 + +description: STM32 family VREF+ node + +compatible: "st,stm32-vref" + +include: sensor-device.yaml + +properties: + vrefint-cal-addr: + type: int + required: true + description: | + Device engineering bytes address containing the factory-measured + calibration bandgap voltage (VREFINT_CAL). + + vrefint-cal-mv: + type: int + required: true + description: | + VDDA/VREF+ voltage in millivolts applied during manufacturing to determine + the internal bandgap voltage reference VREFINT.