Skip to content

Instantly share code, notes, and snippets.

@selimslab
Last active August 7, 2019 20:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save selimslab/b16c013ebbaafee461da2b91eb7bd9a5 to your computer and use it in GitHub Desktop.
Save selimslab/b16c013ebbaafee461da2b91eb7bd9a5 to your computer and use it in GitHub Desktop.
TSL-2561 light sensor library for Nordic nRF 51822
#include < stdio.h >
#include "boards.h"
#include "app_util_platform.h"
#include "app_uart.h"
#include "app_error.h"
#include "nrf_drv_twi.h"
#include "nrf_delay.h"
#include "main.h"
/* Define version of GCC. */
#
define GCC_VERSION(__GNUC__ * 10000\ + __GNUC_MINOR__ * 100\ + __GNUC_PATCHLEVEL__)# ifdef __GNUC_PATCHLEVEL__#
if GCC_VERSION < 50505# pragma GCC diagnostic push# pragma GCC diagnostic ignored "-Wmissing-braces"
// Hack to GCC 4.9.3 bug. Can be deleted after switch on using GCC 5.0.0
#
endif# endif# ifdef __GNUC_PATCHLEVEL__#
if GCC_VERSION < 50505# pragma GCC diagnostic pop# endif# endif
/* Indicates if reading operation from device has ended. */
static volatile bool is_read_done = true;
/* Indicates if setting mode operation has ended. */
static volatile bool is_set_mode_done = false;
/* TWI instance. */
static
const nrf_drv_twi_t twi_instance = NRF_DRV_TWI_INSTANCE(0);
bool device_found = false;
uint8_t CH0_LOW, CH0_HIGH, CH1_LOW, CH1_HIGH;
// these are for holding read values from channel registers
uint16_t ch0, ch1;
// to hold high and low bytes of a register together,
// for example ch0 holds together values in CH0_LOW and CH0_HIGH
uint8_t ch0low = TSL2561_Channel0Low;
uint8_t ch0high = TSL2561_Channel0High;
uint8_t ch1low = TSL2561_Channel1Low;
uint8_t ch1high = TSL2561_Channel1High;
/**
UART events handler.
*/
static void uart_events_handler(app_uart_evt_t * p_event) {
switch (p_event - > evt_type) {
case APP_UART_COMMUNICATION_ERROR:
APP_ERROR_HANDLER(p_event - > data.error_communication);
break;
case APP_UART_FIFO_ERROR:
APP_ERROR_HANDLER(p_event - > data.error_code);
break;
default:
break;
}
}
/**
UART initialization.
*/
static void uart_config(void) {
uint32_t err_code;
const app_uart_comm_params_t comm_params = {
RX_PIN_NUMBER,
TX_PIN_NUMBER,
RTS_PIN_NUMBER,
CTS_PIN_NUMBER,
APP_UART_FLOW_CONTROL_DISABLED,
false,
UART_BAUDRATE_BAUDRATE_Baud115200
};
APP_UART_FIFO_INIT( & comm_params,
UART_RX_BUF_SIZE,
UART_TX_BUF_SIZE,
uart_events_handler,
APP_IRQ_PRIORITY_LOW,
err_code);
APP_ERROR_CHECK(err_code);
}
/*****************************
this is the function that converts the raw data of sensor to SI units in lumens
iGain − gain, where 0:1X, 1:16X
integration_time − integration time, where 0:13.7mS, 1:100mS, 2:402mS,
function has different coefficients for T and CS packages
***************************/
unsigned int CalculateLux(unsigned int iGain, unsigned int integration_time, int package_type) {
//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
// first, scale the channel values depending on the gain and integration time
// 16X, 402mS is nominal.
// scale if integration time is NOT 402 msec
unsigned long chScale;
unsigned long channel1;
unsigned long channel0;
switch (integration_time) {
case 0: // 13.7 msec
chScale = CHSCALE_TINT0;
break;
case 1: // 101 msec
chScale = CHSCALE_TINT1;
break;
default: // assume no scaling
chScale = (1 << CH_SCALE);
break;
}
// scale if gain is NOT 16X
if (!iGain) 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
unsigned long ratio1 = 0;
if (channel0 != 0) ratio1 = (channel1 << (RATIO_SCALE + 1)) / channel0;
// round the ratio value
unsigned long ratio = (ratio1 + 1) >> 1;
// is ratio <= eachBreak ?
unsigned int b = 0, m = 0;
switch (package_type) {
case 0: // T package
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 1: // 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;
}
unsigned long 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
unsigned long lux = temp >> LUX_SCALE;
printf(" lux:%lu \n", lux);
return (lux);
}
// end of CalculateLux
// Function for setting active mode on TSL2561 device.
void enable_device(void) {
ret_code_t err_code;
/* Writing "0x03" on control register enables the device */
uint8_t reg[2] = {
TSL2561_Control,
0x03
};
//Here we are enabling the sensor by writing the control register 0x03,
// or 3h in terms of datasheet
err_code = nrf_drv_twi_tx( & twi_instance, TSL2561_ADDRESS, reg, sizeof(reg), false);
//printf("device POWER ON");
APP_ERROR_CHECK(err_code);
while (is_set_mode_done == false);
}
void read_register() {
ret_code_t err_code;
/*************WARNING*********************
below reading procedure works only using blocking mode, with no handler. but we have a twi_handler
**********************/
//read channel 0 low byte
err_code = nrf_drv_twi_tx( & twi_instance, TSL2561_ADDRESS, (uint8_t * ) & ch0low, sizeof(ch0low), true);
if (NRF_SUCCESS == err_code)
err_code = nrf_drv_twi_rx( & twi_instance, TSL2561_ADDRESS, (uint8_t * ) & CH0_LOW, sizeof(CH0_LOW));
APP_ERROR_CHECK(err_code);
//read channel 0 high byte
err_code = nrf_drv_twi_tx( & twi_instance, TSL2561_ADDRESS, (uint8_t * ) & ch0high, sizeof(ch0high), true);
if (NRF_SUCCESS == err_code)
err_code = nrf_drv_twi_rx( & twi_instance, TSL2561_ADDRESS, (uint8_t * ) & CH0_HIGH, sizeof(CH0_HIGH));
APP_ERROR_CHECK(err_code);
// read channel 1 low byte
err_code = nrf_drv_twi_tx( & twi_instance, TSL2561_ADDRESS, (uint8_t * ) & ch1low, sizeof(ch1low), true);
if (NRF_SUCCESS == err_code)
err_code = nrf_drv_twi_rx( & twi_instance, TSL2561_ADDRESS, (uint8_t * ) & CH1_LOW, sizeof(CH1_LOW));
APP_ERROR_CHECK(err_code);
//read channel 1 high byte
err_code = nrf_drv_twi_tx( & twi_instance, TSL2561_ADDRESS, (uint8_t * ) & ch1high, sizeof(ch1high), true);
if (NRF_SUCCESS == err_code)
err_code = nrf_drv_twi_rx( & twi_instance, TSL2561_ADDRESS, (uint8_t * ) & CH1_HIGH, sizeof(CH1_HIGH));
APP_ERROR_CHECK(err_code);
// low and high bytes of a channel is got together here
ch0 = (CH0_HIGH << 8) | CH0_LOW;
ch1 = (CH1_HIGH << 8) | CH1_LOW;
CalculateLux(0, 0, 0); //T package, no gain, 13ms integration time
}
/**
TWI events handler.
*/
void twi_handler(nrf_drv_twi_evt_t
const * p_event, void * p_context) {
ret_code_t err_code;
switch (p_event - > type) {
case NRF_DRV_TWI_EVT_DONE: //Transfer completed event
printf(" Connected to device \n\r");
read_register();
if ((p_event - > type == NRF_DRV_TWI_EVT_DONE) &&
(p_event - > xfer_desc.type == NRF_DRV_TWI_XFER_TX)) {
if (is_set_mode_done != true) {
is_set_mode_done = true;
return;
}
is_read_done = false;
/* Read 4 bytes from the specified address. */
err_code = nrf_drv_twi_rx( & twi_instance, TSL2561_ADDRESS, (uint8_t * ) & CH0_LOW, sizeof(CH0_LOW))
err_code = nrf_drv_twi_rx( & twi_instance, TSL2561_ADDRESS, (uint8_t * ) & CH0_HIGH, sizeof(CH0_HIGH));
err_code = nrf_drv_twi_rx( & twi_instance, TSL2561_ADDRESS, (uint8_t * ) & CH1_LOW, sizeof(CH1_LOW));
err_code = nrf_drv_twi_rx( & twi_instance, TSL2561_ADDRESS, (uint8_t * ) & CH1_HIGH, sizeof(CH1_HIGH));
APP_ERROR_CHECK(err_code);
} else {
read_register();
is_read_done = true;
}
break;
default:
printf(" \n\r Can't connect to device \n\r");
break;
}
}
void twi_init(void) {
ret_code_t err_code;
const nrf_drv_twi_config_t tsl_twi_config = { /*Structure for TWI instance configuration */
.scl = DEVICE_SCL_PIN,
.sda = DEVICE_SDA_PIN,
.frequency = NRF_TWI_FREQ_100K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH
};
//Function for initializing the TWI instance
err_code = nrf_drv_twi_init( & twi_instance, & tsl_twi_config, twi_handler, NULL);
APP_ERROR_CHECK(err_code);
nrf_drv_twi_enable( & twi_instance);
read_register();
// printf("\n\r Instance INITIALIZED \n\r");
}
/**
Function for main application entry.
*/
int main(void) {
uart_config(); // configures UART, just to see results via UART, all UART functions can be deleted before final application
twi_init();
enable_device(); // Function for setting active mode on TSL2561 device
uint8_t reg = 0;
ret_code_t err_code;
while (true) {
nrf_delay_ms(1000);
// Start transaction with a slave with the specified address.
do {
__WFE(); // means 'Wait for event', used to save power while waiting
} while (is_read_done == false);
// By writing a 00h to control register, the device is powered down.
uint8_t reg[2] = {
TSL2561_Control,
0
};
err_code = nrf_drv_twi_tx( & twi_instance, TSL2561_ADDRESS, & reg, sizeof(reg), true);
APP_ERROR_CHECK(err_code);
is_read_done = false;
}
}
#define MAIN_H
/* device addresses definition
The Slave and SMB Alert Addresses are 7 bits. A read/write bit shouldbe appended to
the slave address by the master device to properly communicate with the TSL256X device.
*/
#define TSL2561_ADDRESS 0x29
#define TSL2561_ADDR_FLOAT (0x39)
#define TSL2561_ADDR_HIGH (0x49)
#define TSL2561_Control 0x00
#define TSL2561_Timing 0x01
#define TSL2561_Interrupt 0x06
#define TSL2561_Channel0Low 0x0C
#define TSL2561_Channel0High 0x0D
#define TSL2561_Channel1Low 0x0E
#define TSL2561_Channel1High 0x0F
// these are for the function that converts raw readings into SI units
#define LUX_SCALE 14 // scale by 2^14
#define RATIO_SCALE 9 // scale ratio by 2^9
#define CH_SCALE 10 // scale channel values by 2^10
#define CHSCALE_TINT0 0x7517 // 322/11 * 2^CH_SCALE
#define CHSCALE_TINT1 0x0fe7 // 322/81 * 2^CH_SCALE
/******************
coeffeficients for standalone circuit and chipscale packages, from the datasheet
*****************////
#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_SCA
#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
/*Pins to connect device. */
#define DEVICE_SCL_PIN 7
#define DEVICE_SDA_PIN 30
/*UART buffer size. */
#define UART_TX_BUF_SIZE 256
#define UART_RX_BUF_SIZE 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment