Society of Robots - Robot Forum

Software => Software => Topic started by: TrickyNekro on May 05, 2010, 01:41:39 PM

Title: My LCD Header File - First C Header file I created -ever- :-D
Post by: TrickyNekro on May 05, 2010, 01:41:39 PM
Hello guys,
It's been a long time since I've been active in this forum, I hope you missed me :-P

So, I had some C courses and finally encouraged myself to start righting my codes in C...
Cause C is simply better than Basic...
So I got my WinAVR and my AVRlib installed and started poking around...

Cause I lack serious debugging instruments and AVR studio debugger is extremely useful but boring...
I said... hey! Let's bring an old habit alive...
What is this?
Well displaying anything I want on a simple 4x20 LCD display based on HD4478... common stuff so far...

So I said and got down to see what AVRlib has to offer (I AM aware of Webbot libraries)...

AVRlib was nice offered many things but, it didn't offer some things I would like...
And instead of poking around with premade libraries I decided to make my own...
WHY???

Because Basic gives you bad habits... I was used to using what ever port or pin I had available to connect the LCD
Something that AVRLib didn't offer...

In 4Bit mode (most of us actually use)... in AVRlib you were supposed to:

Use pins 4 - 7 for data
Have R/W line connected to MCU

Use separate line for control and separate for data (I'm not sure with this.... I don't think you do... but I posted it anyways)

So I changed some things and created my own header file!!! :D

So the code is the following...

Code: [Select]
// **********************************[ LCD PIN CONFIGURATION ]***************************************
#define LCD_PORT_INTERFACE
#define LCD_DATA_4BIT
// Enter the parameters for your chosen interface'
// if you chose the LCD_PORT_INTERFACE:
#ifdef LCD_PORT_INTERFACE
// port and pins you will use for control lines
// We can use only one Port for now
// Set DDRx and PORTx. WARNING!!!
// x Must be the same for both DDRx and PORTx
// Choose any of the available micro Ports
#define LCD_PORT PORTC
#define LCD_DDR DDRC
// As simple as it gets. Match the Port pins
// used with the LCD pins.
#define LCD_CTRL_RS 0
// Reading from LCD in 4Bit Mode is impossible
//#define LCD_CTRL_RW 3     Not used. LCD hardwired to only Write
#define LCD_CTRL_E 1
#define LCD_DATA_4 3
#define LCD_DATA_5 2
#define LCD_DATA_6 5
#define LCD_DATA_7 4
#define DB7             7 - LCD_DATA_7
#define DB6             6 - LCD_DATA_6
#define DB5             5 - LCD_DATA_5
#define DB4             4 - LCD_DATA_4
#endif
// **************************************************************************************************


// LCD display geometry
// change these definitions to adapt settings
#define LCD_LINES 4 // visible lines
#define LCD_LINE_LENGTH 20 // line length (in characters)
// cursor position to DDRAM mapping
#define LCD_LINE0_DDRAMADDR 0x00
#define LCD_LINE1_DDRAMADDR 0x40
#define LCD_LINE2_DDRAMADDR 0x14
#define LCD_LINE3_DDRAMADDR 0x54

// LCD delay
// This delay affects how quickly accesses are made to the LCD controller.
// The HD44780 LCD controller requires an access time of at least 1us.
// LCD_DELAY should be scaled to take at least half that time (500us).
// Each NOP takes 1 CPU clock cycle to execute.  Thus, at 4MHz, you should
// use at least 2 NOPs, at 8MHz at least 4 NOPs, etc.
// You can also use the delay_us(xx) command for longer access times.

// LCD_DELAY is now automatically set in lcd.h,
// however, if you define it here, this definition will override the automatic setting
// use this for a fail-safe delay
//#define LCD_DELAY delay_us(5);
// HD44780 LCD controller command set (do not modify these)
// writing:
#define LCD_CLR             0      // DB0: clear display
#define LCD_HOME            1      // DB1: return to home position
#define LCD_ENTRY_MODE      2      // DB2: set entry mode
#define LCD_ENTRY_INC       1      //   DB1: increment
#define LCD_ENTRY_SHIFT     0      //   DB2: shift
#define LCD_ON_CTRL         3      // DB3: turn lcd/cursor on
#define LCD_ON_DISPLAY      2      //   DB2: turn display on
#define LCD_ON_CURSOR       1      //   DB1: turn cursor on
#define LCD_ON_BLINK        0      //   DB0: blinking cursor
#define LCD_MOVE            4      // DB4: move cursor/display
#define LCD_MOVE_DISP       3      //   DB3: move display (0-> move cursor)
#define LCD_MOVE_RIGHT      2      //   DB2: move right (0-> left)
#define LCD_FUNCTION        5      // DB5: function set
#define LCD_FUNCTION_8BIT   4      //   DB4: set 8BIT mode (0->4BIT mode)
#define LCD_FUNCTION_2LINES 3      //   DB3: two lines (0->one line)
#define LCD_FUNCTION_10DOTS 2      //   DB2: 5x10 font (0->5x7 font)
#define LCD_CGRAM           6      // DB6: set CG RAM address
#define LCD_DDRAM           7      // DB7: set DD RAM address
// reading:
#define LCD_BUSY            7      // DB7: LCD is busy

// Default LCD setup
// this default setup is loaded on LCD initialization
#ifdef LCD_DATA_4BIT
#define LCD_FDEF_1 (0<<LCD_FUNCTION_8BIT)
#else
#define LCD_FDEF_1 (1<<LCD_FUNCTION_8BIT)
#endif
#define LCD_FDEF_2 (1<<LCD_FUNCTION_2LINES)
#define LCD_FUNCTION_DEFAULT ((1<<LCD_FUNCTION) | LCD_FDEF_1 | LCD_FDEF_2)
#define LCD_MODE_DEFAULT ((1<<LCD_ENTRY_MODE) | (1<<LCD_ENTRY_INC))

// custom LCD characters
extern unsigned char __attribute__ ((progmem)) LcdCustomChar[];
#define LCDCHAR_PROGRESS05 0 // 0/5 full progress block
#define LCDCHAR_PROGRESS15 1 // 1/5 full progress block
#define LCDCHAR_PROGRESS25 2 // 2/5 full progress block
#define LCDCHAR_PROGRESS35 3 // 3/5 full progress block
#define LCDCHAR_PROGRESS45 4 // 4/5 full progress block
#define LCDCHAR_PROGRESS55 5 // 5/5 full progress block
#define LCDCHAR_REWINDARROW 6 // rewind arrow
#define LCDCHAR_STOPBLOCK 7 // stop block
#define LCDCHAR_PAUSEBARS 8 // pause bars
#define LCDCHAR_FORWARDARROW 9 // fast-forward arrow
#define LCDCHAR_SCROLLUPARROW 10 // scroll up arrow
#define LCDCHAR_SCROLLDNARROW 11 // scroll down arrow
#define LCDCHAR_BLANK 12 // scroll down arrow
#define LCDCHAR_ANIPLAYICON0 13 // animated play icon frame 0
#define LCDCHAR_ANIPLAYICON1 14 // animated play icon frame 1
#define LCDCHAR_ANIPLAYICON2 15 // animated play icon frame 2
#define LCDCHAR_ANIPLAYICON3 16 // animated play icon frame 3

// progress bar defines
#define PROGRESSPIXELS_PER_CHAR 6


// custom LCD characters
unsigned char __attribute__ ((progmem)) LcdCustomChar[] =
{
0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, // 0. 0/5 full progress block
0x00, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x1F, 0x00, // 1. 1/5 full progress block
0x00, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, // 2. 2/5 full progress block
0x00, 0x1F, 0x1C, 0x1C, 0x1C, 0x1C, 0x1F, 0x00, // 3. 3/5 full progress block
0x00, 0x1F, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x00, // 4. 4/5 full progress block
0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, // 5. 5/5 full progress block
0x03, 0x07, 0x0F, 0x1F, 0x0F, 0x07, 0x03, 0x00, // 6. rewind arrow
0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, // 7. stop block
0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, // 8. pause bars
0x18, 0x1C, 0x1E, 0x1F, 0x1E, 0x1C, 0x18, 0x00, // 9. fast-forward arrow
0x00, 0x04, 0x04, 0x0E, 0x0E, 0x1F, 0x1F, 0x00, // 10. scroll up arrow
0x00, 0x1F, 0x1F, 0x0E, 0x0E, 0x04, 0x04, 0x00, // 11. scroll down arrow
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 12. blank character
0x00, 0x0E, 0x19, 0x15, 0x13, 0x0E, 0x00, 0x00, // 13. animated play icon frame 0
0x00, 0x0E, 0x15, 0x15, 0x15, 0x0E, 0x00, 0x00, // 14. animated play icon frame 1
0x00, 0x0E, 0x13, 0x15, 0x19, 0x0E, 0x00, 0x00, // 15. animated play icon frame 2
0x00, 0x0E, 0x11, 0x1F, 0x11, 0x0E, 0x00, 0x00, // 16. animated play icon frame 3
};

/*************************************************************/
/********************** LOCAL FUNCTIONS **********************/
/*************************************************************/
// *******************************************
void lcdInitHW (void)
{
// Initialise I/O ports
// if I/O interface is in use
#ifdef LCD_PORT_INTERFACE
// initialize LCD control lines
cbi (LCD_PORT, LCD_CTRL_RS);
cbi (LCD_PORT, LCD_CTRL_E);
// initialise LCD control lines to output
sbi (LCD_DDR, LCD_CTRL_RS);
sbi (LCD_DDR, LCD_CTRL_E);
// initialize the LCD data port to input
// initialize the LCD  lines to pull-up
cbi (LCD_DDR, LCD_DATA_4); // Set data
cbi (LCD_DDR, LCD_DATA_5); // I/O lines
cbi (LCD_DDR, LCD_DATA_6); // to input
cbi (LCD_DDR, LCD_DATA_7); // (4bit)
// *******************************************
sbi (LCD_PORT, LCD_DATA_4); // Set
sbi (LCD_PORT, LCD_DATA_5); // pull-ups
sbi (LCD_PORT, LCD_DATA_6); // to on
sbi (LCD_PORT, LCD_DATA_7); // (4bit)

#endif
}
// *******************************************
// *******************************************

void out_LCD(u08 data)
{

LCD_PORT |= ((data&128)>>(DB7))|((data&64)>>(DB6))|((data&32)>>(DB5))|((data&16)>>(DB4));

}


void lcdControlWrite(u08 data)
{
//write control byte to the display controller
#ifdef LCD_PORT_INTERFACE
    DELAY(100);

cbi (LCD_PORT, LCD_CTRL_RS);// set Rs to control
sbi (LCD_PORT, LCD_CTRL_E); // set "E" line
cbi (LCD_PORT, LCD_DATA_4); //
cbi (LCD_PORT, LCD_DATA_5); //
cbi (LCD_PORT, LCD_DATA_6); //
cbi (LCD_PORT, LCD_DATA_7); //
sbi (LCD_DDR, LCD_DATA_4); // Set data
sbi (LCD_DDR, LCD_DATA_5); // I/O lines
sbi (LCD_DDR, LCD_DATA_6); // to output
sbi (LCD_DDR, LCD_DATA_7); // (4bit)

// Send the HIGH.BYTE to the LCD
out_LCD(data);
LCD_DELAY;
LCD_DELAY;
cbi (LCD_PORT, LCD_CTRL_E); // clear "E" line
cbi (LCD_PORT, LCD_DATA_4); //
cbi (LCD_PORT, LCD_DATA_5); //
cbi (LCD_PORT, LCD_DATA_6); //
cbi (LCD_PORT, LCD_DATA_7); //
LCD_DELAY;
LCD_DELAY;
sbi (LCD_PORT, LCD_CTRL_E); // set "E" line
data <<=4;
// Send the LOW.BYTE to the LCD
out_LCD(data);
LCD_DELAY;
LCD_DELAY;
cbi (LCD_PORT, LCD_CTRL_E); // clear "E" line
cbi (LCD_PORT, LCD_DATA_4); //
cbi (LCD_PORT, LCD_DATA_5); //
cbi (LCD_PORT, LCD_DATA_6); //
cbi (LCD_PORT, LCD_DATA_7); //
//**********************************************
//**********************************************
//**********************************************
cbi (LCD_DDR, LCD_DATA_4); // Set data
cbi (LCD_DDR, LCD_DATA_5); // I/O lines
cbi (LCD_DDR, LCD_DATA_6); // to input
cbi (LCD_DDR, LCD_DATA_7); // (4bit)
// *******************************************
sbi (LCD_PORT, LCD_DATA_4); // Set
sbi (LCD_PORT, LCD_DATA_5); // pull-ups
sbi (LCD_PORT, LCD_DATA_6); // to on
sbi (LCD_PORT, LCD_DATA_7); // (4bit)

#endif
}

void lcdDataWrite(u08 data)
{
//write data byte to the display controller
#ifdef LCD_PORT_INTERFACE
    DELAY(100);

sbi (LCD_PORT, LCD_CTRL_RS);// set Rs to control
sbi (LCD_PORT, LCD_CTRL_E); // set "E" line
cbi (LCD_PORT, LCD_DATA_4); //
cbi (LCD_PORT, LCD_DATA_5); //
cbi (LCD_PORT, LCD_DATA_6); //
cbi (LCD_PORT, LCD_DATA_7); //
sbi (LCD_DDR, LCD_DATA_4); // Set data
sbi (LCD_DDR, LCD_DATA_5); // I/O lines
sbi (LCD_DDR, LCD_DATA_6); // to output
sbi (LCD_DDR, LCD_DATA_7); // (4bit)

// Send the HIGH.BYTE to the LCD
out_LCD(data);
LCD_DELAY;
LCD_DELAY;
cbi (LCD_PORT, LCD_CTRL_E); // clear "E" line
cbi (LCD_PORT, LCD_DATA_4); //
cbi (LCD_PORT, LCD_DATA_5); //
cbi (LCD_PORT, LCD_DATA_6); //
cbi (LCD_PORT, LCD_DATA_7); //
LCD_DELAY;
LCD_DELAY;
sbi (LCD_PORT, LCD_CTRL_E); // set "E" line

data <<=4;
// Send the LOW.BYTE to the LCD
out_LCD(data);
//LCD_PORT |= ((data&128)>>(3))|((data&64)>>(1))|((data&32)>>(3))|((data&16)>>(1));
LCD_DELAY;
LCD_DELAY;
cbi (LCD_PORT, LCD_CTRL_E); // clear "E" line
cbi (LCD_PORT, LCD_DATA_4); //
cbi (LCD_PORT, LCD_DATA_5); //
cbi (LCD_PORT, LCD_DATA_6); //
cbi (LCD_PORT, LCD_DATA_7); //
//**********************************************
//**********************************************
//**********************************************
cbi (LCD_DDR, LCD_DATA_4); // Set data
cbi (LCD_DDR, LCD_DATA_5); // I/O lines
cbi (LCD_DDR, LCD_DATA_6); // to input
cbi (LCD_DDR, LCD_DATA_7); // (4bit)
// *******************************************
sbi (LCD_PORT, LCD_DATA_4); // Set
sbi (LCD_PORT, LCD_DATA_5); // pull-ups
sbi (LCD_PORT, LCD_DATA_6); // to on
sbi (LCD_PORT, LCD_DATA_7); // (4bit)

#endif
}

/*************************************************************/
/********************* PUBLIC FUNCTIONS **********************/
/*************************************************************/

void lcdInit(void)
{
// initialize hardware
lcdInitHW();
// LCD function set
lcdControlWrite(LCD_FUNCTION_DEFAULT);
// clear LCD
lcdControlWrite(1<<LCD_CLR);
DELAY(6000); // wait 60ms
// set entry mode
lcdControlWrite(1<<LCD_ENTRY_MODE | 1<<LCD_ENTRY_INC);
// set display to on
lcdControlWrite(1<<LCD_ON_CTRL | 1<<LCD_ON_DISPLAY | 1<<LCD_ON_BLINK);
//lcdControlWrite(1<<LCD_ON_CTRL | 1<<LCD_ON_DISPLAY );
// move cursor to home
lcdControlWrite(1<<LCD_HOME);

}

void lcdHome(void)
{
// move cursor to home
lcdControlWrite(1<<LCD_HOME);
}

void lcdClear(void)
{
// clear LCD
lcdControlWrite(1<<LCD_CLR);
}

void lcdGotoXY(u08 x, u08 y)
{
register u08 DDRAMAddr;

// remap lines into proper order
switch(y)
{
case 0: DDRAMAddr = LCD_LINE0_DDRAMADDR+x; break;
case 1: DDRAMAddr = LCD_LINE1_DDRAMADDR+x; break;
case 2: DDRAMAddr = LCD_LINE2_DDRAMADDR+x; break;
case 3: DDRAMAddr = LCD_LINE3_DDRAMADDR+x; break;
default: DDRAMAddr = LCD_LINE0_DDRAMADDR+x;
}

// set data address
lcdControlWrite(1<<LCD_DDRAM | DDRAMAddr);
}


void LCD(char* data, u08 nBytes)
{
register u08 i;

// check to make sure we have a good pointer
if (!data) return;

// print data
for(i=0; i<nBytes; i++)
{
lcdDataWrite(data[i]);
}
}

void lcdProgressBar(u16 progress, u16 maxprogress, u08 length)
{
u08 i;
u32 pixelprogress;
u08 c;

// draw a progress bar displaying (progress / maxprogress)
// starting from the current cursor position
// with a total length of "length" characters
// ***note, LCD chars 0-5 must be programmed as the bar characters
// char 0 = empty ... char 5 = full

// total pixel length of bargraph equals length*PROGRESSPIXELS_PER_CHAR;
// pixel length of bar itself is
pixelprogress = ((progress*(length*PROGRESSPIXELS_PER_CHAR))/maxprogress);

// print exactly "length" characters
for(i=0; i<length; i++)
{
// check if this is a full block, or partial or empty
// (u16) cast is needed to avoid sign comparison warning
if( ((i*(u16)PROGRESSPIXELS_PER_CHAR)+5) > pixelprogress )
{
// this is a partial or empty block
if( ((i*(u16)PROGRESSPIXELS_PER_CHAR)) > pixelprogress )
{
// this is an empty block
// use space character?
c = 0;
}
else
{
// this is a partial block
c = pixelprogress % PROGRESSPIXELS_PER_CHAR;
}
}
else
{
// this is a full block
c = 5;
}

// write character to display
lcdDataWrite(c);
}

}


Which is supposed to work with the following in conjunction with this global header:

Code: [Select]
// Define System characteristics
#define F_CPU 16000000 // 16Mhz CPU core clock
#define CYCLES_PER_US ((F_CPU+500000)/1000000) // cpu cycles per microsecond


// Define some Macros

typedef unsigned char  u08; // 0 : 255
typedef signed char     s08; // -128 : 127
typedef unsigned short      u16; // 0 : 65535
typedef signed short    s16; // -32768 : 32767
typedef unsigned long  u32; // 0 : 4294967295
typedef signed long    s32; // -2147483648 : 2147483647
typedef unsigned long long  u64; //
typedef signed long long    s64; //

//Define some very useful Macros Operators

#ifndef BV
#define BV(bit) (1<<(bit))
#endif
#ifndef cbi
#define cbi(reg,bit) reg &= ~(BV(bit))
#endif
#ifndef sbi
#define sbi(reg,bit) reg |= (BV(bit))
#endif
#ifndef cli
#define cli() __asm__ __volatile__ ("cli" ::)
#endif
#ifndef sei
#define sei() __asm__ __volatile__ ("sei" ::)
#endif


// Simple DELAY Function
void DELAY(volatile u32 i)
{
while(i)
 {
 i--;
 }

return;
}

//LCD_Delay_time
// So that if the User hasn't defined the
#ifndef LCD_DELAY


#if F_CPU >= 16000000
#define LCD_DELAY asm volatile ("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
#else
#if F_CPU >= 12000000
#define LCD_DELAY asm volatile ("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
#else
#if F_CPU >= 8000000
#define LCD_DELAY asm volatile ("nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
#else
#if F_CPU >= 4000000
#define LCD_DELAY asm volatile ("nop\n nop\n nop\n nop\n nop\n");
#else
#define LCD_DELAY asm volatile ("nop\n nop\n nop\n");

#endif
#endif
#endif
#endif
#endif


The code is optimized so far for a microcontroller that works at 16MHz....
Less it's gonna be more slow than it needs to be...
More it maybe isn't going to work right...
So far you NEED to use one port exclusively but this is going to change.


Also the one big difference is that you don't need the R/W line (which Should be hardwired to ground)
and you can use what ever pin of this "exclusive" port as data lines....
In other words you can define the data lines one by one as you want!!!

This is my approach to a C header file for the LCD, and I just wanted to posted it some
you people can comment and suggest (cause it's my first header file after all)....
And it's some of my first steps in C coding...

I'll try including all the time issues and make it as fast as I can...
Perhaps I can add even more functions...

Anyways, thanks for trying!!!

Lefteris, Greece
Title: Re: My LCD Header File - First C Header file I created -ever- :-D
Post by: TrickyNekro on May 06, 2010, 07:43:22 PM
So as I promised...
I'll be updating the Header file....

I added function LCD_N.
LCD_N outputs numbers that are stored in variables... To keep the code size small enough
the largest value that can be displayed is that of an unsigned short variable or 65535...

The function runs so far in 4 modes...
Modes can be changed in program by the AVR core itself....
So it goes as follows...



Cell holding decimal number mode (mode 0)...

In this mode the LCD outputs a standard of 5 zeros and replaces the zeros with the number sent...
For example...

In LCD screen you will get 00123, 01023, 32451, 4530, 00001.... and so on....

In other words in this mode 5 cells with be always hold to display the data...


Non-Cell holding mode (mode 1)

I think you  get the rest.. The LCD will type 123, 1023, 32451, 4530, 1 and so on...


HEX mode (mode 2)

Instead of a decimal number in exports a HEX number....

So you will get... FFFF, AB3, 86B, F9, 9, E and so on...

As you have noticed there is no cell locking... I'll upgrade it as soon as I have more time to have both modes...

Binary mode (mode 3)

The same thing with binary... Binary come really really really in handy when you want to take a look at a register...

Of course in this mode cell locking is desired by natural so I won't upgrade this function...
But instead I'll upgrade it so you can choose between HIGH.BYTE and LOW.BYTE... and which one you like to display...

So you will see in this mode something like this...

0000100001101011, 1100101100011100, 0000000010110001 and so on....

Hope you like my work... And I'll keep upgrading as soon as I have free time!
It's more than possible that I'll get a good camera so I'll hook up an Axon to the LCD and do a demo...

It's all for Admin after all  ;D ;D ;D ;D


Here's the LCD Header... Again working with the same Global I posted before...

Code: [Select]
// **********************************[ LCD PIN CONFIGURATION ]***************************************
#define LCD_PORT_INTERFACE
#define LCD_DATA_4BIT
// Enter the parameters for your chosen interface'
// if you chose the LCD_PORT_INTERFACE:
#ifdef LCD_PORT_INTERFACE
// port and pins you will use for control lines
// We can use only one Port for now
// Set DDRx and PORTx. WARNING!!!
// x Must be the same for both DDRx and PORTx
// Choose any of the available micro Ports
#define LCD_PORT PORTC
#define LCD_DDR DDRC
// As simple as it gets. Match the Port pins
// used with the LCD pins.
#define LCD_CTRL_RS 0
// Reading from LCD in 4Bit Mode is impossible
//#define LCD_CTRL_RW 3     Not used. LCD hardwired to only Write
#define LCD_CTRL_E 1
#define LCD_DATA_4 3
#define LCD_DATA_5 2
#define LCD_DATA_6 5
#define LCD_DATA_7 4
#define DB7             7 - LCD_DATA_7
#define DB6             6 - LCD_DATA_6
#define DB5             5 - LCD_DATA_5
#define DB4             4 - LCD_DATA_4
#endif
// **************************************************************************************************


// LCD display geometry
// change these definitions to adapt settings
#define LCD_LINES 4 // visible lines
#define LCD_LINE_LENGTH 20 // line length (in characters)
// cursor position to DDRAM mapping
#define LCD_LINE0_DDRAMADDR 0x00
#define LCD_LINE1_DDRAMADDR 0x40
#define LCD_LINE2_DDRAMADDR 0x14
#define LCD_LINE3_DDRAMADDR 0x54

// LCD delay
// This delay affects how quickly accesses are made to the LCD controller.
// The HD44780 LCD controller requires an access time of at least 1us.
// LCD_DELAY should be scaled to take at least half that time (500us).
// Each NOP takes 1 CPU clock cycle to execute.  Thus, at 4MHz, you should
// use at least 2 NOPs, at 8MHz at least 4 NOPs, etc.
// You can also use the delay_us(xx) command for longer access times.

// LCD_DELAY is now automatically set in lcd.h,
// however, if you define it here, this definition will override the automatic setting
// use this for a fail-safe delay
//#define LCD_DELAY delay_us(5);
// HD44780 LCD controller command set (do not modify these)
// writing:
#define LCD_CLR             0      // DB0: clear display
#define LCD_HOME            1      // DB1: return to home position
#define LCD_ENTRY_MODE      2      // DB2: set entry mode
#define LCD_ENTRY_INC       1      //   DB1: increment
#define LCD_ENTRY_SHIFT     0      //   DB2: shift
#define LCD_ON_CTRL         3      // DB3: turn lcd/cursor on
#define LCD_ON_DISPLAY      2      //   DB2: turn display on
#define LCD_ON_CURSOR       1      //   DB1: turn cursor on
#define LCD_ON_BLINK        0      //   DB0: blinking cursor
#define LCD_MOVE            4      // DB4: move cursor/display
#define LCD_MOVE_DISP       3      //   DB3: move display (0-> move cursor)
#define LCD_MOVE_RIGHT      2      //   DB2: move right (0-> left)
#define LCD_FUNCTION        5      // DB5: function set
#define LCD_FUNCTION_8BIT   4      //   DB4: set 8BIT mode (0->4BIT mode)
#define LCD_FUNCTION_2LINES 3      //   DB3: two lines (0->one line)
#define LCD_FUNCTION_10DOTS 2      //   DB2: 5x10 font (0->5x7 font)
#define LCD_CGRAM           6      // DB6: set CG RAM address
#define LCD_DDRAM           7      // DB7: set DD RAM address
// reading:
#define LCD_BUSY            7      // DB7: LCD is busy

// Default LCD setup
// this default setup is loaded on LCD initialization
#ifdef LCD_DATA_4BIT
#define LCD_FDEF_1 (0<<LCD_FUNCTION_8BIT)
#else
#define LCD_FDEF_1 (1<<LCD_FUNCTION_8BIT)
#endif
#define LCD_FDEF_2 (1<<LCD_FUNCTION_2LINES)
#define LCD_FUNCTION_DEFAULT ((1<<LCD_FUNCTION) | LCD_FDEF_1 | LCD_FDEF_2)
#define LCD_MODE_DEFAULT ((1<<LCD_ENTRY_MODE) | (1<<LCD_ENTRY_INC))

// custom LCD characters
extern unsigned char __attribute__ ((progmem)) LcdCustomChar[];
#define LCDCHAR_PROGRESS05 0 // 0/5 full progress block
#define LCDCHAR_PROGRESS15 1 // 1/5 full progress block
#define LCDCHAR_PROGRESS25 2 // 2/5 full progress block
#define LCDCHAR_PROGRESS35 3 // 3/5 full progress block
#define LCDCHAR_PROGRESS45 4 // 4/5 full progress block
#define LCDCHAR_PROGRESS55 5 // 5/5 full progress block
#define LCDCHAR_REWINDARROW 6 // rewind arrow
#define LCDCHAR_STOPBLOCK 7 // stop block
#define LCDCHAR_PAUSEBARS 8 // pause bars
#define LCDCHAR_FORWARDARROW 9 // fast-forward arrow
#define LCDCHAR_SCROLLUPARROW 10 // scroll up arrow
#define LCDCHAR_SCROLLDNARROW 11 // scroll down arrow
#define LCDCHAR_BLANK 12 // scroll down arrow
#define LCDCHAR_ANIPLAYICON0 13 // animated play icon frame 0
#define LCDCHAR_ANIPLAYICON1 14 // animated play icon frame 1
#define LCDCHAR_ANIPLAYICON2 15 // animated play icon frame 2
#define LCDCHAR_ANIPLAYICON3 16 // animated play icon frame 3

// progress bar defines
#define PROGRESSPIXELS_PER_CHAR 6

/*
// custom LCD characters
unsigned char __attribute__ ((progmem)) LcdCustomChar[] =
{
0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, // 0. 0/5 full progress block
0x00, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x1F, 0x00, // 1. 1/5 full progress block
0x00, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, // 2. 2/5 full progress block
0x00, 0x1F, 0x1C, 0x1C, 0x1C, 0x1C, 0x1F, 0x00, // 3. 3/5 full progress block
0x00, 0x1F, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x00, // 4. 4/5 full progress block
0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, // 5. 5/5 full progress block
0x03, 0x07, 0x0F, 0x1F, 0x0F, 0x07, 0x03, 0x00, // 6. rewind arrow
0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, // 7. stop block
0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, // 8. pause bars
0x18, 0x1C, 0x1E, 0x1F, 0x1E, 0x1C, 0x18, 0x00, // 9. fast-forward arrow
0x00, 0x04, 0x04, 0x0E, 0x0E, 0x1F, 0x1F, 0x00, // 10. scroll up arrow
0x00, 0x1F, 0x1F, 0x0E, 0x0E, 0x04, 0x04, 0x00, // 11. scroll down arrow
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 12. blank character
0x00, 0x0E, 0x19, 0x15, 0x13, 0x0E, 0x00, 0x00, // 13. animated play icon frame 0
0x00, 0x0E, 0x15, 0x15, 0x15, 0x0E, 0x00, 0x00, // 14. animated play icon frame 1
0x00, 0x0E, 0x13, 0x15, 0x19, 0x0E, 0x00, 0x00, // 15. animated play icon frame 2
0x00, 0x0E, 0x11, 0x1F, 0x11, 0x0E, 0x00, 0x00, // 16. animated play icon frame 3
};
*/

/*************************************************************/
/********************** LOCAL FUNCTIONS **********************/
/*************************************************************/
// *******************************************
void lcdInitHW (void)
{
// Initialise I/O ports
// if I/O interface is in use
#ifdef LCD_PORT_INTERFACE
// initialize LCD control lines
cbi (LCD_PORT, LCD_CTRL_RS);
cbi (LCD_PORT, LCD_CTRL_E);
// initialise LCD control lines to output
sbi (LCD_DDR, LCD_CTRL_RS);
sbi (LCD_DDR, LCD_CTRL_E);
// initialize the LCD data port to input
// initialize the LCD  lines to pull-up
cbi (LCD_DDR, LCD_DATA_4); // Set data
cbi (LCD_DDR, LCD_DATA_5); // I/O lines
cbi (LCD_DDR, LCD_DATA_6); // to input
cbi (LCD_DDR, LCD_DATA_7); // (4bit)
// *******************************************
sbi (LCD_PORT, LCD_DATA_4); // Set
sbi (LCD_PORT, LCD_DATA_5); // pull-ups
sbi (LCD_PORT, LCD_DATA_6); // to on
sbi (LCD_PORT, LCD_DATA_7); // (4bit)

#endif
}
// *******************************************
// *******************************************

void out_LCD(u08 data)
{

LCD_PORT |= ((data&128)>>(DB7))|((data&64)>>(DB6))|((data&32)>>(DB5))|((data&16)>>(DB4));

}


void lcdControlWrite(u08 data)
{
//write control byte to the display controller
#ifdef LCD_PORT_INTERFACE
    DELAY(100);

cbi (LCD_PORT, LCD_CTRL_RS);// set Rs to control
sbi (LCD_PORT, LCD_CTRL_E); // set "E" line
cbi (LCD_PORT, LCD_DATA_4); //
cbi (LCD_PORT, LCD_DATA_5); //
cbi (LCD_PORT, LCD_DATA_6); //
cbi (LCD_PORT, LCD_DATA_7); //
sbi (LCD_DDR, LCD_DATA_4); // Set data
sbi (LCD_DDR, LCD_DATA_5); // I/O lines
sbi (LCD_DDR, LCD_DATA_6); // to output
sbi (LCD_DDR, LCD_DATA_7); // (4bit)

// Send the HIGH.BYTE to the LCD
out_LCD(data);
LCD_DELAY;
LCD_DELAY;
cbi (LCD_PORT, LCD_CTRL_E); // clear "E" line
cbi (LCD_PORT, LCD_DATA_4); //
cbi (LCD_PORT, LCD_DATA_5); //
cbi (LCD_PORT, LCD_DATA_6); //
cbi (LCD_PORT, LCD_DATA_7); //
LCD_DELAY;
LCD_DELAY;
sbi (LCD_PORT, LCD_CTRL_E); // set "E" line
data <<=4;
// Send the LOW.BYTE to the LCD
out_LCD(data);
LCD_DELAY;
LCD_DELAY;
cbi (LCD_PORT, LCD_CTRL_E); // clear "E" line
cbi (LCD_PORT, LCD_DATA_4); //
cbi (LCD_PORT, LCD_DATA_5); //
cbi (LCD_PORT, LCD_DATA_6); //
cbi (LCD_PORT, LCD_DATA_7); //
//**********************************************
//**********************************************
//**********************************************
cbi (LCD_DDR, LCD_DATA_4); // Set data
cbi (LCD_DDR, LCD_DATA_5); // I/O lines
cbi (LCD_DDR, LCD_DATA_6); // to input
cbi (LCD_DDR, LCD_DATA_7); // (4bit)
// *******************************************
sbi (LCD_PORT, LCD_DATA_4); // Set
sbi (LCD_PORT, LCD_DATA_5); // pull-ups
sbi (LCD_PORT, LCD_DATA_6); // to on
sbi (LCD_PORT, LCD_DATA_7); // (4bit)

#endif
}

void lcdDataWrite(u08 data)
{
//write data byte to the display controller
#ifdef LCD_PORT_INTERFACE
    DELAY(100);

sbi (LCD_PORT, LCD_CTRL_RS);// set Rs to control
sbi (LCD_PORT, LCD_CTRL_E); // set "E" line
cbi (LCD_PORT, LCD_DATA_4); //
cbi (LCD_PORT, LCD_DATA_5); //
cbi (LCD_PORT, LCD_DATA_6); //
cbi (LCD_PORT, LCD_DATA_7); //
sbi (LCD_DDR, LCD_DATA_4); // Set data
sbi (LCD_DDR, LCD_DATA_5); // I/O lines
sbi (LCD_DDR, LCD_DATA_6); // to output
sbi (LCD_DDR, LCD_DATA_7); // (4bit)

// Send the HIGH.BYTE to the LCD
out_LCD(data);
LCD_DELAY;
LCD_DELAY;
cbi (LCD_PORT, LCD_CTRL_E); // clear "E" line
cbi (LCD_PORT, LCD_DATA_4); //
cbi (LCD_PORT, LCD_DATA_5); //
cbi (LCD_PORT, LCD_DATA_6); //
cbi (LCD_PORT, LCD_DATA_7); //
LCD_DELAY;
LCD_DELAY;
sbi (LCD_PORT, LCD_CTRL_E); // set "E" line

data <<=4;
// Send the LOW.BYTE to the LCD
out_LCD(data);
//LCD_PORT |= ((data&128)>>(3))|((data&64)>>(1))|((data&32)>>(3))|((data&16)>>(1));
LCD_DELAY;
LCD_DELAY;
cbi (LCD_PORT, LCD_CTRL_E); // clear "E" line
cbi (LCD_PORT, LCD_DATA_4); //
cbi (LCD_PORT, LCD_DATA_5); //
cbi (LCD_PORT, LCD_DATA_6); //
cbi (LCD_PORT, LCD_DATA_7); //
//**********************************************
//**********************************************
//**********************************************
cbi (LCD_DDR, LCD_DATA_4); // Set data
cbi (LCD_DDR, LCD_DATA_5); // I/O lines
cbi (LCD_DDR, LCD_DATA_6); // to input
cbi (LCD_DDR, LCD_DATA_7); // (4bit)
// *******************************************
sbi (LCD_PORT, LCD_DATA_4); // Set
sbi (LCD_PORT, LCD_DATA_5); // pull-ups
sbi (LCD_PORT, LCD_DATA_6); // to on
sbi (LCD_PORT, LCD_DATA_7); // (4bit)

#endif
}

/*************************************************************/
/********************* PUBLIC FUNCTIONS **********************/
/*************************************************************/

void lcdInit(void)
{
// initialize hardware
lcdInitHW();
// LCD function set
lcdControlWrite(LCD_FUNCTION_DEFAULT);
// clear LCD
lcdControlWrite(1<<LCD_CLR);
DELAY(6000); // wait 60ms
// set entry mode
lcdControlWrite(1<<LCD_ENTRY_MODE | 1<<LCD_ENTRY_INC);
// set display to on
lcdControlWrite(1<<LCD_ON_CTRL | 1<<LCD_ON_DISPLAY | 1<<LCD_ON_BLINK);
//lcdControlWrite(1<<LCD_ON_CTRL | 1<<LCD_ON_DISPLAY );
// move cursor to home
lcdControlWrite(1<<LCD_HOME);

}

void lcdHome(void)
{
// move cursor to home
lcdControlWrite(1<<LCD_HOME);
}

void lcdClear(void)
{
// clear LCD
lcdControlWrite(1<<LCD_CLR);
}

void lcdGotoXY(u08 x, u08 y)
{
register u08 DDRAMAddr;

// remap lines into proper order
if(y==1)
{
DDRAMAddr = LCD_LINE1_DDRAMADDR+x;
}
else if(y==2)
{
DDRAMAddr = LCD_LINE2_DDRAMADDR+x;
}
else if(y==3)
{
DDRAMAddr = LCD_LINE3_DDRAMADDR+x;
}
else
{
DDRAMAddr = LCD_LINE0_DDRAMADDR+x;
}
/*
switch(y)
{
case 0: DDRAMAddr = LCD_LINE0_DDRAMADDR+x; break;
case 1: DDRAMAddr = LCD_LINE1_DDRAMADDR+x; break;
case 2: DDRAMAddr = LCD_LINE2_DDRAMADDR+x; break;
case 3: DDRAMAddr = LCD_LINE3_DDRAMADDR+x; break;
default: DDRAMAddr = LCD_LINE0_DDRAMADDR+x;
}
*/
// set data address
lcdControlWrite(1<<LCD_DDRAM | DDRAMAddr);
}


void LCD(char* data, u08 nBytes)
{
register u08 i;

// check to make sure we have a good pointer
if (!data) return;

// print data
for(i=0; i<nBytes; i++)
{
lcdDataWrite(data[i]);
}
}

void LCD_N(u16 number,u08 mode_check)
{
// we will need a temp to store some data
u08 temp;
u16 i;
u08 checker;
checker = 0;
// Anything non Zero will display a number that
//
if((mode_check == 0) || (mode_check == 1))
{
i = 10000;
if(mode_check == 0)
{
while(i !=0)
{
temp = number/i;
lcdDataWrite(temp|0x30);
number -= temp*i;
i/=10;  
}
}
if(mode_check == 1)
{
while(i !=0)
{
temp = number/i;
if ((temp != 0) || (checker == 1))
{
lcdDataWrite(temp|0x30);
checker = 1;
}
number -= temp*i;
i/=10;
}

}
}
if(mode_check == 2)
{
for(temp = 0; temp < 4;temp++)
{
if((number&(61440>>(temp*4)))||(checker == 1))
{
i = number>>(12 - (temp*4));
if(i < 10)
{
lcdDataWrite(i|0x30);
}
else
{
lcdDataWrite((i-9)|0x40);
}
checker = 1;
}
number &= ~(61440>>temp*4);
}
}
if(mode_check == 3)
{
for(i=0;i<16;i++)
{
if(number&32768>>i)
{
lcdDataWrite(0x31);
}
else
{
lcdDataWrite(0x30);
}
}
}

}

void lcdProgressBar(u16 progress, u16 maxprogress, u08 length)
{
u08 i;
u32 pixelprogress;
u08 c;

// draw a progress bar displaying (progress / maxprogress)
// starting from the current cursor position
// with a total length of "length" characters
// ***note, LCD chars 0-5 must be programmed as the bar characters
// char 0 = empty ... char 5 = full

// total pixel length of bargraph equals length*PROGRESSPIXELS_PER_CHAR;
// pixel length of bar itself is
pixelprogress = ((progress*(length*PROGRESSPIXELS_PER_CHAR))/maxprogress);

// print exactly "length" characters
for(i=0; i<length; i++)
{
// check if this is a full block, or partial or empty
// (u16) cast is needed to avoid sign comparison warning
if( ((i*(u16)PROGRESSPIXELS_PER_CHAR)+5) > pixelprogress )
{
// this is a partial or empty block
if( ((i*(u16)PROGRESSPIXELS_PER_CHAR)) > pixelprogress )
{
// this is an empty block
// use space character?
c = 0;
}
else
{
// this is a partial block
c = pixelprogress % PROGRESSPIXELS_PER_CHAR;
}
}
else
{
// this is a full block
c = 5;
}

// write character to display
lcdDataWrite(c);
}

}