Commit 32222e07 authored by Francois Ramu's avatar Francois Ramu Committed by Carles Cufi
Browse files

drivers: sensor: add a stm32 sensor driver for the internal VBat



Similar to the internal temperature sensor of the stm32
this driver controls the Vbat monitoring in Volts,
using an ADC internal input and the stm32-vbat node of the DTS.
The ref voltage is given by the ADC.
Note that stm32F1x does not propose the feature.

Signed-off-by: default avatarFrancois Ramu <francois.ramu@st.com>
parent 4f7a4905
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -89,6 +89,7 @@ add_subdirectory_ifdef(CONFIG_SI7210 si7210)
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_STTS751		stts751)
add_subdirectory_ifdef(CONFIG_SX9500		sx9500)
add_subdirectory_ifdef(CONFIG_TH02		    th02)
+2 −0
Original line number Diff line number Diff line
@@ -216,6 +216,8 @@ source "drivers/sensor/sm351lt/Kconfig"

source "drivers/sensor/stm32_temp/Kconfig"

source "drivers/sensor/stm32_vbat/Kconfig"

source "drivers/sensor/stts751/Kconfig"

source "drivers/sensor/sx9500/Kconfig"
+5 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: Apache-2.0

zephyr_library()

zephyr_library_sources(stm32_vbat.c)
+10 −0
Original line number Diff line number Diff line
# STM32 battery sensor configuration options

# Copyright (c) 2022 STMicroelectronics
# SPDX-License-Identifier: Apache-2.0

config STM32_VBAT
	bool "STM32 Vbat Sensor"
	depends on ADC && (SOC_FAMILY_STM32 && !SOC_SERIES_STM32F1X)
	help
	  Enable driver for STM32 Vbat sensor and then also ADC
+128 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2022 STMicroelectronics
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <device.h>
#include <drivers/sensor.h>
#include <drivers/adc.h>
#include <logging/log.h>

LOG_MODULE_REGISTER(stm32_vbat, CONFIG_SENSOR_LOG_LEVEL);

#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_vbat)
#define DT_DRV_COMPAT st_stm32_vbat
#else
#error "No compatible devicetree node found"
#endif

struct stm32_vbat_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_vbat_config {
	uint16_t vref_mv;
	int ratio;
};

static int stm32_vbat_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
	struct stm32_vbat_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_vbat_channel_get(const struct device *dev, enum sensor_channel chan,
				  struct sensor_value *val)
{
	struct stm32_vbat_data *data = dev->data;
	const struct stm32_vbat_config *cfg = dev->config;
	float voltage;

	if (chan != SENSOR_CHAN_VOLTAGE) {
		return -ENOTSUP;
	}

	voltage = data->raw * cfg->vref_mv / 0x0FFF; /* Sensor value in millivolts */
	/* considering the vbat input through a resistor bridge */
	voltage = voltage * cfg->ratio / 1000; /* value of SENSOR_CHAN_VOLTAGE in Volt */

	return sensor_value_from_double(val, voltage);
}

static const struct sensor_driver_api stm32_vbat_driver_api = {
	.sample_fetch = stm32_vbat_sample_fetch,
	.channel_get = stm32_vbat_channel_get,
};

static int stm32_vbat_init(const struct device *dev)
{
	struct stm32_vbat_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;
}

#define STM32_VBAT DT_PROP(DT_INST_IO_CHANNELS_CTLR(0), vref_mv)

static struct stm32_vbat_config stm32_vbat_dev_config = {
	.vref_mv = DT_PROP(DT_INST_IO_CHANNELS_CTLR(0), vref_mv),
	.ratio = DT_INST_PROP(0, ratio),
};

static struct stm32_vbat_data stm32_vbat_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,
	},
};

DEVICE_DT_INST_DEFINE(0, stm32_vbat_init, NULL, &stm32_vbat_dev_data, &stm32_vbat_dev_config,
		      POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &stm32_vbat_driver_api);