Add support and sample of the tsl256x light sensor

This commit is contained in:
Schoumi 2022-08-29 11:36:40 +02:00
parent f13349dfc5
commit b90795782a
3 changed files with 587 additions and 0 deletions

View File

@ -0,0 +1,54 @@
/*
The MIT License (MIT)
Copyright (c) 2016 British Broadcasting Corporation.
This software is provided by Lancaster University by arrangement with the BBC.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include "MicroBit.h"
#include "tsl256x.h"
MicroBit uBit;
MicroBitI2C i2c(I2C_SDA0,I2C_SCL0);
int main()
{
// Initialise the micro:bit runtime.
uBit.init();
tsl256x tsl(&uBit,&i2c);
uint16_t comb =0;
uint16_t ir = 0;
uint32_t lux = 0;
while(true)
{
tsl.sensor_read(&comb, &ir, &lux);
ManagedString display = "Lux:" + ManagedString((int)lux);
uBit.display.scroll(display.toCharArray());
uBit.sleep(1000);
}
release_fiber();
}

View File

@ -0,0 +1,262 @@
/****************************************************************************
* extdrv/tsl256x_light_sensor.c
*
* TSL256x I2C luminosity and IR sensor driver
*
* Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
*
*
* 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 3 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 <http://www.gnu.org/licenses/>.
*
*************************************************************************** */
#include <cstdint>
#include "tsl256x.h"
/* Sensor config
* Performs default configuration of the luminosity sensor.
* FIXME : Add more comments about the behavior and the resulting configuration.
*/
#define CONF_BUF_SIZE 2
tsl256x::tsl256x(MicroBit* uB, MicroBitI2C* uBi2c, uint8_t addr, uint8_t pkg,
uint8_t p_gain, uint8_t integration)
:
uBit(uB), i2c(uBi2c), address(addr), package(pkg), gain(p_gain), integration_time(integration)
{
probe_ok = 0;
int ret = 0;
char cmd_buf[CONF_BUF_SIZE] = { TSL256x_CMD(timing), 0, };
cmd_buf[1] = (gain | integration_time);
if (probe_sensor() != 1) {
uBit->display.scroll("TSL256X: No Device");
}
uBit->sleep(1);
ret = i2c->write(address, cmd_buf, CONF_BUF_SIZE);
if (ret != MICROBIT_OK) {
probe_ok = 0;
uBit->display.scroll("TSL256x: Conf Error");
}
}
/* 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 2
int tsl256x::probe_sensor()
{
int ret = 0;
char cmd_buf[PROBE_BUF_SIZE] = { TSL256x_CMD(control), TSL256x_POWER_ON, };
uint8_t control_val = 72;
/* Did we already probe the sensor ? */
if (probe_ok != 1) {
ret = i2c->write(address, cmd_buf, PROBE_BUF_SIZE);
if (ret != MICROBIT_OK) {
return 0;
}
uBit->sleep(500);
i2c->write(address, cmd_buf,1,true);
ret = i2c->read(address, (char*)&control_val, 1);
/* FIXME : check that control_val is TSL256x_POWER_ON... */
if (ret == MICROBIT_OK && (control_val&0x03) == TSL256x_POWER_ON) {
probe_ok = 1;
}
}
return probe_ok;
}
/* FIXME: add comments, and make it work right ... never managed to read the ID given in
* the documentation
*/
#define ID_BUF_SIZE 1
int tsl256x::read_id()
{
int ret = 0;
char cmd_buf[ID_BUF_SIZE] = { TSL256x_CMD(part_id)};
char id = 0;
/* Did we already probe the sensor ? */
if (probe_ok != 1) {
return 0;
}
i2c->write(address, cmd_buf, ID_BUF_SIZE, true);
ret = i2c->read(address, &id, 1);
if (ret != MICROBIT_OK) {
probe_ok = 0;
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.
*/
#define READ_BUF_SIZE 1
int tsl256x::sensor_read(uint16_t* comb, uint16_t* ir, uint32_t* lux)
{
int ret = 0;
char cmd_buf[READ_BUF_SIZE] = { TSL256x_CMD(data)};
uint8_t data[4];
uint16_t comb_raw = 0, ir_raw = 0;
i2c->write(address, cmd_buf, READ_BUF_SIZE,true);
ret = i2c->read(address, (char*)data, 4);
if (ret != MICROBIT_OK) {
probe_ok = 0;
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(comb_raw, ir_raw);
}
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 piecewise 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 tsl256x::calculate_lux(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 (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 (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 (package) {
case TSL256x_PACKAGE_T:
case TSL256x_PACKAGE_FN:
case TSL256x_PACKAGE_CL:
if (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 <= 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));
/* Round lsb (2^(LUX_SCALE1)) */
lux += (1 << (LUX_SCALE - 1));
/* Strip off fractional portion */
lux = lux >> LUX_SCALE;
return lux;
}

View File

@ -0,0 +1,271 @@
/****************************************************************************
* extdrv/tsl256x_light_sensor.h
*
* TSL256x I2C luminosity and IR sensor driver
*
* Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
*
*
* 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 3 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 <http://www.gnu.org/licenses/>.
*
*************************************************************************** */
/**
* This code is mainly an adaptation of code provided by Techno-Innov (Nathael Pajani <nathael.pajani@ed3l.fr>)
* www.techno-innov.fr
* http://git.techno-innov.fr
*/
#ifndef TSL256X_H
#define TSL256X_H
#include <cstdint>
#include "MicroBit.h"
#define TSL256x_ADDR 0x52
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)
/***************************************************************************** */
/* Lux Computation
*
* Copyright E 20042005 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 */
/*
* T, FN, and CL Package coefficients
*
* For Ch1/Ch0=0.00 to 0.50 : Lux/Ch0=0.03040.062*((Ch1/Ch0)^1.4)
* piecewise approximation
* For Ch1/Ch0=0.00 to 0.125: Lux/Ch0=0.03040.0272*(Ch1/Ch0)
* For Ch1/Ch0=0.125 to 0.250: Lux/Ch0=0.03250.0440*(Ch1/Ch0)
* For Ch1/Ch0=0.250 to 0.375: Lux/Ch0=0.03510.0544*(Ch1/Ch0)
* For Ch1/Ch0=0.375 to 0.50: Lux/Ch0=0.03810.0624*(Ch1/Ch0)
*
* For Ch1/Ch0=0.50 to 0.61: Lux/Ch0=0.02240.031*(Ch1/Ch0)
*
* For Ch1/Ch0=0.61 to 0.80: Lux/Ch0=0.01280.0153*(Ch1/Ch0)
*
* For Ch1/Ch0=0.80 to 1.30: Lux/Ch0=0.001460.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.03150.0593*((Ch1/Ch0)^1.4)
* piecewise approximation
* For 0 <= Ch1/Ch0 <= 0.13 : Lux/Ch0 = 0.03150.0262*(Ch1/Ch0)
* For 0.13 <= Ch1/Ch0 <= 0.26 : Lux/Ch0 = 0.03370.0430*(Ch1/Ch0)
* For 0.26 <= Ch1/Ch0 <= 0.39 : Lux/Ch0 = 0.03630.0529*(Ch1/Ch0)
* For 0.39 <= Ch1/Ch0 <= 0.52 : Lux/Ch0 = 0.03920.0605*(Ch1/Ch0)
*
* For 0.52 < Ch1/Ch0 <= 0.65 : Lux/Ch0 = 0.02290.0291*(Ch1/Ch0)
*
* For 0.65 < Ch1/Ch0 <= 0.80 : Lux/Ch0 = 0.001570.00180*(Ch1/Ch0)
*
* For 0.80 < Ch1/Ch0 <= 1.30 : Lux/Ch0 = 0.003380.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*/
class tsl256x {
public:
/* Sensor config
* Performs default configuration of the luminosity sensor.
* FIXME : Add more comments about the behavior and the resulting configuration.
*/
tsl256x(MicroBit* uB, MicroBitI2C* i2c, uint8_t addr = TSL256x_ADDR, uint8_t pkg = TSL256x_PACKAGE_T,
uint8_t p_gain = TSL256x_LOW_GAIN, uint8_t integration = TSL256x_INTEGRATION_100ms);
/* 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 probe_sensor();
/* FIXME: add comments, and make it work right ... never managed to read the ID given in
* the documentation
*/
int read_id();
/* 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.
*/
int sensor_read(uint16_t* comb, uint16_t* ir, uint32_t* lux);
private:
/*
* 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 piecewise 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(uint16_t ch0, uint16_t ch1);
MicroBit* uBit;
MicroBitI2C* i2c;
uint8_t address;
uint8_t package;
uint8_t gain;
uint8_t integration_time;
uint8_t probe_ok;
};
#endif /* TSL256X_H */