xref: /minix/minix/drivers/sensors/sht21/sht21.c (revision 433d6423)
1 /* Driver for the SHT21 Relative Humidity and Temperature Sensor */
2 
3 #include <minix/ds.h>
4 #include <minix/drivers.h>
5 #include <minix/i2c.h>
6 #include <minix/i2cdriver.h>
7 #include <minix/chardriver.h>
8 #include <minix/log.h>
9 
10 #include <time.h>
11 
12 /*
13  * Device Commands
14  */
15 
16 /*
17  * The trigger commands start a measurement. 'Hold' ties up the bus while the
18  * measurement is being performed while 'no hold' requires the driver to poll
19  * the chip until the data is ready. Hold is faster and requires less message
20  * passing while no hold frees up the bus while the measurement is in progress.
21  * The worst case conversion times are 85 ms for temperature and 29 ms for
22  * humidity. Typical conversion times are about 75% of the worst case times.
23  *
24  * The driver uses the 'hold' versions of the trigger commands.
25  */
26 #define CMD_TRIG_T_HOLD 0xe3
27 #define CMD_TRIG_RH_HOLD 0xe5
28 #define CMD_TRIG_T_NOHOLD 0xf3
29 #define CMD_TRIG_RH_NOHOLD 0xf5
30 
31 /* Read and write the user register contents */
32 #define CMD_WR_USR_REG 0xe6
33 #define CMD_RD_USR_REG 0xe7
34 
35 /* Resets the chip */
36 #define CMD_SOFT_RESET 0xfe
37 
38 /* Status bits included in the measurement need to be masked in calculation */
39 #define STATUS_BITS_MASK 0x0003
40 
41 /*
42  * The user register has some reserved bits that the device changes over
43  * time. The driver must preserve the value of those bits when writing to
44  * the user register.
45  */
46 #define USR_REG_RESERVED_MASK ((1<<3)|(1<<4)|(1<<5))
47 
48 /* End of Battery flag is set when the voltage drops below 2.25V. */
49 #define USR_REG_EOB_MASK (1<<6)
50 
51 /* When powered up and communicating, the register should have only the
52  * 'Disable OTP Reload' bit set
53  */
54 #define EXPECTED_PWR_UP_TEST_VAL (1<<1)
55 
56 /* Define some constants for the different sensor types on the chip. */
57 enum sht21_sensors
58 { SHT21_T, SHT21_RH };
59 
60 /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
61 static struct log log = {
62 	.name = "sht21",
63 	.log_level = LEVEL_INFO,
64 	.log_func = default_log
65 };
66 
67 /* device slave address is fixed at 0x40 */
68 static i2c_addr_t valid_addrs[2] = {
69 	0x40, 0x00
70 };
71 
72 /* Buffer to store output string returned when reading from device file. */
73 #define BUFFER_LEN 64
74 char buffer[BUFFER_LEN + 1];
75 
76 /* the bus that this device is on (counting starting at 1) */
77 static uint32_t bus;
78 
79 /* slave address of the device */
80 static i2c_addr_t address;
81 
82 /* endpoint for the driver for the bus itself. */
83 static endpoint_t bus_endpoint;
84 
85 /* Sampling causes self-heating. To limit the self-heating to < 0.1C, the
86  * data sheet suggests limiting sampling to 2 samples per second. Since
87  * the driver samples temperature and relative humidity at the same time,
88  * it's measure function does at most 1 pair of samples per second. It uses
89  * this timestamp to see if a measurement was taken less than 1 second ago.
90  */
91 static time_t last_sample_time = 0;
92 
93 /*
94  * Cache temperature and relative humidity readings. These values are returned
95  * when the last_sample_time == current_time to keep the chip activity below
96  * 10% to help prevent self-heating.
97  */
98 static int32_t cached_t = 0.0;
99 static int32_t cached_rh = 0.0;
100 
101 /*
102  * An 8-bit CRC is used to validate the readings.
103  */
104 #define CRC8_POLYNOMIAL 0x131
105 #define CRC8_INITIAL_CRC 0x00
106 
107 /* main driver functions */
108 static int sht21_init(void);
109 static int sensor_read(enum sht21_sensors sensor, int32_t * measurement);
110 static int measure(void);
111 
112 /* CRC functions */
113 static uint8_t crc8(uint8_t crc, uint8_t byte);
114 static int checksum(uint8_t * bytes, int nbytes, uint8_t expected_crc);
115 
116 /* libchardriver callbacks */
117 static ssize_t sht21_read(devminor_t minor, u64_t position, endpoint_t endpt,
118     cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
119 static void sht21_other(message * m, int ipc_status);
120 
121 /* SEF functions */
122 static int sef_cb_lu_state_save(int);
123 static int lu_state_restore(void);
124 static int sef_cb_init(int type, sef_init_info_t * info);
125 static void sef_local_startup(void);
126 
127 /* Entry points to this driver from libchardriver. */
128 static struct chardriver sht21_tab = {
129 	.cdr_read	= sht21_read,
130 	.cdr_other	= sht21_other
131 };
132 
133 /*
134  * Performs a soft reset and reads the contents of the user register to ensure
135  * that the chip is in a good state and working properly.
136  */
137 static int
138 sht21_init(void)
139 {
140 	int r;
141 	uint8_t usr_reg_val;
142 
143 	/* Perform a soft-reset */
144 	r = i2creg_raw_write8(bus_endpoint, address, CMD_SOFT_RESET);
145 	if (r != OK) {
146 		return -1;
147 	}
148 
149 	/* soft reset takes up to 15 ms to complete. */
150 	micro_delay(15000);
151 
152 	log_debug(&log, "Soft Reset Complete\n");
153 
154 	r = i2creg_read8(bus_endpoint, address, CMD_RD_USR_REG, &usr_reg_val);
155 	if (r != OK) {
156 		return -1;
157 	}
158 
159 	/* Check for End of Battery flag. */
160 	if ((usr_reg_val & USR_REG_EOB_MASK) == USR_REG_EOB_MASK) {
161 		log_warn(&log, "End of Battery Alarm\n");
162 		return -1;
163 	}
164 
165 	/* Check that the non-reserved bits are in the default state. */
166 	if ((usr_reg_val & ~USR_REG_RESERVED_MASK) != EXPECTED_PWR_UP_TEST_VAL) {
167 		log_warn(&log, "USR_REG has non-default values after reset\n");
168 		log_warn(&log, "Expected 0x%x | Actual 0x%x",
169 		    EXPECTED_PWR_UP_TEST_VAL,
170 		    (usr_reg_val & ~USR_REG_RESERVED_MASK));
171 		return -1;
172 	}
173 
174 	return OK;
175 }
176 
177 /*
178  * Read from the sensor, check the CRC, convert the ADC value into the final
179  * representation, and store the result in measurement.
180  */
181 static int
182 sensor_read(enum sht21_sensors sensor, int32_t * measurement)
183 {
184 	int r;
185 	uint8_t cmd;
186 	uint16_t val;
187 	uint8_t bytes[2];
188 	uint32_t val32;
189 	uint8_t expected_crc;
190 
191 	switch (sensor) {
192 	case SHT21_T:
193 		cmd = CMD_TRIG_T_HOLD;
194 		break;
195 	case SHT21_RH:
196 		cmd = CMD_TRIG_RH_HOLD;
197 		break;
198 	default:
199 		log_warn(&log, "sensor_read() called with bad sensor type.\n");
200 		return -1;
201 	}
202 
203 	if (measurement == NULL) {
204 		log_warn(&log, "sensor_read() called with NULL pointer\n");
205 		return -1;
206 	}
207 
208 	r = i2creg_read24(bus_endpoint, address, cmd, &val32);
209 	if (r != OK) {
210 		log_warn(&log, "sensor_read() failed (r=%d)\n", r);
211 		return -1;
212 	}
213 
214 	expected_crc = val32 & 0xff;
215 	val = (val32 >> 8) & 0xffff;
216 
217 	bytes[0] = (val >> 8) & 0xff;
218 	bytes[1] = val & 0xff;
219 
220 	r = checksum(bytes, 2, expected_crc);
221 	if (r != OK) {
222 		return -1;
223 	}
224 
225 	val &= ~STATUS_BITS_MASK;	/* clear status bits */
226 
227 	log_debug(&log, "Read VAL:0x%x CRC:0x%x\n", val, expected_crc);
228 
229 	/* Convert the ADC value to the actual value. */
230 	if (cmd == CMD_TRIG_T_HOLD) {
231 		*measurement = (int32_t)
232 		    ((-46.85 + ((175.72 / 65536) * ((float) val))) * 1000.0);
233 		log_debug(&log, "Measured Temperature %d mC\n", *measurement);
234 	} else if (cmd == CMD_TRIG_RH_HOLD) {
235 		*measurement =
236 		    (int32_t) ((-6.0 +
237 			((125.0 / 65536) * ((float) val))) * 1000.0);
238 		log_debug(&log, "Measured Humidity %d m%%\n", *measurement);
239 	}
240 
241 	return OK;
242 }
243 
244 static int
245 measure(void)
246 {
247 	int r;
248 	time_t sample_time;
249 	int32_t t, rh;
250 
251 	log_debug(&log, "Taking a measurement...");
252 
253 	sample_time = time(NULL);
254 	if (sample_time == last_sample_time) {
255 		log_debug(&log, "measure() called too soon, using cache.\n");
256 		return OK;
257 	}
258 
259 	r = sensor_read(SHT21_T, &t);
260 	if (r != OK) {
261 		return -1;
262 	}
263 
264 	r = sensor_read(SHT21_RH, &rh);
265 	if (r != OK) {
266 		return -1;
267 	}
268 
269 	/* save measured values */
270 	cached_t = t;
271 	cached_rh = rh;
272 	last_sample_time = time(NULL);
273 
274 	log_debug(&log, "Measurement completed\n");
275 
276 	return OK;
277 }
278 
279 /*
280  * Return an updated checksum for the given crc and byte.
281  */
282 static uint8_t
283 crc8(uint8_t crc, uint8_t byte)
284 {
285 	int i;
286 
287 	crc ^= byte;
288 
289 	for (i = 0; i < 8; i++) {
290 
291 		if ((crc & 0x80) == 0x80) {
292 			crc = (crc << 1) ^ CRC8_POLYNOMIAL;
293 		} else {
294 			crc <<= 1;
295 		}
296 	}
297 
298 	return crc;
299 }
300 
301 /*
302  * Compute the CRC of an array of bytes and compare it to expected_crc.
303  * If the computed CRC matches expected_crc, then return OK, otherwise EINVAL.
304  */
305 static int
306 checksum(uint8_t * bytes, int nbytes, uint8_t expected_crc)
307 {
308 	int i;
309 	uint8_t crc;
310 
311 	crc = CRC8_INITIAL_CRC;
312 
313 	log_debug(&log, "Checking CRC\n");
314 
315 	for (i = 0; i < nbytes; i++) {
316 		crc = crc8(crc, bytes[i]);
317 	}
318 
319 	if (crc == expected_crc) {
320 		log_debug(&log, "CRC OK\n");
321 		return OK;
322 	} else {
323 		log_warn(&log,
324 		    "Bad CRC -- Computed CRC: 0x%x | Expected CRC: 0x%x\n",
325 		    crc, expected_crc);
326 		return EINVAL;
327 	}
328 }
329 
330 static ssize_t
331 sht21_read(devminor_t UNUSED(minor), u64_t position, endpoint_t endpt,
332     cp_grant_id_t grant, size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
333 {
334 	u64_t dev_size;
335 	int bytes, r;
336 
337 	r = measure();
338 	if (r != OK) {
339 		return EIO;
340 	}
341 
342 	memset(buffer, '\0', BUFFER_LEN + 1);
343 	snprintf(buffer, BUFFER_LEN, "%-16s: %d.%03d\n%-16s: %d.%03d\n",
344 	    "TEMPERATURE", cached_t / 1000, cached_t % 1000, "HUMIDITY",
345 	    cached_rh / 1000, cached_rh % 1000);
346 
347 	log_trace(&log, "%s", buffer);
348 
349 	dev_size = (u64_t)strlen(buffer);
350 	if (position >= dev_size) return 0;
351 	if (position + size > dev_size)
352 		size = (size_t)(dev_size - position);
353 
354 	r = sys_safecopyto(endpt, grant, 0,
355 	    (vir_bytes)(buffer + (size_t)position), size);
356 
357 	return (r != OK) ? r : size;
358 }
359 
360 static void
361 sht21_other(message * m, int ipc_status)
362 {
363 	int r;
364 
365 	if (is_ipc_notify(ipc_status)) {
366 		if (m->m_source == DS_PROC_NR) {
367 			log_debug(&log,
368 			    "bus driver changed state, update endpoint\n");
369 			i2cdriver_handle_bus_update(&bus_endpoint, bus,
370 			    address);
371 		}
372 		return;
373 	}
374 
375 	log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
376 }
377 
378 static int
379 sef_cb_lu_state_save(int UNUSED(state))
380 {
381 	ds_publish_u32("bus", bus, DSF_OVERWRITE);
382 	ds_publish_u32("address", address, DSF_OVERWRITE);
383 	return OK;
384 }
385 
386 static int
387 lu_state_restore(void)
388 {
389 	/* Restore the state. */
390 	u32_t value;
391 
392 	ds_retrieve_u32("bus", &value);
393 	ds_delete_u32("bus");
394 	bus = (int) value;
395 
396 	ds_retrieve_u32("address", &value);
397 	ds_delete_u32("address");
398 	address = (int) value;
399 
400 	return OK;
401 }
402 
403 static int
404 sef_cb_init(int type, sef_init_info_t * UNUSED(info))
405 {
406 	int r;
407 
408 	if (type == SEF_INIT_LU) {
409 		/* Restore the state. */
410 		lu_state_restore();
411 	}
412 
413 	/* look-up the endpoint for the bus driver */
414 	bus_endpoint = i2cdriver_bus_endpoint(bus);
415 	if (bus_endpoint == 0) {
416 		log_warn(&log, "Couldn't find bus driver.\n");
417 		return EXIT_FAILURE;
418 	}
419 
420 	/* claim the device */
421 	r = i2cdriver_reserve_device(bus_endpoint, address);
422 	if (r != OK) {
423 		log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
424 		    address, r);
425 		return EXIT_FAILURE;
426 	}
427 
428 	r = sht21_init();
429 	if (r != OK) {
430 		log_warn(&log, "Device Init Failed\n");
431 		return EXIT_FAILURE;
432 	}
433 
434 	if (type != SEF_INIT_LU) {
435 
436 		/* sign up for updates about the i2c bus going down/up */
437 		r = i2cdriver_subscribe_bus_updates(bus);
438 		if (r != OK) {
439 			log_warn(&log, "Couldn't subscribe to bus updates\n");
440 			return EXIT_FAILURE;
441 		}
442 
443 		i2cdriver_announce(bus);
444 		log_debug(&log, "announced\n");
445 	}
446 
447 	return OK;
448 }
449 
450 static void
451 sef_local_startup(void)
452 {
453 	/*
454 	 * Register init callbacks. Use the same function for all event types
455 	 */
456 	sef_setcb_init_fresh(sef_cb_init);
457 	sef_setcb_init_lu(sef_cb_init);
458 	sef_setcb_init_restart(sef_cb_init);
459 
460 	/*
461 	 * Register live update callbacks.
462 	 */
463 	/* Agree to update immediately when LU is requested in a valid state. */
464 	sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
465 	/* Support live update starting from any standard state. */
466 	sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
467 	/* Register a custom routine to save the state. */
468 	sef_setcb_lu_state_save(sef_cb_lu_state_save);
469 
470 	/* Let SEF perform startup. */
471 	sef_startup();
472 }
473 
474 int
475 main(int argc, char *argv[])
476 {
477 	int r;
478 
479 	env_setargs(argc, argv);
480 
481 	r = i2cdriver_env_parse(&bus, &address, valid_addrs);
482 	if (r < 0) {
483 		log_warn(&log, "Expecting -args 'bus=X address=0xYY'\n");
484 		log_warn(&log, "Example -args 'bus=1 address=0x40'\n");
485 		return EXIT_FAILURE;
486 	} else if (r > 0) {
487 		log_warn(&log,
488 		    "Invalid slave address for device, expecting 0x40\n");
489 		return EXIT_FAILURE;
490 	}
491 
492 	sef_local_startup();
493 
494 	chardriver_task(&sht21_tab);
495 
496 	return 0;
497 }
498