Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] LCD 4 bit Mode is not working

Status
Not open for further replies.

thannara123

Advanced Member level 5
Joined
Jan 7, 2010
Messages
1,580
Helped
122
Reputation
244
Reaction score
114
Trophy points
1,353
Location
India
Activity points
10,382
I am trying to make an LCD Interfacing with 4 bit mode ,
But not working I tried a lot of
Data looked in Debug mode good not found wrong

what is the problem ?

Attached Code and Proteus files





C:
#pragma config FOSC = XT        // Oscillator Selection bits (XT oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = ON         // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3/PGM pin has PGM function; low-voltage programming enabled)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

#define _XTAL_FREQ 1000000

#include <xc.h>
#define LCD_PORTB PORTB
#define RS  RB0
#define EN  RB1

void Strobe (void)
{
    EN = 1;
    __delay_us(1);
     EN = 0;
 
}
void LCD_cmd( unsigned char cmd)
{
    RS = 0;
    __delay_ms(1);
    LCD_PORTB = (cmd & 0xf0) ;
    Strobe();
    LCD_PORTB = (cmd << 4 );
    Strobe();
}
void LCD_data( unsigned char data)
{
     RS = 1;
    __delay_ms(1);
    LCD_PORTB |= (data & 0xf0);
    Strobe();
    LCD_PORTB |= (data << 4 );
    Strobe();
}
void LCD_init()
{
    __delay_ms(20);
     LCD_cmd(0x38);
     __delay_ms(4);
     LCD_cmd(0x38);
    __delay_ms(10);
   // LCD_cmd(0x38);   // Power on Initilization
    LCD_cmd(0x28);    /* 4bit mode */
    LCD_cmd(0x28);    /* Initialization of 16X2 LCD in 4bit mode */
 
    LCD_cmd(0x0C);    /* Display ON Cursor OFF */
    LCD_cmd(0x06);    /* Auto Increment cursor */
 
}
void string(const char *ptr)
{
    while (*ptr)
    {
        LCD_data(*ptr++);
    }
}

     void main()
    {
       TRISB =0;
       TRISD =0;
   
   
       LCD_init();
       LCD_cmd(0x80);
     //string("HELLO WORLD");
      // LINE2;
       string("IT IS WORKING:-)");
   
       while(1);
        return ;
}

1.jpg
 

Attachments

  • LCD_4_Bit_Interfacing.rar
    156.1 KB · Views: 116

There is still a potential problem with writing to PORTB. When you use:
Code:
PORTB = (cmd & 0xf0) ;
PORTB = (cmd << 4 )&0xf0;
you reset the lower four bits of PORTB which drives EN and RS low.
Since in the actual code - right after both PORTB data loads - there is a Strobe() command handling the EN control pin, at the end seems like being not an issue at all, once data is submitted to the LCD just after issued this command.
 

i have not used a PIC with a display, but I found this:


it includes schematic and downloadable code that may help

even better, this one uses 4 pins instead of 8:

 

The init command sequence doesn't comply with specification.
--- Updated ---

Notice that the first commands are send as 4-bit instructions, with one rather than two strobes.
 
Last edited:

Notice that the first commands are send as 4-bit instructions, with one rather than two strobes.

Indeed, but at the end thie 1st assignment on PORTB to 0x00 is useless at all and could even be removed with no loss. It's rather something that OP forgot to clean. At the end it does nothing, only the 2nd and 3rd assignment take into account 'data' argument to get split into 2 sequential nibble commands.


Code C - [expand]
1
2
3
4
5
6
7
8
void LCD_data( unsigned char data)
{  
    PORTB = 0x00;
    PORTB = (data & 0xf0) | 1;
    Strobe();
    PORTB = (data << 4 ) | 1;
    Strobe();
}

 

LCD_data function should be correct as suggested in post #13, post #16 should also work, PORTB = 0 is superfluous but does nothing wrong.

Problem is however, that init sequence must not use LCD_cmd() function but 4-bit commands at the start. See diagram from Hitachi HD44780 data sheet.

20230112_211827.jpg
 

As you are using the simulator, then you might be fighting a bug in the simulator code. That is why I always recommend using real hardware.
Also that old chip doesn't have LAT registers so you may encounter RMW issues when you move to the real hardware. This has been (sort of) touched on elsewhere in this thread about handling the 'E' and 'R/W' signals in the same port as the 4 data lines.
I would suggest that you create an 8-bit variable within your code where you can set and clear the bits as you need and then write the whole value to the PORT register.
Susan
 

    thannara123

    Points: 2
    Helpful Answer Positive Rating
Didn't you note you are issuing an 8bit mode configuration command to the LCD whereas the actual connections are for 4 bits only ?

Code C - [expand]
1
LCD_cmd(0x38);

I changed as said in data sheet(Below code ) but not working .I think where will be the problem . I want to rectify it .

C:
void LCD_init()
{
    __delay_ms(40);
     LCD_cmd(0x30);
     __delay_ms(5);
     LCD_cmd(0x30);
    __delay_us(100);
    LCD_cmd(0x30);    /* 4bit mode */
    LCD_cmd(0x28);
    LCD_cmd(0x01);
    LCD_cmd(0x06);
}


Some other code working on the same simulation circuit but the code is diffrent (my old project code ) but the above code not .
 

You didn't yet understand the fault.
--- Updated ---

Instead of LCD_cmd, you'll use a PORTB assignment and single strobe() for the first commands.
 

You're likely still not paying attention on the LCD datasheet ( guessing you're using a HD44780 compatible one ).

Code C - [expand]
1
LCD_cmd(0x30);    /* 4bit mode */

The correct command to set the 4bit mode ( default anyway ) should set the 2nd bit of the nibble as '1' and the 1st bit of the nibble as '0' ( RB5=1 and RB4=0 ) which would give the value 0x2x, not 0x3x.

By issuing 0x30 ( RB5=1 and RB4=1 ), you're configuring the communication interface data length as 8-bit ( DL= 1 ):

4-8bits.png

Note that in the command mode, the MSB of the issued ( RBx ) data has the function of selecting the command itself whereas the remaining bits are the argument, when not unused.

So, in your above code, to get things even worse, at the subsequent line right after the 0x30 you are issuing the supposedly correct command:

Code C - [expand]
1
LCD_cmd(0x28);

But, now that you changed the interface length, the LCD_cmd() will no longer work ( this function has no argument to select 4 or 8 bit mode ), so you should be aware of what you are doing here, as @FvM has already pointed out, instead of a bunch trial and error experiments.

If I were you I would review the whole LCD init setup parameters, I'd bet you're screwing up something else.

BTW, it is allways advisable to comment each statement, or even create defined allias for those bits to prevent misuse.
 

    thannara123

    Points: 2
    Helpful Answer Positive Rating
Thanks Learned a lot of from this thread .Finally Worked .
But ,

The Problem was the PORTB = 0x00; and
The PORTBbits.1 connection changes to PORTBbits.2 (EN pin is connected )

when i removed this PORTB = 0x00; and the EN connection changed , its worked

,.Why so ?.

Why didn't work on PORTBbits.1 ?

uploaded the code below
 

Attachments

  • my.txt
    2.1 KB · Views: 76

What is the problem, that is the Enable pin is not working on PORTBbits.1 ,works only on PORTBbits 2
 
Last edited:

There is no relevant difference between PORTB.0, PORTB.1 and PORTB.2, at least as far as your program uses them so it may be a faulty PIC. The other possibility is the RMW problem mentioned by Susan earlier: when you change a bit in a port, it reads the port in (R), modifies (changes) the bit (M) then writes it back to the port (W) so if anything external is influencing the pin state it may read as a different value to the one you last wrote to it. Note that the LCD itself can output status signals at times.

The solution is to use a 'shadow' variable, set/reset the bits in a variable then write that variable to the port in one operation. I avoids any RMW issues at the expense of one extra memory location. Later PIC16 and all PIC18 devices use a slightly different PORT system to prevent it happening.

Brian.
 
As you are using the simulator, then you might be fighting a bug in the simulator code. That is why I always recommend using real hardware.
Also that old chip doesn't have LAT registers so you may encounter RMW issues when you move to the real hardware. This has been (sort of) touched on elsewhere in this thread about handling the 'E' and 'R/W' signals in the same port as the 4 data lines.
I would suggest that you create an 8-bit variable within your code where you can set and clear the bits as you need and then write the whole value to the PORT register.
Susan
My i get a sample code with the use of a variable to remove RMW error.
 

This is written from 'my head' with no compiler or hardware with me at the moment so it isn't tested but it should give some idea of how to shadow the port:
Code:
// control bit placements in low half of PORTB
#define RS 0
#define EN 2

// generic write to PORTB with strobe
void LCD_Write(char data)
{
    unsigned char temp = data;
    PORTB = temp;
    temp |= (1 << EN);
    PORTB = temp;
    __delay(1);
    temp &= ~(1 << EN);
    PORTB = temp;
    __delay(1);
}
    
// write 8 data bits to LCD in 4-bit mode
void LCD_Data(char data)
{
    LCD_Write((data & 0xF0) | (1 << RS));
    LCD_Write((data << 4) | (1 << RS));
}

// write a 4 bit command to the LCD
void LCD_Cmd4(char cmd) 
{   
    LCD_Write((cmd << 4) &= ~(1<< RS));
}

// write a 8 bit command to the LCD
void LCD_Cmd8(char cmd)
{    
    LCD_Write((cmd &= 0xF0) &= ~(1 << RS));
    LCD_Write((cmd << 4) &= ~(1 << RS);
}

Note that all the operations to write to PORTB are 8-bits wide, no bits are set or reset individually.
LCD_Data() writes any char value to the LCD, the two LCD_Cmd() routines are to write 4 or 8 bits as needed.

Brian.
 

    thannara123

    Points: 2
    Helpful Answer Positive Rating
@betwixt sir
// write a 8 bit command to the LCD

Code:
void LCD_Cmd8(char cmd)

{  

    LCD_Write((cmd &= 0xF0) &= ~(1 << RS));

    LCD_Write((cmd << 4) &= ~(1 << RS);

}

Error as shown -only lvalues can be assigned to or modified

Can i use as follows instead of this ,

Code:
 LCD_Write((cmd & 0xF0) | ~(1<<RS));
    LCD_Write((cmd << 4)  | ~(1<<RS));
 

Try it. It is quite possible I made mistakes but the intention was to show how to use a shadow variable to avoid RMW problems. I was writing it while on the move and had no computer to try it out.

Brian.
 
Yes i Understood . Thanks forl all .T
he problem was due to the RMW Error
Also i thought some more techniques from this thread..
Thanks
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top