drivers: sensors: add jedec jc 42.4 compliant temperature sensor

This transforms the existing driver for the Microchip MCP9808
to be used as a generic driver to be used with  all
JEDEC JC 42.4 compliant temperature sensor chips.

Signed-off-by: Fin Maaß <f.maass@vogl-electronic.com>
This commit is contained in:
Fin Maaß 2024-08-19 13:00:14 +02:00 committed by Anas Nashif
commit 2293e6668a
30 changed files with 428 additions and 411 deletions

View file

@ -10,6 +10,7 @@ add_subdirectory(espressif)
add_subdirectory(honeywell)
add_subdirectory(infineon)
add_subdirectory(ite)
add_subdirectory(jedec)
add_subdirectory(maxim)
add_subdirectory(meas)
add_subdirectory(microchip)

View file

@ -94,6 +94,7 @@ source "drivers/sensor/espressif/Kconfig"
source "drivers/sensor/honeywell/Kconfig"
source "drivers/sensor/infineon/Kconfig"
source "drivers/sensor/ite/Kconfig"
source "drivers/sensor/jedec/Kconfig"
source "drivers/sensor/maxim/Kconfig"
source "drivers/sensor/meas/Kconfig"
source "drivers/sensor/microchip/Kconfig"

View file

@ -0,0 +1,6 @@
# Copyright (c) 2024 Vogl Electronic GmbH
# SPDX-License-Identifier: Apache-2.0
# zephyr-keep-sorted-start
add_subdirectory_ifdef(CONFIG_JC42 jc42)
# zephyr-keep-sorted-stop

View file

@ -0,0 +1,6 @@
# Copyright (c) 2024 Vogl Electronic GmbH
# SPDX-License-Identifier: Apache-2.0
# zephyr-keep-sorted-start
source "drivers/sensor/jedec/jc42/Kconfig"
# zephyr-keep-sorted-stop

View file

@ -0,0 +1,7 @@
# Copyright (c) 2024 Vogl Electronic GmbH
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources(jc42.c)
zephyr_library_sources_ifdef(CONFIG_JC42_TRIGGER jc42_trigger.c)

View file

@ -0,0 +1,49 @@
# JEDEC JC 42.4 compliant temperature sensor configuration options
# Copyright (c) 2016 Intel Corporation
# Copyright (c) 2024 Vogl Electronic GmbH
# SPDX-License-Identifier: Apache-2.0
menuconfig JC42
bool "JC42 temperature sensor"
default y
depends on DT_HAS_JEDEC_JC_42_4_TEMP_ENABLED
select I2C
help
Enable driver for JC42 temperature sensor.
if JC42
choice
prompt "JC42 trigger mode"
default JC42_TRIGGER_NONE
config JC42_TRIGGER_NONE
bool "No trigger"
config JC42_TRIGGER_GLOBAL_THREAD
depends on GPIO
select JC42_TRIGGER
bool "Use global thread"
config JC42_TRIGGER_OWN_THREAD
depends on GPIO
select JC42_TRIGGER
bool "Use own thread"
endchoice
config JC42_TRIGGER
bool
config JC42_THREAD_STACK_SIZE
int "Sensor delayed work thread stack size"
depends on JC42_TRIGGER_OWN_THREAD
default 1024
config JC42_THREAD_PRIORITY
int "JC42 thread priority"
depends on JC42_TRIGGER_OWN_THREAD
default 10
endif # JC42

View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 2019 Peter Bigot Consulting, LLC
* Copyright (c) 2016 Intel Corporation
* Copyright (c) 2024 Vogl Electronic GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT jedec_jc_42_4_temp
#include <errno.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/init.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/logging/log.h>
#include "jc42.h"
LOG_MODULE_REGISTER(JC42, CONFIG_SENSOR_LOG_LEVEL);
int jc42_reg_read(const struct device *dev, uint8_t reg, uint16_t *val)
{
const struct jc42_config *cfg = dev->config;
int rc = i2c_write_read_dt(&cfg->i2c, &reg, sizeof(reg), val, sizeof(*val));
if (rc == 0) {
*val = sys_be16_to_cpu(*val);
}
return rc;
}
int jc42_reg_write_16bit(const struct device *dev, uint8_t reg, uint16_t val)
{
const struct jc42_config *cfg = dev->config;
uint8_t buf[3];
buf[0] = reg;
sys_put_be16(val, &buf[1]);
return i2c_write_dt(&cfg->i2c, buf, sizeof(buf));
}
int jc42_reg_write_8bit(const struct device *dev, uint8_t reg, uint8_t val)
{
const struct jc42_config *cfg = dev->config;
uint8_t buf[2] = {
reg,
val,
};
return i2c_write_dt(&cfg->i2c, buf, sizeof(buf));
}
static int jc42_set_temperature_resolution(const struct device *dev, uint8_t resolution)
{
return jc42_reg_write_8bit(dev, JC42_REG_RESOLUTION, resolution);
}
static int jc42_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
struct jc42_data *data = dev->data;
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP);
return jc42_reg_read(dev, JC42_REG_TEMP_AMB, &data->reg_val);
}
static int jc42_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
const struct jc42_data *data = dev->data;
int temp = jc42_temp_signed_from_reg(data->reg_val);
__ASSERT_NO_MSG(chan == SENSOR_CHAN_AMBIENT_TEMP);
if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
return -ENOTSUP;
}
val->val1 = temp / JC42_TEMP_SCALE_CEL;
temp -= val->val1 * JC42_TEMP_SCALE_CEL;
val->val2 = (temp * 1000000) / JC42_TEMP_SCALE_CEL;
return 0;
}
static const struct sensor_driver_api jc42_api_funcs = {
.sample_fetch = jc42_sample_fetch,
.channel_get = jc42_channel_get,
#ifdef CONFIG_JC42_TRIGGER
.attr_set = jc42_attr_set,
.trigger_set = jc42_trigger_set,
#endif /* CONFIG_JC42_TRIGGER */
};
int jc42_init(const struct device *dev)
{
const struct jc42_config *cfg = dev->config;
int rc = 0;
if (!device_is_ready(cfg->i2c.bus)) {
LOG_ERR("Bus device is not ready");
return -ENODEV;
}
rc = jc42_set_temperature_resolution(dev, cfg->resolution);
if (rc) {
LOG_ERR("Could not set the resolution of jc42 module");
return rc;
}
#ifdef CONFIG_JC42_TRIGGER
if (cfg->int_gpio.port) {
rc = jc42_setup_interrupt(dev);
}
#endif /* CONFIG_JC42_TRIGGER */
return rc;
}
#define JC42_DEFINE(inst) \
static struct jc42_data jc42_data_##inst; \
\
static const struct jc42_config jc42_config_##inst = { \
.i2c = I2C_DT_SPEC_INST_GET(inst), \
.resolution = DT_INST_PROP(inst, resolution), \
IF_ENABLED(CONFIG_JC42_TRIGGER, \
(.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}),))}; \
\
SENSOR_DEVICE_DT_INST_DEFINE(inst, jc42_init, NULL, &jc42_data_##inst, \
&jc42_config_##inst, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, &jc42_api_funcs);
DT_INST_FOREACH_STATUS_OKAY(JC42_DEFINE)

View file

@ -0,0 +1,133 @@
/*
* Copyright (c) 2019 Peter Bigot Consulting, LLC
* Copyright (c) 2016 Intel Corporation
* Copyright (c) 2024 Vogl Electronic GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_SENSOR_JEDEC_JC42_H_
#define ZEPHYR_DRIVERS_SENSOR_JEDEC_JC42_H_
#include <errno.h>
#include <zephyr/types.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/util.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/gpio.h>
#define JC42_REG_CONFIG 0x01
#define JC42_REG_UPPER_LIMIT 0x02
#define JC42_REG_LOWER_LIMIT 0x03
#define JC42_REG_CRITICAL 0x04
#define JC42_REG_TEMP_AMB 0x05
/* 16 bits control configuration and state.
*
* * Bit 0 controls alert signal output mode
* * Bit 1 controls interrupt polarity
* * Bit 2 disables upper and lower threshold checking
* * Bit 3 enables alert signal output
* * Bit 4 records alert status
* * Bit 5 records interrupt status
* * Bit 6 locks the upper/lower window registers
* * Bit 7 locks the critical register
* * Bit 8 enters shutdown mode
* * Bits 9-10 control threshold hysteresis
*/
#define JC42_CFG_ALERT_MODE_INT BIT(0)
#define JC42_CFG_ALERT_ENA BIT(3)
#define JC42_CFG_ALERT_STATE BIT(4)
#define JC42_CFG_INT_CLEAR BIT(5)
/* 16 bits are used for temperature and state encoding:
* * Bits 0..11 encode the temperature in a 2s complement signed value
* in Celsius with 1/16 Cel resolution
* * Bit 12 is set to indicate a negative temperature
* * Bit 13 is set to indicate a temperature below the lower threshold
* * Bit 14 is set to indicate a temperature above the upper threshold
* * Bit 15 is set to indicate a temperature above the critical threshold
*/
#define JC42_TEMP_SCALE_CEL 16 /* signed */
#define JC42_TEMP_SIGN_BIT BIT(12)
#define JC42_TEMP_ABS_MASK ((uint16_t)(JC42_TEMP_SIGN_BIT - 1U))
#define JC42_TEMP_LWR_BIT BIT(13)
#define JC42_TEMP_UPR_BIT BIT(14)
#define JC42_TEMP_CRT_BIT BIT(15)
#define JC42_REG_RESOLUTION 0x08
struct jc42_data {
uint16_t reg_val;
#ifdef CONFIG_JC42_TRIGGER
struct gpio_callback alert_cb;
const struct device *dev;
const struct sensor_trigger *trig;
sensor_trigger_handler_t trigger_handler;
#endif
#ifdef CONFIG_JC42_TRIGGER_OWN_THREAD
struct k_sem sem;
#endif
#ifdef CONFIG_JC42_TRIGGER_GLOBAL_THREAD
struct k_work work;
#endif
};
struct jc42_config {
struct i2c_dt_spec i2c;
uint8_t resolution;
#ifdef CONFIG_JC42_TRIGGER
struct gpio_dt_spec int_gpio;
#endif /* CONFIG_JC42_TRIGGER */
};
int jc42_reg_read(const struct device *dev, uint8_t reg, uint16_t *val);
int jc42_reg_write_16bit(const struct device *dev, uint8_t reg, uint16_t val);
int jc42_reg_write_8bit(const struct device *dev, uint8_t reg, uint8_t val);
#ifdef CONFIG_JC42_TRIGGER
int jc42_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr,
const struct sensor_value *val);
int jc42_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
int jc42_setup_interrupt(const struct device *dev);
#endif /* CONFIG_JC42_TRIGGER */
/* Encode a signed temperature in scaled Celsius to the format used in
* register values.
*/
static inline uint16_t jc42_temp_reg_from_signed(int temp)
{
/* Get the 12-bit 2s complement value */
uint16_t rv = temp & JC42_TEMP_ABS_MASK;
if (temp < 0) {
rv |= JC42_TEMP_SIGN_BIT;
}
return rv;
}
/* Decode a register temperature value to a signed temperature in
* scaled Celsius.
*/
static inline int jc42_temp_signed_from_reg(uint16_t reg)
{
int rv = reg & JC42_TEMP_ABS_MASK;
if (reg & JC42_TEMP_SIGN_BIT) {
/* Convert 12-bit 2s complement to signed negative
* value.
*/
rv = -(1U + (rv ^ JC42_TEMP_ABS_MASK));
}
return rv;
}
#endif /* ZEPHYR_DRIVERS_SENSOR_JEDEC_JC42_H_ */

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2019 Peter Bigot Consulting, LLC
* Copyright (c) 2024 Vogl Electronic GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -10,15 +11,15 @@
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/logging/log.h>
#include "mcp9808.h"
#include "jc42.h"
LOG_MODULE_DECLARE(MCP9808, CONFIG_SENSOR_LOG_LEVEL);
LOG_MODULE_DECLARE(JC42, CONFIG_SENSOR_LOG_LEVEL);
int mcp9808_attr_set(const struct device *dev, enum sensor_channel chan,
int jc42_attr_set(const struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
const struct mcp9808_config *cfg = dev->config;
const struct jc42_config *cfg = dev->config;
uint8_t reg_addr;
int temp;
@ -30,10 +31,10 @@ int mcp9808_attr_set(const struct device *dev, enum sensor_channel chan,
switch (attr) {
case SENSOR_ATTR_LOWER_THRESH:
reg_addr = MCP9808_REG_LOWER_LIMIT;
reg_addr = JC42_REG_LOWER_LIMIT;
break;
case SENSOR_ATTR_UPPER_THRESH:
reg_addr = MCP9808_REG_UPPER_LIMIT;
reg_addr = JC42_REG_UPPER_LIMIT;
break;
default:
return -EINVAL;
@ -42,17 +43,17 @@ int mcp9808_attr_set(const struct device *dev, enum sensor_channel chan,
/* Convert temperature to a signed scaled value, then write
* the 12-bit 2s complement-plus-sign-bit register value.
*/
temp = val->val1 * MCP9808_TEMP_SCALE_CEL;
temp += (MCP9808_TEMP_SCALE_CEL * val->val2) / 1000000;
temp = val->val1 * JC42_TEMP_SCALE_CEL;
temp += (JC42_TEMP_SCALE_CEL * val->val2) / 1000000;
return mcp9808_reg_write_16bit(dev, reg_addr,
mcp9808_temp_reg_from_signed(temp));
return jc42_reg_write_16bit(dev, reg_addr,
jc42_temp_reg_from_signed(temp));
}
static inline void setup_int(const struct device *dev,
bool enable)
{
const struct mcp9808_config *cfg = dev->config;
const struct jc42_config *cfg = dev->config;
unsigned int flags = enable
? GPIO_INT_EDGE_TO_ACTIVE
: GPIO_INT_DISABLE;
@ -62,20 +63,20 @@ static inline void setup_int(const struct device *dev,
static void handle_int(const struct device *dev)
{
struct mcp9808_data *data = dev->data;
struct jc42_data *data = dev->data;
setup_int(dev, false);
#if defined(CONFIG_MCP9808_TRIGGER_OWN_THREAD)
#if defined(CONFIG_JC42_TRIGGER_OWN_THREAD)
k_sem_give(&data->sem);
#elif defined(CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD)
#elif defined(CONFIG_JC42_TRIGGER_GLOBAL_THREAD)
k_work_submit(&data->work);
#endif
}
static void process_int(const struct device *dev)
{
struct mcp9808_data *data = dev->data;
struct jc42_data *data = dev->data;
if (data->trigger_handler) {
data->trigger_handler(dev, data->trig);
@ -86,12 +87,12 @@ static void process_int(const struct device *dev)
}
}
int mcp9808_trigger_set(const struct device *dev,
int jc42_trigger_set(const struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
struct mcp9808_data *data = dev->data;
const struct mcp9808_config *cfg = dev->config;
struct jc42_data *data = dev->data;
const struct jc42_config *cfg = dev->config;
int rv = 0;
if (!cfg->int_gpio.port) {
@ -119,22 +120,22 @@ int mcp9808_trigger_set(const struct device *dev,
static void alert_cb(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
struct mcp9808_data *data =
CONTAINER_OF(cb, struct mcp9808_data, alert_cb);
struct jc42_data *data =
CONTAINER_OF(cb, struct jc42_data, alert_cb);
ARG_UNUSED(pins);
handle_int(data->dev);
}
#ifdef CONFIG_MCP9808_TRIGGER_OWN_THREAD
#ifdef CONFIG_JC42_TRIGGER_OWN_THREAD
static void mcp9808_thread_main(void *p1, void *p2, void *p3)
static void jc42_thread_main(void *p1, void *p2, void *p3)
{
ARG_UNUSED(p2);
ARG_UNUSED(p3);
struct mcp9808_data *data = p1;
struct jc42_data *data = p1;
while (true) {
k_sem_take(&data->sem, K_FOREVER);
@ -142,43 +143,43 @@ static void mcp9808_thread_main(void *p1, void *p2, void *p3)
}
}
static K_KERNEL_STACK_DEFINE(mcp9808_thread_stack, CONFIG_MCP9808_THREAD_STACK_SIZE);
static struct k_thread mcp9808_thread;
#else /* CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD */
static K_KERNEL_STACK_DEFINE(jc42_thread_stack, CONFIG_JC42_THREAD_STACK_SIZE);
static struct k_thread jc42_thread;
#else /* CONFIG_JC42_TRIGGER_GLOBAL_THREAD */
static void mcp9808_gpio_thread_cb(struct k_work *work)
static void jc42_gpio_thread_cb(struct k_work *work)
{
struct mcp9808_data *data =
CONTAINER_OF(work, struct mcp9808_data, work);
struct jc42_data *data =
CONTAINER_OF(work, struct jc42_data, work);
process_int(data->dev);
}
#endif /* CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD */
#endif /* CONFIG_JC42_TRIGGER_GLOBAL_THREAD */
int mcp9808_setup_interrupt(const struct device *dev)
int jc42_setup_interrupt(const struct device *dev)
{
struct mcp9808_data *data = dev->data;
const struct mcp9808_config *cfg = dev->config;
int rc = mcp9808_reg_write_16bit(dev, MCP9808_REG_CRITICAL,
MCP9808_TEMP_ABS_MASK);
struct jc42_data *data = dev->data;
const struct jc42_config *cfg = dev->config;
int rc = jc42_reg_write_16bit(dev, JC42_REG_CRITICAL,
JC42_TEMP_ABS_MASK);
if (rc == 0) {
rc = mcp9808_reg_write_16bit(dev, MCP9808_REG_CONFIG,
MCP9808_CFG_ALERT_ENA);
rc = jc42_reg_write_16bit(dev, JC42_REG_CONFIG,
JC42_CFG_ALERT_ENA);
}
data->dev = dev;
#ifdef CONFIG_MCP9808_TRIGGER_OWN_THREAD
#ifdef CONFIG_JC42_TRIGGER_OWN_THREAD
k_sem_init(&data->sem, 0, K_SEM_MAX_LIMIT);
k_thread_create(&mcp9808_thread, mcp9808_thread_stack,
CONFIG_MCP9808_THREAD_STACK_SIZE,
mcp9808_thread_main, data, NULL, NULL,
K_PRIO_COOP(CONFIG_MCP9808_THREAD_PRIORITY),
k_thread_create(&jc42_thread, jc42_thread_stack,
CONFIG_JC42_THREAD_STACK_SIZE,
jc42_thread_main, data, NULL, NULL,
K_PRIO_COOP(CONFIG_JC42_THREAD_PRIORITY),
0, K_NO_WAIT);
#else /* CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD */
data->work.handler = mcp9808_gpio_thread_cb;
#else /* CONFIG_JC42_TRIGGER_GLOBAL_THREAD */
data->work.handler = jc42_gpio_thread_cb;
#endif /* trigger type */
if (!gpio_is_ready_dt(&cfg->int_gpio)) {

View file

@ -4,7 +4,6 @@
# zephyr-keep-sorted-start
add_subdirectory_ifdef(CONFIG_MCP9600 mcp9600)
add_subdirectory_ifdef(CONFIG_MCP970X mcp970x)
add_subdirectory_ifdef(CONFIG_MCP9808 mcp9808)
add_subdirectory_ifdef(CONFIG_TACH_XEC mchp_tach_xec)
add_subdirectory_ifdef(CONFIG_TCN75A tcn75a)
# zephyr-keep-sorted-stop

View file

@ -5,6 +5,5 @@
source "drivers/sensor/microchip/mchp_tach_xec/Kconfig"
source "drivers/sensor/microchip/mcp9600/Kconfig"
source "drivers/sensor/microchip/mcp970x/Kconfig"
source "drivers/sensor/microchip/mcp9808/Kconfig"
source "drivers/sensor/microchip/tcn75a/Kconfig"
# zephyr-keep-sorted-stop

View file

@ -1,6 +0,0 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources(mcp9808.c)
zephyr_library_sources_ifdef(CONFIG_MCP9808_TRIGGER mcp9808_trigger.c)

View file

@ -1,48 +0,0 @@
# MCP9808 temperature sensor configuration options
# Copyright (c) 2016 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
menuconfig MCP9808
bool "MCP9808 temperature sensor"
default y
depends on DT_HAS_MICROCHIP_MCP9808_ENABLED
select I2C
help
Enable driver for MCP9808 temperature sensor.
if MCP9808
choice
prompt "MCP9808 trigger mode"
default MCP9808_TRIGGER_NONE
config MCP9808_TRIGGER_NONE
bool "No trigger"
config MCP9808_TRIGGER_GLOBAL_THREAD
depends on GPIO
select MCP9808_TRIGGER
bool "Use global thread"
config MCP9808_TRIGGER_OWN_THREAD
depends on GPIO
select MCP9808_TRIGGER
bool "Use own thread"
endchoice
config MCP9808_TRIGGER
bool
config MCP9808_THREAD_STACK_SIZE
int "Sensor delayed work thread stack size"
depends on MCP9808_TRIGGER_OWN_THREAD
default 1024
config MCP9808_THREAD_PRIORITY
int "MCP9808 thread priority"
depends on MCP9808_TRIGGER_OWN_THREAD
default 10
endif # MCP9808

View file

@ -1,145 +0,0 @@
/*
* Copyright (c) 2019 Peter Bigot Consulting, LLC
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT microchip_mcp9808
#include <errno.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/init.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/logging/log.h>
#include "mcp9808.h"
LOG_MODULE_REGISTER(MCP9808, CONFIG_SENSOR_LOG_LEVEL);
int mcp9808_reg_read(const struct device *dev, uint8_t reg, uint16_t *val)
{
const struct mcp9808_config *cfg = dev->config;
int rc = i2c_write_read_dt(&cfg->i2c, &reg, sizeof(reg), val,
sizeof(*val));
if (rc == 0) {
*val = sys_be16_to_cpu(*val);
}
return rc;
}
int mcp9808_reg_write_16bit(const struct device *dev, uint8_t reg,
uint16_t val)
{
const struct mcp9808_config *cfg = dev->config;
uint8_t buf[3];
buf[0] = reg;
sys_put_be16(val, &buf[1]);
return i2c_write_dt(&cfg->i2c, buf, sizeof(buf));
}
int mcp9808_reg_write_8bit(const struct device *dev, uint8_t reg,
uint8_t val)
{
const struct mcp9808_config *cfg = dev->config;
uint8_t buf[2] = {
reg,
val,
};
return i2c_write_dt(&cfg->i2c, buf, sizeof(buf));
}
static int mcp9808_set_temperature_resolution(const struct device *dev,
uint8_t resolution)
{
return mcp9808_reg_write_8bit(dev, MCP9808_REG_RESOLUTION, resolution);
}
static int mcp9808_sample_fetch(const struct device *dev,
enum sensor_channel chan)
{
struct mcp9808_data *data = dev->data;
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP);
return mcp9808_reg_read(dev, MCP9808_REG_TEMP_AMB, &data->reg_val);
}
static int mcp9808_channel_get(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
const struct mcp9808_data *data = dev->data;
int temp = mcp9808_temp_signed_from_reg(data->reg_val);
__ASSERT_NO_MSG(chan == SENSOR_CHAN_AMBIENT_TEMP);
if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
return -ENOTSUP;
}
val->val1 = temp / MCP9808_TEMP_SCALE_CEL;
temp -= val->val1 * MCP9808_TEMP_SCALE_CEL;
val->val2 = (temp * 1000000) / MCP9808_TEMP_SCALE_CEL;
return 0;
}
static const struct sensor_driver_api mcp9808_api_funcs = {
.sample_fetch = mcp9808_sample_fetch,
.channel_get = mcp9808_channel_get,
#ifdef CONFIG_MCP9808_TRIGGER
.attr_set = mcp9808_attr_set,
.trigger_set = mcp9808_trigger_set,
#endif /* CONFIG_MCP9808_TRIGGER */
};
int mcp9808_init(const struct device *dev)
{
const struct mcp9808_config *cfg = dev->config;
int rc = 0;
if (!device_is_ready(cfg->i2c.bus)) {
LOG_ERR("Bus device is not ready");
return -ENODEV;
}
rc = mcp9808_set_temperature_resolution(dev, cfg->resolution);
if (rc) {
LOG_ERR("Could not set the resolution of mcp9808 module");
return rc;
}
#ifdef CONFIG_MCP9808_TRIGGER
if (cfg->int_gpio.port) {
rc = mcp9808_setup_interrupt(dev);
}
#endif /* CONFIG_MCP9808_TRIGGER */
return rc;
}
#define MCP9808_DEFINE(inst) \
static struct mcp9808_data mcp9808_data_##inst; \
\
static const struct mcp9808_config mcp9808_config_##inst = { \
.i2c = I2C_DT_SPEC_INST_GET(inst), \
.resolution = DT_INST_PROP(inst, resolution), \
IF_ENABLED(CONFIG_MCP9808_TRIGGER, \
(.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, { 0 }),)) \
}; \
\
SENSOR_DEVICE_DT_INST_DEFINE(inst, mcp9808_init, NULL, \
&mcp9808_data_##inst, &mcp9808_config_##inst, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, &mcp9808_api_funcs); \
DT_INST_FOREACH_STATUS_OKAY(MCP9808_DEFINE)

View file

@ -1,136 +0,0 @@
/*
* Copyright (c) 2019 Peter Bigot Consulting, LLC
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_SENSOR_MCP9808_MCP9808_H_
#define ZEPHYR_DRIVERS_SENSOR_MCP9808_MCP9808_H_
#include <errno.h>
#include <zephyr/types.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/util.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/gpio.h>
#define MCP9808_REG_CONFIG 0x01
#define MCP9808_REG_UPPER_LIMIT 0x02
#define MCP9808_REG_LOWER_LIMIT 0x03
#define MCP9808_REG_CRITICAL 0x04
#define MCP9808_REG_TEMP_AMB 0x05
/* 16 bits control configuration and state.
*
* * Bit 0 controls alert signal output mode
* * Bit 1 controls interrupt polarity
* * Bit 2 disables upper and lower threshold checking
* * Bit 3 enables alert signal output
* * Bit 4 records alert status
* * Bit 5 records interrupt status
* * Bit 6 locks the upper/lower window registers
* * Bit 7 locks the critical register
* * Bit 8 enters shutdown mode
* * Bits 9-10 control threshold hysteresis
*/
#define MCP9808_CFG_ALERT_MODE_INT BIT(0)
#define MCP9808_CFG_ALERT_ENA BIT(3)
#define MCP9808_CFG_ALERT_STATE BIT(4)
#define MCP9808_CFG_INT_CLEAR BIT(5)
/* 16 bits are used for temperature and state encoding:
* * Bits 0..11 encode the temperature in a 2s complement signed value
* in Celsius with 1/16 Cel resolution
* * Bit 12 is set to indicate a negative temperature
* * Bit 13 is set to indicate a temperature below the lower threshold
* * Bit 14 is set to indicate a temperature above the upper threshold
* * Bit 15 is set to indicate a temperature above the critical threshold
*/
#define MCP9808_TEMP_SCALE_CEL 16 /* signed */
#define MCP9808_TEMP_SIGN_BIT BIT(12)
#define MCP9808_TEMP_ABS_MASK ((uint16_t)(MCP9808_TEMP_SIGN_BIT - 1U))
#define MCP9808_TEMP_LWR_BIT BIT(13)
#define MCP9808_TEMP_UPR_BIT BIT(14)
#define MCP9808_TEMP_CRT_BIT BIT(15)
#define MCP9808_REG_RESOLUTION 0x08
struct mcp9808_data {
uint16_t reg_val;
#ifdef CONFIG_MCP9808_TRIGGER
struct gpio_callback alert_cb;
const struct device *dev;
const struct sensor_trigger *trig;
sensor_trigger_handler_t trigger_handler;
#endif
#ifdef CONFIG_MCP9808_TRIGGER_OWN_THREAD
struct k_sem sem;
#endif
#ifdef CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD
struct k_work work;
#endif
};
struct mcp9808_config {
struct i2c_dt_spec i2c;
uint8_t resolution;
#ifdef CONFIG_MCP9808_TRIGGER
struct gpio_dt_spec int_gpio;
#endif /* CONFIG_MCP9808_TRIGGER */
};
int mcp9808_reg_read(const struct device *dev, uint8_t reg, uint16_t *val);
int mcp9808_reg_write_16bit(const struct device *dev, uint8_t reg,
uint16_t val);
int mcp9808_reg_write_8bit(const struct device *dev, uint8_t reg,
uint8_t val);
#ifdef CONFIG_MCP9808_TRIGGER
int mcp9808_attr_set(const struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val);
int mcp9808_trigger_set(const struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
int mcp9808_setup_interrupt(const struct device *dev);
#endif /* CONFIG_MCP9808_TRIGGER */
/* Encode a signed temperature in scaled Celsius to the format used in
* register values.
*/
static inline uint16_t mcp9808_temp_reg_from_signed(int temp)
{
/* Get the 12-bit 2s complement value */
uint16_t rv = temp & MCP9808_TEMP_ABS_MASK;
if (temp < 0) {
rv |= MCP9808_TEMP_SIGN_BIT;
}
return rv;
}
/* Decode a register temperature value to a signed temperature in
* scaled Celsius.
*/
static inline int mcp9808_temp_signed_from_reg(uint16_t reg)
{
int rv = reg & MCP9808_TEMP_ABS_MASK;
if (reg & MCP9808_TEMP_SIGN_BIT) {
/* Convert 12-bit 2s complement to signed negative
* value.
*/
rv = -(1U + (rv ^ MCP9808_TEMP_ABS_MASK));
}
return rv;
}
#endif /* ZEPHYR_DRIVERS_SENSOR_MCP9808_MCP9808_H_ */

View file

@ -1,11 +1,14 @@
# Copyright (c) 2019, Linaro Limited
# Copyright (c) 2024 Vogl Electronic GmbH
# SPDX-License-Identifier: Apache-2.0
description: |
Microchip MCP9808 Digital Temperature Sensor. See
JEDEC JC42.4 compliant temperature sensor. See
http://www.jedec.org/sites/default/files/docs/4_01_04R19.pdf.
Driver based on the Microchip MCP9808 Digital Temperature Sensor. See
http://ww1.microchip.com/downloads/en/DeviceDoc/25095A.pdf.
compatible: "microchip,mcp9808"
compatible: "jedec,jc-42.4-temp"
include: [sensor-device.yaml, i2c-device.yaml]
@ -24,6 +27,10 @@ properties:
description: |
Sensor resolution. Default is 0.0625C (0b11),
which is the power-up default.
0 = 0.5°C
1 = 0.25°C
2 = 0.125°C
3 = 0.0625°C
enum:
- 0 # 0.5C
- 1 # 0.25C

View file

@ -1,7 +1,7 @@
.. _mcp9808-sample:
.. _jc42-sample:
MCP9808 Temperature Sensor
##########################
JEDEC JC 42.4 compliant Temperature Sensor
##########################################
Overview
********
@ -17,6 +17,9 @@ window is moved to center on the new temperature.
Requirements
************
The sample requires a JEDEC JC 42.4 compliant temperature sensor. The
sample is configured to use the MCP9808 sensor.
The MCP9808 digital temperature sensor converts temperatures between -20 |deg|
C and +100 |deg| C to a digital word with |plusminus| 0.5 |deg| C (max.)
accuracy. It is I2C compatible and supports up to 16 devices on the bus.
@ -24,7 +27,8 @@ accuracy. It is I2C compatible and supports up to 16 devices on the bus.
Wiring
*******
The MCP9808 is available in a discrete component form but it is much easier to
The MCP9808, which is a JEDEC JC 42.4 compliant temperature sensor, is
available in a discrete component form but it is much easier to
use it mounted on a breakout board. We used the Adafruit `MCP9808
Sensor`_ breakout board.
@ -37,7 +41,7 @@ After providing a devicetree overlay that specifies the sensor I2C bus
and alert GPIO, build this sample app using:
.. zephyr-app-commands::
:zephyr-app: samples/sensor/mcp9808
:zephyr-app: samples/sensor/jc42
:board: particle_xenon
:goals: build flash

View file

@ -6,7 +6,7 @@
&i2c0 {
mcp9808@18 {
compatible = "microchip,mcp9808";
compatible = "microchip,mcp9808", "jedec,jc-42.4-temp";
reg = <0x18>;
int-gpios = <&gpioc 16 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
};

View file

@ -6,7 +6,7 @@
&i2c1 {
mcp9808@18 {
compatible = "microchip,mcp9808";
compatible = "microchip,mcp9808", "jedec,jc-42.4-temp";
reg = <0x18>;
int-gpios = <&gpiob 4 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
resolution = <3>;

View file

@ -6,7 +6,7 @@
&i2c1 {
mcp9808@18 {
compatible = "microchip,mcp9808";
compatible = "microchip,mcp9808", "jedec,jc-42.4-temp";
reg = <0x18>;
int-gpios = <&gpioa 8 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
resolution = <3>;

View file

@ -6,7 +6,7 @@
&i2c0 {
mcp9808@18 {
compatible = "microchip,mcp9808";
compatible = "microchip,mcp9808", "jedec,jc-42.4-temp";
reg = <0x18>;
int-gpios = <&gpio1 1 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
};

View file

@ -0,0 +1,8 @@
CONFIG_STDOUT_CONSOLE=y
CONFIG_I2C=y
CONFIG_GPIO=y
CONFIG_SENSOR=y
#CONFIG_JC42_TRIGGER_NONE=y
CONFIG_JC42_TRIGGER_OWN_THREAD=y
#CONFIG_JC42_TRIGGER_GLOBAL_THREAD=y
CONFIG_CBPRINTF_FP_SUPPORT=y

View file

@ -1,11 +1,11 @@
sample:
name: MCP9808 Temperature Sensor
name: JEDEC JC 42.4 compliant Temperature Sensor
tests:
sample.sensor.mcp9808:
sample.sensor.jc42:
harness: sensor
tags: sensors
depends_on: i2c
filter: dt_compat_enabled("microchip,mcp9808")
filter: dt_compat_enabled("jedec,jc-42.4-temp")
platform_allow: nucleo_l053r8
integration_platforms:
- nucleo_l053r8

View file

@ -36,7 +36,7 @@ static const char *now_str(void)
return buf;
}
#ifdef CONFIG_MCP9808_TRIGGER
#ifdef CONFIG_JC42_TRIGGER
static struct sensor_trigger sensor_trig;
@ -106,7 +106,7 @@ static void trigger_handler(const struct device *dev,
int main(void)
{
const struct device *const dev = DEVICE_DT_GET_ANY(microchip_mcp9808);
const struct device *const dev = DEVICE_DT_GET_ANY(jedec_jc_42_4_temp);
int rc;
if (dev == NULL) {
@ -118,7 +118,7 @@ int main(void)
return 0;
}
#ifdef CONFIG_MCP9808_TRIGGER
#ifdef CONFIG_JC42_TRIGGER
rc = set_window_ucel(dev, TEMP_INITIAL_CEL * UCEL_PER_CEL);
if (rc == 0) {
sensor_trig.type = SENSOR_TRIG_THRESHOLD;

View file

@ -1,8 +0,0 @@
CONFIG_STDOUT_CONSOLE=y
CONFIG_I2C=y
CONFIG_GPIO=y
CONFIG_SENSOR=y
#CONFIG_MCP9808_TRIGGER_NONE=y
CONFIG_MCP9808_TRIGGER_OWN_THREAD=y
#CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD=y
CONFIG_CBPRINTF_FP_SUPPORT=y

View file

@ -180,8 +180,8 @@ test_i2c_ms5837: ms5837@18 {
reg = <0x18>;
};
test_i2c_mcp9808: mcp9808@19 {
compatible = "microchip,mcp9808";
test_i2c_jc42: jc42@19 {
compatible = "jedec,jc-42.4-temp";
reg = <0x19>;
int-gpios = <&test_gpio 0 0>;
};

View file

@ -33,6 +33,7 @@ CONFIG_IIS2MDC_TRIGGER_GLOBAL_THREAD=y
CONFIG_IIS3DHHC_TRIGGER_GLOBAL_THREAD=y
CONFIG_ISL29035_TRIGGER_GLOBAL_THREAD=y
CONFIG_ISM330DHCX_TRIGGER_GLOBAL_THREAD=y
CONFIG_JC42_TRIGGER_GLOBAL_THREAD=y
CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD=y
CONFIG_LIS2DE12_TRIGGER_GLOBAL_THREAD=y
CONFIG_LIS2DS12_TRIGGER_GLOBAL_THREAD=y
@ -48,7 +49,6 @@ CONFIG_LSM6DSL_TRIGGER_GLOBAL_THREAD=y
CONFIG_LSM6DSO_TRIGGER_GLOBAL_THREAD=y
CONFIG_LSM6DSO16IS_TRIGGER_GLOBAL_THREAD=y
CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD=y
CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD=y
CONFIG_MPU6050_TRIGGER_GLOBAL_THREAD=y
CONFIG_MPU9250_TRIGGER_GLOBAL_THREAD=y
CONFIG_SHT3XD_TRIGGER_GLOBAL_THREAD=y

View file

@ -33,6 +33,7 @@ CONFIG_IIS2MDC_TRIGGER_NONE=y
CONFIG_IIS3DHHC_TRIGGER_NONE=y
CONFIG_ISL29035_TRIGGER_NONE=y
CONFIG_ISM330DHCX_TRIGGER_NONE=y
CONFIG_JC42_TRIGGER_NONE=y
CONFIG_LIS2DH_TRIGGER_NONE=y
CONFIG_LIS2DE12_TRIGGER_NONE=y
CONFIG_LIS2DS12_TRIGGER_NONE=y
@ -48,7 +49,6 @@ CONFIG_LSM6DSL_TRIGGER_NONE=y
CONFIG_LSM6DSO_TRIGGER_NONE=y
CONFIG_LSM6DSO16IS_TRIGGER_NONE=y
CONFIG_LSM6DSV16X_TRIGGER_NONE=y
CONFIG_MCP9808_TRIGGER_NONE=y
CONFIG_MPU6050_TRIGGER_NONE=y
CONFIG_MPU9250_TRIGGER_NONE=y
CONFIG_SHT3XD_TRIGGER_NONE=y

View file

@ -31,6 +31,7 @@ CONFIG_IIS2MDC_TRIGGER_OWN_THREAD=y
CONFIG_IIS3DHHC_TRIGGER_OWN_THREAD=y
CONFIG_ISL29035_TRIGGER_OWN_THREAD=y
CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD=y
CONFIG_JC42_TRIGGER_OWN_THREAD=y
CONFIG_LIS2DH_TRIGGER_OWN_THREAD=y
CONFIG_LIS2DE12_TRIGGER_OWN_THREAD=y
CONFIG_LIS2DS12_TRIGGER_OWN_THREAD=y
@ -46,7 +47,6 @@ CONFIG_LSM6DSO_TRIGGER_OWN_THREAD=y
CONFIG_LSM6DSO16IS_TRIGGER_OWN_THREAD=y
CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD=y
CONFIG_MC3419_TRIGGER_OWN_THREAD=y
CONFIG_MCP9808_TRIGGER_OWN_THREAD=y
CONFIG_MPU6050_TRIGGER_OWN_THREAD=y
CONFIG_MPU9250_TRIGGER_OWN_THREAD=y
CONFIG_SHT3XD_TRIGGER_OWN_THREAD=y