/*******************************************************************************
 *
 *  hal_lcd.c
 *
 *  Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  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
 *  OWNER 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.
 *
 ******************************************************************************/

#include "contiki-conf.h"
#include "core/sys/cc.h"

#include "hal_MSP-EXP430F5438.h"
#include "hal_lcd_fonts.h"

unsigned char LcdInitMacro[] = {
    0x74, 0x00, 0x00, 0x76, 0x00, 0x01,            // R00 start oscillation
    0x74, 0x00, 0x01, 0x76, 0x00, 0x0D,            // R01 driver output control
    0x74, 0x00, 0x02, 0x76, 0x00, 0x4C,            // R02 LCD - driving waveform control
    0x74, 0x00, 0x03, 0x76, 0x12, 0x14,            // R03 Power control
    0x74, 0x00, 0x04, 0x76, 0x04, 0x66,            // R04 Contrast control
    0x74, 0x00, 0x05, 0x76, 0x00, 0x10,            // R05 Entry mode
    0x74, 0x00, 0x06, 0x76, 0x00, 0x00,            // R06 RAM data write mask
    0x74, 0x00, 0x07, 0x76, 0x00, 0x15,            // R07 Display control
    0x74, 0x00, 0x08, 0x76, 0x00, 0x03,            // R08 Cursor Control
    0x74, 0x00, 0x09, 0x76, 0x00, 0x00,            // R09 RAM data write mask
    0x74, 0x00, 0x0A, 0x76, 0x00, 0x15,            // R0A
    0x74, 0x00, 0x0B, 0x76, 0x00, 0x03,            // R0B Horizontal Cursor Position
    0x74, 0x00, 0x0C, 0x76, 0x00, 0x03,            // R0C Vertical Cursor Position
    0x74, 0x00, 0x0D, 0x76, 0x00, 0x00,            // R0D
    0x74, 0x00, 0x0E, 0x76, 0x00, 0x15,            // R0E
    0x74, 0x00, 0x0F, 0x76, 0x00, 0x03,            // R0F
    0x74, 0x00, 0x10, 0x76, 0x00, 0x15,            // R0E
    0x74, 0x00, 0x11, 0x76, 0x00, 0x03,            // R0F
};

unsigned char Read_Block_Address_Macro[] = {0x74, 0x00, 0x12, 0x77, 0x00, 0x00};
unsigned char Draw_Block_Value_Macro[] = {0x74, 0x00, 0x12, 0x76, 0xFF, 0xFF};
unsigned char Draw_Block_Address_Macro[] = {0x74, 0x00, 0x11, 0x76, 0x00, 0x00};

unsigned int LcdAddress = 0, LcdTableAddress = 0;
unsigned char contrast   = 0x66;
unsigned char backlight  = 8;
int LCD_MEM[110 * 17];                             //This array stores a copy of all data on the LCD
//screen. If memory is an issue though, this array
//can be eliminated and the halLcdReadBlock()
//command can be used instead whenever you are
//manipulating the currently displayed data.

/**********************************************************************//**
 * @brief  Sends 3+3 bytes of data to the LCD using the format specified
 *         by the LCD Guide.
 *
 * @param  Data[] Data array for transmission
 *
 * @return none
 *************************************************************************/

void halLcdSendCommand(unsigned char Data[])
{
    unsigned char i;

    LCD_CS_RST_OUT &= ~LCD_CS_PIN;                 //CS = 0 --> Start Transfer
    for (i = 0; i < 6; i++)
    {
        while (!(UCB2IFG & UCTXIFG)) ;             // Wait for TXIFG
        UCB2TXBUF = Data[i];                       // Load data

        if (i == 2)                                //Pull CS up after 3 bytes
        {
            while (UCB2STAT & UCBUSY) ;
            LCD_CS_RST_OUT |= LCD_CS_PIN;          //CS = 1 --> Stop Transfer
            LCD_CS_RST_OUT &= ~LCD_CS_PIN;         //CS = 0 --> Start Transfer
        }
    }
    while (UCB2STAT & UCBUSY) ;
    LCD_CS_RST_OUT |= LCD_CS_PIN;                  //CS = 1 --> Stop Transfer
}

/**********************************************************************//**
 * @brief  Initializes the USCI module, LCD device for communication.
 *
 * - Sets up the SPI2C Communication Module
 * - Performs Hitachi LCD Initialization Procedure
 *
 * @param  none
 *
 * @return none
 *************************************************************************/

void halLcdInit(void)
{
    LCD_CS_RST_OUT |= LCD_CS_PIN | LCD_RESET_PIN;
    LCD_CS_RST_DIR |= LCD_CS_PIN | LCD_RESET_PIN;

    LCD_BACKLT_SEL |= LCD_BACKLIGHT_PIN;

    LCD_CS_RST_OUT &= ~LCD_RESET_PIN;              // Reset LCD
    __delay_cycles(0x47FF);                        //Reset Pulse
    LCD_CS_RST_OUT |= LCD_RESET_PIN;

    // UCLK,MOSI setup, SOMI cleared
    LCD_SPI_SEL |= LCD_MOSI_PIN + LCD_CLK_PIN;
    LCD_SPI_SEL &= ~LCD_MISO_PIN;
    LCD_SPI_DIR &= ~(LCD_MISO_PIN + LCD_MOSI_PIN); // Pin direction controlled by module,
                                                   // Set both pins to input as default

    // Initialize the USCI_B2 module for SPI operation
    UCB2CTL1 = UCSWRST;                            // Hold USCI in SW reset mode while configuring
                                                   // it
    UCB2CTL0 = UCMST + UCSYNC + UCCKPL + UCMSB;    // 3-pin, 8-bit SPI master
    UCB2CTL1 |= UCSSEL_2;                          // SMCLK
    UCB2BR0 = 4;                                   // Note: Do not exceed D/S spec for UCLK!
    UCB2BR1 = 0;
    UCB2CTL1 &= ~UCSWRST;                          // Release USCI state machine
    UCB2IFG &= ~UCRXIFG;

    // Wake-up the LCD as per datasheet specifications
    halLcdActive();

    // LCD Initialization Routine Using Predefined Macros
    halLcdSendCommand(&LcdInitMacro[1 * 6]);
    halLcdSendCommand(&LcdInitMacro[2 * 6]);
    halLcdSendCommand(&LcdInitMacro[4 * 6]);
    halLcdSendCommand(&LcdInitMacro[5 * 6]);
    halLcdSendCommand(&LcdInitMacro[6 * 6]);
    halLcdSendCommand(&LcdInitMacro[7 * 6]);

}

/**********************************************************************//**
 * @brief  Shuts down the LCD display and hdisables the USCI communication.
 *
 * @param  none
 *
 * @return none
 *************************************************************************/

void halLcdShutDown(void)
{
    halLcdStandby();

    LCD_CS_RST_DIR |= LCD_CS_PIN | LCD_RESET_PIN;
    LCD_CS_RST_OUT &= ~(LCD_CS_PIN | LCD_RESET_PIN);
    LCD_CS_RST_OUT &= ~LCD_RESET_PIN;

    LCD_SPI_SEL &= ~(LCD_MOSI_PIN + LCD_CLK_PIN + LCD_MISO_PIN);
    LCD_CS_RST_DIR |= LCD_MOSI_PIN + LCD_CLK_PIN + LCD_MISO_PIN;
    LCD_CS_RST_OUT &= ~(LCD_MOSI_PIN + LCD_CLK_PIN + LCD_MISO_PIN);

    UCB2CTL0 = UCSWRST;
}

/**********************************************************************//**
 * @brief  Initializes the LCD backlight PWM signal.
 *
 * @param  none
 *
 * @return none
 *
 *************************************************************************/

void halLcdBackLightInit(void)
{
    LCD_BACKLT_DIR |= LCD_BACKLIGHT_PIN;
    LCD_BACKLT_OUT |= LCD_BACKLIGHT_PIN;
    LCD_BACKLT_SEL |= LCD_BACKLIGHT_PIN;

    TA0CCTL3 = OUTMOD_7;
    TA0CCR3 = TA0CCR0 >> 1;
    backlight = 8;

    TA0CCR0 = 400;
    TA0CTL = TASSEL_2 + MC_1;
}

/**********************************************************************//**
 * @brief  Get function for the backlight PWM's duty cycle.
 *
 * @param  none
 *
 * @return backlight One of the the 17 possible settings - valued 0 to 16.
 *
 *************************************************************************/

unsigned int halLcdGetBackLight(void)
{
    return backlight;
}

/**********************************************************************//**
 * @brief  Set function for the backlight PWM's duty cycle
 *
 * @param  BackLightLevel The target backlight duty cycle - valued 0 to 16.
 *
 * @return none
 *************************************************************************/

void halLcdSetBackLight(unsigned char BackLightLevel)
{
    unsigned int dutyCycle = 0, i, dummy;

    if (BackLightLevel > 0)
    {
        TA0CCTL3 = OUTMOD_7;
        dummy = (TA0CCR0 >> 4);

        for (i = 0; i < BackLightLevel; i++)
            dutyCycle += dummy;

        TA0CCR3 = dutyCycle;

        // If the backlight was previously turned off, turn it on.
        if (!backlight)
            TA0CTL |= MC0;
    }
    else
    {
        TA0CCTL3 = 0;
        TA0CTL &= ~MC0;
    }
    backlight = BackLightLevel;
}

/**********************************************************************//**
 * @brief  Turns off the backlight.
 *
 * Clears the respective GPIO and timer settings.
 *
 * @param  none
 *
 * @return none
 *************************************************************************/

void halLcdShutDownBackLight(void)
{
    LCD_BACKLT_DIR |= LCD_BACKLIGHT_PIN;
    LCD_BACKLT_OUT &= ~(LCD_BACKLIGHT_PIN);
    LCD_BACKLT_SEL &= ~LCD_BACKLIGHT_PIN;

    TA0CCTL3 = 0;
    TA0CTL = 0;

    backlight = 0;
}

/**********************************************************************//**
 * @brief  Set function for the contrast level of the LCD.
 *
 * @param  ContrastLevel The target contrast level
 *
 * @return none
 *************************************************************************/

void halLcdSetContrast(unsigned char ContrastLevel)
{
    if (ContrastLevel > 127) ContrastLevel = 127;
    if (ContrastLevel < 70) ContrastLevel = 70;
    LcdInitMacro[0x04 * 6 + 5] = ContrastLevel;
    halLcdSendCommand(&LcdInitMacro[0x04 * 6]);
}

/**********************************************************************//**
 * @brief  Get function for the contrast level of the LCD.
 *
 * @param  none
 *
 * @return ContrastLevel The LCD constrast level
 *************************************************************************/

unsigned char halLcdGetContrast(void)
{
    return LcdInitMacro[0x04 * 6 + 5];
}

/**********************************************************************//**
 * @brief  Turns the LCD cursor on at the current text position.
 *
 * @param  none
 *
 * @return none
 *************************************************************************/

void halLcdCursor(void)
{
    LcdInitMacro[8 * 6 + 5] ^= BIT2;
    halLcdSendCommand(&LcdInitMacro[8 * 6]);

    LcdInitMacro[0x0B * 6 + 5] = ((LcdAddress & 0x1F) << 3);
    LcdInitMacro[0x0B * 6 + 4] = ((LcdAddress & 0x1F) << 3) + 3;
    LcdInitMacro[0x0C * 6 + 5] = (LcdAddress >> 5);
    LcdInitMacro[0x0C * 6 + 4] = (LcdAddress >> 5) + 7;
    halLcdSendCommand(&LcdInitMacro[0x0B * 6]);
    halLcdSendCommand(&LcdInitMacro[0x0C * 6]);

    halLcdSetAddress(LcdAddress);
}

/**********************************************************************//**
 * @brief  Turns off the LCD cursor.
 *
 * @param  none
 *
 * @return none
 *************************************************************************/

void halLcdCursorOff(void)
{
    LcdInitMacro[8 * 6 + 5] &= ~BIT2;
    halLcdSendCommand(&LcdInitMacro[8 * 6]);
}

/**********************************************************************//**
 * @brief  Inverts the grayscale values of the LCD display (Black <> white).
 *
 * @param  none
 *
 * @return none
 *************************************************************************/

void halLcdReverse(void)
{
    LcdInitMacro[7 * 6 + 5] ^= BIT1;
    halLcdSendCommand(&LcdInitMacro[7 * 6]);
}

/**********************************************************************//**
 * @brief  Sets the LCD in standby mode to reduce power consumption.
 *
 * @param  none
 *
 * @return none
 *************************************************************************/

void halLcdStandby(void)
{
    LcdInitMacro[3 * 6 + 5] &= (~BIT3) & (~BIT2);
    LcdInitMacro[3 * 6 + 5] |= BIT0;
    halLcdSendCommand(&LcdInitMacro[3 * 6]);
}

/**********************************************************************//**
 * @brief  Puts the LCD into active mode.
 *
 * @param  none
 *
 * @return none
 *************************************************************************/

void halLcdActive(void)
{
    halLcdSendCommand(LcdInitMacro);         // R00 start oscillation

    // Wait a minimum of 25ms after issuing "start oscillation"
    // command (to accomodate for MCLK up to 25MHz)
    {
      int i;
      for(i = 0; i < 5; ++i) {
	__delay_cycles(50000);
      }
    }

    LcdInitMacro[3 * 6 + 5] |= BIT3;
    LcdInitMacro[3 * 6 + 5] &= ~BIT0;
    halLcdSendCommand(&LcdInitMacro[3 * 6]); // R03 Power control
}

/**********************************************************************//**
 * @brief  Sets the pointer location in the LCD.
 *
 * - LcdAddress      = Address
 * - LcdTableAddress = Correct Address Row + Column
 *                   = (Address / 0x20)* 17 + Column
 *
 * @param  Address The target pointer location in the LCD.
 *
 * @return none
 *************************************************************************/

void halLcdSetAddress(int Address)
{
    int temp;

    Draw_Block_Address_Macro[4] = Address >> 8;
    Draw_Block_Address_Macro[5] = Address & 0xFF;
    halLcdSendCommand(Draw_Block_Address_Macro);
    LcdAddress = Address;
    temp = Address >> 5;                     // Divided by 0x20
    temp = temp + (temp << 4);
    //Multiplied by (1+16) and added by the offset
    LcdTableAddress = temp + (Address & 0x1F);
}

/**********************************************************************//**
 * @brief  Draws a block at the specified LCD address.
 *
 * A block is the smallest addressable memory on the LCD and is
 * equivalent to 8 pixels, each of which is represented by 2 bits
 * that represent a grayscale value between 00b and 11b.
 *
 * @param  Address The address at which to draw the block.
 *
 * @param  Value   The value of the block
 *
 * @return none
 *************************************************************************/

void halLcdDrawBlock(unsigned int Address, unsigned int Value)
{
    halLcdSetAddress(Address);
    halLcdDrawCurrentBlock(Value);
}

/**********************************************************************//**
 * @brief  Writes Value to LCD CGram and MSP430 internal LCD table.
 *
 * Also updates the LcdAddress and LcdTableAddress to the correct values.
 *
 * @param  Value The value of the block to be written to the LCD.
 *
 * @return none
 *************************************************************************/

void halLcdDrawCurrentBlock(unsigned int Value)
{
    int temp;

    Draw_Block_Value_Macro[4] = Value >> 8;
    Draw_Block_Value_Macro[5] = Value & 0xFF;
    LCD_MEM[LcdTableAddress] = Value;

    halLcdSendCommand(Draw_Block_Value_Macro);

    LcdAddress++;
    temp = LcdAddress >> 5;                 // Divided by 0x20
    temp = temp + (temp << 4);
    // Multiplied by (1+16) and added by the offset
    LcdTableAddress = temp + (LcdAddress & 0x1F);

    // If LcdAddress gets off the right edge, move to next line
    if ((LcdAddress & 0x1F) > 0x11)
        halLcdSetAddress((LcdAddress & 0xFFE0) + 0x20);
    if (LcdAddress == LCD_Size)
        halLcdSetAddress(0);
}

/**********************************************************************//**
 * @brief  Returns the LCD CGRAM value at location Address.
 *
 * @param  Address The address of the block to be read from the LCD.
 *
 * @return Value   The value held at the specified address.
 *************************************************************************/

int halLcdReadBlock(unsigned int Address)
{
    int i = 0, Value = 0, ReadData[7];

    halLcdSetAddress(Address);
    halLcdSendCommand(Read_Block_Address_Macro);

    LCD_CS_RST_OUT &= ~LCD_CS_PIN;          // start transfer CS=0
    UCB2TXBUF = 0x77;                       // Transmit first character 0x77

    while (!(UCB2IFG & UCTXIFG)) ;
    while (UCB2STAT & UCBUSY) ;

    //Read 5 dummies values and 2 valid address data
    LCD_SPI_SEL &= ~LCD_MOSI_PIN;           //Change SPI2C Dir
    LCD_SPI_SEL |= LCD_MISO_PIN;

    for (i = 0; i < 7; i++)
    {
        UCB2IFG &= ~UCRXIFG;
        UCB2TXBUF = 1;                      // load dummy byte 1 for clk
        while (!(UCB2IFG & UCRXIFG)) ;
        ReadData[i] = UCB2RXBUF;
    }
    LCD_CS_RST_OUT |= LCD_CS_PIN;           // Stop Transfer CS = 1

    LCD_SPI_SEL |= LCD_MOSI_PIN;            //Change SPI2C Dir
    LCD_SPI_SEL &= ~LCD_MISO_PIN;
    LCD_CS_RST_DIR |= LCD_MOSI_PIN + LCD_CLK_PIN;
    LCD_CS_RST_DIR &= ~LCD_MISO_PIN;

    Value = (ReadData[5] << 8) + ReadData[6];
    return Value;
}

/**********************************************************************//**
 * @brief  Draw a Pixel of grayscale at coordinate (x,y) to LCD
 *
 * @param  x         x-coordinate for grayscale value
 *
 * @param  y         y-coordinate for grayscale value
 *
 * @param  GrayScale The intended grayscale value of the pixel - one of
 *                   four possible settings.
 *
 * @return none
 *************************************************************************/

void halLcdPixel(int x, int y, unsigned char GrayScale)
{
    int Address, Value;
    unsigned char offset;

    //Each line increments by 0x20
    if ((x >= 0) && (x < LCD_COL) && (y >= 0) && (y < LCD_ROW))
    {
        Address = (y << 5) + (x >> 3);            //Narrow down to 8 possible pixels

        Value = LCD_MEM[(y << 4) + y + (x >> 3)]; //y * 17 --> row. x>>3 --> column

        offset = (x & 0x07) << 1;                 //3 LSBs = pos. within the 8 columns
        Value &= ~(3 << offset);                  //clear out the corresponding bits
        Value |= GrayScale << offset;             //set pixel to GrayScale level

        halLcdDrawBlock(Address, Value);
    }
}

/**********************************************************************//**
 * @brief  Clears entire LCD CGRAM as well as LCD_MEM.
 *
 * @param  none
 *
 * @return none
 *************************************************************************/

void halLcdClearScreen(void)
{
    int i, j, k, Current_Location = 0;

    halLcdSetAddress(0);

    for (i = 0; i < 110; i++)
    {
        //prepare to send image
        LCD_CS_RST_OUT &= ~LCD_CS_PIN;             //CS = 0 --> Start Transfer
        for (k = 0; k < 3; k++)
        {
            while (!(UCB2IFG & UCTXIFG)) ;         // Wait for TXIFG
            UCB2TXBUF = Draw_Block_Value_Macro[k]; // Load data
        }
        while (UCB2STAT & UCBUSY) ;
        LCD_CS_RST_OUT |= LCD_CS_PIN;              //CS = 1 --> Stop Transfer
        LCD_CS_RST_OUT &= ~LCD_CS_PIN;             //CS = 0 --> Start Transfer
        while (!(UCB2IFG & UCTXIFG)) ;             // Wait for TXIFG
        UCB2TXBUF = Draw_Block_Value_Macro[3];     // Load data

        //send blank line
        for (j = 0; j < 17; j++)
        {
            LCD_MEM[LcdTableAddress++] = 0x00;
            while (!(UCB2IFG & UCTXIFG)) ;         // Wait for TXIFG
            UCB2TXBUF = 0x00;                      // Load data
            while (!(UCB2IFG & UCTXIFG)) ;         // Wait for TXIFG
            UCB2TXBUF = 0x00;                      // Load data
        }
        //Clear the partially visible block at the edge of the screen
        while (!(UCB2IFG & UCTXIFG)) ;             // Wait for TXIFG
        UCB2TXBUF = 0x00;                          // Load data
        while (!(UCB2IFG & UCTXIFG)) ;             // Wait for TXIFG
        UCB2TXBUF = 0x00;                          // Load data
        while (UCB2STAT & UCBUSY) ;
        LCD_CS_RST_OUT |= LCD_CS_PIN;              //CS = 1 --> Stop Transfer

        Current_Location += 0x20;
        halLcdSetAddress(Current_Location);
    }

    halLcdSetAddress(0);
}

/**********************************************************************//**
 * @brief  Loads an image of size = rows * columns, starting at the
 *         coordinate (x,y).
 *
 * @param  Image[] The image to be loaded
 *
 * @param  Rows    The number of rows in the image. Size = Rows * Columns.
 *
 * @param  Columns The number of columns in the image. Size = Rows * Columns.
 *
 * @param  x       x-coordinate of the image's starting location
 *
 * @param  y       y-coordinate of the image's starting location
 *
 * @return none
 *************************************************************************/

void halLcdImage(const unsigned int Image[], int Columns, int Rows, int x, int y)
{
    int i, CurrentLocation;

    CurrentLocation = (y << 5) + (x >> 3);
    halLcdSetAddress(CurrentLocation);
    for (i = 0; i < Rows; i++)
    {
        halLcdDrawCurrentLine(Image, Columns);
        Image += Columns;
        CurrentLocation += 0x20;
        halLcdSetAddress(CurrentLocation);
    }
}

/**********************************************************************//**
 * @brief  Writes Value to LCD CGram and MSP430 internal LCD table.
 *
 * Also updates the LcdAddress and LcdTableAddress to the correct values.
 *
 * @param  *value Pointer to the line to be written to the LCD.
 *
 * @return none
 *************************************************************************/

void halLcdDrawCurrentLine(const unsigned int *value, int Columns)
{
    unsigned char i;

    //prepare to send image
    LCD_CS_RST_OUT &= ~LCD_CS_PIN;                 //CS = 0 --> Start Transfer
    for (i = 0; i < 3; i++)
    {
        while (!(UCB2IFG & UCTXIFG)) ;             // Wait for TXIFG
        UCB2TXBUF = Draw_Block_Value_Macro[i];     // Load data
    }
    while (UCB2STAT & UCBUSY) ;
    LCD_CS_RST_OUT |= LCD_CS_PIN;                  //CS = 1 --> Stop Transfer
    LCD_CS_RST_OUT &= ~LCD_CS_PIN;                 //CS = 0 --> Start Transfer
    while (!(UCB2IFG & UCTXIFG)) ;                 // Wait for TXIFG
    UCB2TXBUF = Draw_Block_Value_Macro[3];         // Load data

    //send the image
    for (i = 0; i < Columns; i++)
    {
        // Make sure we are not writing outside LCD_MEM[]
        if (LcdTableAddress >= sizeof(LCD_MEM)){
            break;
        }
        LCD_MEM[LcdTableAddress++] = *value;
        while (!(UCB2IFG & UCTXIFG)) ;             // Wait for TXIFG
        UCB2TXBUF = (*value) >> 8;                 // Load data
        while (!(UCB2IFG & UCTXIFG)) ;             // Wait for TXIFG
        UCB2TXBUF = (*value++) & 0xFF;             // Load data
    }

    while (UCB2STAT & UCBUSY) ;
    LCD_CS_RST_OUT |= LCD_CS_PIN;                  //CS = 1 --> Stop Transfer
}

/**********************************************************************//**
 * @brief  Clears an image of size rows x columns starting at (x, y).
 *
 * @param  Columns The size, in columns, of the image to be cleared.
 *
 * @param  Rows    The size, in rows, of the image to be cleared.
 *
 * @param  x       x-coordinate of the image to be cleared
 *
 * @param  y       y-coordinate of the image to be cleared
 *
 * @return none
 *************************************************************************/

void halLcdClearImage(int Columns, int Rows, int x, int y)
{
    int i, j, k, Current_Location;

    Current_Location = (y << 5) + (x >> 3);
    halLcdSetAddress(Current_Location);

    for (i = 0; i < Rows; i++)
    {
        //prepare to send image
        LCD_CS_RST_OUT &= ~LCD_CS_PIN;             //CS = 0 --> Start Transfer
        for (k = 0; k < 3; k++)
        {
            while (!(UCB2IFG & UCTXIFG)) ;         // Wait for TXIFG
            UCB2TXBUF = Draw_Block_Value_Macro[k]; // Load data
        }
        while (UCB2STAT & UCBUSY) ;
        LCD_CS_RST_OUT |= LCD_CS_PIN;              //CS = 1 --> Stop Transfer
        LCD_CS_RST_OUT &= ~LCD_CS_PIN;             //CS = 0 --> Start Transfer
        while (!(UCB2IFG & UCTXIFG)) ;             // Wait for TXIFG
        UCB2TXBUF = Draw_Block_Value_Macro[3];     // Load data

        //send blank line
        for (j = 0; j < Columns; j++)
        {
            LCD_MEM[LcdTableAddress++] = 0x00;
            while (!(UCB2IFG & UCTXIFG)) ;         // Wait for TXIFG
            UCB2TXBUF = 0x00;                      // Load data
            while (!(UCB2IFG & UCTXIFG)) ;         // Wait for TXIFG
            UCB2TXBUF = 0x00;                      // Load data
        }
        while (UCB2STAT & UCBUSY) ;
        LCD_CS_RST_OUT |= LCD_CS_PIN;              //CS = 1 --> Stop Transfer

        Current_Location += 0x20;
        halLcdSetAddress(Current_Location);
    }
}

/**********************************************************************//**
 * @brief  Writes Value to LCD CGRAM. Pointers internal to the LCD
 *         are also updated.
 *
 * @param  Value The value to be written to the current LCD pointer
 *
 * @return none
 *************************************************************************/

void halLcdDrawTextBlock(unsigned int Value)
{
    int temp;

    Draw_Block_Value_Macro[4] = Value >> 8;
    Draw_Block_Value_Macro[5] = Value & 0xFF;
    LCD_MEM[LcdTableAddress] = Value;

    halLcdSendCommand(Draw_Block_Value_Macro);

    LcdAddress++;
    temp = LcdAddress >> 5;                 // Divided by 0x20
    temp = temp + (temp << 4);
    //Multiplied by (1+16) and added by the offset
    LcdTableAddress = temp + (LcdAddress & 0x1F);

    // If LcdAddress gets off the right edge, move to next line
    if ((LcdAddress & 0x1F) > 0x10)
        halLcdSetAddress((LcdAddress & 0xFFE0) + 0x20);

    if (LcdAddress >= LCD_Size)
        halLcdSetAddress(0);
}

/**********************************************************************//**
 * @brief  Displays the string to the LCD starting at current location.
 *
 * Writes all the data to LCD_MEM first, then updates all corresponding
 * LCD CGRAM locations at once, in a continuous fashion.
 *
 * @param  String[]  The string to be displayed on LCD.
 *
 * @param  TextStyle Value that specifies whether the string is to be
 *                   inverted or overwritten.
 *                   - Invert    = 0x01
 *                   - Overwrite = 0x04
 *
 * @return none
 *************************************************************************/

void halLcdPrint(char String[], unsigned char TextStyle)
{
    int i, j, Counter = 0, BlockValue;
    int Address, LCD_MEM_Add, ActualAddress;
    int temp;
    char LookUpChar;

    ActualAddress = LcdAddress;
    Counter =  LcdAddress & 0x1F;
    i = 0;

    while (String[i] != 0)                  // Stop on null character
    {
        LookUpChar = fonts_lookup[(unsigned char)String[i]];

        for (j = 0; j < FONT_HEIGHT; j++)
        {
            Address = ActualAddress + j * 0x20;
            temp = Address >> 5;
            temp += (temp << 4);

            LCD_MEM_Add = temp + (Address & 0x1F);

            BlockValue = LCD_MEM[LCD_MEM_Add];

            if (TextStyle & GRAYSCALE_TEXT)
            {
                if (TextStyle & INVERT_TEXT)
                    if (TextStyle & OVERWRITE_TEXT)
                        BlockValue = 0xAAAA - GrayScale_fonts[LookUpChar * (FONT_HEIGHT + 1) + j];
                    else
                        BlockValue |= 0xAAAA - GrayScale_fonts[LookUpChar * (FONT_HEIGHT + 1) + j];
                else
                if (TextStyle & OVERWRITE_TEXT)
                    BlockValue = GrayScale_fonts[LookUpChar * (FONT_HEIGHT + 1) + j];
                else
                    BlockValue |= GrayScale_fonts[LookUpChar * (FONT_HEIGHT + 1) + j];
            }
            else
            {
                if (TextStyle & INVERT_TEXT)
                    if (TextStyle & OVERWRITE_TEXT)
                        BlockValue = 0xFFFF - fonts[LookUpChar * 13 + j];
                    else
                        BlockValue |= 0xFFFF - fonts[LookUpChar * 13 + j];

                else
                if (TextStyle & OVERWRITE_TEXT)
                    BlockValue = fonts[LookUpChar * (FONT_HEIGHT + 1) + j];
                else
                    BlockValue |= fonts[LookUpChar * (FONT_HEIGHT + 1) + j];
            }
            halLcdDrawBlock(Address, BlockValue);
        }

        Counter++;
        if (Counter == 17)
        {
            Counter = 0;
            ActualAddress += 0x20 * FONT_HEIGHT  - 16;
            if (ActualAddress > LCD_Last_Pixel - 0x20 * FONT_HEIGHT)
                ActualAddress = 0;
        }
        else
            ActualAddress++;
        i++;
    }
    halLcdSetAddress(ActualAddress);

}

/**********************************************************************//**
 * @brief  Displays the string to the LCD starting at (x,y) location.
 *
 * Writes all the data to LCD_MEM first, then updates all corresponding
 * LCD CGRAM locations at once, in a continuous fashion.
 *
 * @param  String[]  String to be displayed on LCD
 *
 * @param  x         x-coordinate of the write location on the LCD
 *
 * @param  y         y-coordinate of the write location on the LCD
 *
 * @param  TextStyle Value that specifies whether the string is to be
 *                   inverted or overwritten.
 *                   - Invert    = 0x01
 *                   - Overwrite = 0x04
 *************************************************************************/

void halLcdPrintXY(char String[], int x, int y, unsigned char TextStyle)
{
    //Each line increments by 0x20
    halLcdSetAddress((y << 5) + (x >> 3));  //Narrow down to 8 possible pixels
    halLcdPrint(String,  TextStyle);
}

/**********************************************************************//**
 * @brief  Displays a string on the LCD on the specified line.
 *
 * @param  String[]  The string to be displayed on LCD.
 *
 * @param  Line      The line on the LCD on which to print the string.
 *
 * @param  TextStyle Value that specifies whether the string is to be
 *                   inverted or overwritten.
 *                   - Invert    = 0x01
 *                   - Overwrite = 0x04
 *
 * @return none
 *************************************************************************/

void halLcdPrintLine(char String[], unsigned char Line, unsigned char TextStyle)
{
    int temp;

    temp = Line * FONT_HEIGHT;
    halLcdSetAddress(temp << 5);            // 0x20 = 2^5
    halLcdPrint(String, TextStyle);
}

/**********************************************************************//**
 * @brief  Prints a string beginning on a given line and column.
 *
 * @param  String[]  The string to be displayed on LCD.
 *
 * @param  Line      The line on which to print the string of text
 *
 * @param  Col       The column on which to print the string of text
 *
 * @param  TextStyle Value that specifies whether the string is to be
 *                   inverted or overwritten.
 *                   - Invert    = 0x01
 *                   - Overwrite = 0x04
 *
 * @return none
 *************************************************************************/

void halLcdPrintLineCol(char String[], unsigned char Line, unsigned char Col,
                        unsigned char TextStyle)
{
    int temp;

    temp = Line * FONT_HEIGHT;
    temp <<= 5;
    temp += Col;

    halLcdSetAddress(temp);                 // 0x20 = 2^5
    halLcdPrint(String, TextStyle);
}

/**********************************************************************//**
 * @brief  Draws a horizontral line from (x1,y) to (x2,y) of GrayScale level
 *
 * @param  x1        x-coordinate of the first point
 *
 * @param  x2        x-coordinate of the second point
 *
 * @param  y         y-coordinate of both points
 *
 * @param  GrayScale Grayscale level of the horizontal line
 *
 * @return none
 *************************************************************************/

void halLcdHLine(int x1, int x2, int y, unsigned char GrayScale)
{
    int x_dir, x;

    if (x1 < x2)
        x_dir = 1;
    else
        x_dir = -1;
    x = x1;
    while (x != x2)
    {
        halLcdPixel(x, y, GrayScale);
        x += x_dir;
    }
}

/**********************************************************************//**
 * @brief  Draws a vertical line from (x,y1) to (x,y2) of GrayScale level
 *
 * @param  x         x-coordinate of both points
 *
 * @param  y1        y-coordinate of the first point
 *
 * @param  y2        y-coordinate of the second point
 *
 * @param  GrayScale GrayScale level of the vertical line
 *
 * @return none
 *************************************************************************/

void halLcdVLine(int x, int y1, int y2, unsigned char GrayScale)
{
    int y_dir, y;

    if (y1 < y2)
        y_dir = 1;
    else
        y_dir = -1;
    y = y1;
    while (y != y2)
    {
        halLcdPixel(x, y, GrayScale);
        y += y_dir;
    }
}

/**********************************************************************//**
 * @brief  Draws a line from (x1,y1) to (x2,y2) of GrayScale level.
 *
 * Uses Bresenham's line algorithm.
 *
 * @param  x1         x-coordinate of the first point
 *
 * @param  y1         y-coordinate of the first point
 *
 * @param  x2         x-coordinate of the second point
 *
 * @param  y2         y-coordinate of the second point
 *
 * @param  GrayScale  Grayscale level of the line
 *
 * @return none
 *************************************************************************/

void halLcdLine(int x1, int y1, int x2, int y2, unsigned char GrayScale)
{
    int x, y, deltay, deltax, d;
    int x_dir, y_dir;

    if (x1 == x2)
        halLcdVLine(x1, y1, y2, GrayScale);
    else
    {
        if (y1 == y2)
            halLcdHLine(x1, x2, y1, GrayScale);
        else                                // a diagonal line
        {
            if (x1 > x2)
                x_dir = -1;
            else x_dir = 1;
            if (y1 > y2)
                y_dir = -1;
            else y_dir = 1;

            x = x1;
            y = y1;
            deltay = ABS(y2 - y1);
            deltax = ABS(x2 - x1);

            if (deltax >= deltay)
            {
                d = (deltay << 1) - deltax;
                while (x != x2)
                {
                    halLcdPixel(x, y,  GrayScale);
                    if (d < 0)
                        d += (deltay << 1);
                    else
                    {
                        d += ((deltay - deltax) << 1);
                        y += y_dir;
                    }
                    x += x_dir;
                }
            }
            else
            {
                d = (deltax << 1) - deltay;
                while (y != y2)
                {
                    halLcdPixel(x, y, GrayScale);
                    if (d < 0)
                        d += (deltax << 1);
                    else
                    {
                        d += ((deltax - deltay) << 1);
                        x += x_dir;
                    }
                    y += y_dir;
                }
            }
        }
    }
}

/**********************************************************************//**
 * @brief  Draw a circle of Radius with center at (x,y) of GrayScale level.
 *
 * Uses Bresenham's circle algorithm
 *
 * @param  x         x-coordinate of the circle's center point
 *
 * @param  y         y-coordinate of the circle's center point
 *
 * @param  Radius    Radius of the circle
 *
 * @param  GrayScale Grayscale level of the circle
 *************************************************************************/

void halLcdCircle(int x, int y, int Radius, int GrayScale)
{
    int xx, yy, ddF_x, ddF_y, f;

    ddF_x = 0;
    ddF_y = -(2 * Radius);
    f = 1 - Radius;

    xx = 0;
    yy = Radius;
    halLcdPixel(x + xx, y + yy, GrayScale);
    halLcdPixel(x + xx, y - yy, GrayScale);
    halLcdPixel(x - xx, y + yy, GrayScale);
    halLcdPixel(x - xx, y - yy, GrayScale);
    halLcdPixel(x + yy, y + xx, GrayScale);
    halLcdPixel(x + yy, y - xx, GrayScale);
    halLcdPixel(x - yy, y + xx, GrayScale);
    halLcdPixel(x - yy, y - xx, GrayScale);
    while (xx < yy)
    {
        if (f >= 0)
        {
            yy--;
            ddF_y += 2;
            f += ddF_y;
        }
        xx++;
        ddF_x += 2;
        f += ddF_x + 1;
        halLcdPixel(x + xx, y + yy, GrayScale);
        halLcdPixel(x + xx, y - yy, GrayScale);
        halLcdPixel(x - xx, y + yy, GrayScale);
        halLcdPixel(x - xx, y - yy, GrayScale);
        halLcdPixel(x + yy, y + xx, GrayScale);
        halLcdPixel(x + yy, y - xx, GrayScale);
        halLcdPixel(x - yy, y + xx, GrayScale);
        halLcdPixel(x - yy, y - xx, GrayScale);
    }
}

/**********************************************************************//**
 * @brief  Scrolls a single row of pixels one column to the left.
 *
 * The column that is scrolled out of the left side of the LCD will be
 * displayed the right side of the LCD.
 *
 * @param  y    The row of pixels to scroll. y = 0 is at the top-left
 *              corner of the LCD.
 *
 * @return none
 *************************************************************************/

void halLcdScrollRow(int y)
{
    int i, Address, LcdTableAddressTemp;
    unsigned int temp;

    Address = y << 5;

    halLcdSetAddress(Address);

    //Multiplied by (1+16) and added by the offset
    LcdTableAddressTemp = y + (y << 4);
    temp = ((LCD_MEM[LcdTableAddressTemp] & 0x0003) << 14);

    for (i = 0; i < 0x10; i++)
        halLcdDrawCurrentBlock(((LCD_MEM[LcdTableAddressTemp + i] & 0xFFFC) >> 2) \
                               + ((LCD_MEM[LcdTableAddressTemp + i + 1] & 0x0003) << 14));

    halLcdDrawCurrentBlock(((LCD_MEM[LcdTableAddressTemp + 0x10] & 0xFFFC) >> 2) + temp);
}

/**********************************************************************//**
 * @brief  Scrolls multiple rows of pixels, yStart to yEnd,
 *         one column to the left.
 *
 * The column that is scrolled out of the left side of the LCD will be
 * displayed the right side of the LCD. y = 0 is at the top-left of the
 * LCD screen.
 *
 * @param  yStart The beginning row to be scrolled
 *
 * @param  yEnd   The last row to be scrolled
 *
 * @return none
 *************************************************************************/

void halLcdHScroll(int yStart, int yEnd)
{
    int i;

    for (i = yStart; i < yEnd + 1; i++)
        halLcdScrollRow(i);
}

/**********************************************************************//**
 * @brief  Scrolls a line of text one column to the left.
 *
 * @param  Line The line of text to be scrolled.
 *
 * @return none
 *************************************************************************/

void halLcdScrollLine(int Line)
{
    int i, Row;

    Row = Line * FONT_HEIGHT;

    for (i = Row; i < Row + FONT_HEIGHT; i++)
        halLcdScrollRow(i);
}
