Jump to content
Sign in to follow this  
GeekDoc

HD44780-based LCD - cursor problem

Recommended Posts

Alright, I've been banging my head against the wall trying to figure this one out. :x

 

I have a HD44780-based 2x16 character LCD that I am addressing through a 74HC595 serial in/parallel out latching shift register. I have it all working pretty well, but I cannot seem to move the cursor to the second line (0x40). I can place the cursor anywhere I want on the first line, but if I change the address to anywhere on the second line, the LCD ignores any characters I send. I believe I have the Function_Set correct (0x28), but I get nothing on the second line.

 

I have seen this unit display characters on the second line, and a similar 4-line LCD exhibits the same problems, so I don't believe this is a faulty unit.

 

Here's the low-level portion of the code, grafted and reworked from many places (including gatesphere's ShiftOut function ;) ).

---=== This is NOT ready for prime-time. Many comments are missing, or display original values, since changed. ===---

//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------
#include "globals.h"  
#include "msp430g2211.h"

// Watchdog Timer counter for delay function
int WDTrounds;

#pragma vector = WDT_VECTOR
__interrupt void vTimerWDT_IntMode(void)
{
   WDTrounds--;
}



void vWait( int uibaseinterval,  char ucrounds)
{
   WDTrounds = ucrounds;
     WDTCTL = uibaseinterval;                // set watchdog timer interval time
     IE1 |= WDTIE;                           // enable watchdog timer interrupt, 1

     while(WDTrounds > 0);                      // wait until number of rounds complete

     WDTCTL = WDTPW + WDTHOLD;               // Stop WDT
     IE1 &= ~WDTIE;                          // disable watchdog timer interrupt, 0
     return;
}                                         

//-----------------------------------------------------------------------------
// Module:   LCD_init
// Description: Initialization of LCD module
//-----------------------------------------------------------------------------
void LCD_init(void)
{
   //SOFTWARE RESET FOR 4-BIT MODE
   //shiftOut(0xFF);
   vWait(WDT_MDLY_8, 5);                // 40ms delay

   shiftOut(0x38);                        // send 0x13 1 of 3 for init (enable pin high)
   shiftOut(0x30);                        // same data, enable pin low
   vWait(WDT_MDLY_0_5, 20);            // 10ms delay
   shiftOut(0x38);                        // send 0x13 2 of 3 for init (enable pin high)
   shiftOut(0x30);                        // same data, enable pin low
   vWait(WDT_MDLY_0_5, 2);                // 1ms delay
   shiftOut(0x38);                        // send 0x13 3 of 3 for init (enable pin high)
   shiftOut(0x30);                        // same data, enable pin low
   vWait(WDT_MDLY_0_5, 2);                // 1ms delay
   shiftOut(0x28);                        // send 0x13 3 of 3 for init (enable pin high)
   shiftOut(0x20);                        // same data, enable pin low
   vWait(WDT_MDLY_0_5, 2);                // 1ms delay

   //END RESET

   //CONFIGURE LCD
   LCD_inst(FUNCTION_SET);                //4-bit, 2 line (or 4-line), 5x7 dots
   vWait(WDT_MDLY_0_5, 4);                // 2ms delay    
   LCD_inst(CURSOR_OFF);                //Display ON cursor OFF
   vWait(WDT_MDLY_0_5, 4);                // 2ms delay
   LCD_inst(0x20);                        //Set entry mode (Auto increment)
   vWait(WDT_MDLY_0_5, 4);                // 2ms delay
   //LCD_inst(RETURN_HOME);                 //Bring cursor to line 1, char 1
   //END CONFIGURATION

}

//-----------------------------------------------------------------------------
// Module:   LCD_via_595
// Description: Send instruction to LCD module through 74HC595 shift register
//-----------------------------------------------------------------------------

void LCD_via_595( char data,  char lcd_reg)
{
   //separate data (byte) into upper and lower 4-bits, move data to upper 4 bits, and add the control bits
    char l_byte = ((data << 4) | lcd_reg) ;
    char h_byte = ((data & 0xF0) | lcd_reg);
    char l_byteE = l_byte | LCD_E;
    char h_byteE = h_byte | LCD_E;

   //send h_byte
   //__delay_cycles(1000000);
   shiftOut(h_byteE);     //bring enable pin high also
   vWait(WDT_MDLY_0_5, 4);        // 2ms delay
   shiftOut(h_byte);            //enable low to trigger read
   vWait(WDT_MDLY_0_5, 4);        // 2ms delay

   //send l_byte
   shiftOut(l_byteE);
   vWait(WDT_MDLY_0_5, 4);        // 2ms delay
   shiftOut(l_byte);    
   vWait(WDT_MDLY_0_5, 4);        // 2ms delay    
}


void shiftOut( char data) {
   volatile int i;
   char temp, temp2;
   #ifdef MSBFIRST // reverse order of the bits
       for (i = 0; i < 8; i++) {
           temp = (0x01 & data);
           data = data >> 1;
           temp2 = (temp2 << 1) + temp;
       }
   data = temp2;
   #endif
   for (i = 0; i < 8; i++) {         // 8 bits to a char
       char bittowrite = (0x01 & data); // get bit
       data = data >> 1; // shift data left
       if (bittowrite == 1) {  // send bit
           P1OUT |= DATAPIN;
       } else {
           P1OUT &= ~DATAPIN;
       }
       // pulse clockpin
       P1DIR |= CLOCKPIN;
       P1DIR &= ~CLOCKPIN;
   }
   P1OUT &= ~LATCHPIN;           // take latch low
   P1OUT |= LATCHPIN;            // take latch high (enable output)
   //vWait(WDT_MDLY_8, 10);                // 40ms delay
}

//-----------------------------------------------------------------------------
// Module:   LCD_inst
// Description: Send instruction to LCD module
//-----------------------------------------------------------------------------

void LCD_inst( char data){
    char LCDreg = 0x00;
   LCD_via_595(data, LCDreg);
}



//-----------------------------------------------------------------------------
// Module:   LCD_data
// Description: Display value
//-----------------------------------------------------------------------------
void LCD_data( char data)
{
    char LCDreg = LCD_RS;
   LCD_via_595(data, LCDreg);
}

//-----------------------------------------------------------------------------
// Module:   LCD_char
// Description: Display single character
//-----------------------------------------------------------------------------
void LCD_char( char data)
{
    char LCDreg = LCD_RS;

   LCD_via_595(data, LCDreg);
}

//-----------------------------------------------------------------------------
// Module:   LCD_string
// Description: Send character string to LCD module
//-----------------------------------------------------------------------------
void LCD_string(char const *point)
{
 while(point[0])
 {
     LCD_data(*point);
     *point++;
 }
}

//-----------------------------------------------------------------------------
// Module:   LCD_cursor
// Description: Set display cursor
//-----------------------------------------------------------------------------
//NEED CURSOR POSITIONING INFO 

void LCD_cursor( char position)
{
   position = position + 0x80;
   LCD_inst(position);
}

//-----------------------------------------------------------------------------
// Module:  LCD_write_CGRAM
// Description: Write user defined character pattern to CGRAM
//-----------------------------------------------------------------------------
void LCD_write_CGRAM( char CGR_addr, char const *CGR_data)
{
 char i;
for(i=0; i<8; i++)
 {
   LCD_inst((CGR_addr<<3)|0x40|i);
   LCD_data(*CGR_data);
   *CGR_data++;
 }
}

//-----------------------------------------------------------------------------
// Module:   LCD_init_CGRAM
// Description: Initialization the user defined character pattern
//              (copy from Flash to CGRAM)
//-----------------------------------------------------------------------------
void LCD_init_CGRAM(void)
{
 LCD_write_CGRAM(0x00,&cg_ram00[0]);        // write display character RAM, address 0x00
 LCD_write_CGRAM(0x01,&cg_ram01[0]);        // write display character RAM, address 0x01     
}


/* ============================================================================
//  Function name   : void LCD_disp_allpixels
//
//  Handing-over    : void
//
//  Return          : void
//
//  Description     : displays the character which represents all pixels (0xff)
// ============================================================================
// VERSION      = 1.0
// ==========================================================================*/
void LCD_disp_allpixels (void)
{
  char uccounter;
 uccounter = 0;
 LCD_cursor(0x00);            // set cursor position to start of first line
 while (uccounter < 16)
 {
   LCD_char(0xff);
   uccounter++;
 }

 uccounter = 0;
 LCD_cursor(0x28);            // set cursor position to start of second line
 while (uccounter < 16)
 {
   LCD_char(0xff);
   uccounter++;
 }
}

 

Here's the circuit. The pins on the MSP430 are shown wrong for the code; they should be moved up three pins each, so that they are using pins 2-4 (P1.0-P1.2):

post-73-135135492133_thumb.png

 

Any help, thoughts on additional tests, or additional research sources would be greatly appreciated! :D

 

-Doc

Share this post


Link to post
Share on other sites

Hmm...

looking at the schematic, it looks like you have pin 9 on the '595 (Marked QH* on the symbol) connected to the LCD. Pin 9 on a '595 is for serial pass-thru, so you can daisy-chain them together making a 3-to-n (where n%8 == 0) shift register. You probably don't want that. What that will do is mirror out every data bit as it is sent to the '595 onto that pin. So, if you send 0b00101100, pin 9 will toggle to be 00101100. You'll want to attach that LCD pin to one of the other QA-QH pins (pina 1-7, 15 on the '595) and leave pin 9 unconnected.

 

Aside from that, I see no problems with your code or setup (after a cursory glance, that is... I'm sick at the moment and I've been up too long to really think about it, I'll check it again tomorrow). But for values to a shiftOut type function... when in doubt, send out binary values. This lets you see exactly which pins will be set as HIGH and which as LOW. In C, this is accomplished by prefixing the number with 0b. So, in this example, 0x28 would be 0b00101000... which by the looks of your schematic, would be sending out HIGH to pins QC and QE, tied to the LCD at RS and DB4.

 

One thing to try is putting in a #define MSBFIRST somewhere in there. See if you're sending out bits in the wrong order (which it is very likely to do... again, I'm tired, but it's a possiblity).

 

Anyways, hope this helps, Doc! Get back to us with your results!

Share this post


Link to post
Share on other sites
Hmm...

looking at the schematic, it looks like you have pin 9 on the '595 (Marked QH* on the symbol) connected to the LCD. Pin 9 on a '595 is for serial pass-thru, so you can daisy-chain them together making a 3-to-n (where n%8 == 0) shift register.

:oops: Ummm... that's an oops trying to clean up the schematic for the post too quickly. It is, in fact, connected correctly.

 

Let's just go with the right schematic...

post-73-135135492144_thumb.png

 

... I'm sick at the moment and I've been up too long to really think about it, I'll check it again tomorrow

Sorry to hear you're ill. I had some oral surgery that had me down for a week or so. Still can't open my jaw wide enough to fit a decent bite of meat!

 

But for values to a shiftOut type function... when in doubt, send out binary values...

...One thing to try is putting in a #define MSBFIRST somewhere in there. See if you're sending out bits in the wrong order (which it is very likely to do...

Yes, sending binary may help me visualize what I'm doing wrong. I do have the #define MSBFIRST in in the globals.h, and I'm sure the bits are going in the correct order because I can put whatever I want on the first line. I think I am somehow initializing the controller for a 1-line LCD, though I explicitly set that bit for a 2-line.

 

Thanks for the once-over. Get better soon.

 

-Doc

Share this post


Link to post
Share on other sites
:oops: Ummm... that's an oops trying to clean up the schematic for the post too quickly. It is, in fact, connected correctly.

Ah, gotcha. Good.

 

Sorry to hear you're ill. I had some oral surgery that had me down for a week or so. Still can't open my jaw wide enough to fit a decent bite of meat!

Thanks for the thought, and ouch! Hope your mouth feels better soon!

 

Yes, sending binary may help me visualize what I'm doing wrong. I do have the #define MSBFIRST in in the globals.h, and I'm sure the bits are going in the correct order because I can put whatever I want on the first line. I think I am somehow initializing the controller for a 1-line LCD, though I explicitly set that bit for a 2-line.

Looking at your code and the datasheet (60 pages, yeesh), you do have the right value being sent during initialization. One thing I might suggest is taking out the 595, just for now, to see if you can get it to work directly in 4-wire mode. Again, binary values help there. And since you'd be using P1 directly, you could just set P1OUT to whatever value you needed. One thing I noticed in the sheet and didn't quite find in your code (probably my bad, I'm terrible at reading other people's code) is that the datasheet said that you can simply shift the cursor beyond the 40th character to move it to the beginning of the second line (and any subsequent lines). Maybe something to try?

 

Thanks for the once-over. Get better soon.

Any time. And the same goes for you!

 

EDIT: Also, interesting wait function. How accurate is it? It might be nice to include in MSPhere... :P

Share this post


Link to post
Share on other sites
EDIT: Also, interesting wait function. How accurate is it? It might be nice to include in MSPhere... :P

I actually adapted that from two or three that I found. It is pretty cool in that you can give it any WDT setting and a number of iterations, so you can get about any delay you want.

 

It does seem pretty accurate. I had some timing problems at the beginning (would only initiate every other run), but tweaking the timings according to the sheet cleared that up.

 

I had thought about removing the 595 from the equation, but that seemed like a complete rewrite (and a waste of 3 more pins). I guess I may be down to that now, though.

 

Thanks,

 

-Doc

Share this post


Link to post
Share on other sites
I had thought about removing the 595 from the equation, but that seemed like a complete rewrite (and a waste of 3 more pins). I guess I may be down to that now, though.

 

Well, as I said, just for now. You'd be surprised what good a rewrite will do. Whenever I rewrite my code (often... I'm a Comp. Sci. major, so I have to) it ALWAYS ends up being more efficient, and most of the time, things that didn't work before, now work better than the way I expected them to. But basically, start small (less complicated, no 595) and then grow from there. It's hard to tackle the whole project at once, so break it up!

Share this post


Link to post
Share on other sites
Well, as I said, just for now. You'd be surprised what good a rewrite will do. Whenever I rewrite my code (often... I'm a Comp. Sci. major, so I have to) it ALWAYS ends up being more efficient, and most of the time, things that didn't work before, now work better than the way I expected them to. But basically, start small (less complicated, no 595) and then grow from there. It's hard to tackle the whole project at once, so break it up!

I feel your pain; I just got my BSCS. Lots of code. Lots of rewrites. What are you working on?

 

I'm going to rework the circuit and code. I knew as soon as you suggested it that I would have to. Thanks.

 

-Doc

Share this post


Link to post
Share on other sites
I feel your pain; I just got my BSCS. Lots of code. Lots of rewrites. What are you working on?

Nothing at the moment, aside from an honors thesis I'm doing on Algorithmic Composition. Oh, and some basic AI stuff in Lisp. Last semester though I almost went bald over coding a b-tree delete... :P 47k+ words in a file, load them in, fine. Sort into a b-tree, fine. Make it persistent, still fine. Make it delete a word while maintaining the tree's balance... not so fine. Took me around a month and a half of constant rewrites. :P

 

I'm going to rework the circuit and code. I knew as soon as you suggested it that I would have to. Thanks.

Best of luck! It happens, but ah well. It always seems to be for the best, to me.

Share this post


Link to post
Share on other sites

Two options for debugging, and they can be done simultaneously.

 

First, throw some leds between the shift register and the lcd. Don't know how much current can be sourced from the register, but all you need is enough to light them for debugging purposes. This allows you to ensure every control nibble you push is what it should be.

 

Second, you could tie the register's latch to its clock, and drive the lcd's enable pin directly. More control, same number of lines.

 

Since the 2 line setup comes relatively quick into using the lcd, and the first five you should be able to verify quickly.

 

Additionally, can you place the cursor on ANY line 2 ram address? Try a loop moving it to every position(And write a character) from 0 to 255.

According to http://www.spikenzielabs.com/SpikenzieL ... ow_To.html (and to a page with alot more detailed DDRAM address configurations that I can no longer find since I saw it like 2 weeks ago, errr http://ouwehand.net/~peter/lcd/lcd0.sht ... e_displays), the address for the second row of a 2x16 is at 0xC0. Not 0x40. Try that.

Share this post


Link to post
Share on other sites
First, throw some leds between the shift register and the lcd.

That's a brilliant idea! I know this because I did it pretty much from the start :mrgreen: (sorry, forgot to mention that in the OP). I checked (wrote down) every nibble that went through (I slowed the timing so I could) and hand checked it. Each appeared to be correct. At least, it was sending what I intended to send...

 

Try a loop moving it to every position(And write a character) from 0 to 255.

I did try a loop, but I'm not sure I went far enough to catch 0xC0. I'll have to try that.

 

At this point, I am working on gatesphere's advice and simplifying by directly driving the LCD. There still aren't enough pins to use the 8-bit interface (unless the Xtal pins can be repurposed as outputs, but I don't know how), so it'll be 4-bit. Still, as gatesphere pointed out, simplicity drives elegance, and I can see that, once I move back to the 595, the code will be much cleaner.

 

Thanks very much for the ideas, and the links! :D

 

-Doc

Share this post


Link to post
Share on other sites
unless the Xtal pins can be repurposed as outputs, but I don't know how

This is quite easy, if you don't have the crystal soldered on. These two pins are P2.6 and P2.7, and to enable them as GPIO pins, you simply use something like...

P2SEL |= BIT6 + BIT7;

This register (the PxSEL register) tells the MSP430 which function to use the pin for. P1 is set by default to be GPIO, and P2 is set by default to be for peripheral use. Setting the appropriate bits to 1 in the PxSEL registers tells the MSP430 to use the secondary mode, which for P2 is as a GPIO pin.

 

Hope this helps!

 

Oh, and cde... great idea with the LEDs. I never would have thought of that!

Share this post


Link to post
Share on other sites
This is quite easy, if you don't have the crystal soldered on. These two pins are P2.6 and P2.7, and to enable them as GPIO pins, you simply use something like...

P2SEL |= BIT6 + BIT7;

This register (the PxSEL register) tells the MSP430 which function to use the pin for. P1 is set by default to be GPIO, and P2 is set by default to be for peripheral use. Setting the appropriate bits to 1 in the PxSEL registers tells the MSP430 to use the secondary mode, which for P2 is as a GPIO pin.

 

Wow, that is easy! I'm going to stick with the 4-bit I'm using now, but I'm going to keep that in mind for future needs!

Share this post


Link to post
Share on other sites

actually i think it is the other way around, to use the XIN XOUT as IOs, i have to set them to 0s

i.e.

P2SEL &= ~(BIT6|BIT7);

 

it is more consistent this way, all P1 pins have P1SEL bit low when they are digital IOs.

 

i checked the datasheet p.43 and p.44 to confirm this, the tables also implies that u cannot use P2.7 as IO when P2.6 is timer output.

 

i observed this as i always sets P2SEL=0x00; to make them available as IO.

 

i would guess that at POR value of P2SEL is set to BIT6+BIT7 (aux function, but default setting) and u have to reset them (turn off) to enable IO.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...