Merge pull request #35 from sdalu/sensor_hdc1000

Sensor hdc1000, mcp9808, tsl2561, tsl2591
This commit is contained in:
Fabio Utzig 2016-02-15 19:29:07 -02:00
commit 1548bca80f
11 changed files with 2563 additions and 0 deletions

146
os/various/bswap.h Normal file
View File

@ -0,0 +1,146 @@
/*
Copyright (C) 2016 Stephane D'Alu
Licensed 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 BSWAP_H
#define BSWAP_H
#if defined(__cplusplus)
extern "C" {
#endif
#if !(defined(ARCH_BIG_ENDIAN) || defined(ARCH_LITTLE_ENDIAN))
#error "Need to define one: ARCH_BIG_ENDIAN or ARCH_LITTLE_ENDIAN"
#endif
#if defined(ARCH_BIG_ENDIAN) && defined(ARCH_LITTLE_ENDIAN)
#error "ARCH_BIG_ENDIAN and ARCH_LITTLE_ENDIAN are both set"
#endif
#define BSWAP_16(x) \
(uint16_t)((((x) & 0xFF00) >> 8) | \
(((x) & 0x00FF) << 8))
#define BSWAP_32(x) \
(uint32_t)((((x) & 0xFF000000UL) >> 24UL) | \
(((x) & 0x00FF0000UL) >> 8UL) | \
(((x) & 0x0000FF00UL) << 8UL) | \
(((x) & 0x000000FFUL) << 24UL))
#if defined(ARCH_BIG_ENDIAN)
#define le16_to_cpu(x) bswap_16(x)
#define le32_to_cpu(x) bswap_32(x)
#define be16_to_cpu(x) (x)
#define be32_to_cpu(x) (x)
#define cpu_to_le16(x) bswap_16(x)
#define cpu_to_le32(x) bswap_32(x)
#define cpu_to_be16(x) (x)
#define cpu_to_be32(x) (x)
#define LE16_TO_CPU(x) BSWAP_16(x)
#define LE32_TO_CPU(x) BSWAP_32(x)
#define BE16_TO_CPU(x) (x)
#define BE32_TO_CPU(x) (x)
#define CPU_TO_LE16(x) BSWAP_16(x)
#define CPU_TO_LE32(x) BSWAP_32(x)
#define CPU_TO_BE16(x) (x)
#define CPU_TO_BE32(x) (x)
#endif
#if defined(ARCH_LITTLE_ENDIAN)
#define le16_to_cpu(x) (x)
#define le32_to_cpu(x) (x)
#define be16_to_cpu(x) bswap_16(x)
#define be32_to_cpu(x) bswap_32(x)
#define cpu_to_le16(x) (x)
#define cpu_to_le32(x) (x)
#define cpu_to_be16(x) bswap_16(x)
#define cpu_to_be32(x) bswap_32(x)
#define LE16_TO_CPU(x) (x)
#define LE32_TO_CPU(x) (x)
#define BE16_TO_CPU(x) BSWAP_16(x)
#define BE32_TO_CPU(x) BSWAP_32(x)
#define CPU_TO_LE16(x) (x)
#define CPU_TO_LE32(x) (x)
#define CPU_TO_BE16(x) BSWAP_16(x)
#define CPU_TO_BE32(x) BSWAP_32(x)
#endif
static inline uint16_t bswap_16(const uint16_t x)
__attribute__ ((warn_unused_result))
__attribute__ ((const))
__attribute__ ((always_inline));
static inline uint16_t bswap_16(const uint16_t x) {
if (__builtin_constant_p(x))
return BSWAP_16(x);
uint8_t tmp;
union { uint16_t x; uint8_t b[2]; } data;
data.x = x;
tmp = data.b[0];
data.b[0] = data.b[1];
data.b[1] = tmp;
return data.x;
}
static inline uint32_t bswap_32(const uint32_t x)
__attribute__ ((warn_unused_result))
__attribute__ ((const))
__attribute__ ((always_inline));
static inline uint32_t bswap_32(const uint32_t x) {
if (__builtin_constant_p(x))
return BSWAP_32(x);
uint8_t tmp;
union { uint32_t x; uint8_t b[4]; } data;
data.x = x;
tmp = data.b[0];
data.b[0] = data.b[3];
data.b[3] = tmp;
tmp = data.b[1];
data.b[1] = data.b[2];
data.b[2] = tmp;
return data.x;
}
static inline void bswap_n(void* const data, uint8_t len)
__attribute__ ((nonnull (1)));
static inline void bswap_n(void* const data, uint8_t len) {
uint8_t* ptr = (uint8_t*)data;
for ( ; len > 1 ; ptr++, len -= 2 ) {
uint8_t tmp = *ptr;
*ptr = *(ptr + len - 1);
*(ptr + len - 1) = tmp;
}
}
#if defined(__cplusplus)
}
#endif
#endif

View File

@ -0,0 +1,265 @@
/*
HDC100x for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
Licensed 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.
*/
/**
* @file HDC1000.c
* @brief HDC1000 interface module code.
*
* @addtogroup hdc1000
* @{
*/
#define I2C_HELPERS_AUTOMATIC_DRV TRUE
#include "hal.h"
#include "i2c_helpers.h"
#include "hdc1000.h"
/* DOC: http://www.ti.com/lit/ds/symlink/hdc1008.pdf
*/
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/* I2C Register */
#define HDC1000_REG_TEMP_HUMID 0x00
#define HDC1000_REG_TEMP 0x00
#define HDC1000_REG_HUMID 0x01
#define HDC1000_REG_CONFIG 0x02
#define HDC1000_REG_SERIAL 0xFB
#define HDC1000_REG_SERIAL_1 0xFB
#define HDC1000_REG_SERIAL_2 0xFC
#define HDC1000_REG_SERIAL_3 0xFD
#define HDC1000_REG_MANUF_ID 0xFE
#define HDC1000_REG_DEVICE_ID 0xFF
/* Configuration */
#define HDC1000_CONFIG_RST (1 << 15)
#define HDC1000_CONFIG_HEATER (1 << 13)
#define HDC1000_CONFIG_MODE_ONE (0 << 12)
#define HDC1000_CONFIG_MODE_BOTH (1 << 12)
#define HDC1000_CONFIG_BATT (1 << 11)
#define HDC1000_CONFIG_TRES_14 (0)
#define HDC1000_CONFIG_TRES_11 (1 << 10)
#define HDC1000_CONFIG_HRES_14 (0)
#define HDC1000_CONFIG_HRES_11 (1 << 8)
#define HDC1000_CONFIG_HRES_8 (1 << 9)
/* Value */
#define HDC1000_MANUF_ID 0x5449
#define HDC1000_DEVICE_ID 0x1000
/* Delay in micro seconds */
#define HDC1000_DELAY_ACQUIRE_SAFETY 1000
#define HDC1000_DELAY_ACQUIRE_TRES_14 6350
#define HDC1000_DELAY_ACQUIRE_TRES_11 3650
#define HDC1000_DELAY_ACQUIRE_HRES_14 6500
#define HDC1000_DELAY_ACQUIRE_HRES_11 3850
#define HDC1000_DELAY_ACQUIRE_HRES_8 2500
#define HDC1000_DELAY_STARTUP 15000
// Deefault config (high res)
#define HDC1000_CONFIG_RES (HDC1000_CONFIG_TRES_14 | \
HDC1000_CONFIG_HRES_14)
#define HDC1000_DELAY_ACQUIRE (HDC1000_DELAY_ACQUIRE_TRES_14 + \
HDC1000_DELAY_ACQUIRE_HRES_14)
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
static inline msg_t
_apply_config(HDC1000_drv *drv) {
struct __attribute__((packed)) {
uint8_t reg;
uint16_t conf;
} tx = { HDC1000_REG_CONFIG, cpu_to_be16(drv->cfg) };
return i2c_send((uint8_t*)&tx, sizeof(tx));
}
static inline msg_t
_decode_measure(HDC1000_drv *drv,
uint32_t val, float *temperature, float *humidity) {
(void)drv;
/* Temperature */
if (temperature) {
float temp = (val >> 16);
temp /= 65536;
temp *= 165;
temp -= 40;
*temperature = temp;
}
/* Humidiy */
if (humidity) {
float hum = (val & 0xFFFF);
hum /= 65535;
hum *= 100;
*humidity = hum;
}
/* ok */
return MSG_OK;
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
void
HDC1000_init(HDC1000_drv *drv, HDC1000_config *config) {
drv->config = config;
drv->cfg = HDC1000_CONFIG_RST | HDC1000_CONFIG_MODE_BOTH |
HDC1000_CONFIG_RES;
drv->delay = (HDC1000_DELAY_ACQUIRE +
HDC1000_DELAY_ACQUIRE_SAFETY) / 1000;
drv->state = SENSOR_INIT;
}
msg_t
HDC1000_check(HDC1000_drv *drv) {
uint16_t manuf, device;
msg_t msg;
if (((msg = i2c_reg_recv16_be(HDC1000_REG_MANUF_ID, &manuf )) < MSG_OK) ||
((msg = i2c_reg_recv16_be(HDC1000_REG_DEVICE_ID, &device)) < MSG_OK))
return msg;
if ((manuf != HDC1000_MANUF_ID) || (device != HDC1000_DEVICE_ID))
return SENSOR_NOTFOUND;
return MSG_OK;
}
msg_t
HDC1000_start(HDC1000_drv *drv) {
osalDbgAssert((drv->state == SENSOR_INIT ) ||
(drv->state == SENSOR_ERROR ) ||
(drv->state == SENSOR_STOPPED),
"invalid state");
msg_t msg;
if ((msg = _apply_config(drv)) < MSG_OK) {
drv->state = SENSOR_ERROR;
return msg;
}
drv->state = SENSOR_STARTED;
return MSG_OK;
}
msg_t
HDC1000_stop(HDC1000_drv *drv) {
drv->state = SENSOR_STOPPED;
return MSG_OK;
}
msg_t
HDC1000_setHeater(HDC1000_drv *drv, bool on) {
if (on) { drv->cfg |= HDC1000_CONFIG_HEATER; }
else { drv->cfg &= ~HDC1000_CONFIG_HEATER; }
msg_t msg;
if ((msg = _apply_config(drv)) < MSG_OK) {
drv->state = SENSOR_ERROR;
return msg;
}
return MSG_OK;
}
msg_t
HDC1000_startMeasure(HDC1000_drv *drv) {
msg_t msg;
osalDbgAssert(drv->state == SENSOR_STARTED, "invalid state");
if ((msg = i2c_reg(HDC1000_REG_TEMP_HUMID)) < MSG_OK)
return msg;
drv->state = SENSOR_MEASURING;
return MSG_OK;
}
msg_t
HDC1000_readSerial(HDC1000_drv *drv, uint8_t *serial) {
msg_t msg;
osalDbgAssert(drv->state == SENSOR_STARTED, "invalid state");
if (((msg = i2c_reg_recv16(HDC1000_REG_SERIAL_1,
(uint16_t*)&serial[0])) < MSG_OK) ||
((msg = i2c_reg_recv16(HDC1000_REG_SERIAL_2,
(uint16_t*)&serial[2])) < MSG_OK) ||
((msg = i2c_reg_recv8 (HDC1000_REG_SERIAL_3,
(uint8_t*) &serial[4])) < MSG_OK))
return msg;
return MSG_OK;
}
msg_t
HDC1000_readMeasure(HDC1000_drv *drv,
float *temperature, float *humidity) {
msg_t msg;
uint32_t val;
osalDbgAssert((drv->state == SENSOR_MEASURING) ||
(drv->state == SENSOR_READY ),
"invalid state");
if ((msg = i2c_recv32_be(&val)) < MSG_OK) {
drv->state = SENSOR_ERROR;
return msg;
}
drv->state = SENSOR_STARTED;
return _decode_measure(drv, val, temperature, humidity);
}
msg_t
HDC1000_readTemperatureHumidity(HDC1000_drv *drv,
float *temperature, float *humidity) {
msg_t msg;
uint32_t val;
osalDbgAssert(drv->state == SENSOR_STARTED, "invalid state");
/* Request value */
if ((msg = i2c_reg(HDC1000_REG_TEMP_HUMID)) < MSG_OK)
return msg;
/* Wait */
osalThreadSleepMilliseconds(drv->delay);
/* Get value */
if ((msg = i2c_recv32_be(&val)) < MSG_OK) {
drv->state = SENSOR_ERROR;
return msg;
}
return _decode_measure(drv, val, temperature, humidity);
}
/** @} */

View File

@ -0,0 +1,240 @@
/*
HDC100x for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
Licensed 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.
*/
/**
* @file hdc1000.h
* @brief HDC1000 Temperature/Humidiry sensor interface module header.
*
* When changing sensor settings, you generally need to wait
* for 2 * getAquisitionTime(), as usually the first acquisition
* will be corrupted by the change of settings.
*
* No locking is done.
*
* @{
*/
#ifndef _SENSOR_HDC1000_H_
#define _SENSOR_HDC1000_H_
#include <math.h>
#include <stdbool.h>
#include "i2c_helpers.h"
#include "sensor.h"
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
#define HDC1000_CONTINUOUS_ACQUISITION_SUPPORTED FALSE
/* I2C address */
#define HDC1000_I2CADDR_1 0x40
#define HDC1000_I2CADDR_2 0x41
#define HDC1000_I2CADDR_3 0x42
#define HDC1000_I2CADDR_4 0x43
#define HDC1000_SERIAL_SIZE 5 /**< @brief Size of serial (40bits) */
/**
* @brief Time necessary for the sensor to boot
*/
#define HDC1000_BOOTUP_TIME 15
/**
* @brief Time necessary for the sensor to start
*/
#define HDC1000_STARTUP_TIME 0
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
#define HDC1000_I2CADDR_DEFAULT HDC1000_I2CADDR_1
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief HDC1000 configuration structure.
*/
typedef struct {
I2CHelper i2c; /* keep it first */
} HDC1000_config;
/**
* @brief HDC1000 configuration structure.
*/
typedef struct {
HDC1000_config *config;
sensor_state_t state;
unsigned int delay;
uint16_t cfg;
} HDC1000_drv;
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
/**
* @brief Initialize the sensor driver
*/
void
HDC1000_init(HDC1000_drv *drv,
HDC1000_config *config);
/**
* @brief Start the sensor
*/
msg_t
HDC1000_start(HDC1000_drv *drv);
/**
* @brief Stop the sensor
*
* @details If the sensor support it, it will be put in low energy mode.
*/
msg_t
HDC1000_stop(HDC1000_drv *drv);
/**
* @brief Check that the sensor is really present
*/
msg_t
HDC1000_check(HDC1000_drv *drv);
msg_t
HDC1000_readSerial(HDC1000_drv *drv, uint8_t *serial);
/**
* @brief Control the HD1000 heater.
*/
msg_t
HDC1000_setHeater(HDC1000_drv *drv,
bool on);
/**
* @brief Time in milli-seconds necessary for acquiring a naw measure
*
* @returns
* unsigned int time in millis-seconds
*/
static inline unsigned int
HDC1000_getAcquisitionTime(HDC1000_drv *drv) {
return drv->delay;
}
/**
* @brief Trigger a mesure acquisition
*/
msg_t
HDC1000_startMeasure(HDC1000_drv *drv);
/**
* @brief Read the newly acquiered measure
*
* @note According the the sensor design the measure read
* can be any value acquired after the acquisition time
* and the call to readMeasure.
*/
msg_t
HDC1000_readMeasure(HDC1000_drv *drv,
float *temperature, float *humidity);
/**
* @brief Read temperature and humidity
*
* @details According to the sensor specification/configuration
* (see #HDC1000_CONTINUOUS_ACQUISITION_SUPPORTED),
* if the sensor is doing continuous measurement
* it's value will be requested and returned immediately.
* Otherwise a measure is started, the necessary amount of
* time for acquiring the value is spend sleeping (not spinning),
* and finally the measure is read.
*
* @note In continuous measurement mode, if you just started
* the sensor, you will need to wait getAcquisitionTime()
* in addition to the usual #HDC1000_STARTUP_TIME
* @note If using several sensors, it is better to start all the
* measure together, wait for the sensor having the longuest
* aquisition time, and finally read all the values
*/
msg_t
HDC1000_readTemperatureHumidity(HDC1000_drv *drv,
float *temperature, float *humidity);
/**
* @brief Return the humidity value in percent.
*
* @details Use readTemperatureHumidity() for returning the humidity value.
*
* @note Prefere readTemperatureHumidity(), if you need both temperature
* and humidity, or if you need better error handling.
*
* @returns
* float humidity percent
* NAN on failure
*/
static inline float
HDC1000_getHumidity(HDC1000_drv *drv) {
float humidity = NAN;
HDC1000_readTemperatureHumidity(drv, NULL, &humidity);
return humidity;
}
/**
* @brief Return the temperature value in °C.
*
* @details Use readTemperatureHumidity() for returning the humidity value.
*
* @note Prefere readTemperatureHumidity(), if you need both temperature
* and humidity, or if you need better error handling.
*
* @returns
* float humidity percent
* NAN on failure
*/
static inline float
HDC1000_getTemperature(HDC1000_drv *drv) {
float temperature = NAN;
HDC1000_readTemperatureHumidity(drv, &temperature, NULL);
return temperature;
}
#endif
/**
* @}
*/

View File

@ -0,0 +1,207 @@
/*
MCP9808 for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
Licensed 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.
*/
#define I2C_HELPERS_AUTOMATIC_DRV TRUE
#include "hal.h"
#include "i2c_helpers.h"
#include "mcp9808.h"
// http://www.mouser.com/ds/2/268/25095A-15487.pdf
// http://ww1.microchip.com/downloads/en/DeviceDoc/25095A.pdf
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/* I2C Register */
#define MCP9808_REG_CONFIG 0x01
#define MCP9808_REG_UPPER_TEMP 0x02
#define MCP9808_REG_LOWER_TEMP 0x03
#define MCP9808_REG_CRIT_TEMP 0x04
#define MCP9808_REG_AMBIENT_TEMP 0x05
#define MCP9808_REG_MANUF_ID 0x06
#define MCP9808_REG_DEVICE_ID 0x07
#define MCP9808_REG_RESOLUTION 0x08
/* Config */
#define MCP9808_REG_CONFIG_SHUTDOWN 0x0100
#define MCP9808_REG_CONFIG_CRITLOCKED 0x0080
#define MCP9808_REG_CONFIG_WINLOCKED 0x0040
#define MCP9808_REG_CONFIG_INTCLR 0x0020
#define MCP9808_REG_CONFIG_ALERTSTAT 0x0010
#define MCP9808_REG_CONFIG_ALERTCTRL 0x0008
#define MCP9808_REG_CONFIG_ALERTSEL 0x0002
#define MCP9808_REG_CONFIG_ALERTPOL 0x0002
#define MCP9808_REG_CONFIG_ALERTMODE 0x0001
/* Device Id */
#define MCP9808_MANUF_ID 0x0054
#define MCP9808_DEVICE_ID 0x0400
/* Resolution */
#define MCP9808_RES_2 0x00 /* 1/2 = 0.5 */
#define MCP9808_RES_4 0x01 /* 1/4 = 0.25 */
#define MCP9808_RES_8 0x10 /* 1/8 = 0.125 */
#define MCP9808_RES_16 0x11 /* 1/16 = 0.0625 */
/* Time in milli-seconds */
#define MCP9808_DELAY_ACQUIRE_RES_2 30
#define MCP9808_DELAY_ACQUIRE_RES_4 65
#define MCP9808_DELAY_ACQUIRE_RES_8 130
#define MCP9808_DELAY_ACQUIRE_RES_16 250
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
static inline msg_t
_apply_config(MCP9808_drv *drv) {
struct __attribute__((packed)) {
uint8_t reg;
uint16_t conf;
} tx = { MCP9808_REG_CONFIG, cpu_to_be16(drv->cfg) };
return i2c_send((uint8_t*)&tx, sizeof(tx));
}
static inline msg_t
_decode_measure(MCP9808_drv *drv,
uint16_t val, float *temperature) {
/* Temperature */
if (temperature) {
float temp = val & 0x0fff;
if (val & 0x1000) temp -= 0x1000;
float factor = 16.0F;
switch(drv->resolution) {
case RES_2 : factor = 2.0F; break;
case RES_4 : factor = 4.0F; break;
case RES_8 : factor = 8.0F; break;
case RES_16: factor = 16.0F; break;
}
*temperature = temp / factor;
}
/* Ok */
return MSG_OK;
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
void
MCP9808_init(MCP9808_drv *drv, MCP9808_config *config) {
drv->config = config;
drv->cfg = 0;
drv->resolution = RES_16; /* power up default */
drv->state = SENSOR_INIT;
}
msg_t
MCP9808_check(MCP9808_drv *drv) {
uint16_t manuf, device;
msg_t msg;
if (((msg = i2c_reg_recv16_be(MCP9808_REG_MANUF_ID, &manuf )) < MSG_OK) ||
((msg = i2c_reg_recv16_be(MCP9808_REG_DEVICE_ID, &device)) < MSG_OK))
return msg;
if ((manuf != MCP9808_MANUF_ID) || (device != MCP9808_DEVICE_ID))
return SENSOR_NOTFOUND;
return MSG_OK;
}
msg_t
MCP9808_setResolution(MCP9808_drv *drv, MCP9808_resolution_t res) {
struct __attribute__((packed)) {
uint8_t reg;
uint8_t resolution;
} tx = { MCP9808_REG_RESOLUTION, res };
msg_t msg;
if ((msg = i2c_send((uint8_t*)&tx, sizeof(tx))) < MSG_OK)
return msg;
drv->resolution = res;
return MSG_OK;
}
msg_t
MCP9808_start(MCP9808_drv *drv) {
drv->cfg &= ~(MCP9808_REG_CONFIG_SHUTDOWN);
return _apply_config(drv);
}
msg_t
MCP9808_stop(MCP9808_drv *drv) {
drv->cfg |= (MCP9808_REG_CONFIG_SHUTDOWN);
return _apply_config(drv);
}
unsigned int
MCP9808_getAcquisitionTime(MCP9808_drv *drv) {
switch(drv->resolution) {
case RES_2 : return MCP9808_DELAY_ACQUIRE_RES_2;
case RES_4 : return MCP9808_DELAY_ACQUIRE_RES_4;
case RES_8 : return MCP9808_DELAY_ACQUIRE_RES_8;
case RES_16: return MCP9808_DELAY_ACQUIRE_RES_16;
}
osalDbgAssert(false, "OOPS");
return 0;
}
msg_t
MCP9808_readMeasure(MCP9808_drv *drv,
float *temperature) {
msg_t msg;
uint16_t val;
if ((msg = i2c_reg_recv16_be(MCP9808_REG_AMBIENT_TEMP, &val)) < MSG_OK)
return msg;
return _decode_measure(drv, val, temperature);
}
msg_t
MCP9808_readTemperature(MCP9808_drv *drv,
float *temperature) {
osalDbgAssert(drv->state == SENSOR_STARTED, "invalid state");
msg_t msg;
uint16_t val;
if ((msg = i2c_reg_recv16_be(MCP9808_REG_AMBIENT_TEMP, &val)) < MSG_OK)
return msg;
return _decode_measure(drv, val, temperature);
}

View File

@ -0,0 +1,204 @@
/*
MCP9808 for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
Licensed 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 _SENSOR_MCP9808_H_
#define _SENSOR_MCP9808_H_
#include <math.h>
#include "i2c_helpers.h"
#include "sensor.h"
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
#define MCP9808_CONTINUOUS_ACQUISITION_SUPPORTED TRUE
#define MCP9808_I2CADDR_FIXED 0x18
/**
* @brief Time necessary for the sensor to boot
*/
#define MCP9808_BOOTUP_TIME 0
/**
* @brief Time necessary for the sensor to start
*/
#define MCP9808_STARTUP_TIME 0
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
#define MCP9808_I2CADDR_DEFAULT MCP9808_I2CADDR_FIXED
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief Different possible resolution
*/
typedef enum {
RES_2 = 0x00, /**< @brief Resolution of 1/2 = 0.5 */
RES_4 = 0x01, /**< @brief Resolution of 1/4 = 0.25 */
RES_8 = 0x10, /**< @brief Resolution of 1/8 = 0.125 */
RES_16 = 0x11, /**< @brief Resolution of 1/16 = 0.0625 */
} MCP9808_resolution_t;
/**
* @brief MCP9808 configuration structure.
*/
typedef struct {
I2CHelper i2c; /* keep it first */
} MCP9808_config;
/**
* @brief MCP9808 configuration structure.
*/
typedef struct {
MCP9808_config *config;
sensor_state_t state;
MCP9808_resolution_t resolution;
uint16_t cfg;
} MCP9808_drv;
/**
* @brief MCP9808 measure reading
*/
typedef struct {
float temperature;
} MCP9808_measure;
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
/**
* @brief Initialize the sensor driver
*/
void
MCP9808_init(MCP9808_drv *drv,
MCP9808_config *config);
/**
* @brief Check that the sensor is really present
*/
msg_t
MCP9808_check(MCP9808_drv *drv);
/**
* @brief Start the sensor
*/
msg_t
MCP9808_start(MCP9808_drv *drv);
/**
* @brief Stop the sensor
*
* @details If the sensor support it, it will be put in low energy mode.
*/
msg_t
MCP9808_stop(MCP9808_drv *drv);
/**
* @brief Control the MCP9809 resolution.
*/
msg_t
MCP9808_setResolution(MCP9808_drv *drv,
MCP9808_resolution_t res);
/**
* @brief Time in milli-seconds necessary for acquiring a naw measure
*
* @returns
* unsigned int time in millis-seconds
*/
unsigned int
MCP9808_getAcquisitionTime(MCP9808_drv *drv);
/**
* @brief Trigger a mesure acquisition
*/
static inline msg_t
MCP9808_startMeasure(MCP9808_drv *drv) {
(void)drv;
return MSG_OK;
}
/**
* @brief Read the newly acquiered measure
*
* @note According the the sensor design the measure read
* can be any value acquired after the acquisition time
* and the call to readMeasure.
*/
msg_t
MCP9808_readMeasure(MCP9808_drv *drv,
float *temperature);
/**
* @brief Read temperature and humidity
*
* @details According to the sensor specification/configuration
* (see #MCP9808_CONTINUOUS_ACQUISITION_SUPPORTED),
* if the sensor is doing continuous measurement
* it's value will be requested and returned immediately.
* Otherwise a measure is started, the necessary amount of
* time for acquiring the value is spend sleeping (not spinning),
* and finally the measure is read.
*
* @note In continuous measurement mode, if you just started
* the sensor, you will need to wait getAcquisitionTime()
* in addition to the usual getStartupTime()
* @note If using several sensors, it is better to start all the
* measure together, wait for the sensor having the longuest
* aquisition time, and finally read all the values
*/
msg_t
MCP9808_readTemperature(MCP9808_drv *drv,
float *temperature);
/**
* @brief Return the temperature value in °C.
*
* @note Prefere readTemperature(), if you need better error handling.
*
* @return The temperature in °C
* @retval float humidity percent
* @retval NAN on failure
*/
static inline float
MCP9808_getTemperature(MCP9808_drv *drv) {
float temperature = NAN;
MCP9808_readTemperature(drv, &temperature);
return temperature;
}
#endif

View File

@ -0,0 +1,81 @@
/*
Copyright (C) 2016 Stephane D'Alu
Licensed 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.
*/
/**
*
* Example of function calls.
*
* @code
* static SENSOR_config sensor_config = {
* };
* static SENSOR_drv sensor_drv;
* @endcode
*
*
* @code
* osalThreadSleepMilliseconds(SENSOR_BOOTUP_TIME);
* SENSOR_init(&sensor_drv);
* @endcode
*
* @code
* SENSOR_start(&sensor_drv, &sensor_config);
* osalThreadSleepMilliseconds(SENSOR_STARTUP_TIME);
* @endcode
*
* If using SENSOR_startMeasure()/SENSOR_readMeasure()
* @code
* while(true) {
* SENSOR_startMeasure(&sensor_drv);
* osalThreadSleepMilliseconds(SENSOR_getAcquisitionTime());
* SENSOR_readMeasure(&sensor_drv, ...);
* }
* @endcode
*
* If using SENSOR_readValue() or SENSOR_getValue()
* @code
* #if SENSOR_CONTINUOUS_ACQUISITION_SUPPORTED == TRUE
* osalThreadSleepMilliseconds(SENSOR_getAcquisitionTime())
* #endif
*
* while(true) {
* SENSOR_readValue(&sensor_drv, ...);
* }
* @encode
*/
#ifndef _SENSOR_H_
#define _SENSOR_H_
#define SENSOR_OK MSG_OK /**< @brief Operation successful. */
#define SENSOR_TIMEOUT MSG_TIMEOUT /**< @brief Communication timeout */
#define SENSOR_RESET MSG_REST /**< @brief Communication error. */
#define SENSOR_NOTFOUND (msg_t)-20 /**< @brief Sensor not found. */
/**
* @brief Driver state machine possible states.
*/
typedef enum __attribute__ ((__packed__)) {
SENSOR_UNINIT = 0, /**< Not initialized. */
SENSOR_INIT = 1, /**< Initialized. */
SENSOR_STARTED = 2, /**< Started. */
SENSOR_MEASURING = 4, /**< Measuring. */
SENSOR_READY = 3, /**< Ready. */
SENSOR_STOPPED = 5, /**< Stopped. */
SENSOR_ERROR = 6, /**< Error. */
} sensor_state_t;
#endif

View File

@ -0,0 +1,386 @@
/*
TSL2561 for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
Licensed 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.
*/
/**
* Illuminance calculation code provided by www.taosinc.com
* DOC: http://ams.com/eng/content/download/250096/975518/143687
*/
#define I2C_HELPERS_AUTOMATIC_DRV TRUE
#include "hal.h"
#include "i2c_helpers.h"
#include "tsl2561.h"
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
// Integration time in µs
#define TSL2561_DELAY_INTTIME_SHORT 13700 // 13.7 ms
#define TSL2561_DELAY_INTTIME_MEDIUM 120000 // 120.0 ms
#define TSL2561_DELAY_INTTIME_LONG 450000 // 450.0 ms
#define TSL2561_COMMAND_BIT (0x80)
#define TSL2561_CLEAR_BIT (0x40)
#define TSL2561_WORD_BIT (0x20)
#define TSL2561_BLOCK_BIT (0x10)
#define TSL2561_CONTROL_POWERON (0x03)
#define TSL2561_CONTROL_POWEROFF (0x00)
#define TSL2561_LUX_LUXSCALE (14)
#define TSL2561_LUX_RATIOSCALE (9)
#define TSL2561_LUX_CHSCALE (10) // Scale channel values by 2^10
#define TSL2561_LUX_CHSCALE_TINT0 (0x7517) // 322/11 * 2^TSL2561_LUX_CHSCALE
#define TSL2561_LUX_CHSCALE_TINT1 (0x0FE7) // 322/81 * 2^TSL2561_LUX_CHSCALE
// I2C Register
#define TSL2561_REG_CONTROL 0x00
#define TSL2561_REG_TIMING 0x01
#define TSL2561_REG_THRESHHOLDLLOW 0x02
#define TSL2561_REG_THRESHHOLDLHIGH 0x03
#define TSL2561_REG_THRESHHOLDHLOW 0x04
#define TSL2561_REG_THRESHHOLDHHIGH 0x05
#define TSL2561_REG_INTERRUPT 0x06
#define TSL2561_REG_CRC 0x08
#define TSL2561_REG_ID 0x0A
#define TSL2561_REG_DATA0LOW 0x0C
#define TSL2561_REG_DATA0HIGH 0x0D
#define TSL2561_REG_DATA1LOW 0x0E
#define TSL2561_REG_DATA1HIGH 0x0F
// Auto-gain thresholds
#define TSL2561_AGC_THI_SHORT (4850) // Max value at Ti 13ms = 5047
#define TSL2561_AGC_TLO_SHORT (100)
#define TSL2561_AGC_THI_MEDIUM (36000) // Max value at Ti 101ms = 37177
#define TSL2561_AGC_TLO_MEDIUM (200)
#define TSL2561_AGC_THI_LONG (63000) // Max value at Ti 402ms = 65535
#define TSL2561_AGC_TLO_LONG (500)
// Clipping thresholds
#define TSL2561_CLIPPING_SHORT (4900)
#define TSL2561_CLIPPING_MEDIUM (37000)
#define TSL2561_CLIPPING_LONG (65000)
// T, FN and CL package values
#define TSL2561_LUX_K1T (0x0040) // 0.125 * 2^RATIO_SCALE
#define TSL2561_LUX_B1T (0x01f2) // 0.0304 * 2^LUX_SCALE
#define TSL2561_LUX_M1T (0x01be) // 0.0272 * 2^LUX_SCALE
#define TSL2561_LUX_K2T (0x0080) // 0.250 * 2^RATIO_SCALE
#define TSL2561_LUX_B2T (0x0214) // 0.0325 * 2^LUX_SCALE
#define TSL2561_LUX_M2T (0x02d1) // 0.0440 * 2^LUX_SCALE
#define TSL2561_LUX_K3T (0x00c0) // 0.375 * 2^RATIO_SCALE
#define TSL2561_LUX_B3T (0x023f) // 0.0351 * 2^LUX_SCALE
#define TSL2561_LUX_M3T (0x037b) // 0.0544 * 2^LUX_SCALE
#define TSL2561_LUX_K4T (0x0100) // 0.50 * 2^RATIO_SCALE
#define TSL2561_LUX_B4T (0x0270) // 0.0381 * 2^LUX_SCALE
#define TSL2561_LUX_M4T (0x03fe) // 0.0624 * 2^LUX_SCALE
#define TSL2561_LUX_K5T (0x0138) // 0.61 * 2^RATIO_SCALE
#define TSL2561_LUX_B5T (0x016f) // 0.0224 * 2^LUX_SCALE
#define TSL2561_LUX_M5T (0x01fc) // 0.0310 * 2^LUX_SCALE
#define TSL2561_LUX_K6T (0x019a) // 0.80 * 2^RATIO_SCALE
#define TSL2561_LUX_B6T (0x00d2) // 0.0128 * 2^LUX_SCALE
#define TSL2561_LUX_M6T (0x00fb) // 0.0153 * 2^LUX_SCALE
#define TSL2561_LUX_K7T (0x029a) // 1.3 * 2^RATIO_SCALE
#define TSL2561_LUX_B7T (0x0018) // 0.00146 * 2^LUX_SCALE
#define TSL2561_LUX_M7T (0x0012) // 0.00112 * 2^LUX_SCALE
#define TSL2561_LUX_K8T (0x029a) // 1.3 * 2^RATIO_SCALE
#define TSL2561_LUX_B8T (0x0000) // 0.000 * 2^LUX_SCALE
#define TSL2561_LUX_M8T (0x0000) // 0.000 * 2^LUX_SCALE
// CS package values
#define TSL2561_LUX_K1C (0x0043) // 0.130 * 2^RATIO_SCALE
#define TSL2561_LUX_B1C (0x0204) // 0.0315 * 2^LUX_SCALE
#define TSL2561_LUX_M1C (0x01ad) // 0.0262 * 2^LUX_SCALE
#define TSL2561_LUX_K2C (0x0085) // 0.260 * 2^RATIO_SCALE
#define TSL2561_LUX_B2C (0x0228) // 0.0337 * 2^LUX_SCALE
#define TSL2561_LUX_M2C (0x02c1) // 0.0430 * 2^LUX_SCALE
#define TSL2561_LUX_K3C (0x00c8) // 0.390 * 2^RATIO_SCALE
#define TSL2561_LUX_B3C (0x0253) // 0.0363 * 2^LUX_SCALE
#define TSL2561_LUX_M3C (0x0363) // 0.0529 * 2^LUX_SCALE
#define TSL2561_LUX_K4C (0x010a) // 0.520 * 2^RATIO_SCALE
#define TSL2561_LUX_B4C (0x0282) // 0.0392 * 2^LUX_SCALE
#define TSL2561_LUX_M4C (0x03df) // 0.0605 * 2^LUX_SCALE
#define TSL2561_LUX_K5C (0x014d) // 0.65 * 2^RATIO_SCALE
#define TSL2561_LUX_B5C (0x0177) // 0.0229 * 2^LUX_SCALE
#define TSL2561_LUX_M5C (0x01dd) // 0.0291 * 2^LUX_SCALE
#define TSL2561_LUX_K6C (0x019a) // 0.80 * 2^RATIO_SCALE
#define TSL2561_LUX_B6C (0x0101) // 0.0157 * 2^LUX_SCALE
#define TSL2561_LUX_M6C (0x0127) // 0.0180 * 2^LUX_SCALE
#define TSL2561_LUX_K7C (0x029a) // 1.3 * 2^RATIO_SCALE
#define TSL2561_LUX_B7C (0x0037) // 0.00338 * 2^LUX_SCALE
#define TSL2561_LUX_M7C (0x002b) // 0.00260 * 2^LUX_SCALE
#define TSL2561_LUX_K8C (0x029a) // 1.3 * 2^RATIO_SCALE
#define TSL2561_LUX_B8C (0x0000) // 0.000 * 2^LUX_SCALE
#define TSL2561_LUX_M8C (0x0000) // 0.000 * 2^LUX_SCALE
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
#define CEILING(x,y) (((x) + (y) - 1) / (y))
static inline unsigned int
calculateIlluminance(TSL2561_integration_time_t integration_time,
TSL2561_gain_t gain,
uint16_t broadband, uint16_t ir,
unsigned int partno) {
unsigned long channel_1;
unsigned long channel_0;
/* Get value for channel scaling, and clipping */
uint16_t clip_threshold = 0;
unsigned long channel_scale = 0;
switch (integration_time) {
case TSL2561_INTEGRATIONTIME_SHORT:
clip_threshold = TSL2561_CLIPPING_SHORT;
channel_scale = TSL2561_LUX_CHSCALE_TINT0;
break;
case TSL2561_INTEGRATIONTIME_MEDIUM:
clip_threshold = TSL2561_CLIPPING_MEDIUM;
channel_scale = TSL2561_LUX_CHSCALE_TINT1;
break;
case TSL2561_INTEGRATIONTIME_LONG:
clip_threshold = TSL2561_CLIPPING_LONG;
channel_scale = (1 << TSL2561_LUX_CHSCALE);
break;
default:
// assert failed
break;
}
/* Check for saturated sensor (ie: clipping) */
if ((broadband > clip_threshold) || (ir > clip_threshold)) {
return TSL2561_OVERLOADED;
}
/* Scale for gain (1x or 16x) */
if (gain == TSL2561_GAIN_1X)
channel_scale <<= 4;
/* Scale the channel values */
channel_0 = (broadband * channel_scale) >> TSL2561_LUX_CHSCALE;
channel_1 = (ir * channel_scale) >> TSL2561_LUX_CHSCALE;
/* Find the ratio of the channel values (Channel_1/Channel_0) */
unsigned long _ratio = 0;
if (channel_0 != 0)
_ratio = (channel_1 << (TSL2561_LUX_RATIOSCALE+1)) / channel_0;
unsigned long ratio = (_ratio + 1) >> 1; /* round the ratio value */
/* Find linear approximation */
unsigned int b = 0;
unsigned int m = 0;
switch (partno) {
#if TSL2561_WITH_CS
case 0x1: // 0001 = TSL2561 CS
if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1C))
{ b=TSL2561_LUX_B1C; m=TSL2561_LUX_M1C; }
else if (ratio <= TSL2561_LUX_K2C)
{ b=TSL2561_LUX_B2C; m=TSL2561_LUX_M2C; }
else if (ratio <= TSL2561_LUX_K3C)
{ b=TSL2561_LUX_B3C; m=TSL2561_LUX_M3C; }
else if (ratio <= TSL2561_LUX_K4C)
{ b=TSL2561_LUX_B4C; m=TSL2561_LUX_M4C; }
else if (ratio <= TSL2561_LUX_K5C)
{ b=TSL2561_LUX_B5C; m=TSL2561_LUX_M5C; }
else if (ratio <= TSL2561_LUX_K6C)
{ b=TSL2561_LUX_B6C; m=TSL2561_LUX_M6C; }
else if (ratio <= TSL2561_LUX_K7C)
{ b=TSL2561_LUX_B7C; m=TSL2561_LUX_M7C; }
else if (ratio > TSL2561_LUX_K8C)
{ b=TSL2561_LUX_B8C; m=TSL2561_LUX_M8C; }
break;
#endif
#if TSL2561_WITH_T_FN_CL
case 0x5: // 0101 = TSL2561 T/FN/CL
if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1T))
{ b=TSL2561_LUX_B1T; m=TSL2561_LUX_M1T; }
else if (ratio <= TSL2561_LUX_K2T)
{ b=TSL2561_LUX_B2T; m=TSL2561_LUX_M2T; }
else if (ratio <= TSL2561_LUX_K3T)
{ b=TSL2561_LUX_B3T; m=TSL2561_LUX_M3T; }
else if (ratio <= TSL2561_LUX_K4T)
{ b=TSL2561_LUX_B4T; m=TSL2561_LUX_M4T; }
else if (ratio <= TSL2561_LUX_K5T)
{ b=TSL2561_LUX_B5T; m=TSL2561_LUX_M5T; }
else if (ratio <= TSL2561_LUX_K6T)
{ b=TSL2561_LUX_B6T; m=TSL2561_LUX_M6T; }
else if (ratio <= TSL2561_LUX_K7T)
{ b=TSL2561_LUX_B7T; m=TSL2561_LUX_M7T; }
else if (ratio > TSL2561_LUX_K8T)
{ b=TSL2561_LUX_B8T; m=TSL2561_LUX_M8T; }
break;
#endif
default:
// assert failed
break;
}
/* Compute illuminance */
long ill = ((channel_0 * b) - (channel_1 * m));
if (ill < 0) ill = 0; /* Do not allow negative lux value */
ill += (1 << (TSL2561_LUX_LUXSCALE-1)); /* Round lsb (2^(LUX_SCALE-1)) */
ill >>= TSL2561_LUX_LUXSCALE; /* Strip fractional part */
/* Signal I2C had no errors */
return ill;
}
static inline msg_t
_readChannel(TSL2561_drv *drv, uint16_t *broadband, uint16_t *ir) {
msg_t msg;
if (((msg = i2c_reg_recv16_le(
TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REG_DATA0LOW,
broadband)) < MSG_OK) ||
((msg = i2c_reg_recv16_le(
TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REG_DATA1LOW,
ir )) < MSG_OK))
return msg;
return MSG_OK;
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
void
TSL2561_init(TSL2561_drv *drv, TSL2561_config *config) {
drv->config = config;
drv->gain = TSL2561_GAIN_1X;
drv->integration_time = TSL2561_INTEGRATIONTIME_LONG;
drv->state = SENSOR_INIT;
i2c_reg_recv8(TSL2561_COMMAND_BIT | TSL2561_REG_ID,
(uint8_t*)&drv->id);
}
msg_t
TSL2561_check(TSL2561_drv *drv) {
uint8_t rx;
msg_t msg;
if ((msg = i2c_reg_recv8(TSL2561_REG_ID, &rx)) < MSG_OK)
return msg;
if (!(rx & 0x0A))
return SENSOR_NOTFOUND;
return MSG_OK;
}
msg_t
TSL2561_stop(TSL2561_drv *drv) {
struct __attribute__((packed)) {
uint8_t reg;
uint8_t conf;
} tx = { TSL2561_COMMAND_BIT | TSL2561_REG_CONTROL,
TSL2561_CONTROL_POWEROFF };
return i2c_send((uint8_t*)&tx, sizeof(tx));
}
msg_t
TSL2561_start(TSL2561_drv *drv) {
struct __attribute__((packed)) {
uint8_t reg;
uint8_t conf;
} tx = { TSL2561_COMMAND_BIT | TSL2561_REG_CONTROL,
TSL2561_CONTROL_POWERON };
return i2c_send((uint8_t*)&tx, sizeof(tx));
}
msg_t
TSL2561_setIntegrationTime(TSL2561_drv *drv,
TSL2561_integration_time_t time) {
struct __attribute__((packed)) {
uint8_t reg;
uint8_t conf;
} tx = { TSL2561_COMMAND_BIT | TSL2561_REG_TIMING,
(uint8_t)(time | drv->gain) };
msg_t msg;
if ((msg = i2c_send((uint8_t*)&tx, sizeof(tx))) < MSG_OK)
return msg;
drv->integration_time = time;
return MSG_OK;
}
msg_t
TSL2561_setGain(TSL2561_drv *drv,
TSL2561_gain_t gain) {
struct __attribute__((packed)) {
uint8_t reg;
uint8_t conf;
} tx = { TSL2561_COMMAND_BIT | TSL2561_REG_TIMING,
(uint8_t)(drv->integration_time | gain) };
msg_t msg;
if ((msg = i2c_send((uint8_t*)&tx, sizeof(tx))) < MSG_OK)
return msg;
drv->gain = gain;
return MSG_OK;
}
unsigned int
TSL2561_getAcquisitionTime(TSL2561_drv *drv) {
switch (drv->integration_time) {
case TSL2561_INTEGRATIONTIME_SHORT:
return CEILING(TSL2561_DELAY_INTTIME_SHORT , 1000);
case TSL2561_INTEGRATIONTIME_MEDIUM:
return CEILING(TSL2561_DELAY_INTTIME_MEDIUM, 1000);
case TSL2561_INTEGRATIONTIME_LONG:
return CEILING(TSL2561_DELAY_INTTIME_LONG , 1000);
}
return -1;
}
msg_t
TSL2561_readIlluminance(TSL2561_drv *drv,
unsigned int *illuminance) {
uint16_t broadband;
uint16_t ir;
/* Read channels */
msg_t msg;
if ((msg = _readChannel(drv, &broadband, &ir)) < MSG_OK)
return msg;
/* Calculate illuminance */
*illuminance =
calculateIlluminance(drv->integration_time, drv->gain,
broadband, ir, drv->id.partno);
/* Ok */
return SENSOR_OK;
}

View File

@ -0,0 +1,241 @@
/*
TSL2561 for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
Licensed 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.
*/
/**
* @file tsl2561.h
* @brief TSL2561 Light sensor interface module header.
*
* @{
*/
#ifndef _SENSOR_TSL2561_H_
#define _SENSOR_TSL2561_H_
#include <math.h>
#include "i2c_helpers.h"
#include "sensor.h"
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
#define TSL2561_CONTINUOUS_ACQUISITION_SUPPORTED TRUE
#define TSL2561_OVERLOADED (-1)
/* I2C address */
#define TSL2561_I2CADDR_LOW (0x29)
#define TSL2561_I2CADDR_FLOAT (0x39)
#define TSL2561_I2CADDR_HIGH (0x49)
/**
* @brief Time necessary for the sensor to boot
*/
#define TSL2561_BOOTUP_TIME 0
/**
* @brief Time necessary for the sensor to start
*/
#define TSL2561_STARTUP_TIME 0
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
#ifndef TSL2561_WITH_CS
#define TSL2561_WITH_CS 0
#endif
#ifndef TSL2561_WITH_T_FN_CL
#define TSL2561_WITH_T_FN_CL 1
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
#define TSL2561_I2CADDR_DEFAULT TSL2561_I2CADDR_FLOAT
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief TSL2561 configuration structure.
*/
typedef struct {
I2CHelper i2c; /* keep it first */
} TSL2561_config;
/**
* @brief Available integration time
*
* @details Available integration time are:
* 13.7ms, 101ms, 402ms
*/
typedef enum {
TSL2561_INTEGRATIONTIME_SHORT = 0x00, /**< @brief 13.7ms */
TSL2561_INTEGRATIONTIME_MEDIUM = 0x01, /**< @brief 101.0ms */
TSL2561_INTEGRATIONTIME_LONG = 0x02, /**< @brief 402.0ms */
} TSL2561_integration_time_t;
/**
* @brief Available gain
*
* @details Available gain are 1x, 16x
*/
typedef enum {
TSL2561_GAIN_1X = 0x00, /**< @brief 1x gain */
TSL2561_GAIN_16X = 0x10, /**< @brief 16x gain */
} TSL2561_gain_t;
/**
* @brief TSL2561 configuration structure.
*/
typedef struct {
TSL2561_config *config;
sensor_state_t state;
TSL2561_gain_t gain;
TSL2561_integration_time_t integration_time;
struct PACKED {
uint8_t revno : 4;
uint8_t partno : 4; } id;
} TSL2561_drv;
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
/**
* @brief Initialize the sensor driver
*/
void
TSL2561_init(TSL2561_drv *drv,
TSL2561_config *config);
/**
* @brief Start the sensor
*/
msg_t
TSL2561_start(TSL2561_drv *drv);
/**
* @brief Stop the sensor
*
* @details If the sensor support it, it will be put in low energy mode.
*/
msg_t
TSL2561_stop(TSL2561_drv *drv);
/**
* @brief Check that the sensor is really present
*/
msg_t
TSL2561_check(TSL2561_drv *drv);
/**
* @brief Time in milli-seconds necessary for acquiring a naw measure
*
* @returns
* unsigned int time in millis-seconds
*/
unsigned int
TSL2561_getAcquisitionTime(TSL2561_drv *drv);
/**
* @brief Trigger a mesure acquisition
*/
static inline msg_t
TSL2561_startMeasure(TSL2561_drv *drv) {
(void)drv;
return MSG_OK;
};
/**
* @brief Read the newly acquiered measure
*
* @note According the the sensor design the measure read
* can be any value acquired after the acquisition time
* and the call to readMeasure.
*/
msg_t
TSL2561_readMeasure(TSL2561_drv *drv,
unsigned int illuminance);
msg_t
TSL2561_setGain(TSL2561_drv *drv,
TSL2561_gain_t gain);
msg_t
TSL2561_setIntegrationTime(TSL2561_drv *drv,
TSL2561_integration_time_t time);
/**
* @brief Read temperature and humidity
*
* @details According to the sensor specification/configuration
* (see #TSL2561_CONTINUOUS_ACQUISITION_SUPPORTED),
* if the sensor is doing continuous measurement
* it's value will be requested and returned immediately.
* Otherwise a measure is started, the necessary amount of
* time for acquiring the value is spend sleeping (not spinning),
* and finally the measure is read.
*
* @note In continuous measurement mode, if you just started
* the sensor, you will need to wait getAcquisitionTime()
* in addition to the usual getStartupTime()
* @note If using several sensors, it is better to start all the
* measure together, wait for the sensor having the longuest
* aquisition time, and finally read all the values
*/
msg_t
TSL2561_readIlluminance(TSL2561_drv *drv,
unsigned int *illuminance);
/**
* @brief Return the illuminance value in Lux
*
* @details Use readIlluminance() for returning the humidity value.
*
* @note Prefere readIlluminance()if you need better error handling.
*
* @return Illuminance in Lux
* @retval unsigned int illuminace value
* @retval -1 on failure
*/
static inline unsigned int
TSL2561_getIlluminance(TSL2561_drv *drv) {
unsigned int illuminance = -1;
TSL2561_readIlluminance(drv, &illuminance);
return illuminance;
}
#endif

View File

@ -0,0 +1,272 @@
/*
TSL2591 for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
Licensed 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.
*/
/**
*
* DOC: http://ams.com/eng/content/download/389383/1251117/221235
*/
#define I2C_HELPERS_AUTOMATIC_DRV TRUE
#include "hal.h"
#include "i2c_helpers.h"
#include "tsl2591.h"
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
#define TSL2591_LUX_DF (408.0F)
#define TSL2591_LUX_COEFB (1.64F) // CH0 coefficient
#define TSL2591_LUX_COEFC (0.59F) // CH1 coefficient A
#define TSL2591_LUX_COEFD (0.86F) // CH2 coefficient B
/* I2C registers */
#define TSL2591_REG_ENABLE 0x00
#define TSL2591_REG_CONFIG 0x01 /**< @brief gain and integration */
#define TSL2591_REG_AILTL 0x04
#define TSL2591_REG_AILTH 0x05
#define TSL2591_REG_AIHTL 0x06
#define TSL2591_REG_AIHTH 0x07
#define TSL2591_REG_NPAILTL 0x08
#define TSL2591_REG_NPAILTH 0x09
#define TSL2591_REG_NPAIHTL 0x0A
#define TSL2591_REG_NPAIHTH 0x0B
#define TSL2591_REG_PERSIST 0x0C
#define TSL2591_REG_PID 0x11 /**< @brief Package ID */
#define TSL2591_REG_ID 0x12 /**< @brief Device ID */
#define TSL2591_REG_STATUS 0x13 /**< @brief Device status */
#define TSL2591_REG_C0DATAL 0x14 /**< @brief CH0 ADC low data byte */
#define TSL2591_REG_C0DATAH 0x15 /**< @brief CH0 ADC high data byte */
#define TSL2591_REG_C1DATAL 0x16 /**< @brief CH1 ADC low data byte */
#define TSL2591_REG_C1DATAH 0x17 /**< @brief CH1 ADC high data byte */
#define TSL2591_REG_COMMAND 0x80 /**< @brief Select command register */
#define TSL2591_REG_NORMAL 0x20 /**< @brief Normal opearation */
#define TSL2591_REG_SPECIAL 0x60 /**< @brief Special function */
#define TSL2591_ID_TSL2591 0x50
#define TSL2591_VISIBLE (2) // channel 0 - channel 1
#define TSL2591_INFRARED (1) // channel 1
#define TSL2591_FULLSPECTRUM (0) // channel 0
#define TSL2591_ENABLE_POWERON (0x01)
#define TSL2591_ENABLE_POWEROFF (0x00)
#define TSL2591_ENABLE_AEN (0x02)
#define TSL2591_ENABLE_AIEN (0x10)
#define TSL2591_CONTROL_RESET (0x80)
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
static inline uint32_t
calculateIlluminance(TSL2591_integration_time_t integration_time,
TSL2591_gain_t gain,
uint16_t broadband, uint16_t ir) {
uint16_t atime, again;
/* Check for overflow conditions first */
if ((broadband == 0xFFFF) | (ir == 0xFFFF)) {
return 0xFFFFFFFF; /* Signal overflow */
}
switch (integration_time) {
case TSL2591_INTEGRATIONTIME_100MS : atime = 100; break;
case TSL2591_INTEGRATIONTIME_200MS : atime = 200; break;
case TSL2591_INTEGRATIONTIME_300MS : atime = 300; break;
case TSL2591_INTEGRATIONTIME_400MS : atime = 400; break;
case TSL2591_INTEGRATIONTIME_500MS : atime = 500; break;
case TSL2591_INTEGRATIONTIME_600MS : atime = 600; break;
}
switch (gain) {
case TSL2591_GAIN_1X : again = 1; break;
case TSL2591_GAIN_25X : again = 25; break;
case TSL2591_GAIN_415X : again = 415; break;
case TSL2591_GAIN_10000X : again = 10000; break;
}
// cpl = (ATIME * AGAIN) / DF
float cpl = ((float)(atime * again)) / ((float)TSL2591_LUX_DF);
float lux1 = ( ((float)broadband) - (TSL2591_LUX_COEFB * (float)ir) ) / cpl;
float lux2 = ( (TSL2591_LUX_COEFC * (float)broadband) -
(TSL2591_LUX_COEFD * (float)ir ) ) / cpl;
return (uint32_t) (lux1 > lux2 ? lux1 : lux2);
}
static inline msg_t
_readChannel(TSL2591_drv *drv, uint16_t *broadband, uint16_t *ir) {
msg_t msg;
if (((msg = i2c_reg_recv16_le(
TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_C0DATAL,
broadband)) < MSG_OK) ||
((msg = i2c_reg_recv16_le(
TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_C1DATAL,
ir )) < MSG_OK))
return msg;
return MSG_OK;
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
void
TSL2591_init(TSL2591_drv *drv, TSL2591_config *config) {
drv->config = config;
drv->gain = TSL2591_GAIN_1X;
drv->integration_time = TSL2591_INTEGRATIONTIME_100MS;
drv->state = SENSOR_INIT;
}
msg_t
TSL2591_check(TSL2591_drv *drv) {
uint8_t id;
msg_t msg;
if ((msg = i2c_reg_recv8(TSL2591_REG_COMMAND | TSL2591_REG_NORMAL |
TSL2591_REG_ID, &id)) < MSG_OK)
return msg;
if (id != TSL2591_ID_TSL2591)
return SENSOR_NOTFOUND;
return MSG_OK;
}
msg_t
TSL2591_start(TSL2591_drv *drv) {
struct __attribute__((packed)) {
uint8_t reg;
uint8_t conf;
} tx_config = {
TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_CONFIG,
(uint8_t)(drv->integration_time | drv->gain) };
struct __attribute__((packed)) {
uint8_t reg;
uint8_t conf;
} tx_start = {
TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_ENABLE,
TSL2591_ENABLE_POWERON };
msg_t msg;
if (((msg = i2c_send((uint8_t*)&tx_config, sizeof(tx_config))) < MSG_OK) ||
((msg = i2c_send((uint8_t*)&tx_start, sizeof(tx_start ))) < MSG_OK)) {
drv->state = SENSOR_ERROR;
return msg;
}
drv->state = SENSOR_STARTED;
return MSG_OK;
}
msg_t
TSL2591_stop(TSL2591_drv *drv) {
struct __attribute__((packed)) {
uint8_t reg;
uint8_t conf;
} tx_stop = {
TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_ENABLE,
TSL2591_ENABLE_POWEROFF };
return i2c_send((uint8_t*)&tx_stop, sizeof(tx_stop));
}
msg_t
TSL2591_setIntegrationTime(TSL2591_drv *drv,
TSL2591_integration_time_t time) {
struct __attribute__((packed)) {
uint8_t reg;
uint8_t conf;
} tx = { TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_CONFIG,
(uint8_t)(time | drv->gain) };
msg_t msg;
if ((msg = i2c_send((uint8_t*)&tx, sizeof(tx))) < MSG_OK)
return msg;
drv->integration_time = time;
return MSG_OK;
}
msg_t
TSL2591_setGain(TSL2591_drv *drv,
TSL2591_gain_t gain) {
struct __attribute__((packed)) {
uint8_t reg;
uint8_t conf;
} tx = { TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_CONFIG,
(uint8_t)(drv->integration_time | gain) };
msg_t msg;
if ((msg = i2c_send((uint8_t*)&tx, sizeof(tx))) < MSG_OK)
return msg;
drv->gain = gain;
return MSG_OK;
}
unsigned int
TSL2591_getAcquisitionTime(TSL2591_drv *drv) {
switch (drv->integration_time) {
case TSL2591_INTEGRATIONTIME_100MS : return 100;
case TSL2591_INTEGRATIONTIME_200MS : return 200;
case TSL2591_INTEGRATIONTIME_300MS : return 300;
case TSL2591_INTEGRATIONTIME_400MS : return 400;
case TSL2591_INTEGRATIONTIME_500MS : return 500;
case TSL2591_INTEGRATIONTIME_600MS : return 600;
}
return -1;
}
msg_t
TSL2591_readIlluminance(TSL2591_drv *drv,
unsigned int *illuminance) {
uint16_t broadband;
uint16_t ir;
/* Read channels */
msg_t msg;
if ((msg = _readChannel(drv, &broadband, &ir)) < MSG_OK)
return msg;
/* Calculate illuminance */
*illuminance =
calculateIlluminance(drv->integration_time, drv->gain,
broadband, ir);
/* Ok */
return SENSOR_OK;
}

View File

@ -0,0 +1,238 @@
/*
TSL2591 for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
Licensed 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.
*/
/**
* @file tsl2591.h
* @brief TSL2591 Light sensor interface module header.
*
* @{
*/
#ifndef _SENSOR_TSL2591_H_
#define _SENSOR_TSL2591_H_
#include <math.h>
#include "i2c_helpers.h"
#include "sensor.h"
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/**
* @brief Device sensor continuous acquisition support.
*/
#define TSL2591_CONTINUOUS_ACQUISITION_SUPPORTED TRUE
/**
* @brief I2C address.
*/
#define TSL2591_I2CADDR_FIXED 0x29
/**
* @brief Time necessary for the sensor to boot
*/
#define TSL2591_BOOTUP_TIME 0
/**
* @brief Time necessary for the sensor to start
*/
#define TSL2591_STARTUP_TIME 0
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/**
* @brief Default I2C address (when pin unconfigured)
*/
#define TSL2591_I2CADDR_DEFAULT TSL2591_I2CADDR_FIXED
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief TSL2591 configuration structure.
*/
typedef struct {
I2CHelper i2c; /* keep it first */
} TSL2591_config;
/**
* @brief Available integration time
*
* @details Available integration time are:
* 100ms, 200ms, 300ms, 400ms, 500ms and 600ms
*/
typedef enum {
TSL2591_INTEGRATIONTIME_100MS = 0x00, /**< @brief 100ms */
TSL2591_INTEGRATIONTIME_200MS = 0x01, /**< @brief 200ms */
TSL2591_INTEGRATIONTIME_300MS = 0x02, /**< @brief 300ms */
TSL2591_INTEGRATIONTIME_400MS = 0x03, /**< @brief 400ms */
TSL2591_INTEGRATIONTIME_500MS = 0x04, /**< @brief 500ms */
TSL2591_INTEGRATIONTIME_600MS = 0x05, /**< @brief 600ms */
} TSL2591_integration_time_t;
/**
* @brief Available gain
*
* @details Available gain are 1x, 25x, 415x, 10000x
*/
typedef enum {
TSL2591_GAIN_1X = 0x00, /**< @brief 1x gain */
TSL2591_GAIN_25X = 0x10, /**< @brief 25x gain */
TSL2591_GAIN_415X = 0x20, /**< @brief 415x gain */
TSL2591_GAIN_10000X = 0x30, /**< @brief 10000x gain */
} TSL2591_gain_t;
/**
* @brief TSL2591 configuration structure.
*/
typedef struct {
TSL2591_config *config;
sensor_state_t state;
TSL2591_gain_t gain;
TSL2591_integration_time_t integration_time;
} TSL2591_drv;
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
/**
* @brief Initialize the sensor driver
*/
void
TSL2591_init(TSL2591_drv *drv,
TSL2591_config *config);
/**
* @brief Start the sensor
*/
msg_t
TSL2591_start(TSL2591_drv *drv);
/**
* @brief Stop the sensor
*
* @details If the sensor support it, it will be put in low energy mode.
*/
msg_t
TSL2591_stop(TSL2591_drv *drv);
/**
* @brief Check that the sensor is really present
*/
msg_t
TSL2591_check(TSL2591_drv *drv);
/**
* @brief Time in milli-seconds necessary for acquiring a naw measure
*
* @returns
* unsigned int time in millis-seconds
*/
unsigned int
TSL2591_getAcquisitionTime(TSL2591_drv *drv);
/**
* @brief Trigger a mesure acquisition
*/
static inline msg_t
TSL2591_startMeasure(TSL2591_drv *drv) {
(void)drv;
return MSG_OK;
};
msg_t
TSL2591_setGain(TSL2591_drv *drv,
TSL2591_gain_t gain);
msg_t
TSL2591_setIntegrationTime(TSL2591_drv *drv,
TSL2591_integration_time_t time);
/**
* @brief Read the newly acquiered measure
*
* @note According the the sensor design the measure read
* can be any value acquired after the acquisition time
* and the call to readMeasure.
*/
msg_t
TSL2591_readMeasure(TSL2591_drv *drv,
unsigned int illuminance);
/**
* @brief Read temperature and humidity
*
* @details According to the sensor specification/configuration
* (see #TSL2591_CONTINUOUS_ACQUISITION_SUPPORTED),
* if the sensor is doing continuous measurement
* it's value will be requested and returned immediately.
* Otherwise a measure is started, the necessary amount of
* time for acquiring the value is spend sleeping (not spinning),
* and finally the measure is read.
*
* @note In continuous measurement mode, if you just started
* the sensor, you will need to wait getAcquisitionTime()
* in addition to the usual getStartupTime()
* @note If using several sensors, it is better to start all the
* measure together, wait for the sensor having the longuest
* aquisition time, and finally read all the values
*/
msg_t
TSL2591_readIlluminance(TSL2591_drv *drv,
unsigned int *illuminance);
/**
* @brief Return the illuminance value in Lux
*
* @details Use readIlluminance() for returning the humidity value.
*
* @note Prefere readIlluminance()if you need better error handling.
*
* @return Illuminance in Lux
* @retval unsigned int illuminace value
* @retval -1 on failure
*/
static inline unsigned int
TSL2591_getIlluminance(TSL2591_drv *drv) {
unsigned int illuminance = -1;
TSL2591_readIlluminance(drv, &illuminance);
return illuminance;
}
#endif

283
os/various/i2c_helpers.h Normal file
View File

@ -0,0 +1,283 @@
/*
Copyright (C) 2016 Stephane D'Alu
Licensed 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 I2C_HELPERS_H
#define I2C_HELPERS_H
#include "hal.h"
#include "bswap.h"
typedef struct {
/**
* @brief Pointer to the I2C driver.
*/
I2CDriver *driver;
/**
* @brief I2C address.
*/
i2caddr_t addr;
} I2CHelper;
#if !defined(I2C_HELPERS_AUTOMATIC_DRV) || (I2C_HELPERS_AUTOMATIC_DRV == FALSE)
#define i2c_send(i2c, txbuf, txbytes) \
_i2c_send(i2c, txbuf, txbytes)
#define i2c_transmit(i2c, txbuf, txbytes, rxbuf, rxbytes) \
_i2c_transmit(i2c, txbuf, txbytes, rxbuf, rxbytes)
#define i2c_receive(i2, rxbuf, rxbytes) \
_i2c_receive(i2c, rxbuf, rxbytes)
#define i2c_send_timeout(i2c, txbuf, txbytes) \
_i2c_send(i2c, txbuf, txbytes)
#define i2c_transmit_timeout(i2c, txbuf, txbytes, rxbuf, rxbytes) \
_i2c_transmit(i2c, txbuf, txbytes, rxbuf, rxbytes)
#define i2c_receive_timeout(i2, rxbuf, rxbytes) \
_i2c_receive(i2c, rxbuf, rxbytes)
#define i2c_reg(i2c, reg) \
_i2c_reg(i2c, reg)
#define i2c_reg_recv8(i2c, reg, val) \
_i2c_reg_recv8(i2c, reg, val)
#define i2c_reg_recv16(i2c, reg, val) \
_i2c_reg_recv16(i2c, reg, val)
#define i2c_reg_recv16_le(i2c, reg, val) \
_i2c_reg_recv16_le(i2c, reg, val)
#define i2c_reg_recv16_be(i2c, reg, val) \
_i2c_reg_recv16_be(i2c, reg, val)
#define i2c_reg_recv32(i2c, reg, val) \
_i2c_reg_recv32(i2c, reg, val)
#define i2c_reg_recv32_le(i2c, reg, val) \
_i2c_reg_recv32_le(i2c, reg, val)
#define i2c_reg_recv32_be(i2c, reg, val) \
_i2c_reg_recv32_be(i2c, reg, val)
#define i2c_recv8(i2c, val) \
_i2c_recv8(i2c, val)
#define i2c_recv16(i2c, val) \
_i2c_recv16(i2c, val)
#define i2c_recv16_le(i2c, val) \
_i2c_recv16_le(i2c, val)
#define i2c_recv16_be(i2c, val) \
_i2c_recv16_be(i2c, val)
#define i2c_recv32(i2c, val) \
_i2c_recv32(i2c, val)
#define i2c_recv32_le(i2c, val) \
_i2c_recv32_le(i2c, val)
#define i2c_recv32_be(i2c, val) \
_i2c_recv32_be(i2c, val)
#else
#define i2c_send(txbuf, txbytes) \
_i2c_send(&drv->config->i2c, txbuf, txbytes)
#define i2c_transmit(txbuf, txbytes, rxbuf, rxbytes) \
_i2c_transmit(&drv->config->i2c, txbuf, txbytes, rxbuf, rxbytes)
#define i2c_receive(rxbuf, rxbytes) \
_i2c_receive(&drv->config->i2c, rxbuf, rxbytes)
#define i2c_send_timeout(txbuf, txbytes) \
_i2c_send(&drv->config->i2c, txbuf, txbytes)
#define i2c_transmit_timeout(txbuf, txbytes, rxbuf, rxbytes) \
_i2c_transmit(&drv->config->i2c, txbuf, txbytes, rxbuf, rxbytes)
#define i2c_receive_timeout(rxbuf, rxbytes) \
_i2c_receive(&drv->config->i2c, rxbuf, rxbytes)
#define i2c_reg(reg) \
_i2c_reg(&drv->config->i2c, reg)
#define i2c_reg_recv8(reg, val) \
_i2c_reg_recv8(&drv->config->i2c, reg, val)
#define i2c_reg_recv16(reg, val) \
_i2c_reg_recv16(&drv->config->i2c, reg, val)
#define i2c_reg_recv16_le(reg, val) \
_i2c_reg_recv16_le(&drv->config->i2c, reg, val)
#define i2c_reg_recv16_be(reg, val) \
_i2c_reg_recv16_be(&drv->config->i2c, reg, val)
#define i2c_reg_recv32(reg, val) \
_i2c_reg_recv32(&drv->config->i2c, reg, val)
#define i2c_reg_recv32_le(reg, val) \
_i2c_reg_recv32_le(&drv->config->i2c, reg, val)
#define i2c_reg_recv32_be(reg, val) \
_i2c_reg_recv32_be(&drv->config->i2c, reg, val)
#define i2c_recv8(val) \
_i2c_recv8(&drv->config->i2c, val)
#define i2c_recv16(val) \
_i2c_recv16(&drv->config->i2c, val)
#define i2c_recv16_le(val) \
_i2c_recv16_le(&drv->config->i2c, val)
#define i2c_recv16_be(val) \
_i2c_recv16_be(&drv->config->i2c, val)
#define i2c_recv32(val) \
_i2c_recv32(&drv->config->i2c, val)
#define i2c_recv32_le(val) \
_i2c_recv32_le(&drv->config->i2c, val)
#define i2c_recv32_be(val) \
_i2c_recv32_be(&drv->config->i2c, val)
#endif
static inline msg_t
_i2c_send(I2CHelper *i2c, const uint8_t *txbuf, size_t txbytes) {
return i2cMasterTransmitTimeout(i2c->driver, i2c->addr,
txbuf, txbytes, NULL, 0, TIME_INFINITE);
};
static inline msg_t
_i2c_transmit(I2CHelper *i2c, const uint8_t *txbuf, size_t txbytes,
uint8_t *rxbuf, size_t rxbytes) {
return i2cMasterTransmitTimeout(i2c->driver, i2c->addr,
txbuf, txbytes, rxbuf, rxbytes, TIME_INFINITE);
}
static inline msg_t
_i2c_receive(I2CHelper *i2c, uint8_t *rxbuf, size_t rxbytes) {
return i2cMasterReceiveTimeout(i2c->driver, i2c->addr,
rxbuf, rxbytes, TIME_INFINITE);
};
static inline msg_t
_i2c_send_timeout(I2CHelper *i2c, const uint8_t *txbuf, size_t txbytes,
systime_t timeout) {
return i2cMasterTransmitTimeout(i2c->driver, i2c->addr,
txbuf, txbytes, NULL, 0, timeout);
};
static inline msg_t
_i2c_transmit_timeout(I2CHelper *i2c, const uint8_t *txbuf, size_t txbytes,
uint8_t *rxbuf, size_t rxbytes, systime_t timeout) {
return i2cMasterTransmitTimeout(i2c->driver, i2c->addr,
txbuf, txbytes, rxbuf, rxbytes, timeout);
}
static inline msg_t
_i2c_receive_timeout(I2CHelper *i2c, uint8_t *rxbuf, size_t rxbytes, systime_t timeout) {
return i2cMasterReceiveTimeout(i2c->driver, i2c->addr,
rxbuf, rxbytes, timeout);
};
/*======================================================================*/
static inline msg_t
_i2c_reg(I2CHelper *i2c, uint8_t reg) {
return _i2c_transmit(i2c, &reg, sizeof(reg), NULL, 0);
};
/*======================================================================*/
static inline msg_t
_i2c_reg_recv8(I2CHelper *i2c, uint8_t reg, uint8_t *val) {
return _i2c_transmit(i2c, &reg, sizeof(reg), (uint8_t*)val, sizeof(val));
};
static inline msg_t
_i2c_reg_recv16(I2CHelper *i2c, uint8_t reg, uint16_t *val) {
return _i2c_transmit(i2c, &reg, sizeof(reg), (uint8_t*)val, sizeof(val));
};
static inline msg_t
_i2c_reg_recv16_le(I2CHelper *i2c, uint8_t reg, uint16_t *val) {
int msg = _i2c_reg_recv16(i2c, reg, val);
if (msg >= 0) *val = le16_to_cpu(*val);
return msg;
};
static inline msg_t
_i2c_reg_recv16_be(I2CHelper *i2c, uint8_t reg, uint16_t *val) {
int msg = _i2c_reg_recv16(i2c, reg, val);
if (msg >= 0) *val = be16_to_cpu(*val);
return msg;
};
static inline msg_t
_i2c_reg_recv32(I2CHelper *i2c, uint8_t reg, uint32_t *val) {
return _i2c_transmit(i2c, &reg, sizeof(reg), (uint8_t*)val, sizeof(val));
};
static inline msg_t
_i2c_reg_recv32_le(I2CHelper *i2c, uint8_t reg, uint32_t *val) {
int msg = _i2c_reg_recv32(i2c, reg, val);
if (msg >= 0) *val = le32_to_cpu(*val);
return msg;
};
static inline msg_t
_i2c_reg_recv32_be(I2CHelper *i2c, uint8_t reg, uint32_t *val) {
int msg = _i2c_reg_recv32(i2c, reg, val);
if (msg >= 0) *val = be32_to_cpu(*val);
return msg;
};
/*======================================================================*/
static inline msg_t
_i2c_recv8(I2CHelper *i2c, uint8_t *val) {
return _i2c_receive(i2c, (uint8_t*)val, sizeof(val));
};
static inline msg_t
_i2c_recv16(I2CHelper *i2c, uint16_t *val) {
return _i2c_receive(i2c, (uint8_t*)val, sizeof(val));
};
static inline msg_t
_i2c_recv16_le(I2CHelper *i2c, uint16_t *val) {
int msg = _i2c_recv16(i2c, val);
if (msg >= 0) *val = le16_to_cpu(*val);
return msg;
};
static inline msg_t
_i2c_recv16_be(I2CHelper *i2c, uint16_t *val) {
int msg = _i2c_recv16(i2c, val);
if (msg >= 0) *val = be16_to_cpu(*val);
return msg;
};
static inline msg_t
_i2c_recv32(I2CHelper *i2c, uint32_t *val) {
return _i2c_receive(i2c, (uint8_t*)val, sizeof(val));
};
static inline msg_t
_i2c_recv32_le(I2CHelper *i2c, uint32_t *val) {
int msg = _i2c_recv32(i2c, val);
if (msg >= 0) *val = le32_to_cpu(*val);
return msg;
};
static inline msg_t
_i2c_recv32_be(I2CHelper *i2c, uint32_t *val) {
int msg = _i2c_recv32(i2c, val);
if (msg >= 0) *val = be32_to_cpu(*val);
return msg;
};
#endif