diff options
Diffstat (limited to 'os')
| -rw-r--r-- | os/various/bswap.h | 129 | ||||
| -rw-r--r-- | os/various/devices_lib/sensors/hdc1000.c | 265 | ||||
| -rw-r--r-- | os/various/devices_lib/sensors/hdc1000.h | 240 | ||||
| -rw-r--r-- | os/various/devices_lib/sensors/mcp9808.c | 207 | ||||
| -rw-r--r-- | os/various/devices_lib/sensors/mcp9808.h | 204 | ||||
| -rw-r--r-- | os/various/devices_lib/sensors/sensor.h | 81 | ||||
| -rw-r--r-- | os/various/devices_lib/sensors/tsl2561.c | 384 | ||||
| -rw-r--r-- | os/various/devices_lib/sensors/tsl2561.h | 233 | ||||
| -rw-r--r-- | os/various/i2c_helpers.h | 267 | 
9 files changed, 2010 insertions, 0 deletions
| diff --git a/os/various/bswap.h b/os/various/bswap.h new file mode 100644 index 0000000..b99034e --- /dev/null +++ b/os/various/bswap.h @@ -0,0 +1,129 @@ +#ifndef BSWAP_H +#define BSWAP_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#if !(defined(ARCH_BIG_ENDIAN) || defined(ARCH_LITTLE_ENDIAN)) +#error "ARCH_BIG_ENDIAN or ARCH_LITTLE_ENDIAN not set." +#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 + + diff --git a/os/various/devices_lib/sensors/hdc1000.c b/os/various/devices_lib/sensors/hdc1000.c new file mode 100644 index 0000000..39e47ec --- /dev/null +++ b/os/various/devices_lib/sensors/hdc1000.c @@ -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); +} + + +/** @} */ diff --git a/os/various/devices_lib/sensors/hdc1000.h b/os/various/devices_lib/sensors/hdc1000.h new file mode 100644 index 0000000..e4eae4c --- /dev/null +++ b/os/various/devices_lib/sensors/hdc1000.h @@ -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 + +/** + * @} + */ diff --git a/os/various/devices_lib/sensors/mcp9808.c b/os/various/devices_lib/sensors/mcp9808.c new file mode 100644 index 0000000..4b22ea9 --- /dev/null +++ b/os/various/devices_lib/sensors/mcp9808.c @@ -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);     +} diff --git a/os/various/devices_lib/sensors/mcp9808.h b/os/various/devices_lib/sensors/mcp9808.h new file mode 100644 index 0000000..857f2f9 --- /dev/null +++ b/os/various/devices_lib/sensors/mcp9808.h @@ -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 + diff --git a/os/various/devices_lib/sensors/sensor.h b/os/various/devices_lib/sensors/sensor.h new file mode 100644 index 0000000..bd544b1 --- /dev/null +++ b/os/various/devices_lib/sensors/sensor.h @@ -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 + + diff --git a/os/various/devices_lib/sensors/tsl2561.c b/os/various/devices_lib/sensors/tsl2561.c new file mode 100644 index 0000000..954c098 --- /dev/null +++ b/os/various/devices_lib/sensors/tsl2561.c @@ -0,0 +1,384 @@ +/* +    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. +*/ + +#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_THRESHHOLDL_LOW   0x02 +#define TSL2561_REG_THRESHHOLDL_HIGH  0x03 +#define TSL2561_REG_THRESHHOLDH_LOW   0x04 +#define TSL2561_REG_THRESHHOLDH_HIGH  0x05 +#define TSL2561_REG_INTERRUPT         0x06 +#define TSL2561_REG_CRC               0x08 +#define TSL2561_REG_ID                0x0A +#define TSL2561_REG_CHAN0_LOW         0x0C +#define TSL2561_REG_CHAN0_HIGH        0x0D +#define TSL2561_REG_CHAN1_LOW         0x0E +#define TSL2561_REG_CHAN1_HIGH        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 + + + + +#define CEILING(x,y) (((x) + (y) - 1) / (y)) + +/*===========================================================================*/ +/* Driver exported variables.                                                */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types.                                         */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions.                                                   */ +/*===========================================================================*/ + +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_CHAN0_LOW, +				  broadband)) < MSG_OK) || +	((msg = i2c_reg_recv16_le(TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | +				  TSL2561_REG_CHAN1_LOW, +				  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_SHORT; +    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 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 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 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 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; +} + diff --git a/os/various/devices_lib/sensors/tsl2561.h b/os/various/devices_lib/sensors/tsl2561.h new file mode 100644 index 0000000..94e1ede --- /dev/null +++ b/os/various/devices_lib/sensors/tsl2561.h @@ -0,0 +1,233 @@ +/* +    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; + + +typedef enum { +    TSL2561_INTEGRATIONTIME_SHORT      = 0x00,    // 13.7ms +    TSL2561_INTEGRATIONTIME_MEDIUM     = 0x01,    // 101ms +    TSL2561_INTEGRATIONTIME_LONG       = 0x02,    // 402ms +} TSL2561_integration_time_t; + +typedef enum { +    TSL2561_GAIN_1X                    = 0x00,    // No gain +    TSL2561_GAIN_16X                   = 0x10,    // 16x gain +} TSL2561_gain_t; + +/** + * @brief   TSL2561 configuration structure. + */ +typedef struct { +    TSL2561_config  *config; +    sensor_state_t   state; +    unsigned int     delay;     +    uint16_t         cfg; +    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 + diff --git a/os/various/i2c_helpers.h b/os/various/i2c_helpers.h new file mode 100644 index 0000000..56f9be6 --- /dev/null +++ b/os/various/i2c_helpers.h @@ -0,0 +1,267 @@ +#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, ®, sizeof(reg), NULL, 0); +}; + +/*======================================================================*/ + +static inline msg_t +_i2c_reg_recv8(I2CHelper *i2c, uint8_t reg, uint8_t *val) { +    return _i2c_transmit(i2c, ®, 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, ®, 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, ®, 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 | 
