1 /*
2 * Copyright (c) 2021 Aurelien Jarno
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/drivers/i2c.h>
10 #include <zephyr/drivers/sensor.h>
11 #include <zephyr/sys/byteorder.h>
12 #include <zephyr/sys/__assert.h>
13 #include <zephyr/logging/log.h>
14
15 LOG_MODULE_REGISTER(TI_HDC20XX, CONFIG_SENSOR_LOG_LEVEL);
16
17 /* Register addresses */
18 #define TI_HDC20XX_REG_TEMP 0x00
19 #define TI_HDC20XX_REG_HUMIDITY 0x02
20 #define TI_HDC20XX_REG_INT_EN 0x07
21 #define TI_HDC20XX_REG_CONFIG 0x0E
22 #define TI_HDC20XX_REG_MEAS_CFG 0x0F
23 #define TI_HDC20XX_REG_MANUFACTURER_ID 0xFC
24 #define TI_HDC20XX_REG_DEVICE_ID 0xFE
25
26 /* Register values */
27 #define TI_HDC20XX_MANUFACTURER_ID 0x5449
28 #define TI_HDC20XX_DEVICE_ID 0x07D0
29
30 /* Register bits */
31 #define TI_HDC20XX_BIT_INT_EN_DRDY_EN 0x80
32 #define TI_HDC20XX_BIT_CONFIG_SOFT_RES 0x80
33 #define TI_HDC20XX_BIT_CONFIG_DRDY_INT_EN 0x04
34
35 /* Reset time: not in the datasheet, but found by trial and error */
36 #define TI_HDC20XX_RESET_TIME K_MSEC(1)
37
38 /* Conversion time for 14-bit resolution. Temperature needs 660us and humidity 610us */
39 #define TI_HDC20XX_CONVERSION_TIME K_MSEC(2)
40
41 /* Temperature and humidity scale and factors from the datasheet ("Register Maps" section) */
42 #define TI_HDC20XX_RH_SCALE 100U
43 #define TI_HDC20XX_TEMP_OFFSET -2654208 /* = -40.5 * 2^16 */
44 #define TI_HDC20XX_TEMP_SCALE 165U
45
46 struct ti_hdc20xx_config {
47 struct i2c_dt_spec bus;
48 struct gpio_dt_spec gpio_int;
49 };
50
51 struct ti_hdc20xx_data {
52 struct gpio_callback cb_int;
53 struct k_sem sem_int;
54
55 uint16_t t_sample;
56 uint16_t rh_sample;
57 };
58
ti_hdc20xx_int_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)59 static void ti_hdc20xx_int_callback(const struct device *dev,
60 struct gpio_callback *cb, uint32_t pins)
61 {
62 struct ti_hdc20xx_data *data = CONTAINER_OF(cb, struct ti_hdc20xx_data, cb_int);
63
64 ARG_UNUSED(pins);
65
66 k_sem_give(&data->sem_int);
67 }
68
ti_hdc20xx_sample_fetch(const struct device * dev,enum sensor_channel chan)69 static int ti_hdc20xx_sample_fetch(const struct device *dev,
70 enum sensor_channel chan)
71 {
72 const struct ti_hdc20xx_config *config = dev->config;
73 struct ti_hdc20xx_data *data = dev->data;
74 uint16_t buf[2];
75 int rc;
76
77 __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
78
79 /* start conversion of both temperature and humidity with the default accuracy (14 bits) */
80 rc = i2c_reg_write_byte_dt(&config->bus, TI_HDC20XX_REG_MEAS_CFG, 0x01);
81 if (rc < 0) {
82 LOG_ERR("Failed to write measurement configuration register");
83 return rc;
84 }
85
86 /* wait for the conversion to finish */
87 if (config->gpio_int.port) {
88 k_sem_take(&data->sem_int, K_FOREVER);
89 } else {
90 k_sleep(TI_HDC20XX_CONVERSION_TIME);
91 }
92
93 /* temperature and humidity registers are consecutive, read them in the same burst */
94 rc = i2c_burst_read_dt(&config->bus, TI_HDC20XX_REG_TEMP, (uint8_t *)buf, sizeof(buf));
95 if (rc < 0) {
96 LOG_ERR("Failed to read sample data");
97 return rc;
98 }
99
100 data->t_sample = sys_le16_to_cpu(buf[0]);
101 data->rh_sample = sys_le16_to_cpu(buf[1]);
102
103 return 0;
104 }
105
106
ti_hdc20xx_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)107 static int ti_hdc20xx_channel_get(const struct device *dev,
108 enum sensor_channel chan,
109 struct sensor_value *val)
110 {
111 struct ti_hdc20xx_data *data = dev->data;
112 int32_t tmp;
113
114 /* See datasheet "Register Maps" section for more details on processing sample data. */
115 switch (chan) {
116 case SENSOR_CHAN_AMBIENT_TEMP:
117 /* val = -40.5 + 165 * sample / 2^16 */
118 tmp = data->t_sample * TI_HDC20XX_TEMP_SCALE + TI_HDC20XX_TEMP_OFFSET;
119 val->val1 = tmp >> 16;
120 /* x * 1000000 / 2^16 = x * 15625 / 2^10 */
121 val->val2 = ((tmp & 0xFFFF) * 15625U) >> 10;
122 break;
123 case SENSOR_CHAN_HUMIDITY:
124 /* val = 100 * sample / 2^16 */
125 tmp = data->rh_sample * TI_HDC20XX_RH_SCALE;
126 val->val1 = tmp >> 16;
127 /* x * 1000000 / 2^16 = x * 15625 / 2^10 */
128 val->val2 = ((tmp & 0xFFFF) * 15625U) >> 10;
129 break;
130 default:
131 return -ENOTSUP;
132 }
133
134 return 0;
135 }
136
137 static const struct sensor_driver_api ti_hdc20xx_api_funcs = {
138 .sample_fetch = ti_hdc20xx_sample_fetch,
139 .channel_get = ti_hdc20xx_channel_get,
140 };
141
ti_hdc20xx_reset(const struct device * dev)142 static int ti_hdc20xx_reset(const struct device *dev)
143 {
144 const struct ti_hdc20xx_config *config = dev->config;
145 int rc;
146
147 rc = i2c_reg_write_byte_dt(&config->bus, TI_HDC20XX_REG_CONFIG,
148 TI_HDC20XX_BIT_CONFIG_SOFT_RES);
149 if (rc < 0) {
150 LOG_ERR("Failed to soft-reset device");
151 return rc;
152 }
153 k_sleep(TI_HDC20XX_RESET_TIME);
154
155 return 0;
156 }
157
ti_hdc20xx_init(const struct device * dev)158 static int ti_hdc20xx_init(const struct device *dev)
159 {
160 const struct ti_hdc20xx_config *config = dev->config;
161 struct ti_hdc20xx_data *data = dev->data;
162 uint16_t buf[2];
163 int rc;
164
165 if (!device_is_ready(config->bus.bus)) {
166 LOG_ERR("I2C bus %s not ready", config->bus.bus->name);
167 return -ENODEV;
168 }
169
170 /* manufacturer and device ID registers are consecutive, read them in the same burst */
171 rc = i2c_burst_read_dt(&config->bus, TI_HDC20XX_REG_MANUFACTURER_ID,
172 (uint8_t *)buf, sizeof(buf));
173 if (rc < 0) {
174 LOG_ERR("Failed to read manufacturer and device IDs");
175 return rc;
176 }
177
178 if (sys_le16_to_cpu(buf[0]) != TI_HDC20XX_MANUFACTURER_ID) {
179 LOG_ERR("Failed to get correct manufacturer ID");
180 return -EINVAL;
181 }
182 if (sys_le16_to_cpu(buf[1]) != TI_HDC20XX_DEVICE_ID) {
183 LOG_ERR("Unsupported device ID");
184 return -EINVAL;
185 }
186
187 /* Soft-reset the device to bring all registers in a known and consistent state */
188 rc = ti_hdc20xx_reset(dev);
189 if (rc < 0) {
190 return rc;
191 }
192
193 /* Configure the interrupt GPIO if available */
194 if (config->gpio_int.port) {
195 if (!gpio_is_ready_dt(&config->gpio_int)) {
196 LOG_ERR("Cannot get pointer to gpio interrupt device");
197 return -ENODEV;
198 }
199
200 rc = gpio_pin_configure_dt(&config->gpio_int, GPIO_INPUT);
201 if (rc) {
202 LOG_ERR("Failed to configure interrupt pin");
203 return rc;
204 }
205
206 gpio_init_callback(&data->cb_int, ti_hdc20xx_int_callback,
207 BIT(config->gpio_int.pin));
208
209 rc = gpio_add_callback(config->gpio_int.port, &data->cb_int);
210 if (rc) {
211 LOG_ERR("Failed to set interrupt callback");
212 return rc;
213 }
214
215 rc = gpio_pin_interrupt_configure_dt(&config->gpio_int, GPIO_INT_EDGE_TO_ACTIVE);
216 if (rc) {
217 LOG_ERR("Failed to configure interrupt");
218 return rc;
219 }
220
221 /* Initialize the semaphore */
222 k_sem_init(&data->sem_int, 0, K_SEM_MAX_LIMIT);
223
224 /* Enable the data ready interrupt */
225 rc = i2c_reg_write_byte_dt(&config->bus, TI_HDC20XX_REG_INT_EN,
226 TI_HDC20XX_BIT_INT_EN_DRDY_EN);
227 if (rc) {
228 LOG_ERR("Failed to enable the data ready interrupt");
229 return rc;
230 }
231
232 /* Enable the interrupt pin with level sensitive active low polarity */
233 rc = i2c_reg_write_byte_dt(&config->bus, TI_HDC20XX_REG_CONFIG,
234 TI_HDC20XX_BIT_CONFIG_DRDY_INT_EN);
235 if (rc) {
236 LOG_ERR("Failed to enable the interrupt pin");
237 return rc;
238 }
239 }
240
241 return 0;
242 }
243
244 /* Main instantiation macro */
245 #define TI_HDC20XX_DEFINE(inst, compat) \
246 static struct ti_hdc20xx_data ti_hdc20xx_data_##compat##inst; \
247 static const struct ti_hdc20xx_config ti_hdc20xx_config_##compat##inst = { \
248 .bus = I2C_DT_SPEC_GET(DT_INST(inst, compat)), \
249 .gpio_int = GPIO_DT_SPEC_GET_OR(DT_INST(inst, compat), int_gpios, {0}), \
250 }; \
251 DEVICE_DT_DEFINE(DT_INST(inst, compat), \
252 ti_hdc20xx_init, \
253 NULL, \
254 &ti_hdc20xx_data_##compat##inst, \
255 &ti_hdc20xx_config_##compat##inst, \
256 POST_KERNEL, \
257 CONFIG_SENSOR_INIT_PRIORITY, \
258 &ti_hdc20xx_api_funcs);
259
260 /* Create the struct device for every status "okay" node in the devicetree. */
261 #define TI_HDC20XX_FOREACH_STATUS_OKAY(compat, fn) \
262 COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(compat), \
263 (UTIL_CAT(DT_FOREACH_OKAY_INST_, \
264 compat)(fn)), \
265 ())
266
267 /*
268 * HDC2010 Low-Power Humidity and Temperature Digital Sensors
269 */
270 #define TI_HDC2010_DEFINE(inst) TI_HDC20XX_DEFINE(inst, ti_hdc2010)
271 TI_HDC20XX_FOREACH_STATUS_OKAY(ti_hdc2010, TI_HDC2010_DEFINE)
272
273 /*
274 * HDC2021 High-Accuracy, Low-Power Humidity and Temperature Sensor
275 * With Assembly Protection Cover
276 */
277 #define TI_HDC2021_DEFINE(inst) TI_HDC20XX_DEFINE(inst, ti_hdc2021)
278 TI_HDC20XX_FOREACH_STATUS_OKAY(ti_hdc2021, TI_HDC2021_DEFINE)
279
280 /*
281 * HDC2022 High-Accuracy, Low-Power Humidity and Temperature Sensor
282 * With IP67 Rated Water and Dust Protection Cover
283 */
284 #define TI_HDC2022_DEFINE(inst) TI_HDC20XX_DEFINE(inst, ti_hdc2022)
285 TI_HDC20XX_FOREACH_STATUS_OKAY(ti_hdc2022, TI_HDC2022_DEFINE)
286
287 /*
288 * HDC2080 Low-Power Humidity and Temperature Digital Sensor
289 */
290 #define TI_HDC2080_DEFINE(inst) TI_HDC20XX_DEFINE(inst, ti_hdc2080)
291 TI_HDC20XX_FOREACH_STATUS_OKAY(ti_hdc2080, TI_HDC2080_DEFINE)
292