1 /*
2 * Copyright (c) 2024 Renesas Electronics Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT renesas_hs400x
8
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/sys/__assert.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/sys/byteorder.h>
16 #include <zephyr/sys/crc.h>
17
18 #define CRC_POLYNOMIAL 0x1D
19 #define CRC_INITIAL 0xFF
20
21 LOG_MODULE_REGISTER(HS400X, CONFIG_SENSOR_LOG_LEVEL);
22
23 struct hs400x_config {
24 struct i2c_dt_spec bus;
25 };
26
27 struct hs400x_data {
28 int16_t t_sample;
29 uint16_t rh_sample;
30 };
31
hs400x_read_sample(const struct device * dev,uint16_t * t_sample,uint16_t * rh_sample)32 static int hs400x_read_sample(const struct device *dev, uint16_t *t_sample, uint16_t *rh_sample)
33 {
34 const struct hs400x_config *cfg = dev->config;
35 uint8_t rx_buf[5];
36 int rc;
37
38 rc = i2c_read_dt(&cfg->bus, rx_buf, sizeof(rx_buf));
39 if (rc < 0) {
40 LOG_ERR("Failed to read data from device.");
41 return rc;
42 }
43
44 *rh_sample = sys_get_be16(rx_buf);
45 *t_sample = sys_get_be16(&rx_buf[2]);
46
47 /*
48 * The sensor sends a checkum after each measurement. See datasheet "CRC Checksum
49 * Calculation" section for more details on checking the checksum.
50 */
51 #if CONFIG_HS400X_CRC
52 uint8_t crc = crc8(rx_buf, 4, CRC_POLYNOMIAL, CRC_INITIAL, 0);
53
54 if (crc != rx_buf[4]) {
55 LOG_ERR("CRC check failed: computed=%u,expected=%u", crc, rx_buf[4]);
56 return -EIO;
57 }
58 #endif
59
60 return 0;
61 }
62
hs400x_sample_fetch(const struct device * dev,enum sensor_channel chan)63 static int hs400x_sample_fetch(const struct device *dev, enum sensor_channel chan)
64 {
65 struct hs400x_data *data = dev->data;
66 const struct hs400x_config *cfg = dev->config;
67 int rc;
68 uint8_t no_hold_measurement = 0xF5;
69
70 if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP &&
71 chan != SENSOR_CHAN_HUMIDITY) {
72 return -ENOTSUP;
73 }
74
75 rc = i2c_write_dt(&cfg->bus, (const uint8_t *)&no_hold_measurement, 1);
76 if (rc < 0) {
77 LOG_ERR("Failed to send measurement.");
78 return rc;
79 }
80
81 /*
82 * According to datasheet maximum time to make temperature and humidity
83 * measurements is 33ms, add a little safety margin...
84 */
85 k_msleep(50);
86
87 rc = hs400x_read_sample(dev, &data->t_sample, &data->rh_sample);
88 if (rc < 0) {
89 LOG_ERR("Failed to fetch data.");
90 return rc;
91 }
92
93 return 0;
94 }
95
hs400x_temp_convert(struct sensor_value * val,int16_t raw)96 static void hs400x_temp_convert(struct sensor_value *val, int16_t raw)
97 {
98 int32_t micro_c;
99
100 /*
101 * Convert to micro Celsius. See datasheet "Calculating Humidity and
102 * Temperature Output" section for more details on processing sample data.
103 */
104 micro_c = (((int64_t)raw * 165000000) / 16383) - 40000000;
105
106 val->val1 = micro_c / 1000000;
107 val->val2 = micro_c % 1000000;
108 }
109
hs400x_rh_convert(struct sensor_value * val,uint16_t raw)110 static void hs400x_rh_convert(struct sensor_value *val, uint16_t raw)
111 {
112 int32_t micro_rh;
113
114 /*
115 * Convert to micro %RH. See datasheet "Calculating Humidity and
116 * Temperature Output" section for more details on processing sample data.
117 */
118 micro_rh = ((uint64_t)raw * 100000000) / 16383;
119
120 val->val1 = micro_rh / 1000000;
121 val->val2 = micro_rh % 1000000;
122 }
123
hs400x_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)124 static int hs400x_channel_get(const struct device *dev, enum sensor_channel chan,
125 struct sensor_value *val)
126 {
127 const struct hs400x_data *data = dev->data;
128
129 if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
130 hs400x_temp_convert(val, data->t_sample);
131 } else if (chan == SENSOR_CHAN_HUMIDITY) {
132 hs400x_rh_convert(val, data->rh_sample);
133 } else {
134 return -ENOTSUP;
135 }
136
137 return 0;
138 }
139
hs400x_init(const struct device * dev)140 static int hs400x_init(const struct device *dev)
141 {
142 const struct hs400x_config *cfg = dev->config;
143
144 if (!i2c_is_ready_dt(&cfg->bus)) {
145 LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name);
146 return -ENODEV;
147 }
148
149 return 0;
150 }
151
152 static DEVICE_API(sensor, hs400x_driver_api) = {
153 .sample_fetch = hs400x_sample_fetch,
154 .channel_get = hs400x_channel_get,
155 };
156
157 #define DEFINE_HS400X(n) \
158 static struct hs400x_data hs400x_data_##n; \
159 \
160 static const struct hs400x_config hs400x_config_##n = {.bus = I2C_DT_SPEC_INST_GET(n)}; \
161 \
162 SENSOR_DEVICE_DT_INST_DEFINE(n, hs400x_init, NULL, &hs400x_data_##n, &hs400x_config_##n, \
163 POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
164 &hs400x_driver_api);
165
166 DT_INST_FOREACH_STATUS_OKAY(DEFINE_HS400X)
167