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
);
121 /* Entry points to this driver from libchardriver. */
122 static struct chardriver sht21_tab
= {
123 .cdr_read
= sht21_read
,
124 .cdr_other
= sht21_other
128 * Performs a soft reset and reads the contents of the user register to ensure
129 * that the chip is in a good state and working properly.
137 /* Perform a soft-reset */
138 r
= i2creg_raw_write8(bus_endpoint
, address
, CMD_SOFT_RESET
);
143 /* soft reset takes up to 15 ms to complete. */
146 log_debug(&log
, "Soft Reset Complete\n");
148 r
= i2creg_read8(bus_endpoint
, address
, CMD_RD_USR_REG
, &usr_reg_val
);
153 /* Check for End of Battery flag. */
154 if ((usr_reg_val
& USR_REG_EOB_MASK
) == USR_REG_EOB_MASK
) {
155 log_warn(&log
, "End of Battery Alarm\n");
159 /* Check that the non-reserved bits are in the default state. */
160 if ((usr_reg_val
& ~USR_REG_RESERVED_MASK
) != EXPECTED_PWR_UP_TEST_VAL
) {
161 log_warn(&log
, "USR_REG has non-default values after reset\n");
162 log_warn(&log
, "Expected 0x%x | Actual 0x%x",
163 EXPECTED_PWR_UP_TEST_VAL
,
164 (usr_reg_val
& ~USR_REG_RESERVED_MASK
));
172 * Read from the sensor, check the CRC, convert the ADC value into the final
173 * representation, and store the result in measurement.
176 sensor_read(enum sht21_sensors sensor
, int32_t * measurement
)
183 uint8_t expected_crc
;
187 cmd
= CMD_TRIG_T_HOLD
;
190 cmd
= CMD_TRIG_RH_HOLD
;
193 log_warn(&log
, "sensor_read() called with bad sensor type.\n");
197 if (measurement
== NULL
) {
198 log_warn(&log
, "sensor_read() called with NULL pointer\n");
202 r
= i2creg_read24(bus_endpoint
, address
, cmd
, &val32
);
204 log_warn(&log
, "sensor_read() failed (r=%d)\n", r
);
208 expected_crc
= val32
& 0xff;
209 val
= (val32
>> 8) & 0xffff;
211 bytes
[0] = (val
>> 8) & 0xff;
212 bytes
[1] = val
& 0xff;
214 r
= checksum(bytes
, 2, expected_crc
);
219 val
&= ~STATUS_BITS_MASK
; /* clear status bits */
221 log_debug(&log
, "Read VAL:0x%x CRC:0x%x\n", val
, expected_crc
);
223 /* Convert the ADC value to the actual value. */
224 if (cmd
== CMD_TRIG_T_HOLD
) {
225 *measurement
= (int32_t)
226 ((-46.85 + ((175.72 / 65536) * ((float) val
))) * 1000.0);
227 log_debug(&log
, "Measured Temperature %d mC\n", *measurement
);
228 } else if (cmd
== CMD_TRIG_RH_HOLD
) {
231 ((125.0 / 65536) * ((float) val
))) * 1000.0);
232 log_debug(&log
, "Measured Humidity %d m%%\n", *measurement
);
245 log_debug(&log
, "Taking a measurement...");
247 sample_time
= time(NULL
);
248 if (sample_time
== last_sample_time
) {
249 log_debug(&log
, "measure() called too soon, using cache.\n");
253 r
= sensor_read(SHT21_T
, &t
);
258 r
= sensor_read(SHT21_RH
, &rh
);
263 /* save measured values */
266 last_sample_time
= time(NULL
);
268 log_debug(&log
, "Measurement completed\n");
274 * Return an updated checksum for the given crc and byte.
277 crc8(uint8_t crc
, uint8_t byte
)
283 for (i
= 0; i
< 8; i
++) {
285 if ((crc
& 0x80) == 0x80) {
286 crc
= (crc
<< 1) ^ CRC8_POLYNOMIAL
;
296 * Compute the CRC of an array of bytes and compare it to expected_crc.
297 * If the computed CRC matches expected_crc, then return OK, otherwise EINVAL.
300 checksum(uint8_t * bytes
, int nbytes
, uint8_t expected_crc
)
305 crc
= CRC8_INITIAL_CRC
;
307 log_debug(&log
, "Checking CRC\n");
309 for (i
= 0; i
< nbytes
; i
++) {
310 crc
= crc8(crc
, bytes
[i
]);
313 if (crc
== expected_crc
) {
314 log_debug(&log
, "CRC OK\n");
318 "Bad CRC -- Computed CRC: 0x%x | Expected CRC: 0x%x\n",
325 sht21_read(devminor_t
UNUSED(minor
), u64_t position
, endpoint_t endpt
,
326 cp_grant_id_t grant
, size_t size
, int UNUSED(flags
), cdev_id_t
UNUSED(id
))
336 memset(buffer
, '\0', BUFFER_LEN
+ 1);
337 snprintf(buffer
, BUFFER_LEN
, "%-16s: %d.%03d\n%-16s: %d.%03d\n",
338 "TEMPERATURE", cached_t
/ 1000, cached_t
% 1000, "HUMIDITY",
339 cached_rh
/ 1000, cached_rh
% 1000);
341 log_trace(&log
, "%s", buffer
);
343 dev_size
= (u64_t
)strlen(buffer
);
344 if (position
>= dev_size
) return 0;
345 if (position
+ size
> dev_size
)
346 size
= (size_t)(dev_size
- position
);
348 r
= sys_safecopyto(endpt
, grant
, 0,
349 (vir_bytes
)(buffer
+ (size_t)position
), size
);
351 return (r
!= OK
) ? r
: size
;
355 sht21_other(message
* m
, int ipc_status
)
359 if (is_ipc_notify(ipc_status
)) {
360 if (m
->m_source
== DS_PROC_NR
) {
362 "bus driver changed state, update endpoint\n");
363 i2cdriver_handle_bus_update(&bus_endpoint
, bus
,
369 log_warn(&log
, "Invalid message type (0x%x)\n", m
->m_type
);
373 sef_cb_lu_state_save(int UNUSED(result
), int UNUSED(flags
))
375 ds_publish_u32("bus", bus
, DSF_OVERWRITE
);
376 ds_publish_u32("address", address
, DSF_OVERWRITE
);
381 lu_state_restore(void)
383 /* Restore the state. */
386 ds_retrieve_u32("bus", &value
);
387 ds_delete_u32("bus");
390 ds_retrieve_u32("address", &value
);
391 ds_delete_u32("address");
392 address
= (int) value
;
398 sef_cb_init(int type
, sef_init_info_t
* UNUSED(info
))
402 if (type
== SEF_INIT_LU
) {
403 /* Restore the state. */
407 /* look-up the endpoint for the bus driver */
408 bus_endpoint
= i2cdriver_bus_endpoint(bus
);
409 if (bus_endpoint
== 0) {
410 log_warn(&log
, "Couldn't find bus driver.\n");
414 /* claim the device */
415 r
= i2cdriver_reserve_device(bus_endpoint
, address
);
417 log_warn(&log
, "Couldn't reserve device 0x%x (r=%d)\n",
424 log_warn(&log
, "Device Init Failed\n");
428 if (type
!= SEF_INIT_LU
) {
430 /* sign up for updates about the i2c bus going down/up */
431 r
= i2cdriver_subscribe_bus_updates(bus
);
433 log_warn(&log
, "Couldn't subscribe to bus updates\n");
437 i2cdriver_announce(bus
);
438 log_debug(&log
, "announced\n");
445 sef_local_startup(void)
448 * Register init callbacks. Use the same function for all event types
450 sef_setcb_init_fresh(sef_cb_init
);
451 sef_setcb_init_lu(sef_cb_init
);
452 sef_setcb_init_restart(sef_cb_init
);
455 * Register live update callbacks.
457 sef_setcb_lu_state_save(sef_cb_lu_state_save
);
459 /* Let SEF perform startup. */
464 main(int argc
, char *argv
[])
468 env_setargs(argc
, argv
);
470 r
= i2cdriver_env_parse(&bus
, &address
, valid_addrs
);
472 log_warn(&log
, "Expecting -args 'bus=X address=0xYY'\n");
473 log_warn(&log
, "Example -args 'bus=1 address=0x40'\n");
477 "Invalid slave address for device, expecting 0x40\n");
483 chardriver_task(&sht21_tab
);