Skip to content

Commit 5f08738

Browse files
committed
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 <[email protected]>
1 parent de345e2 commit 5f08738

File tree

6 files changed

+360
-185
lines changed

6 files changed

+360
-185
lines changed

drivers/sensor/mcp9808/mcp9808.c

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
/* sensor_mcp9808.c - Driver for MCP9808 temperature sensor */
2-
31
/*
2+
* Copyright (c) 2019 Peter Bigot Consulting, LLC
43
* Copyright (c) 2016 Intel Corporation
54
*
65
* SPDX-License-Identifier: Apache-2.0
@@ -19,9 +18,11 @@
1918

2019
LOG_MODULE_REGISTER(MCP9808, CONFIG_SENSOR_LOG_LEVEL);
2120

22-
int mcp9808_reg_read(struct mcp9808_data *data, u8_t reg, u16_t *val)
21+
int mcp9808_reg_read(struct device *dev, u8_t reg, u16_t *val)
2322
{
24-
int rc = i2c_write_read(data->i2c_master, data->i2c_slave_addr,
23+
const struct mcp9808_data *data = dev->driver_data;
24+
const struct mcp9808_config *cfg = dev->config->config_info;
25+
int rc = i2c_write_read(data->i2c_master, cfg->i2c_addr,
2526
&reg, sizeof(reg),
2627
val, sizeof(*val));
2728

@@ -38,56 +39,64 @@ static int mcp9808_sample_fetch(struct device *dev, enum sensor_channel chan)
3839

3940
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP);
4041

41-
return mcp9808_reg_read(data, MCP9808_REG_TEMP_AMB, &data->reg_val);
42+
return mcp9808_reg_read(dev, MCP9808_REG_TEMP_AMB, &data->reg_val);
4243
}
4344

4445
static int mcp9808_channel_get(struct device *dev,
4546
enum sensor_channel chan,
4647
struct sensor_value *val)
4748
{
48-
struct mcp9808_data *data = dev->driver_data;
49+
const struct mcp9808_data *data = dev->driver_data;
50+
int temp = mcp9808_temp_signed_from_reg(data->reg_val);
4951

5052
__ASSERT_NO_MSG(chan == SENSOR_CHAN_AMBIENT_TEMP);
5153

52-
val->val1 = (data->reg_val & MCP9808_TEMP_INT_MASK) >>
53-
MCP9808_TEMP_INT_SHIFT;
54-
val->val2 = (data->reg_val & MCP9808_TEMP_FRAC_MASK) * 62500U;
55-
56-
if (data->reg_val & MCP9808_SIGN_BIT) {
57-
val->val1 -= 256;
58-
}
54+
val->val1 = temp / MCP9808_TEMP_SCALE_CEL;
55+
temp -= val->val1 * MCP9808_TEMP_SCALE_CEL;
56+
val->val2 = (temp * 1000000) / MCP9808_TEMP_SCALE_CEL;
5957

6058
return 0;
6159
}
6260

6361
static const struct sensor_driver_api mcp9808_api_funcs = {
6462
.sample_fetch = mcp9808_sample_fetch,
6563
.channel_get = mcp9808_channel_get,
64+
#ifdef CONFIG_MCP9808_TRIGGER
6665
.attr_set = mcp9808_attr_set,
6766
.trigger_set = mcp9808_trigger_set,
67+
#endif /* CONFIG_MCP9808_TRIGGER */
6868
};
6969

7070
int mcp9808_init(struct device *dev)
7171
{
7272
struct mcp9808_data *data = dev->driver_data;
73+
const struct mcp9808_config *cfg = dev->config->config_info;
74+
int rc = 0;
7375

74-
data->i2c_master =
75-
device_get_binding(DT_INST_0_MICROCHIP_MCP9808_BUS_NAME);
76+
data->i2c_master = device_get_binding(cfg->i2c_bus);
7677
if (!data->i2c_master) {
77-
LOG_DBG("mcp9808: i2c master not found: %s",
78-
DT_INST_0_MICROCHIP_MCP9808_BUS_NAME);
78+
LOG_DBG("mcp9808: i2c master not found: %s", cfg->i2c_bus);
7979
return -EINVAL;
8080
}
8181

82-
data->i2c_slave_addr = DT_INST_0_MICROCHIP_MCP9808_BASE_ADDRESS;
82+
#ifdef CONFIG_MCP9808_TRIGGER
83+
rc = mcp9808_setup_interrupt(dev);
84+
#endif /* CONFIG_MCP9808_TRIGGER */
8385

84-
mcp9808_setup_interrupt(dev);
85-
86-
return 0;
86+
return rc;
8787
}
8888

89-
struct mcp9808_data mcp9808_data;
89+
static struct mcp9808_data mcp9808_data;
90+
static const struct mcp9808_config mcp9808_cfg = {
91+
.i2c_bus = DT_INST_0_MICROCHIP_MCP9808_BUS_NAME,
92+
.i2c_addr = DT_INST_0_MICROCHIP_MCP9808_BASE_ADDRESS,
93+
#ifdef CONFIG_MCP9808_TRIGGER
94+
.alert_pin = DT_INST_0_MICROCHIP_MCP9808_INT_GPIOS_PIN,
95+
.alert_flags = DT_INST_0_MICROCHIP_MCP9808_INT_GPIOS_FLAGS,
96+
.alert_controller = DT_INST_0_MICROCHIP_MCP9808_INT_GPIOS_CONTROLLER,
97+
#endif /* CONFIG_MCP9808_TRIGGER */
98+
};
9099

91100
DEVICE_AND_API_INIT(mcp9808, DT_INST_0_MICROCHIP_MCP9808_LABEL, mcp9808_init,
92-
&mcp9808_data, NULL, POST_KERNEL,
101+
&mcp9808_data, &mcp9808_cfg, POST_KERNEL,
93102
CONFIG_SENSOR_INIT_PRIORITY, &mcp9808_api_funcs);

drivers/sensor/mcp9808/mcp9808.h

Lines changed: 77 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright (c) 2019 Peter Bigot Consulting, LLC
23
* Copyright (c) 2016 Intel Corporation
34
*
45
* SPDX-License-Identifier: Apache-2.0
@@ -21,41 +22,74 @@
2122
#define MCP9808_REG_CRITICAL 0x04
2223
#define MCP9808_REG_TEMP_AMB 0x05
2324

24-
#define MCP9808_ALERT_INT BIT(0)
25-
#define MCP9808_ALERT_CNT BIT(3)
26-
#define MCP9808_INT_CLEAR BIT(5)
27-
28-
#define MCP9808_SIGN_BIT BIT(12)
29-
#define MCP9808_TEMP_INT_MASK 0x0ff0
30-
#define MCP9808_TEMP_INT_SHIFT 4
31-
#define MCP9808_TEMP_FRAC_MASK 0x000f
32-
33-
#define MCP9808_TEMP_MAX 0xffc
25+
/* 16 bits control configuration and state.
26+
*
27+
* * Bit 0 controls alert signal output mode
28+
* * Bit 1 controls interrupt polarity
29+
* * Bit 2 disables upper and lower threshold checking
30+
* * Bit 3 enables alert signal output
31+
* * Bit 4 records alert status
32+
* * Bit 5 records interrupt status
33+
* * Bit 6 locks the upper/lower window registers
34+
* * Bit 7 locks the critical register
35+
* * Bit 8 enters shutdown mode
36+
* * Bits 9-10 control threshold hysteresis
37+
*/
38+
#define MCP9808_CFG_ALERT_MODE_INT BIT(0)
39+
#define MCP9808_CFG_ALERT_ENA BIT(3)
40+
#define MCP9808_CFG_ALERT_STATE BIT(4)
41+
#define MCP9808_CFG_INT_CLEAR BIT(5)
42+
43+
/* 16 bits are used for temperature and state encoding:
44+
* * Bits 0..11 encode the temperature in a 2s complement signed value
45+
* in Celsius with 1/16 Cel resolution
46+
* * Bit 12 is set to indicate a negative temperature
47+
* * Bit 13 is set to indicate a temperature below the lower threshold
48+
* * Bit 14 is set to indicate a temperature above the upper threshold
49+
* * Bit 15 is set to indicate a temperature above the critical threshold
50+
*/
51+
#define MCP9808_TEMP_SCALE_CEL 16 /* signed */
52+
#define MCP9808_TEMP_SIGN_BIT BIT(12)
53+
#define MCP9808_TEMP_ABS_MASK ((u16_t)(MCP9808_TEMP_SIGN_BIT - 1U))
54+
#define MCP9808_TEMP_LWR_BIT BIT(13)
55+
#define MCP9808_TEMP_UPR_BIT BIT(14)
56+
#define MCP9808_TEMP_CRT_BIT BIT(15)
3457

3558
struct mcp9808_data {
3659
struct device *i2c_master;
37-
u16_t i2c_slave_addr;
3860

3961
u16_t reg_val;
4062

41-
struct gpio_callback gpio_cb;
63+
#ifdef CONFIG_MCP9808_TRIGGER
64+
struct device *alert_gpio;
65+
struct gpio_callback alert_cb;
66+
67+
struct device *dev;
68+
69+
struct sensor_trigger trig;
70+
sensor_trigger_handler_t trigger_handler;
71+
#endif
4272

4373
#ifdef CONFIG_MCP9808_TRIGGER_OWN_THREAD
4474
struct k_sem sem;
4575
#endif
4676

4777
#ifdef CONFIG_MCP9808_TRIGGER_GLOBAL_THREAD
4878
struct k_work work;
49-
struct device *dev;
5079
#endif
80+
};
5181

82+
struct mcp9808_config {
83+
const char *i2c_bus;
84+
u16_t i2c_addr;
5285
#ifdef CONFIG_MCP9808_TRIGGER
53-
struct sensor_trigger trig;
54-
sensor_trigger_handler_t trigger_handler;
55-
#endif
86+
u8_t alert_pin;
87+
u8_t alert_flags;
88+
const char *alert_controller;
89+
#endif /* CONFIG_MCP9808_TRIGGER */
5690
};
5791

58-
int mcp9808_reg_read(struct mcp9808_data *data, u8_t reg, u16_t *val);
92+
int mcp9808_reg_read(struct device *dev, u8_t reg, u16_t *val);
5993

6094
#ifdef CONFIG_MCP9808_TRIGGER
6195
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,
6498
int mcp9808_trigger_set(struct device *dev,
6599
const struct sensor_trigger *trig,
66100
sensor_trigger_handler_t handler);
67-
void mcp9808_setup_interrupt(struct device *dev);
68-
#else
69-
static inline int mcp9808_attr_set(struct device *dev,
70-
enum sensor_channel chan,
71-
enum sensor_attribute attr,
72-
const struct sensor_value *val)
73-
{
74-
return -ENOTSUP;
75-
}
101+
int mcp9808_setup_interrupt(struct device *dev);
102+
#endif /* CONFIG_MCP9808_TRIGGER */
76103

77-
static inline int mcp9808_trigger_set(struct device *dev,
78-
const struct sensor_trigger *trig,
79-
sensor_trigger_handler_t handler)
104+
/* Encode a signed temperature in scaled Celsius to the format used in
105+
* register values.
106+
*/
107+
static inline u16_t mcp9808_temp_reg_from_signed(int temp)
80108
{
81-
return -ENOTSUP;
109+
/* Get the 12-bit 2s complement value */
110+
u16_t rv = temp & MCP9808_TEMP_ABS_MASK;
111+
112+
if (temp < 0) {
113+
rv |= MCP9808_TEMP_SIGN_BIT;
114+
}
115+
return rv;
82116
}
83117

84-
static void mcp9808_setup_interrupt(struct device *dev)
118+
/* Decode a register temperature value to a signed temperature in
119+
* scaled Celsius.
120+
*/
121+
static inline int mcp9808_temp_signed_from_reg(u16_t reg)
85122
{
123+
int rv = reg & MCP9808_TEMP_ABS_MASK;
124+
125+
if (reg & MCP9808_TEMP_SIGN_BIT) {
126+
/* Convert 12-bit 2s complement to signed negative
127+
* value.
128+
*/
129+
rv = -(1U + (rv ^ MCP9808_TEMP_ABS_MASK));
130+
}
131+
return rv;
86132
}
87-
#endif /* CONFIG_MCP9808_TRIGGER */
88133

89134
#endif /* ZEPHYR_DRIVERS_SENSOR_MCP9808_MCP9808_H_ */

0 commit comments

Comments
 (0)