From: Nathael Pajani Date: Tue, 13 Sep 2016 16:30:57 +0000 (+0200) Subject: TSL256x I2C luminosity and IR sensor driver X-Git-Url: http://git.techno-innov.fr/?a=commitdiff_plain;h=bd278cde9027634d02eeedf6a0b1073d5350fdc3;p=soft%2Flpc82x%2Fcore TSL256x I2C luminosity and IR sensor driver --- diff --git a/extdrv/tsl256x_light_sensor.c b/extdrv/tsl256x_light_sensor.c new file mode 100644 index 0000000..b08dc46 --- /dev/null +++ b/extdrv/tsl256x_light_sensor.c @@ -0,0 +1,278 @@ +/**************************************************************************** + * extdrv/tsl256x_light_sensor.c + * + * TSL256x I2C luminosity and IR sensor driver + * + * Copyright 2016 Nathael Pajani + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *************************************************************************** */ + + +#include "lib/stdint.h" +#include "core/system.h" +#include "lib/errno.h" +#include "drivers/i2c.h" + +#include "extdrv/tsl256x_light_sensor.h" + + + +/* Check the sensor presence, return 1 if found + * This is done by writing to the control register to set the power state to ON and + * reading the register to check the value, as stated in the TSL256x documentation page 14, + * (Register Set definitions) + * FIXME : Never managed to read the required value, though the sensor is running and + * provides seemingly accurate values. + */ +#define PROBE_BUF_SIZE 3 +int tsl256x_probe_sensor(struct tsl256x_sensor_config* conf) +{ + int ret = 0; + char cmd_buf[PROBE_BUF_SIZE] = { conf->addr, TSL256x_CMD(control), TSL256x_POWER_ON, }; + char ctrl_buf[PROBE_BUF_SIZE] = { I2C_CONT, I2C_DO_REPEATED_START, I2C_CONT, }; + uint8_t control_val = 0; + + /* Did we already probe the sensor ? */ + if (conf->probe_ok != 1) { + ret = i2c_write(conf->bus_num, cmd_buf, PROBE_BUF_SIZE, NULL); + if (ret != PROBE_BUF_SIZE) { + return 0; + } + msleep(500); + cmd_buf[2] = (conf->addr | I2C_READ_BIT); + ret = i2c_read(conf->bus_num, cmd_buf, PROBE_BUF_SIZE, ctrl_buf, &control_val, 1); + /* FIXME : check that control_val is TSL256x_POWER_ON ... */ + if (ret == 1) { + conf->probe_ok = 1; + } + } + return conf->probe_ok; +} + + + +/* FIXME: add comments, and make it work right ... never managed to read the ID given in + * the documentation + */ + +#define ID_BUF_SIZE 3 +int tsl256x_read_id(struct tsl256x_sensor_config* conf) +{ + int ret = 0; + char cmd_buf[ID_BUF_SIZE] = { conf->addr, TSL256x_CMD(part_id), (conf->addr | I2C_READ_BIT), }; + char ctrl_buf[ID_BUF_SIZE] = { I2C_CONT, I2C_DO_REPEATED_START, I2C_CONT, }; + uint8_t id = 0; + + /* Did we already probe the sensor ? */ + if (conf->probe_ok != 1) { + return 0; + } + ret = i2c_read(conf->bus_num, cmd_buf, ID_BUF_SIZE, ctrl_buf, &id, 1); + if (ret != 1) { + return ret; + } + return id; +} + + +/* Lux Read + * Performs a non-blocking read of the luminosity from the sensor. + * 'lux' 'ir' and 'comb': integer addresses for conversion result, may be NULL. + * Return value(s): + * Upon successfull completion, returns 0 and the luminosity read is placed in the + * provided integer(s). On error, returns a negative integer equivalent to errors from + * glibc. + * -EBADFD : I2C not initialized + * -EBUSY : Device or ressource Busy or Arbitration lost + * -EINVAL : Invalid argument (buf) + * -ENODEV : No such device + * -EREMOTEIO : Device did not acknowledge : Any device present ? + * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine + */ +#define READ_BUF_SIZE 3 +int tsl256x_sensor_read(struct tsl256x_sensor_config* conf, uint16_t* comb, uint16_t* ir, uint32_t* lux) +{ + int ret = 0; + char cmd_buf[READ_BUF_SIZE] = { conf->addr, TSL256x_CMD(data), (conf->addr | I2C_READ_BIT), }; + char ctrl_buf[READ_BUF_SIZE] = { I2C_CONT, I2C_DO_REPEATED_START, I2C_CONT, }; + uint8_t data[4]; + uint16_t comb_raw = 0, ir_raw = 0; + + ret = i2c_read(conf->bus_num, cmd_buf, READ_BUF_SIZE, ctrl_buf, data, 4); + if (ret != 4) { + return ret; + } + comb_raw = (data[0] & 0xFF) | ((data[1] << 8) & 0xFF00); + ir_raw = (data[2] & 0xFF) | ((data[3] << 8) & 0xFF00); + + if (comb != NULL) { + *comb = comb_raw; + } + if (ir != NULL) { + *ir = ir_raw; + } + if (lux != NULL) { + *lux = calculate_lux(conf, comb_raw, ir_raw); + } + + return 0; +} + + +/* Sensor config + * Performs default configuration of the luminosity sensor. + * FIXME : Add more comments about the behavior and the resulting configuration. + * Return value: + * Upon successfull completion, returns 0. On error, returns a negative integer + * equivalent to errors from glibc. + * -EBADFD : I2C not initialized + * -EBUSY : Device or ressource Busy or Arbitration lost + * -EINVAL : Invalid argument (buf) + * -ENODEV : No such device + * -EREMOTEIO : Device did not acknowledge : Any device present ? + * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine + */ +#define CONF_BUF_SIZE 3 +int tsl256x_configure(struct tsl256x_sensor_config* conf) +{ + int ret = 0; + char cmd_buf[CONF_BUF_SIZE] = { conf->addr, TSL256x_CMD(timing), 0, }; + + cmd_buf[2] = (conf->gain | conf->integration_time); + + if (tsl256x_probe_sensor(conf) != 1) { + return -ENODEV; + } + ret = i2c_write(conf->bus_num, cmd_buf, CONF_BUF_SIZE, NULL); + if (ret != CONF_BUF_SIZE) { + return -EIO; + } + return 0; +} + + + +/***************************************************************************** */ +/* + * lux equation approximation without floating point calculations + * + * Description: + * Calculate the approximate illuminance (lux) given the raw channel values of + * the TSL2560. The equation if implemented as a piece−wise linear approximation. + * + * Arguments: + * uint16_t ch0 − raw channel value from channel 0 of TSL2560 + * uint16_t ch1 − raw channel value from channel 1 of TSL2560 + * + * Return: uint32_t − the approximate illuminance (lux) + * + */ +uint32_t calculate_lux(struct tsl256x_sensor_config* conf, uint16_t ch0, uint16_t ch1) +{ + /* First, scale the channel values depending on the gain and integration time + * 16X, 402mS is nominal. + * Scale if integration time is NOT 402 msec */ + uint32_t chScale = 0; + uint32_t channel1 = 0, channel0 = 0; + uint32_t ratio = 0, lux = 0; + uint32_t b = 0, m = 0; + + switch (conf->integration_time) { + case TSL256x_INTEGRATION_13ms: /* 13.7 msec */ + chScale = CHSCALE_TINT0; + break; + case TSL256x_INTEGRATION_100ms: /* 101 msec */ + chScale = CHSCALE_TINT1; + break; + case TSL256x_INTEGRATION_400ms: /* 402 msec */ + default: /* assume no scaling */ + chScale = (1 << CH_SCALE); + break; + } + + /* Scale if gain is NOT 16X */ + if (conf->gain == TSL256x_LOW_GAIN) { + chScale = chScale << 4; /* Scale 1X to 16X */ + } + + // Scale the channel values */ + channel0 = (ch0 * chScale) >> CH_SCALE; + channel1 = (ch1 * chScale) >> CH_SCALE; + + /* Find the ratio of the channel values (Channel1/Channel0) */ + /* Protect against divide by zero */ + if (channel0 != 0) { + ratio = (channel1 << (RATIO_SCALE + 1)) / channel0; + } + /* Round the ratio value */ + ratio = (ratio + 1) >> 1; + + /* Is ratio <= eachBreak ? */ + switch (conf->package) { + case TSL256x_PACKAGE_T: + case TSL256x_PACKAGE_FN: + case TSL256x_PACKAGE_CL: + if ((ratio >= 0) && (ratio <= K1T)) { + b = B1T; m = M1T; + } else if (ratio <= K2T) { + b = B2T; m = M2T; + } else if (ratio <= K3T) { + b = B3T; m = M3T; + } else if (ratio <= K4T) { + b = B4T; m = M4T; + } else if (ratio <= K5T) { + b = B5T; m = M5T; + } else if (ratio <= K6T) { + b = B6T; m = M6T; + } else if (ratio <= K7T) { + b = B7T; m = M7T; + } else if (ratio > K8T) { + b = B8T; m = M8T; + } break; + case TSL256x_PACKAGE_CS: /* CS package */ + if ((ratio >= 0) && (ratio <= K1C)) { + b = B1C; m = M1C; + } else if (ratio <= K2C) { + b = B2C; m = M2C; + } else if (ratio <= K3C) { + b = B3C; m = M3C; + } else if (ratio <= K4C) { + b = B4C; m = M4C; + } else if (ratio <= K5C) { + b = B5C; m = M5C; + } else if (ratio <= K6C) { + b = B6C; m = M6C; + } else if (ratio <= K7C) { + b = B7C; m = M7C; + }else if (ratio > K8C) { + b = B8C; m = M8C; + } break; + } + lux = ((channel0 * b) - (channel1 * m)); + + /* Do not allow negative lux value */ + if (lux < 0) { + lux = 0; + } + /* Round lsb (2^(LUX_SCALE−1)) */ + lux += (1 << (LUX_SCALE - 1)); + /* Strip off fractional portion */ + lux = lux >> LUX_SCALE; + + return lux; +} + diff --git a/include/extdrv/tsl256x_light_sensor.h b/include/extdrv/tsl256x_light_sensor.h new file mode 100644 index 0000000..afd96b8 --- /dev/null +++ b/include/extdrv/tsl256x_light_sensor.h @@ -0,0 +1,273 @@ +/**************************************************************************** + * extdrv/tsl256x_light_sensor.h + * + * TSL256x I2C luminosity and IR sensor driver + * + * Copyright 2016 Nathael Pajani + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *************************************************************************** */ + + +#include "lib/stdint.h" +#include "core/system.h" + + +/* TSL256x sensor instance data. + * Use one of this for each sensor you want to access. + * - addr is the sensor address on most significant bits (8bits address). + */ +struct tsl256x_sensor_config { + uint8_t addr; + uint8_t bus_num; + uint8_t package; + uint8_t gain; + uint8_t integration_time; + uint8_t probe_ok; +}; + +enum tsl256x_pkg_types { + TSL256x_PACKAGE_T = 0, + TSL256x_PACKAGE_FN, + TSL256x_PACKAGE_CL, + TSL256x_PACKAGE_CS, +}; + +struct tsl256x_internal_regs { + uint8_t control; /* Control of basic functions */ + uint8_t timing; /* Integration time/gain control */ + uint16_t low_int_threshold; /* low interrupt threshold, in little endian byte order */ + uint16_t high_int_threshold; /* high interrupt threshold, in little endian byte order */ + uint8_t interrupt; /* interrupt control */ + uint8_t reserved0[3]; + uint8_t part_id; /* Part number and revision ID */ + uint8_t reserved1; + uint16_t data[2]; /* Data from both ADC, in little endian byte order */ +}; +#define TSL256x_REGS(x) ((uint8_t)offsetof(struct tsl256x_internal_regs, x)) + +/* Defines for command byte */ +#define TSL256x_CMD_REG_SELECT (1 << 7) +#define TSL256x_INT_CLEAR (1 << 6) +#define TSL256x_USE_WORD (1 << 5) +#define TSL256x_USE_BLOCK (1 << 4) +#define TSL256x_REG_ADDR(x) ((x) & 0x0F) + +#define TSL256x_CMD(x) (TSL256x_CMD_REG_SELECT | TSL256x_REGS(x)) + +/* Defines for control register */ +#define TSL256x_POWER_ON (0x03) + +/* Defines for timing register */ +/* See page 22 of tsl256x manual for information on how to calculate lux. */ +#define TSL256x_LOW_GAIN (0x00) +#define TSL256x_HIGH_GAIN_16X (1 << 4) +#define TSL256x_CONVERSION_START (1 << 3) +#define TSL256x_CONVERSION_MANUAL (0x03) +#define TSL256x_INTEGRATION_400ms (0x02) +#define TSL256x_INTEGRATION_100ms (0x01) +#define TSL256x_INTEGRATION_13ms (0x00) + +/* Defines for interrupt control register */ +#define TSL256x_INTR_NONE (0x00) +#define TSL256x_INTR_LEVEL (0x01 << 4) +#define TSL256x_INTR_SMBUS (0x02 << 4) +#define TSL256x_INTR_ON_CONV_DONE (0x00) +#define TSL256x_INTR_NB_CYCLE(x) ((x) & 0x0F) + +/* Defines for part ID and revision ID register */ +#define TSL256x_PART_ID(x) (((x) & 0xF0) >> 4) +#define TSL256x_PART_REV(x) ((x) & 0x0F) + + +/***************************************************************************** */ + +/* Check the sensor presence, return 1 if found + * This is done by writing to the control register to set the power state to ON and + * reading the register to check the value, as stated in the TSL256x documentation page 14, + * (Register Set definitions) + * FIXME : Never managed to read the required value, though the sensor is running and + * provides seemingly accurate values. + */ +int tsl256x_probe_sensor(struct tsl256x_sensor_config* conf); + + +/* FIXME: add comments, and make it work right ... never managed to read the ID given in + * the documentation + */ +int tsl256x_read_id(struct tsl256x_sensor_config* conf); + + +/* Sensor read + * Performs a non-blocking read of the luminosity from the sensor. + * 'lux' 'ir' and 'comb': integer addresses for conversion result, may be NULL. + * Return value(s): + * Upon successfull completion, returns 0 and the luminosity read is placed in the + * provided integer(s). On error, returns a negative integer equivalent to errors from + * glibc. + * -EBADFD : I2C not initialized + * -EBUSY : Device or ressource Busy or Arbitration lost + * -EINVAL : Invalid argument (buf) + * -ENODEV : No such device + * -EREMOTEIO : Device did not acknowledge : Any device present ? + * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine + */ +int tsl256x_sensor_read(struct tsl256x_sensor_config* conf, uint16_t* comb, uint16_t* ir, uint32_t* lux); + + +/* Sensor config + * Performs default configuration of the luminosity sensor. + * FIXME : Add more comments about the behavior and the resulting configuration. + * Return value: + * Upon successfull completion, returns 0. On error, returns a negative integer + * equivalent to errors from glibc. + * -EBADFD : I2C not initialized + * -EBUSY : Device or ressource Busy or Arbitration lost + * -EINVAL : Invalid argument (buf) + * -ENODEV : No such device + * -EREMOTEIO : Device did not acknowledge : Any device present ? + * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine + */ +int tsl256x_configure(struct tsl256x_sensor_config* conf); + + + + +/***************************************************************************** */ +/* Lux Computation + * + * Copyright E 2004−2005 TAOS, Inc. + * + * THIS CODE AND INFORMATION IS PROVIDED ”AS IS” WITHOUT WARRANTY OF ANY KIND, + * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define LUX_SCALE 14 /* scale by 2^14 */ +#define RATIO_SCALE 9 /* scale ratio by 2^9 */ + +/* Integration time scaling factors */ +#define CH_SCALE 10 /* scale channel values by 2^10 */ +#define CHSCALE_TINT0 ((322 / 11) * (2 << CH_SCALE)) /* = 0x7517 = 322/11 * 2^CH_SCALE */ +#define CHSCALE_TINT1 ((322 / 81) * (2 << CH_SCALE)) /* = 0x0fe7 = 322/81 * 2^CH_SCALE */ + + +/* + * lux equation approximation without floating point calculations + * + * Description: + * Calculate the approximate illuminance (lux) given the raw channel values of + * the TSL2560. The equation if implemented as a piece−wise linear approximation. + * + * Arguments: + * uint16_t ch0 − raw channel value from channel 0 of TSL2560 + * uint16_t ch1 − raw channel value from channel 1 of TSL2560 + * + * Return: uint32_t − the approximate illuminance (lux) + * + */ +uint32_t calculate_lux(struct tsl256x_sensor_config* conf, uint16_t ch0, uint16_t ch1); + +/* + * T, FN, and CL Package coefficients + * + * For Ch1/Ch0=0.00 to 0.50 : Lux/Ch0=0.0304−0.062*((Ch1/Ch0)^1.4) + * piecewise approximation + * For Ch1/Ch0=0.00 to 0.125: Lux/Ch0=0.0304−0.0272*(Ch1/Ch0) + * For Ch1/Ch0=0.125 to 0.250: Lux/Ch0=0.0325−0.0440*(Ch1/Ch0) + * For Ch1/Ch0=0.250 to 0.375: Lux/Ch0=0.0351−0.0544*(Ch1/Ch0) + * For Ch1/Ch0=0.375 to 0.50: Lux/Ch0=0.0381−0.0624*(Ch1/Ch0) + * + * For Ch1/Ch0=0.50 to 0.61: Lux/Ch0=0.0224−0.031*(Ch1/Ch0) + * + * For Ch1/Ch0=0.61 to 0.80: Lux/Ch0=0.0128−0.0153*(Ch1/Ch0) + * + * For Ch1/Ch0=0.80 to 1.30: Lux/Ch0=0.00146−0.00112*(Ch1/Ch0) + * + * For Ch1/Ch0>1.3: Lux/Ch0=0 + * + */ +#define K1T 0x0040 /* 0.125 * 2^RATIO_SCALE */ +#define B1T 0x01f2 /* 0.0304 * 2^LUX_SCALE */ +#define M1T 0x01be /* 0.0272 * 2^LUX_SCALE */ +#define K2T 0x0080 /* 0.250 * 2^RATIO_SCALE */ +#define B2T 0x0214 /* 0.0325 * 2^LUX_SCALE */ +#define M2T 0x02d1 /* 0.0440 * 2^LUX_SCALE */ +#define K3T 0x00c0 /* 0.375 * 2^RATIO_SCALE */ +#define B3T 0x023f /* 0.0351 * 2^LUX_SCALE */ +#define M3T 0x037b /* 0.0544 * 2^LUX_SCALE */ +#define K4T 0x0100 /* 0.50 * 2^RATIO_SCALE */ +#define B4T 0x0270 /* 0.0381 * 2^LUX_SCALE */ +#define M4T 0x03fe /* 0.0624 * 2^LUX_SCALE */ +#define K5T 0x0138 /* 0.61 * 2^RATIO_SCALE */ +#define B5T 0x016f /* 0.0224 * 2^LUX_SCALE */ +#define M5T 0x01fc /* 0.0310 * 2^LUX_SCALE */ +#define K6T 0x019a /* 0.80 * 2^RATIO_SCALE */ +#define B6T 0x00d2 /* 0.0128 * 2^LUX_SCALE */ +#define M6T 0x00fb /* 0.0153 * 2^LUX_SCALE */ +#define K7T 0x029a /* 1.3 * 2^RATIO_SCALE */ +#define B7T 0x0018 /* 0.00146 * 2^LUX_SCALE */ +#define M7T 0x0012 /* 0.00112 * 2^LUX_SCALE */ +#define K8T 0x029a /* 1.3 * 2^RATIO_SCALE */ +#define B8T 0x0000 /* 0.000 * 2^LUX_SCALE */ +#define M8T 0x0000 /* 0.000 * 2^LUX_SCALE*/ + + +/* + * CS package coefficients + * + * For 0 <= Ch1/Ch0 <= 0.52 : Lux/Ch0 = 0.0315−0.0593*((Ch1/Ch0)^1.4) + * piecewise approximation + * For 0 <= Ch1/Ch0 <= 0.13 : Lux/Ch0 = 0.0315−0.0262*(Ch1/Ch0) + * For 0.13 <= Ch1/Ch0 <= 0.26 : Lux/Ch0 = 0.0337−0.0430*(Ch1/Ch0) + * For 0.26 <= Ch1/Ch0 <= 0.39 : Lux/Ch0 = 0.0363−0.0529*(Ch1/Ch0) + * For 0.39 <= Ch1/Ch0 <= 0.52 : Lux/Ch0 = 0.0392−0.0605*(Ch1/Ch0) + * + * For 0.52 < Ch1/Ch0 <= 0.65 : Lux/Ch0 = 0.0229−0.0291*(Ch1/Ch0) + * + * For 0.65 < Ch1/Ch0 <= 0.80 : Lux/Ch0 = 0.00157−0.00180*(Ch1/Ch0) + * + * For 0.80 < Ch1/Ch0 <= 1.30 : Lux/Ch0 = 0.00338−0.00260*(Ch1/Ch0) + * + * For Ch1/Ch0 > 1.30 : Lux = 0 + * + */ +#define K1C 0x0043 /* 0.130 * 2^RATIO_SCALE */ +#define B1C 0x0204 /* 0.0315 * 2^LUX_SCALE */ +#define M1C 0x01ad /* 0.0262 * 2^LUX_SCALE */ +#define K2C 0x0085 /* 0.260 * 2^RATIO_SCALE */ +#define B2C 0x0228 /* 0.0337 * 2^LUX_SCALE */ +#define M2C 0x02c1 /* 0.0430 * 2^LUX_SCALE */ +#define K3C 0x00c8 /* 0.390 * 2^RATIO_SCALE */ +#define B3C 0x0253 /* 0.0363 * 2^LUX_SCALE */ +#define M3C 0x0363 /* 0.0529 * 2^LUX_SCALE*/ +#define K4C 0x010a /* 0.520 * 2^RATIO_SCALE */ +#define B4C 0x0282 /* 0.0392 * 2^LUX_SCALE */ +#define M4C 0x03df /* 0.0605 * 2^LUX_SCALE */ +#define K5C 0x014d /* 0.65 * 2^RATIO_SCALE */ +#define B5C 0x0177 /* 0.0229 * 2^LUX_SCALE */ +#define M5C 0x01dd /* 0.0291 * 2^LUX_SCALE */ +#define K6C 0x019a /* 0.80 * 2^RATIO_SCALE */ +#define B6C 0x0101 /* 0.0157 * 2^LUX_SCALE */ +#define M6C 0x0127 /* 0.0180 * 2^LUX_SCALE */ +#define K7C 0x029a /* 1.3 * 2^RATIO_SCALE */ +#define B7C 0x0037 /* 0.00338 * 2^LUX_SCALE */ +#define M7C 0x002b /* 0.00260 * 2^LUX_SCALE */ +#define K8C 0x029a /* 1.3 * 2^RATIO_SCALE */ +#define B8C 0x0000 /* 0.000 * 2^LUX_SCALE */ +#define M8C 0x0000 /* 0.000 * 2^LUX_SCALE*/ + +