Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

16X2 Character Display Fills Screen

Status
Not open for further replies.
Hi John,

The data sheet says more than 4.1ms after sending the first command, then what works after that is 100us min between remaining commands, including the 4 bit mode command. So what works is (in order of line by line here):
0x03
4500us
0x03
150us
0x03
150us
0x02
150us
then set lines, font, etc.
60us
 
Some people do POR and the early steps for initialization and function setting by sending only 4 bits to the controller rather than two writes of 4-bits each . The controller defaults in 8-bit mode, so until the controller is in 4-bit mode, two, 4-bit sends might be interpreted as two instructions, not one. To protect against that, it seems that early on, the controller ignores the second write. Thus, you can send either a 4-bit nibble or the two nibbles as is done once in 4-bit mode. The question is, at what point does the 4-bit mode become active, so the user must do two writes of 4 bits each?

I think I found that step, but from reading the Hitachi comments, it was not clear to me until I knew the answer. Then, it was obvious (duh). There are 5 choices. Two are pretty obviously not right. Which one is it? BTW, this is really a question of understanding the DS for me. In reality, it seems you can use the 8-bit codes in 4-bit mode from the very start. You just have to be sure to start 4-bit mode at the right time.

The controller starts up in 8 bit mode and stays in 8 bit mode until you specifically send it a command to put it into 4 bit mode. As long as it is in 8 bit mode, any data that you send will be interpreted as a complete 8 bit command, but with four of the bits missing, since they aren't wired up. So, if you try to send 4 bit commands or data as two 4 bit nibbles while in 8 bit mode, then it will see these as two separate 8 bit commands. However, if the second nibble is sent right after the first, with no delay, then the controller may still be busy executing what it thinks is an 8 bit command, and so the second nibble is missed. But, this is not a reliable way of doing things.

My understanding of how to switch from the default 8 bit mode to 4 bit mode, is that you send only single nibble commands until putting it into 4 bit mode. This is possible, because many of the commands don't require bits d0..d3, including the command that sets 4/8 bit mode. So you can send these commands while in 8 bit mode without having d0..d3 wired up. (The controller does read bits d0..d3, but their state will be indeterminate since they aren't wired, but they have no influence on the command anyway.)

Hence, the proper start up sequence is to send whatever initialization commands as necessary, as single nibble commands, but you should send the command to put the LCD into 4 bit mode ASAP. Then, everything after that is a 2 nibble command.
 
Hi Bob,

Your explanation makes a lot more sense than another that proposed the second nibble was ignored. Thanks for making that clear. The nibble routine is quite simple.

John
 
Hi,

To say that the device starts up in 8 bit mode may be a mistake, because according to the data sheet the first three commands are interpreted as 4 bit commands, not 8 bit, so the 4 upper bits are looked at and the 4 lower bits are ignored. If these three commands are to set to 8 bit mode, then the fourth command will be a 6 bit command with upper 6 bits valid and lower 2 bits ignored, then everything after that the full 8 bits are used.
Here is the sequence for 8 bit mode:
Power On
wait more than 15ms
upper four bits: 0x03
wait more than 4.1ms
0x03
wait more than 100us
0x03
wait more than 100us
In binary: 0011 NFxx (upper 4 bits 0x03, two bits NF, two bits ignored)
probably wait 100us
0x08 (display off and first 8 bit mode command)
then continue

Some of the timings are not shown because they probably assume you are checking the BUSY flag. The data sheet says that when the BUSY flag is 1 the user must wait until after ensuring that the BUSY flag went back to 0 before sending the next command. Since this can be output to DB7, it can be sensed by the micro controller if that bit is changed to an input temporarily. Of course if you use the shift register as a cheap serial interface, then you need to wire at least one input pin to the output that goes do DB7 or else devise a way to isolate the output for that pin so that you can detect a forced LOW from the LCD from the data pin (two pin communication).
This might be accomplished by using a resistor from that output to DB7, forcing the shift reg to be high, then checking the data line for a low. If a low is detected then the busy flag went to zero.
You'd have to figure out what size resistor to use, one that is low enough in value for good communication yet high enough in value to allow the LCD to pull the DB7 pin low without excess current flow. 1k might work, but check the DC specs.
 
Last edited:
Thanks all.
When I retired last night I had a better appreciation for what the LCD processor was doing. One of my points of confusion was the table in Figure 24 of the datasheet showing steps for a 4 bit interface. First, the comments didn't line up with the rows in the table, and second, it wasn't completely clear to me at the time whether the pairs of 4-bit instructions should be entered sequentially or as a single 8-bit literal (also mentioned above).

I have included my working code (4-bit test.pdf) and a slightly revised initialization table (Initialization HD44780.pdf) with rows and instructions lined up. My code is really not that different than what others do.

Regards, John
 

Attachments

  • 4-bit test.pdf
    15.9 KB · Views: 386
  • Initialization HD44780.pdf
    34.2 KB · Views: 1,848
Very nice... If you can tolerate an unnecessary 50-usec delay between nibbles, you could optimize it a bit (you'd have to use 0x30 and 0x20 values in your init code);

Code:
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  LCD Subroutines                                       ~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

PutCmd
        bcf     RS              ; RS = 0, 'command'
        goto    $+2             ;
PutDat
        bsf     RS              ; RS = 1, 'data'
        call    PutNib          ; clock out the hi nibble
PutNib                          ; fall thru for lo nibble
        swapf   WREG,W          ; swap nibbles
        movwf   PORTB           ; LCD D7..D4 = RB3..RB0
        bsf     E               ;
        bcf     E               ;
        DelayCy (50*usecs)      ;
        return                  ;
 
Last edited:
To say that the device starts up in 8 bit mode may be a mistake, because according to the data sheet the first three commands are interpreted as 4 bit commands, not 8 bit, so the 4 upper bits are looked at and the 4 lower bits are ignored.
That's curious. I never saw that in the datasheet. My comments about the controller starting up in 8-bit mode were based on the datasheet description of the power on reset sequence (see attached).
 

Attachments

  • HD44780p24.pdf
    42.8 KB · Views: 291
I didn't see it either. Here is a snippet from the datasheet:
upload_2015-11-22_17-11-54.png


As typical, that comment is not particularly clear to me. Nevertheless, even if the first 3 writes are 4-bit, you still have the fourth write (changing to 4-bit mode) to deal with.

John
 
I think what they are referring to is that the command that puts it into 4-bit mode, 0010.xxxx, includes additional options in the low nibble which cannot be sent because the x bits are not wired up. So, after sending this command, you have to send it again in 4-bit mode so that you can set the low nibble options. For example you would send 0010.xxxx to select 4-bit mode, and then you would send 0010.1000 again to select 4-bit mode plus the 'two line' display option.
 
BobW, Yes agreed.

I was responding to the assertion that the first three commands were considered 4-bit commands, not 8-bit for which the lower nibble didn't matter. I do not have an 8-bit interface up and running. Maybe tomorrow, I will test an 8-bit interface by sending 0x3c or ox3F for the first three commands.

John
 
A couple more comments:

1.
I was looking at this line in your code:
Code:
  DelayCy (50*usecs)      ;
Looks like a macro to me, and I was wondering if this is one that you wrote yourself, or if it's part of a library somewhere.

2.
Regarding adding a piggyback chip to the LCD display, I'd seen a couple of references to Mike-K8LH's design, but couldn't find it anywhere. Can someone post a link? I'd been thinking of doing something like this too. I've got a pile of 74HC164 shift registers, but because of the rather clumsy communication architecture of the HD44780, it wouldn't really save very many pins on the PIC. It seems to me that the best piggyback chip would be another PIC with an SPI or I2C interface back to the main PIC. Then, you could offload a lot of the awkward timing and initialization to the piggyback PIC. Then the main PIC could run without having to waste time sitting in delay loops. In the piggyback PIC you could possibly even get rid of the delays there too, and make use of the HD44780's busy flag, to properly handshake all of the communications.
 
Hi Bob,

There's an ETO 'Article' for a shift-register LCD Backpack that uses 8-bit interface mode.

There's also a novelty serial LCD Backpack design using an 8-pin PIC but I don't believe I ever really wrote it up anywhere. Since it has a little 'smarts' it does initialize the HD44780U and so it doesn't start looking for serial data from the host until 80-msecs or so after power-up. It also looks for an 0xFE character as a prefix for any LCD 'command' characters. I've attached a file with firmware for a 12F683 @9600 baud.

K8LH Serial Backpack.jpg


Of course you can do much much better than that little 8-pin design and there are a bunch of other really nice designs to be found...

There's a brief description of one version of the K8LH 'cycle-accurate' fixed delay subsystem in this post.

My apologies for hi-jacking John's thread.

Have fun guys...

Cheerful regards, Mike
 

Attachments

  • ez-lcd v3 (12F683).zip
    4.3 KB · Views: 267
Last edited:
Be very careful when using things like DelayCy (100*usecs). I can't remember if it applies in asm but C18 treats all constants as 8 bit and calculates values wrong.

In C18,
unsigned int temp=10*60;
results in temp containing 88 and not 600.

Mike.
 
A couple more comments:

I was looking at this line in your code:
Code:
  DelayCy (50*usecs)      ;
Looks like a macro to me, and I was wondering if this is one that you wrote yourself, or if it's part of a library somewhere.

Yes, it is a macro from Mike - K8LH. He has posted versions for 12-bit-core and 14-bit-core PIC's. Here is a link to the 14-bit- core version: https://www.electro-tech-online.com/threads/delay-macro-timing.125341/#post-1038243

A search for "delay macro" by member Mike-K8LH will give you the other version. I have used that macro as a standard part of code many times. Basically, it is in the template I use. One nice part is you can adjust the delay for overhead when you invoke the macro, if an accurate timing is critical. Of course, it is limited to delays of a few hundred milliseconds, but that is rarely a problem.

John
 
Hi again,

I mentioned that calling the start up mode "8 bit" may not be the best idea because the LCD only sees 4 bits anyway. So in reality what happens is something like the following...

We have an 8 lane highway, with only 4 lanes being used. We still call it an 8 lane highway. But if 4 of those lanes to the left are still under construction, we still call it a 4 lane highway until AFTER the other four lanes are completed and cars are allowed to drive on those extra four lanes.

Now the data sheet is effectively calling that highway an 8 lane highway, even though the extra 4 lanes can not yet be used for anything. It's only AFTER we give the command to activate (or confirm) the 8 bit mode that we are allowed to send data on those extra four "lanes".

So the data sheet is referring to the physical layout where there are actually 8 pins present, even though only 4 can be used at startup.
If you think about it, how else could 4 bit mode work anyway. If you needed 8 bits at the start there would be no way to set 4 bit mode using only 4 bits and 4 physical wire connections...you would need all 8 bits and 8 wire connections just to get to 4 bit mode :)

As a side note, another trick is to use only 7 bits keeping the most significant bit low (or high as needed). This still requires a physical connection to the LCD msb, but no uC pin, just a permanent jumper to ground. This reduces the character set to the lower ASCII set, but often that is enough to produce the characters we need. The upper 128 characters are often just not needed anyway so that msb will end up being sent as a zero all the time anyway. I did this with the WinTek display to save on one i/o port because that display only worked in 8 bit mode. A side effect of doing this (with that display and maybe more) is that you can no longer test the busy flag with software, but often we dont do that anyway.
 
If I may... a more recent version of DelayCy() provides the capability to extend the delay range at the cost of using more program memory in the 'uLoop' timing subroutine. The method uses some of the 'nop' instructions at the top of the 'uLoop' subroutine as part of the timing loop.

If you needed a 250-millisecond delay in a 32-MHz project, you would need to set the 'dloop' constant to 31 (cycles per loop) to allow for the 2,000,000 cycles needed for that delay. In this case, the original 9 word (5 cycle) 'uLoop' subroutine grows to a whopping 35 words in size, but it still only uses one variable while providing an expanded range of 11..2031626 cycles.

Regards, Mike

Code:
        cblock 0x70
delayhi                 ; DelayCy() sub-system timing variable
        endc
;==================================================================
;  K8LH DelayCy() subsystem macro generates four instructions     =
;==================================================================
        radix   dec
clock   equ     32              ; 4, 8, 12, 16, 20 (MHz), etc.
usecs   equ     clock/4         ; cycles/microsecond multiplier
msecs   equ     usecs*1000      ; cycles/millisecond multiplier
dloop   equ     31              ; loop size, 5 to ??? cycles
;
;  -- loop --  -- delay range --  -- memory overhead ----------
;  5-cyc loop, 11..327690 cycles,  9 words (+4 each macro call)
;  6-cyc loop, 11..393226 cycles, 10 words (+4 each macro call)
;  7-cyc loop, 11..458762 cycles, 11 words (+4 each macro call)
;  8-cyc loop, 11..524298 cycles, 12 words (+4 each macro call)
;  9-cyc loop, 11..589834 cycles, 13 words (+4 each macro call)
;
DelayCy macro   cycles          ; range, see above
    if (cycles<11)|(cycles>(dloop*65536+10))
        error " DelayCy range error "
    else
        movlw   high((cycles-11)/dloop)+1
        movwf   delayhi
        movlw   low ((cycles-11)/dloop)
        call    uLoop-((cycles-11)%dloop)
    endif
        endm

;******************************************************************
;  example code for simulation testing                            *
;******************************************************************
        org     0x000
SimTest
        DelayCy(200*msecs)      ; <- put simulator PC here
        goto    $               ; <- put simulator break point here

;******************************************************************
;  K8LH DelayCy() subsystem 16-bit 'uLoop' timing subroutine      *
;******************************************************************
a = dloop-1
    while a > 0
        nop                     ; (cycles-11)%dloop entry points  |B0
a -= 1
    endw
uLoop   addlw   -1              ; subtract 'dloop' loop time      |B0
        skpc                    ; borrow? no, skip, else          |B0
        decfsz  delayhi,F       ; done?  yes, skip, else          |B0
        goto    uLoop-dloop+5   ; do another loop                 |B0
        return                  ;                                 |B0

;******************************************************************
        end
 
Last edited:
I mentioned that calling the start up mode "8 bit" may not be the best idea because the LCD only sees 4 bits anyway.
My analysis suggests the display comes up in 8-bit mode automatically after its power-up reset. The Datasheet shows you can send a "function set" instruction immediately after the self reset that includes data in the lower (bit 3 & bit 2) data bits.
... another trick is to use only 7 bits keeping the most significant bit low (or high as needed).
Wouldn't that prevent you from sending "set DDRAM address" commands which require bit 7 (D7) = 1?
 
Last edited:
My analysis suggests the display comes up in 8-bit mode automatically after its power-up reset. The Datasheet shows you can send a "function set" instruction immediately after the self reset that includes data in the lower (bit 3 & bit 2) data bits.

Wouldn't that prevent you from sending "set DDRAM address" commands which require bit 7 (D7) = 1?

Hi again,

Do you mean that you found that you do not have to send the start up sequence for 8 bit mode too then, only for 4 bit mode?
I would have to wonder then why they show the start up sequence for 8 bit mode then too. I suppose that is a possibility, but i would have to see that test result. Otherwise we are sending something that looks like 8 bits but is really just 4 bits as described previously. If you have evidence to the contrary please supply that information.

If you only use the lower 7 bits there will be some limitations that's right, but those limitations do not always stop us from using the LCD. If we dont need the required functions, we dont need that bit. Setting the DRAM address is only necessary when we also have to set some custom characters and is not necessary when just using the lower 128 characters of the ASCII character set. The DRAM address also 'wraps' around back to start if we send dummy characters. For example:
A to Z, a to z, 0 through 9, plus various punctuation like comma, period, question mark, inequality signs, equals sign, etc., all possible without that last bit.
We still need bits 0 through 6 however even though in 8 bit mode, and that last bit either has to be grounded permanently or pulled high permanently.
I had to do this when using one of the PIC chips because i had limited i/o pins and still wanted to use the display and that display only worked in 8 bit mode.
One caveat using the 1602 displays with only 7 bits instead of 8 is that some of them can not display a normal backslash character and instead in it's code position there is a character for Japanese currency. To get around this we need that last bit because we'd have to write a custom 'backslash' manually using code, which requires setting one of the addresses. There may be a way around this too but i have not looked into it. Of course if we dont need the backslash (as when displaying some paths) then there's no problem.
 
Last edited:
Do you mean that you found that you do not have to send the start up sequence for 8 bit mode too then, only for 4 bit mode?

No, that's not what I said. Please re-read the post.

If you have evidence to the contrary please supply that information.
If you mean evidence that the HD44780U comes up in 8-bit interface mode after power-up reset, I would refer you to this section of the Hitachi HD44780U (LCD-II) Datasheet.

HD44780U.png
 
Last edited:
No, that's not what I said. Please re-read the post.


If you mean evidence that the HD44780U comes up in 8-bit interface mode after power-up reset, I would refer you to this section of the Hitachi HD44780U (LCD-II) Datasheet.

View attachment 95547


Hi,

Thanks for posting that, but you seem to be missing the point. What i am talking about here is realism.

One of the definitions of realism is and i quote:
"The attitude or practice of accepting a situation as it is and being prepared to deal with it accordingly."

Let me emphasize the phrase, "as it is". This is in stark contrast to, "as it was written".

Although the data sheet *was written" stating that the LCD comes up in "8 bit mode", that's not how it really works, even if we interpret the data sheet literally later on because it states that we are to send it codes that only utilize 4 out of 8 bits before we can start using it for ANYTHING.
That's why i asked for proof to the contrary, because there might be some shortcut since one of the bits is set that indicates 8 bit mode. But just because the bit is set that also does not mean it is really in 8 bit mode because again we have to send it data that only utilizes 4 bits.

Im sure you know the difference between 4 bits and 8 bits.

Proof to the contrary would NOT come from the data sheet in that form alone. It would come from an experiment to find out if we can skip some startup code listed on the data sheet that allows us to use it in 8 bit mode without having to send 4 bit codes. That might be a benefit for people that want to use it in 8 bit mode, if it is even possible. The data sheet suggests otherwise however, that we must send a series of 4 bit codes to get it up and running and switched into ANY MODE. So it sounds like we cant even use it until we send a series of FOUR BIT codes, and that suggests that it really comes up in a FOUR BIT initialization mode not 8 bit. If it came up in 8 bit mode, we'd have to send it 8 bit codes right away.

I made this as clear as it possibly can be.
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top