A driver for the Sensirion SHT40/SHT41/SHT45 temperature and humidity
sensor. An example of this chip is: https://www.adafruit.com/product/4885 This is a lower cost chip that provides higher then usual precision according to the data sheet. This driver supports all of the published functions that the chip has.
This commit is contained in:
parent
0eda81aab2
commit
ee2dd9d79d
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: mi,v 1.1725 2021/08/01 21:56:26 andvar Exp $
|
||||
# $NetBSD: mi,v 1.1726 2021/10/03 17:27:02 brad Exp $
|
||||
#
|
||||
# Note: don't delete entries from here - mark them as "obsolete" instead.
|
||||
#
|
||||
|
@ -1723,6 +1723,7 @@
|
|||
./usr/share/man/cat4/shb.0 man-sys-catman .cat
|
||||
./usr/share/man/cat4/shmif.0 man-sys-catman .cat
|
||||
./usr/share/man/cat4/shpcic.0 man-sys-catman .cat
|
||||
./usr/share/man/cat4/sht4xtemp.0 man-sys-catman .cat
|
||||
./usr/share/man/cat4/si.0 man-sys-catman .cat
|
||||
./usr/share/man/cat4/si70xxtemp.0 man-sys-catman .cat
|
||||
./usr/share/man/cat4/siisata.0 man-sys-catman .cat
|
||||
|
@ -4896,6 +4897,7 @@
|
|||
./usr/share/man/html4/shb.html man-sys-htmlman html
|
||||
./usr/share/man/html4/shmif.html man-sys-htmlman html
|
||||
./usr/share/man/html4/shpcic.html man-sys-htmlman html
|
||||
./usr/share/man/html4/sht4xtemp.html man-sys-htmlman html
|
||||
./usr/share/man/html4/si.html man-sys-htmlman html
|
||||
./usr/share/man/html4/si70xxtemp.html man-sys-htmlman html
|
||||
./usr/share/man/html4/siisata.html man-sys-htmlman html
|
||||
|
@ -7975,6 +7977,7 @@
|
|||
./usr/share/man/man4/shb.4 man-sys-man .man
|
||||
./usr/share/man/man4/shmif.4 man-sys-man .man
|
||||
./usr/share/man/man4/shpcic.4 man-sys-man .man
|
||||
./usr/share/man/man4/sht4xtemp.4 man-sys-man .man
|
||||
./usr/share/man/man4/si.4 man-sys-man .man
|
||||
./usr/share/man/man4/si70xxtemp.4 man-sys-man .man
|
||||
./usr/share/man/man4/siisata.4 man-sys-man .man
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: mi,v 1.146 2021/09/11 16:10:37 pgoyette Exp $
|
||||
# $NetBSD: mi,v 1.147 2021/10/03 17:27:02 brad Exp $
|
||||
#
|
||||
# Note: don't delete entries from here - mark them as "obsolete" instead.
|
||||
#
|
||||
|
@ -397,6 +397,8 @@
|
|||
./@MODULEDIR@/securelevel/securelevel.kmod modules-base-kernel kmod
|
||||
./@MODULEDIR@/sequencer modules-base-kernel kmod
|
||||
./@MODULEDIR@/sequencer/sequencer.kmod modules-base-kernel kmod
|
||||
./@MODULEDIR@/sht4xtemp modules-base-kernel kmod
|
||||
./@MODULEDIR@/sht4xtemp/sht4xtemp.kmod modules-base-kernel kmod
|
||||
./@MODULEDIR@/si70xxtemp modules-base-kernel kmod
|
||||
./@MODULEDIR@/si70xxtemp/si70xxtemp.kmod modules-base-kernel kmod
|
||||
./@MODULEDIR@/skipjack modules-base-kernel kmod
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: Makefile,v 1.715 2021/08/01 21:56:27 andvar Exp $
|
||||
# $NetBSD: Makefile,v 1.716 2021/10/03 17:27:02 brad Exp $
|
||||
# @(#)Makefile 8.1 (Berkeley) 6/18/93
|
||||
|
||||
MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \
|
||||
|
@ -56,7 +56,7 @@ MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \
|
|||
rnd.4 route.4 rs5c372rtc.4 rtk.4 rtsx.4 rtw.4 rtwn.4 rum.4 run.4 \
|
||||
s390rtc.4 satalink.4 sbus.4 schide.4 \
|
||||
scsi.4 sctp.4 sd.4 se.4 seeprom.4 sem.4 \
|
||||
ses.4 sf.4 sfb.4 sgsmix.4 shb.4 shmif.4 shpcic.4 si70xxtemp.4 \
|
||||
ses.4 sf.4 sfb.4 sgsmix.4 shb.4 shmif.4 shpcic.4 sht4xtemp.4 si70xxtemp.4 \
|
||||
siisata.4 siop.4 sip.4 siside.4 sk.4 sl.4 slide.4 \
|
||||
sm.4 smscphy.4 smsh.4 sn.4 sony.4 spc.4 speaker.4 spif.4 sqphy.4 \
|
||||
srt.4 ss.4 \
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
.\" $NetBSD: sht4xtemp.4,v 1.1 2021/10/03 17:27:02 brad Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2021 Brad Spencer <brad@anduin.eldar.org>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd September 28th, 2021
|
||||
.Dt SHT4XTEMP 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm sht4xtemp
|
||||
.Nd Driver for Sensirion SHT40/SHT41/SHT45 sensor chip via I2C bus
|
||||
.Sh SYNOPSIS
|
||||
.Cd "sht4xtemp* at iic? addr 0x44"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
driver provides measurements from the SHT40/SHT41/SHT45 humidity/temperature
|
||||
sensors via the
|
||||
.Xr envsys 4
|
||||
framework.
|
||||
The
|
||||
.Nm
|
||||
.Ar addr
|
||||
argument selects the address at the
|
||||
.Xr iic 4
|
||||
bus.
|
||||
The resolution, heater controls and crc validity can be changed through
|
||||
.Xr sysctl 8
|
||||
nodes.
|
||||
.Sh SYSCTL VARIABLES
|
||||
The following
|
||||
.Xr sysctl 3
|
||||
variables are provided:
|
||||
.Bl -tag -width indent
|
||||
.It hw.sht4xtemp0.resolutions
|
||||
Lists the resolutions supported by the driver and chip.
|
||||
.It hw.sht4xtemp0.resolution
|
||||
Set the resolution, or number of bits, used for %RH and temperature.
|
||||
Use one of the strings listed in hw.sht4xtemp.resolutions.
|
||||
.It hw.sht4xtemp0.ignorecrc
|
||||
If set, the crc calculation for %RH and temperature will be ignored.
|
||||
.It hw.sht4xtemp0.heateron
|
||||
Turn the heater on and off. Please note that the heater is turned on right
|
||||
before the measurement and runs for a pulse width of time. Then the measurement
|
||||
is taken and the heater is turned off. There is no way to keep the heater running
|
||||
with this chip.
|
||||
.It hw.sht4xtemp0.heaterstrength
|
||||
From 1 to 3, the amount of energy put into the heater.
|
||||
The higher the number, the more power used.
|
||||
.It hw.sht4xtemp0.heaterpulses
|
||||
Lists the valid heater pulses supported by the driver and chip.
|
||||
.It hw.sht4xtemp0.heaterpulse
|
||||
Set the heater pulse length. Use one of the strings listed in
|
||||
hw.sht4xtemp.heaterpulses
|
||||
.It hw.sht4xtemp0.debug
|
||||
If the driver is compiled with
|
||||
.Dv SI70XX_DEBUG ,
|
||||
this node will appear and can be used to set the debugging level.
|
||||
.It hw.sht4xtemp0.readattempts
|
||||
To read %RH or temperature the chip requires that the command be sent,
|
||||
then a delay must be observed before a read can be done to get the values
|
||||
back. The delays are documented in the datasheet for the chip.
|
||||
The driver will attempt to read back the values readattempts number of
|
||||
times.
|
||||
The default is 10 which should be more than enough for most purposes.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr envsys 4 ,
|
||||
.Xr iic 4 ,
|
||||
.Xr envstat 8 ,
|
||||
.Xr sysctl 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
driver first appeared in
|
||||
.Nx 10.0 .
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm
|
||||
driver was written by
|
||||
.An Brad Spencer Aq Mt brad@anduin.eldar.org .
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: files.i2c,v 1.116 2021/07/27 20:23:41 macallan Exp $
|
||||
# $NetBSD: files.i2c,v 1.117 2021/10/03 17:27:02 brad Exp $
|
||||
|
||||
obsolete defflag opt_i2cbus.h I2C_SCAN
|
||||
define i2cbus { }
|
||||
|
@ -395,6 +395,11 @@ device cwfg: sysmon_envsys
|
|||
attach cwfg at iic
|
||||
file dev/i2c/cwfg.c cwfg
|
||||
|
||||
# Sensirion SHT40/SHT41/SHT45 Temperature and Humidity sensor
|
||||
device sht4xtemp
|
||||
attach sht4xtemp at iic
|
||||
file dev/i2c/sht4x.c sht4xtemp
|
||||
|
||||
# Philips PCA955x GPIO
|
||||
device pcagpio: leds
|
||||
attach pcagpio at iic
|
||||
|
|
|
@ -0,0 +1,886 @@
|
|||
/* $NetBSD: sht4x.c,v 1.1 2021/10/03 17:27:02 brad Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2021 Brad Spencer <brad@anduin.eldar.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: sht4x.c,v 1.1 2021/10/03 17:27:02 brad Exp $");
|
||||
|
||||
/*
|
||||
Driver for the Sensirion SHT40/SHT41/SHT45
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mutex.h>
|
||||
|
||||
#include <dev/sysmon/sysmonvar.h>
|
||||
#include <dev/i2c/i2cvar.h>
|
||||
#include <dev/i2c/sht4xreg.h>
|
||||
#include <dev/i2c/sht4xvar.h>
|
||||
|
||||
|
||||
static uint8_t sht4x_crc(uint8_t *, size_t);
|
||||
static int sht4x_poke(i2c_tag_t, i2c_addr_t, bool);
|
||||
static int sht4x_match(device_t, cfdata_t, void *);
|
||||
static void sht4x_attach(device_t, device_t, void *);
|
||||
static int sht4x_detach(device_t, int);
|
||||
static void sht4x_refresh(struct sysmon_envsys *, envsys_data_t *);
|
||||
static int sht4x_verify_sysctl(SYSCTLFN_ARGS);
|
||||
static int sht4x_verify_sysctl_resolution(SYSCTLFN_ARGS);
|
||||
static int sht4x_verify_sysctl_heateron(SYSCTLFN_ARGS);
|
||||
static int sht4x_verify_sysctl_heatervalue(SYSCTLFN_ARGS);
|
||||
static int sht4x_verify_sysctl_heaterpulse(SYSCTLFN_ARGS);
|
||||
|
||||
#define SHT4X_DEBUG
|
||||
#ifdef SHT4X_DEBUG
|
||||
#define DPRINTF(s, l, x) \
|
||||
do { \
|
||||
if (l <= s->sc_sht4xdebug) \
|
||||
printf x; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
#else
|
||||
#define DPRINTF(s, l, x)
|
||||
#endif
|
||||
|
||||
CFATTACH_DECL_NEW(sht4xtemp, sizeof(struct sht4x_sc),
|
||||
sht4x_match, sht4x_attach, sht4x_detach, NULL);
|
||||
|
||||
static struct sht4x_sensor sht4x_sensors[] = {
|
||||
{
|
||||
.desc = "humidity",
|
||||
.type = ENVSYS_SRELHUMIDITY,
|
||||
},
|
||||
{
|
||||
.desc = "temperature",
|
||||
.type = ENVSYS_STEMP,
|
||||
}
|
||||
};
|
||||
|
||||
/* The typical delays are documented in the datasheet for the chip.
|
||||
There is no need to be very accurate with these, just rough estimates
|
||||
will work fine.
|
||||
*/
|
||||
|
||||
static struct sht4x_timing sht4x_timings[] = {
|
||||
{
|
||||
.cmd = SHT4X_READ_SERIAL,
|
||||
.typicaldelay = 5000,
|
||||
},
|
||||
{
|
||||
.cmd = SHT4X_SOFT_RESET,
|
||||
.typicaldelay = 1000,
|
||||
},
|
||||
{
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION,
|
||||
.typicaldelay = 8000,
|
||||
},
|
||||
{
|
||||
.cmd = SHT4X_MEASURE_MEDIUM_PRECISION,
|
||||
.typicaldelay = 4000,
|
||||
},
|
||||
{
|
||||
.cmd = SHT4X_MEASURE_LOW_PRECISION,
|
||||
.typicaldelay = 2000,
|
||||
},
|
||||
{
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_1_S,
|
||||
.typicaldelay = 1000000,
|
||||
},
|
||||
{
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_1_S,
|
||||
.typicaldelay = 1000000,
|
||||
},
|
||||
{
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_1_S,
|
||||
.typicaldelay = 1000000,
|
||||
},
|
||||
{
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_TENTH_S,
|
||||
.typicaldelay = 100000,
|
||||
},
|
||||
{
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_TENTH_S,
|
||||
.typicaldelay = 100000,
|
||||
},
|
||||
{
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_TENTH_S,
|
||||
.typicaldelay = 100000,
|
||||
}
|
||||
};
|
||||
|
||||
/* Used when the heater is not on to find the command to use for the
|
||||
* measurement.
|
||||
*/
|
||||
|
||||
static struct sht4x_resolution sht4x_resolutions[] = {
|
||||
{
|
||||
.text = "high",
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION,
|
||||
},
|
||||
{
|
||||
.text = "medium",
|
||||
.cmd = SHT4X_MEASURE_MEDIUM_PRECISION,
|
||||
},
|
||||
{
|
||||
.text = "low",
|
||||
.cmd = SHT4X_MEASURE_LOW_PRECISION,
|
||||
}
|
||||
};
|
||||
|
||||
static const char sht4x_resolution_names[] =
|
||||
"high, medium, low";
|
||||
|
||||
static struct sht4x_heaterpulse sht4x_heaterpulses[] = {
|
||||
{
|
||||
.length = "short",
|
||||
},
|
||||
{
|
||||
.length = "long",
|
||||
}
|
||||
};
|
||||
|
||||
/* This is consulted when the heater is on for which command is to be
|
||||
used for the measurement.
|
||||
*/
|
||||
|
||||
static struct sht4x_heateron_command sht4x_heateron_commands[] = {
|
||||
{
|
||||
.heatervalue = 1,
|
||||
.pulselength = "short",
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_TENTH_S,
|
||||
},
|
||||
{
|
||||
.heatervalue = 2,
|
||||
.pulselength = "short",
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_TENTH_S,
|
||||
},
|
||||
{
|
||||
.heatervalue = 3,
|
||||
.pulselength = "short",
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_TENTH_S,
|
||||
},
|
||||
{
|
||||
.heatervalue = 1,
|
||||
.pulselength = "long",
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_1_S,
|
||||
},
|
||||
{
|
||||
.heatervalue = 2,
|
||||
.pulselength = "long",
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_1_S,
|
||||
},
|
||||
{
|
||||
.heatervalue = 3,
|
||||
.pulselength = "long",
|
||||
.cmd = SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_1_S,
|
||||
}
|
||||
};
|
||||
|
||||
static const char sht4x_heaterpulse_names[] =
|
||||
"short, long";
|
||||
|
||||
int
|
||||
sht4x_verify_sysctl(SYSCTLFN_ARGS)
|
||||
{
|
||||
int error, t;
|
||||
struct sysctlnode node;
|
||||
|
||||
node = *rnode;
|
||||
t = *(int *)rnode->sysctl_data;
|
||||
node.sysctl_data = &t;
|
||||
error = sysctl_lookup(SYSCTLFN_CALL(&node));
|
||||
if (error || newp == NULL)
|
||||
return error;
|
||||
|
||||
if (t < 0)
|
||||
return EINVAL;
|
||||
|
||||
*(int *)rnode->sysctl_data = t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* None of the heater and resolutions sysctls change anything on the chip in
|
||||
real time. The values set are used to send different commands depending on
|
||||
how they are set up.
|
||||
|
||||
What this implies is that the chip could be reset and the driver would not care.
|
||||
|
||||
*/
|
||||
|
||||
int
|
||||
sht4x_verify_sysctl_resolution(SYSCTLFN_ARGS)
|
||||
{
|
||||
char buf[SHT4X_RES_NAME];
|
||||
struct sht4x_sc *sc;
|
||||
struct sysctlnode node;
|
||||
int error = 0;
|
||||
size_t i;
|
||||
|
||||
node = *rnode;
|
||||
sc = node.sysctl_data;
|
||||
(void) memcpy(buf, sc->sc_resolution, SHT4X_RES_NAME);
|
||||
node.sysctl_data = buf;
|
||||
error = sysctl_lookup(SYSCTLFN_CALL(&node));
|
||||
if (error || newp == NULL)
|
||||
return error;
|
||||
|
||||
for (i = 0; i < __arraycount(sht4x_resolutions); i++) {
|
||||
if (strncmp(node.sysctl_data, sht4x_resolutions[i].text,
|
||||
SHT4X_RES_NAME) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == __arraycount(sht4x_resolutions))
|
||||
return EINVAL;
|
||||
(void) memcpy(sc->sc_resolution, node.sysctl_data, SHT4X_RES_NAME);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
sht4x_verify_sysctl_heateron(SYSCTLFN_ARGS)
|
||||
{
|
||||
int error;
|
||||
bool t;
|
||||
struct sht4x_sc *sc;
|
||||
struct sysctlnode node;
|
||||
|
||||
node = *rnode;
|
||||
sc = node.sysctl_data;
|
||||
t = sc->sc_heateron;
|
||||
node.sysctl_data = &t;
|
||||
error = sysctl_lookup(SYSCTLFN_CALL(&node));
|
||||
if (error || newp == NULL)
|
||||
return error;
|
||||
|
||||
sc->sc_heateron = t;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
sht4x_verify_sysctl_heatervalue(SYSCTLFN_ARGS)
|
||||
{
|
||||
int error = 0, t;
|
||||
struct sht4x_sc *sc;
|
||||
struct sysctlnode node;
|
||||
|
||||
node = *rnode;
|
||||
sc = node.sysctl_data;
|
||||
t = sc->sc_heaterval;
|
||||
node.sysctl_data = &t;
|
||||
error = sysctl_lookup(SYSCTLFN_CALL(&node));
|
||||
if (error || newp == NULL)
|
||||
return (error);
|
||||
|
||||
if (t < 1 || t > 3)
|
||||
return (EINVAL);
|
||||
|
||||
sc->sc_heaterval = t;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
sht4x_verify_sysctl_heaterpulse(SYSCTLFN_ARGS)
|
||||
{
|
||||
char buf[SHT4X_PULSE_NAME];
|
||||
struct sht4x_sc *sc;
|
||||
struct sysctlnode node;
|
||||
int error = 0;
|
||||
size_t i;
|
||||
|
||||
node = *rnode;
|
||||
sc = node.sysctl_data;
|
||||
(void) memcpy(buf, sc->sc_heaterpulse, SHT4X_PULSE_NAME);
|
||||
node.sysctl_data = buf;
|
||||
error = sysctl_lookup(SYSCTLFN_CALL(&node));
|
||||
if (error || newp == NULL)
|
||||
return error;
|
||||
|
||||
for (i = 0; i < __arraycount(sht4x_heaterpulses); i++) {
|
||||
if (strncmp(node.sysctl_data, sht4x_heaterpulses[i].length,
|
||||
SHT4X_RES_NAME) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == __arraycount(sht4x_heaterpulses))
|
||||
return EINVAL;
|
||||
(void) memcpy(sc->sc_heaterpulse, node.sysctl_data, SHT4X_PULSE_NAME);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
sht4x_cmddelay(uint8_t cmd)
|
||||
{
|
||||
int r = -1;
|
||||
|
||||
for(int i = 0;i < __arraycount(sht4x_timings);i++) {
|
||||
if (cmd == sht4x_timings[i].cmd) {
|
||||
r = sht4x_timings[i].typicaldelay;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (r == -1) {
|
||||
panic("Bad command look up in cmd delay: cmd: %d\n",cmd);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
sht4x_cmd(i2c_tag_t tag, i2c_addr_t addr, uint8_t *cmd,
|
||||
uint8_t clen, uint8_t *buf, size_t blen, int readattempts)
|
||||
{
|
||||
int error;
|
||||
int cmddelay;
|
||||
|
||||
error = iic_exec(tag,I2C_OP_WRITE_WITH_STOP,addr,cmd,clen,NULL,0,0);
|
||||
|
||||
/* Every command returns something except for the soft reset
|
||||
which returns nothing. This chip is also nice in that pretty
|
||||
much every command that returns something does it in the same way.
|
||||
*/
|
||||
if (error == 0 && cmd[0] != SHT4X_SOFT_RESET) {
|
||||
cmddelay = sht4x_cmddelay(cmd[0]);
|
||||
delay(cmddelay);
|
||||
|
||||
for (int aint = 0; aint < readattempts; aint++) {
|
||||
error = iic_exec(tag,I2C_OP_READ_WITH_STOP,addr,NULL,0,buf,blen,0);
|
||||
if (error == 0)
|
||||
break;
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
sht4x_cmdr(struct sht4x_sc *sc, uint8_t cmd, uint8_t *buf, size_t blen)
|
||||
{
|
||||
return sht4x_cmd(sc->sc_tag, sc->sc_addr, &cmd, 1, buf, blen, sc->sc_readattempts);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
sht4x_crc(uint8_t * data, size_t size)
|
||||
{
|
||||
uint8_t crc = 0xFF;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
crc ^= data[i];
|
||||
for (size_t j = 8; j > 0; j--) {
|
||||
if (crc & 0x80)
|
||||
crc = (crc << 1) ^ 0x131;
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static int
|
||||
sht4x_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug)
|
||||
{
|
||||
uint8_t reg = SHT4X_READ_SERIAL;
|
||||
uint8_t buf[6];
|
||||
int error;
|
||||
|
||||
error = sht4x_cmd(tag, addr, ®, 1, buf, 6, 10);
|
||||
if (matchdebug) {
|
||||
printf("poke X 1: %d\n", error);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
sht4x_sysctl_init(struct sht4x_sc *sc)
|
||||
{
|
||||
int error;
|
||||
const struct sysctlnode *cnode;
|
||||
int sysctlroot_num;
|
||||
|
||||
if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
|
||||
0, CTLTYPE_NODE, device_xname(sc->sc_dev),
|
||||
SYSCTL_DESCR("sht4x controls"), NULL, 0, NULL, 0, CTL_HW,
|
||||
CTL_CREATE, CTL_EOL)) != 0)
|
||||
return error;
|
||||
|
||||
sysctlroot_num = cnode->sysctl_num;
|
||||
|
||||
#ifdef SHT4X_DEBUG
|
||||
if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
|
||||
CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
|
||||
SYSCTL_DESCR("Debug level"), sht4x_verify_sysctl, 0,
|
||||
&sc->sc_sht4xdebug, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
|
||||
CTL_EOL)) != 0)
|
||||
return error;
|
||||
|
||||
#endif
|
||||
|
||||
if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
|
||||
CTLFLAG_READWRITE, CTLTYPE_INT, "readattempts",
|
||||
SYSCTL_DESCR("The number of times to attempt to read the values"),
|
||||
sht4x_verify_sysctl, 0, &sc->sc_readattempts, 0, CTL_HW,
|
||||
sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
|
||||
return error;
|
||||
|
||||
if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
|
||||
CTLFLAG_READONLY, CTLTYPE_STRING, "resolutions",
|
||||
SYSCTL_DESCR("Valid resolutions"), 0, 0,
|
||||
__UNCONST(sht4x_resolution_names),
|
||||
sizeof(sht4x_resolution_names) + 1,
|
||||
CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
|
||||
return error;
|
||||
|
||||
if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
|
||||
CTLFLAG_READWRITE, CTLTYPE_STRING, "resolution",
|
||||
SYSCTL_DESCR("Resolution of RH and Temp"),
|
||||
sht4x_verify_sysctl_resolution, 0, (void *) sc,
|
||||
SHT4X_RES_NAME, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
|
||||
return error;
|
||||
|
||||
if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
|
||||
CTLFLAG_READWRITE, CTLTYPE_BOOL, "ignorecrc",
|
||||
SYSCTL_DESCR("Ignore the CRC byte"), NULL, 0, &sc->sc_ignorecrc,
|
||||
0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
|
||||
return error;
|
||||
|
||||
if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
|
||||
CTLFLAG_READWRITE, CTLTYPE_BOOL, "heateron",
|
||||
SYSCTL_DESCR("Heater on"), sht4x_verify_sysctl_heateron, 0,
|
||||
(void *)sc, 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
|
||||
return error;
|
||||
|
||||
if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
|
||||
CTLFLAG_READWRITE, CTLTYPE_INT, "heaterstrength",
|
||||
SYSCTL_DESCR("Heater strength 1 to 3"),
|
||||
sht4x_verify_sysctl_heatervalue, 0, (void *)sc, 0, CTL_HW,
|
||||
sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
|
||||
return error;
|
||||
|
||||
if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
|
||||
CTLFLAG_READONLY, CTLTYPE_STRING, "heaterpulses",
|
||||
SYSCTL_DESCR("Valid heater pulse lengths"), 0, 0,
|
||||
__UNCONST(sht4x_heaterpulse_names),
|
||||
sizeof(sht4x_heaterpulse_names) + 1,
|
||||
CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
|
||||
return error;
|
||||
|
||||
if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
|
||||
CTLFLAG_READWRITE, CTLTYPE_STRING, "heaterpulse",
|
||||
SYSCTL_DESCR("Heater pulse length"),
|
||||
sht4x_verify_sysctl_heaterpulse, 0, (void *) sc,
|
||||
SHT4X_RES_NAME, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sht4x_match(device_t parent, cfdata_t match, void *aux)
|
||||
{
|
||||
struct i2c_attach_args *ia = aux;
|
||||
int error, match_result;
|
||||
const bool matchdebug = false;
|
||||
|
||||
if (iic_use_direct_match(ia, match, NULL, &match_result))
|
||||
return match_result;
|
||||
|
||||
/* indirect config - check for configured address */
|
||||
if (ia->ia_addr != SHT4X_TYPICAL_ADDR)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Check to see if something is really at this i2c address. This will
|
||||
* keep phantom devices from appearing
|
||||
*/
|
||||
if (iic_acquire_bus(ia->ia_tag, 0) != 0) {
|
||||
if (matchdebug)
|
||||
printf("in match acquire bus failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
error = sht4x_poke(ia->ia_tag, ia->ia_addr, matchdebug);
|
||||
iic_release_bus(ia->ia_tag, 0);
|
||||
|
||||
return error == 0 ? I2C_MATCH_ADDRESS_AND_PROBE : 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sht4x_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
struct sht4x_sc *sc;
|
||||
struct i2c_attach_args *ia;
|
||||
int error, i;
|
||||
int ecount = 0;
|
||||
uint8_t buf[6];
|
||||
uint8_t sncrcpt1, sncrcpt2;
|
||||
|
||||
ia = aux;
|
||||
sc = device_private(self);
|
||||
|
||||
sc->sc_dev = self;
|
||||
sc->sc_tag = ia->ia_tag;
|
||||
sc->sc_addr = ia->ia_addr;
|
||||
sc->sc_sht4xdebug = 0;
|
||||
strlcpy(sc->sc_resolution,"high",SHT4X_RES_NAME);
|
||||
sc->sc_readattempts = 10;
|
||||
sc->sc_ignorecrc = false;
|
||||
sc->sc_heateron = false;
|
||||
sc->sc_heaterval = 1;
|
||||
strlcpy(sc->sc_heaterpulse,"short",SHT4X_PULSE_NAME);
|
||||
sc->sc_sme = NULL;
|
||||
|
||||
aprint_normal("\n");
|
||||
|
||||
mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
|
||||
sc->sc_numsensors = __arraycount(sht4x_sensors);
|
||||
|
||||
if ((sc->sc_sme = sysmon_envsys_create()) == NULL) {
|
||||
aprint_error_dev(self,
|
||||
"Unable to create sysmon structure\n");
|
||||
sc->sc_sme = NULL;
|
||||
return;
|
||||
}
|
||||
if ((error = sht4x_sysctl_init(sc)) != 0) {
|
||||
aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = iic_acquire_bus(sc->sc_tag, 0);
|
||||
if (error) {
|
||||
aprint_error_dev(self, "Could not acquire iic bus: %d\n",
|
||||
error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = sht4x_cmdr(sc, SHT4X_SOFT_RESET, NULL, 0);
|
||||
if (error != 0)
|
||||
aprint_error_dev(self, "Reset failed: %d\n", error);
|
||||
|
||||
delay(1000); /* 1 ms max */
|
||||
|
||||
error = sht4x_cmdr(sc, SHT4X_READ_SERIAL, buf, 6);
|
||||
if (error) {
|
||||
aprint_error_dev(self, "Failed to read serial number: %d\n",
|
||||
error);
|
||||
ecount++;
|
||||
}
|
||||
|
||||
sncrcpt1 = sht4x_crc(&buf[0],2);
|
||||
sncrcpt2 = sht4x_crc(&buf[3],2);
|
||||
|
||||
DPRINTF(sc, 2, ("%s: read serial number values: %02x%02x - %02x, %02x%02x - %02x ; %02x %02x\n",
|
||||
device_xname(sc->sc_dev), buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], sncrcpt1, sncrcpt2));
|
||||
|
||||
iic_release_bus(sc->sc_tag, 0);
|
||||
if (error != 0) {
|
||||
aprint_error_dev(self, "Unable to setup device\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < sc->sc_numsensors; i++) {
|
||||
strlcpy(sc->sc_sensors[i].desc, sht4x_sensors[i].desc,
|
||||
sizeof(sc->sc_sensors[i].desc));
|
||||
|
||||
sc->sc_sensors[i].units = sht4x_sensors[i].type;
|
||||
sc->sc_sensors[i].state = ENVSYS_SINVALID;
|
||||
|
||||
DPRINTF(sc, 2, ("%s: registering sensor %d (%s)\n", __func__, i,
|
||||
sc->sc_sensors[i].desc));
|
||||
|
||||
error = sysmon_envsys_sensor_attach(sc->sc_sme,
|
||||
&sc->sc_sensors[i]);
|
||||
if (error) {
|
||||
aprint_error_dev(self,
|
||||
"Unable to attach sensor %d: %d\n", i, error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
sc->sc_sme->sme_name = device_xname(sc->sc_dev);
|
||||
sc->sc_sme->sme_cookie = sc;
|
||||
sc->sc_sme->sme_refresh = sht4x_refresh;
|
||||
|
||||
DPRINTF(sc, 2, ("sht4x_attach: registering with envsys\n"));
|
||||
|
||||
if (sysmon_envsys_register(sc->sc_sme)) {
|
||||
aprint_error_dev(self,
|
||||
"unable to register with sysmon\n");
|
||||
sysmon_envsys_destroy(sc->sc_sme);
|
||||
sc->sc_sme = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* There is no documented way to ask the chip what version it is. This
|
||||
is likely fine as the only apparent difference is in how precise the
|
||||
measurements will be. The actual conversation with the chip is
|
||||
identical no matter which one you are talking to.
|
||||
*/
|
||||
|
||||
aprint_normal_dev(self, "Sensirion SHT40/SHT41/SHT45, "
|
||||
"Serial number: %02x%02x%02x%02x%s",
|
||||
buf[0], buf[1], buf[3], buf[4],
|
||||
(sncrcpt1 == buf[2] && sncrcpt2 == buf[5]) ? "\n" : " (bad crc)\n");
|
||||
return;
|
||||
out:
|
||||
sysmon_envsys_destroy(sc->sc_sme);
|
||||
sc->sc_sme = NULL;
|
||||
}
|
||||
|
||||
/* If you use the heater on this chip, there is no documented choice but to use
|
||||
the highest precision. If the heater is not in use one may select different
|
||||
precisions or repeatability for the measurement.
|
||||
|
||||
Further, if the heater is used, it will only be active during the measurement.
|
||||
The use of the heater will add delay to the measurement as chip will not
|
||||
return anything until the heater pulse time is over.
|
||||
*/
|
||||
|
||||
static uint8_t
|
||||
sht4x_compute_measure_command(char *resolution, bool heateron,
|
||||
int heatervalue, char *heaterpulse)
|
||||
{
|
||||
int i;
|
||||
uint8_t r;
|
||||
|
||||
if (heateron == false) {
|
||||
for (i = 0; i < __arraycount(sht4x_resolutions); i++) {
|
||||
if (strncmp(resolution, sht4x_resolutions[i].text,
|
||||
SHT4X_RES_NAME) == 0) {
|
||||
r = sht4x_resolutions[i].cmd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == __arraycount(sht4x_resolutions))
|
||||
panic("Heater off could not find command for resolution: %s\n",resolution);
|
||||
} else {
|
||||
for (i = 0; i < __arraycount(sht4x_heateron_commands); i++) {
|
||||
if (heatervalue == sht4x_heateron_commands[i].heatervalue &&
|
||||
strncmp(heaterpulse, sht4x_heateron_commands[i].pulselength,
|
||||
SHT4X_PULSE_NAME) == 0) {
|
||||
r = sht4x_heateron_commands[i].cmd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == __arraycount(sht4x_heateron_commands))
|
||||
panic("Heater on could not find command for heatervalue, heaterpulse: %d %s\n",
|
||||
heatervalue,heaterpulse);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
sht4x_refresh(struct sysmon_envsys * sme, envsys_data_t * edata)
|
||||
{
|
||||
struct sht4x_sc *sc;
|
||||
sc = sme->sme_cookie;
|
||||
int error;
|
||||
uint8_t rawdata[6];
|
||||
uint8_t measurement_command;
|
||||
edata->state = ENVSYS_SINVALID;
|
||||
|
||||
mutex_enter(&sc->sc_mutex);
|
||||
error = iic_acquire_bus(sc->sc_tag, 0);
|
||||
if (error) {
|
||||
DPRINTF(sc, 2, ("%s: Could not acquire i2c bus: %x\n",
|
||||
device_xname(sc->sc_dev), error));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
The documented conversion calculations for the raw values are as follows:
|
||||
|
||||
%RH = (-6 + 125 * rawvalue / 65535)
|
||||
|
||||
T in Celsius = (-45 + 175 * rawvalue / 65535)
|
||||
|
||||
It follows then:
|
||||
|
||||
T in Kelvin = (229.15 + 175 * rawvalue / 65535)
|
||||
|
||||
given the relationship between Celsius and Kelvin.
|
||||
|
||||
What follows reorders the calculation a bit and scales it up to avoid
|
||||
the use of any floating point. All that would really have to happen
|
||||
is a scale up to 10^6 for the sysenv framework, which wants
|
||||
temperature in micro-kelvin and percent relative humidity scaled up
|
||||
10^6, but since this conversion uses 64 bits due to intermediate
|
||||
values that are bigger than 32 bits the conversion first scales up to
|
||||
10^9 and the scales back down by 10^3 at the end. This preserves some
|
||||
precision in the conversion that would otherwise be lost.
|
||||
*/
|
||||
|
||||
measurement_command = sht4x_compute_measure_command(sc->sc_resolution,
|
||||
sc->sc_heateron, sc->sc_heaterval, sc->sc_heaterpulse);
|
||||
DPRINTF(sc, 2, ("%s: Measurement command: %02x\n",
|
||||
device_xname(sc->sc_dev), measurement_command));
|
||||
|
||||
/* This chip is pretty nice in that all commands are the same length and
|
||||
return the same result. What is not so nice is that you can not ask
|
||||
for temperature and humidity independently.
|
||||
|
||||
The result will be 16 bits of raw temperature and a CRC byte followed
|
||||
by 16 bits of humidity followed by a CRC byte.
|
||||
*/
|
||||
|
||||
error = sht4x_cmdr(sc,measurement_command,rawdata,6);
|
||||
|
||||
if (error == 0) {
|
||||
DPRINTF(sc, 2, ("%s: Raw data: %02x%02x %02x - %02x%02x %02x\n",
|
||||
device_xname(sc->sc_dev), rawdata[0], rawdata[1], rawdata[2],
|
||||
rawdata[3], rawdata[4], rawdata[5]));
|
||||
|
||||
|
||||
uint8_t *svalptr;
|
||||
uint64_t svalue;
|
||||
int64_t v1;
|
||||
uint64_t v2;
|
||||
uint64_t d1 = 65535;
|
||||
uint64_t mul1;
|
||||
uint64_t mul2;
|
||||
uint64_t div1 = 10000;
|
||||
uint64_t q;
|
||||
|
||||
switch (edata->sensor) {
|
||||
case SHT4X_TEMP_SENSOR:
|
||||
svalptr = &rawdata[0];
|
||||
v1 = 22915; /* this is scaled up already from 229.15 */
|
||||
v2 = 175;
|
||||
mul1 = 10000000000;
|
||||
mul2 = 100000000;
|
||||
break;
|
||||
case SHT4X_HUMIDITY_SENSOR:
|
||||
svalptr = &rawdata[3];
|
||||
v1 = -6;
|
||||
v2 = 125;
|
||||
mul1 = 10000000000;
|
||||
mul2 = 10000000000;
|
||||
break;
|
||||
default:
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (error == 0) {
|
||||
uint8_t testcrc;
|
||||
|
||||
/* Fake out the CRC check if being asked to ignore CRC */
|
||||
if (sc->sc_ignorecrc) {
|
||||
testcrc = *(svalptr + 2);
|
||||
} else {
|
||||
testcrc = sht4x_crc(svalptr,2);
|
||||
}
|
||||
|
||||
if (*(svalptr + 2) == testcrc) {
|
||||
svalue = *svalptr << 8 | *(svalptr + 1);
|
||||
DPRINTF(sc, 2, ("%s: Raw sensor 16 bit: %#jx\n",
|
||||
device_xname(sc->sc_dev), (uintmax_t)svalue));
|
||||
|
||||
/* Scale up */
|
||||
svalue = svalue * mul1;
|
||||
v1 = v1 * mul2;
|
||||
/* Perform the conversion */
|
||||
q = ((v2 * (svalue / d1)) + v1) / div1;
|
||||
|
||||
DPRINTF(sc, 2, ("%s: Computed sensor: %#jx\n",
|
||||
device_xname(sc->sc_dev), (uintmax_t)q));
|
||||
/* The results will fit in 32 bits, so nothing will be lost */
|
||||
edata->value_cur = (uint32_t) q;
|
||||
edata->state = ENVSYS_SVALID;
|
||||
} else {
|
||||
error = EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
DPRINTF(sc, 2, ("%s: Failed to get new status in refresh %d\n",
|
||||
device_xname(sc->sc_dev), error));
|
||||
}
|
||||
|
||||
iic_release_bus(sc->sc_tag, 0);
|
||||
out:
|
||||
mutex_exit(&sc->sc_mutex);
|
||||
}
|
||||
|
||||
static int
|
||||
sht4x_detach(device_t self, int flags)
|
||||
{
|
||||
struct sht4x_sc *sc;
|
||||
|
||||
sc = device_private(self);
|
||||
|
||||
mutex_enter(&sc->sc_mutex);
|
||||
|
||||
/* Remove the sensors */
|
||||
if (sc->sc_sme != NULL) {
|
||||
sysmon_envsys_unregister(sc->sc_sme);
|
||||
sc->sc_sme = NULL;
|
||||
}
|
||||
mutex_exit(&sc->sc_mutex);
|
||||
|
||||
/* Remove the sysctl tree */
|
||||
sysctl_teardown(&sc->sc_sht4xlog);
|
||||
|
||||
/* Remove the mutex */
|
||||
mutex_destroy(&sc->sc_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE(MODULE_CLASS_DRIVER, sht4xtemp, "i2cexec,sysmon_envsys");
|
||||
|
||||
#ifdef _MODULE
|
||||
#include "ioconf.c"
|
||||
#endif
|
||||
|
||||
static int
|
||||
sht4xtemp_modcmd(modcmd_t cmd, void *opaque)
|
||||
{
|
||||
|
||||
switch (cmd) {
|
||||
case MODULE_CMD_INIT:
|
||||
#ifdef _MODULE
|
||||
return config_init_component(cfdriver_ioconf_sht4xtemp,
|
||||
cfattach_ioconf_sht4xtemp, cfdata_ioconf_sht4xtemp);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
case MODULE_CMD_FINI:
|
||||
#ifdef _MODULE
|
||||
return config_fini_component(cfdriver_ioconf_sht4xtemp,
|
||||
cfattach_ioconf_sht4xtemp, cfdata_ioconf_sht4xtemp);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
default:
|
||||
return ENOTTY;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/* $NetBSD: sht4xreg.h,v 1.1 2021/10/03 17:27:02 brad Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2021 Brad Spencer <brad@anduin.eldar.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _DEV_I2C_SHT4XREG_H_
|
||||
#define _DEV_I2C_SHT4XREG_H_
|
||||
|
||||
#define SHT4X_TYPICAL_ADDR 0x44
|
||||
|
||||
#define SHT4X_READ_SERIAL 0x89
|
||||
#define SHT4X_SOFT_RESET 0x94
|
||||
|
||||
/* If you do not use the heater, you can take measurements at a couple
|
||||
of different percisions */
|
||||
#define SHT4X_MEASURE_HIGH_PRECISION 0xFD
|
||||
#define SHT4X_MEASURE_MEDIUM_PRECISION 0xF6
|
||||
#define SHT4X_MEASURE_LOW_PRECISION 0xE0
|
||||
|
||||
/* The SHT4X chip only support the heater when reading with the
|
||||
highest percision and then only when the measurement is happening.
|
||||
You can have the heater on for 1 second or 1 tenth of a second.
|
||||
After the measurement the heater will switch itself off */
|
||||
#define SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_1_S 0x39
|
||||
#define SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_TENTH_S 0x32
|
||||
#define SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_1_S 0x2F
|
||||
#define SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_TENTH_S 0x24
|
||||
#define SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_1_S 0x1E
|
||||
#define SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_TENTH_S 0x15
|
||||
|
||||
#endif
|
|
@ -0,0 +1,72 @@
|
|||
/* $NetBSD: sht4xvar.h,v 1.1 2021/10/03 17:27:02 brad Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2021 Brad Spencer <brad@anduin.eldar.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _DEV_I2C_SHT4XVAR_H_
|
||||
#define _DEV_I2C_SHT4XVAR_H_
|
||||
|
||||
#define SHT4X_NUM_SENSORS 2
|
||||
#define SHT4X_HUMIDITY_SENSOR 0
|
||||
#define SHT4X_TEMP_SENSOR 1
|
||||
|
||||
#define SHT4X_RES_NAME 7
|
||||
#define SHT4X_PULSE_NAME 6
|
||||
|
||||
struct sht4x_sc {
|
||||
int sc_sht4xdebug;
|
||||
device_t sc_dev;
|
||||
i2c_tag_t sc_tag;
|
||||
i2c_addr_t sc_addr;
|
||||
kmutex_t sc_mutex;
|
||||
int sc_numsensors;
|
||||
struct sysmon_envsys *sc_sme;
|
||||
struct sysctllog *sc_sht4xlog;
|
||||
envsys_data_t sc_sensors[SHT4X_NUM_SENSORS];
|
||||
bool sc_ignorecrc;
|
||||
char sc_resolution[SHT4X_RES_NAME];
|
||||
int sc_readattempts;
|
||||
bool sc_heateron;
|
||||
int sc_heaterval;
|
||||
char sc_heaterpulse[SHT4X_PULSE_NAME];
|
||||
};
|
||||
|
||||
struct sht4x_sensor {
|
||||
const char *desc;
|
||||
enum envsys_units type;
|
||||
};
|
||||
|
||||
struct sht4x_timing {
|
||||
uint8_t cmd;
|
||||
int typicaldelay;
|
||||
};
|
||||
|
||||
struct sht4x_resolution {
|
||||
const char *text;
|
||||
uint8_t cmd;
|
||||
};
|
||||
|
||||
struct sht4x_heaterpulse {
|
||||
const char *length;
|
||||
};
|
||||
|
||||
struct sht4x_heateron_command {
|
||||
int heatervalue;
|
||||
const char *pulselength;
|
||||
uint8_t cmd;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: Makefile,v 1.256 2021/09/25 17:55:37 maya Exp $
|
||||
# $NetBSD: Makefile,v 1.257 2021/10/03 17:27:02 brad Exp $
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
||||
|
@ -69,6 +69,7 @@ SUBDIR+= hfs
|
|||
SUBDIR+= hythygtemp
|
||||
SUBDIR+= si70xxtemp
|
||||
SUBDIR+= am2315temp
|
||||
SUBDIR+= sht4xtemp
|
||||
SUBDIR+= i2cexec
|
||||
SUBDIR+= i2c_bitbang
|
||||
SUBDIR+= if_agr
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
.include "../Makefile.inc"
|
||||
|
||||
.PATH: ${S}/dev/i2c
|
||||
|
||||
KMOD= sht4xtemp
|
||||
IOCONF= sht4xtemp.ioconf
|
||||
SRCS= sht4x.c
|
||||
|
||||
WARNS= 3
|
||||
|
||||
.include <bsd.kmodule.mk>
|
|
@ -0,0 +1,7 @@
|
|||
ioconf sht4xtemp
|
||||
|
||||
include "conf/files"
|
||||
|
||||
pseudo-root iic*
|
||||
|
||||
sht4xtemp* at iic? addr 0x44
|
Loading…
Reference in New Issue