drivers: sensor: mcp9808: fix various problems and improve test

Correct handling of device encoded temperature values, which combine a
12-bit 2s complement signed value with a separate sign bit.  Rework
conversion between device and sensor temperature representations to
support negative temperatures in both domains.

Use a much simpler trigger configuration where the alert is driven by
comparator output, rather than as an interrupt that requires a pair of
I2C transactions to read and clear the flag.

Refactor the trigger infrastructure to use the setup/handle/process
idiom, which reduces duplicated code and to correctly detect alerts
present when the triggers are set.

Completely replace the sample with something that demonstrates
updating upper and lower threshold values to track moving
temperatures.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
This commit is contained in:
Peter A. Bigot 2019-12-20 15:07:16 -06:00 committed by Anas Nashif
parent c393f3f87a
commit f05cbb421d
6 changed files with 366 additions and 191 deletions

View file

@ -1,6 +1,5 @@
/* sensor_mcp9808.c - Driver for MCP9808 temperature sensor */
/*
* Copyright (c) 2019 Peter Bigot Consulting, LLC
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
@ -19,9 +18,11 @@
LOG_MODULE_REGISTER(MCP9808, CONFIG_SENSOR_LOG_LEVEL);
int mcp9808_reg_read(struct mcp9808_data *data, u8_t reg, u16_t *val)
int mcp9808_reg_read(struct device *dev, u8_t reg, u16_t *val)
{
int rc = i2c_write_read(data->i2c_master, data->i2c_slave_addr,
const struct mcp9808_data *data = dev->driver_data;
const struct mcp9808_config *cfg = dev->config->config_info;
int rc = i2c_write_read(data->i2c_master, cfg->i2c_addr,
&reg, sizeof(reg),
val, sizeof(*val));
@ -38,24 +39,21 @@ static int mcp9808_sample_fetch(struct device *dev, enum sensor_channel chan)
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP);
return mcp9808_reg_read(data, MCP9808_REG_TEMP_AMB, &data->reg_val);
return mcp9808_reg_read(dev, MCP9808_REG_TEMP_AMB, &data->reg_val);
}
static int mcp9808_channel_get(struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct mcp9808_data *data = dev->driver_data;
const struct mcp9808_data *data = dev->driver_data;
int temp = mcp9808_temp_signed_from_reg(data->reg_val);
__ASSERT_NO_MSG(chan == SENSOR_CHAN_AMBIENT_TEMP);
val->val1 = (data->reg_val & MCP9808_TEMP_INT_MASK) >>
MCP9808_TEMP_INT_SHIFT;
val->val2 = (data->reg_val & MCP9808_TEMP_FRAC_MASK) * 62500U;
if (data->reg_val & MCP9808_SIGN_BIT) {
val->val1 -= 256;
}
val->val1 = temp / MCP9808_TEMP_SCALE_CEL;
temp -= val->val1 * MCP9808_TEMP_SCALE_CEL;
val->val2 = (temp * 1000000) / MCP9808_TEMP_SCALE_CEL;
return 0;
}
@ -63,31 +61,42 @@ static int mcp9808_channel_get(struct device *dev,
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(struct device *dev)
{
struct mcp9808_data *data = dev->driver_data;
const struct mcp9808_config *cfg = dev->config->config_info;
int rc = 0;
data->i2c_master =
device_get_binding(DT_INST_0_MICROCHIP_MCP9808_BUS_NAME);
data->i2c_master = device_get_binding(cfg->i2c_bus);
if (!data->i2c_master) {
LOG_DBG("mcp9808: i2c master not found: %s",
DT_INST_0_MICROCHIP_MCP9808_BUS_NAME);
LOG_DBG("mcp9808: i2c master not found: %s", cfg->i2c_bus);
return -EINVAL;
}
data->i2c_slave_addr = DT_INST_0_MICROCHIP_MCP9808_BASE_ADDRESS;
#ifdef CONFIG_MCP9808_TRIGGER
rc = mcp9808_setup_interrupt(dev);
#endif /* CONFIG_MCP9808_TRIGGER */
mcp9808_setup_interrupt(dev);
return 0;
return rc;
}
struct mcp9808_data mcp9808_data;
static struct mcp9808_data mcp9808_data;
static const struct mcp9808_config mcp9808_cfg = {
.i2c_bus = DT_INST_0_MICROCHIP_MCP9808_BUS_NAME,
.i2c_addr = DT_INST_0_MICROCHIP_MCP9808_BASE_ADDRESS,
#ifdef CONFIG_MCP9808_TRIGGER
.alert_pin = DT_INST_0_MICROCHIP_MCP9808_INT_GPIOS_PIN,
.alert_flags = DT_INST_0_MICROCHIP_MCP9808_INT_GPIOS_FLAGS,
.alert_controller = DT_INST_0_MICROCHIP_MCP9808_INT_GPIOS_CONTROLLER,
#endif /* CONFIG_MCP9808_TRIGGER */
};
DEVICE_AND_API_INIT(mcp9808, DT_INST_0_MICROCHIP_MCP9808_LABEL, mcp9808_init,
&mcp9808_data, NULL, POST_KERNEL,
&mcp9808_data, &mcp9808_cfg, POST_KERNEL,
CONFIG_SENSOR_INIT_PRIORITY, &mcp9808_api_funcs);

View file

@ -1,4 +1,5 @@
/*
* Copyright (c) 2019 Peter Bigot Consulting, LLC
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
@ -21,24 +22,53 @@
#define MCP9808_REG_CRITICAL 0x04
#define MCP9808_REG_TEMP_AMB 0x05
#define MCP9808_ALERT_INT BIT(0)
#define MCP9808_ALERT_CNT BIT(3)
#define MCP9808_INT_CLEAR BIT(5)
/* 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)
#define MCP9808_SIGN_BIT BIT(12)
#define MCP9808_TEMP_INT_MASK 0x0ff0
#define MCP9808_TEMP_INT_SHIFT 4
#define MCP9808_TEMP_FRAC_MASK 0x000f
#define MCP9808_TEMP_MAX 0xffc
/* 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 ((u16_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)
struct mcp9808_data {
struct device *i2c_master;
u16_t i2c_slave_addr;
u16_t reg_val;
struct gpio_callback gpio_cb;
#ifdef CONFIG_MCP9808_TRIGGER
struct device *alert_gpio;
struct gpio_callback alert_cb;
struct device *dev;
struct sensor_trigger trig;
sensor_trigger_handler_t trigger_handler;
#endif
#ifdef CONFIG_MCP9808_TRIGGER_OWN_THREAD
struct k_sem sem;
@ -46,16 +76,20 @@ struct mcp9808_data {
#ifdef CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD
struct k_work work;
struct device *dev;
#endif
#ifdef CONFIG_MCP9808_TRIGGER
struct sensor_trigger trig;
sensor_trigger_handler_t trigger_handler;
#endif
};
int mcp9808_reg_read(struct mcp9808_data *data, u8_t reg, u16_t *val);
struct mcp9808_config {
const char *i2c_bus;
u16_t i2c_addr;
#ifdef CONFIG_MCP9808_TRIGGER
u8_t alert_pin;
u8_t alert_flags;
const char *alert_controller;
#endif /* CONFIG_MCP9808_TRIGGER */
};
int mcp9808_reg_read(struct device *dev, u8_t reg, u16_t *val);
#ifdef CONFIG_MCP9808_TRIGGER
int mcp9808_attr_set(struct device *dev, enum sensor_channel chan,
@ -64,26 +98,37 @@ int mcp9808_attr_set(struct device *dev, enum sensor_channel chan,
int mcp9808_trigger_set(struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
void mcp9808_setup_interrupt(struct device *dev);
#else
static inline int mcp9808_attr_set(struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
return -ENOTSUP;
}
static inline int mcp9808_trigger_set(struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
return -ENOTSUP;
}
static void mcp9808_setup_interrupt(struct device *dev)
{
}
int mcp9808_setup_interrupt(struct device *dev);
#endif /* CONFIG_MCP9808_TRIGGER */
/* Encode a signed temperature in scaled Celsius to the format used in
* register values.
*/
static inline u16_t mcp9808_temp_reg_from_signed(int temp)
{
/* Get the 12-bit 2s complement value */
u16_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(u16_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,7 +1,5 @@
/* sensor_mcp9808_trigger.c - Trigger support for MCP9808 */
/*
* Copyright (c) 2016 Intel Corporation
* Copyright (c) 2019 Peter Bigot Consulting, LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -16,54 +14,28 @@
LOG_MODULE_DECLARE(MCP9808, CONFIG_SENSOR_LOG_LEVEL);
static int mcp9808_reg_write(struct mcp9808_data *data, u8_t reg, u16_t val)
static int mcp9808_reg_write(struct device *dev, u8_t reg, u16_t val)
{
const struct mcp9808_data *data = dev->driver_data;
const struct mcp9808_config *cfg = dev->config->config_info;
u8_t buf[3] = {
reg,
val >> 8, /* big-endian register storage */
val & 0xFF,
};
return i2c_write(data->i2c_master, buf, sizeof(buf), data->i2c_slave_addr);
}
static int mcp9808_reg_update(struct mcp9808_data *data, u8_t reg,
u16_t mask, u16_t val)
{
u16_t old_val, new_val;
if (mcp9808_reg_read(data, reg, &old_val) < 0) {
return -EIO;
}
new_val = old_val & ~mask;
new_val |= val;
if (new_val == old_val) {
return 0;
}
return mcp9808_reg_write(data, reg, new_val);
return i2c_write(data->i2c_master, buf, sizeof(buf), cfg->i2c_addr);
}
int mcp9808_attr_set(struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
struct mcp9808_data *data = dev->driver_data;
u16_t reg_val = 0U;
u8_t reg_addr;
s32_t val2;
int temp;
__ASSERT_NO_MSG(chan == SENSOR_CHAN_AMBIENT_TEMP);
val2 = val->val2;
while (val2 > 0) {
reg_val += (1 << 2);
val2 -= 250000;
}
reg_val |= val->val1 << 4;
switch (attr) {
case SENSOR_ATTR_LOWER_THRESH:
reg_addr = MCP9808_REG_LOWER_LIMIT;
@ -75,7 +47,53 @@ int mcp9808_attr_set(struct device *dev, enum sensor_channel chan,
return -EINVAL;
}
return mcp9808_reg_write(data, reg_addr, reg_val);
/* 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;
return mcp9808_reg_write(dev, reg_addr,
mcp9808_temp_reg_from_signed(temp));
}
static inline void setup_int(struct device *dev,
bool enable)
{
const struct mcp9808_data *data = dev->driver_data;
const struct mcp9808_config *cfg = dev->config->config_info;
if (enable) {
gpio_pin_enable_callback(data->alert_gpio, cfg->alert_pin);
} else {
gpio_pin_disable_callback(data->alert_gpio, cfg->alert_pin);
}
}
static void handle_int(struct device *dev)
{
struct mcp9808_data *data = dev->driver_data;
setup_int(dev, false);
#if defined(CONFIG_MCP9808_TRIGGER_OWN_THREAD)
k_sem_give(&data->sem);
#elif defined(CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD)
k_work_submit(&data->work);
#endif
}
static void process_int(struct device *dev)
{
struct mcp9808_data *data = dev->driver_data;
if (data->trigger_handler) {
data->trigger_handler(dev, &data->trig);
}
if (data->trigger_handler) {
setup_int(dev, true);
}
}
int mcp9808_trigger_set(struct device *dev,
@ -83,27 +101,40 @@ int mcp9808_trigger_set(struct device *dev,
sensor_trigger_handler_t handler)
{
struct mcp9808_data *data = dev->driver_data;
const struct mcp9808_config *cfg = dev->config->config_info;
int rv = 0;
setup_int(dev, false);
data->trig = *trig;
data->trigger_handler = handler;
return mcp9808_reg_update(data, MCP9808_REG_CONFIG, MCP9808_ALERT_CNT,
handler == NULL ? 0 : MCP9808_ALERT_CNT);
if (handler != NULL) {
u32_t val;
setup_int(dev, true);
rv = gpio_pin_read(data->alert_gpio, cfg->alert_pin, &val);
if ((rv == 0) && (val == 0)) {
handle_int(dev);
}
}
return rv;
}
#ifdef CONFIG_MCP9808_TRIGGER_OWN_THREAD
static void mcp9808_gpio_cb(struct device *dev,
struct gpio_callback *cb, u32_t pins)
static void alert_cb(struct device *dev, struct gpio_callback *cb, u32_t pins)
{
struct mcp9808_data *data =
CONTAINER_OF(cb, struct mcp9808_data, gpio_cb);
CONTAINER_OF(cb, struct mcp9808_data, alert_cb);
ARG_UNUSED(pins);
k_sem_give(&data->sem);
handle_int(data->dev);
}
#ifdef CONFIG_MCP9808_TRIGGER_OWN_THREAD
static void mcp9808_thread_main(int arg1, int arg2)
{
struct device *dev = INT_TO_POINTER(arg1);
@ -111,11 +142,9 @@ static void mcp9808_thread_main(int arg1, int arg2)
ARG_UNUSED(arg2);
while (1) {
while (true) {
k_sem_take(&data->sem, K_FOREVER);
data->trigger_handler(dev, &data->trig);
mcp9808_reg_update(data, MCP9808_REG_CONFIG,
MCP9808_INT_CLEAR, MCP9808_INT_CLEAR);
process_int(dev);
}
}
@ -123,40 +152,29 @@ static K_THREAD_STACK_DEFINE(mcp9808_thread_stack, CONFIG_MCP9808_THREAD_STACK_S
static struct k_thread mcp9808_thread;
#else /* CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD */
static void mcp9808_gpio_cb(struct device *dev,
struct gpio_callback *cb, u32_t pins)
{
struct mcp9808_data *data =
CONTAINER_OF(cb, struct mcp9808_data, gpio_cb);
ARG_UNUSED(pins);
k_work_submit(&data->work);
}
static void mcp9808_gpio_thread_cb(struct k_work *work)
{
struct mcp9808_data *data =
CONTAINER_OF(work, struct mcp9808_data, work);
struct device *dev = data->dev;
data->trigger_handler(dev, &data->trig);
mcp9808_reg_update(data, MCP9808_REG_CONFIG,
MCP9808_INT_CLEAR, MCP9808_INT_CLEAR);
process_int(data->dev);
}
#endif /* CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD */
void mcp9808_setup_interrupt(struct device *dev)
int mcp9808_setup_interrupt(struct device *dev)
{
struct mcp9808_data *data = dev->driver_data;
const struct mcp9808_config *cfg = dev->config->config_info;
struct device *gpio;
int rc = mcp9808_reg_write(dev, MCP9808_REG_CRITICAL,
MCP9808_TEMP_ABS_MASK);
if (rc == 0) {
rc = mcp9808_reg_write(dev, MCP9808_REG_CONFIG,
MCP9808_CFG_ALERT_ENA);
}
mcp9808_reg_update(data, MCP9808_REG_CONFIG, MCP9808_ALERT_INT,
MCP9808_ALERT_INT);
mcp9808_reg_write(data, MCP9808_REG_CRITICAL, MCP9808_TEMP_MAX);
mcp9808_reg_update(data, MCP9808_REG_CONFIG, MCP9808_INT_CLEAR,
MCP9808_INT_CLEAR);
data->dev = dev;
#ifdef CONFIG_MCP9808_TRIGGER_OWN_THREAD
k_sem_init(&data->sem, 0, UINT_MAX);
@ -167,18 +185,27 @@ void mcp9808_setup_interrupt(struct device *dev)
K_PRIO_COOP(CONFIG_MCP9808_THREAD_PRIORITY), 0, K_NO_WAIT);
#else /* CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD */
data->work.handler = mcp9808_gpio_thread_cb;
data->dev = dev;
#endif
#endif /* trigger type */
gpio = device_get_binding(DT_INST_0_MICROCHIP_MCP9808_INT_GPIOS_CONTROLLER);
gpio_pin_configure(gpio, DT_INST_0_MICROCHIP_MCP9808_INT_GPIOS_PIN,
GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE |
GPIO_INT_ACTIVE_LOW | GPIO_INT_DEBOUNCE);
gpio = device_get_binding(cfg->alert_controller);
if (gpio != NULL) {
data->alert_gpio = gpio;
} else {
rc = -ENOENT;
}
gpio_init_callback(&data->gpio_cb,
mcp9808_gpio_cb,
BIT(DT_INST_0_MICROCHIP_MCP9808_INT_GPIOS_PIN));
if (rc == 0) {
rc = gpio_pin_configure(gpio, cfg->alert_pin,
GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE |
GPIO_PUD_PULL_UP |
GPIO_INT_ACTIVE_LOW | GPIO_INT_DEBOUNCE);
}
gpio_add_callback(gpio, &data->gpio_cb);
gpio_pin_enable_callback(gpio, DT_INST_0_MICROCHIP_MCP9808_INT_GPIOS_PIN);
if (rc == 0) {
gpio_init_callback(&data->alert_cb, alert_cb, BIT(cfg->alert_pin));
rc = gpio_add_callback(gpio, &data->alert_cb);
}
return rc;
}

View file

@ -6,42 +6,68 @@ MCP9808 Temperature Sensor
Overview
********
Sample application that periodically reads temperature from the MCP9808 sensor.
This sample application periodically (0.5 Hz) measures the ambient
temperature. The result is written to the console.
If triggered measurements are enabled the sample initializes and
maintains a |plusminus| 2 |deg| C window around the current temperature.
When the temperature moves outside the window an alert is given, and the
window is moved to center on the new temperature.
Requirements
************
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. We do not require pullup resistors on the
data or clock signals as they are already installed on the breakout board.
The MCP9808 is available in a discrete component form but it is much easier to
use it mounted on a breakout board. We used the Adafruit breakout board.
- `MCP9808 Sensor`_
This sample uses the sensor APIs and the provided driver for the MCP9808 sensor.
accuracy. It is I2C compatible and supports up to 16 devices on the bus.
Wiring
*******
The MCP9808 requires 2 wires for the I2C bus plus power and ground. The power
can be either 5V or 3.3V.
We connect the Data and clock wires to Analog ports A4 and A5 which is the I2C
pins on Arduino compatible boards.
In this hookup we are only connecting one device to one of the supported boards.
It reads the temperature and displays it on the console.
References
***********
- http://www.microchip.com/wwwproducts/en/en556182
The MCP9808 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.
.. _`MCP9808 Sensor`: https://www.adafruit.com/product/1782
Building and Running
********************
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
:board: particle_xenon
:goals: build flash
Sample Output
=============
Note that in the capture below output from the trigger callback and the
main thread are interleaved.
.. code-block:: console
*** Booting Zephyr OS build zephyr-v2.1.0-537-gbbdeaa1ae5bb ***
Alert on temp outside [24500, 25500] milli-Celsius
Trtrigger fired 1, temp 15.9375 C
iggAlert on temp outside [15437, 16437] milli-Celsius
er set got 0
0:00:00.017: 15.9375 C
0:00:02.020: 16 C
0:00:04.022: 16.125 C
0:00:06.024: 16.1875 C
trigger fired 2, temp 16.3125 C
Alert on temp outside [15812, 16812] milli-Celsius
0:00:08.027: 16.3125 C
0:00:10.029: 16.375 C
0:00:12.032: 16.5 C
0:00:14.034: 16.5625 C
0:00:16.037: 16.5625 C
0:00:18.039: 16.625 C
0:00:20.042: 16.6875 C
trigger fired 3, temp 16.8125 C
Alert on temp outside [16312, 17312] milli-Celsius
0:00:22.044: 16.8125 C
0:00:24.047: 16.875 C

View file

@ -3,4 +3,6 @@ CONFIG_I2C=y
CONFIG_GPIO=y
CONFIG_SENSOR=y
CONFIG_MCP9808=y
CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD=y
#CONFIG_MCP9808_TRIGGER_NONE=y
CONFIG_MCP9808_TRIGGER_OWN_THREAD=y
#CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD=y

View file

@ -1,4 +1,5 @@
/*
* Copyright (c) 2019 Peter Bigot Consulting, LLC
* Copyright (c) 2016 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
@ -9,54 +10,118 @@
#include <drivers/sensor.h>
#include <stdio.h>
#define UCEL_PER_CEL 1000000
#define UCEL_PER_MCEL 1000
#define TEMP_INITIAL_CEL 25
#define TEMP_WINDOW_HALF_UCEL 500000
static const char *now_str(void)
{
static char buf[16]; /* ...HH:MM:SS.MMM */
u32_t now = k_uptime_get_32();
unsigned int ms = now % MSEC_PER_SEC;
unsigned int s;
unsigned int min;
unsigned int h;
now /= MSEC_PER_SEC;
s = now % 60U;
now /= 60U;
min = now % 60U;
now /= 60U;
h = now;
snprintf(buf, sizeof(buf), "%u:%02u:%02u.%03u",
h, min, s, ms);
return buf;
}
#ifdef CONFIG_MCP9808_TRIGGER
static struct sensor_trigger trig;
static int set_window(struct device *dev,
const struct sensor_value *temp)
{
const int temp_ucel = temp->val1 * UCEL_PER_CEL + temp->val2;
const int low_ucel = temp_ucel - TEMP_WINDOW_HALF_UCEL;
const int high_ucel = temp_ucel + TEMP_WINDOW_HALF_UCEL;
struct sensor_value val = {
.val1 = low_ucel / UCEL_PER_CEL,
.val2 = low_ucel % UCEL_PER_CEL,
};
int rc = sensor_attr_set(dev, SENSOR_CHAN_AMBIENT_TEMP,
SENSOR_ATTR_LOWER_THRESH, &val);
if (rc == 0) {
val.val1 = high_ucel / UCEL_PER_CEL,
val.val2 = high_ucel % UCEL_PER_CEL,
rc = sensor_attr_set(dev, SENSOR_CHAN_AMBIENT_TEMP,
SENSOR_ATTR_UPPER_THRESH, &val);
}
if (rc == 0) {
printf("Alert on temp outside [%d, %d] milli-Celsius\n",
low_ucel / UCEL_PER_MCEL,
high_ucel / UCEL_PER_MCEL);
}
return rc;
}
static inline int set_window_ucel(struct device *dev,
int temp_ucel)
{
struct sensor_value val = {
.val1 = temp_ucel / UCEL_PER_CEL,
.val2 = temp_ucel % UCEL_PER_CEL,
};
return set_window(dev, &val);
}
static void trigger_handler(struct device *dev, struct sensor_trigger *trig)
{
struct sensor_value temp;
static size_t cnt;
++cnt;
sensor_sample_fetch(dev);
sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
printf("trigger fired, temp %d.%06d\n", temp.val1, temp.val2);
printf("trigger fired %u, temp %g deg C\n", cnt,
sensor_value_to_double(&temp));
set_window(dev, &temp);
}
#endif
void main(void)
{
struct device *dev = device_get_binding("MCP9808");
const char *const devname = DT_INST_0_MICROCHIP_MCP9808_LABEL;
struct device *dev = device_get_binding(devname);
int rc;
if (dev == NULL) {
printf("device not found. aborting test.\n");
printf("Device %s not found.\n", devname);
return;
}
#ifdef DEBUG
printf("dev %p\n", dev);
printf("dev %p name %s\n", dev, dev->config->name);
#endif
#ifdef CONFIG_MCP9808_TRIGGER
struct sensor_value val;
struct sensor_trigger trig;
rc = set_window_ucel(dev, TEMP_INITIAL_CEL * UCEL_PER_CEL);
if (rc == 0) {
trig.type = SENSOR_TRIG_THRESHOLD;
trig.chan = SENSOR_CHAN_AMBIENT_TEMP;
rc = sensor_trigger_set(dev, &trig, trigger_handler);
}
val.val1 = 26;
val.val2 = 0;
sensor_attr_set(dev, SENSOR_CHAN_AMBIENT_TEMP,
SENSOR_ATTR_UPPER_THRESH, &val);
trig.type = SENSOR_TRIG_THRESHOLD;
trig.chan = SENSOR_CHAN_AMBIENT_TEMP;
if (sensor_trigger_set(dev, &trig, trigger_handler)) {
printf("Could not set trigger. aborting test.\n");
if (rc != 0) {
printf("Trigger set failed: %d\n", rc);
return;
}
printk("Trigger set got %d\n", rc);
#endif
while (1) {
struct sensor_value temp;
int rc;
rc = sensor_sample_fetch(dev);
if (rc != 0) {
@ -70,8 +135,9 @@ void main(void)
break;
}
printf("temp: %d.%06d\n", temp.val1, temp.val2);
printf("%s: %g C\n", now_str(),
sensor_value_to_double(&temp));
k_sleep(K_MSEC(2000));
k_sleep(K_SECONDS(2));
}
}