Pages: [1] 2   Go Down
Author Topic: 4 bit bus mode for a LCD  (Read 1963 times)
0 Members and 1 Guest are viewing this topic.
Norway
Offline Offline
Jr. Member
**
Karma: 1
Posts: 71
Does it work?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hello.

Im putting this out while investigating the lcd -library:

Im currently trying to understand howto communicate with  a 2x16 LCD in general in 4 bit bus mode. No code made yet.

Ive read some docs on internet and the specific LCD datasheet itself. (GDM1602k) http://www.sparkfun.com/datasheets/LCD/GDM1602K-Extended.pdf

I notice from using a LCD library following Arduinoapplication, its possible to manipulate the display both for instructionmode and information mode (text to display) by using only 4 databits (4 digital input pins on arduino) and some for the E-RS and RW command.

What I dont quite understand is howto "mark" the 4 bits sent as either a command (i.e...cursorpos,cleardisplay...) or text to be seen on display (written to dpy ram), as the high nibble or the low nibble(4 bits)?

Thank You in advance for any links or hints to my question.

regards
Jan
Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 32
Posts: 4260
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
What I dont quite understand is howto "mark" the 4 bits sent as either a command (i.e...cursorpos,cleardisplay...) or text to be seen on display (written to dpy ram), as the high nibble or the low nibble(4 bits)?
That is determined by the RS (Register Select) pin.  You drive it low for a command and high for data.

Don
Logged

Norway
Offline Offline
Jr. Member
**
Karma: 1
Posts: 71
Does it work?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for answer. Sorry Im a bit unclear.
Im aware of the RS status.
What Im not aware of is howto send 8bit value to the display when You only have 4 bits data available at the hardware(Arduino)?

I think I read somewhere You might send the 4bit information twice. Howto do that is, to me, not understandable atm.
Do I send the two 4 bits nibbles right after each other and then change the enable status?

regards
Jan

(ive studied the lcd library, but sadly I just know some of the C programming style so I didnt get answer to the above question).

Quote
What I dont quite understand is howto "mark" the 4 bits sent as either a command (i.e...cursorpos,cleardisplay...) or text to be seen on display (written to dpy ram), as the high nibble or the low nibble(4 bits)?
That is determined by the RS (Register Select) pin.  You drive it low for a command and high for data.

Don
« Last Edit: January 13, 2012, 07:18:24 pm by janeik » Logged

Dallas, TX USA
Offline Offline
Faraday Member
**
Karma: 63
Posts: 2648
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

look at the LiquidCrystal.cpp file in the liquidcrystal library which is in {installdir}/libraries/LiquidCrystal.

Focus on routines at the bottom of the code
near the comment:
Code:
/************ low level data pushing commands **********/

Its not much code so I'll duplicate it here:

Code:
// write either command or data, with automatic 4/8-bit selection
void LiquidCrystal::send(uint8_t value, uint8_t mode) {
  digitalWrite(_rs_pin, mode);

  // if there is a RW pin indicated, set it low to Write
  if (_rw_pin != 255) {
    digitalWrite(_rw_pin, LOW);
  }
 
  if (_displayfunction & LCD_8BITMODE) {
    write8bits(value);
  } else {
    write4bits(value>>4);
    write4bits(value);
  }
}

void LiquidCrystal::pulseEnable(void) {
  digitalWrite(_enable_pin, LOW);
  delayMicroseconds(1);   
  digitalWrite(_enable_pin, HIGH);
  delayMicroseconds(1);    // enable pulse must be >450ns
  digitalWrite(_enable_pin, LOW);
  delayMicroseconds(100);   // commands need > 37us to settle
}

void LiquidCrystal::write4bits(uint8_t value) {
  for (int i = 0; i < 4; i++) {
    pinMode(_data_pins[i], OUTPUT);
    digitalWrite(_data_pins[i], (value >> i) & 0x01);
  }

  pulseEnable();
}

Notice the check in send() for 8 bit mode.
It either calls write8bits() or write4bits() twice.
(I admit the code there is a a bit obtuse)

In either case the bits are set and the EN is strobed.
In 8 bit mode you send all 8 bits.
In 4 bit mode you send the upper 4 bits, strobe EN, then the lower 4 bits, then strobe EN.

you will see in send() the two calls to write4bits()
The value passed to the first write4bits()  is  value >> 4 which is the upper 4 bits since the value is shifted to the right 4 bits.
The value passed to the second call to write4bits() is the full original 8 bit value.
write4bits() sends the lower 4 bits of the value it received by placing those bits on d4-d7 of the lcd module pins.
The first call will write the upper 4 bits
and while the 2nd call to write4bits() received the full 8 bit value since only the lower 4 bits are used,
the lower 4 bits is sent to the lcd.

The pulseEnable() routine is doing more than just handling the EN line for the LCD as it is also waiting
for the faster low level commands to complete. That is what the 100us delay is for.

Don, correct me if I'm wrong but in 4 bit mode this extra delay between nibbles is not really needed.
It is only there because of the way the code was written as 4 bit mode shares the same routine
as the 8 bit routine.

--- bill
Logged

Norway
Offline Offline
Jr. Member
**
Karma: 1
Posts: 71
Does it work?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank You very much for clearing up :-)

I guess I wasnt clever enough to read the solution from the timing sequence diagram in the LCD documentation.
Ill write my code here after trying out some basics.

Acctually its a test for two groups of my pupils, programming a LCD not using a library :-).  I nearly manage to keep a horsehead ahead of them.
Thanks for the helpout.

regards

Jan


look at the LiquidCrystal.cpp file in the liquidcrystal library which is in {installdir}/libraries/LiquidCrystal.

Focus on routines at the bottom of the code
near the comment:
Code:
/************ low level data pushing commands **********/

Its not much code so I'll duplicate it here:

Code:
// write either command or data, with automatic 4/8-bit selection
void LiquidCrystal::send(uint8_t value, uint8_t mode) {
  digitalWrite(_rs_pin, mode);

  // if there is a RW pin indicated, set it low to Write
  if (_rw_pin != 255) {
    digitalWrite(_rw_pin, LOW);
  }
 
  if (_displayfunction & LCD_8BITMODE) {
    write8bits(value);
  } else {
    write4bits(value>>4);
    write4bits(value);
  }
}

void LiquidCrystal::pulseEnable(void) {
  digitalWrite(_enable_pin, LOW);
  delayMicroseconds(1);   
  digitalWrite(_enable_pin, HIGH);
  delayMicroseconds(1);    // enable pulse must be >450ns
  digitalWrite(_enable_pin, LOW);
  delayMicroseconds(100);   // commands need > 37us to settle
}

void LiquidCrystal::write4bits(uint8_t value) {
  for (int i = 0; i < 4; i++) {
    pinMode(_data_pins[i], OUTPUT);
    digitalWrite(_data_pins[i], (value >> i) & 0x01);
  }

  pulseEnable();
}

Notice the check in send() for 8 bit mode.
It either calls write8bits() or write4bits() twice.
(I admit the code there is a a bit obtuse)

In either case the bits are set and the EN is strobed.
In 8 bit mode you send all 8 bits.
In 4 bit mode you send the upper 4 bits, strobe EN, then the lower 4 bits, then strobe EN.

you will see in send() the two calls to write4bits()
The value passed to the first write4bits()  is  value >> 4 which is the upper 4 bits since the value is shifted to the right 4 bits.
The value passed to the second call to write4bits() is the full original 8 bit value.
write4bits() sends the lower 4 bits of the value it received by placing those bits on d4-d7 of the lcd module pins.
The first call will write the upper 4 bits
and while the 2nd call to write4bits() received the full 8 bit value since only the lower 4 bits are used,
the lower 4 bits is sent to the lcd.

The pulseEnable() routine is doing more than just handling the EN line for the LCD as it is also waiting
for the faster low level commands to complete. That is what the 100us delay is for.

Don, correct me if I'm wrong but in 4 bit mode this extra delay between nibbles is not really needed.
It is only there because of the way the code was written as 4 bit mode shares the same routine
as the 8 bit routine.

--- bill
Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 32
Posts: 4260
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
ive studied the lcd library, but sadly I just know some of the C programming style so I didnt get answer to the above question
Would assembly language examples help? 

Don
Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 32
Posts: 4260
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Don, correct me if I'm wrong but in 4 bit mode this extra delay between nibbles is not really needed.
It is only there because of the way the code was written as 4 bit mode shares the same routine
as the 8 bit routine.
That is correct.  The delay is not needed until after the LCD controller reassembles the two nibbles and it is actually executing the command.

Don
Logged

Dallas, TX USA
Offline Offline
Faraday Member
**
Karma: 63
Posts: 2648
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

jan,
If you are looking for another C++ example library,
there is another LiquidCrystal library by forum member "fm".
It is a direct fully backward compatible replacement that is quite a bit faster.
It supports:
4bit, 8 bit parallel
i2c
2 pin or 3 pin shift register.
Here is a link to the code site:
https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home

--- bill
Logged

Norway
Offline Offline
Jr. Member
**
Karma: 1
Posts: 71
Does it work?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hello.

Using that on an Amiga years ago, long time forgotten, I dont think so.
Thanks for the offer though.

regards

Quote
ive studied the lcd library, but sadly I just know some of the C programming style so I didnt get answer to the above question
Would assembly language examples help? 

Don

Logged

Norway
Offline Offline
Jr. Member
**
Karma: 1
Posts: 71
Does it work?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I guess Im doing something really bad here as nothing works as expected. (init 4bit data comm and setting cursorblink,home...)
Code:
/*
testing av lcd display
 */
byte pin4=4; // pinnumber of datapin LSB
byte pin5=5;
byte pin6=6;
byte pin7=7;
byte registerselect= 9;
byte enable = 8;
byte readwrite = 10;
byte highnib = B00000000; //contains the highnibble to be sent first, to LCD in 4 bit datamode
byte lownib= B00000000;  // contains the lownibble to be sent as 2dn to LCD in 4 bit datamode
void cleardpy();
void enablepulse();
void writetodpy();

void setup()
{
  Serial.begin(9600);
  pinMode(pin4, OUTPUT);
  pinMode(pin5, OUTPUT);
  pinMode(pin6, OUTPUT);
  pinMode(pin7, OUTPUT);
 
  pinMode(enable, OUTPUT);
  pinMode(readwrite, OUTPUT);
  pinMode(registerselect, OUTPUT);

 
 
// To send data to the LCD, your program should make sure
// enable is low (0) and then set the other two control lines
// and/or put data on the data bus. When the other lines are completely ready, bring EN high (1) and wait for the minimum amount of time required by the LCD datasheet (this varies from LCD to LCD), and end by bringing it low (0) again.
 digitalWrite(enable,LOW);
 digitalWrite(readwrite,LOW);
 digitalWrite(registerselect,HIGH);
 
 delayMicroseconds(50000); //await start process to finish
 
 
  cleardpy();
  writetodpy(); //write 4bits twice to display using shift
  init4dpy();
}

void loop()
{
 
}

void cleardpy()
{


}

void writetodpy()
{

}

void enablepulse() //send a pulse to the enable output
{
  digitalWrite(enable,HIGH);
  delayMicroseconds(1);
  digitalWrite(enable,HIGH);
 }
 
void init4dpy()
{
 
//  highnib=B00110000; //according to instruction table and masked 4 low order bits.
//  lownib=B00000000;  //according to instruction table and masked 4 high order bits
digitalWrite(pin4,HIGH);
digitalWrite(pin5,HIGH);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();

digitalWrite(pin4,LOW);
digitalWrite(pin5,LOW);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();
delayMicroseconds(4500);

//set 4 bit data mode
digitalWrite(pin4,LOW);
digitalWrite(pin5,HIGH);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();

digitalWrite(pin4,LOW);
digitalWrite(pin5,LOW);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();
delayMicroseconds(4500);


// display on-cursor on, cursor blink i.e.: highnib=B00000000 lownib=B00001111

digitalWrite(pin4,LOW);
digitalWrite(pin5,LOW);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();
digitalWrite(pin4,HIGH);
digitalWrite(pin5,HIGH);
digitalWrite(pin6,HIGH);
digitalWrite(pin7,HIGH);
enablepulse();
delayMicroseconds(4500);

// home cursor
digitalWrite(pin4,LOW);
digitalWrite(pin5,LOW);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();

digitalWrite(pin4,LOW);
digitalWrite(pin5,HIGH);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();

/*
  delay(lcd_delay);
                lcd_init_write(0x30);   //Special Sequence:Write Function Set.
                delay(lcd_delay);
                lcd_init_write(0x30);              //Special Sequence:Write Function Set.
                delay(lcd_delay);
                lcd_init_write(0x30);              //Special Sequence:Write Function Set.
                delay(lcd_delay);
                lcd_init_write(0x20);   // 0x20 for 4-bit
                delay(lcd_delay);
                lcd_com(0x28);         //Display Off, Cursor Off, Blink Off
                delay(lcd_delay);
                lcd_com(4);                                                // Clear Screen & Returns the Cursor Home
                delay(lcd_delay);
                lcd_com(0x85);       
                delay(lcd_delay);       
                lcd_com(6);           //Inc cursor to the right when writing and don’t shift screen
                delay(lcd_delay);
                lcd_com(1);
                delay(lcd_delay);
*/

}
« Last Edit: January 14, 2012, 05:31:19 pm by janeik » Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 32
Posts: 4260
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It does not look like you are initializing your LCD controller properly.  If I am following your code correctly I see that you are sending the initial 'B00110000' only once and you are sending it as two separate 4-bit nibbles.  This information has to be sent three times and it is sent as an 8-bit byte (even though the lower four bits are unused) since the device is still in the 8-bit mode.  Then, after you change modes you are turning the display ON where the datasheet clearly says to turn it OFF.  I know that most of the code that you look at, including the LiquidCrystal library, turns the LCD ON at this point but that doesn't mean that they are doing things correctly.  Next you are 'homing' the cursor which is unnecessary if you use the correct instruction which is to clear the display.  I don't see where you are setting the entry mode.

At this point I have two suggestions.  (1) Follow the LCD Addressing link at http://web.alfredstate.edu/weimandn to find out what you have to do and what sequence you have to do it in, and (2) start out with the 8-bit mode which is a lot easier to get working.

Also - when you post code you should highlight the code and use the code button (#) so that it looks like this excerpt:

Code:
//  highnib=B00110000; //according to instruction table and masked 4 low order bits.
//  lownib=B00000000;  //according to instruction table and masked 4 high order bits
digitalWrite(pin4,HIGH);
digitalWrite(pin5,HIGH);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();

Don
Logged

Norway
Offline Offline
Jr. Member
**
Karma: 1
Posts: 71
Does it work?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello. thanks for the link showing the proper way of init.
I know Im doing lots of error. I did write 3 times but forgot to edit it here.

Ill do the 8bit tranferre trippel initially as I read the 4 lower bits wasnt read my lcd anyway.

Else Ill change accordingly to the init link routines You listed.

I tried to copy the code in the arduinoapp, but it didnt show all code when glued in.

Ow well, I hope Ill manage to learn the trick from 8 to 4 then. The 8bit mode isnt a challenge.

thank You


It does not look like you are initializing your LCD controller properly.  If I am following your code correctly I see that you are sending the initial 'B00110000' only once and you are sending it as two separate 4-bit nibbles.  This information has to be sent three times and it is sent as an 8-bit byte (even though the lower four bits are unused) since the device is still in the 8-bit mode.  Then, after you change modes you are turning the display ON where the datasheet clearly says to turn it OFF.  I know that most of the code that you look at, including the LiquidCrystal library, turns the LCD ON at this point but that doesn't mean that they are doing things correctly.  Next you are 'homing' the cursor which is unnecessary if you use the correct instruction which is to clear the display.  I don't see where you are setting the entry mode.

At this point I have two suggestions.  (1) Follow the LCD Addressing link at http://web.alfredstate.edu/weimandn to find out what you have to do and what sequence you have to do it in, and (2) start out with the 8-bit mode which is a lot easier to get working.

Also - when you post code you should highlight the code and use the code button (#) so that it looks like this excerpt:

Code:
//  highnib=B00110000; //according to instruction table and masked 4 low order bits.
//  lownib=B00000000;  //according to instruction table and masked 4 high order bits
digitalWrite(pin4,HIGH);
digitalWrite(pin5,HIGH);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();

Don
« Last Edit: January 14, 2012, 04:23:13 pm by janeik » Logged

Norway
Offline Offline
Jr. Member
**
Karma: 1
Posts: 71
Does it work?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have rewritten the code after reading init protocol.
But the display turns on with cursor at random position on the first line.
Code:
/*
testing av lcd display
 */
int pin4=4; // pinnumber of datapin LSB
int pin5=5;
int pin6=6;
int pin7=7;
int registerselect= 9;
int enable = 8;
int readwrite = 10;

int enablepulse();
int init4dpy();

void setup()
{
 
  Serial.begin(9600);
 
  delay(3000);
 
  pinMode(pin4, OUTPUT);
  pinMode(pin5, OUTPUT);
  pinMode(pin6, OUTPUT);
  pinMode(pin7, OUTPUT);
  pinMode(enable, OUTPUT);
  pinMode(readwrite, OUTPUT);
  pinMode(registerselect, OUTPUT);
 
 
  init4dpy();
}

void loop()
{
 
}

int enablepulse() //send a pulse to the enable output
{
  digitalWrite(enable,LOW);
  digitalWrite(enable,HIGH);
  delayMicroseconds(1);
  digitalWrite(enable,LOW);
 }
 
int init4dpy()
{
 
// delayMicroseconds(50000); //await start process to finish

 digitalWrite(readwrite,LOW);
 digitalWrite(registerselect,LOW);

// write 3times to lcd with content ox03
digitalWrite(pin4,HIGH);
digitalWrite(pin5,HIGH);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();
delayMicroseconds(4500);

digitalWrite(pin4,HIGH);
digitalWrite(pin5,HIGH);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();
delayMicroseconds(150);

digitalWrite(pin4,HIGH);
digitalWrite(pin5,HIGH);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();
delayMicroseconds(150);


//set 4 bit data mode writing highbit
digitalWrite(pin4,LOW);
digitalWrite(pin5,HIGH);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();
delayMicroseconds(150);

// 4 bit mode start from here and downwards

// function set: 4 bit bus mode, 2 line dpy, 5x8dots format

digitalWrite(pin4,LOW);
digitalWrite(pin5,HIGH);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();
digitalWrite(pin4,LOW);
digitalWrite(pin5,LOW);
digitalWrite(pin6,LOW);
digitalWrite(pin7,HIGH);
enablepulse();
delayMicroseconds(60);

// turn off dpy, cursor and blink (ddram still exists)

digitalWrite(pin4,LOW);
digitalWrite(pin5,LOW);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();
digitalWrite(pin4,LOW);
digitalWrite(pin5,LOW);
digitalWrite(pin6,LOW);
digitalWrite(pin7,HIGH);
enablepulse();
delayMicroseconds(60);

// clear dpy
digitalWrite(pin4,LOW);
digitalWrite(pin5,LOW);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();
digitalWrite(pin4,HIGH);
digitalWrite(pin5,LOW);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();
delayMicroseconds(35000);

// entry mode: cursor/blink moves to right and ddram adress is increased by 1 and shift right. I dont fully understand SH parameter yet.

digitalWrite(pin4,LOW);
digitalWrite(pin5,LOW);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();
digitalWrite(pin4,LOW);
digitalWrite(pin5,HIGH);
digitalWrite(pin6,HIGH);
digitalWrite(pin7,LOW);
enablepulse();
delayMicroseconds(60);

// end of initializing

// turn dpy, cursor on and blinking off

digitalWrite(pin4,LOW);
digitalWrite(pin5,LOW);
digitalWrite(pin6,LOW);
digitalWrite(pin7,LOW);
enablepulse();
digitalWrite(pin4,HIGH);
digitalWrite(pin5,HIGH);
digitalWrite(pin6,HIGH);
digitalWrite(pin7,HIGH);
enablepulse();
delayMicroseconds(60);

delay(1000);
}
« Last Edit: January 15, 2012, 06:34:26 am by janeik » Logged

Norway
Offline Offline
Jr. Member
**
Karma: 1
Posts: 71
Does it work?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Oh my, now the display doesnt react on anything. Tested it with lcd.lib following arduino, no response at all.

Ill burry myself 6 feet below smiley-money
Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 32
Posts: 4260
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Oh my, now the display doesnt react on anything. Tested it with lcd.lib following arduino, no response at all.
You probably just forgot to tell the LiquidCrystal library that you are using the RW pin.  Your best bet is to just connect it (LCD pin 5) to GND.

Your own initialization sequence is almost correct.  You messed up one bit in the 'Entry Mode Set' command.

Don

Logged

Pages: [1] 2   Go Up
Jump to: