/*
------------------------------------------------------------
CharacterLCD.h

Character LCD 8/4 Bit Interface 16x2, 20x4
Easily Portable to any MCU (8051, PIC, ARM, ST, Renesas, etc)

Version    : 1.0.4
Programmer : Pallav Aggarwal
Dated      : 16 June 2012
MCU        : Tested with P89V51RD2 & R8C27
------------------------------------------------------------

Limitation:
1.Functions do not check validity of parameters & its range. User need to understand what needs to input to the functions before using it.
2.Port initialisation is not done, user needs to do that seperatly in some functions like LCD_io_port_init().

*/

#ifndef _CharacterLCD_H_
#define _CharacterLCD_H_

#define LCD_INTERFACE             FOUR_BIT_HIGH

#define EIGHT_BIT                 0 //(WORKING OK)
#define FOUR_BIT_LOW              1 //(WORKING OK)
#define FOUR_BIT_HIGH             2 //(WORKING OK)
#define FOUR_BIT_RANDOM           3 //(WORKING OK)

#if LCD_INTERFACE==EIGHT_BIT
    //LCD Data Port Configuration
    #define LCD_PORT              P2
#elif  LCD_INTERFACE==FOUR_BIT_LOW
    //LCD Data Port Configuration
    #define LCD_PORT              P2
#elif  LCD_INTERFACE==FOUR_BIT_HIGH
    //LCD Data Port Configuration
    #define LCD_PORT              P2
#elif  LCD_INTERFACE==FOUR_BIT_RANDOM
    //LCD Data pins Configuration
    #define D4_PORT               P2
    #define D4_PIN                4
    #define D5_PORT               P2
    #define D5_PIN                5
    #define D6_PORT               P2
    #define D6_PIN                6
    #define D7_PORT               P2
    #define D7_PIN                7
#endif

#if LCD_INTERFACE==EIGHT_BIT
    #define LCD_INIT_COMMAND_1    0x30 /* 8bit Interface                        */
    #define LCD_INIT_COMMAND_2    0x30 /* 8bit Interface                        */
    #define LCD_INIT_COMMAND_3    0x30 /* 8bit Interface                        */
    #define LCD_INIT_COMMAND_4    0x38 /* 8bit , 2 Line                         */
    #define LCD_INIT_COMMAND_5    0x0C /* Display ON, Cursor OFF                */
    #define LCD_INIT_COMMAND_6    0x06 /* Entry Mode - No shift, Auto Increment */
    #define LCD_INIT_COMMAND_7    0x01 /* Clear LCD                             */
#elif (LCD_INTERFACE==FOUR_BIT_LOW)||(LCD_INTERFACE==FOUR_BIT_HIGH)||(LCD_INTERFACE==FOUR_BIT_RANDOM)
    #define LCD_INIT_COMMAND_1    0x20 /* 4bit Interface                        */
    #define LCD_INIT_COMMAND_2    0x20 /* 4bit Interface                        */
    #define LCD_INIT_COMMAND_3    0x20 /* 4bit Interface                        */
    #define LCD_INIT_COMMAND_4    0x28 /* 4bit , 2 Line                         */
    #define LCD_INIT_COMMAND_5    0x0C /* Display ON, Cursor OFF                */
    #define LCD_INIT_COMMAND_6    0x06 /* Entry Mode - No shift, Auto Increment */
    #define LCD_INIT_COMMAND_7    0x01 /* Clear LCD                             */
#endif

//LCD Control Pins
#define RS_PORT          P3
#define RS_PIN           7
#define EN_PORT          P3
#define EN_PIN           6

#define SETBIT(PORT,PIN) PORT |= (0x01 << PIN)
#define CLRBIT(PORT,PIN) PORT &=~(0x01 << PIN)

#define LCD_INIT_DELAY   1000
#define LCD_EN_DELAY     5

#define COMMAND          0 
#define DATA             1

#define LINE1            0x80
#define LINE2            0xC0
#define LINE3            0x94
#define LINE4            0xD4

#define YES_SHOW_LEADING_ZEROS     1
#define NO_SHOW_LEADING_ZEROS      0

void init_lcd(void);//8 Bit Interface or 4 Bit Interface LCD
void send_lcd(      unsigned char byte, //LCD Data
                    unsigned char type  //Type Command or Display Data
                    );
void put_chr_lcd(   unsigned char line_no,  // Line no of LCD
                    unsigned char position, // Position of character
                    char data_byte          // Character to print on LCD
                    );
void put_str_lcd(   unsigned char line_no,        //LINE no of LCD
                    unsigned char start_position, //Starting Position of String
                    char *ptr,                    //Pointer to String
                    unsigned char str_len         //String Length
                    );
void put_str_of_dec(unsigned char line_no,        //Line no of LCD
                    unsigned char start_position, //start position of string
                    unsigned long dec_number,     //Number
                    unsigned char str_len,        //Length of String
                    unsigned char LeadingZero     //if 1 then Display Leading Zero other wise don't display leading zero
                    );
unsigned char Dec2Str(unsigned long Number,unsigned char NoOfDigits,unsigned char lzero);
//void put_str_of_dec_with_dot(unsigned char line_no,unsigned char start_position,unsigned long dec_number,unsigned char dot_position,unsigned char str_len);
void delay(unsigned int cycles);
#endif



/*
------------------------------------------------------------
Character LCD 8/4 Bit Interface 16x2, 20x4
Easily Portable to any MCU (8051, PIC, ARM, ST, Renesas, etc)

Version    : 1.0.4
Programmer : Pallav Aggarwal
Dated      : 16 June 2012
MCU        : Tested with P89V51RD2 & R8C27
------------------------------------------------------------
*/
#include <REG51.H>
#include "CharacterLCD.h"

/*
Example to use
main()
{
    io_port_initialisation();
    init_lcd();
    init_lcd();
    init_lcd();
    while(1)
    {
    //put_chr_lcd(LINE1,15,'3');
    //put_chr_lcd(LINE2,15,'2');
    put_str_lcd(LINE1,0,"  Hello World   ",16);
    put_str_of_dec(LINE1,0,256,4,YES_SHOW_LEADING_ZEROS);
    }
}

*/

unsigned char buff[10];//Buffer to Store Converted String

void init_lcd(void)
{
    unsigned char tmp;/*Temperary Variable*/
    unsigned char LCD_init_command[]={
                                        LCD_INIT_COMMAND_1,
                                        LCD_INIT_COMMAND_2,
                                        LCD_INIT_COMMAND_3,
                                        LCD_INIT_COMMAND_4,
                                        LCD_INIT_COMMAND_5,
                                        LCD_INIT_COMMAND_6,
                                        LCD_INIT_COMMAND_7
                                        };
    for(tmp=0;tmp<7;tmp++)
    {
        send_lcd(LCD_init_command[tmp],COMMAND);
        delay(LCD_INIT_DELAY);
    }
}

void send_lcd(unsigned char data_byte, unsigned char type)
{
    //COMMAND or DATA Selection
    if(type==COMMAND)
    {
        CLRBIT(RS_PORT,RS_PIN);
    }
    else if(type==DATA)
    {
        SETBIT(RS_PORT,RS_PIN);
    }

#if LCD_INTERFACE==EIGHT_BIT
    LCD_PORT = data_byte;
    SETBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);
    CLRBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);
#elif  LCD_INTERFACE==FOUR_BIT_LOW
    //Data Higher Nibble >> Port Lower Nibble
    LCD_PORT = (LCD_PORT & 0xF0) | (data_byte >> 4);
    SETBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);
    CLRBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);

    //Data Lower Nibble >> Port Lower Nibble
    LCD_PORT = (LCD_PORT & 0xF0) | (data_byte & 0x0F);
    SETBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);
    CLRBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);
#elif  LCD_INTERFACE==FOUR_BIT_HIGH
    //Data Higher Nibble >> Port Higher Nibble
    LCD_PORT = (LCD_PORT & 0x0F) | (data_byte & 0xF0);
    SETBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);
    CLRBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);

    //Data Lower Nibble >> Port Higher Nibble
    LCD_PORT = (LCD_PORT & 0x0F) | (data_byte<<4);
    SETBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);
    CLRBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);
#elif  LCD_INTERFACE==FOUR_BIT_RANDOM
    //Higher Nibble
    if(data_byte & 0x10) SETBIT(D4_PORT,D4_PIN);
    else CLRBIT(D4_PORT,D4_PIN);
    if(data_byte & 0x20) SETBIT(D5_PORT,D5_PIN);
    else CLRBIT(D5_PORT,D5_PIN);
    if(data_byte & 0x40) SETBIT(D6_PORT,D6_PIN);
    else CLRBIT(D6_PORT,D6_PIN);
    if(data_byte & 0x80) SETBIT(D7_PORT,D7_PIN);
    else CLRBIT(D7_PORT,D7_PIN);

    SETBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);
    CLRBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);

    //Lower Nibble
    if(data_byte & 0x01) SETBIT(D4_PORT,D4_PIN);
    else CLRBIT(D4_PORT,D4_PIN);
    if(data_byte & 0x02) SETBIT(D5_PORT,D5_PIN);
    else CLRBIT(D5_PORT,D5_PIN);
    if(data_byte & 0x04) SETBIT(D6_PORT,D6_PIN);
    else CLRBIT(D6_PORT,D6_PIN);
    if(data_byte & 0x08) SETBIT(D7_PORT,D7_PIN);
    else CLRBIT(D7_PORT,D7_PIN);

    SETBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);
    CLRBIT(EN_PORT,EN_PIN);delay(LCD_EN_DELAY);
#endif
}

void put_chr_lcd(unsigned char line_no,unsigned char position,char data_byte)
{
    send_lcd(line_no + position,COMMAND);
    send_lcd(data_byte,DATA);
}

void put_str_lcd(unsigned char line_no,unsigned char start_position,char *ptr,unsigned char str_len)
{
    send_lcd(line_no + start_position,COMMAND);
    while((*ptr) && (str_len>0))
        {
            send_lcd(*ptr,DATA);
            ptr++;
            str_len--;
        }
}

void put_str_of_dec(unsigned char line_no,unsigned char start_position,unsigned long dec_number,unsigned char str_len,unsigned char LeadingZero)
{
    unsigned char return_len=0;
    return_len=Dec2Str(dec_number,str_len,LeadingZero);
    put_str_lcd(line_no,start_position,buff,str_len);
}

unsigned char Dec2Str(unsigned long Number,unsigned char NoOfDigits,unsigned char lzero)//Converts max 10 Digit Number to String
{
    unsigned char tmp=0;//temperary variable
    unsigned char len=0;
    //Clear Buffer
    if(lzero==1)
    {
    for(tmp=0;tmp<10;tmp++) buff[tmp]=0x30;
    }
    else if(lzero==0)
    {
    for(tmp=0;tmp<10;tmp++) buff[tmp]=' ';
    }

    tmp=0;

    while(Number)
    {
        buff[NoOfDigits-1]=((Number % 10) + 0x30);//Divide number by 10 & put reminder in buff + convert it to ASCII charcter by adding 0x30
        Number /= 10;//Divide Number by 10
        NoOfDigits--;
        tmp++;
    }
    return tmp;//Length of the String
}

/*
void put_str_of_dec_with_dot(unsigned char line_no,unsigned char start_position,unsigned long dec_number,unsigned char dot_position,unsigned char str_len)
{

}
*/

void delay(unsigned int cycles)
{
    while(cycles) cycles--;
}