1 /*
2 * Copyright (c) 2023 Ian Morris
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT renesas_hs300x
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
17 #define HS300X_STATUS_MASK (BIT(0) | BIT(1))
18
19 LOG_MODULE_REGISTER(HS300X, CONFIG_SENSOR_LOG_LEVEL);
20
21 struct hs300x_config {
22 struct i2c_dt_spec bus;
23 };
24
25 struct hs300x_data {
26 int16_t t_sample;
27 uint16_t rh_sample;
28 };
29
hs300x_read_sample(const struct device * dev,uint16_t * t_sample,uint16_t * rh_sample)30 static int hs300x_read_sample(const struct device *dev, uint16_t *t_sample, uint16_t *rh_sample)
31 {
32 const struct hs300x_config *cfg = dev->config;
33 uint8_t rx_buf[4];
34 int rc;
35
36 rc = i2c_read_dt(&cfg->bus, rx_buf, sizeof(rx_buf));
37 if (rc < 0) {
38 LOG_ERR("Failed to read data from device.");
39 return rc;
40 }
41
42 if ((rx_buf[3] & HS300X_STATUS_MASK) != 0) {
43 LOG_ERR("Stale data");
44 return -EIO;
45 }
46
47 *rh_sample = sys_get_be16(rx_buf);
48 *t_sample = sys_get_be16(&rx_buf[2]);
49
50 /* Remove status bits (only present in temperature value)*/
51 *t_sample >>= 2;
52
53 return 0;
54 }
55
hs300x_sample_fetch(const struct device * dev,enum sensor_channel chan)56 static int hs300x_sample_fetch(const struct device *dev, enum sensor_channel chan)
57 {
58 struct hs300x_data *data = dev->data;
59 const struct hs300x_config *cfg = dev->config;
60 int rc;
61 uint8_t df_dummy = 0x0;
62
63 if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP &&
64 chan != SENSOR_CHAN_HUMIDITY) {
65 return -ENOTSUP;
66 }
67
68 /*
69 * By default, the sensor should be factory-programmed to operate in Sleep Mode.
70 * A Measurement Request (MR) command is required to exit the sensor
71 * from its sleep state. An MR command should consist of the 7-bit address followed
72 * by an eighth bit set to 0 (write). However, many I2C controllers cannot generate
73 * merely the address byte with no data. To overcome this limitation the MR command
74 * should be followed by a dummy byte (zero value).
75 */
76 rc = i2c_write_dt(&cfg->bus, (const uint8_t *)&df_dummy, 1);
77 if (rc < 0) {
78 LOG_ERR("Failed to start measurement.");
79 return rc;
80 }
81
82 /*
83 * According to datasheet maximum time to make temperature and humidity
84 * measurements is 33ms, add a little safety margin...
85 */
86 k_msleep(50);
87
88 rc = hs300x_read_sample(dev, &data->t_sample, &data->rh_sample);
89 if (rc < 0) {
90 LOG_ERR("Failed to fetch data.");
91 return rc;
92 }
93
94 return 0;
95 }
96
hs300x_temp_convert(struct sensor_value * val,int16_t raw)97 static void hs300x_temp_convert(struct sensor_value *val, int16_t raw)
98 {
99 int32_t micro_c;
100
101 /*
102 * Convert to micro Celsius. See datasheet "Calculating Humidity and
103 * Temperature Output" section for more details on processing sample data.
104 */
105 micro_c = (((int64_t)raw * 165000000) / 16383) - 40000000;
106
107 val->val1 = micro_c / 1000000;
108 val->val2 = micro_c % 1000000;
109 }
110
hs300x_rh_convert(struct sensor_value * val,uint16_t raw)111 static void hs300x_rh_convert(struct sensor_value *val, uint16_t raw)
112 {
113 int32_t micro_rh;
114
115 /*
116 * Convert to micro %RH. See datasheet "Calculating Humidity and
117 * Temperature Output" section for more details on processing sample data.
118 */
119 micro_rh = ((uint64_t)raw * 100000000) / 16383;
120
121 val->val1 = micro_rh / 1000000;
122 val->val2 = micro_rh % 1000000;
123 }
124
hs300x_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)125 static int hs300x_channel_get(const struct device *dev, enum sensor_channel chan,
126 struct sensor_value *val)
127 {
128 const struct hs300x_data *data = dev->data;
129
130 if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
131 hs300x_temp_convert(val, data->t_sample);
132 } else if (chan == SENSOR_CHAN_HUMIDITY) {
133 hs300x_rh_convert(val, data->rh_sample);
134 } else {
135 return -ENOTSUP;
136 }
137
138 return 0;
139 }
140
hs300x_init(const struct device * dev)141 static int hs300x_init(const struct device *dev)
142 {
143 const struct hs300x_config *cfg = dev->config;
144
145 if (!i2c_is_ready_dt(&cfg->bus)) {
146 LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name);
147 return -ENODEV;
148 }
149
150 return 0;
151 }
152
153 static DEVICE_API(sensor, hs300x_driver_api) = {
154 .sample_fetch = hs300x_sample_fetch,
155 .channel_get = hs300x_channel_get,
156 };
157
158 #define DEFINE_HS300X(n) \
159 static struct hs300x_data hs300x_data_##n; \
160 \
161 static const struct hs300x_config hs300x_config_##n = {.bus = I2C_DT_SPEC_INST_GET(n)}; \
162 \
163 SENSOR_DEVICE_DT_INST_DEFINE(n, hs300x_init, NULL, &hs300x_data_##n, &hs300x_config_##n, \
164 POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
165 &hs300x_driver_api);
166
167 DT_INST_FOREACH_STATUS_OKAY(DEFINE_HS300X)
168