Shop OBEX P1 Docs P2 Docs Learn Events
16x2 Parallel LCD Display is slow? — Parallax Forums

16x2 Parallel LCD Display is slow?

GodslasGodslas Posts: 4
edited 2014-02-25 15:35 in Propeller 1
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:
''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

  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-02-18 12:25
    I was just playing around with a 16x2 parallel LCD a couple of days ago. I didn't notice any problem when using a Spin driver.

    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?
  • TCTC Posts: 1,019
    edited 2014-02-18 12:51
    Change this
      outa[E] := 1
      outa[DATAMSB..DATALSB] := x
      outa[E] := 0
    

    to this
      outa[DATAMSB..DATALSB] := x
      outa[E] := 1
      outa[E] := 0
    

    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,
        outa[E] := 0
        outa[E] := 1
    

    change to
        outa[E] := 1
        outa[E] := 0
    
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2014-02-18 15:08
    Character LCD modules have been around since the early 80's and the controllers have hardly changed so they are not well suited for 21st century interfacing (not even 1980's) as they were designed for slow 8-bit data buses. However there is one optimization that I ALWAYS employ, and that is to never read from the display, the busy bit is the only "bit" that is ever really read and yet it is REDUNDANT. Knowing that you can tie the R/W line low, forget about protecting the Prop pins and just write to as fast as you can except for the home and clear instructions and during initialization. When I say as fast as you can it just means all the overhead involved in putting data on the "data bus" from an I/O port and setting the RS line and toggling the enable line takes a lot longer than it takes the LCD so that it is never busy when you send the next character. So at the very very most you sometimes just need a small delay but that's still faster than fumbling I/O lines to read it, especially when the code is in slow old Spin.

    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.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-02-18 15:25
    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.

    I was wondering about this a few days ago.

    Thanks for mentioning this.
  • GodslasGodslas Posts: 4
    edited 2014-02-18 15:41
    You problem with the PLL sure seems strange. Which board are you using?
    I'm actually just wiring it all up on a breadboard.

    HU9FU4R.jpg


    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.
    1024 x 768 - 118K
  • Bill HenningBill Henning Posts: 6,445
    edited 2014-02-18 16:06
    Your PLL issues are likely due to not having decoupling capacitors for the IC's

    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,
      dira[DATAMSB..DATALSB] := 111111
    

    is not correct, I'd use
      dira[DATAMSB..DATALSB]~~
    
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2014-02-18 16:17
    Your PLL issues are likely due to not having decoupling capacitors for the IC's

    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,
      dira[DATAMSB..DATALSB] := 111111
    

    is not correct, I'd use
      dira[DATAMSB..DATALSB]~~
    

    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 :)
  • Cluso99Cluso99 Posts: 18,069
    edited 2014-02-18 17:16
    Just to echo Peters quote on not reading the LCD (or busy bit). The normal chip 4770??? used in these displays has a spec somewhere on the internet which details the delays required for the few instructions requiring delays. Just use them and you will be fine. I even use 4 bit mode.
  • GodslasGodslas Posts: 4
    edited 2014-02-19 12:21
    Your PLL issues are likely due to not having decoupling capacitors for the IC's

    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,
      dira[DATAMSB..DATALSB] := 111111
    

    is not correct, I'd use
      dira[DATAMSB..DATALSB]~~
    

    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.
    BTW, Godslas, thanks for the photos and video, it's what is always needed but very rarely do we ever get to see any

    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!
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2014-02-19 14:56
    Godslas wrote: »
    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.

    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 JakackiPeter Jakacki Posts: 10,193
    edited 2014-02-20 23:55
    So Godslas, what's the verdict? Was it a miswired breadboard that led to the demise of the unfortunate Prop PLL?
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2014-02-21 11:48
    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.

    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:
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2014-02-22 05:51
    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).
    p1390-ecolcd.jpg


    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).
    798 x 390 - 63K
  • GodslasGodslas Posts: 4
    edited 2014-02-23 23:29
    So Godslas, what's the verdict? Was it a miswired breadboard that led to the demise of the unfortunate Prop PLL?

    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.
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2014-02-24 11:16
    @Peter - I did forget to mention that I have not (to date) connected a Hitachi-Compatible LCD to the Propeller. You would think by now there would be some 3.3V variants of these displays, but anything I have seen that is has a serial backpack on it, limiting character speed to the maximum baud rate supported by the backpack. Which as I have found out breaks a few programs I wrote back in the day, including an 8-digit rolling odometer, which doesn't update properly if the display can't be refreshed at a minimum rate much higher than a serial display can do.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2014-02-24 15:09
    @Peter - I did forget to mention that I have not (to date) connected a Hitachi-Compatible LCD to the Propeller. You would think by now there would be some 3.3V variants of these displays, but anything I have seen that is has a serial backpack on it, limiting character speed to the maximum baud rate supported by the backpack. Which as I have found out breaks a few programs I wrote back in the day, including an 8-digit rolling odometer, which doesn't update properly if the display can't be refreshed at a minimum rate much higher than a serial display can do.

    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.
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2014-02-24 16:01
    If it were just the last character rolling the serial display might have worked. But the code I have rolls them all. Because of this it is sending many commands between characters changing the pointers to the custom characters to get the effect. Because of this throughput is higher than just redrawing the display. 19.2 Kbps wasn't fast enough. I will publish the code at some point once I find a practical way to demo it on a Parallel Display with something other than the Z80 I used to use. :nerd:
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2014-02-24 16:16
    If it were just the last character rolling the serial display might have worked. But the code I have rolls them all. Because of this it is sending many commands between characters changing the pointers to the custom characters to get the effect. Because of this throughput is higher than just redrawing the display. 19.2 Kbps wasn't fast enough. I will publish the code at some point once I find a practical way to demo it on a Parallel Display with something other than the Z80 I used to use. :nerd:

    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
  • Cluso99Cluso99 Posts: 18,069
    edited 2014-02-24 18:06
    FWIW I drive a 4x20 LCD using those pesky HDxx780??? protocols. While it is a special LCD, I drive the contrast and backlight using the prop and PWM. Originally I had a P2N2222A driven by the prop to the backlight to get higher currents. I connect on the low side with the high side directly to 5V and a series resistor to the collector of the transistor.

    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.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2014-02-25 01:21
    If it were just the last character rolling the serial display might have worked. But the code I have rolls them all. Because of this it is sending many commands between characters changing the pointers to the custom characters to get the effect. Because of this throughput is higher than just redrawing the display. 19.2 Kbps wasn't fast enough. I will publish the code at some point once I find a practical way to demo it on a Parallel Display with something other than the Z80 I used to use. :nerd:

    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]
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2014-02-25 15:35
    Peter,

    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:
Sign In or Register to comment.