Merge pull request #228 from bschwind/tsl2561
Add a TSL2561 driver and example usage
This commit is contained in:
commit
add6d85182
5 changed files with 512 additions and 0 deletions
3
examples/tsl2561/Makefile
Normal file
3
examples/tsl2561/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
PROGRAM=tsl2561_example
|
||||
EXTRA_COMPONENTS = extras/i2c extras/tsl2561
|
||||
include ../../common.mk
|
69
examples/tsl2561/tsl2561_example.c
Normal file
69
examples/tsl2561/tsl2561_example.c
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* This sample code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp/uart.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "i2c/i2c.h"
|
||||
#include "task.h"
|
||||
#include "tsl2561/tsl2561.h"
|
||||
|
||||
/* An example using the TSL2561 light sensor
|
||||
* to read and print lux values from a sensor
|
||||
* attached to GPIO pin 2 (SCL) and GPIO pin 0 (SDA)
|
||||
* Connect 3.3v from the ESP to Vin and GND to GND
|
||||
*/
|
||||
|
||||
#define SCL_PIN (2)
|
||||
#define SDA_PIN (0)
|
||||
|
||||
void tsl2561MeasurementTask(void *pvParameters)
|
||||
{
|
||||
tsl2561_t lightSensor;
|
||||
|
||||
// Options:
|
||||
// TSL2561_I2C_ADDR_VCC (0x49)
|
||||
// TSL2561_I2C_ADDR_GND (0x29)
|
||||
// TSL2561_I2C_ADDR_FLOAT (0x39) Default
|
||||
lightSensor.i2c_addr = TSL2561_I2C_ADDR_FLOAT;
|
||||
|
||||
tsl2561_init(&lightSensor);
|
||||
|
||||
// Options:
|
||||
// TSL2561_INTEGRATION_13MS (0x00)
|
||||
// TSL2561_INTEGRATION_101MS (0x01)
|
||||
// TSL2561_INTEGRATION_402MS (0x02) Default
|
||||
tsl2561_set_integration_time(&lightSensor, TSL2561_INTEGRATION_402MS);
|
||||
|
||||
// Options:
|
||||
// TSL2561_GAIN_1X (0x00) Default
|
||||
// TSL2561_GAIN_16X (0x10)
|
||||
tsl2561_set_gain(&lightSensor, TSL2561_GAIN_1X);
|
||||
|
||||
uint32_t lux = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (tsl2561_read_lux(&lightSensor, &lux))
|
||||
{
|
||||
printf("Lux: %u\n", lux);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Could not read data from TSL2561\n");
|
||||
}
|
||||
|
||||
// 0.1 second delay
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void user_init(void)
|
||||
{
|
||||
uart_set_baud(0, 115200);
|
||||
i2c_init(SCL_PIN, SDA_PIN);
|
||||
|
||||
xTaskCreate(tsl2561MeasurementTask, (signed char *)"tsl2561MeasurementTask", 256, NULL, 2, NULL);
|
||||
}
|
9
extras/tsl2561/component.mk
Normal file
9
extras/tsl2561/component.mk
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Component makefile for extras/tsl2561
|
||||
|
||||
# Include the TSL2561 driver as "tsl2561/tsl2561.h"
|
||||
INC_DIRS += $(tsl2561_ROOT)..
|
||||
|
||||
# args for passing into compile rule generation
|
||||
tsl2561_SRC_DIR = $(tsl2561_ROOT)
|
||||
|
||||
$(eval $(call component_compile_rules,tsl2561))
|
375
extras/tsl2561/tsl2561.c
Normal file
375
extras/tsl2561/tsl2561.c
Normal file
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
* Part of esp-open-rtos
|
||||
* Copyright (C) 2016 Brian Schwind (https://github.com/bschwind)
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "FreeRTOS.h"
|
||||
#include "i2c/i2c.h"
|
||||
#include "task.h"
|
||||
#include "tsl2561.h"
|
||||
|
||||
// Registers
|
||||
#define TSL2561_REG_COMMAND 0x80
|
||||
#define TSL2561_REG_CONTROL 0x00
|
||||
#define TSL2561_REG_TIMING 0x01
|
||||
#define TSL2561_REG_THRESHOLD_LOW_0 0x02
|
||||
#define TSL2561_REG_THRESHOLD_LOW_1 0x03
|
||||
#define TSL2561_REG_THRESHOLD_HIGH_0 0x04
|
||||
#define TSL2561_REG_THRESHOLD_HIGH_1 0x05
|
||||
#define TSL2561_REG_INTERRUPT 0x06
|
||||
#define TSL2561_REG_PART_ID 0x0A
|
||||
#define TSL2561_REG_CHANNEL_0_LOW 0x0C
|
||||
#define TSL2561_REG_CHANNEL_0_HIGH 0x0D
|
||||
#define TSL2561_REG_CHANNEL_1_LOW 0x0E
|
||||
#define TSL2561_REG_CHANNEL_1_HIGH 0x0F
|
||||
|
||||
// TSL2561 Misc Values
|
||||
#define TSL2561_ON 0x03
|
||||
#define TSL2561_OFF 0x00
|
||||
#define TSL2561_READ_WORD 0x20
|
||||
|
||||
// Integration times in milliseconds
|
||||
#define TSL2561_INTEGRATION_TIME_13MS 20
|
||||
#define TSL2561_INTEGRATION_TIME_101MS 110
|
||||
#define TSL2561_INTEGRATION_TIME_402MS 410 // Default
|
||||
|
||||
// Calculation constants
|
||||
#define LUX_SCALE 14
|
||||
#define RATIO_SCALE 9
|
||||
#define CH_SCALE 10
|
||||
#define CHSCALE_TINT0 0x7517
|
||||
#define CHSCALE_TINT1 0x0fe7
|
||||
|
||||
// Package constants
|
||||
#define TSL2561_PACKAGE_CS 0x00
|
||||
#define TSL2561_PACKAGE_T_FN_CL 0x01
|
||||
|
||||
// Constants from the TSL2561 data sheet
|
||||
#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
|
||||
#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
|
||||
|
||||
static bool write_register(uint8_t i2c_addr, uint8_t reg, uint8_t value)
|
||||
{
|
||||
uint8_t data[2];
|
||||
data[0] = TSL2561_REG_COMMAND | reg;
|
||||
data[1] = value;
|
||||
return i2c_slave_write(i2c_addr, data, 2);
|
||||
}
|
||||
|
||||
static uint8_t read_register(uint8_t i2c_addr, uint8_t reg)
|
||||
{
|
||||
uint8_t data[1];
|
||||
|
||||
if (!i2c_slave_read(i2c_addr, TSL2561_REG_COMMAND | reg, data, 1))
|
||||
{
|
||||
printf("Error in tsl261 read_register\n");
|
||||
}
|
||||
|
||||
return data[0];
|
||||
}
|
||||
|
||||
static uint16_t read_register_16(uint8_t i2c_addr, uint8_t low_register_addr)
|
||||
{
|
||||
uint16_t value = 0;
|
||||
uint8_t data[2];
|
||||
|
||||
if (!i2c_slave_read(i2c_addr, TSL2561_REG_COMMAND | TSL2561_READ_WORD | low_register_addr, data, 2))
|
||||
{
|
||||
printf("Error with i2c_slave_read in read_register_16\n");
|
||||
}
|
||||
|
||||
value = ((uint16_t)data[1] << 8) | (data[0]);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool enable(uint8_t i2c_addr)
|
||||
{
|
||||
return write_register(i2c_addr, TSL2561_REG_CONTROL, TSL2561_ON);
|
||||
}
|
||||
|
||||
static bool disable(uint8_t i2c_addr)
|
||||
{
|
||||
return write_register(i2c_addr, TSL2561_REG_CONTROL, TSL2561_OFF);
|
||||
}
|
||||
|
||||
void tsl2561_init(tsl2561_t *device)
|
||||
{
|
||||
if (!enable(device->i2c_addr))
|
||||
{
|
||||
printf("Error initializing tsl2561\n");
|
||||
}
|
||||
|
||||
uint8_t control_reg = (read_register(device->i2c_addr, TSL2561_REG_CONTROL) & TSL2561_ON);
|
||||
|
||||
if (control_reg != TSL2561_ON)
|
||||
{
|
||||
printf("Error initializing tsl2561, control register wasn't set to ON\n");
|
||||
}
|
||||
|
||||
// Fetch the package type
|
||||
uint8_t part_reg = read_register(device->i2c_addr, TSL2561_REG_PART_ID);
|
||||
uint8_t package = part_reg >> 6;
|
||||
device->package_type = package;
|
||||
|
||||
// Fetch the gain and integration time
|
||||
uint8_t timing_register = read_register(device->i2c_addr, TSL2561_REG_TIMING);
|
||||
device->gain = timing_register & 0x10;
|
||||
device->integration_time = timing_register & 0x03;
|
||||
|
||||
disable(device->i2c_addr);
|
||||
}
|
||||
|
||||
void tsl2561_set_integration_time(tsl2561_t *device, tsl2561_integration_time_t integration_time_id)
|
||||
{
|
||||
enable(device->i2c_addr);
|
||||
write_register(device->i2c_addr, TSL2561_REG_TIMING, integration_time_id | device->gain);
|
||||
disable(device->i2c_addr);
|
||||
|
||||
device->integration_time = integration_time_id;
|
||||
}
|
||||
|
||||
void tsl2561_set_gain(tsl2561_t *device, tsl2561_gain_t gain)
|
||||
{
|
||||
enable(device->i2c_addr);
|
||||
write_register(device->i2c_addr, TSL2561_REG_TIMING, gain | device->integration_time);
|
||||
disable(device->i2c_addr);
|
||||
|
||||
device->gain = gain;
|
||||
}
|
||||
|
||||
static void get_channel_data(tsl2561_t *device, uint16_t *channel0, uint16_t *channel1)
|
||||
{
|
||||
enable(device->i2c_addr);
|
||||
|
||||
// Since we just enabled the chip, we need to sleep
|
||||
// for the chip's integration time so it can gather a reading
|
||||
switch (device->integration_time)
|
||||
{
|
||||
case TSL2561_INTEGRATION_13MS:
|
||||
vTaskDelay(TSL2561_INTEGRATION_TIME_13MS / portTICK_RATE_MS);
|
||||
break;
|
||||
case TSL2561_INTEGRATION_101MS:
|
||||
vTaskDelay(TSL2561_INTEGRATION_TIME_101MS / portTICK_RATE_MS);
|
||||
break;
|
||||
default:
|
||||
vTaskDelay(TSL2561_INTEGRATION_TIME_402MS / portTICK_RATE_MS);
|
||||
break;
|
||||
}
|
||||
|
||||
*channel0 = read_register_16(device->i2c_addr, TSL2561_REG_CHANNEL_0_LOW);
|
||||
*channel1 = read_register_16(device->i2c_addr, TSL2561_REG_CHANNEL_1_LOW);
|
||||
|
||||
disable(device->i2c_addr);
|
||||
}
|
||||
|
||||
bool tsl2561_read_lux(tsl2561_t *device, uint32_t *lux)
|
||||
{
|
||||
bool success = true;
|
||||
uint32_t chScale;
|
||||
uint32_t channel1;
|
||||
uint32_t channel0;
|
||||
|
||||
switch (device->integration_time)
|
||||
{
|
||||
case TSL2561_INTEGRATION_13MS:
|
||||
chScale = CHSCALE_TINT0;
|
||||
break;
|
||||
case TSL2561_INTEGRATION_101MS:
|
||||
chScale = CHSCALE_TINT1;
|
||||
break;
|
||||
default:
|
||||
chScale = (1 << CH_SCALE);
|
||||
break;
|
||||
}
|
||||
|
||||
// Scale if gain is 1x
|
||||
if (device->gain == TSL2561_GAIN_1X)
|
||||
{
|
||||
// 16x is nominal, so if the gain is set to 1x then
|
||||
// we need to scale by 16
|
||||
chScale = chScale << 4;
|
||||
}
|
||||
|
||||
uint16_t ch0;
|
||||
uint16_t ch1;
|
||||
get_channel_data(device, &ch0, &ch1);
|
||||
|
||||
// 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
|
||||
uint32_t ratio1 = 0;
|
||||
if (channel0 != 0)
|
||||
{
|
||||
ratio1 = (channel1 << (RATIO_SCALE+1)) / channel0;
|
||||
}
|
||||
|
||||
// Round the ratio value
|
||||
uint32_t ratio = (ratio1 + 1) >> 1;
|
||||
|
||||
uint32_t b;
|
||||
uint32_t m;
|
||||
switch (device->package_type)
|
||||
{
|
||||
case TSL2561_PACKAGE_CS:
|
||||
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;
|
||||
case TSL2561_PACKAGE_T_FN_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;
|
||||
default:
|
||||
printf("Invalid package type in CalculateLux\n");
|
||||
b = 0;
|
||||
m = 0;
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t temp;
|
||||
temp = ((channel0 * b) - (channel1 * m));
|
||||
|
||||
// Do not allow negative lux value
|
||||
if (temp < 0)
|
||||
{
|
||||
temp = 0;
|
||||
}
|
||||
|
||||
// Round lsb (2^(LUX_SCALE−1))
|
||||
temp += (1 << (LUX_SCALE - 1));
|
||||
|
||||
// Strip off fractional portion
|
||||
*lux = temp >> LUX_SCALE;
|
||||
|
||||
return success;
|
||||
}
|
56
extras/tsl2561/tsl2561.h
Normal file
56
extras/tsl2561/tsl2561.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Part of esp-open-rtos
|
||||
* Copyright (C) 2016 Brian Schwind (https://github.com/bschwind)
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
|
||||
#ifndef __TSL2561_H__
|
||||
#define __TSL2561_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// I2C Addresses
|
||||
typedef enum
|
||||
{
|
||||
TSL2561_I2C_ADDR_VCC = 0x49,
|
||||
TSL2561_I2C_ADDR_GND = 0x29,
|
||||
TSL2561_I2C_ADDR_FLOAT = 0x39 // Default
|
||||
} tsl2561_i2c_addr_t;
|
||||
|
||||
// Integration time IDs
|
||||
typedef enum
|
||||
{
|
||||
TSL2561_INTEGRATION_13MS = 0x00,
|
||||
TSL2561_INTEGRATION_101MS = 0x01,
|
||||
TSL2561_INTEGRATION_402MS = 0x02 // Default
|
||||
} tsl2561_integration_time_t;
|
||||
|
||||
// Gain IDs
|
||||
typedef enum
|
||||
{
|
||||
TSL2561_GAIN_1X = 0x00, // Default
|
||||
TSL2561_GAIN_16X = 0x10
|
||||
} tsl2561_gain_t;
|
||||
|
||||
typedef struct {
|
||||
tsl2561_i2c_addr_t i2c_addr;
|
||||
uint8_t integration_time;
|
||||
uint8_t gain;
|
||||
uint8_t package_type;
|
||||
} tsl2561_t;
|
||||
|
||||
void tsl2561_init(tsl2561_t *device);
|
||||
void tsl2561_set_integration_time(tsl2561_t *device, tsl2561_integration_time_t integration_time_id);
|
||||
void tsl2561_set_gain(tsl2561_t *device, tsl2561_gain_t gain);
|
||||
bool tsl2561_read_lux(tsl2561_t *device, uint32_t *lux);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __TSL2561_H__
|
Loading…
Reference in a new issue