7e8424903a
This adds a sensor driver for the hyt271, hyt221, and hyt939 sensor module from vendor iST (Innovative Sensor Technology). This sensor type is connected via i2c bus and allows the measurement of relative humidity and temperature. Each driver instance supports two different character device for each type (/dev/sensor/tempX and /dev/sensor/humiX). This driver also supports changing the i2c address of one connected sensor on the bus. This requires a callback to the platform-specific board logic which must ensure a power-on reset. Optional the driver allows setting up a read interval for fetching sensor data. This is done by a worker thread and can be useful when watching several sensors via poll() by the userspace application. Signed-off-by: Marco Krahl <ocram.lhark@gmail.com>
977 lines
26 KiB
C
977 lines
26 KiB
C
/****************************************************************************
|
|
* drivers/sensors/hyt271.c
|
|
* Character driver for HYT271 Digital Humidity and Temperature Module.
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/kthread.h>
|
|
#include <nuttx/signal.h>
|
|
#include <nuttx/semaphore.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/i2c/i2c_master.h>
|
|
#include <nuttx/sensors/hyt271.h>
|
|
#include <nuttx/sensors/sensor.h>
|
|
|
|
#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_HYT271)
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define HYT271_TEMPDATA_MASK 0x3fff
|
|
#define HYT271_HUMIDATA_MASK 0x3fff
|
|
|
|
#define HYT271_TEMPDATA_SHIFT(x) ((x) >> 2)
|
|
#define HYT271_HUMIDATA_SHIFT(x) ((x) >> 16)
|
|
|
|
#define HYT271_TEMPRAWDATA(x) (HYT271_TEMPDATA_SHIFT(x) & HYT271_TEMPDATA_MASK)
|
|
#define HYT271_TEMPRAWEQUAL(x, y) \
|
|
(HYT271_TEMPRAWDATA(x) == HYT271_TEMPRAWDATA(y))
|
|
|
|
#define HYT271_HUMIRAWDATA(x) (HYT271_HUMIDATA_SHIFT(x) & HYT271_HUMIDATA_MASK)
|
|
#define HYT271_HUMIRAWEQUAL(x, y) \
|
|
(HYT271_HUMIRAWDATA(x) == HYT271_HUMIRAWDATA(y))
|
|
|
|
#define HYT271_TEMPDATA(x) (HYT271_TEMPRAWDATA(x) * 165.0 / 16383.0 - 40.0)
|
|
#define HYT271_HUMIDATA(x) (HYT271_HUMIRAWDATA(x) * 100.0 / 16383.0)
|
|
|
|
#define HYT271_SENSOR_HUMI 0
|
|
#define HYT271_SENSOR_TEMP 1
|
|
#define HYT271_SENSOR_MAX 2
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct hyt271_sensor_data_s
|
|
{
|
|
uint32_t data;
|
|
uint64_t timestamp;
|
|
};
|
|
|
|
struct hyt271_dev_s;
|
|
struct hyt271_sensor_s
|
|
{
|
|
struct sensor_lowerhalf_s lower; /* Common lower interface */
|
|
unsigned int buffer_size; /* Data size for reading */
|
|
#ifdef CONFIG_SENSORS_HYT271_POLL
|
|
bool enabled; /* Sensor activated */
|
|
#endif
|
|
FAR struct hyt271_dev_s *dev; /* Driver instance */
|
|
};
|
|
|
|
struct hyt271_dev_s
|
|
{
|
|
struct hyt271_sensor_s sensor[HYT271_SENSOR_MAX]; /* Sensor types */
|
|
FAR struct i2c_master_s *i2c; /* I2C interface */
|
|
FAR struct hyt271_bus_s *bus; /* Bus power interface */
|
|
sem_t lock_measure_cylce; /* Locks measure cycle */
|
|
uint32_t freq; /* I2C Frequency */
|
|
#ifdef CONFIG_SENSORS_HYT271_POLL
|
|
unsigned int interval; /* Polling interval */
|
|
sem_t run; /* Locks sensor thread */
|
|
bool initial_read; /* Already read */
|
|
#endif
|
|
uint8_t addr; /* I2C address */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* Sensor functions */
|
|
|
|
static int hyt271_active(FAR struct sensor_lowerhalf_s *lower,
|
|
unsigned char enabled);
|
|
|
|
static int hyt271_fetch(FAR struct sensor_lowerhalf_s *lower,
|
|
FAR char *buffer, size_t buflen);
|
|
|
|
static int hyt271_control(FAR struct sensor_lowerhalf_s *lower,
|
|
int cmd, unsigned long arg);
|
|
|
|
#ifdef CONFIG_SENSORS_HYT271_POLL
|
|
static int hyt271_set_interval(FAR struct sensor_lowerhalf_s *lower,
|
|
FAR unsigned int *period_us);
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct sensor_ops_s g_hyt271_ops =
|
|
{
|
|
#ifdef CONFIG_SENSORS_HYT271_POLL
|
|
.set_interval = hyt271_set_interval,
|
|
#endif
|
|
.activate = hyt271_active,
|
|
.fetch = hyt271_fetch,
|
|
.control = hyt271_control
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_humi_from_rawdata
|
|
*
|
|
* Description: Helper for converting raw data to temperature.
|
|
*
|
|
* Parameter:
|
|
* data - Pointer to internal datat structure for measured data
|
|
* temp - Pointer to sensor data structure for humidity
|
|
****************************************************************************/
|
|
|
|
static void hyt271_humi_from_rawdata(FAR struct hyt271_sensor_data_s *data,
|
|
FAR struct sensor_event_humi *humi)
|
|
{
|
|
humi->timestamp = data->timestamp;
|
|
humi->humidity = HYT271_HUMIDATA(data->data);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_temp_from_rawdata
|
|
*
|
|
* Description: Helper for converting raw data to temperature.
|
|
*
|
|
* Parameter:
|
|
* data - Pointer to internal datat structure for measured data
|
|
* temp - Pointer to sensor data structure for temperature
|
|
****************************************************************************/
|
|
|
|
static void hyt271_temp_from_rawdata(FAR struct hyt271_sensor_data_s *data,
|
|
FAR struct sensor_event_temp *temp)
|
|
{
|
|
temp->timestamp = data->timestamp;
|
|
temp->temperature = HYT271_TEMPDATA(data->data);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_curtime
|
|
*
|
|
* Description: Helper to get current timestamp.
|
|
*
|
|
* Return:
|
|
* Timestamp in nsec
|
|
****************************************************************************/
|
|
|
|
static unsigned long hyt271_curtime(void)
|
|
{
|
|
struct timespec ts;
|
|
#ifdef CONFIG_CLOCK_MONOTONIC
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
#else
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
#endif
|
|
|
|
return 1000000ull * ts.tv_sec + ts.tv_nsec / 1000;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_init_rw_buffer
|
|
*
|
|
* Description: Helper for buffer initializing.
|
|
*
|
|
* Parameter:
|
|
* buffer - Pointer to the buffers memory region
|
|
* cmd - The command to set in the buffer
|
|
* size - The buffer size
|
|
****************************************************************************/
|
|
|
|
static void hyt271_init_rw_buffer(FAR uint8_t *buffer, uint8_t cmd,
|
|
uint8_t size)
|
|
{
|
|
int n;
|
|
|
|
buffer[0] = cmd;
|
|
|
|
for (n = 1; n < size; n++)
|
|
{
|
|
buffer[n] = 0x00;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_df
|
|
*
|
|
* Description: Helper for reading data by a data fetch.
|
|
*
|
|
* Parameter:
|
|
* dev - Pointer to private driver instance
|
|
* config - I2C configuration
|
|
* buffer - Pointer to the buffers memory region
|
|
* size - The buffer size
|
|
*
|
|
* Return:
|
|
* OK - on success
|
|
****************************************************************************/
|
|
|
|
static int hyt271_df(FAR struct hyt271_dev_s *dev,
|
|
FAR struct i2c_config_s *config, FAR uint8_t *buffer,
|
|
uint8_t size)
|
|
{
|
|
int ret;
|
|
|
|
/* Read data from the device */
|
|
|
|
ret = i2c_read(dev->i2c, config, buffer, size);
|
|
if (ret < 0)
|
|
{
|
|
snerr ("i2c_read failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_mr
|
|
*
|
|
* Description: Helper for sending a measurement cylce request.
|
|
*
|
|
* Parameter:
|
|
* dev - Pointer to private driver instance
|
|
* config - I2C configuration
|
|
*
|
|
* Return:
|
|
* OK - on success
|
|
****************************************************************************/
|
|
|
|
static int hyt271_mr(FAR struct hyt271_dev_s *dev,
|
|
FAR struct i2c_config_s *config)
|
|
{
|
|
uint8_t buffer;
|
|
int ret;
|
|
|
|
/* We only must send the i2c address with write bit enabled here. This
|
|
* isn't provided by the i2c api, so instead sending a null byte seems
|
|
* working fine.
|
|
*/
|
|
|
|
buffer = 0x00;
|
|
|
|
/* Send address only with write bit enabled */
|
|
|
|
ret = i2c_write(dev->i2c, config, &buffer, 1);
|
|
if (ret < 0)
|
|
{
|
|
snerr ("i2c_write failed: %d\n", ret);
|
|
}
|
|
|
|
/* Wait until measure cycle is done. This takes between 60 - 100 ms. */
|
|
|
|
nxsig_usleep(100000);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_cmd
|
|
*
|
|
* Description: Helper for sending a command.
|
|
*
|
|
* Parameter:
|
|
* dev - Pointer to private driver instance
|
|
* config - I2C configuration
|
|
* buffer - Pointer to the buffers memory region
|
|
* size - The buffer size
|
|
*
|
|
* Return:
|
|
* OK - on success
|
|
****************************************************************************/
|
|
|
|
static int hyt271_cmd(FAR struct hyt271_dev_s *dev,
|
|
FAR struct i2c_config_s *config, FAR uint8_t *buffer,
|
|
uint8_t size)
|
|
{
|
|
int ret;
|
|
|
|
/* Write command data to the device */
|
|
|
|
ret = i2c_write(dev->i2c, config, buffer, size);
|
|
if (ret < 0)
|
|
{
|
|
snerr ("i2c_write failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_cmd_response
|
|
*
|
|
* Description: Helper for sending a command fetching the response after a
|
|
* neccessary timeout.
|
|
* Parameter:
|
|
* dev - Pointer to private driver instance
|
|
* config - I2C configuration
|
|
* buffer - Pointer to the buffers memory region
|
|
* wsize - Bytes to write from the buffer
|
|
* rsize - Bytes to read into the buffer
|
|
* usec - Timeout between write and read operation in usec
|
|
*
|
|
* Return:
|
|
* OK - on success
|
|
****************************************************************************/
|
|
|
|
static int hyt271_cmd_response(FAR struct hyt271_dev_s *dev,
|
|
FAR struct i2c_config_s *config,
|
|
FAR uint8_t *buffer, uint8_t wsize,
|
|
uint8_t rsize, unsigned int usec)
|
|
{
|
|
int ret;
|
|
|
|
/* Send command */
|
|
|
|
ret = hyt271_cmd(dev, config, buffer, wsize);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Sleep for usec µs until response is ready */
|
|
|
|
nxsig_usleep(usec);
|
|
|
|
/* Read response and return */
|
|
|
|
hyt271_init_rw_buffer(buffer, 0x00, rsize);
|
|
ret = hyt271_df(dev, config, buffer, rsize);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Check if ack response is valid */
|
|
|
|
if (buffer[0] != 0x81)
|
|
{
|
|
snerr ("wrong ack response: 0x%x\n", buffer[0]);
|
|
ret = -EIO;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_change_addr
|
|
*
|
|
* Description: Change I2C address of the chip.
|
|
*
|
|
* Parameter:
|
|
* dev - Pointer to private driver instance
|
|
* addr - The new I2C address
|
|
*
|
|
* Return:
|
|
* OK - on success
|
|
****************************************************************************/
|
|
|
|
static int hyt271_change_addr(FAR struct hyt271_dev_s *dev, uint8_t addr)
|
|
{
|
|
int ret;
|
|
irqstate_t irq_flags;
|
|
struct i2c_config_s config;
|
|
uint8_t buffer[3];
|
|
|
|
if (dev->bus == NULL)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
if (addr & 0x80)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Reset and subsequently operation must be mutual exclusive
|
|
* Prevents from beeing used by another open driver instance during this
|
|
* operations.
|
|
*/
|
|
|
|
ret = nxsem_wait(&dev->lock_measure_cylce);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Set up the I2C configuration */
|
|
|
|
config.frequency = dev->freq;
|
|
config.address = dev->addr;
|
|
config.addrlen = 7;
|
|
|
|
/* Enter in command mode */
|
|
|
|
hyt271_init_rw_buffer(buffer, HYT271_CMD_START_CM, 3);
|
|
|
|
/* The following section must be done within a time-constrained window.
|
|
* It should be enough to lock this cpu than in the case of SMP all the
|
|
* others.
|
|
*/
|
|
|
|
irq_flags = up_irq_save();
|
|
|
|
/* Power on reset the bus.
|
|
* Entering in command mode must be done within the next 10 ms after power
|
|
* up.
|
|
*/
|
|
|
|
ret = dev->bus->pwonreset(dev->bus);
|
|
if (ret < 0)
|
|
{
|
|
ret = -EIO;
|
|
goto err_unlock;
|
|
}
|
|
|
|
ret = hyt271_cmd_response(dev, &config, buffer, 3, 1, 100);
|
|
|
|
/* The chip should be in command mode now */
|
|
|
|
up_irq_restore(irq_flags);
|
|
|
|
if (ret < 0)
|
|
{
|
|
goto err_unlock;
|
|
}
|
|
|
|
/* Fetch current I2C address */
|
|
|
|
hyt271_init_rw_buffer(buffer, HYT271_CMD_EEPROM_READ, 3);
|
|
ret = hyt271_cmd_response(dev, &config, buffer, 3, 3, 100);
|
|
if (ret < 0)
|
|
{
|
|
goto err_unlock;
|
|
}
|
|
|
|
if ((buffer[2] & 0x7f) != dev->addr)
|
|
{
|
|
snerr("wrong current I2C address responsed: 0x%x\n", buffer[2] & 0x7f);
|
|
ret = -EIO;
|
|
goto err_unlock;
|
|
}
|
|
|
|
/* Write new I2C address */
|
|
|
|
hyt271_init_rw_buffer(buffer, HYT271_CMD_EEPROM_WRITE, 3);
|
|
buffer[2] = addr;
|
|
ret = hyt271_cmd_response(dev, &config, buffer, 3, 3, 12000);
|
|
if (ret < 0)
|
|
{
|
|
goto err_unlock;
|
|
}
|
|
|
|
/* Fetch new I2C address again for confirmation */
|
|
|
|
hyt271_init_rw_buffer(buffer, HYT271_CMD_EEPROM_READ, 3);
|
|
ret = hyt271_cmd_response(dev, &config, buffer, 3, 3, 100);
|
|
if (ret < 0)
|
|
{
|
|
goto err_unlock;
|
|
}
|
|
|
|
if ((buffer[2] & 0x7f) != addr)
|
|
{
|
|
snerr("wrong changed I2C address responsed: 0x%x\n", buffer[2] & 0x7f);
|
|
ret = -EIO;
|
|
goto err_unlock;
|
|
}
|
|
|
|
/* Finally go back to normal mode */
|
|
|
|
hyt271_init_rw_buffer(buffer, HYT271_CMD_START_NOM, 3);
|
|
ret = hyt271_cmd(dev, &config, buffer, 3);
|
|
if (ret < 0)
|
|
{
|
|
goto err_unlock;
|
|
}
|
|
|
|
/* Set new address */
|
|
|
|
dev->addr = addr;
|
|
|
|
nxsem_post(&dev->lock_measure_cylce);
|
|
return OK;
|
|
|
|
err_unlock:
|
|
nxsem_post(&dev->lock_measure_cylce);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_measure_read
|
|
*
|
|
* Description:
|
|
* Performs a measuremnt cylce and reads measured data.
|
|
*
|
|
* Parameter:
|
|
* dev - Pointer to private driver instance
|
|
* data - Pointer to internal data structure for measured data
|
|
|
|
* Return:
|
|
* OK - on success
|
|
****************************************************************************/
|
|
|
|
static int hyt271_measure_read(FAR struct hyt271_dev_s *dev,
|
|
FAR struct hyt271_sensor_data_s *data)
|
|
{
|
|
int ret;
|
|
struct i2c_config_s config;
|
|
uint8_t buffer[4];
|
|
|
|
/* Measure request and read operation must be mutual exclusive
|
|
* Prevents from beeing used by another open driver instance during this
|
|
* read operation.
|
|
*/
|
|
|
|
ret = nxsem_wait(&dev->lock_measure_cylce);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Set up the I2C configuration */
|
|
|
|
config.frequency = dev->freq;
|
|
config.address = dev->addr;
|
|
config.addrlen = 7;
|
|
|
|
/* Start a measurement request */
|
|
|
|
ret = hyt271_mr(dev, &config);
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: Error starting measure cycle\n");
|
|
goto err_unlock;
|
|
}
|
|
|
|
/* Read humity and temperature from the sensor */
|
|
|
|
ret = hyt271_df(dev, &config, buffer, 4);
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: Error reading sensor data!\n");
|
|
goto err_unlock;
|
|
}
|
|
|
|
sninfo("value: %02x %02x %02x %02x ret: %d\n",
|
|
buffer[0], buffer[1], buffer[2], buffer[3], ret);
|
|
|
|
data->data = buffer[0] << 24 | buffer[1] << 16 | \
|
|
buffer[2] << 8 | buffer[3];
|
|
data->timestamp = hyt271_curtime();
|
|
|
|
nxsem_post(&dev->lock_measure_cylce);
|
|
|
|
return OK;
|
|
|
|
err_unlock:
|
|
nxsem_post(&dev->lock_measure_cylce);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_fetch
|
|
*
|
|
* Description: Performs a measuremnt cylce and data read with data
|
|
* conversion.
|
|
*
|
|
* Parameter:
|
|
* lower - Pointer to lower half sensor driver instance
|
|
* buffer - Pointer to the buffer for reading data
|
|
* buflen - Size of the buffer
|
|
*
|
|
* Return:
|
|
* OK - on success
|
|
****************************************************************************/
|
|
|
|
static int hyt271_fetch(FAR struct sensor_lowerhalf_s *lower,
|
|
FAR char *buffer, size_t buflen)
|
|
{
|
|
int ret;
|
|
struct hyt271_sensor_data_s data;
|
|
FAR struct hyt271_sensor_s *priv = (FAR struct hyt271_sensor_s *)lower;
|
|
|
|
/* Check if the user is reading the right size */
|
|
|
|
if (buflen != priv->buffer_size)
|
|
{
|
|
snerr("ERROR: You need to read %d bytes from this sensor!\n",
|
|
priv->buffer_size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = hyt271_measure_read(priv->dev, &data);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
switch (lower->type)
|
|
{
|
|
case SENSOR_TYPE_AMBIENT_TEMPERATURE:
|
|
{
|
|
struct sensor_event_temp temp;
|
|
hyt271_temp_from_rawdata(&data, &temp);
|
|
memcpy(buffer, &temp, sizeof(temp));
|
|
}
|
|
break;
|
|
case SENSOR_TYPE_RELATIVE_HUMIDITY:
|
|
{
|
|
struct sensor_event_humi humi;
|
|
hyt271_humi_from_rawdata(&data, &humi);
|
|
memcpy(buffer, &humi, sizeof(humi));
|
|
}
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return buflen;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_control
|
|
*
|
|
* Description: Interface function of struct sensor_ops_s.
|
|
*
|
|
* Return:
|
|
* OK - on success
|
|
****************************************************************************/
|
|
|
|
static int hyt271_control(FAR struct sensor_lowerhalf_s *lower,
|
|
int cmd, unsigned long arg)
|
|
{
|
|
int ret;
|
|
struct hyt271_sensor_s *priv = (FAR struct hyt271_sensor_s *)lower;
|
|
|
|
switch (cmd)
|
|
{
|
|
/* Read i2c address */
|
|
|
|
case SNIOC_READADDR:
|
|
{
|
|
FAR uint8_t *ptr = (FAR uint8_t *)((uintptr_t)arg);
|
|
DEBUGASSERT(ptr != NULL);
|
|
*ptr = priv->dev->addr;
|
|
sninfo("readaddr: %02x\n", *ptr);
|
|
ret = OK;
|
|
}
|
|
break;
|
|
|
|
/* Change i2c address */
|
|
|
|
case SNIOC_CHANGEADDR:
|
|
{
|
|
uint8_t addr = arg;
|
|
ret = hyt271_change_addr(priv->dev, addr);
|
|
sninfo("changeaddr: %02x\n", addr);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
snerr("ERROR: Unrecognized cmd: %d\n", cmd);
|
|
ret = -ENOTTY;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_active
|
|
*
|
|
* Description: Interface function of struct sensor_ops_s.
|
|
*
|
|
* Return:
|
|
* OK - on success
|
|
****************************************************************************/
|
|
|
|
static int hyt271_active(FAR struct sensor_lowerhalf_s *lower,
|
|
unsigned char enabled)
|
|
{
|
|
#ifdef CONFIG_SENSORS_HYT271_POLL
|
|
bool start_thread = false;
|
|
struct hyt271_sensor_s *priv = (FAR struct hyt271_sensor_s *)lower;
|
|
|
|
if (enabled)
|
|
{
|
|
if (!priv->dev->sensor[HYT271_SENSOR_TEMP].enabled &&
|
|
!priv->dev->sensor[HYT271_SENSOR_HUMI].enabled)
|
|
{
|
|
start_thread = true;
|
|
}
|
|
}
|
|
|
|
priv->enabled = enabled;
|
|
|
|
if (start_thread == true)
|
|
{
|
|
/* Wake up the thread */
|
|
|
|
nxsem_post(&priv->dev->run);
|
|
}
|
|
#endif
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_set_interval
|
|
*
|
|
* Description: Interface function of struct sensor_ops_s.
|
|
*
|
|
* Return:
|
|
* OK - on success
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_SENSORS_HYT271_POLL
|
|
static int hyt271_set_interval(FAR struct sensor_lowerhalf_s *lower,
|
|
FAR unsigned int *period_us)
|
|
{
|
|
FAR struct hyt271_sensor_s *priv = (FAR struct hyt271_sensor_s *)lower;
|
|
priv->dev->interval = *period_us;
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_thread
|
|
*
|
|
* Description: Thread for performing interval measurement cycle and data
|
|
* read.
|
|
*
|
|
* Parameter:
|
|
* argc - Number opf arguments
|
|
* argv - Pointer to argument list
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_SENSORS_HYT271_POLL
|
|
static int hyt271_thread(int argc, char** argv)
|
|
{
|
|
FAR struct hyt271_dev_s *priv = (FAR struct hyt271_dev_s *)
|
|
((uintptr_t)strtoul(argv[1], NULL, 0));
|
|
|
|
while (true)
|
|
{
|
|
int ret;
|
|
uint32_t orawdata;
|
|
struct hyt271_sensor_data_s data;
|
|
struct hyt271_sensor_s *hsensor = &priv->sensor[HYT271_SENSOR_HUMI];
|
|
struct hyt271_sensor_s *tsensor = &priv->sensor[HYT271_SENSOR_TEMP];
|
|
|
|
if (!hsensor->enabled && !tsensor->enabled)
|
|
{
|
|
/* Reset inital read identifier */
|
|
|
|
priv->initial_read = false;
|
|
|
|
/* Waiting to be woken up */
|
|
|
|
ret = nxsem_wait(&priv->run);
|
|
if (ret < 0)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Store the last sensor data for later comparison */
|
|
|
|
orawdata = data.data;
|
|
|
|
ret = hyt271_measure_read(priv, &data);
|
|
|
|
if (!ret)
|
|
{
|
|
/* Notify upper */
|
|
|
|
if (priv->initial_read == false || (hsensor->enabled == true &&
|
|
!HYT271_HUMIRAWEQUAL(orawdata, data.data)))
|
|
{
|
|
struct sensor_event_humi humi;
|
|
hyt271_humi_from_rawdata(&data, &humi);
|
|
hsensor->lower.push_event(hsensor->lower.priv, &humi,
|
|
sizeof(struct sensor_event_humi));
|
|
}
|
|
|
|
if (priv->initial_read == false || (tsensor->enabled == true &&
|
|
!HYT271_TEMPRAWEQUAL(orawdata, data.data)))
|
|
{
|
|
struct sensor_event_temp temp;
|
|
hyt271_temp_from_rawdata(&data, &temp);
|
|
tsensor->lower.push_event(tsensor->lower.priv, &temp,
|
|
sizeof(struct sensor_event_temp));
|
|
}
|
|
|
|
if (priv->initial_read == false)
|
|
{
|
|
priv->initial_read = true;
|
|
}
|
|
}
|
|
|
|
/* Sleeping thread before fetching the next sensor data */
|
|
|
|
nxsig_usleep(priv->interval);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: hyt271_register
|
|
*
|
|
* Description:
|
|
* Register the HYT271 character device.
|
|
*
|
|
* Input Parameters:
|
|
* devno - The user specifies device number, from 0.
|
|
* i2c - An instance of the I2C interface to communicate with HYT271
|
|
* sensor.
|
|
*
|
|
* addr - The I2C address of the HYT271.
|
|
* bus - Callback to board specific logic for i2c bus power.
|
|
* Will be used for changing i2c address of the sensor and can be
|
|
* set to NULL when not supported.
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) on success; a negated errno value on failure.
|
|
****************************************************************************/
|
|
|
|
int hyt271_register(int devno, FAR struct i2c_master_s *i2c, uint8_t addr,
|
|
FAR struct hyt271_bus_s *bus)
|
|
{
|
|
int ret;
|
|
struct hyt271_sensor_s *tmp;
|
|
#ifdef CONFIG_SENSORS_HYT271_POLL
|
|
FAR char *argv[2];
|
|
char arg1[32];
|
|
#endif
|
|
|
|
/* Sanity check */
|
|
|
|
if (!i2c)
|
|
{
|
|
snerr("Invalid i2c instance\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Initialize the HYT271 device structure */
|
|
|
|
FAR struct hyt271_dev_s *priv = (FAR struct hyt271_dev_s *)
|
|
kmm_malloc(sizeof(struct hyt271_dev_s));
|
|
|
|
if (priv == NULL)
|
|
{
|
|
snerr("ERROR: Failed to allocate instance\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
priv->i2c = i2c;
|
|
priv->freq = 100000;
|
|
priv->addr = addr;
|
|
priv->bus = bus;
|
|
#ifdef CONFIG_SENSORS_HYT271_POLL
|
|
priv->interval = CONFIG_SENSORS_HYT271_POLL_INTERVAL;
|
|
priv->initial_read = false;
|
|
#endif
|
|
|
|
nxsem_init(&priv->lock_measure_cylce, 0, 1);
|
|
#ifdef CONFIG_SENSORS_HYT271_POLL
|
|
nxsem_init(&priv->run, 0, 0);
|
|
nxsem_set_protocol(&priv->run, SEM_PRIO_NONE);
|
|
#endif
|
|
|
|
/* Humidity register */
|
|
|
|
tmp = &priv->sensor[HYT271_SENSOR_HUMI];
|
|
tmp->dev = priv;
|
|
#ifdef CONFIG_SENSORS_HYT271_POLL
|
|
tmp->enabled = false;
|
|
#endif
|
|
tmp->buffer_size = sizeof(struct sensor_event_humi);
|
|
tmp->lower.ops = &g_hyt271_ops;
|
|
tmp->lower.type = SENSOR_TYPE_RELATIVE_HUMIDITY;
|
|
tmp->lower.uncalibrated = false;
|
|
tmp->lower.buffer_number = 1;
|
|
ret = sensor_register(&tmp->lower, devno);
|
|
if (ret < 0)
|
|
{
|
|
goto humi_err;
|
|
}
|
|
|
|
/* Temperature register */
|
|
|
|
tmp = &priv->sensor[HYT271_SENSOR_TEMP];
|
|
tmp->dev = priv;
|
|
#ifdef CONFIG_SENSORS_HYT271_POLL
|
|
tmp->enabled = false;
|
|
#endif
|
|
tmp->buffer_size = sizeof(struct sensor_event_temp);
|
|
tmp->lower.ops = &g_hyt271_ops;
|
|
tmp->lower.type = SENSOR_TYPE_AMBIENT_TEMPERATURE;
|
|
tmp->lower.uncalibrated = false;
|
|
tmp->lower.buffer_number = 1;
|
|
ret = sensor_register(&tmp->lower, devno);
|
|
if (ret < 0)
|
|
{
|
|
goto temp_err;
|
|
}
|
|
|
|
#ifdef CONFIG_SENSORS_HYT271_POLL
|
|
/* Create thread for sensor */
|
|
|
|
snprintf(arg1, 16, "0x%" PRIxPTR, (uintptr_t)priv);
|
|
argv[0] = arg1;
|
|
argv[1] = NULL;
|
|
ret = kthread_create("hyt271_thread", SCHED_PRIORITY_DEFAULT,
|
|
CONFIG_SENSORS_HYT271_THREAD_STACKSIZE,
|
|
hyt271_thread, argv);
|
|
if (ret > 0)
|
|
#endif
|
|
{
|
|
return OK;
|
|
}
|
|
|
|
humi_err:
|
|
sensor_unregister(&priv->sensor[HYT271_SENSOR_HUMI].lower, devno);
|
|
temp_err:
|
|
sensor_unregister(&priv->sensor[HYT271_SENSOR_TEMP].lower, devno);
|
|
|
|
nxsem_destroy(&priv->lock_measure_cylce);
|
|
kmm_free(priv);
|
|
return ret;
|
|
}
|
|
#endif /* CONFIG_I2C && CONFIG_SENSORS_HYT271 */
|