/****************************************************************************/
/*
 * MODULE              JN-AN-1162 JenNet-IP Smart Home
 *
 * DESCRIPTION         Illuminance driver implementation for TSL2550
 */
/****************************************************************************/
/*
 * This software is owned by NXP B.V. and/or its supplier and is protected
 * under applicable copyright laws. All rights are reserved. We grant You,
 * and any third parties, a license to use this software solely and
 * exclusively on NXP products [NXP Microcontrollers such as JN5168, JN5164].
 * You, and any third parties must reproduce the copyright and warranty notice
 * and any other legend of ownership on each copy or partial copy of the
 * software.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Copyright NXP B.V. 2014. All rights reserved
 */
/****************************************************************************/

/****************************************************************************/
/***        Include files                                                 ***/
/****************************************************************************/
#include <jendefs.h>
#include <AppHardwareApi.h>
#include <SMBus.h>
#include "DriverIlluminance.h"
#include "DeviceDefs.h"
//#include "Uart.h"

/****************************************************************************/
/***        Macro Definitions                                             ***/
/****************************************************************************/
/* Address for TSL2550 */
#define TSL2550_ADDRESS				0x39
/* Commands for TSL2550 */
#define TSL2550_POWER_DOWN			0x00
#define TSL2550_POWER_UP			0x03
#define TSL2550_MODE_STANDARD		0x18
#define TSL2550_MODE_EXTENDED		0x1d
#define TSL2550_READ_ADC_0			0x43
#define TSL2550_READ_ADC_1			0x83

/* Select operating mode */
#define TSL2550_MODE TSL2550_MODE_EXTENDED

/* Standard mode parameters */
#if TSL2550_MODE == TSL2550_MODE_STANDARD
/* Conversion timers (expected 400ms per channel) */
#define TSL2550_TIMER_READY 		  40
#define TSL2550_TIMER_EXPIRED 		  50
/* Conversion scaling factor */
#define TSL2550_SCALE				   1
#endif

/* Extended mode parameters */
#if TSL2550_MODE == TSL2550_MODE_EXTENDED
/* Conversion timers (expected 80ms per channel) */
#define TSL2550_TIMER_READY 		   8 /*40*/
#define TSL2550_TIMER_EXPIRED 		  10 /*50*/
/* Conversion scaling factor */
#define TSL2550_SCALE				   5
#endif

/* Maximum Lux for TSL2550 (1846 in Standard Mode) */
#define TSL2550_MAX_LUX 		   (1846 * TSL2550_SCALE)

/****************************************************************************/
/***        Type Definitions                                              ***/
/****************************************************************************/

/****************************************************************************/
/***        Local Function Prototypes                                     ***/
/****************************************************************************/
PRIVATE uint16 DriverIlluminance_u16CalculateLux(uint8 u8Result0, uint8 u8Result1);

/****************************************************************************/
/***        Exported Variables                                            ***/
/****************************************************************************/

/****************************************************************************/
/***        Local Variables                                               ***/
/****************************************************************************/
PRIVATE bool_t    bReady   = FALSE;			/* Conversion is ready */
PRIVATE uint8    u8Channel = 0xFF;			/* Current channel */
PRIVATE uint16  u16Timer;					/* Conversion timer */
PRIVATE uint8	au8Result[2];				/* Conversion result for each channel */
PRIVATE uint16  u16Lux     = 0xFFFF;		/* Conversion result (lux) */
PRIVATE bool_t	  bPower;					/* Sensor powered flag */
PRIVATE bool_t	  bOpen;					/* Bus open flag */

/****************************************************************************/
/***        Exported Functions                                            ***/
/****************************************************************************/

/****************************************************************************
 *
 * NAME: DriverIlluminance_bInit
 *
 * DESCRIPTION:
 * Initialises the TSL2550 ambient light sensor IC
 *
 * PARAMETERS: None
 *
 * RETURNS:
 * bool_t:	TRUE if the function completed successfully
 *			FALSE if there was an error
 *
 ****************************************************************************/
PUBLIC bool_t DriverIlluminance_bInit(void)
{
	bool_t bOk = TRUE;

	/* Open the bus */
	DriverIlluminance_vOpen();

	/* Is the bus powered down ? */
	if (FALSE == bPower)
	{
		/* UART ? */
		#ifdef UART_H_INCLUDED
		{
			/* Initialise UART */
			UART_vChar('u');
		}
		#endif

		bOk &= bSMBusWrite(TSL2550_ADDRESS, TSL2550_POWER_UP, 0, NULL);
		bOk &= bSMBusWrite(TSL2550_ADDRESS, TSL2550_MODE,     0, NULL);
		bPower = TRUE;
	}

	return(bOk);
}

/****************************************************************************
 *
 * NAME: DriverIlluminance_bPowerDown
 *
 * DESCRIPTION:
 * Places the TSL2550 ambient light sensor IC in power down mode
 *
 * PARAMETERS: None
 *
 * RETURNS:
 * bool_t:	TRUE if the function completed successfully
 *			FALSE if there was an error
 *
 ****************************************************************************/
PUBLIC bool_t DriverIlluminance_bPowerDown(void)
{
	bool_t bOk = TRUE;

	/* Is the power up ? */
	if (TRUE == bPower)
	{
		/* UART ? */
		#ifdef UART_H_INCLUDED
		{
			/* Initialise UART */
			UART_vChar('d');
		}
		#endif

		bOk &= bSMBusWrite(TSL2550_ADDRESS, TSL2550_POWER_DOWN, 0, NULL);
		bPower = FALSE;
	}

	/* Close the bus */
	DriverIlluminance_vClose();

	return(bOk);
}

/****************************************************************************
 *
 * NAME: DriverIlluminance_vWake
 *
 * DESCRIPTION:
 * Closes the bus
 *
 * PARAMETERS: None
 *
 ****************************************************************************/
PUBLIC void DriverIlluminance_vOpen(void)
{
	/* Is the bus closed ? */
	if (FALSE == bOpen)
	{
		/* UART ? */
		#ifdef UART_H_INCLUDED
		{
			/* Initialise UART */
			UART_vChar('o');
		}
		#endif

		/* Enable bus at 400KHz */
		vAHI_SiMasterConfigure(TRUE, FALSE, 31);
		bOpen = TRUE;
	}
}

/****************************************************************************
 *
 * NAME: DriverIlluminance_vClose
 *
 * DESCRIPTION:
 * Closes the bus
 *
 * PARAMETERS: None
 *
 ****************************************************************************/
PUBLIC void DriverIlluminance_vClose(void)
{
	/* Is the bus open ? */
	if (TRUE == bOpen)
	{
		/* UART ? */
		#ifdef UART_H_INCLUDED
		{
			/* Initialise UART */
			UART_vChar('l');
		}
		#endif

		/* Disable bus */
		vAHI_SiMasterDisable();
		bOpen = FALSE;
	}
}

/****************************************************************************
 *
 * NAME: DriverIlluminance_bStart
 *
 * DESCRIPTION:
 * Starts the TSL2550 ambient light sensor IC sampling. There are two
 * channels, one for the full light range and one for infra-red only.
 *
 * PARAMETERS:      Name            RW  Usage
 *                  u8Channel       R   channel to sample
 *
 * RETURNS:
 * bool_t:	TRUE if the function completed successfully
 *			FALSE if there was an error
 *
 ****************************************************************************/
PUBLIC bool_t DriverIlluminance_bStart(void)
{
	bool_t bOk = TRUE;

	/* Not already converting ? */
	if (u8Channel > 1)
	{
		/* UART ? */
		#ifdef UART_H_INCLUDED
		{
			/* Initialise UART */
			UART_vChar('t');
		}
		#endif
		/* Initiate reading on channel 0 */
		bOk &= bSMBusWrite(TSL2550_ADDRESS, TSL2550_READ_ADC_0, 0, NULL);
		/* Conversion started ? */
		if (bOk)
		{
			/* Note converting channel 0 */
			u8Channel = 0;
			/* Reset conversion timer */
			u16Timer  = 0;
			/* Note conversion is not ready */
			bReady    = FALSE;
			/* Invalidate conversion result */
			u16Lux    = 0xFFFF;
		}
	}

	return(bOk);
}

/****************************************************************************
 *
 * NAME: DriverIlluminance_bTick
 *
 * DESCRIPTION:
 * Called every 10ms, monitors
 * channels, one for the full light range and one for infra-red only.
 *
 * PARAMETERS:      Name            RW  Usage
 *                  u8Channel       R   channel to sample
 *
 * RETURNS:
 * bool_t:	TRUE if the function completed successfully
 *			FALSE if there was an error
 *
 ****************************************************************************/
PUBLIC bool_t DriverIlluminance_bTick(void)
{
	/* UART ? */
	#ifdef UART_H_INCLUDED
	{
		/* Initialise UART */
		UART_vChar('a');
	}
	#endif

	/* Conversion in progress ? */
	if (u8Channel <= 1)
	{
		/* Increment timer */
		u16Timer += DEVICE_WAKE_TIMER_PERIOD_10MS;
		/* Timeout - been waiting for result for too long ? */
		if (u16Timer > TSL2550_TIMER_EXPIRED)
		{
			/* Indicate timeout as result */
			u16Lux = 0xFFFE;
			/* Invalidate the channel */
			u8Channel = 0xFF;
			/* Flag conversion as ready */
			bReady = TRUE;
		}
		/* Conversion should be ready ? */
		else if (u16Timer >= TSL2550_TIMER_READY)
		{
			/* UART ? */
			#ifdef UART_H_INCLUDED
			{
				/* Initialise UART */
				UART_vChar('r');
			}
			#endif
			/* Can we read the channel result ? */
			if (bSMBusSequentialRead(TSL2550_ADDRESS, 1, &au8Result[u8Channel]))
			{
				/* Has the conversion completed ? */
				if (au8Result[u8Channel] & 0x80)
				{
					/* Channel 0 completed ? */
					if (u8Channel == 0)
					{
						/* Can we initiate reading on channel 1 */
						if (bSMBusWrite(TSL2550_ADDRESS, TSL2550_READ_ADC_1, 0, NULL))
						{
							/* Note converting channel 1 */
							u8Channel = 1;
							/* Reset conversion timer */
							u16Timer  = 0;
						}
					}
					/* Channel 1 completed ? */
					else if (u8Channel == 1)
					{
						/* Calculate Lux */
						u16Lux = DriverIlluminance_u16CalculateLux(au8Result[0], au8Result[1]);
						/* Note no longer converting a channel */
						u8Channel = 0xFF;
						/* Note result is now ready */
						bReady = TRUE;
					}
				}
			}
		}
	}

	return(bReady);
}

/****************************************************************************
 *
 * NAME: DriverIlluminance_bReady
 *
 * DESCRIPTION:
 * Returns if result is ready
 *
 * RETURNS:
 * bool_t:	TRUE if the result completed successfully
 *			FALSE if there was an error
 *
 ****************************************************************************/
PUBLIC bool_t DriverIlluminance_bReady(void)
{
	return bReady;
}

/****************************************************************************
 *
 * NAME: DriverIlluminance_u16Lux
 *
 * DESCRIPTION:
 * Returns completed result in Lux
 *
 ****************************************************************************/
PUBLIC uint16 DriverIlluminance_u16Lux(void)
{
	/* Return most recent reading */
	return u16Lux;
}

/****************************************************************************
 *
 * NAME: DriverIlluminance_u8Type
 *
 * DESCRIPTION:
 * Returns sensor type 0=Photodiode, 1= CMOS, Others=Reserved, 255=Unknown
 *
 ****************************************************************************/
PUBLIC uint8  DriverIlluminance_u8Type(void)
{
	/* Return photodiode */
	return(0);
}

/****************************************************************************
 *
 * NAME: DriverIlluminance_u16LuxMin
 *
 * DESCRIPTION:
 * Returns minimim reading sensor can take
 *
 ****************************************************************************/
PUBLIC uint16 DriverIlluminance_u16LuxMin(void)
{
	/* Return 0 */
	return(0);
}

/****************************************************************************
 *
 * NAME: DriverIlluminance_u16LuxMax
 *
 * DESCRIPTION:
 * Returns minimim reading sensor can take
 *
 ****************************************************************************/
PUBLIC uint16 DriverIlluminance_u16LuxMax(void)
{
	/* Return maximum value */
	return(TSL2550_MAX_LUX);
}


/****************************************************************************
 *
 * NAME: DriverIlluminance_u16LuxTolerance
 *
 * DESCRIPTION:
 * Returns tolerance of sensor reading
 *
 ****************************************************************************/
PUBLIC uint16 DriverIlluminance_u16LuxTolerance(void)
{
	return(0xFFFF);
}

/****************************************************************************/
/***        Local Functions                                               ***/
/****************************************************************************/

/****************************************************************************
 *
 * NAME: DriverIlluminance_u16CalculateLux()
 *
 * DESCRIPTION:
 * Simplified lux equation approximation using lookup tables from:
 * Simplified TSL2550 Lux Calculation for Embedded and Micro Controllers Application Note
 *
 * RETURNS:
 * Value in range 0-TSL2550_MAX_LUX, 0 is darkest
 *
 ****************************************************************************/
PRIVATE uint16 DriverIlluminance_u16CalculateLux(uint8 u8Result0, uint8 u8Result1)
{
	uint32 u32Ratio = 128;
	uint32 u32Count0;
	uint32 u32Count1;
	uint32 u32Result;

	/* Lookup table to convert channel values to counts */
	static const uint16 au16CountLut[128] =
	{
		   0,   1,   2,   3,   4,   5,   6,   7,
		   8,   9,  10,  11,  12,  13,  14,  15,
		  16,  18,  20,  22,  24,  26,  28,  30,
		  32,  34,  36,  38,  40,  42,  44,  46,
		  49,  53,  57,  61,  65,  69,  73,  77,
		  81,  85,  89,  93,  97, 101, 105, 109,
		 115, 123, 131, 139, 147, 155, 163, 171,
		 179, 187, 195, 203, 211, 219, 227, 235,
		 247, 263, 279, 295, 311, 327, 343, 359,
		 375, 391, 407, 423, 439, 455, 471, 487,
		 511, 543, 575, 607, 639, 671, 703, 735,
		 767, 799, 831, 863, 895, 927, 959, 991,
		1039,1103,1167,1231,1295,1359,1423,1487,
		1551,1615,1679,1743,1807,1871,1935,1999,
		2095,2223,2351,2479,2607,2735,2863,2991,
		3119,3247,3375,3503,3631,3759,3887,4015
	};
	/* Lookup table for channel u8Ratio (i.e. channel1 / channel0) */
	static const uint8 au8RatioLut[129] =
	{
		 100, 100, 100, 100, 100, 100, 100, 100,
		 100, 100, 100, 100, 100, 100,  99,  99,
		  99,  99,  99,  99,  99,  99,  99,  99,
		  99,  99,  99,  98,  98,  98,  98,  98,
		  98,  98,  97,  97,  97,  97,  97,  96,
		  96,  96,  96,  95,  95,  95,  94,  94,
		  93,  93,  93,  92,  92,  91,  91,  90,
		  89,  89,  88,  87,  87,  86,  85,  84,
		  83,  82,  81,  80,  79,  78,  77,  75,
		  74,  73,  71,  69,  68,  66,  64,  62,
		  60,  58,  56,  54,  52,  49,  47,  44,
		  42,  41,  40,  40,  39,  39,  38,  38,
		  37,  37,  37,  36,  36,  36,  35,  35,
		  35,  35,  34,  34,  34,  34,  33,  33,
		  33,  33,  32,  32,  32,  32,  32,  31,
		  31,  31,  31,  31,  30,  30,  30,  30,
		  30
	};

	/* Look up counts */
	u32Count0 = au16CountLut[(u8Result0 & 0x7F)];
	u32Count1 = au16CountLut[(u8Result1 & 0x7F)];
	/* Channel 0 is not zero (avoid division by zero) and
       Channel 1 is not greater than Channel 0 */
	if (u32Count0 != 0 && u32Count1 <= u32Count0)
	{
		/* Calculate ratio (the "128" is a scaling factor) */
		u32Ratio = u32Count1 * 128 / u32Count0;
	}
	/* Calculate lux (the "256" is a scaling factor) */
	u32Result  = ((u32Count0-u32Count1) * (uint32) au8RatioLut[u32Ratio] * TSL2550_SCALE) / 256;
	/* Range check lux */
	if (u32Result > TSL2550_MAX_LUX) u32Result = TSL2550_MAX_LUX;

	return((uint16) u32Result);
}

/****************************************************************************/
/***        END OF FILE                                                   ***/
/****************************************************************************/
