ST7066U 20x4 LCD problems(Solved)

Status
Not open for further replies.
OK, the encoder routine works - sort of - the sort of is it steps 4 counts for one detent to detent move of the encoder ie, steps from 87.00 to 87.04 instead of to 87.01.

I haven't posted the whole file, just the 'main' section and I had to intialise 'previous' at startup otherwise it ticked through and did an 'automatic' step.

The encoder table is at the beginning, just before 'start' in the file.

And despite all that, the problem of it works with the 16x2 and not the 20x4 still exists.

C-like:
main
;////////////////////////////////////////////////////////////////////////////////

    rlf    previous,f
    rlf    previous,w
    andlw    0x0c        ;keep only bits 2 & 3
    btfss    PORTA,0        ;move encoder bits
    iorlw    1        ;to bits 0 & 1
    btfss    PORTA,1
    iorlw    2
    movwf    previous    ;keep for next time
    call    enc_table

    addlw    0x00
    btfsc    STATUS,Z
    goto    main
    xorlw    1
    btfsc    STATUS,Z
    goto    up

;////////////////////////////////////////////////////////////////////////////////
;    Based on the knob direction, either increment or decrement the frequency,
;    update the LCD and SAA1057.
;
down
    call    dec_freq    ;
    goto    update        ; Update LCD
up
    call    inc_freq    ;

update
    call    bin2BCD        ; Convert the frequency to BCD
    movlw    3
    call    wait
    movlw    0xc5
    call    cmnd2LCD
    call    show_freq    ; Display the frequency on the LCD
;    call    send_word    ; Send the control word to the chip
    goto    main        ; Continue main loop
 
What happens if you remove these two lines?
Code:
   call    cmnd2LCD
    call    show_freq    ; Display the frequency on the LCD
The 4 times inc/dec is because there are 4 steps to each detent. If you need it to be one then you could copy and right shift the frequency variable in show frequency.

Mike.
 
I'm going to have to deduct 3 every time from the 'freq0 & _1' variables because the data word for the SAA1057 and the show freq routine variables are all derived from there.

To set the SAA1057 to a frequency eg 87.00MHz, the (16bit) data word is 'd'8700 and increments by 1 up to 'd'10800 for 108MHz.

Off to find a wall to bang my head against while I ponder this LCD problem some more.

Edit: Should add I'm aware of why there are 4 steps, just need to cogitate on a simple solution - like throwing out the encoder and using push buttons
 
Just looking at this code,
Code:
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
I don't like the computed gotos - a bug waiting to bite you IMHO. Is the goto $+4 correct? Will it land in the middle of the goto $+2 instruction?
I would advise going through all your code and eliminate any computed gotos by replacing with labels.

Mike.
 
A suggestion for dealing with the counting 4 problem,
Code:
        rlf     previous,f
        rlf     previous,w
        andlw   0x0c        ;keep only bits 2 & 3
        btfss   PORTA,0        ;move encoder bits
        iorlw   1        ;to bits 0 & 1
        btfss   PORTA,1
        iorlw   2
        movwf   previous    ;keep for next time
        call    enc_table
        addwf   clicks,f
        movlw   4
        xorwf   clicks,w
        btfsc   STATUS,Z
        goto    up
        movlw   0xfc            ;-4
        xorwf   clicks,w
        btfsc   STATUS,Z
        goto    down
        goto    main
       
;////////////////////////////////////////////////////////////////////////////////
;    Based on the knob direction, either increment or decrement the frequency,
;    update the LCD and SAA1057.
;
down
        clrf    clicks
        call    dec_freq    ;
        goto    update        ; Update LCD
up
        clrf    clicks
        call    inc_freq    ;
This will count in clicks and only change the frequency if it gets to ±4.
Note that it clears clicks in the up/down routine.

Mike.
 
I do just that once I have finished messing about, I.E. once the software is doing what it should, I go through and tidy it up, saves a few brain cells thinking up label names until the end.

Also inevitably find things that can be even more simplified during the process too.

And yes, it bit me in this one when I modified that routine while searching for an answer to the LCD problem earlier on, just like when I inserted your encoder routine, I couldn't work out why the screen was filling with odd numbers until I remembered the I had taken out the LCD position code from the 'show_freq' routine.

That's why it is just before the call to the routine at the moment.

Also, before I forget, how should I credit you for the routine in any software I use it in? Just me, but I like to credit those that have written bits of code I reuse.
 
Managed to sneak it in before tea - works well thankyou Mike (my encoder is well worn so there is the occasional missed/added step but it won't be noticed in the scheme of things, I'll swap it out tomorrow).
 
The reason I mentioned the computed gotos is bugs like jumping to the wrong place tend to do weird things when the length (or the order) of code changes. So changing the display will change the code length and make the bug appear/disappear in a mysterious fashion, like what's happening here.

No credit needed or wanted.

Mike.
BTW, I really think the goto $+4 is wrong, think it should be $+5 but use a label instead. If you get rid of all the goto $+n I think it will cure your problem.
 
Last edited:
The reason for the occasional missed/added step is probably it not being called often enough. If you call it on an interrupt and only update the display when the frequency changes it should work much better.

Mike.
 
Are you game to try and get this working on interrupt? I think if it's called every 5mS it'll work very smoothly.

Mike.
 
I would like to, but the 628 doesn't support interrupts on PORTA (assuming you are talking the encoder triggering the interrupt).

On the other hand, if you are talking using a timed interrupt to check if the encoder is moving, yes, I'll give that a go.

At this point in time, the encoder routine is not being called, it is just sitting there polling the pins until something happens, I'll shift it off somewhere when all the extra's are added.

Eventually, this will be transferred to a 16F886 (I think, have to dig out the packet to check what I got) when the "extra's" are added - Audio in level metering, transmitter output level & SWR is what I think is wanted, but it may change.

When that happens, I will be able to shift stuff around to different pins and give the triggered interrupt bit a go.

The +4 is right, that first piece puts a space in if the Tenk variable is a zero or puts in a number if it is greater than zero.
I have changed the computed goto's to labelled jumps, but not had a chance to test it.

Once this is all complete, I will be publishing it on my website, along with the code to make it work.
 
I was thinking a timer interrupt using timer 2 at 5mS - maybe 2mS if it's not responsive enough. Both will work depending on how fast you intend to whizz that dial. We appear to be on similar timezones so will look at this tomorrow. I'm thinking all the inc/dec of frequency done in shadow registers in the interrupt and main code can just request it when needed. I'll post something tomorrow and see what you think.

Mike.
P.S. hoping the lack of computed gotos will fix your problem.
Edit, if a 2 or 5ms interrupt is used then you can place stuff on those time intervals. First interrupt, update display, 2nd, send to frequency generator, third do task3 etc. So, everything gets done 50 times per second.
Edit2, not in the interrupt of course but in the main code just timed by the interrupt.
BTW, the link in your signature doesn't work.
 
Last edited:
OK Mike and yes, South Australia

Changing the computed goto's didn't fix anything unfortunately.

Signature had a comma instead of a full stop and now it won't let me insert an active link.
 
Just for info... I run my encoder at 4Mhz but that's all the chip does... I do not miss a pulse and I measure wire ropes traveling 4~5 metres per second up to 10 kilometers... I use a smaller pic12f675 and pass the deciphered pulses to a bigger pic18..

The speed of turning with fingers can be done by scanning the two pins... You will be fine... 5ms will be far more than you need..
 
Here is a method to find a quadrature rotary encoder step in the CW or CCW direction that does not use a table lookup:
Code:
;
; The current sample is AC.
; The previous sample is ac.
;
; Clockwise direction:
; c = a XOR b
; ab  ac  AC  AC-ac
; 00  00  01   01
; 01  01  10   01
; 11  10  11   01
; 10  11  00   01
;
; Counter Clockwise direction:
; c = a XOR b
; ab  ac  AC  AC-ac
; 00  00  11   11
; 10  11  10   11
; 11  10  01   11
; 01  01  00   11
;
;
; RA1 = A , RA0 = B
;
Temp    EQU 0x70
Last_AC EQU 0x71
;
QuadCheck:
    movf  PORTA,W       ; Sample rotary dial
    andlw 3             ; mask for bits AB
    movwf Temp          ; Save for later
    rrf   Temp,F        ; Shift for XOR of A with B
    xorwf Temp,W        ; Change 2-bit Gray code to binary
    xorwf Last_AC,W     ; Use three XORs to swap the
    xorwf Last_AC,F     ; current sample with
    xorwf Last_AC,W     ; the last sample.
    subwf Last_AC,W     ; Calculate the difference (Current-Last)
    andlw 3             ; W = 01 for CW step, W = 11 for CCW step
    xorlw 1             ; Test for Clockwise step
    btfsc STATUS,Z      ; skip if not CW step
    retlw +1
    xorlw 2             ; Test for Counter Clockwise step
    btfsc STATUS,Z      ; skip if not CCW step
    retlw -1
    retlw 0
 
Last edited:
OK, morning now, had my coffee so here we go.

To do the reading on interrupt and keep the divide by four needs the interrupt to do the increment, decrement and range check of the frequency.
With that in mind, here's a first attempt at the ISR,
Code:
;note W_TEMP and STATUS_TEMP need to be in the common area (above 0x70)
ISR            MOVWF   W_TEMP            ; SAVE OFF CURRENT W REGISTER CONTENTS
                MOVF    STATUS,W          ; MOVE STATUS REGISTER INTO W REGISTER
                MOVWF   STATUS_TEMP       ; SAVE OFF CONTENTS OF STATUS REGISTER
                BCF            STATUS,RP0
                BCF             PIR1,TMR2IF
                RLF     previous,F
                RLF     previous,W
                ANDLW   0X0C                    ;KEEP ONLY BITS 2 & 3
                BTFSS   PORTA,0                ;MOVE ENCODER BITS
                IORLW   1                            ;TO BITS 0 & 1
                BTFSS   PORTA,1
                IORLW   2
                MOVWF   previous                ;KEEP FOR NEXT TIME
                CALL    ENC_TABLE
                ADDWF        clicks,F
                MOVLW        4
                XORWF        clicks,W
                BTFSS        STATUS,Z
                GOTO        CHECK_DOWN
                CLRF        clicks                    ;added this line
                MOVLW        high 10800            ;test if frequency has reached 108MHz
                XORWF        freqHigh,W
                BTFSS        STATUS,Z
                GOTO        DOINC                        ;high bytes don't match so do increment
                MOVLW        low 10800
                XORWF        freqLow,W
                BTFSC        STATUS,Z                ;low bytes don't match so do increment
                GOTO        EXIT                        ;if it has, exit
DOINC        INCF    freqHigh,F            ;if not then increment it
                INCFSZ  freqLow,F
                DECF    freqHigh,F
                GOTO        EXIT
CHECK_DOWN
                MOVLW        0XFC            ;-4
                XORWF        clicks,W
                BTFSC        STATUS,Z
                GOTO        EXIT
                CLRF        clicks                    ;added this line
                MOVLW        high 8700
                XORWF        freqHigh,W
                BTFSS        STATUS,Z
                GOTO        DODEC
                MOVLW        low 8700
                XORWF        freqLow,W
                BTFSC        STATUS,Z
                GOTO        EXIT
DODEC        MOVF    freqLow,F
                SKPNZ
                DECF    freqHigh,F
                DECF    freqLow,F
                GOTO        EXIT
;         RESTORE CONTEXT BEFORE RETURNING FROM INTERRUPT
EXIT        MOVF    STATUS_TEMP,W     ; RETRIEVE COPY OF STATUS REGISTER
                MOVWF   STATUS            ; RESTORE PRE-ISR STATUS REGISTER CONTENTS
                SWAPF   W_TEMP,F
                SWAPF   W_TEMP,W          ; RESTORE PRE-ISR W REGISTER CONTENTS
                RETFIE                    ; RETURN FROM INTERRUPT
Note, I've no way to test this or even to assemble it so it may have silly errors.

Now we have an ISR we need to setup the timer to generate some interrupts,
Code:
initTmr2
                ;using internal 4MHz clock = 1MHz instruction time
                ;prescaler = 4 = clocked at 250kHz
                ;PR2 = 249        will clock around every 250 cycles
                ;postscaler = 5 = clock every 250*4*5 = 5000 cycles = 5mS interrupt
                MOVLW        0B00100101
                MOVWF        T2CON
                BSF            STATUS,RP0
                MOVLW        249
                MOVWF        PR2
                BSF            PIE1,TMR2IE
                BCF            STATUS,RP0
                BSF            STATUS,PEIE
                BSF            STATUS,GIE
                RETURN
Changing the postscaler will change the time, change to 3 will be 3mS etc.

As frequency is 16 bit and is changed on interrupt, we need to ensure no interrupts occur whilst we're using it.
Simplest way is to copy it to the original variables
Code:
GETfreq    BCF            STATUS,GIE
                MOVFW        freqLow
                MOVWF        freq_0
                MOVFW        freqHigh
                MOVWF        freq_1
                BSF            STATUS,GIE
                RETURN
We also need to be able to initialize it,
Code:
SETfreq    BCF            STATUS,GIE
                MOVFW        freq_0
                MOVWF        freqLow
                MOVFW        freq_1
                MOVWF        freqHigh
                BSF            STATUS,GIE
                RETURN

And, finally, I forgot we need to change the interrupt vector to go to our ISR,
Code:
                ORG            4
                GOTO        ISR

It seems like a lot of code but is less than 90 lines in total.

Timing shouldn't be a problem, the ISR is called every 5000 instruction cycles, it's ~50 instructions long. Assuming each instruction takes 2 cycles (they don't) then it's 2% of the processor time.

It's been fun going back to assembler.

Mike.
Edit, forgot how much I hate the way this board messes up the formatting of code.
Edit2, forgot to clear clicks in ISR - added two lines.
 
Last edited:
OK, thankyou Dan & Mike.

On the third cuppa now so hopefully the brain is awake.

Give me a little while to set this up and test (I'll run Dans encoder code first as that will be quick and then run yours Mike).
 
First up - Dan - your encoder routine works with a couple of exceptions.

1. Occasionally when first powering up it will add or subtract a step without touching the encoder.

2. It needs some software debouncing for noisy encoders, it is particularly jittery at the end limits i.e. if you keep turning the encoder once it has reached either the high or low limit for the frequency in my program, the frequency will bounce back and forth one or two steps.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…