1 /* Driver for the SHT21 Relative Humidity and Temperature Sensor */
4 #include <minix/drivers.h>
6 #include <minix/i2cdriver.h>
7 #include <minix/chardriver.h>
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.
24 * The driver uses the 'hold' versions of the trigger commands.
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
31 /* Read and write the user register contents */
32 #define CMD_WR_USR_REG 0xe6
33 #define CMD_RD_USR_REG 0xe7
36 #define CMD_SOFT_RESET 0xfe
38 /* Status bits included in the measurement need to be masked in calculation */
39 #define STATUS_BITS_MASK 0x0003
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
46 #define USR_REG_RESERVED_MASK ((1<<3)|(1<<4)|(1<<5))
48 /* End of Battery flag is set when the voltage drops below 2.25V. */
49 #define USR_REG_EOB_MASK (1<<6)
51 /* When powered up and communicating, the register should have only the
52 * 'Disable OTP Reload' bit set
54 #define EXPECTED_PWR_UP_TEST_VAL (1<<1)
56 /* Define some constants for the different sensor types on the chip. */
58 { SHT21_T
, SHT21_RH
};
60 /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
61 static struct log log
= {
63 .log_level
= LEVEL_INFO
,
64 .log_func
= default_log
67 /* device slave address is fixed at 0x40 */
68 static i2c_addr_t valid_addrs
[2] = {
72 /* Buffer to store output string returned when reading from device file. */
74 char buffer
[BUFFER_LEN
+ 1];
76 /* the bus that this device is on (counting starting at 1) */
79 /* slave address of the device */
80 static i2c_addr_t address
;
82 /* endpoint for the driver for the bus itself. */
83 static endpoint_t bus_endpoint
;
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.
91 static time_t last_sample_time
= 0;
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.
98 static int32_t cached_t
= 0.0;
99 static int32_t cached_rh
= 0.0;
102 * An 8-bit CRC is used to validate the readings.
104 #define CRC8_POLYNOMIAL 0x131
105 #define CRC8_INITIAL_CRC 0x00
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);
113 static uint8_t crc8(uint8_t crc
, uint8_t byte
);
114 static int checksum(uint8_t * bytes
, int nbytes
, uint8_t expected_crc
);
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
);
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);
127 /* Entry points to this driver from libchardriver. */
128 static struct chardriver sht21_tab
= {
129 .cdr_read
= sht21_read
,
130 .cdr_other
= sht21_other
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.
143 /* Perform a soft-reset */
144 r
= i2creg_raw_write8(bus_endpoint
, address
, CMD_SOFT_RESET
);
149 /* soft reset takes up to 15 ms to complete. */
152 log_debug(&log
, "Soft Reset Complete\n");
154 r
= i2creg_read8(bus_endpoint
, address
, CMD_RD_USR_REG
, &usr_reg_val
);
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");
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
));
178 * Read from the sensor, check the CRC, convert the ADC value into the final
179 * representation, and store the result in measurement.
182 sensor_read(enum sht21_sensors sensor
, int32_t * measurement
)
189 uint8_t expected_crc
;
193 cmd
= CMD_TRIG_T_HOLD
;
196 cmd
= CMD_TRIG_RH_HOLD
;
199 log_warn(&log
, "sensor_read() called with bad sensor type.\n");
203 if (measurement
== NULL
) {
204 log_warn(&log
, "sensor_read() called with NULL pointer\n");
208 r
= i2creg_read24(bus_endpoint
, address
, cmd
, &val32
);
210 log_warn(&log
, "sensor_read() failed (r=%d)\n", r
);
214 expected_crc
= val32
& 0xff;
215 val
= (val32
>> 8) & 0xffff;
217 bytes
[0] = (val
>> 8) & 0xff;
218 bytes
[1] = val
& 0xff;
220 r
= checksum(bytes
, 2, expected_crc
);
225 val
&= ~STATUS_BITS_MASK
; /* clear status bits */
227 log_debug(&log
, "Read VAL:0x%x CRC:0x%x\n", val
, expected_crc
);
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
) {
237 ((125.0 / 65536) * ((float) val
))) * 1000.0);
238 log_debug(&log
, "Measured Humidity %d m%%\n", *measurement
);
251 log_debug(&log
, "Taking a measurement...");
253 sample_time
= time(NULL
);
254 if (sample_time
== last_sample_time
) {
255 log_debug(&log
, "measure() called too soon, using cache.\n");
259 r
= sensor_read(SHT21_T
, &t
);
264 r
= sensor_read(SHT21_RH
, &rh
);
269 /* save measured values */
272 last_sample_time
= time(NULL
);
274 log_debug(&log
, "Measurement completed\n");
280 * Return an updated checksum for the given crc and byte.
283 crc8(uint8_t crc
, uint8_t byte
)
289 for (i
= 0; i
< 8; i
++) {
291 if ((crc
& 0x80) == 0x80) {
292 crc
= (crc
<< 1) ^ CRC8_POLYNOMIAL
;
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.
306 checksum(uint8_t * bytes
, int nbytes
, uint8_t expected_crc
)
311 crc
= CRC8_INITIAL_CRC
;
313 log_debug(&log
, "Checking CRC\n");
315 for (i
= 0; i
< nbytes
; i
++) {
316 crc
= crc8(crc
, bytes
[i
]);
319 if (crc
== expected_crc
) {
320 log_debug(&log
, "CRC OK\n");
324 "Bad CRC -- Computed CRC: 0x%x | Expected CRC: 0x%x\n",
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
))
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);
347 log_trace(&log
, "%s", buffer
);
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
);
354 r
= sys_safecopyto(endpt
, grant
, 0,
355 (vir_bytes
)(buffer
+ (size_t)position
), size
);
357 return (r
!= OK
) ? r
: size
;
361 sht21_other(message
* m
, int ipc_status
)
365 if (is_ipc_notify(ipc_status
)) {
366 if (m
->m_source
== DS_PROC_NR
) {
368 "bus driver changed state, update endpoint\n");
369 i2cdriver_handle_bus_update(&bus_endpoint
, bus
,
375 log_warn(&log
, "Invalid message type (0x%x)\n", m
->m_type
);
379 sef_cb_lu_state_save(int UNUSED(state
))
381 ds_publish_u32("bus", bus
, DSF_OVERWRITE
);
382 ds_publish_u32("address", address
, DSF_OVERWRITE
);
387 lu_state_restore(void)
389 /* Restore the state. */
392 ds_retrieve_u32("bus", &value
);
393 ds_delete_u32("bus");
396 ds_retrieve_u32("address", &value
);
397 ds_delete_u32("address");
398 address
= (int) value
;
404 sef_cb_init(int type
, sef_init_info_t
* UNUSED(info
))
408 if (type
== SEF_INIT_LU
) {
409 /* Restore the state. */
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");
420 /* claim the device */
421 r
= i2cdriver_reserve_device(bus_endpoint
, address
);
423 log_warn(&log
, "Couldn't reserve device 0x%x (r=%d)\n",
430 log_warn(&log
, "Device Init Failed\n");
434 if (type
!= SEF_INIT_LU
) {
436 /* sign up for updates about the i2c bus going down/up */
437 r
= i2cdriver_subscribe_bus_updates(bus
);
439 log_warn(&log
, "Couldn't subscribe to bus updates\n");
443 i2cdriver_announce(bus
);
444 log_debug(&log
, "announced\n");
451 sef_local_startup(void)
454 * Register init callbacks. Use the same function for all event types
456 sef_setcb_init_fresh(sef_cb_init
);
457 sef_setcb_init_lu(sef_cb_init
);
458 sef_setcb_init_restart(sef_cb_init
);
461 * Register live update callbacks.
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
);
470 /* Let SEF perform startup. */
475 main(int argc
, char *argv
[])
479 env_setargs(argc
, argv
);
481 r
= i2cdriver_env_parse(&bus
, &address
, valid_addrs
);
483 log_warn(&log
, "Expecting -args 'bus=X address=0xYY'\n");
484 log_warn(&log
, "Example -args 'bus=1 address=0x40'\n");
488 "Invalid slave address for device, expecting 0x40\n");
494 chardriver_task(&sht21_tab
);