drivers: sensor: Added driver for the Würth Elektronik WSEN-TIDS sensor

Added sample for the WSEN-TIDS temperature sensor.

Signed-off-by: Matthias Hauser <Matthias.Hauser@we-online.de>
This commit is contained in:
Matthias Hauser 2023-02-02 10:08:06 +01:00 committed by Carles Cufí
parent 6ecc6c3cec
commit d4e9e5f46c
9 changed files with 651 additions and 0 deletions

View File

@ -113,6 +113,7 @@ add_subdirectory_ifdef(CONFIG_TEMP_KINETIS nxp_kinetis_temp)
add_subdirectory_ifdef(CONFIG_TACH_XEC mchp_tach_xec)
add_subdirectory_ifdef(CONFIG_WSEN_HIDS wsen_hids)
add_subdirectory_ifdef(CONFIG_ITDS wsen_itds)
add_subdirectory_ifdef(CONFIG_WSEN_TIDS wsen_tids)
add_subdirectory_ifdef(CONFIG_MCUX_ACMP mcux_acmp)
add_subdirectory_ifdef(CONFIG_TACH_NPCX nuvoton_tach_npcx)
add_subdirectory_ifdef(CONFIG_ADC_CMP_NPCX nuvoton_adc_cmp_npcx)

View File

@ -265,6 +265,8 @@ source "drivers/sensor/wsen_hids/Kconfig"
source "drivers/sensor/wsen_itds/Kconfig"
source "drivers/sensor/wsen_tids/Kconfig"
source "drivers/sensor/mcux_acmp/Kconfig"
source "drivers/sensor/nuvoton_tach_npcx/Kconfig"

View File

@ -0,0 +1,7 @@
# Copyright (c) 2022 Würth Elektronik eiSos GmbH & Co. KG
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources(wsen_tids.c)
zephyr_library_sources_ifdef(CONFIG_WSEN_TIDS_TRIGGER wsen_tids_trigger.c)

View File

@ -0,0 +1,53 @@
# Copyright (c) 2022 Würth Elektronik eiSos GmbH & Co. KG
# SPDX-License-Identifier: Apache-2.0
menuconfig WSEN_TIDS
bool "WSEN-TIDS temperature sensor"
default y
depends on DT_HAS_WE_WSEN_TIDS_ENABLED
select I2C if $(dt_compat_on_bus,$(DT_COMPAT_WE_WSEN_TIDS),i2c)
select HAS_WESENSORS
help
Enable driver for the WSEN-TIDS I2C-based temperature sensor.
if WSEN_TIDS
choice WSEN_TIDS_TRIGGER_MODE
prompt "Trigger mode"
default WSEN_TIDS_TRIGGER_NONE
help
Specify the type of triggering to be used by the driver.
config WSEN_TIDS_TRIGGER_NONE
bool "No trigger"
config WSEN_TIDS_TRIGGER_GLOBAL_THREAD
bool "Use global thread"
depends on GPIO
select WSEN_TIDS_TRIGGER
config WSEN_TIDS_TRIGGER_OWN_THREAD
bool "Use own thread"
depends on GPIO
select WSEN_TIDS_TRIGGER
endchoice # WSEN_TIDS_TRIGGER_MODE
config WSEN_TIDS_TRIGGER
bool
config WSEN_TIDS_THREAD_PRIORITY
int "Thread priority"
depends on WSEN_TIDS_TRIGGER_OWN_THREAD
default 10
help
Priority of thread used by the driver to handle interrupts.
config WSEN_TIDS_THREAD_STACK_SIZE
int "Thread stack size"
depends on WSEN_TIDS_TRIGGER_OWN_THREAD
default 1024
help
Stack size of thread used by the driver to handle interrupts.
endif # WSEN_TIDS

View File

@ -0,0 +1,229 @@
/*
* Copyright (c) 2022 Würth Elektronik eiSos GmbH & Co. KG
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT we_wsen_tids
#include <stdlib.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/logging/log.h>
#include "wsen_tids.h"
LOG_MODULE_REGISTER(WSEN_TIDS, CONFIG_SENSOR_LOG_LEVEL);
/*
* List of supported output data rates. Index into this list is used as
* argument for TIDS_setOutputDataRate()
*/
static const int32_t tids_odr_list[] = {
25,
50,
100,
200,
};
static int tids_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
struct tids_data *data = dev->data;
int16_t raw_temperature;
if ((chan != SENSOR_CHAN_ALL) && (chan != SENSOR_CHAN_AMBIENT_TEMP)) {
LOG_ERR("Fetching is not supported on channel %d.", chan);
return -EINVAL;
}
if (TIDS_getRawTemperature(&data->sensor_interface, &raw_temperature) != WE_SUCCESS) {
LOG_ERR("Failed to fetch data sample");
return -EIO;
}
data->temperature = raw_temperature;
return 0;
}
static int tids_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
struct tids_data *data = dev->data;
if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
/* Convert temperature from 0.01 degrees Celsius to degrees Celsius */
val->val1 = data->temperature / 100;
val->val2 = ((int32_t)data->temperature % 100) * (1000000 / 100);
} else {
return -ENOTSUP;
}
return 0;
}
/* Set output data rate. See tids_odr_list for allowed values. */
static int tids_odr_set(const struct device *dev, const struct sensor_value *odr)
{
struct tids_data *data = dev->data;
int odr_index;
for (odr_index = 0; odr_index < ARRAY_SIZE(tids_odr_list); odr_index++) {
if (odr->val1 == tids_odr_list[odr_index] && odr->val2 == 0) {
break;
}
}
if (odr_index == ARRAY_SIZE(tids_odr_list)) {
/* ODR not allowed (was not found in tids_odr_list) */
LOG_ERR("Bad sampling frequency %d.%d", odr->val1, abs(odr->val2));
return -EINVAL;
}
if (TIDS_setOutputDataRate(&data->sensor_interface, (TIDS_outputDataRate_t)odr_index) !=
WE_SUCCESS) {
LOG_ERR("Failed to set output data rate");
return -EIO;
}
return 0;
}
static int tids_attr_set(const struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr, const struct sensor_value *val)
{
if (chan != SENSOR_CHAN_ALL) {
LOG_WRN("attr_set() is not supported on channel %d.", chan);
return -ENOTSUP;
}
switch (attr) {
case SENSOR_ATTR_SAMPLING_FREQUENCY:
return tids_odr_set(dev, val);
#ifdef CONFIG_WSEN_TIDS_TRIGGER
case SENSOR_ATTR_LOWER_THRESH:
return tids_threshold_set(dev, val, false);
case SENSOR_ATTR_UPPER_THRESH:
return tids_threshold_set(dev, val, true);
#endif /* CONFIG_WSEN_TIDS_TRIGGER */
default:
LOG_ERR("Operation not supported.");
return -ENOTSUP;
}
}
static const struct sensor_driver_api tids_driver_api = {
.attr_set = tids_attr_set,
#if CONFIG_WSEN_TIDS_TRIGGER
.trigger_set = tids_trigger_set,
#endif
.sample_fetch = tids_sample_fetch,
.channel_get = tids_channel_get,
};
static int tids_init(const struct device *dev)
{
const struct tids_config *const config = dev->config;
struct tids_data *data = dev->data;
int status;
uint8_t device_id;
struct sensor_value odr;
/* Initialize WE sensor interface */
TIDS_getDefaultInterface(&data->sensor_interface);
data->sensor_interface.interfaceType = WE_i2c;
data->sensor_interface.handle = (void *)&config->bus_cfg.i2c;
/* First communication test - check device ID */
if (TIDS_getDeviceID(&data->sensor_interface, &device_id) != WE_SUCCESS) {
LOG_ERR("Failed to read device ID.");
return -EIO;
}
if (device_id != TIDS_DEVICE_ID_VALUE) {
LOG_ERR("Invalid device ID 0x%x.", device_id);
return -EIO;
}
/* Reset the sensor with an arbitrary off time of 5 us */
TIDS_softReset(&data->sensor_interface, TIDS_enable);
k_sleep(K_USEC(5));
TIDS_softReset(&data->sensor_interface, TIDS_disable);
odr.val1 = tids_odr_list[config->odr];
odr.val2 = 0;
status = tids_odr_set(dev, &odr);
if (status < 0) {
LOG_ERR("Failed to set output data rate.");
return status;
}
if (TIDS_enableBlockDataUpdate(&data->sensor_interface, TIDS_enable) != WE_SUCCESS) {
LOG_ERR("Failed to enable block data update.");
return -EIO;
}
if (TIDS_enableContinuousMode(&data->sensor_interface, TIDS_enable) != WE_SUCCESS) {
LOG_ERR("Failed to enable continuous mode.");
return -EIO;
}
#ifdef CONFIG_WSEN_TIDS_TRIGGER
status = tids_init_interrupt(dev);
if (status < 0) {
LOG_ERR("Failed to initialize threshold interrupt.");
return status;
}
#endif
return 0;
}
#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0
#warning "TIDS driver enabled without any devices"
#endif
/*
* Device creation macros
*/
#define TIDS_DEVICE_INIT(inst) \
SENSOR_DEVICE_DT_INST_DEFINE(inst, \
tids_init, \
NULL, \
&tids_data_##inst, \
&tids_config_##inst, \
POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, \
&tids_driver_api);
#ifdef CONFIG_WSEN_TIDS_TRIGGER
#define TIDS_CFG_IRQ(inst) \
.gpio_threshold = GPIO_DT_SPEC_INST_GET(inst, int_gpios), \
.high_threshold = DT_INST_PROP(inst, temp_high_threshold), \
.low_threshold = DT_INST_PROP(inst, temp_low_threshold)
#else
#define TIDS_CFG_IRQ(inst)
#endif /* CONFIG_WSEN_TIDS_TRIGGER */
/*
* Main instantiation macro.
*/
#define TIDS_DEFINE(inst) \
static struct tids_data tids_data_##inst; \
static const struct tids_config tids_config_##inst = \
{ \
.bus_cfg = { \
.i2c = I2C_DT_SPEC_INST_GET(inst), \
}, \
.odr = (TIDS_outputDataRate_t)(DT_INST_ENUM_IDX(inst, odr)), \
COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, int_gpios), \
(TIDS_CFG_IRQ(inst)), ()) \
}; \
TIDS_DEVICE_INIT(inst)
DT_INST_FOREACH_STATUS_OKAY(TIDS_DEFINE)

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2022 Würth Elektronik eiSos GmbH & Co. KG
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_SENSOR_WSEN_TIDS_WSEN_TIDS_H_
#define ZEPHYR_DRIVERS_SENSOR_WSEN_TIDS_WSEN_TIDS_H_
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/sensor.h>
#include <weplatform.h>
#include "WSEN_TIDS_2521020222501.h"
#include <zephyr/drivers/i2c.h>
struct tids_data {
/* WE sensor interface configuration */
WE_sensorInterface_t sensor_interface;
/* Last temperature sample */
int16_t temperature;
#ifdef CONFIG_WSEN_TIDS_TRIGGER
const struct device *dev;
/* Callback for high/low limit interrupts */
struct gpio_callback threshold_cb;
struct sensor_trigger threshold_trigger;
sensor_trigger_handler_t threshold_handler;
#if defined(CONFIG_WSEN_TIDS_TRIGGER_OWN_THREAD)
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_WSEN_TIDS_THREAD_STACK_SIZE);
struct k_thread thread;
struct k_sem threshold_sem;
#elif defined(CONFIG_WSEN_TIDS_TRIGGER_GLOBAL_THREAD)
struct k_work work;
#endif
#endif /* CONFIG_WSEN_TIDS_TRIGGER */
};
struct tids_config {
union {
const struct i2c_dt_spec i2c;
} bus_cfg;
/* Output data rate */
const TIDS_outputDataRate_t odr;
#ifdef CONFIG_WSEN_TIDS_TRIGGER
/* Interrupt pin used for high and low limit interrupt events */
const struct gpio_dt_spec gpio_threshold;
/* High temperature interrupt threshold */
const int high_threshold;
/* Low temperature interrupt threshold */
const int low_threshold;
#endif
};
#ifdef CONFIG_WSEN_TIDS_TRIGGER
int tids_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
int tids_threshold_set(const struct device *dev, const struct sensor_value *thresh_value,
bool upper);
int tids_init_interrupt(const struct device *dev);
#endif
int tids_i2c_init(const struct device *dev);
#endif /* ZEPHYR_DRIVERS_SENSOR_WSEN_TIDS_WSEN_TIDS_H_ */

View File

@ -0,0 +1,238 @@
/*
* Copyright (c) 2022 Würth Elektronik eiSos GmbH & Co. KG
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT we_wsen_tids
#include <stdlib.h>
#include <zephyr/logging/log.h>
#include "wsen_tids.h"
LOG_MODULE_DECLARE(WSEN_TIDS, CONFIG_SENSOR_LOG_LEVEL);
#define THRESHOLD_TEMPERATURE2REGISTER_OFFSET (double)63.
#define THRESHOLD_TEMPERATURE2REGISTER_STEP (double)0.64
/* Enable/disable threshold interrupt handling */
static inline void tids_setup_threshold_interrupt(const struct device *dev, bool enable)
{
const struct tids_config *cfg = dev->config;
unsigned int flags = enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE;
gpio_pin_interrupt_configure_dt(&cfg->gpio_threshold, flags);
}
/*
* Is called when a "threshold exceeded" interrupt occurred. Triggers
* asynchronous processing of the interrupt in tids_process_threshold_interrupt().
*/
static void tids_handle_threshold_interrupt(const struct device *dev)
{
struct tids_data *data = dev->data;
/* Disable interrupt handling until the interrupt has been processed */
tids_setup_threshold_interrupt(dev, false);
#if defined(CONFIG_WSEN_TIDS_TRIGGER_OWN_THREAD)
k_sem_give(&data->threshold_sem);
#elif defined(CONFIG_WSEN_TIDS_TRIGGER_GLOBAL_THREAD)
k_work_submit(&data->work);
#endif
}
/*
* Is called after a "threshold exceeded" interrupt occurred.
* Checks the sensor's status register for the limit exceeded flags and
* calls the trigger handler if one of the flags is set.
*/
static void tids_process_threshold_interrupt(const struct device *dev)
{
struct tids_data *data = dev->data;
TIDS_status_t status;
/*
* Read the sensor's status register - this also causes the interrupt pin
* to be de-asserted
*/
if (TIDS_getStatusRegister(&data->sensor_interface, &status) != WE_SUCCESS) {
LOG_ERR("Failed to read status register");
return;
}
if (data->threshold_handler != NULL &&
(status.upperLimitExceeded != 0 || status.lowerLimitExceeded != 0)) {
data->threshold_handler(dev, &data->threshold_trigger);
}
if (data->threshold_handler != NULL) {
tids_setup_threshold_interrupt(dev, true);
}
}
/* Enables/disables processing of the "threshold exceeded" interrupt. */
int tids_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
struct tids_data *data = dev->data;
const struct tids_config *cfg = dev->config;
if (trig->type != SENSOR_TRIG_THRESHOLD) {
LOG_ERR("Unsupported sensor trigger");
return -ENOTSUP;
}
tids_setup_threshold_interrupt(dev, false);
data->threshold_handler = handler;
if (handler == NULL) {
return 0;
}
data->threshold_trigger = *trig;
tids_setup_threshold_interrupt(dev, true);
/*
* If threshold interrupt is active we probably won't get the rising edge, so
* invoke the callback manually.
*/
if (gpio_pin_get_dt(&cfg->gpio_threshold) > 0) {
tids_handle_threshold_interrupt(dev);
}
return 0;
}
static void tids_threshold_callback(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
struct tids_data *data = CONTAINER_OF(cb, struct tids_data, threshold_cb);
ARG_UNUSED(pins);
tids_handle_threshold_interrupt(data->dev);
}
#ifdef CONFIG_WSEN_TIDS_TRIGGER_OWN_THREAD
static void tids_thread(struct tids_data *tids)
{
while (true) {
k_sem_take(&tids->threshold_sem, K_FOREVER);
tids_process_threshold_interrupt(tids->dev);
}
}
#endif /* CONFIG_WSEN_TIDS_TRIGGER_OWN_THREAD */
#ifdef CONFIG_WSEN_TIDS_TRIGGER_GLOBAL_THREAD
static void tids_work_cb(struct k_work *work)
{
struct tids_data *tids = CONTAINER_OF(work, struct tids_data, work);
tids_process_threshold_interrupt(tids->dev);
}
#endif /* CONFIG_WSEN_TIDS_TRIGGER_GLOBAL_THREAD */
int tids_threshold_set(const struct device *dev, const struct sensor_value *thresh_value,
bool upper)
{
struct tids_data *data = dev->data;
double thresh = (sensor_value_to_double(thresh_value) / THRESHOLD_TEMPERATURE2REGISTER_STEP)
+ THRESHOLD_TEMPERATURE2REGISTER_OFFSET;
if (thresh < 0) {
thresh = 0;
} else if (thresh > 255) {
thresh = 255;
}
if (upper) {
if (TIDS_setTempHighLimit(&data->sensor_interface, (uint8_t)thresh) != WE_SUCCESS) {
LOG_ERR("Failed to set high temperature threshold to %d.%d (%d).",
thresh_value->val1, abs(thresh_value->val2), (uint8_t)thresh);
return -EIO;
}
} else {
if (TIDS_setTempLowLimit(&data->sensor_interface, (uint8_t)thresh) != WE_SUCCESS) {
LOG_ERR("Failed to set low temperature threshold to %d.%d (%d).",
thresh_value->val1, abs(thresh_value->val2), (uint8_t)thresh);
return -EIO;
}
}
return 0;
}
int tids_init_interrupt(const struct device *dev)
{
struct tids_data *data = dev->data;
const struct tids_config *cfg = dev->config;
int status;
struct sensor_value upper_limit;
struct sensor_value lower_limit;
if (cfg->gpio_threshold.port == NULL) {
LOG_ERR("int-gpios is not defined in the device tree.");
return -EINVAL;
}
if (!device_is_ready(cfg->gpio_threshold.port)) {
LOG_ERR("Device %s is not ready", cfg->gpio_threshold.port->name);
return -ENODEV;
}
data->dev = dev;
/* Setup threshold gpio interrupt */
status = gpio_pin_configure_dt(&cfg->gpio_threshold, GPIO_INPUT);
if (status < 0) {
LOG_ERR("Failed to configure %s.%02u", cfg->gpio_threshold.port->name,
cfg->gpio_threshold.pin);
return status;
}
gpio_init_callback(&data->threshold_cb, tids_threshold_callback,
BIT(cfg->gpio_threshold.pin));
status = gpio_add_callback(cfg->gpio_threshold.port, &data->threshold_cb);
if (status < 0) {
LOG_ERR("Failed to set gpio callback.");
return status;
}
/*
* Enable interrupt on high/low temperature (interrupt generation is enabled if at
* least one threshold is non-zero)
*/
upper_limit.val1 = cfg->high_threshold;
upper_limit.val2 = 0;
lower_limit.val1 = cfg->low_threshold;
lower_limit.val2 = 0;
status = tids_threshold_set(dev, &upper_limit, true);
if (status < 0) {
return status;
}
status = tids_threshold_set(dev, &lower_limit, false);
if (status < 0) {
return status;
}
#if defined(CONFIG_WSEN_TIDS_TRIGGER_OWN_THREAD)
k_sem_init(&data->threshold_sem, 0, K_SEM_MAX_LIMIT);
k_thread_create(&data->thread, data->thread_stack, CONFIG_WSEN_TIDS_THREAD_STACK_SIZE,
(k_thread_entry_t)tids_thread, data, NULL, NULL,
K_PRIO_COOP(CONFIG_WSEN_TIDS_THREAD_PRIORITY), 0, K_NO_WAIT);
#elif defined(CONFIG_WSEN_TIDS_TRIGGER_GLOBAL_THREAD)
data->work.handler = tids_work_cb;
#endif
tids_setup_threshold_interrupt(dev, true);
return 0;
}

View File

@ -0,0 +1,37 @@
# Copyright (c) 2022 Würth Elektronik eiSos GmbH & Co. KG
# SPDX-License-Identifier: Apache-2.0
description: |
Würth Elektronik WSEN-TIDS temperature sensor
compatible: "we,wsen-tids"
include: [sensor-device.yaml, i2c-device.yaml]
properties:
int-gpios:
type: phandle-array
description: Threshold interrupt pin.
Interrupt is active low by default.
odr:
type: int
required: true
enum:
- 25
- 50
- 100
- 200
description: |
Sensor output data rate expressed in samples per second.
Data rates supported by the chip are 25, 50, 100 and 200.
temp-high-threshold:
type: int
description: |
Threshold for temperature high limit interrupt event.
temp-low-threshold:
type: int
description: |
Threshold for temperature low limit interrupt event.

View File

@ -657,3 +657,10 @@ test_i2c_akm09918c: akm09918c@66 {
compatible = "asahi-kasei,akm09918c";
reg = <0x66>;
};
test_i2c_wsen_tids: wsen_tids@67 {
compatible = "we,wsen-tids";
reg = <0x67>;
int-gpios = <&test_gpio 0 0>;
odr = <25>;
};