diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index d3d4dbfb586..a6ee116d8fe 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -171,6 +171,35 @@ config SENSORS_DHTXX ---help--- Enable driver support for the DHTxx humidity/temperature sensor. +config SENSORS_DS18B20 + bool "Maxim Integrated DS18B20 Temperature Sensor support" + default n + select 1WIRE + ---help--- + Enable driver support for the DS18B20 temperature sensor. + +config SENSORS_DS18B20_POLL + bool "Enables polling sensor data" + depends on SENSORS_DS18B20 + default n + ---help--- + Enables polling of sensor. + +config SENSORS_DS18B20_POLL_INTERVAL + int "Polling interval in microseconds, default 1 sec" + depends on SENSORS_DS18B20 && SENSORS_DS18B20_POLL + default 1000000 + range 0 4294967295 + ---help--- + The interval until a new sensor measurement will be triggered. + +config SENSORS_DS18B20_THREAD_STACKSIZE + int "Worker thread stack size" + depends on SENSORS_DS18B20 && SENSORS_DS18B20_POLL + default 1024 + ---help--- + The stack size for the worker thread. + config SENSORS_FXOS8700CQ bool "NXP FXOS8700CQ Motion Sensor support" default n diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index d1fd73da203..6c52e22cb3c 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -260,6 +260,16 @@ endif endif # CONFIG_SPI +# These drivers depend on 1WIRE support + +ifeq ($(CONFIG_1WIRE),y) + +ifeq ($(CONFIG_SENSORS_DS18B20),y) + CSRCS += ds18b20.c +endif + +endif # CONFIG_1WIRE + ifeq ($(CONFIG_SENSORS_MPU60X0),y) CSRCS += mpu60x0.c endif diff --git a/drivers/sensors/ds18b20.c b/drivers/sensors/ds18b20.c new file mode 100644 index 00000000000..fdb521d7677 --- /dev/null +++ b/drivers/sensors/ds18b20.c @@ -0,0 +1,987 @@ +/**************************************************************************** + * drivers/sensors/ds18b20.c + * Character driver for DS18B20 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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_1WIRE) && defined(CONFIG_SENSORS_DS18B20) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_ENDIAN_BIG +# define ds18b20_leuint64(x) (x) +#endif + +/* Scratchpad data definition for reading */ + +#define DS18B20_SPAD_LSB_OFFSET 0 +#define DS18B20_SPAD_MSB_OFFSET 1 +#define DS18B20_SPAD_TH_OFFSET 2 +#define DS18B20_SPAD_TL_OFFSET 3 +#define DS18B20_SPAD_RES_OFFSET 4 +#define DS18B20_SPAD_CRC_OFFSET 9 +#define DS18B20_SPAD_SIZE 9 + +/* Scratchpad data definition for writing */ + +#define DS18B20_WSPAD_CMD_OFFSET 0 +#define DS18B20_WSPAD_TH_OFFSET 0 +#define DS18B20_WSPAD_TL_OFFSET 1 +#define DS18B20_WSPAD_RES_OFFSET 2 +#define DS18B20_WSPAD_SIZE 3 + +/* Measurement resolution bit definition */ + +#define DS18B20_RESMIN 0 +#define DS18B20_RESMAX 3 +#define DS18B20_NRES DS18B20_RESMAX - DS18B20_RESMIN + 1 + +#define DS18B20_RES_VAL(x) (((x) >> 5) & 0x3) +#define DS18B20_RES_CONV(x) (((x) & 0x3) << 5) + +/* Measurement timneout offset */ + +#define DS18B20_TIMEOUT_OFFSET(x) (DS18B20_RESMAX - (x)) + +/* Default alarm temperature */ + +#define DS18B20_TALARM_MIN -55 +#define DS18B20_TALARM_MAX 125 + +/* Helper for getting data from scratchpad memory */ + +#define DS18B20_TEMP_PREDEC(lsb, msb) (int8_t)(((msb) << 4) | ((lsb) >> 4)) +#define DS18B20_TEMP_RES(lsb, res) (DS18B20_RESMAX - DS18B20_RES_VAL(res)) +#define DS18B20_TEMP_RESMASK(lsb, res) (0x0f << DS18B20_TEMP_RES(lsb, res)) +#define DS18B20_TEMP_DEC(lsb, res) (((lsb) & 0x0f) & \ + DS18B20_TEMP_RESMASK(lsb, res)) +#define DS18B20_TEMP(lsb, msb, res) (DS18B20_TEMP_PREDEC(lsb, msb) + \ + DS18B20_TEMP_DEC(lsb, res) / 16.0) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Internal sensor data */ + +struct ds18b20_sensor_data_s +{ + uint64_t timestamp; + uint8_t spad[DS18B20_SPAD_SIZE]; +}; + +/* User configuration register */ + +struct ds18b20_config_s +{ + uint8_t res; /* Sensor resolution */ + struct ds18b20_alarm_s alarm; /* Sensor temperature alarm */ +}; + +/* Callback handling for alarm detection */ + +struct ds18b20_cb_alarm_s +{ + bool isalarm; + uint64_t romcode; +}; + +/* Lower sensor */ + +struct ds18b20_sensor_s +{ + struct sensor_lowerhalf_s lower; /* Common lower interface */ +#ifdef CONFIG_SENSORS_DS18B20_POLL + bool enabled; /* Sensor activated */ +#endif +}; + +/* Internal device */ + +struct ds18b20_dev_s +{ + struct ds18b20_sensor_s sensor; /* Sensor */ + FAR struct onewire_master_s *master; /* 1wire master interface */ + struct onewire_config_s config; /* 1wire device configuration */ + struct ds18b20_config_s reg; /* Sensor resolution */ +#ifdef CONFIG_SENSORS_DS18B20_POLL + unsigned int interval; /* Polling interval */ + sem_t run; /* Locks sensor thread */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Sensor functions */ + +static int ds18b20_active(FAR struct sensor_lowerhalf_s *lower, + unsigned char enabled); + +static int ds18b20_fetch(FAR struct sensor_lowerhalf_s *lower, + FAR char *buffer, size_t buflen); + +static int ds18b20_control(FAR struct sensor_lowerhalf_s *lower, + int cmd, unsigned long arg); + +#ifdef CONFIG_SENSORS_DS18B20_POLL +static int ds18b20_set_interval(FAR struct sensor_lowerhalf_s *lower, + FAR unsigned int *period_us); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Timeout definitions after measurement according to the data sheet */ + +static const uint32_t g_res_timeout[DS18B20_NRES] = +{ + 93750, + 187500, + 375000, + 750000 +}; + +static const struct sensor_ops_s g_ds18b20_ops = +{ +#ifdef CONFIG_SENSORS_DS18B20_POLL + .set_interval = ds18b20_set_interval, +#endif + .activate = ds18b20_active, + .fetch = ds18b20_fetch, + .control = ds18b20_control +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ds18b20_rawdata + * + * Description: + * Helper for extract temperature data from scratchpad + * + * Return: + * Raw temperature data + * + ****************************************************************************/ + +static inline int16_t ds18b20_rawtemp(FAR const uint8_t *spad) +{ + return (spad[DS18B20_SPAD_LSB_OFFSET] | + (spad[DS18B20_SPAD_MSB_OFFSET] << 8)); +} + +/**************************************************************************** + * Name: ds18b20_tempdata + * + * Description: + * Helper for converting temperatur data from raw sensor data + * + * Return: + * Temperature data + * + ****************************************************************************/ + +static inline float ds18b20_temp(FAR const uint8_t *spad) +{ + int8_t predec = DS18B20_TEMP_PREDEC(spad[DS18B20_SPAD_LSB_OFFSET], + spad[DS18B20_SPAD_MSB_OFFSET]); + uint8_t dec = DS18B20_TEMP_DEC(spad[DS18B20_SPAD_LSB_OFFSET], + spad[DS18B20_SPAD_RES_OFFSET]); + + return predec + dec / 16.0; +} + +/**************************************************************************** + * Name: ds18b20_alarm_cb + * + * Description: + * Call back function for searching a single alarm device. + * + * Input Parameters: + * romcode - Unique romcode of a device with alarm flag set + * arg - Pointer to struct onewire_alarm_s + * + ****************************************************************************/ + +#ifdef CONFIG_SENSORS_DS18B20_POLL +static void ds18b20_alarm_cb(int family, uint64_t romcode, FAR void *arg) +{ + FAR struct ds18b20_cb_alarm_s *priv = (FAR struct ds18b20_cb_alarm_s *)arg; + + if (romcode == priv->romcode) + { + priv->isalarm = true; + } +} +#endif + +/**************************************************************************** + * Name: ds18b20_isalarm + * + * Description: + * Check if a 1wire device has set the alarm flag in an atomic operation. + * + * Input Parameters: + * master - Pointer to the allocated 1-wire interface + * config - Described the 1WIRE configuration + * + * Return Value: + * 0 - no alarm flag is set + * 1 - alarm flag is set + * < 0 - in case of error + * + ****************************************************************************/ + +#ifdef CONFIG_SENSORS_DS18B20_POLL +static int ds18b20_isalarm(FAR struct onewire_master_s *master, + FAR const struct onewire_config_s *config) +{ + int ret; + struct ds18b20_cb_alarm_s alarm; + alarm.romcode = config->romcode; + alarm.isalarm = false; + + ret = onewire_search(master, config->family, true, ds18b20_alarm_cb, + &alarm); + if (ret > 0) + { + /* Check if our device is in the alarm list */ + + if (alarm.isalarm == true) + { + ret = 1; + } + else + { + /* Device found, but not our */ + + ret = 0; + } + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: ds18b20_measure + * + * Description: Perform a measurement + * + * Parameter: + * dev - Internal private lower half driver instance + * + * Return: + * OK - on success + ****************************************************************************/ + +static int ds18b20_measure(FAR struct ds18b20_dev_s *dev) +{ + int ret; + uint8_t buf = DS18B20_CMD_CONVERTT; + + ret = onewire_write(dev->master, &dev->config, &buf, 1); + if (ret < 0) + { + snerr("ERROR: Unable to perform a temperature measurement\n"); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: ds18b20_write_spad + * + * Description: Write scratchpad data to chip + * + * Parameter: + * dev - Internal private lower half driver instance + * th - High alarm temperature + * lh - Low alarm temperature + * res - Temperature resolution + * + * Return: + * OK - on success + ****************************************************************************/ + +static int ds18b20_write_spad(FAR struct ds18b20_dev_s *dev, int8_t th, + int8_t tl, uint8_t res) +{ + int ret; + uint8_t wbuf[DS18B20_WSPAD_SIZE + 1]; + + wbuf[DS18B20_WSPAD_CMD_OFFSET] = ONEWIRE_CMD_WRITE_SCRATCHPAD; + wbuf[DS18B20_WSPAD_TH_OFFSET + 1] = th; + wbuf[DS18B20_WSPAD_TL_OFFSET + 1] = tl; + wbuf[DS18B20_WSPAD_RES_OFFSET + 1] = res; + + ret = onewire_write(dev->master, &dev->config, wbuf, sizeof(wbuf)); + if (ret < 0) + { + snerr("ERROR: Unable to write scratchpad\n"); + } + + return ret; +} + +/**************************************************************************** + * Name: ds18b20_read_spad + * + * Description: Read scratchpad data from chip + * + * Parameter: + * dev - Internal private lower half driver instance + * spad - Pointer to store the scratchpad data + * + * Return: + * OK - on success + ****************************************************************************/ + +static int ds18b20_read_spad(FAR struct ds18b20_dev_s *dev, + FAR uint8_t *spad) +{ + int ret; + uint8_t crc; + uint8_t wbuf = ONEWIRE_CMD_READ_SCRATCHPAD; + FAR struct ds18b20_config_s *reg = &dev->reg; + + ret = onewire_writeread(dev->master, &dev->config, &wbuf, 1, spad, + DS18B20_SPAD_SIZE); + if (ret < 0) + { + snerr("ERROR: Unable to read scratchpad\n"); + return ret; + } + + crc = onewire_crc8(spad, DS18B20_SPAD_SIZE - 1); + if (crc != spad[DS18B20_SPAD_CRC_OFFSET - 1]) + { + snerr("ERROR: crc mismatch in scratchpad\n"); + return -EIO; + } + + /* Update configurable register state each time reading the scratchpad */ + + reg->res = spad[DS18B20_SPAD_RES_OFFSET]; + + if (reg->res < DS18B20_RES_CONV(DS18B20_RESMIN) || + reg->res > DS18B20_RES_CONV(reg->res)) + { + /* Usually this should not happen, but be sure that we did not run into + * a buffer overflow later. + */ + + swarn("WARNING: Sensor responsed unknown resolution: %d\n", reg->res); + reg->res = DS18B20_RES_CONV(DS18B20_RESMAX); + } + + reg->alarm.thigh = spad[DS18B20_SPAD_TH_OFFSET]; + reg->alarm.tlow = spad[DS18B20_SPAD_TL_OFFSET]; + + return OK; +} + +/**************************************************************************** + * Name: ds18b20_set_res + * + * Description: Set resolution for temperature measurement + * + * Parameter: + * dev - Internal private lower half driver instance + * res - Resolution, can be 9 to 12 bit + * + * Return: + * OK - on success + ****************************************************************************/ + +static int ds18b20_set_res(FAR struct ds18b20_dev_s *dev, uint8_t res) +{ + int ret; + uint8_t spad[DS18B20_SPAD_SIZE]; + + /* Read current scratchpad first */ + + ret = ds18b20_read_spad(dev, spad); + if (ret < 0) + { + return ret; + } + + ret = ds18b20_write_spad(dev, dev->reg.alarm.thigh, dev->reg.alarm.tlow, + res); + if (ret < 0) + { + return ret; + } + + /* Read current scratchpad again and verify that res is set */ + + ret = ds18b20_read_spad(dev, spad); + if (ret < 0) + { + return ret; + } + + if (DS18B20_RES_VAL(spad[DS18B20_SPAD_RES_OFFSET]) != DS18B20_RES_VAL(res)) + { + snerr("ERROR: Expected resolution not matched, received RES: %d\n", + spad[DS18B20_SPAD_RES_OFFSET]); + return -EIO; + } + + return OK; +} + +/**************************************************************************** + * Name: ds18b20_set_alarm + * + * Description: Set temperature alarm + * + * Parameter: + * dev - Internal private lower half driver instance + * alarm - Pointer to store the alarm temperature + * + * Return: + * OK - on success + ****************************************************************************/ + +static int ds18b20_set_alarm(FAR struct ds18b20_dev_s *dev, + FAR const struct ds18b20_alarm_s *alarm) +{ + int ret; + uint8_t spad[DS18B20_SPAD_SIZE]; + + /* Read current scratchpad first, to get current resolution */ + + ret = ds18b20_read_spad(dev, spad); + if (ret < 0) + { + return ret; + } + + ret = ds18b20_write_spad(dev, alarm->thigh, alarm->tlow, dev->reg.res); + if (ret < 0) + { + return ret; + } + +#ifdef CONFIG_SENSORS_DS18B20_POLL + dev->reg.alarm.wakeup = alarm->wakeup; +#endif + + /* Read current scratchpad again and verify that alarm is set */ + + ret = ds18b20_read_spad(dev, spad); + if (ret < 0) + { + return ret; + } + + if (spad[DS18B20_SPAD_TH_OFFSET] != alarm->thigh || + spad[DS18B20_SPAD_TL_OFFSET] != alarm->tlow) + { + snerr("ERROR: Expected alarm trigger does not match, " \ + "received TH: %d, TL: %d\n", + spad[DS18B20_SPAD_TH_OFFSET], + spad[DS18B20_SPAD_TL_OFFSET]); + return -EIO; + } + + return ret; +} + +/**************************************************************************** + * Name: ds18b20_curtime + * + * Description: Helper to get current timestamp. + * + * Return: + * Timestamp in nsec + ****************************************************************************/ + +static unsigned long ds18b20_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: ds18b20_notify + * + * Description: + * Notify upper about data has been changed. + * + * Parameter: + * dev - Internal private lower half driver instance + * data - Read sensor data + * + ****************************************************************************/ + +#ifdef CONFIG_SENSORS_DS18B20_POLL +static void ds18b20_notify(FAR struct ds18b20_dev_s *dev, + FAR struct ds18b20_sensor_data_s *data) +{ + FAR struct ds18b20_sensor_s *sensor = &dev->sensor; + struct sensor_event_temp temp; + + temp.temperature = ds18b20_temp(data->spad); + temp.timestamp = data->timestamp; + sensor->lower.push_event(sensor->lower.priv, &temp, + sizeof(struct sensor_event_temp)); +} +#endif + +/**************************************************************************** + * Name: ds18b20_measure_read + * + * Description: Perform a measurement and read last measured temperature + * + * Parameter: + * dev - Internal private lower half driver instance + * data - Pointer to store sensor data + * + * Return: + * OK - on success + ****************************************************************************/ + +static int ds18b20_measure_read(FAR struct ds18b20_dev_s *dev, + FAR struct ds18b20_sensor_data_s *data) +{ + int ret; + + ret = ds18b20_measure(dev); + if (ret < 0) + { + return ret; + } + + nxsig_usleep(g_res_timeout[DS18B20_RES_VAL(dev->reg.res)]); + + ret = ds18b20_read_spad(dev, data->spad); + if (ret < 0) + { + return ret; + } + + data->timestamp = ds18b20_curtime(); + return OK; +} + +/**************************************************************************** + * Name: ds18b20_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 ds18b20_fetch(FAR struct sensor_lowerhalf_s *lower, + FAR char *buffer, size_t buflen) +{ + int ret; + struct ds18b20_sensor_data_s data; + FAR struct ds18b20_dev_s *priv = (FAR struct ds18b20_dev_s *)lower; + + /* Check if the user is reading the right size */ + + if (buflen != sizeof(struct sensor_event_temp)) + { + snerr("ERROR: You need to read %d bytes from this sensor!\n", + sizeof(struct sensor_event_temp)); + return -EINVAL; + } + + ret = ds18b20_measure_read(priv, &data); + if (!ret) + { + FAR struct sensor_event_temp *temp = + (FAR struct sensor_event_temp *)buffer; + temp->temperature = ds18b20_temp(data.spad); + temp->timestamp = data.timestamp; + } + else + { + return ret; + } + + return buflen; +} + +/**************************************************************************** + * Name: ds18b20_control + * + * Description: Interface function of struct sensor_ops_s. + * + * Return: + * OK - on success + ****************************************************************************/ + +static int ds18b20_control(FAR struct sensor_lowerhalf_s *lower, + int cmd, unsigned long arg) +{ + int ret; + struct ds18b20_dev_s *priv = (FAR struct ds18b20_dev_s *)lower; + + switch (cmd) + { + /* Read rom code */ + + case SNIOC_READROMCODE: + { + FAR uint64_t *ptr = (FAR uint64_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + *ptr = priv->config.romcode; + sninfo("read romcode: %16llx\n", *ptr); + ret = OK; + } + break; + + /* Set new temperature alarm */ + + case SNIOC_SETALARM: + { + FAR struct ds18b20_alarm_s *ptr = + (FAR struct ds18b20_alarm_s *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + sninfo("set alarm: [TH/TL] = %d/%d °C\n", ptr->thigh, ptr->tlow); + ret = ds18b20_set_alarm(priv, ptr); + } + break; + + /* Set sensor resolution */ + + case SNIOC_SETRESOLUTION: + { + uint8_t res = arg - 9; + sninfo("set resolution: %ld\n", arg); + if (res >= DS18B20_RESMIN && res <= DS18B20_RESMAX) + { + ret = ds18b20_set_res(priv, DS18B20_RES_CONV(res)); + } + else + { + ret = -EINVAL; + } + } + break; + + default: + snerr("ERROR: Unrecognized cmd: %d\n", cmd); + ret = -ENOTTY; + break; + } + + return ret; +} + +/**************************************************************************** + * Name: ds18b20_active + * + * Description: Interface function of struct sensor_ops_s. + * + * Return: + * OK - on success + ****************************************************************************/ + +static int ds18b20_active(FAR struct sensor_lowerhalf_s *lower, + unsigned char enabled) +{ +#ifdef CONFIG_SENSORS_DS18B20_POLL + bool start_thread = false; + struct ds18b20_dev_s *priv = (FAR struct ds18b20_dev_s *)lower; + + if (enabled) + { + if (!priv->sensor.enabled) + { + start_thread = true; + } + } + + priv->sensor.enabled = enabled; + + if (start_thread == true) + { + /* Wake up the thread */ + + nxsem_post(&priv->run); + } +#endif + + return OK; +} + +/**************************************************************************** + * Name: ds18b20_set_interval + * + * Description: Interface function of struct sensor_ops_s. + * + * Return: + * OK - on success + ****************************************************************************/ + +#ifdef CONFIG_SENSORS_DS18B20_POLL +static int ds18b20_set_interval(FAR struct sensor_lowerhalf_s *lower, + FAR unsigned int *period_us) +{ + FAR struct ds18b20_dev_s *priv = (FAR struct ds18b20_dev_s *)lower; + priv->interval = *period_us; + return OK; +} +#endif + +/**************************************************************************** + * Name: ds18b20_thread + * + * Description: Thread for performing interval measurement cycle and data + * read. + * + * Parameter: + * argc - Number opf arguments + * argv - Pointer to argument list + ****************************************************************************/ + +#ifdef CONFIG_SENSORS_DS18B20_POLL +static int ds18b20_thread(int argc, char** argv) +{ + uint16_t orawdata; + FAR struct ds18b20_dev_s *priv = (FAR struct ds18b20_dev_s *) + ((uintptr_t)strtoul(argv[1], NULL, 0)); + + /* Set initial value to out of measurement range to ensure that the first + * data read leads to an upper notification. + */ + + orawdata = 0xffff; + + while (true) + { + int ret; + struct ds18b20_sensor_data_s data; + FAR struct ds18b20_sensor_s *sensor = &priv->sensor; + FAR struct ds18b20_alarm_s *alarm = &priv->reg.alarm; + + if (!sensor->enabled) + { + /* Waiting to be woken up */ + + nxsem_wait(&priv->run); + } + + if (alarm->wakeup == true) + { + /* Perform a temperature conversion to update the alarm flag */ + + ret = ds18b20_measure(priv); + if (!ret) + { + /* Wait until temperature conversion is done and the alarm flag + * is up to date. + */ + + nxsig_usleep(g_res_timeout[DS18B20_RES_VAL(priv->reg.res)]); + + /* Check for existing temperature alarm */ + + ret = ds18b20_isalarm(priv->master, &priv->config); + if (ret == 1) + { + ret = ds18b20_measure_read(priv, &data); + if (!ret && sensor->enabled == true) + { + /* Notify upper */ + + ds18b20_notify(priv, &data); + } + } + } + } + else + { + /* Default nofitication when temperature has been changed */ + + ret = ds18b20_measure_read(priv, &data); + if (!ret) + { + uint16_t rawtemp = ds18b20_rawtemp(data.spad); + + if (sensor->enabled == true && orawdata != rawtemp) + { + ds18b20_notify(priv, &data); + } + + /* Store the last sensor data for later comparison */ + + orawdata = ds18b20_rawtemp(data.spad); + } + } + + /* Sleeping thread before fetching the next sensor data */ + + nxsig_usleep(priv->interval); + } + + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ds18b20_register + * + * Description: + * Register the DS18B20 character device. + * + * Input Parameters: + * devno - The user specifies device number, from 0. + * onewire - An instance of the 1wire interface to communicate with DS18B20 + * sensor. + * romcode - The ROM code of the DS18B20. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + ****************************************************************************/ + +int ds18b20_register(int devno, FAR struct onewire_master_s *onewire, + uint64_t romcode) +{ + int ret; + struct ds18b20_sensor_s *tmp; +#ifdef CONFIG_SENSORS_DS18B20_POLL + FAR char *argv[2]; + char arg1[32]; +#endif + + /* Sanity check */ + + if (!onewire) + { + snerr("Invalid 1wire instance\n"); + return -EINVAL; + } + + /* Initialize the DS18B20 device structure */ + + FAR struct ds18b20_dev_s *priv = (FAR struct ds18b20_dev_s *) + kmm_malloc(sizeof(struct ds18b20_dev_s)); + + if (priv == NULL) + { + snerr("ERROR: Failed to allocate instance\n"); + return -ENOMEM; + } + + priv->master = onewire; + priv->config.romcode = romcode; + priv->config.family = DS18B20_DEVICE_FAMILY; + priv->reg.res = DS18B20_RES_CONV(DS18B20_RESMAX); + priv->reg.alarm.thigh = DS18B20_TALARM_MAX; + priv->reg.alarm.tlow = DS18B20_TALARM_MIN; +#ifdef CONFIG_SENSORS_DS18B20_POLL + priv->reg.alarm.wakeup = false; + priv->interval = CONFIG_SENSORS_DS18B20_POLL_INTERVAL; + + nxsem_init(&priv->run, 0, 0); + nxsem_set_protocol(&priv->run, SEM_PRIO_NONE); +#endif + + /* Temperature register */ + + tmp = &priv->sensor; +#ifdef CONFIG_SENSORS_DS18B20_POLL + tmp->enabled = false; +#endif + tmp->lower.ops = &g_ds18b20_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 sensor_err; + } + +#ifdef CONFIG_SENSORS_DS18B20_POLL + + /* Create thread for polling sensor data */ + + snprintf(arg1, 16, "0x%" PRIxPTR, (uintptr_t)priv); + argv[0] = arg1; + argv[1] = NULL; + ret = kthread_create("ds18b20_thread", SCHED_PRIORITY_DEFAULT, + CONFIG_SENSORS_DS18B20_THREAD_STACKSIZE, + ds18b20_thread, argv); + if (ret > 0) +#endif + { + return OK; + } + +sensor_err: + sensor_unregister(&priv->sensor.lower, devno); + kmm_free(priv); + return ret; +} +#endif /* CONFIG_1WIRE && CONFIG_SENSORS_DS18B20 */ diff --git a/include/nuttx/sensors/ds18b20.h b/include/nuttx/sensors/ds18b20.h new file mode 100644 index 00000000000..7b8204c767a --- /dev/null +++ b/include/nuttx/sensors/ds18b20.h @@ -0,0 +1,95 @@ +/**************************************************************************** + * include/nuttx/sensors/ds18b20.h + * Character driver for DS18B20 Digital 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_SENSORS_DS18B20_H +#define __INCLUDE_NUTTX_SENSORS_DS18B20_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#if defined(CONFIG_1WIRE) && defined(CONFIG_SENSORS_DS18B20) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DS18B20_DEVICE_FAMILY (0x28) + +#define DS18B20_CMD_CONVERTT (0x44) +#define DS18B20_CMD_COPYSPAD (0x48) +#define DS18B20_CMD_RECALL (0xb8) +#define DS18B20_CMD_READPOWS (0xb4) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct onewire_master_s; + +struct ds18b20_alarm_s +{ + int8_t thigh; /* Upper alarm temperature */ + int8_t tlow; /* Lower alarm temperature */ +# ifdef CONFIG_SENSORS_DS18B20_POLL + bool wakeup; /* Wakeup poll requests only when alarm detected */ +# endif +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: ds18b20_register + * + * Description: + * Register the DS18B20 character device. + * + * Input Parameters: + * devno - The user specifies device number, from 0. + * onewire - An instance of the 1wire interface to communicate with DS18B20 + * sensor. + * romcode - The ROM code of the DS18B20. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + ****************************************************************************/ + +int ds18b20_register(int devno, FAR struct onewire_master_s *dev, + uint64_t romcode); +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_1WIRE && CONFIG_SENSORS_DS18B20 */ +#endif /* __INCLUDE_NUTTX_SENSORS_DS18B20_H */ diff --git a/include/nuttx/sensors/ioctl.h b/include/nuttx/sensors/ioctl.h index c8ad99b7aa1..a24e169c4ab 100644 --- a/include/nuttx/sensors/ioctl.h +++ b/include/nuttx/sensors/ioctl.h @@ -229,26 +229,33 @@ #define SNIOC_READADDR SNIOC_READID /* Arg: uint8_t* pointer */ +/* IOCTL commands unique to the DS18B20 */ + +/* SNIOC_SETRESOLUTION */ /* Arg: uint8_t value */ + +#define SNIOC_READROMCODE _SNIOC(0x0067) /* Arg: uint64_t* pointer */ +#define SNIOC_SETALARM _SNIOC(0x0068) /* Arg: struct ds18b20_alarm_s* */ + /* Command: SNIOC_ACTIVATE * Description: Enable or disable sensor * Argument: true or false. */ -#define SNIOC_ACTIVATE _SNIOC(0x0067) +#define SNIOC_ACTIVATE _SNIOC(0x0080) /* Command: SNIOC_SET_INTERVAL * Description: Set interval between samples * Argument: This is the interval pointer, in microseconds */ -#define SNIOC_SET_INTERVAL _SNIOC(0x0068) +#define SNIOC_SET_INTERVAL _SNIOC(0x0081) /* Command: SNIOC_BATCH * Description: Set batch latency between batch data. * Argument: This is the latency pointer, in microseconds */ -#define SNIOC_BATCH _SNIOC(0x0069) +#define SNIOC_BATCH _SNIOC(0x0082) /* Command: SNIOC_GET_NEVENTBUF * Description: the number of sensor events that sensor buffer of upper half @@ -265,7 +272,7 @@ * See sensor.h(struct sensor_lower_half_s buffer_bytes). */ -#define SNIOC_GET_NEVENTBUF _SNIOC(0x0070) +#define SNIOC_GET_NEVENTBUF _SNIOC(0x0083) /* Command: SNIOC_SET_BUFFER_NUMBER * Description: Set the number of events intermediate circualr buffer can @@ -275,6 +282,6 @@ * circualr buffer can hold by this ioctl command. */ -#define SNIOC_SET_BUFFER_NUMBER _SNIOC(0x0071) +#define SNIOC_SET_BUFFER_NUMBER _SNIOC(0x0084) #endif /* __INCLUDE_NUTTX_SENSORS_IOCTL_H */