/******************************************************************************
*
* Freescale Semiconductor Inc.
* (c) Copyright 2004-2013 Freescale Semiconductor, Inc.
* ALL RIGHTS RESERVED.
*
******************************************************************************
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED 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 FREESCALE OR ITS 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.
*
**************************************************************************//*!
*
* @file twrpi_slcd.c
*
* @author  
*
* @version 2.0
*
* @date Apr-24-2013
*
* @brief Driver for TWRPI-SLCD module on Kinetis devices
*
*****************************************************************************/

/***************************************************************************
* Header files
***************************************************************************/
#include "twrpi_slcd.h"

/***************************************************************************
* Functions
***************************************************************************/

/***************************************************************************
* funciton: vfnSlcdInit
*
* description: Initializes SLCD module
*
* input:
*       -u8ClkSource: Select the SLCD clock source
*                       -SLCD_CLK_DEFAULT: Uses default clock (RTC oscillator)
*                       -SLCD_CLK_ALTERNATE: Uses alternate clock (Slow IRC)
*
* output: (none)
*
****************************************************************************/
void vfnSlcdInit(unsigned char u8ClkSource)
{	
  unsigned int u32Delay;
  unsigned char u8Counter;
  unsigned char au8PinShift[] = {
    SLCD_LCDPIN_COM1, 
    SLCD_LCDPIN_COM2, 
    SLCD_LCDPIN_COM3, 
    SLCD_LCDPIN_COM4, 
    SLCD_LCDPIN_PIN5, 
    SLCD_LCDPIN_PIN6, 
    SLCD_LCDPIN_PIN7, 
    SLCD_LCDPIN_PIN8, 
    SLCD_LCDPIN_PIN9, 
    SLCD_LCDPIN_PIN10,
    SLCD_LCDPIN_PIN11
  };
  
  //Enalbe all GPIOs
  SIM_SCGC5 |= SIM_SCGC5_PORTA_MASK | SIM_SCGC5_PORTB_MASK | SIM_SCGC5_PORTC_MASK | SIM_SCGC5_PORTD_MASK;

  //Enable SLCD module
  SIM_SCGC3 |= SIM_SCGC3_SLCD_MASK;

  //LCD pins as LCD pin function	
  SLCD_COM1_PCR = PORT_PCR_MUX(0);		//COM1		
  SLCD_COM2_PCR = PORT_PCR_MUX(0);		//COM2		
  SLCD_COM3_PCR = PORT_PCR_MUX(0);		//COM3		
  SLCD_COM4_PCR = PORT_PCR_MUX(0);		//COM4		
  SLCD_PIN5_PCR = PORT_PCR_MUX(0);		//PIN 5		
  SLCD_PIN6_PCR = PORT_PCR_MUX(0);		//PIN 6		
  SLCD_PIN7_PCR = PORT_PCR_MUX(0);		//PIN 7		
  SLCD_PIN8_PCR = PORT_PCR_MUX(0);		//PIN 8		
  SLCD_PIN9_PCR = PORT_PCR_MUX(0);		//PIN 9		
  SLCD_PIN10_PCR = PORT_PCR_MUX(0);		//PIN 10	
  SLCD_PIN11_PCR = PORT_PCR_MUX(0);		//PIN 11	

  //Enable LCD pins on the SLCD controller
  LCD_PENH = 0x0u;      
  LCD_PENL = 0x0u; 
  for(u8Counter = 0; u8Counter < sizeof(au8PinShift); u8Counter++)
  {
    if(au8PinShift[u8Counter] > 32)
      LCD_PENH |= (unsigned int)(1<<(au8PinShift[u8Counter]-32));
    else
      LCD_PENL |= (unsigned int)(1<<au8PinShift[u8Counter]);
  }		

  //Configure backplane pins
  LCD_BPENL = 0x0u;
  LCD_BPENH = 0x0u;
  for(u8Counter = 0; u8Counter < 4; u8Counter++)
  {
    if(au8PinShift[u8Counter] > 32)
      LCD_BPENH |= (unsigned int)(1<<(au8PinShift[u8Counter]-32));
    else
      LCD_BPENL |= (unsigned int)(1<<au8PinShift[u8Counter]);
  }
  LCD_WF8B(SLCD_LCDPIN_COM1) = 0x01;		//Backplane 1 on in phase A
  LCD_WF8B(SLCD_LCDPIN_COM2) = 0x02;		//Backplane 2 on in phase B
  LCD_WF8B(SLCD_LCDPIN_COM3) = 0x04;		//Backplane 3 on in phase C
  LCD_WF8B(SLCD_LCDPIN_COM4) = 0x08;		//Backplane 4 on in phase D

  //Turn on all the SLCD segments
  LCD_WF8B(SLCD_LCDPIN_PIN5) = 0x0F;		//Set data for PIN 5
  LCD_WF8B(SLCD_LCDPIN_PIN6) = 0x0F;		//Set data for PIN 6
  LCD_WF8B(SLCD_LCDPIN_PIN7) = 0x0F;		//Set data for PIN 7
  LCD_WF8B(SLCD_LCDPIN_PIN8) = 0x0F;		//Set data for PIN 8
  LCD_WF8B(SLCD_LCDPIN_PIN9) = 0x0F;		//Set data for PIN 9
  LCD_WF8B(SLCD_LCDPIN_PIN10) = 0x0F;		//Set data for PIN 10
  LCD_WF8B(SLCD_LCDPIN_PIN11) = 0x0F;		//Set data for PIN 11

  /* Reset LCD_GCR register */
  LCD_GCR = 0x08310003;

  /* Configure SLCD module clock */
  if(u8ClkSource == SLCD_CLK_DEFAULT)
  {
    /* Defult clock source selected, it can be OSCOUTCLK or RTCOUTCLK */
    /* Selected RTCOUTCLK for low power purposes */
    
    SIM_SCGC6 |= SIM_SCGC6_RTC_MASK;        //Enable clock on RTC registers
    SIM_SOPT1 |= SIM_SOPT1_OSC32KSEL(2);
    if(!(RTC_CR & RTC_CR_OSCE_MASK))        //Check if RTC oscillator is not already enabled
    {
      RTC_CR |= RTC_CR_OSCE_MASK;              //Enable RTC oscillator and 2pF capacitors
      /*Wait to all the 32 kHz to stabilize, refer to the crystal startup time in the crystal datasheet*/
      for(u32Delay=0;u32Delay<0x1000;u32Delay++);
    }
    LCD_GCR&= ~LCD_GCR_SOURCE_MASK;         //select default clock OSC32K Output
  }

  if(u8ClkSource == SLCD_CLK_ALTERNATE)
  {
    /* Alternate clock source selected, it is IRC for K53N512 */
    // enable IRCLK
    MCG_C1 |= MCG_C1_IRCLKEN_MASK;
    LCD_GCR |= LCD_GCR_SOURCE_MASK; //select alt clock IRC
  }

  //Configure power source
  LCD_GCR |= LCD_GCR_VSUPPLY(3);
  LCD_GCR |= LCD_GCR_CPSEL_MASK;
  LCD_GCR |= LCD_GCR_RVEN_MASK;

  //Configure RVTRIM (improve contrast)
  LCD_GCR &= ~LCD_GCR_RVTRIM_MASK;
  LCD_GCR |= LCD_GCR_RVTRIM(15); //+10.5%

  //Configure frame rate settings
  LCD_GCR |= LCD_GCR_DUTY(3);
  LCD_GCR |= LCD_GCR_LCLK(4);

  //Enable LCD
  LCD_GCR|= LCD_GCR_LCDEN_MASK;
}

/***************************************************************************
* funciton: vfnSlcdRefresh
*
* description: Refresh the SLCD screen to show the values programmed in slcd_matrix_t
*
* input:
*       -Matrix: tSLCD structure containing the new SLCD segments to turn on
*
* output: (none)
*
****************************************************************************/
void vfnSlcdRefresh(tSLCD Matrix)
{
  unsigned char bCol[7];
  unsigned char bColIndex;

  for(bColIndex = 0; bColIndex<7; bColIndex++)
  {
      bCol[bColIndex]  = ((Matrix.bCom1 & (1<<bColIndex)) >> bColIndex);
      bCol[bColIndex] |= (((Matrix.bCom2 & (1<<bColIndex)) >> bColIndex) << 1);
      bCol[bColIndex] |= (((Matrix.bCom3 & (1<<bColIndex)) >> bColIndex) << 2);
      bCol[bColIndex] |= (((Matrix.bCom4 & (1<<bColIndex)) >> bColIndex) << 3);
  }

  LCD_WF8B(SLCD_LCDPIN_PIN5) = bCol[6];		//Set data for PIN 5
  LCD_WF8B(SLCD_LCDPIN_PIN6) = bCol[5];		//Set data for PIN 6
  LCD_WF8B(SLCD_LCDPIN_PIN7) = bCol[4];		//Set data for PIN 7
  LCD_WF8B(SLCD_LCDPIN_PIN8) = bCol[3];		//Set data for PIN 8
  LCD_WF8B(SLCD_LCDPIN_PIN9) = bCol[2];		//Set data for PIN 9
  LCD_WF8B(SLCD_LCDPIN_PIN10) = bCol[1];		//Set data for PIN 10
  LCD_WF8B(SLCD_LCDPIN_PIN11) = bCol[0];		//Set data for PIN 11
}

/********************************************************************
* Function: vfnSlcdDeinit
*
* Description: Turns of the Slcd controller
*
* Input: void
*
* Output: void
*
* Warning: 
**********************************************************************/
void vfnSlcdDeinit (void)
{
  LCD_GCR = 0x08350003;               //Set GCR register to the RESET value
  SIM_SCGC3 ^= SIM_SCGC3_SLCD_MASK;   //Turn off SLCD Clock
}

/********************************************************************
* Function: u8SlcdSetNumericSeg
*
* Description: Configures the input matrix with a number in the specified numeric segment.
*              Number is shown in the TWRPI-SLCD when function vfnSlcdRefresh is called
*
* Input: u8Segment
*        Numeric segment to set the number. Numeric segments available are 1, 2 and 3 (See TWRPI_SLCD datasheet)
*
* Input: u8Number
*        Number to set in the selected numeric segment. It must be a 1 digit number (0-9)
*
* Input: &inputMatrix
*        pointer to the matrix to configure
*
* Output: unsigned char
*         Error code. Returns 0 if OK. Returns 1 if some error occured
*
* Warning: This function only sets the segments to turn on within the matrix. To update the SLCD screen
*          use vfnSlcdRefresh(inputMatrix)
**********************************************************************/
unsigned char u8SlcdSetNumericSeg(unsigned char u8Segment, unsigned char u8Number, tSLCD* inputMatrix)
{
    tSLCD tempMatrix;

    tempMatrix = *inputMatrix; 

    if(u8Number > 9)
    return 1;     //Number too big, return error code

    if(u8Segment > 3 || u8Segment < 1)
    return 1;    //Invalid number segment, return error code

    switch (u8Segment)
    {
    case 1:
    CLR_NUM1(tempMatrix);
    switch(u8Number)
    {
    case 0:
    SET_NUM1_A(tempMatrix);
    SET_NUM1_B(tempMatrix);
    SET_NUM1_C(tempMatrix);
    SET_NUM1_D(tempMatrix);
    SET_NUM1_E(tempMatrix);
    SET_NUM1_F(tempMatrix);
    break;

    case 1:
    SET_NUM1_B(tempMatrix);
    SET_NUM1_C(tempMatrix);
    break;

    case 2:
    SET_NUM1_A(tempMatrix);
    SET_NUM1_B(tempMatrix);
    SET_NUM1_D(tempMatrix);
    SET_NUM1_E(tempMatrix);
    SET_NUM1_G(tempMatrix);
    break;

    case 3:
    SET_NUM1_A(tempMatrix);
    SET_NUM1_B(tempMatrix);
    SET_NUM1_C(tempMatrix);
    SET_NUM1_D(tempMatrix);
    SET_NUM1_G(tempMatrix);
    break;

    case 4:
    SET_NUM1_B(tempMatrix);
    SET_NUM1_C(tempMatrix);
    SET_NUM1_G(tempMatrix);
    SET_NUM1_F(tempMatrix);
    break;

    case 5:
    SET_NUM1_A(tempMatrix);
    SET_NUM1_C(tempMatrix);
    SET_NUM1_D(tempMatrix);
    SET_NUM1_G(tempMatrix);
    SET_NUM1_F(tempMatrix);
    break;

    case 6:
    SET_NUM1_G(tempMatrix);
    SET_NUM1_C(tempMatrix);
    SET_NUM1_D(tempMatrix);
    SET_NUM1_E(tempMatrix);
    SET_NUM1_F(tempMatrix);
    break;

    case 7:
    SET_NUM1_A(tempMatrix);
    SET_NUM1_B(tempMatrix);
    SET_NUM1_C(tempMatrix);
    break;

    case 8:
    SET_NUM1_A(tempMatrix);
    SET_NUM1_B(tempMatrix);
    SET_NUM1_C(tempMatrix);
    SET_NUM1_D(tempMatrix);
    SET_NUM1_E(tempMatrix);
    SET_NUM1_F(tempMatrix);
    SET_NUM1_G(tempMatrix);
    break;

    case 9:
    SET_NUM1_A(tempMatrix);
    SET_NUM1_B(tempMatrix);
    SET_NUM1_C(tempMatrix);
    SET_NUM1_G(tempMatrix);
    SET_NUM1_F(tempMatrix);
    break;

    default:
    break;
    }
    break;

    case 2:
    CLR_NUM2(tempMatrix);
    switch(u8Number)
    {
    case 0:
    SET_NUM2_A(tempMatrix);
    SET_NUM2_B(tempMatrix);
    SET_NUM2_C(tempMatrix);
    SET_NUM2_D(tempMatrix);
    SET_NUM2_E(tempMatrix);
    SET_NUM2_F(tempMatrix);
    break;

    case 1:
    SET_NUM2_B(tempMatrix);
    SET_NUM2_C(tempMatrix);
    break;

    case 2:
    SET_NUM2_A(tempMatrix);
    SET_NUM2_B(tempMatrix);
    SET_NUM2_D(tempMatrix);
    SET_NUM2_E(tempMatrix);
    SET_NUM2_G(tempMatrix);
    break;

    case 3:
    SET_NUM2_A(tempMatrix);
    SET_NUM2_B(tempMatrix);
    SET_NUM2_C(tempMatrix);
    SET_NUM2_D(tempMatrix);
    SET_NUM2_G(tempMatrix);
    break;

    case 4:
    SET_NUM2_B(tempMatrix);
    SET_NUM2_C(tempMatrix);
    SET_NUM2_G(tempMatrix);
    SET_NUM2_F(tempMatrix);
    break;

    case 5:
    SET_NUM2_A(tempMatrix);
    SET_NUM2_C(tempMatrix);
    SET_NUM2_D(tempMatrix);
    SET_NUM2_G(tempMatrix);
    SET_NUM2_F(tempMatrix);
    break;

    case 6:
    SET_NUM2_G(tempMatrix);
    SET_NUM2_C(tempMatrix);
    SET_NUM2_D(tempMatrix);
    SET_NUM2_E(tempMatrix);
    SET_NUM2_F(tempMatrix);
    break;

    case 7:
    SET_NUM2_A(tempMatrix);
    SET_NUM2_B(tempMatrix);
    SET_NUM2_C(tempMatrix);
    break;

    case 8:
    SET_NUM2_A(tempMatrix);
    SET_NUM2_B(tempMatrix);
    SET_NUM2_C(tempMatrix);
    SET_NUM2_D(tempMatrix);
    SET_NUM2_E(tempMatrix);
    SET_NUM2_F(tempMatrix);
    SET_NUM2_G(tempMatrix);
    break;

    case 9:
    SET_NUM2_A(tempMatrix);
    SET_NUM2_B(tempMatrix);
    SET_NUM2_C(tempMatrix);
    SET_NUM2_G(tempMatrix);
    SET_NUM2_F(tempMatrix);
    break;

    default:
    break;
    }
    break;

    case 3:
    CLR_NUM3(tempMatrix);
    switch(u8Number)
    {
    case 0:
    SET_NUM3_A(tempMatrix);
    SET_NUM3_B(tempMatrix);
    SET_NUM3_C(tempMatrix);
    SET_NUM3_D(tempMatrix);
    SET_NUM3_E(tempMatrix);
    SET_NUM3_F(tempMatrix);
    break;

    case 1:
    SET_NUM3_B(tempMatrix);
    SET_NUM3_C(tempMatrix);
    break;

    case 2:
    SET_NUM3_A(tempMatrix);
    SET_NUM3_B(tempMatrix);
    SET_NUM3_D(tempMatrix);
    SET_NUM3_E(tempMatrix);
    SET_NUM3_G(tempMatrix);
    break;

    case 3:
    SET_NUM3_A(tempMatrix);
    SET_NUM3_B(tempMatrix);
    SET_NUM3_C(tempMatrix);
    SET_NUM3_D(tempMatrix);
    SET_NUM3_G(tempMatrix);
    break;

    case 4:
    SET_NUM3_B(tempMatrix);
    SET_NUM3_C(tempMatrix);
    SET_NUM3_G(tempMatrix);
    SET_NUM3_F(tempMatrix);
    break;

    case 5:
    SET_NUM3_A(tempMatrix);
    SET_NUM3_C(tempMatrix);
    SET_NUM3_D(tempMatrix);
    SET_NUM3_G(tempMatrix);
    SET_NUM3_F(tempMatrix);
    break;

    case 6:
    SET_NUM3_G(tempMatrix);
    SET_NUM3_C(tempMatrix);
    SET_NUM3_D(tempMatrix);
    SET_NUM3_E(tempMatrix);
    SET_NUM3_F(tempMatrix);
    break;

    case 7:
    SET_NUM3_A(tempMatrix);
    SET_NUM3_B(tempMatrix);
    SET_NUM3_C(tempMatrix);
    break;

    case 8:
    SET_NUM3_A(tempMatrix);
    SET_NUM3_B(tempMatrix);
    SET_NUM3_C(tempMatrix);
    SET_NUM3_D(tempMatrix);
    SET_NUM3_E(tempMatrix);
    SET_NUM3_F(tempMatrix);
    SET_NUM3_G(tempMatrix);
    break;

    case 9:
    SET_NUM3_A(tempMatrix);
    SET_NUM3_B(tempMatrix);
    SET_NUM3_C(tempMatrix);
    SET_NUM3_G(tempMatrix);
    SET_NUM3_F(tempMatrix);
    break;

    default:
    break;
    }
    break;

    default:
    break;
    }

    *inputMatrix = tempMatrix;

    return 0; //Return OK code
}

/********************************************************************
* Function: u8SlcdSetNumber
*
* Description: Configures the input matrix with a 4 digit number to draw on the SLCD screen
*
* Input: u16Number
*        Number to configure in the matrix. Number must be less or equal to 1999 
*
* Input: &inputMatrix
*       Pointer to the matrix to configure
*
* Output: unsigned char
*         Error code. Returns 0 if OK. Returns 1 if some error occured 
*
* Warning: This function only sets the segments to turn on within the matrix. To update the SLCD screen
*          use vfnSlcdRefresh(inputMatrix)
**********************************************************************/
unsigned char u8SlcdSetNumber(unsigned short u16Number, tSLCD* inputMatrix)
{
  tSLCD  tempMatrix;
  unsigned char u8Segments[4];
  
  if(u16Number > 1999)
     return 1;         //Unsupoerted number, send error status
  
  tempMatrix = *inputMatrix;
  
  //Separate segment values
  
  u8Segments[0] =  u16Number/1000;
  u8Segments[1] = (u16Number/100)-(u8Segments[0]*10);
  u8Segments[2] = (u16Number/10)-((u8Segments[0]*100)+(u8Segments[1]*10));
  u8Segments[3] = (u16Number)-((u8Segments[0]*1000)+(u8Segments[1]*100)+(u8Segments[2]*10));
              
  //Sets each individual segment in the matrix
  if(u8Segments[0] == 0)
  {
    CLR_NUM0(tempMatrix);
  }
  else
  {
    SET_NUM0(tempMatrix);
  }
  
  if(u8Segments[0] == 0 && u8Segments[1] == 0)
  {
   CLR_NUM1(tempMatrix);
  }
  else
  {
   u8SlcdSetNumericSeg(1, u8Segments[1], &tempMatrix); 
  }
  
  
  if(u8Segments[0] == 0 && u8Segments[1] == 0 && u8Segments[2] == 0)
  {
   CLR_NUM2(tempMatrix);
  }
  else
  {
   u8SlcdSetNumericSeg(2, u8Segments[2], &tempMatrix); 
  }
  
   u8SlcdSetNumericSeg(3, u8Segments[3], &tempMatrix);
  
  *inputMatrix = tempMatrix;
  
  return 0; //Return OK code
}

/********************************************************************
* Function: vfnSlcdSetTextLp
*
* Description: Sets LP (Low Power) in the selected matrix
*
* Input: &matrix
*       Pointer to the matrix to configure
*
* Output: void
*
* Warning: This function only sets the segments to turn on within the matrix. To update the SLCD screen
*          use vfnSlcdRefresh(inputMatrix)
**********************************************************************/
void vfnSlcdSetTextLp (tSLCD* matrix)
{
  tSLCD  tempMatrix;
  
  tempMatrix = *matrix;
  
  CLR_NUM1(tempMatrix);
  CLR_NUM2(tempMatrix);
  CLR_NUM3(tempMatrix);
  
  SET_NUM2_D(tempMatrix);
  SET_NUM2_E(tempMatrix);
  SET_NUM2_F(tempMatrix);
  
  SET_NUM3_A(tempMatrix);
  SET_NUM3_B(tempMatrix);
  SET_NUM3_E(tempMatrix);
  SET_NUM3_F(tempMatrix);
  SET_NUM3_G(tempMatrix);
  
  *matrix = tempMatrix;
}

/********************************************************************
* Function: vfnSlcdSetTextUsb
*
* Description: Sets USb on the selected matrix
*
* Input: &matrix
*       Pointer to the matrix to configure
*
* Output: void
*
* Warning: This function only sets the segments to turn on within the matrix. To update the SLCD screen
*          use vfnSlcdRefresh(inputMatrix)
**********************************************************************/
void vfnSlcdSetTextUsb (tSLCD* matrix)
{
  tSLCD  tempMatrix;
    
  tempMatrix = *matrix;
  
  CLR_NUM1(tempMatrix);
  CLR_NUM2(tempMatrix);
  CLR_NUM3(tempMatrix);
  
  SET_NUM1_B(tempMatrix);
  SET_NUM1_C(tempMatrix);
  SET_NUM1_D(tempMatrix);
  SET_NUM1_E(tempMatrix);
  SET_NUM1_F(tempMatrix);
  
  SET_NUM2_A(tempMatrix);
  SET_NUM2_C(tempMatrix);
  SET_NUM2_D(tempMatrix);
  SET_NUM2_F(tempMatrix);
  SET_NUM2_G(tempMatrix);
  
  SET_NUM3_C(tempMatrix);
  SET_NUM3_D(tempMatrix);
  SET_NUM3_E(tempMatrix);
  SET_NUM3_F(tempMatrix);
  SET_NUM3_G(tempMatrix);
  
  *matrix = tempMatrix;
}
