View previous topic :: View next topic |
Author |
Message |
Bryan
Joined: 23 Apr 2005 Posts: 73
|
LCD on multiple ports |
Posted: Sun May 15, 2005 7:11 pm |
|
|
I am attempting to modify Mark's driver that allows for data lines and control lines on different ports for LCD control but haven't been able to get it to initialize. I think my problem involves #locate and locating the tris or ports incorrectly (Mark used ports B and D while I am using A and B) but I am not sure how to do this correctly. I know the problem is not in my test program since the LCD works fine with the standard CCS driver set solely on port B. As you can see in the code I use A1-A3 for control lines and B4-B7 for data lines. Here is my code:
Code: |
////////////////////////////////////////////////////////////////////////////
//// LCDD.C ////
//// Driver for common LCD modules ////
//// ////
//// lcd_init() Must be called before any other function. ////
//// ////
//// lcd_putc(c) Will display c on the next position of the LCD. ////
//// The following have special meaning: ////
//// \f Clear display ////
//// \n Go to start of second line ////
//// \b Move back one position ////
//// ////
//// lcd_gotoxy(x,y) Set write position on LCD (upper left is 1,1) ////
//// ////
//// lcd_getc(x,y) Returns character at position x,y on LCD ////
//// ////
////////////////////////////////////////////////////////////////////////////
//// (C) Copyright 1996,1997 Custom Computer Services ////
//// This source code may only be used by licensed users of the CCS C ////
//// compiler. This source code may only be distributed to other ////
//// licensed users of the CCS C compiler. No other use, reproduction ////
//// or distribution is permitted without written permission. ////
//// Derivative programs created using this software in object code ////
//// form are not restricted in any way. ////
////////////////////////////////////////////////////////////////////////////
// This structure is overlayed onto the data ports so that you may use
// whatever ports you desire
struct lcd_pin_map
{
BOOLEAN dummy; // PinA0 is not used
BOOLEAN enable; // PinA1
BOOLEAN rw; // PinA2
BOOLEAN rs; // PinA3
int unusedA : 4; // The rest of portA
int unusedB : 4; // upper nibble of portB is not used
int data : 4; // lower nibble of portB is used for data lines
} lcd;
#if defined(__PCH__)
#locate lcd = 0xF80
#else
#locate lcd = 5
#endif
struct lcd_tris_map
{
BOOLEAN dummy; // PinA0 is not used
int control : 3; // Control lines
int unusedA : 4; // The rest of portA
int unusedB : 4; // portB unused
int data : 4; // Data lines
} lcdtris;
#if defined(__PCH__)
#locate lcdtris = 0xF92
#else
#locate lcdtris = 0x85
#endif
#define set_tris_lcd(x) lcdtris.data = (x); lcdtris.control = 0;
#define lcd_type 2 // 0=5x7, 1=5x10, 2=2 lines
#define lcd_line_two 0x40 // LCD RAM address for the second line
BYTE const LCD_INIT_STRING[4] = {0x20 | (lcd_type << 2), 0xc, 1, 6};
// These bytes need to be sent to the LCD
// to start it up.
// The following are used for setting
// the I/O port direction register.
#define LCD_WRITE 0 // For write mode all pins are out
#define LCD_READ 15 // For read mode data pins are in
BYTE lcd_read_byte() {
BYTE low,high;
set_tris_lcd(LCD_READ);
lcd.rw = 1;
delay_cycles(1);
lcd.enable = 1;
delay_cycles(1);
high = lcd.data;
lcd.enable = 0;
delay_cycles(1);
lcd.enable = 1;
delay_us(1);
low = lcd.data;
lcd.enable = 0;
set_tris_lcd(LCD_WRITE);
return( (high<<4) | low);
}
void lcd_send_nibble( BYTE n ) {
lcd.data = n;
delay_cycles(1);
lcd.enable = 1;
delay_us(2);
lcd.enable = 0;
}
void lcd_send_byte( BYTE address, BYTE n ) {
lcd.rs = 0;
while ( bit_test(lcd_read_byte(),7) ) ;
lcd.rs = address;
delay_cycles(1);
lcd.rw = 0;
delay_cycles(1);
lcd.enable = 0;
lcd_send_nibble(n >> 4);
lcd_send_nibble(n & 0xf);
}
void lcd_init() {
BYTE i;
set_tris_lcd(LCD_WRITE);
lcd.rs = 0;
lcd.rw = 0;
lcd.enable = 0;
delay_ms(15);
for(i=1;i<=3;++i) {
lcd_send_nibble(3);
delay_ms(5);
}
lcd_send_nibble(2);
for(i=0;i<=3;++i)
lcd_send_byte(0,LCD_INIT_STRING[i]);
}
void lcd_gotoxy( BYTE x, BYTE y) {
BYTE address;
if(y!=1)
address=lcd_line_two;
else
address=0;
address+=x-1;
lcd_send_byte(0,0x80|address);
}
void lcd_putc( char c) {
switch (c) {
case '\f' : lcd_send_byte(0,1);
delay_ms(2);
break;
case '\n' : lcd_gotoxy(1,2); break;
case '\b' : lcd_send_byte(0,0x10); break;
default : lcd_send_byte(1,c); break;
}
}
char lcd_getc( BYTE x, BYTE y) {
char value;
lcd_gotoxy(x,y);
lcd.rs=1;
value = lcd_read_byte();
lcd.rs=0;
return(value);
}
|
Anyone see the error? |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun May 15, 2005 8:48 pm |
|
|
Tell us which PIC pins that you are using for each LCD signal.
Give us the exact pins (don't just say "upper pins", etc.) |
|
![](templates/subSilver/images/spacer.gif) |
Bryan
Joined: 23 Apr 2005 Posts: 73
|
|
Posted: Sun May 15, 2005 9:45 pm |
|
|
A1: enable
A2: rw
A3: rs
B4: data1
B5: data2
B6: data3
B7: data4
I avoided A4 and A5 purposefully since I/O is tricky with them. |
|
![](templates/subSilver/images/spacer.gif) |
Bryan
Joined: 23 Apr 2005 Posts: 73
|
|
Posted: Sun May 15, 2005 10:06 pm |
|
|
I just realized that I had the pins hooked up incorrectly in my delirium (Was trying to work on it late last night on little sleep so that's what I get ). I just redid them as described in my previous post and it works fine now. Thanks Mark for writing a great driver! |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun May 15, 2005 10:24 pm |
|
|
Oh -- well, while you were doing that, I was typing in one million lines
of a response to your question. Should I not post it ? No, I'm going
to post it anyway.
---------
Quote: | A1: enable
A2: rw
A3: rs
B4: data1
B5: data2
B6: data3
B7: data4 |
I'm not sure where you're getting those names for the LCD data bus pins.
Here is a link to the Hantronix 16x2 LCD (without backlight) data sheet.
In this data sheet, they refer to the data bus signals as D0-D7.
http://www.hantronix.com/down/16216h5.pdf
When you use a character LCD in 4-bit mode, you always use LCD signals
D4-D7. (The 4-bit mode is commonly used in embedded applications
because it uses the minimal amount of micro-controller pins).
So your PIC to LCD connections should really look like this:
Code: | A0: Not used
A1: E
A2: R/W
A3: RS
B4: D4
B5: D5
B6: D6
B7: D7 |
In the pin map structures, you have to carefully enter the bits
that are going to be used within each port. Equally carefully,
you have to put in placeholders for all the bits that are not
going to be used, either in entire ports, or in portions of ports.
If you leave anything out, the addresses could be incorrect
when the code is compiled. The following code shows how to
do it. The bits used by your LCD are shown in bold.
Quote: | struct lcd_pin_map
{
BOOLEAN dummy; // PinA0 is not used
BOOLEAN enable; // PinA1
BOOLEAN rw; // PinA2
BOOLEAN rs; // PinA3
int unusedA : 4; // The rest of portA is not used.
int unusedB:4; // Lower 4 bits of portB are not used
int data:4; // Upper 4 bits of Port B are used.
int unusedC; // portC is not used
int unusedD; // portD is not used
} lcd;
#if defined(__PCH__)
#locate lcd = 0xF80
#else
#locate lcd = 5
#endif
struct lcd_tris_map
{
BOOLEAN dummy; // PinA0 is not used
int control : 3; // Bits 1,2,and 3 of PortA are used.
int unusedA : 4; // The rest of portA
int unusedB : 4; // The lower 4 bits of PortB are not used
int data : 4; // The upper 4 bits of PortB are used.
int unusedC; // PortC is not used
int unusedD : 4; // PortD is not used
} lcdtris;
#if defined(__PCH__)
#locate lcdtris = 0xF92
#else
#locate lcdtris = 0x85
#endif
#define set_tris_lcd(x) lcdtris.data = (x); lcdtris.control = 0; |
|
|
![](templates/subSilver/images/spacer.gif) |
Bryan
Joined: 23 Apr 2005 Posts: 73
|
|
Posted: Sun May 15, 2005 11:38 pm |
|
|
Thanks for posting! I realize that my designating the pins as data1-data4 could be confusing, but I was just trying to show that the data pins I was using were on pins B4-B7 since in the struct you can see that pins B0-B3 are placeholders (I did have the structs right for my PIC setup, just physically connected the pins wrong on my board). The only things I'm still curious about are how you know where to set the #locate statements for the tris and lcd structs and what the #define statements mean as shown below (What does the x mean in #define set_tris_lcd(x) lcdtris.data = (x); lcdtris.control = 0;?):
Code: |
#if defined(__PCH__)
#locate lcd = 0xF80
#else
#locate lcd = 5
#endif
#if defined(__PCH__)
#locate lcdtris = 0xF92
#else
#locate lcdtris = 0x85
#endif
#define set_tris_lcd(x) lcdtris.data = (x); lcdtris.control = 0;
|
|
|
![](templates/subSilver/images/spacer.gif) |
newguy
Joined: 24 Jun 2004 Posts: 1903
|
|
Posted: Sun May 15, 2005 11:50 pm |
|
|
That information is in the PIC's datasheet. Look in the memory organization section. There will be a table with the "special function registers" of that particular pic. For instance, address 0xf80 is the address for port A for the 18 series. 0xf81 = port B, etc. |
|
![](templates/subSilver/images/spacer.gif) |
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Mon May 16, 2005 5:37 am |
|
|
The 'x' is a variable parameter. Think of this as being a macro. |
|
![](templates/subSilver/images/spacer.gif) |
|