LCD 16x2 command sequence issue - Page 1
Forum section: electronics / general

author: DTJ
author_rank: Super Contributor
timestamp: November 14, 2014, 08:10:11 am
content: Hi I've got a problem talking to a Winstar 1602 LCD that's beating me.My string write function works ok.The cursor location code works - I can change the command value and place the cursor anywhere I want.The cursor home code works.However if I move the cursor to a position just prior to the string write it crashes - just leaves cursor flashing, no string written.Poking around with the debugger and CRO it looks like the LCD gets stuck in the busy mode when trying to write the string after the cursor position command - hence the flashing cursor left on the screen.Why does the cursor position command cause this to happen? Am I misusing it?I need to be able to position the cursor and then write a string so I can write to line 2 of the display.Any advice welcome!This Code WORKS - the string is written to line 1 of LCD=======================================================//set cursor to line position 1DATA_PORT = 0x80; //Send 0x80 | 0x00 = 0x80 sets cursor to line 1 pos 0x00RS_PIN=0; //sending cmdRW_PIN=0; //write to lcdStrobeE_PIN(); //load command into LCD// cursor homeDATA_PORT = 0b00000011; //Send 0x03 sets cursor to homeRS_PIN=0; //sending cmdRW_PIN=0; //write to lcdStrobeE_PIN(); //load command into LCDLCD_string(str1_splash); //write string ==> LCDThis Code DOES NOT WORK - the cursor just blinks at home positionThe order of cursor home & cursor to line 1 position 1 code sections are swapped.===============================================================// cursor home as inline codeDATA_PORT = 0b00000011; //Send 0x03 sets cursor to homeRS_PIN=0; //sending cmdRW_PIN=0; //write to lcdStrobeE_PIN(); //load command into LCD//set cursor to line position 1DATA_PORT = 0x80; //Send 0x80 | 0x00 = 0x80 sets cursor to line 1 pos 0x00RS_PIN=0; //sending cmdRW_PIN=0; //write to lcdStrobeE_PIN(); //load command into LCDLCD_string(str1_splash); //write string ==> LCD

author: DTJ
author_rank: Super Contributor
timestamp: November 14, 2014, 11:39:12 am
content: I just discovered something else...If I write a single ASCII character to the LCD >>AFTER<< the "set cursor to line code", then the next "string write" works ok.So writing a single character to the LCD seems to correct or reset the condition that the "set cursor to line code" is creating.The single character write is the same function called by the string write code.Apologies in advance for the grungie code....String write code=============void LCD_string(char *buffer){while(*buffer) // Write data to LCD up to null{while(LCD_busy()); // Wait while LCD is busyLCD_char(*buffer); // Write character to LCDbuffer++; // Increment buffer}return;}Single Character write code===================void LCD_char(char char2LCD){RS_PIN=1; //sending dataRW_PIN=0; //write to lcdDATA_PORT = char2LCD; //character = LCD data portStrobeE_PIN();RS_PIN=0; //restore RS_PIN to low state} // end of LCD_char()

author: dannyf
author_rank: Super Contributor
timestamp: November 14, 2014, 11:47:35 am
content: I would suggest that you read the datasheet and compare it, both in terms of data sent and timing, vs. your code.The issues are fairly obvious.

author: mikerj
author_rank: Super Contributor
timestamp: November 14, 2014, 11:52:38 am
content: Where is the code for either reading back the busy flag before (or after) and operation, or alternatively the delays to wait for the maximum specified time for any given operation?

author: DTJ
author_rank: Super Contributor
timestamp: November 14, 2014, 12:03:45 pm
content: Hi Mikerj - when I write a character I don't check for busy flag - it works ok without delays at the moment.Hi Dannyf - I'm obviously having problems spotting the obvious. Any clues?Thanks guys.Single character write code;====================void LCD_char(char char2LCD){RS_PIN=1; //sending dataRW_PIN=0; //write to lcdDATA_PORT = char2LCD; //character = LCD data portStrobeE_PIN();RS_PIN=0; //restore RS_PIN to low state} // end of LCD_char()When writing the string, the busy flag is checked prior to writing to the lcd;=====================================================void LCD_string(char *buffer){while(*buffer) // Write data to LCD up to null{while(LCD_busy()); // Wait while LCD is busyLCD_char(*buffer);buffer++; // Increment buffer}return;}This is my busy flag code;==================unsigned char LCD_busy(void){RW_PIN = 1; // Set the control bits for readRS_PIN = 0;DelayFor18TCY();E_PIN = 1; // Clock in the commandDelayFor18TCY();// 8-bit interfaceif(DATA_PORT&0x80) // Read bit 7 (busy bit){ // If highE_PIN = 0; // Reset clock lineRW_PIN = 0; // Reset control linereturn 1; // Return TRUE}else // Bit 7 low{E_PIN = 0; // Reset clock lineRW_PIN = 0; // Reset control linereturn 0; // Return FALSE}}

author: dannyf
author_rank: Super Contributor
timestamp: November 14, 2014, 12:20:54 pm
content: Easy: take out the datasheet, go to the section about commands / instructions and look up the ones you are using. Compare them vs. your code.What differences do you see?

author: DJohn
author_rank: Regular Contributor
timestamp: November 14, 2014, 12:51:03 pm
content: You don't say what processor you're using, or how fast it runs. It's possible that the code is running slowly enough that the faster commands complete before you send the next one.Home is not one of the faster commands. You must either delay for the recommended time (1.something ms) or (much better) poll the busy signal until it is finished. If you try sending another command before it has finished, anything can happen. It might work. It might ignore you. It might catch fire (it won't, but you wouldn't get any sympathy if it did).These displays don't like short-cuts. Do what the datasheet says, to the letter, and it'll work fine.

author: DTJ
author_rank: Super Contributor
timestamp: November 14, 2014, 12:59:33 pm
content: Hi Danny - thanks for the reply. The commands seem ok? I've compared them below.Thanks DJohn, yes home cursor is 1.53ms. I'll have another look at the code in the morning & see if I'm catering for this.I'm using a PIC18F25K22 @ 16MHz via the internal clock and learning C in the process.....For set cursor to line 1, first position;==========================Winstar data sheet:To position cursor;Set DDRAM Address; Set DDRAM address in address counter; 1 AC6 AC5 AC4 AC3 AC2 AC1 AC0For the first position, line 1 AC6....AC0 all are 0.39?s execution timeCommand = 1000 0000 = 0x80My command:DATA_PORT = 0x80; //Send 0x80 | 0x00 = 0x80 sets cursor to line 1 pos 0x00For cursor home============Winstar data sheet:Return Home; Set DDRAM address to “00H” from AC and return cursor to its original position if shifted. Thecontents of DDRAM are not changed.1.53ms execution time execution time0 0 0 0 0 0 0 0 1 xMy command:DATA_PORT = 0b00000011; //Send 0x03 sets cursor to home

author: dannyf
author_rank: Super Contributor
timestamp: November 14, 2014, 02:33:31 pm
content: 1. The LCD is non response while in software reset under command 0x01. You have to wait for the execution to finish.2. To relocate the sensor, or the address with 0x80 and send it as command to the LCD. Rows are represented as different starting addresses.All from memory. Hope it helps.

author: mikerj
author_rank: Super Contributor
timestamp: November 14, 2014, 07:09:58 pm
content: Quote from: DTJ on November 14, 2014, 12:03:45 pmClearly it does not!Unless your compiler is producing horrendously inefficient code, or you have an extremely low clock speed on your micro then you will either need delays or (much better) to poll the busy flag to ensure one operation completes before you send the next.

author: SirNick
author_rank: Frequent Contributor
timestamp: November 14, 2014, 08:02:58 pm
content: If you have access to a logic analyzer, capture the pin behavior while running your code. Compare that with the timing diagrams in the data sheet. Flipping pins as fast as the code can run is rarely wise unless you know for certain your micro's clock is the bottleneck.If you DON'T have access to a logic analyzer, go buy one. They are invaluable for solving problems like this.

author: DTJ
author_rank: Super Contributor
timestamp: November 15, 2014, 11:35:54 am
content: SOLVED!I found the problem.The LCD would stop responding after I sent it the 0x80 command to set the cursor to the first position. The LCD was then apparently stuck in the busy state.What was happening was the LCD busy check code was faulty. I grabbed it from a Microchip example. The code did not flip the LCD data port direction from output to input in order to test the BUSY signal on D7.For most commands D7 is not 1 so the busy check would return as "not busy". However when sending the 0x80 (1000 0000) command the D7 bit was set. This was read as the busy bit from the LCD and hence locked the code in a loop thinking the LCD was busy.Adding a couple of lines to flip the LCD data port direction inside the busy check function fixed the problem.Thanks for you help guys - I was about to give up and needed a push to go back and have a closer look.SirNick, +100 on the logic analyser. They are a big help, beats dicking around with the 2 channel CRO trying to work out whats going on.Cheers.