From f05cbb421ddfea03961b552977e4336d0ebdef63 Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Fri, 20 Dec 2019 15:07:16 -0600 Subject: [PATCH] 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 --- drivers/sensor/mcp9808/mcp9808.c | 55 ++++--- drivers/sensor/mcp9808/mcp9808.h | 121 ++++++++++----- drivers/sensor/mcp9808/mcp9808_trigger.c | 185 +++++++++++++---------- samples/sensor/mcp9808/README.rst | 78 ++++++---- samples/sensor/mcp9808/prj.conf | 4 +- samples/sensor/mcp9808/src/main.c | 114 +++++++++++--- 6 files changed, 366 insertions(+), 191 deletions(-) diff --git a/drivers/sensor/mcp9808/mcp9808.c b/drivers/sensor/mcp9808/mcp9808.c index 434aeddd58..3b19dae361 100644 --- a/drivers/sensor/mcp9808/mcp9808.c +++ b/drivers/sensor/mcp9808/mcp9808.c @@ -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, ®, 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); diff --git a/drivers/sensor/mcp9808/mcp9808.h b/drivers/sensor/mcp9808/mcp9808.h index 0bd684f75f..5ee2791400 100644 --- a/drivers/sensor/mcp9808/mcp9808.h +++ b/drivers/sensor/mcp9808/mcp9808.h @@ -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_ */ diff --git a/drivers/sensor/mcp9808/mcp9808_trigger.c b/drivers/sensor/mcp9808/mcp9808_trigger.c index 3f0f774cf8..12eaae36c8 100644 --- a/drivers/sensor/mcp9808/mcp9808_trigger.c +++ b/drivers/sensor/mcp9808/mcp9808_trigger.c @@ -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; } diff --git a/samples/sensor/mcp9808/README.rst b/samples/sensor/mcp9808/README.rst index 4c7baa42f5..3901b5e40d 100644 --- a/samples/sensor/mcp9808/README.rst +++ b/samples/sensor/mcp9808/README.rst @@ -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 diff --git a/samples/sensor/mcp9808/prj.conf b/samples/sensor/mcp9808/prj.conf index 03c6d388cc..f5524b5c9f 100644 --- a/samples/sensor/mcp9808/prj.conf +++ b/samples/sensor/mcp9808/prj.conf @@ -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 diff --git a/samples/sensor/mcp9808/src/main.c b/samples/sensor/mcp9808/src/main.c index 78c76bcaf8..5809945a89 100644 --- a/samples/sensor/mcp9808/src/main.c +++ b/samples/sensor/mcp9808/src/main.c @@ -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 #include +#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)); } }