Loading...
Pages: 1 [2] 3   Go Down
Author Topic: Reading from HD44780-based LCDs  (Read 1287 times)
0 Members and 1 Guest are viewing this topic.
The Wired 2.0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 60
Destroy the core!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I seem to remember seeing this somewhere....

Touche... I wrote the code in a rush from what I had in memory. It pretty much implements what we've spoken about.

I should assume I do not need to comment the enum or the struct as what they define should be evident.

Code:
#include <inttypes.h>   //needed for uint8_t datatype
#include "WProgram.h"    //needed for wiring extensions to C.
//some other include that aren't relevant to our issue.
// some interfacing code that has nothing to do with this: lcd_sendRaw()


uint8_t lcd_getRaw(LCD *l, uint8_t rs_mode){
      digitalWrite(l->rs_pin, rs_mode);    // registry select
      digitalWrite(l->rw_pin, HIGH);    //set read mode

      uint8_t retVal = 0;    // initializes return variable

      for (int i = 0; i < l->bitmode; i++){    //if bitmode is 4, it will loop until i < 4, if bitmode is 8, it will loop until i<8
            // set pin to input mode
            pinMode(l->datapin[i], INPUT);
            // reads pin, shift value to proper place and add it to retVal
            retVal |= (digitalRead(l->datapin[i]) << i);
            set pin to output mode
            pinMode(l->datapin[i], OUTPUT);
      }

      if (l->bitmode == BIT_MODE_4){
            // LCD is in 4 bit mode, only one nibble was obtained
            // shift nibble to upper end of byte and loop to obtain low
            // end of byte
            retVal <<= 4; //obtained high nibble, must obtain low.

            // trigger the strobe to signal for the next nibble
            digitalWrite(l->enable_pin, HIGH);
            digitalWrite(l->enable_pin, LOW);

            // same loop as above, only this time we know the upper limit
            for (int i = 0; i < 4; i++){
                  pinMode(l->datapin[i], INPUT);
                  retVal |= (digitalRead(l->datapin[i]) << i);
                  pinMode(l->datapin[i], OUTPUT);
            }
      }

      // end of operation, trigger the strobe
      digitalWrite(l->enable_pin, HIGH);
      digitalWrite(l->enable_pin, LOW);

      // return the complete value
      return retVal;
}

Evaluating bitwise OR:
As per Boolean Algebra:
0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1
C/C++ Operator for this operation is |

Bitshifts:
Bitshifting is an operation done to, of course, bits. As the name implies, all that is done is shift to the right or left. A left bitshift by n is equivalent to multiplying by 2^n. A right bitshift by n is equivalent to dividing by 2^n.
Examples:
(bitshift two one to the left) dec2 << dec1 = bin10 << dec1 = bin100 = 4
(bitshift two one to the right) dec2 >> dec1 = bin10 >> dec1 = bin1 = 1 (the zero gets dropped)

Algorithm to obtain raw data
We can store all data that is read from the LCD in one byte (8 bits). This will be stored in an 8 bit integer (same as a char type). For ease of access, we use an 8bit unsigned integer as defined in <inttypes.h>. Unsigned 8 bit integers range from dec0 to dec255, or b00000000 to b11111111.

We loop to get data out of the LCD. The first loop is generic and gets executed no matter whether the LCD is in 8 bit mode or 4 bit mode. The process is simple:

1) Initialize the return value to 0.
2) Read the first pin (i = 0), this is the least significant digit. In 4 bit mode, this would be data pin 4 on the LCD board, in 8 bit mode, this would be data pin 0. The output is either a HIGH (1) or LOW (0). Let the reading be the bit [D0]. It gets bitshifted by zero places to the left. The operation performed will be:
0 | [D0] = [D0]
which is stored back in the return value.
3) Read the second pin (i = 1). Let the output of this be the bit [D1]. It gets bitshifted one to the left and OR'ed to our return value:
[D1] << 1 = [D1]0
[D0] | [D1]0 = [D1][D0]

4) This process gets repeated until the upper limit is reached. If the display was operating on 8 bit mode, we end up with the full byte by the end of the loop, and it is returned. If the display is in 4 bit mode, we only have one nibble [D3][D2][D1][D0], the if clause executes.

In case of 4 bit mode
5) Bitshift retVal 4 places to the left. This puts the nibble as the upper end of the byte.
[D3][D2][D1][D0] << 4 = [D3][D2][D1][D0]0000
6) Trigger the strobe (This is what this entire argument has been about, and what I am not sure that should be the proper procedure).
7) Repeat process above to get the second nibble. By the end of the loop, the lower end of the byte will be completed and retVal can be returned.

Using returned data
The data returned will have the form [B7][B6][B5][B4][B3][B2][B1][B0]. What the data represents depends on value fed to register select (rs_pin). If rs_pin is given a LOW, then [B7] is the busy flag status and [B6]-[B0] will contain the Address Counter. If rs_pin is given a HIGH, then the return value will be data from DDRAM or CGRAM.

To filter out the busy flag, all that needs to be done is bitshift the return value 7 places to the right, which leaves [B7] by itself. To filter out the Address Counter a bitwise AND can be performed with 127dec (01111111), leaving [B6]-[B0].

Observations
This is obviously a backend to be used by other functions which will filter the data for the end user. At some point in time in the execution, the LCD's output gets corrupted, some things disappear.

A question if I may, and please take no offence on it, was my code that obfuscated that it needed documentation? It is not my intention to write unreadable code. I consider obfuscated code to be bad code.
« Last Edit: September 25, 2009, 01:17:17 am by TRON007 » Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 17
Posts: 3465
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Orlando:

Now I have something I can work with.  I don't do much C programming but I am pretty sure I can follow what you have given me.  

So far I have not really studied your code but from what I have said before it appears that somewhere when you think you are  'reading' from the LCD you are actually 'writing' to it.  For that reason it may be beneficial for me to look at lcd_sendRaw() so that I can compare it to lcd_getRaw().


Quote
please take no offence on it
If you can put up with my barbs I certainly can deal with yours...

Quote
was my code that obfuscated that it needed documentation?
It's no worse than most other C code that I try to read.  Some programmers seem to believe that C is 'self commenting' which is a real joke.  Let them try to figure out a program that someone else wrote or one that themselves they wrote ten or fifteen years ago and then see what they say about comments.

Check out the 'Comments on Commenting' at http://www.ganssle.com/articles/Comments.htm.  In the 'Commenting Suggestions' at the end he states "My standard for commenting is that someone versed in the functionality of the product - but not the software - should be able to follow the program flow by reading the comments without reference to the code itself."

This agrees with my criteria which maintains that the comments should almost always be language independent.  You should be able to use the same comments in an assembly language program as in a C program (and in a flowchart as well).

Don

p.s. I find the term 'obfuscated' to be about as obfuscated as they come.
Logged

The Wired 2.0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 60
Destroy the core!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is lcd_sendRaw(). The algorithm is pretty much the same concept, except that instead of reading from the LCD, we write to the LCD.

Code:
void lcd_sendRaw(LCD *l, uint8_t val, uint8_t rs_mode){
      digitalWrite(l->rs_pin, rs_mode);
      digitalWrite(l->rw_pin, LOW);    // write to LCD

      if (l->bitmode == BIT_MODE_4){
            for (int i = 0; i<4; i++)
                  digitalWrite(l->datapin[i], (val >> (i + 4)) & 0x01);

            // trigger strobe
            digitalWrite(l->enable_pin, HIGH);
            digitalWrite(l->enable_pin, LOW);
      }

      for (int i = 0; i < l->bitmode; i++)
            digitalWrite(l->datapin[i], (val >> i) & 0x01);

      // trigger strobe
      digitalWrite(l->enable_pin, HIGH);
      digitalWrite(l->enable_pin, LOW);
}

The data to be written is stored in an unsigned 8 bit integer (char type). For ease of reading, we use an unsigned 8 bit integer as defined in <inttypes.h>. The data has the form [B7][B6][B5][B4][B3][B2][B1][B0].

If the LCD is in 8 bit mode, the second loop is the only one that executes. The operation in question is extracting the least significant bit not sent. This is done by bitshifting the data to the right n places and bitwise anding with dec1 (b00000001).

Assuming the LCD is in 8 bit mode, the following is executed:
i=0) [B7][B6][B5][B4][B3][B2][B1][B0] >> 0 = [B7][B6][B5][B4][B3][B2][B1][B0]
[B7][B6][B5][B4][B3][B2][B1][B0] & 0x01 = [B0]
[B0] is then written to datapin[0]
i=1) [B7][B6][B5][B4][B3][B2][B1][B0] >> 1 = [B7][B6][B5][B4][B3][B2][B1]
[B7][B6][B5][B4][B3][B2][B1] & 0x01 = [B1]
[B1] is then written to datapin[1]
and so on, until all data is written.

If the LCD is in 4 bit mode, the byte must be sent in nibbles. The second loop is generic and can send the low end of the nibble (loops from i = 0 to 3, yielding B0 to B3). The first loop is executed before in this case.
i=0) [B7][B6][B5][B4][B3][B2][B1][B0] >> (0 + 4) = [B7][B6][B5][B4]
[B7][B6][B5][B4] & 0x01 = [B4]
[B4] is then written to the LCD.
i=1) [B7][B6][B5][B4][B3][B2][B1][B0] >> (0 + 1) = [B7][B6][B5]
[B7][B6][B5] & 0x01 = [B5]
and so on until i = 3, where all data has been sent.

This should be the same algorithm that is used in the current LiquidCrystal library that comes with the Arduino IDE, only the way it is implemented there is too bloated for my taste in programming.

Thanks and cheers, Orlando.

Edit: I just committed the entire code I have for this library to my subversion repo in google code. The code is available here. It is all GPLv3. Since the lcd_getRaw() function is suspected to be broken, the lcd_getState() function is not made to use the lcd_getRaw() function.
« Last Edit: September 25, 2009, 01:32:39 pm by TRON007 » Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 17
Posts: 3465
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Orlando:

You have finally provided enough information for me to make some more educated guesses.  From the information in your last two posts it appears to me that you do indeed know the signal flow involved in reading and writing in both 4 and 8 bit modes.  Since I am not much more than a rookie C programmer I will assume that your C code is correct as well.  I still stand by my analysis that in order for the display to become corrupted the DDRAM has to be getting messed up somehow.

What is missing from your code, as far as I can tell, is proper implementation of at least some of the timing requirements.  For example look at the Enable pulse width which must be at least 150 nS wide according to some datasheets but must be as long as 450 nS on others.  In assembly language (and maybe in very efficient C) pulsing the enable pin high then low can be done with two 2-cycle instructions which takes 270 nS at 16 MHz.  Therefore this may work all of the time on some displays, some of the time on all displays, etc. but not necessarily all of the time on all displays.

There are also some setup times, some hold times and also the Enable cycle time that should be considered.  I don't know the ramifications of not meeting some of these requirements.  I would suspect that the device would just not work, but in your case you could be right on the borderline with even more spetacular results.

I would start by liberally sprinkling your program with nice long time delays and see what happens.  If that doesn't work I have some more troubleshooting ideas in mind.

By the way, keep in mind that these timing problems just get worse as processor speeds increase and LCD controller speeds stay essentially the same.  To make your code more repairable in the future I recommend that for each time delay specified in the LCD datasheet you include at least a comment to that effect at the point in your program where that time delay would occur.

Don
Logged

The Wired 2.0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 60
Destroy the core!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The timing part I know is something I am missing. If you look over lcd_init() over at my subversion repo at Google Code, you will find that initialization is not done properly either. I planned to add this at some point, it looks however, that it may be the root of the issue. The lcd_sendCommand() and lcd_sendChar() functions however rely on reading the busy flag and waiting for the LCD to become available again before exiting.

Yeah, DDRAM must be getting corrupted somewhere. What I don't understand is how is something that was written to the LCD disappear and the rest of the stuff still be around. It's not like random characters came up or anything...
The LCD was supposed to show "Distance: ##cm", with ## being a two or three digit number. If distance was less than 25 cm, it would show "PROXIMITY WARNING" on the second line. Instead, it showed "##cm" and would display the warning whenever the case. Reverting the library to its previous state fixed the problem. In case you are wondering, the distance was obtained from a PING sensor.
I'll add proper timing and initialization to the library and we'll see what happens afterwards.
« Last Edit: September 25, 2009, 09:53:06 pm by TRON007 » Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 17
Posts: 3465
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Orlando:
Quote
subversion repo
Could you rephrase this in English?

Quote
you will find that initialization is not done properly either
I was going to hit you with that later - it's the first thing I look for when I check out LCD code.  Have you seen my information at http://web.alfredstate.edu/weimandn ?

Don

Logged

The Wired 2.0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 60
Destroy the core!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Subversion is a version control system. I use it to store code and track changes I make to it, that way I don't have to memorize what I did and I can revert to earlier versions, branch code, merge code and stuff like that effortlessly. It works with a centralized repository (often shortened as repo) which in my case I keep at Google Code. Mandatory Wikipedia entry.

Quote
Have you seen my information at http://web.alfredstate.edu/weimandn ?
I saw something similar to that in the HD44780 datasheet. The instructions on your site however are far more understandable than what I've been able to find. I thank you for that *bookmarks page*.

Working on adding proper timing and initialization now...
Cheers and thanks, Orlando.
« Last Edit: September 26, 2009, 01:10:37 am by TRON007 » Logged

The Wired 2.0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 60
Destroy the core!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

ATCH! POOPIE!!! Sorry for the double post...

Well, now I really broke things... I implemented initialization according to the provided specification (using proper timing and all):

Code:
void lcd_init(LCD *l){
      // initialize pinout
      for (int i = 0; i < ((l->bitmode == BIT_MODE_8) ? 8:4);i++)
            pinMode(l->datapin[i], OUTPUT);

      pinMode(l->rs_pin, OUTPUT);
      pinMode(l->rw_pin, OUTPUT);
      pinMode(l->enable_pin, OUTPUT);

      digitalWrite(l->enable_pin, LOW);

      //initialization
      if (l->bitmode == BIT_MODE_8){
            delay(20);                              //wait for more than 15ms
            lcd_sendRaw(l, 0x30, LOW);            //function set
            delay(7);                              //wait for more than 4.1ms
            lcd_sendRaw(l, 0x30, LOW);            //function set
            delay(1);                              //wait for more than 1us
            lcd_sendRaw(l, 0x30, LOW);            //function set
            delay(1);                              //wait for more than 1us
      } else {
            digitalWrite(l->rs_pin, LOW);
            delay(20);                              //wait for more than 15ms
            lcd_sendNibble(l, 0x3);            //function set
            delay(7);                              //wait for more than 4.1ms
            lcd_sendNibble(l, 0x3);            //function set
            delay(1);                              //wait for more than 1us
            lcd_sendNibble(l, 0x3);            //function set
            delay(1);                              //wait for more than 1us
            lcd_sendNibble(l, 0x2);            //initial flow function
            delay(1);
      }

      // debug purposes
      hs_init();
      hs_start(0, 9600);
      hs_writeStr(0, "LCD STARTUP COMPLETED.\r\n");

      // startup continued
      lcd_sendCommand(l, (l->bitmode == BIT_MODE_8) ? 0x38:0x28);
      lcd_sendCommand(l, BLANK_DISPLAY);
      lcd_clear(l);
    lcd_sendCommand(l, CURSOR_INVISIBLE);
    lcd_sendCommand(l, EM_INCREMENT_OFF);

}

And used the lcd_getRaw() function to obtain the busy flag, modifying the lenght of the enable pulse to match specifications as well (450us).
Code:
uint8_t lcd_getRaw(LCD *l, uint8_t rs_mode){
      digitalWrite(l->rs_pin, rs_mode);
      digitalWrite(l->rw_pin, HIGH);

      uint8_t retVal = 0;

      // Added for safekeeping. May not be needed.
      for (int i = 0; i< l->bitmode; i++)
            digitalWrite(l->datapin[i], LOW);

      for (int i = 0; i < l->bitmode; i++){
            pinMode(l->datapin[i], INPUT);
            retVal |= (digitalRead(l->datapin[i]) << i);
            pinMode(l->datapin[i], OUTPUT);
      }

      if (l->bitmode == BIT_MODE_4){
            retVal <<= 4;

            lcd_triggerStrobe(l);

            for (int i = 0; i < 4; i++){
                  pinMode(l->datapin[i], INPUT);
                  retVal |= (digitalRead(l->datapin[i]) << i);
                  pinMode(l->datapin[i], OUTPUT);
            }
      }

      lcd_triggerStrobe(l);
      return retVal;
}

void lcd_triggerStrobe(LCD *l){
      digitalWrite(l->enable_pin, HIGH);
      delayMicroseconds(1);
      digitalWrite(l->enable_pin, LOW);
}

And interestingly enough, the program did not start up! Nothing would show on the LCD screen. I thus decided to inject some of my serial port library onto this matter and send some debug information back to the PC to see what was going on, and the lcd_getRaw() function returns only one value: bin11111111!

I will upload all my code upon request. Something is seriously wrong here...

Looking thru the datasheet once more I found this (click for full page):


Which suggests that the algorithm to read from the LCD is indeed correct, which then means that the way I implemented it seems to be the wrong one. You were right to begin with, Don. Anybody wants to take a shot at what I did wrong? Thanks.
« Last Edit: September 26, 2009, 10:22:59 pm by TRON007 » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 3
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm not sure if you've taken a look at the implementation of the LiquidCrystal library shipped with the Arduino IDE but it works very well and has all of the timings correct, it might be worth taking a look at. It doesn't do any reading from the display but it has all of the correct timings.

It's part of the 017 release and is located in arduino-0017\hardware\libraries\LiquidCrystal\LiquidCrystal.cpp
« Last Edit: September 27, 2009, 05:21:23 am by RobH » Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 17
Posts: 3465
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Robert:

I am familiar with the library and it is the best one available at this time.  It has one minor discrepancy (variation from the datasheet) in the LCD initialization which I doubt will have any negative effect.  It indents on lines 3 and 4 of 16 x 4 displays because it does not account for the fact that the initial addresses for those lines differ from those on 16 x 20 displays.

Don
Logged

The Wired 2.0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 60
Destroy the core!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

When I started implementing my own library, I took a look at the original LiquidCrystal. I was disgusted with the way it was coded and the lack of documentation it had. The new LiquidCrystal library is as unreadable as it was before, it will take me a day or two to actually follow what that code does. I did see the timing sequence in the new LiquidCrystal library, I decided to give my timing sequence longer intervals during initialization just in case.

What worries me though is the fact that the lcd_getRaw() function always returns dec255 [bin11111111]. From what I saw on the datasheet, Don was correct in stating that triggering the strobe would yield the second nibble.

This can only point to one thing: My implementation has an error somewhere. The question is, where is that error at?
Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 17
Posts: 3465
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Orlando:

From my Reply #18
Quote
If that doesn't work I have some more troubleshooting ideas in mind.
I think it's time to start over.  Here's my suggestion for an approach - it really isn't as bad as it looks as long as you cut and paste alot.  

Start with an 8-bit setup which as you know is quite straightforward, and make sure you get everything you want to do later in the 4-bit mode working properly.  I would start with a program using just software delays (no reading from the LCD).  From that I would progress to one that uses the busy flag wherever possible, which is everywhere after the initialization.  Finally I would implement reading addresses and memory content which is what got us into this mess in the first place.

Next I would do this all over again for the 4-bit interface.  This is pretty straightforward for the time delay version.  The initialization has to change a bit as does the routine that puts info on the data lines.  Reading the busy flag comes next and you already have picked up on the fact that you have to pulse the Enable line twice each time you check and ignore the stuff that comes back from the second pulse.  Finally modify this to get the address info along with the busy flag and do another modification to get DDRAM info back.  These last two require reconstruction of the 8-bit data byte from the two 4-bit nibbles followed by some masking.

I would stop right there.   I can't see the reason for having one LCD program for both the 4-bit and the 8-bit interfaces when it isn't likely that any specific project would use both methods at the same time.   Separate libraries make more sense to me since each library becomes smaller, the code in each is easier to follow, and the code will probably run faster.  


Don
Logged

The Wired 2.0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 60
Destroy the core!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

A fine suggestion that is, my good friend. I'm starting the library over, with a different structure to boot as well. Luckily, I am on vacation this week, so I'll be able to spend some more time on this. As of now, I'm writing some pieces at a time, starting with 8 bit mode. I'll have to rewire my stuff, but the final product will be totally worth it. I just wish I had an oscilloscope to debug the signals though...

I'll let you know how this works out. Thanks for the help so far, Orlando.
Logged

The Wired 2.0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 60
Destroy the core!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, I am almost done implementing the 8bit side of operations from scratch. I am writing the 'check busy flag' code, although I am not making it use of it from other parts of the library/final program until I am sure the rest works ok.

The question is as follows, and this is perhaps where I screwed up the implementation last time. Consider the timing diagram below:



Noticing the first busy flag check, and the behaviour of the strobe (enable pin E) and data pin 7 (data bus DB7), if the timing in the diagram is correct, then DB7 becomes readable a few moments after E becomes high and keeps its state whilst E is high. I assume the timing can be obtained from this chart:



And the measured timing diagram is:


Am I correct in assuming that in order for me to read data, either being the busy flag, AC, or DDRAM, I must send a pulse to the enable pin after setting up R/~W to 1 and RS to the proper value? The way the last image shows, I must set up R/W~1 to 1, RS to what I want to read for, pulse the enable pin, wait at most 160 ns, then read the data. Is this then the proper procedure?

 Thanks, Orlando.
« Last Edit: September 28, 2009, 07:45:21 pm by TRON007 » Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 17
Posts: 3465
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Orlando:

Here's how I interpret Figure 15:

1.  It's impossible to tell whether RS is high or low from this diagram, but it says 'Instruction Write' at the bottom so I will assume that the programmer is sending a series of instructions to the LCD.  It really doesn't matter, this could just as well be character data going to the other register.

2.  The program has set up RS and the data lines, R/W~ is low, and then the first pulse of the Enable bit occurs.

3.  On the negative edge of that first enable bit the LCD controller reads the data lines and then starts dealing with the data. Note that the 'Internal Operation' graph shows that the LCD controller is 'Functioning'.  In other words it is busy.

4.  The program then makes R/W~ high in order to check the busy flag.  The next two Enable pulses come while the LCD controller is 'Functioning' so the busy flag comes back high (busy).
  
5. The LCD controller finishes up whatever it was doing before the fourth Enable pulse, so when that pulse is sent the busy flag comes back low (not busy).

6.  At this point the program makes R/W~ low again and writes the next instruction.


Here's how I interpret Figure 26:

1.  After RS and R/W~ are set up you must wait at least 40nS (tAS) before driving E high.

2.  After E is high it takes at least 160 nS (tDDR) until the data is valid.  After that time you should read the data lines, but many programs I have seen wait until after E goes low (in the next step).

3.  After E has been high for 230 nS (PWEH) you can drive E low.  

4.  It looks like they only guarantee the data for 5 nS (tDHR) after E goes low, so maybe reading the data after E goes low isn't such a good idea.

5.  You shouldn't mess with RS or R/W~ for 10 nS (tAH) after E goes low.

6.  You must wait at least 500 nS (tcycle) until you drive E high again

So in answer to:
 
Quote
I must  ... pulse the enable pin, wait at most 160 ns, then read the data. Is this then the proper procedure?
I would say 'not exactly'.   You don't want to 'pulse' the enable pin, you just want to drive it high.  You don't want to wait at most 160 nS you want to wait at least 160 nS.  The 'max' in the data sheet refers to the maximum amount of time it will take until the data is stable which then becomes the minimum amount of time you must wait.  (It's kind of like insurance - the maximum amount your insurer will pay becomes the minimum amount your doctor or mechanic will accept.)  After you read the data you can drive the enable pin low.  Also, I would check the original Hitachi data sheet for the timings.  It is the oldest data sheet around and therefore has the worst (longest) timing data.  If you want your library to work for all displays you have to program for the worst case.

Disclaimer:  It seems that every time I look at these diagrams I interpret them differently.  I hope I got it right this time.

Don

p.s. Did you get my PM?
« Last Edit: September 28, 2009, 11:31:31 pm by floresta » Logged

Pages: 1 [2] 3   Go Up
Print
 
Jump to: