# [LCD] 4 Bit Mode not showing anything on the LCD

Joined Aug 7, 2015
11
I'm trying to implement a function that displays the letter c on the LCD using 4-bit mode. But when I run the code on Proteus I get a blank lcd.
I tried the code with 8 bit mode (without the bits manipulation) and works perfectly. So there's nothing wrong with lcd_send() and lcd_save() i guess.
I know there's tons of libraries for that, but i'm trying to write it for learning purpose.

Here's my code:
Code:
void init_lcd()
{
//Setting DDRA and DDRC to output
struct conf con;
con.value= 0xff;
set_direction_port(con);

con.value= 0xff;
set_direction_port(con);

lcd_send_cmd(0x28);
lcd_send_cmd(0x0c);
}
void lcd_send_char(char c)
{
// Sets direction to write
set_lcd_dir(0);
// Enables data register
set_control_reg(1);

lcd_send(c & 0xf0);
lcd_save();
set_lcd_dir(0);
set_control_reg(1);
lcd_send((c & 0x0f) << 4);
lcd_save();
}

void lcd_send(char c)
{
struct conf con;
con.value= c;

write_port(con);
}

void lcd_save()
{
//Saving
set_lcd_enable(0);
_delay_ms(1);
set_lcd_enable(1);
_delay_ms(1);
set_lcd_enable(0);
}
I will attach the code with proteus file in case you needed it.

If there's any information needed, please tell me. Thanks.

#### Attachments

• 55.5 KB Views: 3
Last edited:

#### spinnaker

Joined Oct 29, 2009
7,815
Where is you initialization function?

Joined Aug 7, 2015
11

#### ErnieM

Joined Apr 24, 2011
7,991
There are several very necessary delays you need to add to get the LCD up and running. There allow the display to do its power on reset, and also time to process the commands.

These delays vary all over the place so you need to read the data sheet of the specific device you have.

#### spinnaker

Joined Oct 29, 2009
7,815
I am not seeing where you are telling the lcd it is 4 bit mode.

This is what I do for the LCD library I use.

Code:
  LCD_nybble(0x02,0);  //Enable 4 bit mode
delay_ms(1);

LCD_cmd(0b00101100);       //set 4-bit mode and 2 lines @ 5x7
delay_us(160);
And you need delays like Ernie mentioned unless it is in your other functions which you seem reluctant to post all of your relevant code for some reason.

Joined Aug 7, 2015
11
I am not seeing where you are telling the lcd it is 4 bit mode.

This is what I do for the LCD library I use.

Code:
  LCD_nybble(0x02,0);  //Enable 4 bit mode
delay_ms(1);

LCD_cmd(0b00101100);       //set 4-bit mode and 2 lines @ 5x7
delay_us(160);
And you need delays like Ernie mentioned unless it is in your other functions which you seem reluctant to post all of your relevant code for some reason.
Here in the init function:
Code:
    lcd_send_cmd(0x28);
I've attached the whole project files.

Joined Aug 7, 2015
11
There are several very necessary delays you need to add to get the LCD up and running. There allow the display to do its power on reset, and also time to process the commands.

These delays vary all over the place so you need to read the data sheet of the specific device you have.
So what're you saying is that it maybe a delay problem ? not a bit fiddling problem for instance ?

#### spinnaker

Joined Oct 29, 2009
7,815
I would have to analyze what a 0x28 does as I have not worked with LCDs for some time now. Just showing you what works for me. You can do with that what you want.

#### spinnaker

Joined Oct 29, 2009
7,815
So what're you saying is that it maybe a delay problem ? not a bit fiddling problem for instance ?
The delay in sending commands is extremely important in the LCD. And you can't just send your init routine at power up. I delay 40 ms before sending init data.

#### ErnieM

Joined Apr 24, 2011
7,991
Since this is a Proteus simulation it may work fine without delays, such is a limitation of simulation. It will not work on the real world hardware.

If you use the same routine to send a character in either an 8 bit or a 4 bit mode then you are doing it wrong. 4 bit mode means you literally send the 8 bit character 4 bits at a time, in 2 separate transactions.

Look, everyone who tries this gets stuck for some time, most make it thru anyway. The ones who get thru the fastest start with some example they read off the interwebs. Google is your friend here, try searching for "Proteus LCD example" and add in the processor you are using. I suspect someone else who did this and got it to work will have posted his code somewhere.

#### dannyf

Joined Sep 13, 2015
2,196
But when I run the code on Proteus I get a blank lcd.
That means the LCD isn't properly initialized - if you look at your init_lcd() routine vs. the datasheet, the errors are obvious.

Embedded programming is easy in that you just need to code to the datasheet of the device that you are programming to / on. In this case, your code isn't even close to the datasheet.

Joined Aug 7, 2015
11
That means the LCD isn't properly initialized - if you look at your init_lcd() routine vs. the datasheet, the errors are obvious.

Embedded programming is easy in that you just need to code to the datasheet of the device that you are programming to / on. In this case, your code isn't even close to the datasheet.

#### dannyf

Joined Sep 13, 2015
2,196

BTW, Proteus is very realistic. If you get your code working on Proteus, it has a great chance to work in the real world.

#### spinnaker

Joined Oct 29, 2009
7,815

BTW, Proteus is very realistic. If you get your code working on Proteus, it has a great chance to work in the real world.
I would not be so sure of that statement.

#### spinnaker

Joined Oct 29, 2009
7,815

Believe it or not I was just looking at mine to upgrade an old project.

As long as it is Hantonix based here.

#### Attachments

• 161.2 KB Views: 4

#### jpanhalt

Joined Jan 18, 2008
7,691
Here is assembly for the initialization:
Code:
POR
;POR according to Hitachi datasheet.  NB: RB<0:3> are mapped to display lines
;DB<4:7>, in order.  Use PutNib for the POR unitl 4-bit mode is set.   A swapf
;is not needed in PutNib as the lower 4 bits of PORTB are sent first and only
;those bits are sent.

DelayCy   (100*msecs)
movlw     0x03
call      PutNib
DelayCy   (5*msecs)
movlw     0x03
call      PutNib
DelayCy   (1*msecs)
movlw     0x03
call      PutNib
movlw     02           ;Function set: read as 0x20
call      PutNib         ;4-bit mode is now set, N & F not defined yet

;LCD is now in 4-bitmode and 8-bit instructions and data are sent as two nibbles.

movlw     0x28           ;Function set: 8 bits sent 4-bit mode
call      PutCmd         ;N & F are now defined
movlw     0x08           ;Display on/off control: display off
call      PutCmd
movlw     0x01           ;Clear display: clears display
call      PutCmd
DelayCy   (2*msecs)
movlw     0x06           ;Entry mode set: increment address (cursor)
call       PutCmd
movlw     0x0C           ;Display on/off: display on/cursor off/blink off
call       PutCmd
return
Don't know if that helps. Here is a link to the Hitachi datasheet: https://www.sparkfun.com/datasheets/LCD/HD44780.pdf

John

Edit: That code was written before I started using the busy signal for timing. Once you enter 4-bit mode, using busy removes doubts about how long the delays should be.

Last edited:

#### ErnieM

Joined Apr 24, 2011
7,991

BTW, Proteus is very realistic. If you get your code working on Proteus, it has a great chance to work in the real world.
I would turn that statement around as such:
"If you can't get your code working on Proteus, you have no chance it will work in the real world."

Simulators cannot possibly cover all real world possibilities.

#### spinnaker

Joined Oct 29, 2009
7,815
Here is assembly for the initialization:
Code:
POR
;POR according to Hitachi datasheet.  NB: RB<0:3> are mapped to display lines
;DB<4:7>, in order.  Use PutNib for the POR unitl 4-bit mode is set.   A swapf
;is not needed in PutNib as the lower 4 bits of PORTB are sent first and only
;those bits are sent.

DelayCy   (100*msecs)
movlw     0x03
call      PutNib
DelayCy   (5*msecs)
movlw     0x03
call      PutNib
DelayCy   (1*msecs)
movlw     0x03
call      PutNib
movlw     02           ;Function set: read as 0x20
call      PutNib         ;4-bit mode is now set, N & F not defined yet

;LCD is now in 4-bitmode and 8-bit instructions and data are sent as two nibbles.

movlw     0x28           ;Function set: 8 bits sent 4-bit mode
call      PutCmd         ;N & F are now defined
movlw     0x08           ;Display on/off control: display off
call      PutCmd
movlw     0x01           ;Clear display: clears display
call      PutCmd
DelayCy   (2*msecs)
movlw     0x06           ;Entry mode set: increment address (cursor)
call       PutCmd
movlw     0x0C           ;Display on/off: display on/cursor off/blink off
call       PutCmd
return
Don't know if that helps. Here is a link to the Hitachi datasheet: https://www.sparkfun.com/datasheets/LCD/HD44780.pdf

John

Edit: That code was written before I started using the busy signal for timing. Once you enter 4-bit mode, using busy removes doubts about how long the delays should be.

Op is programming in C.

I have attached the code that I use in C. You will need to customize it.

#### Attachments

• 2 KB Views: 2

#### jpanhalt

Joined Jan 18, 2008
7,691
Op is programming in C.

I have attached the code that I use in C. You will need to customize it.
That was obvious, hence my comment. Nevertheless, converting the algorithm/sequence from Assembly to C should not be difficult. In fact, that is why I didn't include the code for PutCmd, etc. The OP was simply not following the datasheet.

John