16x2 Parallel LCD Display is slow?
Godslas
Posts: 4
I've recently started playing around with the Propeller and a standard 16x2 parallel LCD display. Just for some fun and a little bit of practice, I wrote my own object with SPIN to control the LCD screen, and then I made a test program to see if the object was working correctly. It does seem to work, but it's slow. Characters appear one after another, which doesn't make for a very nice menu screen. I have a few ideas as to why it's so slow, but I don't want to jump to conclusions when I'm not 100%.
- I'm not running the propeller at full speed. I think this chip has a problem with its PLL, because it doesn't work at all at 80MHz. Works fine at 40MHz.
- Is SPIN too slow for this, and if so, should I give PASM a shot? (I've used a bit of assembly before, but not with a propeller.)
- I could be wasting time somewhere with unneeded commands?
If anyone has any ideas or tips for cleaner or more efficient coding, let me know, everybody has to start somewhere!
Here is the code for the driver object:
And here is the test program:
- I'm not running the propeller at full speed. I think this chip has a problem with its PLL, because it doesn't work at all at 80MHz. Works fine at 40MHz.
- Is SPIN too slow for this, and if so, should I give PASM a shot? (I've used a bit of assembly before, but not with a propeller.)
- I could be wasting time somewhere with unneeded commands?
If anyone has any ideas or tips for cleaner or more efficient coding, let me know, everybody has to start somewhere!
Here is the code for the driver object:
''LCDDriver.spin CON ''These are commands for controlling LCD display: CD = $01 ''Clear display RH = $02 ''Return to origin of display DO = $0C ''Display on, cursor off DC = $0E ''Display on, cursor on DB = $0F ''Display on, cursor blinking VAR byte DATALSB ''Pin of LSB of data byte DATAMSB ''Pin of MSB of data byte RS ''Pin of Register Select, 0 for command, 1 for data byte RW ''Pin of Read/write select, 0 for write, 1 for read byte E ''Pin Enable pin, pulsed high-low for write, low-high for read. PUB init(RegSel, ReadWrite, Enable, DataM, DataL) RS := RegSel ''The init function sets the pin numbers of RW := ReadWrite ''all the I/O pins you need, as well as E := Enable ''enables them as outputs. DATAMSB := DataM DATALSB := DataL dira[RS] := 1 dira[RW] := 1 dira[E] := 1 dira[DATAMSB..DATALSB] := 111111 PUB sndcmd(x) lcdready ''The sndcmd function sends a command outa[RS] := 0 ''to the LCD. outa[RW]:= 0 outa[E] := 1 outa[DATAMSB..DATALSB] := x outa[E] := 0 PUB snddata(x) lcdready ''The snddata function sends data outa[RS] := 1 ''to be displayed to the LCD. outa[RW]:= 0 outa[E] := 1 outa[DATAMSB..DATALSB] := x outa[E] := 0 PUB str(stringptr) repeat strsize(stringptr) ''The str function takes a string snddata(byte[stringptr++]) ''and sends it out as data to the LCD. PUB move(x, y) | ADR ''y 1-2, x 1-16 ADR := (y - 1) * 64 ''The move function moves the cursor ADR += (x - 1) + 128 ''to the specified (x, y) coordinate. sndcmd(ADR) PUB clear sndcmd(CD) ''Simple function for clearing the screen. PUB disp(mode) ''mode can be 0, 1, or 2 The disp function takes in a number from 0-2 ''0 = no cursor and sets the display cursor. if mode == 0 ''1 = cursor sndcmd(DO) ''2 = blinking cursor elseif mode == 1 sndcmd(DC) elseif mode ==2 sndcmd(DB) PUB origin sndcmd(RH) ''Simple function to move display to ''the origin of the LCD, (1,1). PUB lcdready dira[DATAMSB]~ ''lcdready function is for checking outa[RS] := 0 ''if the LCD display is ready to outa[RW] := 1 ''display the next character or take repeat while DATAMSB == 1 ''the next command. outa[E] := 0 outa[E] := 1 dira[DATAMSB]~~
And here is the test program:
CON _xinfreq = 5_000_000 _clkmode = xtal1 + pll8x RS = 12 RW = 13 E = 14 DATALSB = 16 DATAMSB = 23 OBJ LCD : "LCDDriver.spin" PUB main LCD.init(RS, RW, E, DATAMSB, DATALSB) LCD.disp(0) LCD.clear LCD.str(string("Test 1")) LCD.clear LCD.sndcmd($02) LCD.disp(1) LCD.str(string("Test 2")) LCD.clear LCD.disp(2) LCD.str(string("Test 3")) repeat
Comments
I used JonnyMac's object from the OBEX and it seemed to work just fine. You might want to give his code a try.
From my limited experience with the LCDs, it looks like the code you're using isn't toggling the enable pin correctly. The data pins should be set before the enable pin is set high. The code you posted sets enable and then the data.
You problem with the PLL sure seems strange. Which board are you using?
to this
You are telling the display to read what is on the inputs, then you are changing the data on the inputs. You want to have the date ready, then tell the display to read the inputs.
** EDIT ** Also, your lcdready,
change to
So many times you hear some "expert" say "better be safe and read the busy flag" but that is absolute rot. I have decades of commercial volume use that just treat LCDs as predictable write-only devices.
Here's a link to an LCD interfacing page that I setup and although the code is in Tachyon Forth there is plenty of information there to help anyone who wants to interface LCDs. I treat the LCD as if it were a serial device that responds to the control codes etc so your application doesn't need special routines for anything, it just treats it as an output channel.
I was wondering about this a few days ago.
Thanks for mentioning this.
I went and changed my code so that the data was set before the enable pulse, and I also changed lcdready so that the enable bit goes from high to low instead of low to high, but it didn't have any effect on the operation. Here is a quick 30 second video I took to show that it is indeed working, just much slower then I would like it to.
Thanks for the tip on making it a write-only device. I'll give that a shot to see how it improves timing.
On both sides of the prop, add the following between 3v3 and GND:
- 100nF ceramic decoupling cap
- 33uF electrolytic cap
If you ever run it without a propplug, you should add a 10k pullup from reset to 3v3
I'd have 10K pullups on both SDA and SCL.
You are also missing a 100nF decoupling capacitor at the EEPROM
In init,
is not correct, I'd use
I think that the 111111 problem has always been a BBCode problem as it intercepts all the "percentage" symbols (can't put one in here now can I).
BTW, Godslas, thanks for the photos and video, it's what is always needed but very rarely do we ever get to see any
Thanks for the advice, I always forget decoupling capacitors. Adding your suggestions didn't rectify my PLL problems though. To test it I wrote a little bit of code to toggle a single LED, which doesn't do a thing at 80MHz, but works fine at 40. I'm starting to get the feeling that there is something wrong inside my propeller.
I used to work at an internet support desk, troubleshooting without the ability to be hands on can be very difficult! A picture and video aren't quite hands on, but they help better then a blindfold!
Unfortunately you may have zapped the Prop PLL and looking at your photo we of course see the lack of decoupling capacitors and those electrolytics that you do have aren't much help, it is quite possible for the regulators (don't know which type they are) to spike on line and load transients, they need small value ceramic caps. Also with breadboarding too you can have a link loose and the biggest reason why the Prop's PLL gets zapped is due to not having all the grounds and Vdd pins connected (at some time) or using these pins as if they were internally linked, that is putting 3.3V on on Vdd pin and then assume that the other Vdd pin can be used to power a load. In fact I'm looking at your photo and I can't see a link joining the two sides of the breadboard up to 3.3V?
Peter, sadly I am one of those, "experts" that says that. Started using Hitachi displays on the 6502 in 1989 IIRC and then in 1991 on the Z80. I was using ASM routines and always did check the flag as per the manufacturer's datasheet. Even now I incorporate that into my assembly code, however I've never had any delays (certainly nothing visible) related to doing that. :nerd:
Hi Chris, we all have to use this silly controller because that's what the manufacturers in their conventional and unimaginative wisdom think that we want. We don't have to be limited by this "wisdom" though. Rather than those one page datasheets I use the actual and rather voluminous "Hitachi LCD/Controller Driver LSI" databook (Sept 1988) and before that I had access to various display manufacturers datasheets. When you initialize the displays you have to use delays as you cannot read the busy flag, isn't that right? Anyhow, besides running the LCD in write only mode for decades in commercial volume product it seems in this 3.3V age that it is ever less wise to try to read from a 5V data bus which requires 8 protection resistors and an extra R/W line all for the sake of a redundant busy flag. Yes, a busy flag may respond sooner than a delay but Spin can't write characters faster than this anyway and while it's fumbling the I/O lines in emulating a "data bus" it could have written several more characters anyway. Having a PASM driver is a terrible waste of a cog and how many bytes have to be written to a display?
Given this information and parameters it would be remiss to not optimize the operation of this otherwise 5V 80's data bus tech for today's 3.3V I/O tech. I can't remember when I first used write-only mode but I do remember that I was doing this in the late 80's with remote LCDs on I2C port expanders. It made little sense to read a busy flag as the I/O fumbling required took around 1ms yet the manual says that the maximum time for any normal instruction or data is 37us while the little used clear display/return home instruction takes 1.5ms. So guess what, I did the logical thing and simply put a delay in there and just wrote without ever reading and over I2C this actually proved to be far less time consuming.
As I said I have done this ever since in commercial designs and the other thing I did away with was the ridiculous contrast pot as it was bulky and never an easy thing to access or wanting it to be accessed, RC PWM makes far more sense with an optional manual push-button. Same with the backlight, I just use a tiny SOT-23 NPN to switch up to a few hundred millliamps as you get on the larger displays. Here's a sample of one of my commerical OEM 20x4 LCDs with "surface-mounted" backpack. Without the optional buzzer and vertical connector the whole assembly doesn't protrude beyond the bezel's metal tabs (2.8mm).
It would also be remiss of me to not pass on any of these tips that I have to share but it is also extremely frustrating when I see the traditional approach being propagated and followed blindly. I'm sure you can appreciate improved methods and ways of doing things, it's also the same reason we are using Props and not AVRs or PICs (execpt on my backpack).
After going over the wiring, it seemed everything was correct. There are jumpers in place connecting the two sides of the breadboard to power and ground, so it wasn't that. I also checked continuity everywhere, and everything was connected properly, so it wasn't a loose connection either. I'm assuming it's something that happened to the chip previously. I made a digital guitar pedal with it back in 2011. It wasn't professional, by any means, but it worked, and it was more to prove the theory because it was for my final project in school. I haven't used it much since then, but it never had a problem until I decided to wire it up again and start playing with it recently.
I plan on getting some new props soon, so hopefully that will solve my problems.
I think all the common displays are still 5V, at least for the LCD where the Vlcd contrast pin voltage is specified as being negative to the supply volts, so it might be possible to run the display at 3.3V if you could take the contrast pin another 1.5V or so negative. The controller's Vih is specified for TTL so it will accept 2V logic highs.
I've had quite a number of Props hooked directly to the LCD but when compared to my serial displays (can't speak about others) I don't suffer any limit in update rates. Although my displays may go a lot faster the 9600 baud seems to be a standard and at that rate a normal 16x2 display could easily be updated 30 times a second so I can't see that being a problem which BTW is too fast for these twisted nematic (TN/STN/FSTN) LCDs as the "crystals" can't flip back fast enough anyway.
Come to think of it there may be some backpacks out there that implement a bit-bang receive and these ones may have some speed limitations. I once fitted serial backpack code into an 8-pin PIC, 4 I/O for data with 1 of those connected to an RC into the RS line, one I/O for enable, and the input ony pin was the serial receive. Only did that as an experiment though.
Did your code write to a programmable character to make the last digit appear to roll? That sounds clever. I should try that on the serial display to see how that runs.
I thought it might have been staggered too, the normal serial displays mightn't be up to that at 19.2k but I think I can run up to 250K baud (untested) which is about as fast as you could write to the LCD anyway. There is no need to have receive interrupts as it's a simple matter of grabbing the character when the UART is ready and doing a blind write to the display, and no need to implement delays as the serial throughput is slower than the display.
EDIT: I might write a bit of quick code in TF talking to a serial display and post a video
Everything worked fine, and although we were driving the backlight with <30mA, I was still using that npn transistor circuit. To my horror, we failed FCC EMI testing. Removed the transistor and all was fine. So now I am using a 47R (1.4V -> 30mA max) from the prop and PWM to lower from max brightness - this passed FCC EMI.
I got around to writing some code to talk to a serial 20x4 LCD at 9600 baud. It seems to be quite smooth. I will also try this at higher baud rates too. Here's a link to my Tachyon Forth code document. (more videos at higher baud rates)
[video=youtube_share;AVgq-X3BAOs]
What are you running that code on? Mine was converted from Z80 assembly to run on a BASIC Stamp 2px initially just because I needed to test the logic. If it's the Propeller, can you attach the code so I can test it?
EDIT: just noticed the link...doh!
P.S. - Wow, Forth is like a foreign language to me. :blank: