• 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.

ST7066U 20x4 LCD problems(Solved)

I'm in the process of writing a program that works fine on an HD44780 2x16 LCD but transferring it to a ST7066U based 20x4 LCD is driving me nuts.
There's two problems but I will deal with one here and start another thread for the other once this one is sorted.

To explain the program first, it is a very simple controller for an SAA1057 based transmitter (old school stuff).

The first line on the LCD just displays the title and the second line displays "Freq: 87.00MHz" and the numbers increment/decrement as you turn a rotary encoder and sends the control data to the chip.
As I said above, works fine on a 2x16 LCD, but on the 20x4 LCD (after finally getting the inialisation sequence right), it will display the text, but the numbers remain static instead of incrementing/decrementing when the encoder is turned.

I'm 99.9% sure it is just a timing problem but for the life of me just can't see it, so hopefully a fresh set of eyes can help pinpoint it.

There is a Busy Check routine in the code below commented out as that is the second problem and I'm just using software delays at the moment.

Code:
;
; *******************************************************************************
; *    SAA1057 Tx controller 20x4 LCD                        *
; *    Version 0.01                                *
; *    February 2021                                *
; *    Author: T Mowles VK5TM                            *
; *    Copyright 2021 All Rights reserved.                    *
; *******************************************************************************
;
; *******************************************************************************
; *    Description:                                *
; *    SAA1057 FM transmitter controller software                *
; *******************************************************************************
;                                                                             
;    Target Controller - PIC16F628A
;                                 __________                                         
;    SPARE----------------RA2 |1       18| RA1----ENCODER A                   
;    SPARE----------------RA3 |2       17| RA0----ENCODER B                   
;    SPARE----------------RA4 |3       16| RA7----SPARE                       
;    SPARE----------------RA5 |4       15| RA6----SPARE                       
;    Ground---------------Vss |5       14| VDD----+5 V                       
;    LCD D4---------------RB0 |6       13| RB7----SAA1057 DLEN                   
;    LCD D5---------------RB1 |7       12| RB6----LCD_rs  (LCD Pin 4)         
;    LCD D6/1057_CLK------RB2 |8       11| RB5----LCD_rw  (LCD Pin 5)           
;    LCD D7/1057_DATA-----RB3 |9       10| RB4----LCD_e   (LCD Pin 6)         
;                                 ----------                                         
;                                                                             
; *******************************************************************************
; *    Device type and options.                        *
; *******************************************************************************
;
    processor 16F628A
    radix     dec
    errorlevel -302  ; Skip out of bank nuisance messages
;
; *******************************************************************************
; *    Configuration fuse information for 16F628A:                *
; *******************************************************************************

    include   <P16F628A.INC>

    __config _CP_OFF&_LVP_OFF&_BODEN_OFF&_MCLRE_OFF&_PWRTE_ON&_WDT_OFF& _INTOSC_OSC_NOCLKOUT 

;
; *******************************************************************************
; Info for power-up display                                                   
MCODE_REV_0  equ  'V'     ;
MCODE_REV_1  equ  '0'     ;
MCODE_REV_2  equ  '.'     ;
MCODE_REV_3  equ  '0'     ;
MCODE_REV_4  equ  '1'     ;
MCODE_REV_5  equ  ' '     ;
MCODE_REV_6  equ  ' '     ;
MCODE_REV_7  equ  ' '     ;
;                                                                             
; *******************************************************************************
; *    General equates                                *
; *******************************************************************************
;
; default 0 & 1 contains the default startup frequency as a 16 bit integer.
; control 0 & 1 contains the control word data as a 16 bit integer.
;
default_1 equ 0x21        ; Most significant byte
default_0 equ 0xFC        ; Least significant byte (87MHz)

control_1 equ b'11000101'    ; Control_1 data 11000101 (MSB)
control_0 equ b'11110101'    ; Control_0 data 11110101 (LSB)
;
; *******************************************************************************
; *    Setup the initial constants                        *
; *******************************************************************************
;
    ORG    0x2100
;
;    startup frequency bytes must be next 4 bytes of EEPROM
    DATA    default_1    ; startup -> MSB
    DATA    default_0    ; startup -> LSB
;
;       Clear unused EEPROM bytes (128 bytes for 16F628)
;
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0           
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0           
;
; *******************************************************************************
; *    Assign names to IO pins                            *
; *******************************************************************************
;
;    B register bits:
;
CLK        equ    0x02    ; SAA1057 write clock
DAT        equ    0x03    ; SAA1057 serial data input
LCD_busy    equ    0x03    ; LCD busy bit
LCD_e        equ    0x04    ; 0=disable, 1=enable
LCD_rw        equ    0x05    ; 0=write, 1=read
LCD_rs        equ    0x06    ; 0=instruction, 1=data
DLEN        equ    0x07    ; SAA1057 Data load control pin - high to load data
;
; *******************************************************************************
; *    Allocate variables in general purpose register space            *
; *******************************************************************************
;
    CBLOCK    0x20        ; Start Data Block
    bit_count        ;   
    byte2send        ;
    LCD_char        ; Character being sent to the LCD
    timer1            ; Used in delay routines
    CounterA        ;   "
    CounterB        ;   "
    CounterC        ;   "
    CounterD        ;   "
    CounterE        ;   "
    ren_new            ; New value of encoder pins A and B
    ren_old            ; Old value of encoder pins A and B
    ren_read        ; Encoder pins A and B and switch pin
    dir            ; Indicates last direction of encoder
    next_dir        ; Indicates expected direction
    count            ; loop counter  (gets reused)

    enc_counter        ; Divide by 4 counter for mechanical encoder

    rs_value        ; The LCD rs line flag value
    hcomp
    lcomp
    NumH
    NumL
    TenK
    Thou
    Hund
    Tens
    Ones

    ENDC            ; End of Data Block

    CBLOCK  0x70
    freq_1            ; Use bank common locations - saves bank switching when
        freq_0            ; saving/retreiving to/from EEPROM
    ENDC            ; End of Data Block
;
; *******************************************************************************
; * The 16F628 resets to 0x00                            *
; * The Interrupt vector is at 0x04                        *
; *******************************************************************************
;
    ORG    0x0000               
    goto    start        ; Jump around the band table to main program

    ORG    0x0004    ;
    goto    start
;
; *******************************************************************************
; * Purpose:                                    *
; *    This is the start of the program                    *
; *                                        *
; *******************************************************************************
;
start
    movlw    0x07        ; Code to turn off the analog comparitors
    movwf    CMCON        ; Turn off comparators
    clrf    PORTA
    clrf    PORTB
    bsf    STATUS,RP0    ; Switch to bank 1
    bsf    OPTION_REG,7    ; Disable weak pullups
    movlw    0xFF        ; Tristate PORTA (all Inputs)
    movwf    TRISA        ;
    clrf    TRISB        ; Set port B to all outputs
    bcf    STATUS,RP0    ; Switch back to bank 0

    bcf    PORTB,DLEN    ; Hold DLEN low

    movlw    control_1    ; send start sequence or control word (twice) to SAA1057
    movwf    freq_1
    movlw    control_0
    movwf    freq_0
    call    send_word    ; Send it to the SAA1057
    call    send_word    ; twice to initialise chip
;
    call    init_LCD    ; Initialize the LCD
    call    display_version    ; Display title and version
;
;       Set the power on frequency to the defined value
;
read_EEocs
    call    LINE2
    bsf    STATUS,RP0    ; Switch to bank 1
    clrf    EEADR        ; Reset the EEPROM read address
    call    read_EEPROM    ; Read EEPROM
    movf    EEDATA,w    ; Get the first default freq byte
    movwf    freq_1        ; Save it
    call    read_EEPROM    ; Read EEPROM
    movf    EEDATA,w    ; Get the next freq byte
    movwf    freq_0        ; Save it
    bcf    STATUS,RP0    ; Back to bank 0

;    Display the power on frequency
;
    call    bin2BCD        ; Convert it to BCD
    call    show_freq    ; Display it
;
    call    send_word    ; Send the power-on frequency to the
                ; SAA1057 in serial mode
;
;    Get the power on encoder value
;
    movf    PORTA,w        ; Read port A
    movwf    ren_read    ; Save it in ren_read
    movlw    0x03        ; Get encoder mask (RA0 and RA1)
    andwf    ren_read,w    ; Get encoder bits
    movwf    ren_old        ; Save in ren_old
;
    clrf    dir        ; Clear the knob direction indicator
;
; Fall into the Main Program Loop
;
; *******************************************************************************
; * Purpose:                                    *
; *    This is the Main Program Loop. The program's main loop            *
; *    calls poll_encoder, which continuously polls the rotary shaft        *
; *    encoder.  When the shaft encoder has changed, the direction        *
; *    it moved is determined and stored in last_dir. The subroutine        *
; *    then returns to main.                            *                       
; *                                        *
; *******************************************************************************
;
main
        call    poll_encoder    ; Check for knob movement (wait there!)     
                ; Return here when encoder change detected
;
;    Based on the knob direction, either increment or decrement the frequency,
;    update the LCD and SAA1057.
;
    btfsc    dir,1        ; Is the knob going up?
    goto    up        ; Yes, then increase frequency
down
    call    dec_freq    ;
    goto    update        ; Update LCD and DDS                         
up
    call    inc_freq    ;

update
    call    bin2BCD        ; Convert the frequency to BCD
    call    show_freq    ; Display the frequency on the LCD
    call    send_word    ; Send the control word to the chip
    goto    main        ; Continue main loop
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  This routine does the following:                                *
; *                                                  *
; *           Reads the encoder bits until a change is detected, then         *
; *           determines the direction the knob was moved.                *
; *                                                                           *
; *   Input:  Knob input read from port A                                     *
; *           ren_old -> the last encoder bits read                           *
; *           last_dir -> the last direction moved                            *
; *                                                                           *
; *  Output:  ren_new -> the current encoder bits                             *
; *           last_dir -> the last direction (0 = down, 2 = up)               *
; *                                                                           *
; *****************************************************************************
;
poll_encoder

no_inc                ;
    movf    PORTA,w        ; Get the current encoder value
    movwf    ren_read    ; Save it

;!!!--- DETENT_ENCODER --- ADDED CODE FOR MECHANICAL ENCODER ----- !!!
    call    wait_1ms    ; debounce time
    movf    PORTA,w        ; read the port again
    xorwf    ren_read,w    ; Compare with previous value
    btfss    STATUS,Z    ; Are they equal?
    goto    poll_encoder    ; Poll again if not
;!!! ------------------------------------------------------------- !!!

    movlw    0x03        ; Get encoder mask (to isolate RA0 and RA1)
    andwf    ren_read,w    ; Isolated encoder bits into W
    movwf    ren_new        ; Save new value
    xorwf    ren_old,w    ; Has it changed?
    btfsc    STATUS,Z    ; Check zero-flag (zero if no change)
    goto    poll_encoder    ; No change, keep looking until it changes
;                ; Zero-flag is not set, so continue on

    bcf    STATUS,C    ; Clear the carry bit to prepare for rotate
    rlf    ren_old,f    ; Rotate old bits left to align "Right-Bit"
    movf    ren_new,w    ; Set up new bits in W
    xorwf    ren_old,f    ; XOR old (left shifted) with new bits
    movf    ren_old,w    ; Put XOR results into W also
    andlw    0x02        ; Mask to look at only "Left-Bit" of pair
    movwf    next_dir    ; Save result (in W) as direction (bit=UP)
    xorwf    dir,w        ; See if direction is same as before

pe_continue
    clrf    dir        ; Clear last_dir (default is DN)
    btfsc    ren_old,1    ; Are we going UP?
    goto    up2        ; No, exit3

;!!! --- DETENT_ENCODER --- ADDED CODE FOR MECHANICAL ENCODER ----- !!!
    movf    ren_new,w    ; Get the current encoder bits
    movwf    ren_old        ; Save them in ren_old for the next time
    decf    enc_counter,f    ; decrement the inter-detent counter
    btfsc    enc_counter,0    ; Check if bit 0 is cleared
    goto    poll_encoder    ; If not, then poll some more
    btfsc    enc_counter,1    ; Else, check if bit 1 is cleared
    goto    poll_encoder    ; If not, then poll some more
                ; Else, assume the encoder is on a detent
;!!! -------------------------------------------------------------- !!!
    goto    exit3
                                
up2
    movlw    0x02        ; Get UP value
    movwf    dir        ; and set in last_dir

;!!! --- DETENT_ENCODER --- ADDED CODE FOR MECHANICAL ENCODER ----- !!!
    movf    ren_new,w    ; Get the current encoder bits
    movwf    ren_old        ; Save them in ren_old for the next time
    incf    enc_counter,f    ; Increment the encoder counter
    btfsc    enc_counter,0    ; Return only on modulo 4 counts
    goto    poll_encoder    ;
    btfsc    enc_counter,1
    goto    poll_encoder    ; Loop again if not modulo 4
;!!! -------------------------------------------------------------- !!!
                    
exit3
    movf    ren_new,w    ; Get the current encoder bits
    movwf    ren_old        ; Save them in ren_old for the next time
    return            ; Return to the caller
;
; *******************************************************************************
; * Purpose:                                    *
; *    Increment frequency    16 bit inc/dec From Scott Dattalo        *
; *                                        *
; *******************************************************************************
;
inc_freq
    incf    freq_1,f
    incfsz    freq_0,f
    decf    freq_1,f

    MOVLW    low 10800    ; Initialize a 16 bit value
    MOVWF    lcomp
    MOVLW    high 10800
    MOVWF    hcomp
    call    COMP        ; Test if frequency is above 108MHz

    addlw    0x00        ; Test if 'w'is 0 or 1
    btfsc    STATUS,Z    ; The Z or Zero bit is set to "1" when the result
    return            ; of an arithmetic or logical operation is zero
    
    MOVLW    low 10800    ; Initialize a 16 bit value
    MOVWF    freq_0
    MOVLW    high 10800
    MOVWF    freq_1
    return
;
; *******************************************************************************
; * Purpose:                                    *
; *    Decrement the freq, checking that it does not go below lowest freq    *
; *                                        *
; *******************************************************************************
;
dec_freq
    movf    freq_0,f
    skpnz
    decf    freq_1,f
    decf    freq_0,f

    MOVLW    low 8700    ; Initialize a 16 bit value
    MOVWF    lcomp
    MOVLW    high 8700
    MOVWF    hcomp
    call    COMP        ; Test if frequency is below 87MHz

    addlw    0x00        ; Test if 'w'is 0 or 1
    btfss    STATUS,Z    ; The Z or Zero bit is set to "1" when the result
    return            ; of an arithmetic or logical operation is zero
    
    MOVLW    low 8700    ; Initialize a 16 bit value
    MOVWF    freq_0
    MOVLW    high 8700
    MOVWF    freq_1
    return
;
; *******************************************************************************
; * Purpose:                                    *
; *    Check if freq exceeds upper or lower limit                *
; *                                        *
; *    A not too optimised 16 bit compare routine for 16bit absolute values,    *
; *    ie 0 -> 65536.    Antonio L Benci (PIClist)                *
; *    Compare WORD to COMP (a word value).                    *
; *    If WORD = COMP return with 00                        *
; *    If WORD > COMP return with 01                        *
; *    If WORD < COMP return with 00                        *                   
; *******************************************************************************
;
COMP
    movfw    hcomp        ; get high byte of comp value
    subwf    freq_1,w    ; subtract values
    btfsc    STATUS,Z    ; first check if result is 0
    goto    COMPL        ; if zero compare low bytes
    btfsc    STATUS,C    ; else test carry bit
    retlw    0x01        ; if WORD > COMP, return with 01h
    retlw    0x00        ; if WORD < COMP, return with 00h

COMPL
    movfw    lcomp        ; get low byte of comp value
    subwf    freq_0,w    ; subtract values
    btfsc    STATUS,Z    ; first check if result is 0
    retlw    0x00        ; if result is 0, return with 00
    btfsc    STATUS,C    ; if c set then WORD > COMP
    retlw    0x01        ; if WORD > COMP, return with 01h
    retlw    0x00        ; if WORD < COMP, return with 00h
;
; *******************************************************************************
; * Purpose:                                    *
; *    This routine sends data to the SAA1057 using serial data transfer mode    *
; *                                        *
; *    To send data:-                                *
; *      Set Clock low                                *
; *                                        *
; *        Set DLEN high                                *
; *                                        *
; *        Set Data low                                *
; *                                        *
; *        Send clock bit ->high->low                        *
; *                                        *
; *    1 Get Data bit from file                        *
; *                                        *
; *        Send clock bit ->high->low                        *
; *                                        *
; *        Repeat from 1 for remaining bits                    *
; *                                        *
; *        Set DLEN low                                *
; *                                        *
; *        Send clock bit ->high->low                        *
; *                                        *
; *      End of sequence                                *
; *                                        *
; *******************************************************************************
;
send_word
    bcf    PORTB,CLK    ; Set clock low
    NOP
    bsf    PORTB,DLEN    ; Set data load high
    NOP
    bcf    PORTB,DAT    ; Set Data low
    NOP
    bsf    PORTB,CLK    ; Toggle write clock
    NOP
    bcf    PORTB,CLK    ;
    
    movlw    freq_1        ; Point FSR at most Significant Byte
    movwf    FSR        ;
next_byte
    movf    INDF,w        ;
    movwf    byte2send    ;
    movlw    0x08        ; Set counter to 8
    movwf    bit_count    ;
next_bit
    rlf    byte2send,f    ; Test if next bit is 1 or 0
    btfss    STATUS,C    ; Was it zero?
    goto    send0        ; Yes, send zero
    bsf    PORTB,DAT    ; No, send one
    goto    break        ;
send0
    bcf    PORTB,DAT    ; Send zero
    NOP

break
    bsf    PORTB,CLK    ; Toggle write clock
    NOP
    bcf    PORTB,CLK    ;
    decfsz    bit_count,f    ; Has the whole byte been sent?
    goto    next_bit    ; No, keep going
    incf    FSR,f        ; Start the next byte unless finished
    movlw    freq_0+1    ; Next byte (past the end)
    subwf    FSR,w        ;
    btfss    STATUS,C    ;
    goto    next_byte    ;
    bcf    PORTB,DLEN    ; Send load signal to the SAA1057
    NOP
    bsf    PORTB,CLK    ; Toggle write clock
    NOP
    bcf    PORTB,CLK    ;
    return
;
; *******************************************************************************
; * Purpose:                                    *
; *    Power on initialization of Liquid Crystal Display            *
; *    The LCD controller chip must be equivalent to an Hitachi 44780        *
; *                                        *
; *******************************************************************************
;
init_LCD
    movlw    100
    call    wait        ; Wait for LCD to power up
;    call    wait_1ms
;    Put 4-bit command in RB3..RB0                                         
;    PIC's RB3..RB0 lines connect to LCD's DB7..DB4 (pins 14-11)           
    movlw    0x03        ; LCD init instruction (First)               
    movwf    PORTB        ; Send to LCD via RB3..RB0                   
    bsf    PORTB,LCD_e    ; Set the LCD E line high,
    call    wait_1ms
    bcf    PORTB,LCD_e    ; and then Clear E

    movlw    100
    call    wait        ; wait a "long" time,

    movlw    0x03        ; LCD init instruction (Second)             
    movwf    PORTB        ; Send to LCD via RB3..RB0                   
    bsf    PORTB,LCD_e    ; Set E high,
    call    wait_1ms
    bcf    PORTB,LCD_e    ; and then Clear E

    movlw    32
    call    wait        ; wait a while,

    movlw    0x03        ; LCD init instruction (Third)               
    movwf    PORTB        ; Send to LCD via RB3..RB0                   
    bsf    PORTB,LCD_e    ; Set E high,
    call    wait_1ms
    bcf    PORTB,LCD_e    ; and then Clear E

    movlw    32
    call    wait        ; wait a while,

    movlw    0x02        ; 4-bit mode instruction                     
    movwf    PORTB        ; Send to LCD via RB3..RB0                   
    bsf    PORTB,LCD_e    ; Set E high,
    call    wait_1ms
    bcf    PORTB,LCD_e    ; and then Clear E

    movlw    16
    call     wait        ; wait a while,

    movlw    0x28        ; 1/16 duty cycle, 5x8 matrix
    call    cmnd2LCD    ; Send command in w to LCD

    call    wait_1ms

    movlw    0x08        ; Display off, cursor and blink off
    call    cmnd2LCD    ; Send command to LCD

    call    wait_1ms

    movlw    0x01        ; Clear and reset cursor
    call    cmnd2LCD    ; Send command in w to LCD

    movlw    2
    call     wait        ; wait a while,

    movlw    0x06        ; Set cursor to move right, no shift
    call    cmnd2LCD    ; Send command in w to LCD

    call    wait_1ms

    movlw    0x0C        ; Display on, cursor and blink off
    call    cmnd2LCD    ; Send command in w to LCD

    return            ;
;
; ****************************************************************************P
; *                                                                           P
; * Purpose:  Display version and other info on LCD for 2 seconds             P
; *           upon power-up                                                   P
; *                                                                           P
; *   Input:  MCODE_REV_0 through MCODE_REV_4 set up                          P
; *                                                                           P
; *  Output:  LCD displays debug info                                         P
; *                                                                           P
; ****************************************************************************P
;
display_version
    movlw    0x80        ; Point LCD at digit 1               
    call    cmnd2LCD    ;
    movlw    'S'        ; digit 1             
    call    data2LCD    ;
    movlw    'A'        ; digit 2                 
    call    data2LCD    ;
    movlw    'A'        ; digit 3               
    call    data2LCD    ;
    movlw    '1'        ; digit 4               
    call    data2LCD    ;
    movlw    '0'        ; digit 5             
    call    data2LCD    ;       
    movlw    '5'        ; digit 6             
    call    data2LCD    ;
    movlw    '7'        ; digit 7               
    call    data2LCD    ;
    movlw    ' '        ; digit 8               
    call    data2LCD    ;
    movlw    MCODE_REV_0    ; Get mcode rev byte
    call    data2LCD    ; and display it (digit 9)
    movlw    MCODE_REV_1    ; Get mcode rev byte
    call    data2LCD    ; and display it (digit 10)
    movlw    MCODE_REV_2    ; Get mcode rev byte
    call    data2LCD    ; and display it (digit 11)
    movlw    MCODE_REV_3    ; Get mcode rev byte
    call    data2LCD    ; and display it (digit 12)
    movlw    MCODE_REV_4    ; Get mcode rev byte
    call    data2LCD    ; and display it (digit 13)
    movlw    MCODE_REV_5    ; Get mcode rev byte
    call    data2LCD    ; and display it (digit 14)
    movlw    MCODE_REV_6    ; Get mcode rev byte
    call    data2LCD    ; and display it (digit 15)
    movlw    MCODE_REV_7    ; Get mcode rev byte
    call    data2LCD    ; and display it (digit 16)

    call    wait_a_sec    ; Wait one second

LINE2
    movlw    0xC0        ; Point LCD at digit 1 of second line
    call    cmnd2LCD    ;
    movlw    'F'        ; digit 1
    call    data2LCD    ;
    movlw    'R'        ; digit 2
    call    data2LCD    ;
    movlw    'E'        ; digit 3
    call    data2LCD    ;
    movlw    'Q'        ; digit 4
    call    data2LCD    ;
    movlw    ':'        ; digit 5
    call    data2LCD    ;
    movlw    ' '        ; digit 6 0xC5
    call    data2LCD    ;
    movlw    ' '        ; digit 7
    call    data2LCD    ;
    movlw    ' '        ; digit 8
    call    data2LCD    ;
    movlw    ' '        ; digit 9
    call    data2LCD    ;
    movlw    ' '        ; digit 10
    call    data2LCD    ;
    movlw    ' '        ; digit 11
    call    data2LCD    ;
    movlw    'M'        ; digit 12
    call    data2LCD    ;
    movlw    'H'        ; digit 13
    call    data2LCD    ;
    movlw    'z'        ; digit 14
    call    data2LCD    ;
    movlw    ' '        ; digit 15
    call    data2LCD    ;
    movlw    ' '        ; digit 16
    call    data2LCD    ;
    return


; *******************************************************************************
; * Purpose:                                    *
; *    This subroutine converts a 16 bit binary number to decimal in       *
; *    TenK, Thou, Hund, Tens, Ones                        *
; *    Written by John Payson.                            *
; *******************************************************************************
;
bin2BCD    ; Takes number in freq_1:freq_0 returns decimal in TenK:Thou:Hund:Tens:Ones
    movfw    freq_1
    movwf    NumH
    movfw    freq_0
    movwf    NumL

    swapf    NumH,w
    IORLW    0xF0
    movwf    Thou
    addwf    Thou,f
    addlw    0xE2
    movwf    Hund
    addlw    0x32
    movwf    Ones

    movf    NumH,w
    andlw    0x0F
    addwf    Hund,f
    addwf    Hund,f
    addwf    Ones,f
    addlw    0xE9
    movwf    Tens
    addwf    Tens,f
    addwf    Tens,f

    swapf    NumL,w
    andlw    0x0F
    addwf    Tens,f
    addwf    Ones,f

    rlf    Tens,f
    rlf    Ones,f
    comf    Ones,f
    rlf    Ones,f

    movf    NumL,w
    andlw    0x0F
    addwf    Ones,f
    rlf    Thou,f

    movlw    0x07
    movwf    TenK

; At this point, the original number is equal to TenK*10000+Thou*1000+Hund*100+Tens*10+Ones
; if those entities are regarded as two's compliment binary.
; To be precise, all of them are negative except TenK. 
; Now the number needs to be normalized, but this can all be done with simple byte arithmetic.

    movlw    0x0A        ; Ten
Lb1:
    addwf    Ones,f
    decf    Tens,f
    btfss    STATUS,C
    goto    Lb1
Lb2:
    addwf    Tens,f
    decf    Hund,f
    btfss    STATUS,C
    goto    Lb2
Lb3:
    addwf    Hund,f
    decf    Thou,f
    btfss    STATUS,C
    goto    Lb3
Lb4:
    addwf    Thou,f
    decf    TenK,f
    btfss    STATUS,C
    goto    Lb4

    return
;
; *******************************************************************************
; * Purpose:                                    *
; *    Display the frequency on the LCD.                    *
; *                                        *
; *******************************************************************************
;
show_freq
    movlw    0xC5        ; Point the LCD to digit location
    call    cmnd2LCD    ; Send digit location to LCD
;
    movfw    TenK
    addlw    0x00        ; Test if 'w'is 0 or 1
    btfss    STATUS,Z    ; The Z or Zero bit is set to "1" when the result
    goto    $+4        ; of an arithmetic or logical operation is zero

    movlw    ' '        ; TenK = 0, insert space
    call    data2LCD    ; Send byte in W to LCD
    goto    $+2

;    movfw    TenK        ; TenK is not 0, insert number
    call    process        ;

    movfw    Thou
    call    process        ;

    movfw    Hund
    call    process        ;
 
    movlw    '.'        ; insert seperator
    call    data2LCD    ; Send byte in W to LCD

    movfw    Tens
    call    process        ;

    movfw    Ones
    call    process        ;

    return
process
    addlw    0x30
    call    data2LCD
    return
;
; *******************************************************************************
; * Purpose:                                    *
; *    Send Command or Data byte to the LCD                    *
; *    Entry point cmnd2LCD: Send a Command to the LCD                *
; *    Entry Point data2LCD: Send a Data byte to the LCD            *
; *                                        *
; *******************************************************************************
;
cmnd2LCD   ; ****** Entry point ******
    clrf    rs_value        ; Remember to clear RS  (clear rs_value)   
    goto    write2LCD        ; Go to common code
data2LCD   ; ****** Entry point ********
    bsf    rs_value,0        ; Remember to set RS (set bit 0 of rs_value)
write2LCD
    movwf    LCD_char        ; Save byte to write to LCD
;    call    busy_check        ; Check to see if LCD is ready for new data
    call    wait_1ms
    MOVLW    2            ;
    MOVWF    count            ; SET UP LOOP COUNTER
write_again
    SWAPF    LCD_char,F        ; SWAP MS & LS NIBBLES
    MOVF    LCD_char,W
    ANDLW    15            ; MAKE TOP 4 BITS OF W 0
    MOVWF    PORTB            ; SEND MS NIBBLE
    bcf    PORTB,LCD_rs        ; Guess RS should be clear - command mode
    btfsc    rs_value,0        ; Should RS be clear?  (is bit 0 == 0?)
    bsf    PORTB,LCD_rs        ; No, set RS - data mode
    NOP
    BSF    PORTB,LCD_e        ; SET E HIGH
    NOP
    BCF    PORTB,LCD_e        ; SET E LOW
    DECFSZ    count,F
    GOTO    write_again        ; GO BACK AND SEND LS NIBBLE
    return
;
; *******************************************************************************
; * Purpose:                                    *
; *    Check if LCD is done with the last operation.                *
; *    This subroutine polls the LCD busy flag to determine if previous    *
; *    operations are completed.                        *
; *                                        *
; *******************************************************************************
;
;busy_check
;    clrf    PORTB        ; Clear all outputs on PORTB
;    banksel    TRISB        ; Switch to bank for Tris operation
;    movlw    b'00001000'    ; Set RB3 input, others outputs
;    movwf    TRISB        ;
;
;    banksel    PORTB
;    bcf    PORTB,LCD_rs    ; Set up LCD for Read Busy Flag (RS = 0)
;    nop
;    bsf    PORTB,LCD_rw    ; Set up LCD for Read (RW = 1)
;    nop            ;
;    bsf    PORTB,LCD_e    ; Set E high
;    nop
;    bcf    PORTB,LCD_e    ; Drop E again
;
;LCD_is_busy
;    call    wait_100us    ;
;    bsf    PORTB,LCD_e    ; Set E high
;    nop
;    bcf    PORTB,LCD_e    ; Drop E again
;
;    btfss    PORTB,LCD_busy    ; Is Busy Flag (RB3) clear?
;    goto    not_busy    ; Yes - exit
;    nop            ; No
;    nop            ; Wait a while
;
;    bsf    PORTB,LCD_e    ; Pulse E high (dummy read of lower nibble),
;    nop            ; wait,
;    bcf    PORTB,LCD_e    ; and drop E again
;
;    goto    LCD_is_busy    ; If not, it is busy so jump back
;
;not_busy
;    banksel    TRISB        ; Switch to bank 1 for Tristate operation
;    clrf    TRISB        ; All pins (RB7..RB0) are back to outputs
;    banksel    PORTB        ; Switch to bank 0
;    clrf    PORTB        ; Clear all of Port B (inputs and outputs)
;    return
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  Read a byte of EEPROM data at address EEADR into EEDATA.        *
; *                                                                           *
; *   Input:  The address EEADR.                                              *
; *                                                                           *
; *  Output:  The value in EEDATA.                                            *
; *                                                                           *
; *  NOTE:  All in BANK 1                                                     *
; *****************************************************************************
;
read_EEPROM

    bsf    EECON1,RD    ; Request the read
    movf    EEDATA,W    ; Get the data
    incf    EEADR,f        ; Increment the read address
    return            ; Return to the caller
;
; *******************************************************************************
; *                                        *
; * Purpose:  Delay routines                            *
; *                                        *
; *******************************************************************************
;
wait_a_sec ;PIC Time Delay = 0.99999900 s with Osc = 4000000 Hz
    movlw    D'6'
    movwf    CounterC
    movlw    D'19'
    movwf    CounterB
    movlw    D'172'
    movwf    CounterA
loop
    decfsz    CounterA,f
    goto    loop
    decfsz    CounterB,f
    goto    loop
    decfsz    CounterC,f
    goto    loop
    return

wait
    movwf    timer1        ; Set up counter
    call    wait_1ms    ; Go to wait loops
    decfsz    timer1,f
    goto    $-2
    return

wait_1ms     ;PIC Time Delay = 0.00099800 s with Osc = 4MHz
    movlw    D'2'
    movwf    CounterD
    movlw    D'73'
    movwf    CounterE
loop1
    decfsz    CounterE,f
    goto    loop1
    decfsz    CounterD,f
    goto    loop1
    return

wait_100us    ;PIC Time Delay = 0.00009800 s with Osc = 4000000 Hz
    movlw    D'31'
    movwf    CounterA
loop2
    decfsz    CounterA,1
    goto    loop2
    return
;       
; *****************************************************************************
;
    END
 

fourtytwo

Active Member
There's a few things to check that have got me in the past, E clock width, setup & hold wrt E AND busy time allowance, remember these things have RC clocks so there's a wide variation on busy time and it's instruction dependent. I hope your not changing your MPU clock between devices, I don't see it clearly defined ?
 

Nigel Goodwin

Super Moderator
Most Helpful Member
There's a few things to check that have got me in the past, E clock width, setup & hold wrt E AND busy time allowance, remember these things have RC clocks so there's a wide variation on busy time and it's instruction dependent. I hope your not changing your MPU clock between devices, I don't see it clearly defined ?
As far as I recall the 628 has fairly limited oscillator options? (compared with modern devices) - looks like it's set to the internal 4MHz oscillator.
 
16F628A only has one internal clock frequency of 4MHz which is set in the Config set-up (the 16F628 won't be the final PIC used, just using it for development as I have plenty of them).

I did put 1mS delays in between setting and clearing E in the routine that sends data to the LCD but it made no difference (they are there in the LCD init sequence).

So can't be that.

Just weird that it works fine on a 2x16 HD44780 display but not the ST7066U display, think I might have to find a '44780 based 20x4 to see if it works on that.
 

JimB

Super Moderator
Most Helpful Member
As I said above, works fine on a 2x16 LCD, but on the 20x4 LCD (after finally getting the inialisation sequence right), it will display the text, but the numbers remain static instead of incrementing/decrementing when the encoder is turned.
Some of this sounds familiar.
A couple of years ago I was given a 4x20 LCD display to use on a project.
I had started the project using ?? a 2x16 maybe, and the display worked OK.
But when I migrated it to the 4x20, it all went bad.
I could write the data to the 4x20 and it would display correctly only once, it could not be updated.
Switch everything off and restart and again it would initially display correctly but not update.

The fix was to use a good 4x20 display from a good source, that display worked perfectly and updated correctly, no messing about.

So, my advice is to try a new 4x20 display from the likes of RS or Farnell, not a cheap one from ???

JimB
 
Now this just gets weirder.

Found a genuine HD44780 20x4 (old enough it actually has IC's and not black blobs on the back) that does exactly the same thing, so it's not a timing issue.

Time to put it down for a few days to let the brain refocus.
 
Just a quick update - not solved, but I tried several 'hardware' things just in case the display didn't like floating pins.

Hooked the display up to an Arduino and ran the example "Hello World" file and messed about shifting text around and of course it all worked fine.

Tried various combinations of delays (increased and decreased) in various places and it made no difference.

It's almost as if the display goes to sleep once it has got the start up text and wont accept any further commands.
 

fourtytwo

Active Member
It's almost as if the display goes to sleep once it has got the start up text and wont accept any further commands.
Do you mean some text printed on the display or just the init sequence ?
Assuming the former then there might be something at the tail of that string upsetting it OR your "further command" code is doing something different. Maybe you need to build small bricks at a time like only use one new piece of code at a time. In particular check your addressing, it is easy to get that wrong and then nothing appears to work as your writing to a non-display area.
 
It does the init sequence and then prints a string of text on the first line (basically a title and version #) and on the second line prints "Freq: 87.00MHz", that is what happens at first power up.

What is supposed to happen then, is with every increment of the rotary encoder, it should increment the frequency display ie 87.01, 87.02 and so on and the reverse if the encoder is turned backwards.

This all happens fine with a 16x2 LCD but with the 20x4 it won't update the frequency on the display and the only thing changed is the display, the code remains the same.
 
The 'Enable' lines for each ('E' for LCD & 'DLEN' for SAA1057) are on non-shared pins.

I had thought about this being a possible problem and have tried the software with the SAA1057 routines disabled and it makes no difference.
 

Pommie

Well-Known Member
Most Helpful Member
Can I suggest you remove all non LCD code and put it in a loop writing an incrementing value to each line in succession. Are you able to compile a test C program?

Mike.
 
Bit scrappy but did what it should, stepped through the character set, displaying on each line in turn (and yes, did it in Assembler).

I'll start re-inserting other blocks of code starting with the BCD routine to see where it has a hernia -Thanks Mike.

C-like:
;
; *******************************************************************************
; *    LCD test program                            *
; *    Version 0.01                                *
; *    March 2021                                *
; *    Author: T Mowles VK5TM                            *
; *******************************************************************************
;
;                                                                            
;    Target Controller - PIC16F628A
;                        __________                                        
;    SPARE-------RA2 |1       18| RA1----ENCODER A                  
;    SPARE-------RA3 |2       17| RA0----ENCODER B                  
;    SPARE-------RA4 |3       16| RA7----SPARE                      
;    SPARE-------RA5 |4       15| RA6----SPARE                      
;    Ground------Vss |5       14| VDD----+5 V                      
;    LCD D4------RB0 |6       13| RB7----SPARE                  
;    LCD D5------RB1 |7       12| RB6----LCD_rs  (LCD Pin 4)        
;    LCD D6------RB2 |8       11| RB5----LCD_rw  (LCD Pin 5)          
;    LCD D7------RB3 |9       10| RB4----LCD_e   (LCD Pin 6)        
;                        ----------                                        
;                                                                            
; *******************************************************************************
; *    Device type and options.                        *
; *******************************************************************************
;
    processor 16F628A
    radix     dec
    errorlevel -302  ; Skip out of bank nuisance messages
;
; *******************************************************************************
; *    Configuration fuse information for 16F628A:                *
; *******************************************************************************
;
    include   <P16F628A.INC>

    __config _CP_OFF&_LVP_OFF&_BODEN_OFF&_MCLRE_OFF&_PWRTE_ON&_WDT_OFF& _INTOSC_OSC_NOCLKOUT
;
; *******************************************************************************
; *    Assign names to IO pins                            *
; *******************************************************************************
;
;    B register bits:
;
LCD_busy    equ    0x03    ; LCD busy bit
LCD_e        equ    0x04    ; 0=disable, 1=enable
LCD_rw        equ    0x05    ; 0=write, 1=read
LCD_rs        equ    0x06    ; 0=instruction, 1=data
;
; *******************************************************************************
; *    Allocate variables in general purpose register space            *
; *******************************************************************************
;
    CBLOCK    0x20        ; Start Data Block
    LCD_char        ; Character being sent to the LCD
    timer1            ; Used in delay routines
    CounterA        ;   "
    CounterB        ;   "
    CounterC        ;   "
    CounterD        ;   "
    CounterE        ;   "
    count            ; loop counter  (gets reused)
    rs_value        ; The LCD rs line flag value
    counter

    ENDC            ; End of Data Block
;
; *******************************************************************************
; * The 16F628 resets to 0x00                            *
; * The Interrupt vector is at 0x04                        *
; *******************************************************************************
;
    ORG    0x0000              
    goto    start        ;

    ORG    0x0004    ;
    goto    start
;
; *******************************************************************************
; * Purpose:                                    *
; *    This is the start of the program                    *
; *                                        *
; *******************************************************************************
;
start
    clrf    PORTA
    clrf    PORTB
    movlw    0x07        ; Code to turn off the analog comparators
    movwf    CMCON        ; Turn off comparators

    banksel    TRISA        ; Switch to bank 1
    movlw    0xFF        ; Tristate PORTA (all Inputs)
    movwf    TRISA        ;
    clrf    TRISB        ; Set port B to all outputs
    banksel    PORTB        ; Switch back to bank 0
    clrf    PORTB

    call    init_LCD    ; Initialize the LCD
    call    wait_250
;
main
    clrf    counter
loopA
    movlw    0x80        ; Line 1
    call    cmnd2LCD
    movfw    counter
    call    process
    call    wait_250

    movlw    0xC0        ; Line 2
    call    cmnd2LCD
    movfw    counter
    call    process
    call    wait_250

    movlw    0x94        ; Line 3
    call    cmnd2LCD
    movfw    counter
    call    process
    call    wait_250

    movlw    0xD4        ; Line 4
    call    cmnd2LCD
    movfw    counter
    call    process
    call    wait_250

    movfw    counter
    xorlw    255
    btfsc    STATUS,Z
    goto    reset_counter
    incf    counter
    goto    loopA

reset_counter            ;if counter = 255 reset counter
    clrf    counter
    goto    loopA

process
    addlw    0x30
    call    data2LCD
    return
;
; *******************************************************************************
; * Purpose:                                    *
; *    Power on initialization of Liquid Crystal Display            *
; *    The LCD controller chip must be equivalent to an Hitachi 44780        *
; *                                        *
; *******************************************************************************
;
init_LCD
    movlw    100
    call    wait        ; Wait for LCD to power up

;    Put 4-bit command in RB3..RB0                                        
;    PIC's RB3..RB0 lines connect to LCD's DB7..DB4 (pins 14-11)          
    movlw    0x03        ; LCD init instruction (First)              
    movwf    PORTB        ; Send to LCD via RB3..RB0                  
    bsf    PORTB,LCD_e    ; Set the LCD E line high,
    nop
    bcf    PORTB,LCD_e    ; and then Clear E

    call    wait_100us

    movlw    0x03        ; LCD init instruction (Second)            
    movwf    PORTB        ; Send to LCD via RB3..RB0                  
    bsf    PORTB,LCD_e    ; Set E high,
    nop
    bcf    PORTB,LCD_e    ; and then Clear E

    call    wait_100us

    movlw    0x03        ; LCD init instruction (Third)              
    movwf    PORTB        ; Send to LCD via RB3..RB0                  
    bsf    PORTB,LCD_e    ; Set E high,
    nop
    bcf    PORTB,LCD_e    ; and then Clear E

    call    wait_100us    ; wait a while,

    movlw    0x02        ; 4-bit mode instruction                    
    movwf    PORTB        ; Send to LCD via RB3..RB0                  
    bsf    PORTB,LCD_e    ; Set E high,
    nop
    bcf    PORTB,LCD_e    ; and then Clear E

    call    wait_100us    ; wait a while,

    movlw    0x28        ; 1/16 duty cycle, 5x8 matrix
    call    cmnd2LCD    ; Send command in w to LCD

    call    wait_100us

;    movlw    0x08        ; Display off, cursor and blink off
;    movlw    0x0C        ; Display on, cursor and blink off
    movlw    0x0E        ; Display on, cursor on and blink off
;    movlw    0x0F        ; Display on, cursor and blink on
    call    cmnd2LCD    ; Send command to LCD

    call    wait_100us

    movlw    0x01        ; Clear and reset cursor
    call    cmnd2LCD    ; Send command in w to LCD

    movlw    2
    call     wait        ; wait a while,

    movlw    0x06        ; Set cursor to move right, no shift
    call    cmnd2LCD    ; Send command in w to LCD

    return            ;
;

;
; *******************************************************************************
; * Purpose:                                    *
; *    Display the frequency on the LCD.                    *
; *                                        *
; *******************************************************************************
;
;show_freq
;    movlw    0xC5        ; Point the LCD to digit location
;    call    cmnd2LCD    ; Send digit location to LCD
;#ifdef    sim
;    nop
;#else
;    movlw    255
;    call    wait
;#endif
;;
;    movfw    TenK
;    addlw    0x00        ; Test if 'w'is 0 or 1
;    btfss    STATUS,Z    ; The Z or Zero bit is set to "1" when the result
;    goto    $+4        ; of an arithmetic or logical operation is zero
;
;    movlw    ' '        ; TenK = 0, insert space
;    call    data2LCD    ; Send byte in W to LCD
;    goto    $+2
;
;;    movfw    TenK        ; TenK is not 0, insert number
;    call    process        ;
;
;    movfw    Thou
;    call    process        ;
;
;    movfw    Hund
;    call    process        ;
;
;    movlw    '.'        ; insert seperator
;    call    data2LCD    ; Send byte in W to LCD
;
;    movfw    Tens
;    call    process        ;
;
;    movfw    Ones
;    call    process        ;
;
;    return
;process
;    addlw    0x30
;    call    data2LCD
;    return
;
; *******************************************************************************
; * Purpose:                                    *
; *    Send Command or Data byte to the LCD                    *
; *    Entry point cmnd2LCD: Send a Command to the LCD                *
; *    Entry Point data2LCD: Send a Data byte to the LCD            *
; *                                        *
; *******************************************************************************
;
cmnd2LCD   ; ****** Entry point ******
    clrf    rs_value        ; Remember to clear RS  (clear rs_value)  
    goto    write2LCD        ; Go to common code
data2LCD   ; ****** Entry point ********
    bsf    rs_value,0        ; Remember to set RS (set bit 0 of rs_value)
write2LCD
    movwf    LCD_char        ; Save byte to write to LCD
;    call    busy_check        ; Check to see if LCD is ready for new data
    call    wait_100us

    MOVLW    2            ;
    MOVWF    count            ; SET UP LOOP COUNTER
write_again
    SWAPF    LCD_char,F        ; SWAP MS & LS NIBBLES
    MOVF    LCD_char,W
    ANDLW    15            ; MAKE TOP 4 BITS OF W 0
    MOVWF    PORTB            ; SEND MS NIBBLE
    bcf    PORTB,LCD_rs        ; Guess RS should be clear - command mode
    btfsc    rs_value,0        ; Should RS be clear?  (is bit 0 == 0?)
    bsf    PORTB,LCD_rs        ; No, set RS - data mode
    NOP
    BSF    PORTB,LCD_e        ; SET E HIGH
    NOP
    BCF    PORTB,LCD_e        ; SET E LOW
    DECFSZ    count,F
    GOTO    write_again        ; GO BACK AND SEND LS NIBBLE
    clrf    PORTB
    return
;
; *******************************************************************************
; * Purpose:                                    *
; *    Check if LCD is done with the last operation.                *
; *    This subroutine polls the LCD busy flag to determine if previous    *
; *    operations are completed.                        *
; *                                        *
; *******************************************************************************
;
;busy_check
;    clrf    PORTB        ; Clear all outputs on PORTB
;    banksel    TRISB        ; Switch to bank for Tris operation
;    movlw    b'00001000'    ; Set RB3 input, others outputs
;    movwf    TRISB        ;
;
;    banksel    PORTB
;    bcf    PORTB,LCD_rs    ; Set up LCD for Read Busy Flag (RS = 0)
;    nop
;    bsf    PORTB,LCD_rw    ; Set up LCD for Read (RW = 1)
;    nop            ;
;    bsf    PORTB,LCD_e    ; Set E high
;    nop
;    bcf    PORTB,LCD_e    ; Drop E again
;
;LCD_is_busy
;    call    wait_100us    ;
;    bsf    PORTB,LCD_e    ; Set E high
;    nop
;    bcf    PORTB,LCD_e    ; Drop E again
;
;    btfss    PORTB,LCD_busy    ; Is Busy Flag (RB3) clear?
;    goto    not_busy    ; Yes - exit
;    nop            ; No
;    nop            ; Wait a while
;
;    bsf    PORTB,LCD_e    ; Pulse E high (dummy read of lower nibble),
;    nop            ; wait,
;    bcf    PORTB,LCD_e    ; and drop E again
;
;    goto    LCD_is_busy    ; If not, it is busy so jump back
;
;not_busy
;    banksel    TRISB        ; Switch to bank 1 for Tristate operation
;    clrf    TRISB        ; All pins (RB7..RB0) are back to outputs
;    banksel    PORTB        ; Switch to bank 0
;    clrf    PORTB        ; Clear all of Port B (inputs and outputs)
;    return
;
; *******************************************************************************
; *                                        *
; * Purpose:  Delay routines                            *
; *                                        *
; *******************************************************************************
;
wait_a_sec ;PIC Time Delay = 0.99999900 s with Osc = 4000000 Hz
    movlw    D'6'
    movwf    CounterC
    movlw    D'19'
    movwf    CounterB
    movlw    D'172'
    movwf    CounterA
loop
    decfsz    CounterA,f
    goto    loop
    decfsz    CounterB,f
    goto    loop
    decfsz    CounterC,f
    goto    loop
    return

wait_250
    movlw    250

wait
    movwf    timer1        ; Set up counter
    call    wait_1ms    ; Go to wait loops
    decfsz    timer1,f
    goto    $-2
    return

wait_1ms     ;PIC Time Delay = 0.00099800 s with Osc = 4MHz
    movlw    D'2'
    movwf    CounterD
    movlw    D'74'
    movwf    CounterE
loop1
    decfsz    CounterE,f
    goto    loop1
    decfsz    CounterD,f
    goto    loop1
    return

wait_100us    ;PIC Time Delay = 0.00009800 s with Osc = 4000000 Hz
    movlw    D'31'
    movwf    CounterA
loop2
    decfsz    CounterA,1
    goto    loop2
    return
;      
; *****************************************************************************
;
    END
 

Pommie

Well-Known Member
Most Helpful Member
Just out of curiosity, I converted your initLCD routine to C.
Straight through code is almost identical to the assembly version without the bank shifting.
Code:
void initLCD(){
    __delay_ms(100);
    PORTB=0x03;                //first
    LCD_E=1;
    NOP();
    LCD_E=0;
    __delay_us(100);
    PORTB=0x03;                //second
    LCD_E=1;
    NOP();
    LCD_E=0;
    __delay_us(100);
    PORTB=0x03;                //third
    LCD_E=1;
    NOP();
    LCD_E=0;
    __delay_us(100);
    PORTB=0x02;                //4 bit mode
    LCD_E=1;
    NOP();
    LCD_E=0;
    __delay_us(100);
    cmd2LCD(0x28);
    __delay_us(100);
    cmd2LCD(0x0E);
    __delay_us(100);
    cmd2LCD(0x01);
    __delay_us(100);
    cmd2LCD(0x05);
    __delay_us(100);
}
With a for loop, it's a bit smaller,
Code:
void initLCD(){
    __delay_ms(100);
    for(char i=0;i<3;i++){
        PORTB=0x03;                //first, second and third.
        LCD_E=1;
        NOP();
        LCD_E=0;
        __delay_us(100);
    }
    PORTB=0x02;                //4 bit mode
    LCD_E=1;
    NOP();
    LCD_E=0;
    __delay_us(100);
    cmd2LCD(0x28);
    __delay_us(100);
    cmd2LCD(0x0E);
    __delay_us(100);
    cmd2LCD(0x01);
    __delay_us(100);
    cmd2LCD(0x05);
    __delay_us(100);
}
Up to about 6 years ago I was an assembly only guy but now do everything in C. It isn't that hard to learn and is (IMHO) well worth it.

Mike.
 

Pommie

Well-Known Member
Most Helpful Member
Seems my 6 years ago was more like 12 years ago. Just did a search and came across this from 2008.

I was asking what the difference was between these pieces of code except for readablility.
Code:
   PORTB=0b10000000;    //compiles to 9 instructions
    TRISB=0b01111111;       //in BoostC
    SPBRG=103;
    BAUDCTL.BRG16=1;

and
    movlw    0b10000000        ;is 15 instructions
    banksel    PORTB             ;in MPASM
    movwf    PORTB
    movlw    0b01111111
    banksel    TRISB
    movwf    TRISB
    movlw    d'103'
    banksel    SPBRG
    movwf    SPBRG
    banksel    BAUDCTL
    bsf    BAUDCTL,BRG16
OK, I'll shut up now.:D

Mike.
 
Bit more progress-
The bin2BCD, show_freq, inc_freq, & COMP routines are all doing as they should, which doesn't leave much else (dec_freq and encoder routines).

Yes, I know some of this is longer than it should be, but the friend I'm doing this for only has a very basic grasp of PIC programming (in any language) and he does like to experiment occasionally :oops:

I am on my umpteenth try at learning C, but it is starting to look like spaghetti o_O again, so it is on the backburner for a little while.
 

Dan Soze

Member
The 'Enable' lines for each ('E' for LCD & 'DLEN' for SAA1057) are on non-shared pins.

I had thought about this being a possible problem and have tried the software with the SAA1057 routines disabled and it makes no difference.
Today I created an example just for you:
Code:
;
; https://www.electro-tech-online.com/threads/st7066u-20x4-lcd-problems.160879/
;
    list        n=0,c=255
    radix       dec
    errorlevel  -302        ; Skip out of bank nuisance messages
    processor   16F628A
;
; File:   main.c
; Author: dan1138
; Target: PIC16F628A
;
; Created on March 2, 2021, 9:38 AM
; Description:
;
;   This is an example of one way to implement the 4-bit parallel
;   mode interface for a Hitachi HD44780 LCD module controller.
;
;                                       PIC16F628A
;                                +----------:_:----------+
;                           <> 1 : RA2               RA1 : 18 <> ENCODER A
;                           <> 2 : RA3               RA0 : 17 <> ENCODER B
;                           <> 3 : RA4          OSC1/RA7 : 16 <>
;                       VPP -> 4 : RA5/VPP      OSC2/RA6 : 15 <>
;                       GND -> 5 : VSS               VDD : 14 <- 5v0
;       LCD_D4              <> 6 : RB0/INT       PGD/RB7 : 13 <> PGD/DLEN(SAA1057)
;       LCD_D5              <> 7 : RB1/RX/DT     PGC/RB6 : 12 <> PGC/LCD_RS
;       LCD_D6/CLK(SAA1057) <> 8 : RB2/RX/CK         RB5 : 11 <>     LCD_RW
;       LCD_D7/DAT(SAA1057) <> 9 : RB3/CCP       PGM/RB4 : 10 <>     LCD_E
;                                +-----------------------:
;                                         DIP-18
;
; LCD display module is SC2004CULB-SO-GB-K
;
; SUNLIKE DISPLAY Model No: SC2004C
; https://electronlab.com.ua/files/_prod/Sunl_337/SC2004CULB-XH-GS.pdf
;
; Samsung KS0066 LCD controller
; https://www.lcd-module.de/eng/pdf/zubehoer/ks0066.pdf
;
; pin  1 - VSS Ground
; pin  2 - VDD +5
; pin  3 - Vo  Contrast
; pin  4 - RS  Register select
; pin  5 - RW  Read/Write select
; pin  6 - E   Enable active high strobe
; pin  7 - DB0 Data bit 0
; pin  8 - DB0 Data bit 1
; pin  9 - DB0 Data bit 2
; pin 10 - DB0 Data bit 3
; pin 11 - DB0 Data bit 4
; pin 12 - DB0 Data bit 5
; pin 13 - DB0 Data bit 6
; pin 14 - DB0 Data bit 7
;
; Definitions of target specific Special Function Registers
;
    include     <P16F628A.INC>
;
; Target specific configuration words
;
    __config    _CP_OFF&_LVP_OFF&_BODEN_OFF&_MCLRE_ON&_PWRTE_ON&_WDT_OFF& _INTOSC_OSC_NOCLKOUT
;
; Define macros to help with
; bank selection
;
#define BANK0  (h'000')
#define BANK1  (h'080')
#define BANK2  (h'100')
#define BANK3  (h'180')
;
; Macro to send message from ROM to LCD display
;
Show_LCD_Message MACRO LCD_message
    movlw   LOW(LCD_message)
    movwf   pszLCD_RomStr
    movlw   HIGH(LCD_message)
    movwf   pszLCD_RomStr+1
    call    LCD_putrs
    ENDM
;
; Constants used in application
;
; The StartFreq constant has units of MHz times 100
;
#define StartFreq (8700)
;
; Start address of each line on LCD module
;
#define LINE_ONE    0x00
#define LINE_TWO    0x40
#define LINE_THREE  0x14
#define LINE_FOUR   0x54
;
; Data used for ISR and LCD
;
  cblock 0x70       ; memory present in all banks
    WREG_save   : 1
    STATUS_save : 1
    LCD_Temp    : 1
    pszLCD_RomStr : 2
  endc
;
; Reset vector
;
    org     0x000                     ; processor reset vector
    nop                               ; ICD2 needs this
    goto    Start                     ; go to beginning of program
;
; Interrupt vector
;
    org     0x004
    movwf   WREG_save               ; Save register that
    movf    STATUS,W                ; must be restored to
    movwf   STATUS_save             ; preserve the context
    clrf    STATUS                  ; Use BANK 0 as default
;
; This is the area where your interrupt service routines go
;
;        KEEP THEIR EXECUTION TIME AS SHORT AS POSSIBLE.
; DO NOT DO WORK THAT SHOULD BE DONE IN THE APPLICATION LOOP!
;
    movf    STATUS_save,W
    movwf   STATUS
    swapf   WREG_save,F
    swapf   WREG_save,W
    retfie
;
; Application start
;
Start:
    clrf    INTCON      ; Disable all interrupt sources
    banksel PIE1
    clrf    PIE1
    banksel CMCON       ; Make all GPIO use digital I/O
    movlw   0x07
    movwf   CMCON
;
    call    LCD_Init    ; Initialize LCD module
;
; Show the initial LCD screen
;
    movlw   LINE_ONE
    call    LCD_SetPosition
    Show_LCD_Message LCD_message1
 
    movlw   LINE_TWO
    call    LCD_SetPosition
    Show_LCD_Message LCD_message2
 
    movlw   LINE_THREE
    call    LCD_SetPosition
    Show_LCD_Message LCD_message3
 
    movlw   LINE_FOUR
    call    LCD_SetPosition
    Show_LCD_Message LCD_message4
;
; Application loop
;
AppLoop:
    goto    AppLoop
;
; LCD functions
;
#define LCD_PORT PORTB
#define LCD_DATA_BITS (0x0F)
#define LCD_RS PORTB,RB6
#define LCD_RW PORTB,RB5
#define LCD_E  PORTB,RB4
;
;*****************************************************************
; Subroutine Name: LCD_Init
; Function: Initialize the LCD interface.
;
; Inputs:  none
;
; Outputs: none
;
; Uses:    WREG, STATUS
;
;*****************************************************************
LCD_Init:
    call    Delay_4us       ; Used to test the timing
    call    Delay_40us      ; of the delay functions
    call    Delay_5ms       ; with the simulator.
    call    LCD_POR_Delay
    banksel BANK1
    movlw   ~LCD_DATA_BITS  ; Make GPIO bits for
    andwf   LCD_PORT,F      ; LCD interface output bits
    bcf     LCD_E
    bcf     LCD_RW
    bcf     LCD_RS
    banksel BANK0
    andwf   LCD_PORT,F
    bcf     LCD_E
    bcf     LCD_RW
    bcf     LCD_RS
;
; Force LCD module to 4-bit parallel mode
;
    movlw   0x33
    andlw   LCD_DATA_BITS
    movwf   LCD_PORT
    bsf     LCD_E           ; Assert LCD_E high
    call    Delay_4us
    bcf     LCD_E           ; Assert LCD_E low
    call    Delay_5ms
    bsf     LCD_E           ; Assert LCD_E high
    call    Delay_4us
    bcf     LCD_E           ; Assert LCD_E low
    call    Delay_5ms
    bsf     LCD_E           ; Assert LCD_E high
    call    Delay_4us
    bcf     LCD_E           ; Assert LCD_E low
    call    Delay_5ms
    movlw   0x22
    andlw   LCD_DATA_BITS
    movwf   LCD_PORT
    bsf     LCD_E           ; Assert LCD_E high
    call    Delay_4us
    bcf     LCD_E           ; Assert LCD_E low
    call    Delay_5ms
;
; Configure 20x4 LCD character module
;
    movlw   (0x28)          ; 4-bit parallel, 5x7, multiline mode
    call    LCD_WriteCommand

    movlw   (0x08)          ; Display off, cursor off, blink off
    call    LCD_WriteCommand

    movlw   (0x01)          ; Clear display
    call    LCD_WriteCommand

    movlw   (0x0C)          ; Display on, cursor off, blink off
    call    LCD_WriteCommand

    movlw   (0x13)          ; Cursor shifts to the left
    call    LCD_WriteCommand

    return
;
;*****************************************************************
; Subroutine Name: Delay_4us, Delay_20us, Delay_40us
; Function: provides a delay for 4, 20 or 40 microseconds
;           Code relies on a 4MHz system oscillator
;
; Inputs:  none
;
; Outputs: none
;
;*****************************************************************

Delay_40us:
    call    Delay_20us              ; Wait at least 40 microseconds
Delay_20us:                         ; for command to complete.
    call    Delay_4us
    call    Delay_4us
    call    Delay_4us
    call    Delay_4us
    goto    Delay_4us
;
;*****************************************************************
; Subroutine Name: Delay_5ms
; Function: Provides a delay for at least 5 milliseconds
;           Code relies on a 4MHz system oscillator
;
; Inputs:  none
;
; Outputs: none
;
; Uses:    WREG, STATUS
;
;*****************************************************************
Delay_5ms:
    call    Dly0
    call    Dly0
Dly0:
    movlw   208     ; Magic number to get 5 milliseconds of delay
Dly1:
    call    Dly2
    addlw   -1
    bnz     Dly1
Dly2:
Delay_4us:
    return
;
;*****************************************************************
; Subroutine Name: LCD_POR_Delay
; Function: provides a delay for at least 15 milliseconds
;
; Inputs:  none
;
; Outputs: none
;
; Uses:    WREG, STATUS
;
;*****************************************************************
LCD_POR_Delay:
    call    Delay_5ms
    call    Delay_5ms
    goto    Delay_5ms
;
;*****************************************************************
; Subroutine Name: LCD_WriteCommand
; Function: Set LCD_RS to command mode
;           Write command byte to PORTB
;           Pulse the LCD_E high for 4 microseconds
;           Wait for 5 milliseconds
;
; Inputs:  WREG     Command to be sent to LCD module
;
; Outputs: none
;
; Uses:    WREG, STATUS
;
;*****************************************************************
LCD_WriteCommand:
    banksel LCD_PORT
    bcf     LCD_RW          ; Assert LCD_RW low
    bcf     LCD_RS          ; Assert LCD_RS low
    call    LCD_Write1
    goto    Delay_5ms
;
;*****************************************************************
; Subroutine Name: LCD_WriteData
; Function: Set LCD_RS to data mode
;           Write command byte to PORTB
;           Pulse the LCD_E high for 4 microseconds
;           Wait for 40 microseconds
;
; Inputs:  WREG     Data to be sent to LCD module
;
; Outputs: none
;
; Uses:    WREG, STATUS
;
;*****************************************************************
LCD_WriteData:
    banksel LCD_PORT
    bcf     LCD_RW          ; Assert LCD_RW low
    bsf     LCD_RS          ; Assert LCD_RS high
LCD_Write1:
    movwf   LCD_Temp
if (LCD_DATA_BITS & 0x0F)
    swapf   LCD_Temp,F
    movf    LCD_Temp,W
endif
    xorwf   LCD_PORT,W
    andlw   LCD_DATA_BITS
    xorwf   LCD_PORT,F      ; Write high nibble
    bsf     LCD_E           ; Assert LCD_E high
    call    Delay_4us
    bcf     LCD_E           ; Assert LCD_E low
    call    Delay_4us
    swapf   LCD_Temp,W
    xorwf   LCD_PORT,W
    andlw   LCD_DATA_BITS
    xorwf   LCD_PORT,F      ; Write low nibble
    bsf     LCD_E           ; Assert LCD_E high
    call    Delay_4us
    bcf     LCD_E           ; Assert LCD_E low
    goto    Delay_40us
;
;*****************************************************************
; Subroutine Name: LCD_SetPosition
; Function: Set the position where character are to be
;           written to the LCD display.
;
; Inputs:  WREG     Line and position on that lone
;
; Outputs: none
;
; Uses:    WREG, STATUS
;
;*****************************************************************
LCD_SetPosition:
    iorlw   0x80        ; Set position LCD module command
    goto    LCD_WriteCommand
;
;*****************************************************************
; Subroutine Name: LCD_putrs
;
; Function: This routine writes a string of bytes to the
;           Hitachi HD44780 LCD controller.
;
; Inputs:  pszLCD_RomStr: pointer to string;
;
; Outputs: none
;
; Uses:    WREG, STATUS
;
;*****************************************************************
LCD_putrs:
    call    TableLookUp
    iorlw   0
    skpnz
    return
    call    LCD_WriteData
    incf    pszLCD_RomStr,F
    skpnz
    incf    pszLCD_RomStr+1,F
    goto    LCD_putrs
 
TableLookUp:
    movfw   pszLCD_RomStr+1
    movwf   PCLATH
    movfw   pszLCD_RomStr
    movwf   PCL
;
; ROM based LCD messages
;
LCD_message1:
    dt  "Ver 1.0 - 20x4 LCD",0
LCD_message2:
    dt  "Line 2 2021-MAR-02",0
LCD_message3:
    dt  "Line 3 19:05:16",0
LCD_message4:
    dt  "123456789.123456789.",0
;
; Initialize EEPROM data
;
    ORG         0x2100
    ;startup frequency bytes must be next 4 bytes of EEPROM
    DATA        StartFreq>>8    ; Most significant byte
    DATA        StartFreq&0xFF  ; Least significant byte (87MHz)
    DATA        0
    DATA        0
 
    END
There is no support for the SAA1057, an incremental encoder dial or converting binary numbers to ASCII strings.

This kind of code is much easier to create in C, but the PIC16F628A does not have a lot of code space so you will be able to add more features using assembly language. Then again if you do not need more feature use C.
 
Last edited:

Latest threads

EE World Online Articles

Loading
Top