sensor: Add threshold trigger support for MCP9808
The MCP9808 supports threshold triggering. Add support for this feature in the driver and the sample application. If triggering is activated, the driver can create its own fiber or use the system-wide sensor fiber. Origin: Original Change-Id: Ie825a22245cb48cbdffba3049011e4d305975d53 Signed-off-by: Vlad Dogaru <vlad.dogaru@intel.com>
This commit is contained in:
parent
d30b3ca516
commit
49d96d95ab
|
@ -78,3 +78,52 @@ config MCP9808_I2C_DEV_NAME
|
|||
help
|
||||
Specify the device name of the I2C master device to which MCP9808 is
|
||||
connected.
|
||||
|
||||
choice
|
||||
prompt "MCP9808 trigger mode"
|
||||
depends on MCP9808
|
||||
default MCP9808_TRIGGER_NONE
|
||||
|
||||
config MCP9808_TRIGGER_NONE
|
||||
bool "No trigger"
|
||||
|
||||
config MCP9808_TRIGGER_GLOBAL_FIBER
|
||||
depends on GPIO
|
||||
select MCP9808_TRIGGER
|
||||
select SENSOR_DELAYED_WORK
|
||||
bool "Use global fiber"
|
||||
|
||||
config MCP9808_TRIGGER_OWN_FIBER
|
||||
depends on GPIO
|
||||
select MCP9808_TRIGGER
|
||||
bool "Use own fiber"
|
||||
|
||||
endchoice
|
||||
|
||||
config MCP9808_TRIGGER
|
||||
bool
|
||||
depends on MCP9808
|
||||
|
||||
config MCP9808_GPIO_CONTROLLER
|
||||
string "GPIO controller for MCP9808 interrupt"
|
||||
depends on MCP9808 && MCP9808_TRIGGER
|
||||
default "GPIO_0"
|
||||
help
|
||||
The GPIO controller the MCP9808 interrupt is connected to.
|
||||
|
||||
config MCP9808_GPIO_PIN
|
||||
int "GPIO pin for MCP9808 interrupt"
|
||||
depends on MCP9808 && MCP9808_TRIGGER
|
||||
default 3
|
||||
help
|
||||
The GPIO pin the MCP9808 interrupt is connected to.
|
||||
|
||||
config MCP9808_FIBER_STACK_SIZE
|
||||
int "Sensor delayed work fiber stack size"
|
||||
depends on MCP9808 && MCP9808_TRIGGER_OWN_FIBER
|
||||
default 1024
|
||||
|
||||
config MCP9808_FIBER_PRIORITY
|
||||
int "MCP9808 fiber priority"
|
||||
depends on MCP9808 && MCP9808_TRIGGER_OWN_FIBER
|
||||
default 10
|
||||
|
|
|
@ -3,3 +3,4 @@ ccflags-y +=-I$(srctree)/drivers
|
|||
obj-$(CONFIG_SENSOR_DELAYED_WORK) += sensor.o
|
||||
|
||||
obj-$(CONFIG_MCP9808) += sensor_mcp9808.o
|
||||
obj-$(CONFIG_MCP9808_TRIGGER) += sensor_mcp9808_trigger.o
|
||||
|
|
|
@ -18,20 +18,13 @@
|
|||
|
||||
#include <nanokernel.h>
|
||||
#include <i2c.h>
|
||||
#include <sensor.h>
|
||||
#include <init.h>
|
||||
#include <gpio.h>
|
||||
#include <misc/byteorder.h>
|
||||
#include "sensor_mcp9808.h"
|
||||
|
||||
#ifndef CONFIG_SENSOR_DEBUG
|
||||
#define DBG(...) { ; }
|
||||
#else
|
||||
#include <misc/printk.h>
|
||||
#define DBG printk
|
||||
#endif /* CONFIG_SENSOR_DEBUG */
|
||||
struct mcp9808_data mcp9808_data;
|
||||
|
||||
static int mcp9808_reg_read(struct mcp9808_data *data, uint8_t reg, uint16_t *val)
|
||||
int mcp9808_reg_read(struct mcp9808_data *data, uint8_t reg, uint16_t *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -93,6 +86,8 @@ static int mcp9808_channel_get(struct device *dev,
|
|||
static struct sensor_driver_api mcp9808_api_funcs = {
|
||||
.sample_fetch = mcp9808_sample_fetch,
|
||||
.channel_get = mcp9808_channel_get,
|
||||
.attr_set = mcp9808_attr_set,
|
||||
.trigger_set = mcp9808_trigger_set,
|
||||
};
|
||||
|
||||
int mcp9808_init(struct device *dev)
|
||||
|
@ -110,10 +105,10 @@ int mcp9808_init(struct device *dev)
|
|||
|
||||
data->i2c_slave_addr = CONFIG_MCP9808_I2C_ADDR;
|
||||
|
||||
mcp9808_setup_interrupt(dev);
|
||||
|
||||
return DEV_OK;
|
||||
}
|
||||
|
||||
static struct mcp9808_data mcp9808_data;
|
||||
|
||||
DEVICE_INIT(mcp9808, CONFIG_MCP9808_DEV_NAME, mcp9808_init, &mcp9808_data,
|
||||
NULL, SECONDARY, CONFIG_MCP9808_INIT_PRIORITY);
|
||||
|
|
|
@ -19,20 +19,82 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <device.h>
|
||||
#include <sensor.h>
|
||||
#include <misc/util.h>
|
||||
|
||||
#ifndef CONFIG_SENSOR_DEBUG
|
||||
#define DBG(...) { ; }
|
||||
#else
|
||||
#include <misc/printk.h>
|
||||
#define DBG printk
|
||||
#endif /* CONFIG_SENSOR_DEBUG */
|
||||
|
||||
#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
|
||||
|
||||
#define MCP9808_ALERT_INT BIT(0)
|
||||
#define MCP9808_ALERT_CNT BIT(3)
|
||||
#define MCP9808_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
|
||||
|
||||
struct mcp9808_data {
|
||||
struct device *i2c_master;
|
||||
uint16_t i2c_slave_addr;
|
||||
|
||||
uint16_t reg_val;
|
||||
|
||||
#ifdef CONFIG_MCP9808_TRIGGER_OWN_FIBER
|
||||
struct nano_sem sem;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MCP9808_TRIGGER_GLOBAL_FIBER
|
||||
struct sensor_work work;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MCP9808_TRIGGER
|
||||
struct sensor_trigger trig;
|
||||
sensor_trigger_handler_t trigger_handler;
|
||||
#endif
|
||||
};
|
||||
|
||||
int mcp9808_reg_read(struct mcp9808_data *data, uint8_t reg, uint16_t *val);
|
||||
|
||||
#ifdef CONFIG_MCP9808_TRIGGER
|
||||
int mcp9808_attr_set(struct device *dev, enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
const struct sensor_value *val);
|
||||
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 DEV_INVALID_OP;
|
||||
}
|
||||
|
||||
static inline int mcp9808_trigger_set(struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
return DEV_INVALID_OP;
|
||||
}
|
||||
|
||||
static void mcp9808_setup_interrupt(struct device *dev)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_MCP9808_TRIGGER */
|
||||
|
||||
#endif /* __SENSOR_MCP9808_H__ */
|
||||
|
|
201
drivers/sensor/sensor_mcp9808_trigger.c
Normal file
201
drivers/sensor/sensor_mcp9808_trigger.c
Normal file
|
@ -0,0 +1,201 @@
|
|||
/* sensor_mcp9808_trigger.c - Trigger support for MCP9808 */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <nanokernel.h>
|
||||
#include <i2c.h>
|
||||
#include <gpio.h>
|
||||
#include <misc/byteorder.h>
|
||||
#include "sensor_mcp9808.h"
|
||||
|
||||
extern struct mcp9808_data mcp9808_data;
|
||||
|
||||
static int mcp9808_reg_write(struct mcp9808_data *data, uint8_t reg, uint16_t val)
|
||||
{
|
||||
uint16_t be_val = sys_cpu_to_be16(val);
|
||||
|
||||
struct i2c_msg msgs[2] = {
|
||||
{
|
||||
.buf = ®,
|
||||
.len = 1,
|
||||
.flags = I2C_MSG_WRITE | I2C_MSG_RESTART,
|
||||
},
|
||||
{
|
||||
.buf = (uint8_t *) &be_val,
|
||||
.len = 2,
|
||||
.flags = I2C_MSG_WRITE | I2C_MSG_STOP,
|
||||
},
|
||||
};
|
||||
|
||||
return i2c_transfer(data->i2c_master, msgs, 2, data->i2c_slave_addr);
|
||||
}
|
||||
|
||||
static int mcp9808_reg_update(struct mcp9808_data *data, uint8_t reg,
|
||||
uint16_t mask, uint16_t val)
|
||||
{
|
||||
int ret;
|
||||
uint16_t old_val, new_val;
|
||||
|
||||
ret = mcp9808_reg_read(data, reg, &old_val);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
new_val = old_val & ~mask;
|
||||
new_val |= val;
|
||||
|
||||
if (new_val == old_val) {
|
||||
return DEV_OK;
|
||||
}
|
||||
|
||||
return mcp9808_reg_write(data, reg, new_val);
|
||||
}
|
||||
|
||||
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;
|
||||
uint16_t reg_val = 0;
|
||||
uint8_t reg_addr;
|
||||
int32_t val2;
|
||||
|
||||
#ifdef CONFIG_SENSOR_DEBUG
|
||||
if (chan != SENSOR_CHAN_TEMP) {
|
||||
return DEV_INVALID_CONF;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (val->type) {
|
||||
case SENSOR_TYPE_INT_PLUS_MICRO:
|
||||
val2 = val->val2;
|
||||
while (val2 > 0) {
|
||||
reg_val += (1 << 2);
|
||||
val2 -= 250000;
|
||||
}
|
||||
/* Fall through. */
|
||||
case SENSOR_TYPE_INT:
|
||||
reg_val |= val->val1 << 4;
|
||||
break;
|
||||
default:
|
||||
return DEV_INVALID_CONF;
|
||||
}
|
||||
|
||||
switch (attr) {
|
||||
case SENSOR_ATTR_LOWER_THRESH:
|
||||
reg_addr = MCP9808_REG_LOWER_LIMIT;
|
||||
break;
|
||||
case SENSOR_ATTR_UPPER_THRESH:
|
||||
reg_addr = MCP9808_REG_UPPER_LIMIT;
|
||||
break;
|
||||
default:
|
||||
return DEV_INVALID_CONF;
|
||||
}
|
||||
|
||||
return mcp9808_reg_write(data, reg_addr, reg_val);
|
||||
}
|
||||
|
||||
int mcp9808_trigger_set(struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
struct mcp9808_data *data = dev->driver_data;
|
||||
|
||||
data->trig = *trig;
|
||||
data->trigger_handler = handler;
|
||||
|
||||
return mcp9808_reg_update(data, MCP9808_REG_CONFIG, MCP9808_ALERT_CNT,
|
||||
handler == NULL ? 0 : MCP9808_ALERT_CNT);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MCP9808_TRIGGER_OWN_FIBER
|
||||
|
||||
static void mcp9808_gpio_cb(struct device *dev, uint32_t pin)
|
||||
{
|
||||
struct mcp9808_data *data = &mcp9808_data;
|
||||
|
||||
nano_isr_sem_give(&data->sem);
|
||||
}
|
||||
|
||||
static void mcp9808_fiber_main(int arg1, int arg2)
|
||||
{
|
||||
struct device *dev = INT_TO_POINTER(arg1);
|
||||
struct mcp9808_data *data = dev->driver_data;
|
||||
|
||||
ARG_UNUSED(arg2);
|
||||
|
||||
while (1) {
|
||||
nano_fiber_sem_take(&data->sem, TICKS_UNLIMITED);
|
||||
data->trigger_handler(dev, &data->trig);
|
||||
mcp9808_reg_update(data, MCP9808_REG_CONFIG,
|
||||
MCP9808_INT_CLEAR, MCP9808_INT_CLEAR);
|
||||
}
|
||||
}
|
||||
|
||||
static char __stack mcp9808_fiber_stack[CONFIG_MCP9808_FIBER_STACK_SIZE];
|
||||
|
||||
#else /* CONFIG_MCP9808_TRIGGER_GLOBAL_FIBER */
|
||||
|
||||
static void mcp9808_gpio_cb(struct device *dev, uint32_t pin)
|
||||
{
|
||||
struct mcp9808_data *data = &mcp9808_data;
|
||||
|
||||
nano_isr_fifo_put(sensor_get_work_fifo(), &data->work);
|
||||
}
|
||||
|
||||
static void mcp9808_gpio_fiber_cb(void *arg)
|
||||
{
|
||||
struct device *dev = arg;
|
||||
struct mcp9808_data *data = dev->driver_data;
|
||||
|
||||
data->trigger_handler(dev, &data->trig);
|
||||
mcp9808_reg_update(data, MCP9808_REG_CONFIG,
|
||||
MCP9808_INT_CLEAR, MCP9808_INT_CLEAR);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MCP9808_TRIGGER_GLOBAL_FIBER */
|
||||
|
||||
void mcp9808_setup_interrupt(struct device *dev)
|
||||
{
|
||||
struct mcp9808_data *data = dev->driver_data;
|
||||
struct device *gpio;
|
||||
|
||||
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);
|
||||
|
||||
#ifdef CONFIG_MCP9808_TRIGGER_OWN_FIBER
|
||||
nano_sem_init(&data->sem);
|
||||
|
||||
fiber_fiber_start(mcp9808_fiber_stack,
|
||||
CONFIG_MCP9808_FIBER_STACK_SIZE,
|
||||
mcp9808_fiber_main, POINTER_TO_INT(dev), 0,
|
||||
CONFIG_MCP9808_FIBER_PRIORITY, 0);
|
||||
#else /* CONFIG_MCP9808_TRIGGER_GLOBAL_FIBER */
|
||||
data->work.handler = mcp9808_gpio_fiber_cb;
|
||||
data->work.arg = dev;
|
||||
#endif
|
||||
|
||||
gpio = device_get_binding(CONFIG_MCP9808_GPIO_CONTROLLER);
|
||||
gpio_pin_configure(gpio, CONFIG_MCP9808_GPIO_PIN,
|
||||
GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE |
|
||||
GPIO_INT_ACTIVE_LOW | GPIO_INT_DEBOUNCE);
|
||||
gpio_set_callback(gpio, mcp9808_gpio_cb);
|
||||
gpio_pin_enable_callback(gpio, CONFIG_MCP9808_GPIO_PIN);
|
||||
}
|
|
@ -4,8 +4,11 @@ CONFIG_I2C=y
|
|||
CONFIG_I2C_QUARK_SE_SS=y
|
||||
CONFIG_I2C_QUARK_SE_SS_0=y
|
||||
|
||||
CONFIG_GPIO=y
|
||||
|
||||
CONFIG_NANO_TIMEOUTS=y
|
||||
|
||||
CONFIG_SENSOR=y
|
||||
CONFIG_SENSOR_DEBUG=y
|
||||
CONFIG_MCP9808=y
|
||||
CONFIG_MCP9808_TRIGGER_GLOBAL=y
|
||||
|
|
|
@ -28,6 +28,18 @@
|
|||
#define PRINT printk
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MCP9808_TRIGGER
|
||||
static void trigger_handler(struct device *dev, struct sensor_trigger *trig)
|
||||
{
|
||||
struct sensor_value temp;
|
||||
|
||||
sensor_sample_fetch(dev);
|
||||
sensor_channel_get(dev, SENSOR_CHAN_TEMP, &temp);
|
||||
|
||||
PRINT("trigger fired, temp %d.%06d\n", temp.val1, temp.val2);
|
||||
}
|
||||
#endif
|
||||
|
||||
void main(void)
|
||||
{
|
||||
struct device *dev = device_get_binding("MCP9808");
|
||||
|
@ -42,6 +54,22 @@ void main(void)
|
|||
PRINT("dev %p name %s\n", dev, dev->config->name);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MCP9808_TRIGGER
|
||||
struct sensor_value val;
|
||||
struct sensor_trigger trig;
|
||||
|
||||
val.type = SENSOR_TYPE_INT;
|
||||
val.val1 = 26;
|
||||
|
||||
sensor_attr_set(dev, SENSOR_CHAN_TEMP,
|
||||
SENSOR_ATTR_UPPER_THRESH, &val);
|
||||
|
||||
trig.type = SENSOR_TRIG_THRESHOLD;
|
||||
trig.chan = SENSOR_CHAN_TEMP;
|
||||
|
||||
sensor_trigger_set(dev, &trig, trigger_handler);
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
struct sensor_value temp;
|
||||
|
||||
|
|
Loading…
Reference in a new issue