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.

PIC LCD fun!

Status
Not open for further replies.

Mike - K8LH

Well-Known Member
Hi guys,

I just hooked up a 44780 type LCD to a PIC for the first time a few days ago. No big deal, right? Well, I haven't worked with a 44780 type LCD in about 10 years (on a 68HC12 project) so it was pretty exciting for me when it came to life on the first try (grin). I'm sure all of you know that feeling.

Anyway, I was wondering if anyone was interested in my approach for the 44780 subsystem code (in assembler)? The subsystem is relatively small and includes a PutLCD macro with 'type' and 'data' parameters, an InitLCD subroutine to reset the LCD and place it in 4 bit interface mode, and a low level driver. I'm also using my little 12 word DelayCy() subsystem for LCD write delays because it's easy to setup for almost any clock frequency.

The PutLCD macro only supports five different 'type' parameters at the moment (more to come) but I'm very pleased with it's capabilities so far. The macro automatically sets the LCD 'RS' line before calling the low level driver and it also selects the appropriate LCD write time delay after a write operation (it knows to use a 1.53 msec delay after a Clear or Home command, for example). Here's an example to illustrate how I'm using the macro and subsystem;

What do you think?

Mike

Code:
;
;  44780 "function set" bit masks
;
m8bits  equ     b'00110000'     ; select 8 bit interface
m4bits  equ     b'00100000'     ; select 4 bit interface
m1line  equ     b'00100000'     ; select 1 line display
m2line  equ     b'00101000'     ; select 2 line display
fon5x7  equ     b'00100000'     ; select 5x7 font

;******************************************************************
;                                                                 *
;  init 2x16 LCD using the 44780 procedure for a 4 bit interface  *
;                                                                 *
InitLCD
        DelayMS(15)             ; wait 15 msecs after power up    |B0
        PutLCD  nyb, m8bits     ; step 1, 8 bit reset (160 usec)  |B0
        DelayMS(4)              ; wait 4.1 msecs total, then      |B0
        PutLCD  nyb, m8bits     ; step 2, 8 bit reset (160 usec)  |B0
        PutLCD  nyb, m8bits     ; step 3, 8 bit reset (160 usec)  |B0
        PutLCD  nyb, m4bits     ; set 4 bit mode from 8 bit mode  |B0
        PutLCD  cmd, m4bits|m2line|fon5x7    ; "function set"     |B0
        PutLCD  cmd, DispOff    ; display, cursor, blink all off  |B0
        PutLCD  cmd, Clear      ; clear display (1.53 msecs)      |B0
        PutLCD  cmd, CursInc    ; cursor inc, shift off           |B0
        PutLCD  cmd, DispOn     ; display on, leave cursor off    |B0
        PutLCD  pwm, 100        ; set 100% backlight brightness   |B0
        return                  ;                                 |B0
Code:
;******************************************************************
;                                                                 *
Main
        call    InitLCD         ; initialize LCD subsystem        |B0
        PutLCD  cmd, line1+0    ; line 1, tab 0                   |B0
        PutLCD  str, "K8LH Photo Timer"
        PutLCD  cmd, line2+0    ; line 2, tab 0                   |B0
        PutLCD  str, "Off 00:00:23.750"
 

Attachments

  • K8LH LCD Photo 1.JPG
    K8LH LCD Photo 1.JPG
    36.5 KB · Views: 295
I bought the same one, to display a 5 digit number.

Leaving me room for another 5 digit below the first one.

That's my next project.
 
New development..... K8LH 12F635 Serial 1-Pin 44780 LCD Interface

Late tonight I got an 8 pin 12F635 device working as a Serial 1 Pin 44780 LCD Interface at 19200 baud. The host was a 16F690 device.

As far as I know, no one has used an 8 pin PIC with only 5 outputs to drive a 44780 type LCD which needs six signals, so I'm pretty geeked. Basically, I tied the serial Rx line to the PIC and to the 'RS' line on the LCD. The LCD ignores the signals on any of its control lines until it receives an 'E' strobe so I simply latch the D7..D4 data bits onto the PIC outputs as they arrive in the serial bit stream and I provide the 'E' pulse to the LCD during the middle of the 'RS' bit as it arrives in the serial bit stream. I should mention that I send each 8 bit data byte as two separate serial bytes so I use a custom low level driver on the 16F690 host device.

I've attached the 12F635 source file for those who may be interested and I'll follow up with an example host side low level driver in a couple days.

Have fun. Mike
 

Attachments

  • K8LH 1-Wire LCD Interface.PNG
    K8LH 1-Wire LCD Interface.PNG
    30.5 KB · Views: 276
  • ez-lcd v1 (12F635).asm
    4.3 KB · Views: 165
Hi Mike,

Re your initial post about your parallel Lcd code, I for one would be interested to see more of your code, the PutLcd macro and Low Level Driver ? - sound very interesting.

I'm always looking for a more compact lcd code than the one I've always used from Ron Kreymborg

thanks

Richard
 
Hi Richard,

Thank you for expressing an interest. I'm not sure my subsystem code will be more compact than what you're using now.

I modified the low-level driver last night so it's a bit messy. It now supports an LCD connected either directly with 6 pins or connected via the serial port to that little 12F635 serial LCD interface. This is what it looks like at the moment;
Code:
;******************************************************************
;  K8LH low level 44780 LCD driver (LCD in 4 bit interface mode)  *
;                                                                 *
;  this driver supports either the "K8LH Serial 1-Pin Interface"  *
;  using the serial port or "direct" connection to the LCD using  *
;  using six lines.  the "direct" connection requires LCD D7..D4  *
;  lines connected to either the 7..4 bits or the 3..0 bits on a  *
;  port.  the LCD RS and E lines can be connected to a different  *
;  port or the same port.                                         *
;                                                                 *
;******************************************************************
;
;  driver type definitions
;
LoBits  equ     0               ; D7..D4 lines on port pins 3..0
HiBits  equ     1               ; D7..D4 lines on port pins 7..4
Serial  equ     2               ; K8LH 1-Pin Serial Interface
;
;  user must specify driver type from the selections above
;
drvtype equ     Serial          ; specify driver type from list
;
;  the following defines are required when not using the "Serial"
;  driver type
;
#define LCD_RS  PORTC,4         ; RC4 -----> LCD RS
#define LCD_E   PORTC,7         ; RC7 -----> LCD E
#define LCD_Dat PORTC           ; RC3..RC0 > LCD D7..D4
;
;  WREG contains byte to be written to LCD
;
PutCmd                          ; entry point for "cmd" data
        clrc                    ; RS = 0 (command)                |B0
        skpnc                   ;                                 |B0
;
;  WREG contains byte to be written to LCD
;
PutDat                          ; entry point for "dat" data
        setc                    ; RS = 1 (data)                   |B0
        movwf   Temp            ; save WREG data byte             |B0
  if drvtype != Serial          ; if 'HiBits' or 'LoBits' driver
     if drvtype == HiBits       ; if 'HiBits' driver
        call    PutNyb          ; send left nybble                |B0
        swapf   Temp,W          ; swap nybbles in W               |B0
        call    PutNyb          ; send right nybble               |B0
     else                       ; if 'LoBits' driver
        swapf   Temp,W          ; swap nybbles in W               |B0
        call    PutNyb          ; send left nybble                |B0
        movf    Temp,W          ;                                 |B0
        call    PutNyb          ; send right nybble               |B0
     endif
        DelayCy(tDef)           ; default 40 usec delay           |B0
        return                  ;                                 |B0
  else                          ; if 'Serial' driver
        swapf   Temp,W          ;                                 |B0
        call    PutNyb          ; send left nybble                |B0
        movf    Temp,W          ; send right nybble               |B0
  endif
PutNyb
  if drvtype == Serial          ; if K8LH 1-Pin Serial Interface
        andlw   0x0F            ; mask off left nybble            |B0
        skpnc                   ; RS = 0?  yes, skip, else        |B0
        iorlw   b'10000000'     ; set RS bit to '1'               |B0
        btfss   PIR1,TXIF       ; UART transmit buffer empty?     |B0
        goto    $-1             ; no, branch and wait, else       |B0
        movwf   TXREG           ; send it                         |B0
        return                  ;                                 |B0
  else                          ; 'LoBits' or 'HiBits' driver
        bcf     LCD_RS          ; clr LCD RS line (RS = 0)        |B0
    if drvtype == LoBits        ; if 'LoBits' driver
        andlw   0x0F            ; mask off left nybble            |B0
    else                        ; if 'HiBits' driver
        andlw   0xF0            ; mask off right nybble           |B0
    endif
        movwf   LCD_Dat         ; setup LCD D7..D4 lines          |B0
        skpnc                   ; RS = 0?  yes, skip, else        |B0
        bsf     LCD_RS          ; set LCD RS line (RS = 1)        |B0
        bsf     LCD_E           ; strobe LCD E line               |B0
        nop                     ;                                 |B0
        bcf     LCD_E           ;                                 |B0
        return                  ;                                 |B0
  endif
As you can probably tell from the listing, the PutLCD macro will call either the PutCmd or the PutDat low-level driver entry point depending on the PutLCD "type" operand. The "cmd" and "nyb" types use the PutCmd entry point (RS=0) and the "dat" and "str" types use the PutDat entry point (RS=1).

The "nyb" type is a special case and is used only in during LCD initialization to send a 4 bit nybble while the LCD is still in an 8-bit interface mode. I need to add a preprocessor directive to correctly format the data for both the Hi and Lo 'direct' interfaces.

Anyway, the PutLCD macro needs much more work. It also needs some new "types" to support literal constant numbers and variable numbers and some simple conversions; bin2dec, bcd2dec, etc.

This code is definately a work-in-progress and is provided simply to give you some food for thought. Caveat!

Code:
;******************************************************************
;                                                                 *
;  PutLCD  cmd, data   ' send literal const data byte, RS = 0     *
;  PutLCD  dat, data   ' send literal const data byte, RS = 1     *
;  PutLCD  dec, file   ' future                                   *
;  PutLCD  hex, file   ' future                                   *
;  PutLCD  bin, file   ' future                                   *
;  PutLCD  str, data   ' send inline const string data, RS = 1    *
;  PutLCD  nyb, data   ' send const b7..b4, 8 bit mode, RS = 0    *
;  PutLCD  pwm, 0..100 ' LCD backlight, 0..100% brightness        *
;                                                                 *
cmd     equ     1               ; 1 byte command/control type
dat     equ     2               ; 1 byte character/data type
dec     equ     3               ;  future
hex     equ     4               ;  future
bin     equ     5               ;  future
str     equ     6               ; multi-byte string character
nyb     equ     7               ; d7..d4 nybble in 8 bit mode
pwm     equ     8               ;  future
tab     equ     cmd             ;

tClr    equ     1530*usecs      ; 1.53 msecs -> "clear" command
tHome   equ     1530*usecs      ; 1.53 msecs -> "home" command
tNyb    equ     160*usecs       ; 160 usecs --> for Nybble ops
tDef    equ     40*usecs        ; 40 usecs ---> all others


PutLCD  macro   pType,pData

    if pType == cmd             ;
        movlw   pData           ; W = literal const pData         |
        call    PutCmd          ; send to RS = 0 + tDef delay     |
      if (pData) == Clear       ; if "clear" command
        DelayCy(tClr-tDef)      ; use 1.53 msec delay             |
      endif                     ;
      if (pData) == Home        ; if "home" command
        DelayCy(tHome-tDef)     ; use 1.53 msec delay             |
      endif                     ;
    endif

    if pType == dat             ;
        movlw   pData           ; W = literal const pdata
        call    PutDat          ; send with RS=1
    endif

    if pType == dec             ;
        movf    pData,W         ; Wreg = variable
        iorlw   '0'             ; 0..9 -> ASCII "0..9"
        call    PutDat          ; send with RS=1
    endif

    if pType == str             ;
        local   string,doit
        goto    doit
string  dt      pData,0         ; null terminated string table
doit    movlw   low(string)     ;
        movwf   PtrL            ;
        movlw   high(string)    ;
        movwf   PtrH            ;
        call    PutStr          ;
    endif

    if pType == nyb             ; ** note to self: fix this code **
        clrc                    ; RS = 0 (command)
        movlw   pData>>4        ; put b7..b4 bits in b3..b0
        call    PutNyb          ; send nybble from 8 bit mode
        DelayCy(tNyb)           ; delay 160 usecs
    endif

    if pType == pwm             ;
        nop                     ; future
    endif
        endm

;******************************************************************
;                                                                 *
;  LCD PutStr subroutine                                          *
;                                                                 *
PutStr
        call    GetTable        ; get a table character           |B0
        andlw   b'11111111'     ;                                 |B0
        skpnz                   ; 00 byte, last character?        |B0
        return                  ; yes, return, else               |B0
        call    PutDat          ; output character                |B0
        incf    PtrL,F          ; increment pointer               |B0
        skpnz                   ;                                 |B0
        incf    PtrH,F          ;                                 |B0
        goto    PutStr          ;                                 |B0
GetTable
        movf    PtrH,W          ;                                 |B0
        movwf   PCLATH          ;                                 |B0
        movf    PtrL,W          ;                                 |B0
        movwf   PCL             ;                                 |B0
I also use a small 12 word DelayCy() subsystem for the LCD write delay timing. Let me know if you'd like to see that.

You're welcome to send me a PM if you'd like updates as the code evolves.

Regards, Mike
 
Hi Mike,

Thanks for sharing the code - sure looks some fancy coding in there !

I will copy it over a give it a try on my own little dev board and let you know how it goes. ( my existing delays should modify to match your code ok)

thanks again

Richard
 
Richard,

You're welcome. Let me know if you find anything useful in that mess?

Mike

-------------

BTW, here's a hard coded low level driver for the "12F635 Serial LCD Interface". This would be the code produced from the conditional assembly instructions in that previous listing when you use the "Serial" option. Hopefully this code is a little easier to analyze for those people interested in playing with the interface.

Code:
;******************************************************************
;  K8LH low level 44780 LCD driver (12F635 Serial LCD Interface)  *
;******************************************************************

PutCmd                          ; entry point for "cmd" data
        clrc                    ; RS = 0 (command)                |B0
        skpnc                   ;                                 |B0
PutDat                          ; entry point for "dat" data
        setc                    ; RS = 1 (data)                   |B0
        movwf   Temp            ; save WREG data byte             |B0
        swapf   Temp,W          ;                                 |B0
        call    PutNyb          ; send left nybble                |B0
        movf    Temp,W          ; send right nybble               |B0
PutNyb
        andlw   0x0F            ; mask off left nybble            |B0
        skpnc                   ; RS = 0?  yes, skip, else        |B0
        iorlw   b'10000000'     ; set RS bit to '1'               |B0
        btfss   PIR1,TXIF       ; UART transmit buffer empty?     |B0
        goto    $-1             ; no, branch and wait, else       |B0
        movwf   TXREG           ; send it                         |B0
        return                  ;                                 |B0

;******************************************************************
 
Last edited:
My novelty 12F635 Serial LCD Interface is now working at 57600 baud.

It didn't come up on the first try but then I realized I had a 3.68% bit rate error when I setup the host 16F690 for 57600 baud using BRGH=1 and BRG16=0. There's also that 1% tolerance of the INTOSC to consider.

The interface did come right up however after switching to BRGH=1 and BRG16=1 on the 16F690. That configuration produces a much more tolerable 0.8% bit rate error.

Here's a picture of my LCD test board with 16F690 host driving the 12F635 interface at 57600 baud.

Mike
 

Attachments

  • 12F635 LCD 1.JPG
    12F635 LCD 1.JPG
    56.2 KB · Views: 234
Updated drawing and host low-level driver;

k8lh-12f635-lcd-interface-png.43792



Code:
;******************************************************************
;  K8LH low level 44780 LCD driver (12F635 Serial LCD Interface)  *
;******************************************************************
PutCmd                          ; entry point for "cmd" data
        clrc                    ; RS = 0 (command)                |B0
        skpnc                   ;                                 |B0
PutDat                          ; entry point for "dat" data
        setc                    ; RS = 1 (data)                   |B0
        movwf   Temp            ; save WREG data byte             |B0
        swapf   Temp,W          ;                                 |B0
        call    PutNyb          ; send left nybble                |B0
        movf    Temp,W          ; send right nybble               |B0
PutNyb
        andlw   0x0F            ; mask off left nybble            |B0
        skpnc                   ; RS = 0?  yes, skip, else        |B0
        iorlw   b'11000000'     ; set RS bit to '1'               |B0
        btfss   PIR1,TXIF       ; UART transmit buffer empty?     |B0
        goto    $-1             ; no, branch and wait, else       |B0
        movwf   TXREG           ; send it                         |B0
        return                  ;                                 |B0
;******************************************************************
 

Attachments

  • K8LH 12F635 LCD Interface.PNG
    K8LH 12F635 LCD Interface.PNG
    31.5 KB · Views: 598
Status
Not open for further replies.

Latest threads

Back
Top