05b889457e
Removes the family identifier from the device configuration, since the device family is already part of the rom code and the underlying logic does know how to extract this information. include/nuttx/1wire drivers/sensors stm32f103-minimim Signed-off-by: Marco Krahl <ocram.lhark@gmail.com>
987 lines
27 KiB
C
987 lines
27 KiB
C
/****************************************************************************
|
|
* 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 <nuttx/config.h>
|
|
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/kthread.h>
|
|
#include <nuttx/signal.h>
|
|
#include <nuttx/semaphore.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/1wire/1wire.h>
|
|
#include <nuttx/1wire/1wire_master.h>
|
|
#include <nuttx/1wire/1wire_crc.h>
|
|
#include <nuttx/sensors/ds18b20.h>
|
|
#include <nuttx/sensors/sensor.h>
|
|
|
|
#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, DS18B20_DEVICE_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->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 */
|