Understanding the bl**dy HD44780 data sheet

Go To Last Post
27 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi guys

EDIT: *this part is solved, plz skip to post 5*
:)

EDIT 2: While I still think the data sheet is cr*ppy, basically I solved the problem of initializing the LCD with the code linked in the first reply post. If you read this because you have the same problem, I recommend you look at that link.
Also, I hope the rest of the thread will guide you as it did me.

Attachment(s): 

Last Edited: Thu. Oct 20, 2011 - 07:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, that didn't make me much wiser, but it did make my code work excellently, once I'd stolen yours.
I guess that'll have to do for now.
Thanks :)

I have a question, though:
I want the "lcd_puts()"-routine to print the value of the int "percent" on the display. How do I do that?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I want the "lcd_puts()"-routine to print the value of the int "percent" on the display. How do I do that?

#include 

char buffer[10];
int percent = 73;
itoa(percent, buffer, 10);
lcd_puts(buffer);

(itoa is non-standard (but curiously in stdlib.h if it exists) so not all C compilers have it or have the same interface - the above assumes GCC)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
The data sheet for my HD44780 (attached) contains the standard initialization description, but I have a hard time understanding what I read.
For a step by step explanation follow the LCD Initialization link at http://web.alfredstate.edu/weimandn.

Quote:
The "//?" indicates that the datasheet specifies this byte to be sent (as I read it), but I don't know what the purpose is.
These three initial 'Function Set' instructions are used to reset the controller to a known state. They are sent using 8-bit techniques because it is possible that the actual physical implementation is for an 8-bit interface. Only the upper nibble is evaluated since it is also possible that the actual physical implementation is for a 4-bit interface.

Quote:
Does this mean that I can send, for example 0x00 then immediately after 0x60 in the entry mode set block?
Yes

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi again

First of all, thank you for your great answers.
Floresta, that link you sent was excellent!

Now I have a new question:
Modifying Peter's lcd code made it easy for me to interface with my lcd.
However, now the code doesn't work, and I have yet to find the error.
I suspect that it has something to do with a message I get when I compile.

The compiler says:

AVR Memory Usage
----------------
Device: attiny2313

Program:    1262 bytes (61.6% Full)
(.text + .data + .bootloader)

Data:        148 bytes (115.6% Full)
(.data + .bss + .noinit)


Build succeeded with 0 Warnings...

Where does that data of 148 bytes come from?
It seems like the use of bytes increase as I call the lcd_puts() more often.
Is it a problem that the current usage is over 100%?
And what can I do to bring it down?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
and I have yet to find the error. ...Data: 148 bytes (115.6% Full)
Quote:
Is it a problem that the current usage is over 100%?
Very much, even if it's more than about 70% as you also need stack space.
Quote:
And what can I do to bring it down?
If you have any strings that end up in ram you can use the progmem attribute and you will clear up some ram.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So the data-parameter actually describes RAM use?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well yes in this case, but you can have data in FLASH or EEPROM too.

A string starts off being located in flash but gets copied to ram unless we do a bit of contorsionism not to. This is where progmem comes in handy as the data, string in this case, stays in flash at all times rather than being copied to ram to keep the vagaries of the C compiler and the Harvard architecture somewhat happy.

Do you have any strings being printed? Can you post that bit of code?

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sure, but be warned that it's awful code :)
(also, this is a copy paste of something I'm working on as we type, so not all the small functions are written or in use (the last four).

As the processor has very little RAM and EEPROM, I now plan to use Peter's code only to initialize the display, then make my own drivers for after the successful init.

// Source code for MT56 - Plant Workday Progression Meter
// Version: 1.1
// Written in: AVRStudio 4
// Program suite: WinAVR
// Test environment: AVR Studio 4
// Processor: ATTiny2313
// Processor clock: 1MHz Internal (MHz internal divided by 8 with fuse)
//  <  >
//---------------------------------- 
// #include < >
#include  //Standardbibliotek
#include <./stdio.h> //Standardbibliotek
#include <./stdlib.h> //Standardbibliotek
#include  //Standardbibliotek
#include 
#include "main.h"
#include "lcd_drv.h"
#include "lcd_drv.c"


//----------------------------------
//Fuses settings noter

//Fuses settings noter
//----------------------------------
//Function prototyping area
void init(void);
void dispinit(void);
void dumbdelay(uint16_t);
void backlight(int);
void timeupdate(void);
void inttochar(int);
void sendbyte(int int);
void setnibble(int);
void registerselect(int);
void dispclock(void);
//Function prototyping area
//----------------------------------
//Defines
#define ON 1
#define OFF 0
#define INSTRUCTION 0
#define DATA 1
#define READ 1
#define WRITE 0
#define T1PRELOAD 62680
//Defines
//----------------------------------
//Global variables
int waste=0; //Random variable for optimisation/simulation purposes.
int loopvar=0; //Variable used by while loops.
int testvar1=0;
int testvar2=0;
int onemillipercent=0; //One shift of 8 hrs is 28800 seconds, 0.01% of that is 2.88 seconds.
int tenmillipercent=0;
int onepercent=0;
int tenpercent=0;
int hundredpercent=0;
int timewatch=0; //Tested by main program loop for timer overrun.
int timer1reg=0; //Used by timer1 to regulate cycle time of 2880 us til 2880 ms


//Global variables

//----------------------------------

//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------

ISR(TIMER1_OVF_vect)//Interruptrutine ved timer1 overflow
{
timer1reg++;
TCNT1=T1PRELOAD;
TIFR|=0x80; //Timer 1 overflow flag cleared.
//TCCR1B=0x01; //Timer 1 now overflows after 2880 pulses.
if(timer1reg==1000)
	{
	timer1reg=0;
	onemillipercent++;
	if(onemillipercent==10)
		{
		onemillipercent=0;
		tenmillipercent++;
		if(tenmillipercent==10)
			{
			tenmillipercent=0;
			onepercent++;
			if(onepercent==10)
				{
				onepercent=0;
				tenpercent++;
				if(tenpercent==10)
					{
					tenpercent=0;
					hundredpercent++;
					if(hundredpercent==10)
						{
						hundredpercent=0;
						tenpercent=0;
						onepercent=0;
						tenmillipercent=0;
						onemillipercent=0;
						};
					};
				};
			};
		};
	};
};

//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
*/

int main(void)
{

init();

while(1)
	{
	  lcd_init();
	  lcd_xy( 0, 0 );
	  lcd_puts( "     Plant     " );
	#ifdef LCD_LINE2
	  lcd_xy( 0, 1 );
	  lcd_puts( "   Time Buster  " );
	#endif  
	loopvar=6;
	while(loopvar!=0)
		{
		dumbdelay(65000);
		loopvar--;
		};
	lcd_xy( 0, 0 );
	lcd_puts( "       By       " );
	lcd_xy( 0, 1 );
	lcd_puts( "  M. Malmkvist  " );
	loopvar=8;
	while(loopvar!=0)
		{
		dumbdelay(65000);
		loopvar--;
		};
	lcd_xy( 0, 0 );
	lcd_puts( " CP Time Buster " );
	lcd_xy( 0, 1 );
	lcd_puts( "                " );
	TCCR1B=0x01; //Timer1 started
	  
	  for(;;){
		timewatch=0; 
		timeupdate();
		dumbdelay(5);
   	  }

	};


};

void init(void)
{
//OPSÆTNING AF PROCESSOR
//------------------------------------
//CLOCK SETUP

//NOTE: Bemærk at ovenstående operation måske skal køres to gange, jvf. https://www.avrfreaks.net/index.php?module=PNphpBB2&file=viewtopic&t=10830&highlight=

//I/O-PORT SETUP
MCUCR|=0x00; //
DDRB=0xFF; //Port B is all outputs.
PORTB=0x00; //All PB outputs low.
DDRD=0xFF; //Port D is all outputs.
PORTD=0x00; //All PB outputs low.

//ANALOG COMPARATOR SETUP

//INTERRUPT-SETUP

//TIMER0 SET-UP 


//TIMER1 SET-UP
TCNT1=T1PRELOAD;
//TCCR1B=0x01; //Timer1 startes uden overflowflag.

//Global interrupt enable
TIMSK|=0x80; //Timer1 overflow enabled and timer0 overflow disabled.
SREG |= 0x80; //I bit in SREG is set for global interrupt enable


//Initialisering af særlige registre

};


//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
void dumbdelay(uint16_t delay) //Delay = ~87 us when delay=10.
{
while (delay!=0)
	{
	waste=PIND;
	waste++;
	delay--;
	};
};
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
void backlight(int toggle)
{
if (toggle==ON) PORTB|=0x04;
if (toggle==OFF) PORTB&=0xFB;
};
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
void timeupdate(void)
{
/*
//Hundredpercent
lcd_xy( 5, 1 );
inttochar(hundredpercent);
	
//Tenpercent
lcd_xy( 6, 1 );
inttochar(tenpercent);

//Onepercent
lcd_xy( 6, 1 );
inttochar(onepercent);

//Decimal
lcd_xy( 7, 1 );
lcd_puts(".");

//Tenmillipercent
lcd_xy( 8, 1 );
inttochar(tenmillipercent);

//Onemillipercent
lcd_xy( 9, 1 );
inttochar(onemillipercent);

//Percent-sign
lcd_xy( 10, 1 );
lcd_puts("%");
*/
};
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
void inttochar(int value)
{
if(value==0) lcd_puts("0");
if(value==1) lcd_puts("1");
if(value==2) lcd_puts("2");
if(value==3) lcd_puts("3");
if(value==4) lcd_puts("4");
if(value==5) lcd_puts("5");
if(value==6) lcd_puts("6");
if(value==7) lcd_puts("7");
if(value==8) lcd_puts("8");
if(value==9) lcd_puts("9");

};
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
void sendbyte(int dcom int byte);
{

};
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
void setnibble(int nibble);
{

};
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
void registerselect(int rs);
{
if (rs==INSTRUCTION) PORTB&=0xFE;
if (rs==DATA) PORTB|=0x01;
};
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
void dispclock(void);
{
PORTD|=0x20;  //Display enable is on PD.5 and is active high.
dumbdelay(1);
PORTD&=0xDF;
};
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well all your strings are taking up RAM space ie

lcd_puts( "  M. Malmkvist  " );

I'm not sure how or if you can use progmem with lcd_puts so that the string doesn't eat up ram. May have to wait for someone else with some knowledge of that function to help out, sorry. :(

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, I removed all cases of lcd_puts, except the ones right after the init. I'm down to a data use of 96.8% and the program runs now :-P
So the rest of the way, I'll write my own code to send stuff to the display.
Hail to lazynesss. And thank you for your insigt.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Someone will give a you a better way of doing this:

#include 
.
.
void s_puts_P (char *data)
{
	uint8_t c, i=0;

	while ( (c=pgm_read_byte (&data [i])) != '\0' ) 
   		{
		usart0_putchar (c);
     	i++; 
   		}   
}
.
.
s_puts_P ( PSTR ("New "));

replacing the lcd_puts with something like s_puts_P and replacing usart0_putchar with lcd_putchar may work. But there may be already a way of doing this with lcd_puts.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The solution is easy. Simply use the lcd_puts_P() macro from the Fleury "lcd.h". e.g.

   ...
   lcd_puts_P( "  M. Malmkvist  " );
   ...
   lcd_puts_P( " CP Time Buster " );

Note that this macro only takes "anonymous quoted strings". You cannot use expressions.
But it certainly makes your life nice and easy.

The macro takes your "string". Wraps it with PSTR(), so that it is always kept in flash. Then uses the lcd_puts_p() function to display a flash string.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Off-topic:
Malmkvist, why did you erase your first post once it was solved? Don't you think it's possible that someone else can benefit from the knowledge later on?

-Pantelis

Professor of Applied Murphology, University of W.T.F.Justhappened.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes I did...
My reasoning was twofold:
1. People can derive from the headline as well as the immediate answers that the topic was, once again, a noob programmer who couldn't figure out how to interface with a HD44780. Nothing new there.
2. I didn't want to risk spending my readers' attention spans with a post which was already solved, when I have new, juicy questions at the end of the thread :-P

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is very public spirited of you to think of the readers.
OTOH, a reader may see "HD44780 + solved" and think "this is worth reading" !!

I give great importance to the prettiness of any code. If it is attractive, people will look at it. There is a program called "indent" that will do the job for you. It must be syntactically correct first. I use "indent -kr -nut". Other people may pregfer other styles.

It looks as if you are using Danni's excellent LCD functions.
I thought you were using the Fleury library.

Whichever you choose, you will probably find that they both already have suitable functions. This is why they come with HTML documentation. I rather like the Fleury macro.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void inttochar(int value) 
{ 
if(value==0) lcd_puts("0"); 
if(value==1) lcd_puts("1"); 
if(value==2) lcd_puts("2"); 
if(value==3) lcd_puts("3"); 
if(value==4) lcd_puts("4"); 
if(value==5) lcd_puts("5"); 
if(value==6) lcd_puts("6"); 
if(value==7) lcd_puts("7"); 
if(value==8) lcd_puts("8"); 
if(value==9) lcd_puts("9"); 
}; 

You can replace this monstrosity with this:

lcd_putc(value + '0');

But then, dividing the "percent" values up into individual digits is useless. The only reason to do so is to make it faster to put the value out to the LCD, but any method that you use will be far faster than a person can read it anyways. I would use just two values, one for the right side of the "." and one for the left. Your "for" loop that displays the values likely takes less than 1ms (even with the delay and the fact that the timeupdate routine is very inefficient). So you end up updating the display more than 1000 times a second.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The bit about the readers was just me making a little fun :-P
And, Koshchi... Well, you're right, compared to the normal standard, my code IS very crude. I'm self-taught and started programming in assembler, which my code is still characterised by.
Also, I'm pretty sure I don't have much of the knowledge you'd consider basic. For one, I have no idea how to use a macro :roll:
So often when I have questions, I will post the kind of code you saw posted. In time, I'll hopefully improve.

As it is, I used Danni's code, yes, but due to restrictions in flash space, I ended up only using his initialization and lcd_xy, plus lcd_puts twice during start up.
My code was finished today and I ended up using 98.6% of the available space and 60% of the RAM, so I'm happy.

And I couldn't have done it without the help of you guys and the forum, so thanks a lot :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you are accustomed to assembler, you are probably happy with factoring code. e.g. spotting similar sequences, and creating a subroutine.

This is why I am keen on good layout. The likely candidates just scream at you. koshchi has shown one dramatic simplification.

Other ones are lcd_gotoxy_and_print_an int().

Sometimes avr-gcc will just re-arrange the code by itself.
CodeVision is very good at this.

All the same, common sense shows obvious simplifications.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What is it, with all the semi-colons after the closing braces?

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Of course once you have enough flash space to be able to use fprintf_P your life becomes easier, just use it to talk to the lcd or any other display the same way.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@david, I'll try out your examples and see how it works for me. You're probably right, though.

@LDevries, I thought they were required. Seriously. All the projects I've ever made in C have had them.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

John , your function won't work IIRC ( I tried it that way ). '&' will pull from RAM, not a flash address.

This works w/o warnings:

const char   Enter_Freq[] PROGMEM = {"Enter Frequency" };

void   lcd_putstr_P( PGM_P s ){

char   temp;

	while( ( temp = pgm_read_byte( s++ )) != '\0' )
	
	{	lcd_putc( temp );
	};

}// End lcd_putstr_P


lcd_putstr_P( Enter_Freq );

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
For one, I have no idea how to use a macro
Good. Or, not, depending on your point of view. Personally I think the majority of Macros were created by the DEVIL!

Yes, 'sometimes' they are useful, often though, they just complicate code. Use them only where they make sense in the code, this, only you can decide (and the programmers that come after you!).

--greg
Still learning, don't shout at me, educate me.
Starting the fire is easy; the hardest part is learning how to keep the flame!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
John , your function won't work IIRC
It's part of working code as I posted it. Some wit will explain why I guess, one way or another. :?

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

js's code should work just fine. The semantics are a little suspect. You will probably get some warnings.

At the end of the day, you will use some form of lcd_put_string_from_flash() function. Obviously you have to give it a valid address in flash for the string.

lcd_put_string_from_flash(PSTR("this is in flash");

Most libraries will have a suitable function. God invented HTML so that you could read the documentation of your library.

Personally, I prefer the code to look readable. Macros can cope with the syntax horrors. You either like them or you don't. One advantage is that you only write the macro once. The downside is that you have to write it correctly.

David.