1 /*
2 * Copyright (c) 2022 Thomas Stranger
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * Driver for DS18B20 and DS18S20 1-Wire temperature sensors
9 * Datasheets for the compatible sensors are available at:
10 * - https://www.analog.com/media/en/technical-documentation/data-sheets/ds18b20.pdf
11 * - https://www.analog.com/media/en/technical-documentation/data-sheets/ds18s20.pdf
12 *
13 * Parasite power configuration is not supported by the driver.
14 */
15
16 #include <stdbool.h>
17
18 #include <zephyr/device.h>
19 #include <zephyr/devicetree.h>
20 #include <zephyr/drivers/sensor.h>
21 #include <zephyr/drivers/sensor/w1_sensor.h>
22 #include "zephyr/drivers/w1.h"
23 #include <zephyr/kernel.h>
24 #include <zephyr/logging/log.h>
25 #include <zephyr/sys/__assert.h>
26 #include <zephyr/sys/util_macro.h>
27
28 LOG_MODULE_REGISTER(DS18B20, CONFIG_SENSOR_LOG_LEVEL);
29
30 #define DS18B20_CMD_CONVERT_T 0x44
31 #define DS18B20_CMD_WRITE_SCRATCHPAD 0x4E
32 #define DS18B20_CMD_READ_SCRATCHPAD 0xBE
33 #define DS18B20_CMD_COPY_SCRATCHPAD 0x48
34 #define DS18B20_CMD_RECALL_EEPROM 0xB8
35 #define DS18B20_CMD_READ_POWER_SUPPLY 0xB4
36
37 /* resolution is set using bit 5 and 6 of configuration register
38 * macro only valid for values 9 to 12
39 */
40 #define DS18B20_RESOLUTION_POS 5
41 #define DS18B20_RESOLUTION_MASK (BIT_MASK(2) << DS18B20_RESOLUTION_POS)
42 /* convert resolution in bits to scratchpad config format */
43 #define DS18B20_RESOLUTION(res) ((res - 9) << DS18B20_RESOLUTION_POS)
44 /* convert resolution in bits to array index (for resolution specific elements) */
45 #define DS18B20_RESOLUTION_INDEX(res) (res - 9)
46
47 #define DS18B20_FAMILYCODE 0x28
48 #define DS18S20_FAMILYCODE 0x10
49
50 enum chip_type {type_ds18b20, type_ds18s20};
51
52 struct ds18b20_scratchpad {
53 int16_t temp;
54 uint8_t alarm_temp_high;
55 uint8_t alarm_temp_low;
56 uint8_t config;
57 uint8_t res[3];
58 uint8_t crc;
59 } __packed;
60
61 struct ds18b20_config {
62 const struct device *bus;
63 uint8_t family;
64 uint8_t resolution;
65 enum chip_type chip;
66 };
67
68 struct ds18b20_data {
69 struct w1_slave_config config;
70 struct ds18b20_scratchpad scratchpad;
71 bool lazy_loaded;
72 };
73
74 static int ds18b20_configure(const struct device *dev);
75 static int ds18b20_read_scratchpad(const struct device *dev, struct ds18b20_scratchpad *scratchpad);
76
77 /* measure wait time for 9-bit, 10-bit, 11-bit, 12-bit resolution respectively */
78 static const uint16_t measure_wait_ds18b20_ms[4] = { 94, 188, 376, 750 };
79
80 /* ds18s20 always needs 750ms */
81 static const uint16_t measure_wait_ds18s20_ms = { 750 };
82
ds18b20_temperature_from_raw(const struct device * dev,uint8_t * temp_raw,struct sensor_value * val)83 static inline void ds18b20_temperature_from_raw(const struct device *dev,
84 uint8_t *temp_raw,
85 struct sensor_value *val)
86 {
87 const struct ds18b20_config *cfg = dev->config;
88 int16_t temp = sys_get_le16 (temp_raw);
89
90 if (cfg->chip == type_ds18s20) {
91 val->val1 = temp / 2;
92 val->val2 = (temp % 2) * 5000000;
93 } else {
94 val->val1 = temp / 16;
95 val->val2 = (temp % 16) * 1000000 / 16;
96 }
97 }
98
slave_responded(uint8_t * rx_buf,size_t len)99 static inline bool slave_responded(uint8_t *rx_buf, size_t len)
100 {
101 uint8_t cmp_byte = 0xff;
102
103 for (int i = 0; i < len; i++) {
104 cmp_byte &= rx_buf[i];
105 }
106
107 return (cmp_byte == 0xff) ? false : true;
108 }
109
110 /*
111 * Write scratch pad, read back, then copy to eeprom
112 */
ds18b20_write_scratchpad(const struct device * dev,struct ds18b20_scratchpad scratchpad)113 static int ds18b20_write_scratchpad(const struct device *dev,
114 struct ds18b20_scratchpad scratchpad)
115 {
116 struct ds18b20_data *data = dev->data;
117 const struct ds18b20_config *cfg = dev->config;
118 const struct device *bus = cfg->bus;
119 int ret;
120 uint8_t sp_data[4] = {
121 DS18B20_CMD_WRITE_SCRATCHPAD,
122 scratchpad.alarm_temp_high,
123 scratchpad.alarm_temp_low,
124 scratchpad.config
125 };
126
127 ret = w1_write_read(bus, &data->config, sp_data, sizeof(sp_data), NULL, 0);
128 if (ret != 0) {
129 return ret;
130 }
131
132 ret = ds18b20_read_scratchpad(dev, &scratchpad);
133 if (ret != 0) {
134 return ret;
135 }
136
137 if ((sp_data[3] & DS18B20_RESOLUTION_MASK) !=
138 (scratchpad.config & DS18B20_RESOLUTION_MASK)) {
139 return -EIO;
140 }
141
142 return 0;
143 }
144
ds18b20_read_scratchpad(const struct device * dev,struct ds18b20_scratchpad * scratchpad)145 static int ds18b20_read_scratchpad(const struct device *dev,
146 struct ds18b20_scratchpad *scratchpad)
147 {
148 struct ds18b20_data *data = dev->data;
149 const struct ds18b20_config *cfg = dev->config;
150 const struct device *bus = cfg->bus;
151 int ret;
152 uint8_t cmd = DS18B20_CMD_READ_SCRATCHPAD;
153 uint8_t crc;
154
155 memset(scratchpad, 0, sizeof(*scratchpad));
156 ret = w1_write_read(bus, &data->config, &cmd, 1,
157 (uint8_t *)scratchpad, sizeof(*scratchpad));
158 if (ret != 0) {
159 return ret;
160 }
161
162 if (!slave_responded((uint8_t *)scratchpad, sizeof(*scratchpad))) {
163 LOG_WRN("Slave not reachable");
164 return -ENODEV;
165 }
166
167 crc = w1_crc8((uint8_t *)scratchpad, sizeof(*scratchpad) - 1);
168 if (crc != scratchpad->crc) {
169 LOG_WRN("CRC does not match");
170 return -EIO;
171 }
172
173 return 0;
174 }
175
176 /* Starts sensor temperature conversion without waiting for completion. */
ds18b20_temperature_convert(const struct device * dev)177 static int ds18b20_temperature_convert(const struct device *dev)
178 {
179 int ret;
180 struct ds18b20_data *data = dev->data;
181 const struct ds18b20_config *cfg = dev->config;
182 const struct device *bus = cfg->bus;
183
184 (void)w1_lock_bus(bus);
185 ret = w1_reset_select(bus, &data->config);
186 if (ret != 0) {
187 goto out;
188 }
189 ret = w1_write_byte(bus, DS18B20_CMD_CONVERT_T);
190 out:
191 (void)w1_unlock_bus(bus);
192 return ret;
193 }
194
195 /*
196 * Write resolution into configuration struct,
197 * but don't write it to the sensor yet.
198 */
ds18b20_set_resolution(const struct device * dev,uint8_t resolution)199 static void ds18b20_set_resolution(const struct device *dev, uint8_t resolution)
200 {
201 struct ds18b20_data *data = dev->data;
202
203 data->scratchpad.config &= ~DS18B20_RESOLUTION_MASK;
204 data->scratchpad.config |= DS18B20_RESOLUTION(resolution);
205 }
206
measure_wait_ms(const struct device * dev)207 static uint16_t measure_wait_ms(const struct device *dev)
208 {
209 const struct ds18b20_config *cfg = dev->config;
210
211 if (cfg->chip == type_ds18s20) {
212 return measure_wait_ds18s20_ms;
213 }
214
215 return measure_wait_ds18b20_ms[DS18B20_RESOLUTION_INDEX(cfg->resolution)];
216 }
217
ds18b20_sample_fetch(const struct device * dev,enum sensor_channel chan)218 static int ds18b20_sample_fetch(const struct device *dev,
219 enum sensor_channel chan)
220 {
221 struct ds18b20_data *data = dev->data;
222 int status;
223
224 __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL ||
225 chan == SENSOR_CHAN_AMBIENT_TEMP);
226
227 if (!data->lazy_loaded) {
228 status = ds18b20_configure(dev);
229 if (status < 0) {
230 return status;
231 }
232 data->lazy_loaded = true;
233 }
234
235 status = ds18b20_temperature_convert(dev);
236 if (status < 0) {
237 LOG_DBG("W1 fetch error");
238 return status;
239 }
240 k_msleep(measure_wait_ms(dev));
241 return ds18b20_read_scratchpad(dev, &data->scratchpad);
242 }
243
ds18b20_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)244 static int ds18b20_channel_get(const struct device *dev,
245 enum sensor_channel chan,
246 struct sensor_value *val)
247 {
248 struct ds18b20_data *data = dev->data;
249
250 if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
251 return -ENOTSUP;
252 }
253
254 ds18b20_temperature_from_raw(dev, (uint8_t *)&data->scratchpad.temp, val);
255 return 0;
256 }
257
ds18b20_configure(const struct device * dev)258 static int ds18b20_configure(const struct device *dev)
259 {
260 const struct ds18b20_config *cfg = dev->config;
261 struct ds18b20_data *data = dev->data;
262 int ret;
263
264 if (w1_reset_bus(cfg->bus) <= 0) {
265 LOG_ERR("No 1-Wire slaves connected");
266 return -ENODEV;
267 }
268
269 /* In single drop configurations the rom can be read from device */
270 if (w1_get_slave_count(cfg->bus) == 1) {
271 if (w1_rom_to_uint64(&data->config.rom) == 0ULL) {
272 (void)w1_read_rom(cfg->bus, &data->config.rom);
273 }
274 } else if (w1_rom_to_uint64(&data->config.rom) == 0ULL) {
275 LOG_DBG("nr: %d", w1_get_slave_count(cfg->bus));
276 LOG_ERR("ROM required, because multiple slaves are on the bus");
277 return -EINVAL;
278 }
279
280 if ((cfg->family != 0) && (cfg->family != data->config.rom.family)) {
281 LOG_ERR("Found 1-Wire slave is not a %s", dev->name);
282 return -EINVAL;
283 }
284
285 /* write default configuration */
286 if (cfg->chip == type_ds18b20) {
287 ds18b20_set_resolution(dev, cfg->resolution);
288 ret = ds18b20_write_scratchpad(dev, data->scratchpad);
289 if (ret < 0) {
290 return ret;
291 }
292 }
293 LOG_DBG("Init %s: ROM=%016llx\n", dev->name,
294 w1_rom_to_uint64(&data->config.rom));
295
296 return 0;
297 }
298
ds18b20_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * thr)299 int ds18b20_attr_set(const struct device *dev, enum sensor_channel chan,
300 enum sensor_attribute attr, const struct sensor_value *thr)
301 {
302 struct ds18b20_data *data = dev->data;
303
304 if ((enum sensor_attribute_w1)attr != SENSOR_ATTR_W1_ROM) {
305 return -ENOTSUP;
306 }
307
308 data->lazy_loaded = false;
309 w1_sensor_value_to_rom(thr, &data->config.rom);
310 return 0;
311 }
312
313 static DEVICE_API(sensor, ds18b20_driver_api) = {
314 .attr_set = ds18b20_attr_set,
315 .sample_fetch = ds18b20_sample_fetch,
316 .channel_get = ds18b20_channel_get,
317 };
318
ds18b20_init(const struct device * dev)319 static int ds18b20_init(const struct device *dev)
320 {
321 const struct ds18b20_config *cfg = dev->config;
322 struct ds18b20_data *data = dev->data;
323
324 if (device_is_ready(cfg->bus) == 0) {
325 LOG_DBG("w1 bus is not ready");
326 return -ENODEV;
327 }
328
329 w1_uint64_to_rom(0ULL, &data->config.rom);
330 data->lazy_loaded = false;
331 /* in multidrop configurations the rom is need, but is not set during
332 * driver initialization, therefore do lazy initialization in all cases.
333 */
334
335 return 0;
336 }
337
338 #define DS18B20_CONFIG_INIT(inst, default_family_code, chip_type) \
339 { \
340 .bus = DEVICE_DT_GET(DT_INST_BUS(inst)), \
341 .family = (uint8_t)DT_INST_PROP_OR(inst, family_code, default_family_code), \
342 .resolution = DT_INST_PROP_OR(inst, resolution, 12), \
343 .chip = chip_type, \
344 }
345
346 #define DS18B20_DEFINE(inst, name, family_code, chip_type) \
347 static struct ds18b20_data data_##name##_##inst; \
348 static const struct ds18b20_config config_##name##_##inst = \
349 DS18B20_CONFIG_INIT(inst, family_code, chip_type); \
350 SENSOR_DEVICE_DT_INST_DEFINE(inst, \
351 ds18b20_init, \
352 NULL, \
353 &data_##name##_##inst, \
354 &config_##name##_##inst, \
355 POST_KERNEL, \
356 CONFIG_SENSOR_INIT_PRIORITY, \
357 &ds18b20_driver_api);
358
359 #define DT_DRV_COMPAT maxim_ds18b20
360 DT_INST_FOREACH_STATUS_OKAY_VARGS(DS18B20_DEFINE, DT_DRV_COMPAT,
361 DS18B20_FAMILYCODE,
362 type_ds18b20)
363 #undef DT_DRV_COMPAT
364
365 #define DT_DRV_COMPAT maxim_ds18s20
366 DT_INST_FOREACH_STATUS_OKAY_VARGS(DS18B20_DEFINE, DT_DRV_COMPAT,
367 DS18S20_FAMILYCODE,
368 type_ds18s20)
369 #undef DT_DRV_COMPAT
370