Help with LCD Program

Thread Starter

beeson76

Joined Apr 19, 2010
211
This is kind of a follow up post to Using 2 Different Ports for the Same Input post by me (beeson76). That post contains the main program and this post contains the LCD.c program.

I have an LCD program that is from the samples directory of Hi-Tech complier. I would like to get all communication with the LCD and outputs on PORTC. Here is the program. As you can see the program uses both PORTA and PORTC. PORTA for communication with the LCD and PORTC with OUTPUT.

Rich (BB code):
#include "htc.h"
#include "lcd.h"

void pause(unsigned short usvalue);

#define         LCD_RS          RA2
#define         LCD_RW          RA4
#define         LCD_EN          RA1
#define         LCD_DATA        PORTC
#define         LCD_STROBE()    ((LCD_EN = 1),(LCD_EN = 0))

void
lcd_write (unsigned char c)
{
        pause (1);
        LCD_DATA = ((c >> 4) & 0x0F);
        LCD_STROBE();
        LCD_DATA = (c & 0x0F);
        LCD_STROBE();
}

void
lcd_clear(void)
{
        LCD_RS = 0;
        lcd_write (0x1);
        pause(2);
}

void
lcd_puts(const char *s)
{
        LCD_RS = 1;
        while (*s)
                lcd_write(*s++);
}

void
lcd_putch(char c)
{
        LCD_RS = 1;
        lcd_write(c);
}

void
lcd_goto(unsigned char pos)
{
        LCD_RS = 0;
        lcd_write (0x80 + pos);
}

void
lcd_init()
{
        char init_value;

//      ANSEL = 0;

        init_value = 0x3;
        TRISA = 0;
        TRISC = 0;
        LCD_RS = 0;
        LCD_EN = 0;
        LCD_RW = 0;

        pause(15);
        LCD_DATA = init_value;
        LCD_STROBE();
        pause(10);
        LCD_STROBE();
        pause(10);
        LCD_STROBE();
        pause(10);
        LCD_DATA = 2;
        LCD_STROBE();

        lcd_write (0x28);
        lcd_write (0xF);
        lcd_clear();
        lcd_write(0x6);
}

void lcd_putint (int i)
{
        LCD_RS = i;
        lcd_write(i);
}

I would like for
LCD_EN to be RC0
LCD_RS to be RC1
LCD_RW to be RC2

and I would like for DB4-DB7 of the LCD inputs to be RC4-RC7.

Thanks for any help. Appreciate it very much.
 

t06afre

Joined May 11, 2009
5,934
You can try this. It is just from the top of my head. Not compiled or tested.
Rich (BB code):
/*LCD_EN to be RC0
LCD_RS to be RC1
LCD_RW to be RC2
write cycle R/_S=1,R/_W=0 EN=1*/
lcd_write (unsigned char c)
{       unsigned char low_nibble=c
        low_nibble=(low_nibble >> 4) | (low_nibble << 4); //C version of swapf asm instruction
        c=c|0b00000011;  //LCD_EN=1,LCD_RS=1
        c=c&0b11111011;  //LCD_RW=0
        low_nibble=low_nibble|0b00000011; //LCD_EN=1,LCD_RS=1
        low_nibble=low_nibble&0b11111011; //LCD_RW=0
        pause (1);
        LCD_DATA =C;  //high_nibble out
        LCD_STROBE();
        LCD_DATA = low_nibble; //low_nibble out
        LCD_STROBE();
}
Edit: Remember to define LCD_EN, LCD_RS and LCD_RW as described in your post
 
Last edited:

Thread Starter

beeson76

Joined Apr 19, 2010
211
Thanks t06afre for the reply. I will certainly look it over and try it.

From what I understand, because this program assigns

Rich (BB code):
LCD_DATA = PORTC;
this will assign DB4-DB7 to PORTC0..C3. Thus when I

Rich (BB code):
#define		LCD_RS		RC1
#define		LCD_RW		RC2										
#define		LCD_EN		RC0
I have a conflict.

Because of this, I need to move my DB4-DB7 to the upper bits (upper nibbles) of LCD_DATA. Is that right?
 

t06afre

Joined May 11, 2009
5,934
In 4 bit mode you only use the d4 to d7 of the LCD databus. First you write the Hi nibble then low. However I think you need to modify you init ruotine somewhat.
Initialization for 4-bit operation
The module powers up in 8-bit mode. The initial start-up instructions are sent in 8-bit mode, with the lower four
bits (which are not connected) of each instruction as don't cares.
<POWER ON>
<Wait at least 15ms>
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 1 1 n/c n/c n/c n/c
<Wait at least 4.1ms>
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 1 1 n/c n/c n/c n/c
<Wait at least 100us>
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 1 1 n/c n/c n/c n/c
<Wait 4.1ms>
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 1 0 n/c n/c n/c n/c
After the fourth instruction shown above, which switches the module to 4-bit operation, the control bytes are
sent on consecutive enable cycles (no delay is required between nibbles). The most significant nibble is sent
first, followed immediately by the least significant nibble.
Rich (BB code):
void
lcd_init()
{
char init_value;
 
// ANSEL = 0;
 
//init_value = 0x3;
TRISA = 0;//do we need this?
TRISC = 0;
/*LCD_RS = 0;
LCD_EN = 0;
LCD_RW = 0;
 */
pause(15);
LCD_DATA = 0b00110000;
LCD_STROBE();
pause(10);
LCD_STROBE();
pause(10);
LCD_STROBE();
pause(10);
LCD_DATA = 00100000;
LCD_STROBE();
pause(10);
lcd_write (0x28);
lcd_write (0xF);
lcd_clear();
lcd_write(0x6);
}
 

BMorse

Joined Sep 26, 2009
2,675
in order to accomplish what you want, you will just have to write the data nibbles to the upper 4 bits of the port you are writing to instead of the lower 4 bits...


Try this instead, instead of shifting the data 4 bits to the right, shift it 4 bits to the left and mask it with 0xF0 instead of 0x0F....

Rich (BB code):
void
lcd_write (unsigned char c)
{
        pause (1);
        LCD_DATA = ((c << 4) & 0xF0);
        LCD_STROBE();
        LCD_DATA = (c & 0xF0);
        LCD_STROBE();
}
P.S.
One thing I did notice with using the other 4 bits of the same port as the LCD, is that if you have those configured as outputs, their state will change when data is written to the LCD..... so if you are using those other 4 bits as outputs, expect them to change to low whenever you write to the LCD, it would be best if those pins are used as inputs, then they will not be affected by the LCD data.....

B. Morse
 

Thread Starter

beeson76

Joined Apr 19, 2010
211
Thanks (BMorse and T06afre) for the replies.

I have a question about the lcd_init.

Why did you:

LCD_DATA = 0b00110000;

and

LCD_DATA = 0b00100000;

Thanks. I appreciate the help.
 

Thread Starter

beeson76

Joined Apr 19, 2010
211
Never mind as far as the 0b00110000 and 0b00100000. I figured it out. It is part of the initialization process for the 4 bit mode. Thanks for the nice explanation of it T06afre of 4 bit initialization.
 

Thread Starter

beeson76

Joined Apr 19, 2010
211
If I don't read from the LCD could all my pins on my chip be set as outputs? All my pins on PORTC are set as outputs. Is that correct?
 

Thread Starter

beeson76

Joined Apr 19, 2010
211
Do these pieces of code do the same thing:

Rich (BB code):
void
lcd_write (unsigned char c)
{
      pause (1);
      LCD_DATA = ((c << 4) & 0xF0);
      LCD_STROBE();
      LCD_DATA = (c & 0xF0);
      LCD_STROBE();

}
Rich (BB code):
void
lcd_write (unsigned char c)
{
        pause (1);
	LCD_DATA &= 0x0F;					
	LCD_DATA |= (c & 0xF0);				
	LCD_STROBE();						
        LCD_DATA &= 0x0F;					
        LCD_DATA = ((c << 4) & 0xF0); 		
        LCD_STROBE();						
}
Rich (BB code):
void
lcd_write (unsigned char c)
{
	pause(1);
        LCD_DATA = (LCD_DATA & 0x0F) + (c & 0xF0);								
	LCD_STROBE();
	LCD_DATA = (LCD_DATA & 0x0F) + ((c & 0x0F) << 4);							 	
	LCD_STROBE();
}
 
Last edited:

t06afre

Joined May 11, 2009
5,934
If I don't read from the LCD could all my pins on my chip be set as outputs? All my pins on PORTC are set as outputs. Is that correct?
Yes that is correct. You can even tie the R/W input on the display to GND. If you still struggle I have some code you can lock at. My first suggestion did not take care of R/S signal properly. And I think also that will be a problem in the suggestion from BMorse
 

t06afre

Joined May 11, 2009
5,934
Here is something you can try. It take care off different RS line settings. It will compile in Lite mode. I have simulated and so far as I can see it should work. I have not included any __config() settings. So that must be done
By the way here is the new Hi-tech C compiler. It take care of a bug then using __dealy_ms() function
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en542849
Rich (BB code):
#define _XTAL_FREQ 4000000
#include <htc.h>
#define  LCD_RS  RC1
#define  LCD_RW  RC2          
#define  LCD_EN  RC0
#define         LCD_DATA        PORTC
#define         LCD_STROBE()    ((LCD_EN = 1),(LCD_EN = 0))
//3.2.4 Bit Instructions
//Wherever possible, HI-TECH C will attempt to use the PIC10/12/16 bit instructions. For
//example, when using a bitwise operator and a mask to alter a bit within an integral type,
//the compiler will check the mask value to determine if a bit instruction can achieve the
//same functionality.
//unsigned int foo;
//foo |= 0x40;
//will produce the instruction:
//BSF _foo,6
//To set or clear individual bits within integral type, the following macros could be used:
//#define bitset(var, bitno) ((var) |= 1UL << (bitno))
//#define bitclr(var, bitno) ((var) &= ~(1UL << (bitno)))
//To perform the same operation as above, the bitset macro could be employed as
//follows:
//bitset(foo,6);
#define bitset(var, bitno) ((var) |= 1UL << (bitno))
#define bitclr(var, bitno) ((var) &= ~(1UL << (bitno)))
unsigned char control_bus;
 

lcd_write ( unsigned char c)
{       unsigned char low_nibble=c;
        low_nibble=(low_nibble >> 4) | (low_nibble << 4); //C version of swapf asm instruction
        bitset(control_bus,0);//Set EN=0x01
        c=c&0xf0;  //mask 4 lower bits
        c=c|control_bus; 
        low_nibble=low_nibble&0xf0;  //mask 4 lower bits
        low_nibble=low_nibble|control_bus; 
        LCD_DATA =c;  //high_nibble out
        LCD_STROBE();
        LCD_DATA = low_nibble; //low_nibble out
        LCD_STROBE();
 __delay_us(50);
}
void lcd_clear(void)
{
        bitclr(control_bus, 1);//Set RS line=0
        lcd_write (0x1);
        __delay_ms(5);
}
void lcd_puts(const char *s)
{
        bitset(control_bus, 1);//Set RS line=0x01
        while (*s)
                lcd_write(*s++);
}
void lcd_putch(char c)
{
     bitset(control_bus, 1);//Set RS line=0x01
        lcd_write(c);
}
void lcd_goto(unsigned char pos)
{
        bitclr(control_bus, 1);//Set RS line=0x00
        lcd_write (0x80 + pos);
}
void lcd_init()
{
//char init_value;
 
ANSEL = 0;
ANSELH =0;
 
//init_value = 0x3;
TRISA = 0;
TRISC = 0;
bitclr(control_bus,0);
bitclr(control_bus,1);
bitclr(control_bus,2);
 
__delay_ms(30);
LCD_DATA = 0b00110000;
LCD_STROBE();
__delay_ms(15);
LCD_STROBE();
__delay_ms(5);
LCD_STROBE();
__delay_ms(5);
LCD_DATA = 00100000;
LCD_STROBE();
__delay_ms(5);
lcd_write (0x28);
lcd_write (0xF);
lcd_clear();
lcd_write(0x6);
}
void lcd_putint (int i)
{
      bitclr(control_bus, 1);//Set RS line=0x00;
        lcd_write(i);
}
 
void main(void)
{   
    control_bus=0;
 lcd_init();
 lcd_goto(0); // select first line
 lcd_puts("12345678");
 lcd_goto(0x40); // Select second line
 lcd_puts("Hello world");
 for(;;);
 

Thread Starter

beeson76

Joined Apr 19, 2010
211
Thanks T06afre for the replies. I appreciate it very much. Sorry for the delay in the "thanks". I wan't able to get to the website for a few days. I have worked up my code and here is what I got. It seems to work ok by way of simulator, but I am going to try to work up a circuit now, which I haven't yet done with a microcontroller. Here is my code.

Rich (BB code):
#include <htc.h>
#include "lcd.h"

void pause(unsigned short usvalue);

#define		LCD_RS		RC1
#define		LCD_RW		RC2										
#define		LCD_EN		RC0
#define		LCD_DATA	PORTC
#define		LCD_STROBE()	((LCD_EN = 1),(LCD_EN = 0))

void
lcd_write (unsigned char c)
{
										//(cccc) = clearing bit - this is what is LCD_STROBE()
										//(kkkk) = keeping bit - this is kept because of LCD_RS, LCD_RW, LCD_EN

	
						//(& 0bcccckkkk) clearing top nibble of LCD_DATA and keeping bottom nibble of LCD_DATA
					//(| 0bkkkkcccc) filling top nibble of LCD_DATA with top nibble of c
							//sending top nibble to LCD
    					//clearing top nibble of LCD_DATA and keeping bottom nibble of LCD_DATA
     		//moving bottom nibble of c to high bits of LCD_DATA
    						//sending top nibble of LCD_DATA to LCD

	pause(1);
    LCD_DATA = (LCD_DATA & 0x0F) + (c & 0xF0);								
	LCD_STROBE();
	LCD_DATA = (LCD_DATA & 0x0F) + ((c & 0x0F) << 4);							 	
	LCD_STROBE();


}

void
lcd_clear(void)
{
	LCD_RS = 0;
	lcd_write (0x1);
	pause(2);
}

void 
lcd_puts(const char *s)
{
	LCD_RS = 1;
	while (*s)
		lcd_write(*s++);
}

void
lcd_putch(char c)
{
	LCD_RS = 1;
	lcd_write(c);
}

void
lcd_goto(unsigned char pos)
{
	LCD_RS = 0;
	lcd_write (0x80 + pos);
}

void
lcd_init()
{
	TRISC = 0;
	LCD_EN = 0;													 
	LCD_RS = 0;
	LCD_RW = 0;

	pause(15);
	
	LCD_DATA &=0x0F;
	LCD_DATA = 0x30;
	LCD_STROBE();
	
	pause(5);
	LCD_STROBE();
	
	pause(1);
	LCD_STROBE();
	
	pause(5);
	LCD_DATA &= 0x0F;
	LCD_DATA = 0x20;
	LCD_STROBE();
	

	lcd_write (0x28);										
	lcd_write (0x0F);										
	lcd_clear();					
	lcd_write(0x06);											
}
 
Last edited:
Top