SparkFun 4x20 LCD Redo

Thread Starter

jpanhalt

Joined Jan 18, 2008
9,699
Seeking input for my next "project."

Awhile back I bought the Sparkfun 4x20 LCD display (LCD-09568, https://www.sparkfun.com/products/9568 ) with a 1-wire interface good up to "38,400" baud . It was finicky and locked up frequently at 38,400 baud and at lower baud rates too. That problem is noted in several of the reviews. To SparkFun's credit, it offered to refund my whole cost. However, when I noticed it used the 18-pin PIC 16F88 chip that appears to be pin compatible to the 18-pin 16F1826/27 chips, with which I am familiar, and there are pads on the board for programming, I declined their kind offer. Only one pin of the MCU is not brought out to a pad on the board, RB5 pin 11. Also, note that the schematic available for a previous version of a SF serial display backpack "v28" is not the same as for the LCD-09568. I have attached a schematic that shows the pinouts and connections of the 16F88 as best I can determine:

Schematic.png
(I didn't bother to trace out all of the resistors.)
My plan is to make it work with the PIC 16F88/16F1826/27 chips. My suspicion is that the code uses fixed delays rather than monitoring the "busy" signal.

Questions:
1) I would love to hear from others who have significant experience with that display.
2) Is 38,400 baud a reasonable target for that controller?

Finally, I have absolutely no financial motive in this and have told SparkFun that anything I develop will be open source. I paid full price plus shipping for the display I will be using for this project.

Regards, John

EDIT: Added "no" to my comment about financial motive. As I have said before and others here have witnessed, I am a horrible proofreader and have always had to rely on help from others for that.
 
Last edited:

joeyd999

Joined Jun 6, 2011
4,401
Seeking input for my next "project."

Awhile back I bought the Sparkfun 4x20 LCD display (LCD-09568, https://www.sparkfun.com/products/9568 ) with a 1-wire interface good up to "38,400" baud . It was finicky and locked up frequently at 38,400 baud and at lower baud rates too. That problem is noted in several of the reviews. To SparkFun's credit, it offered to refund my whole cost. However, when I noticed it used the 18-pin PIC 16F88 chip that appears to be pin compatible to the 18-pin 16F1826/27 chips, with which I am familiar, and there are pads on the board for programming, I declined their kind offer. Only one pin of the MCU is not brought out to a pad on the board, RB5 pin 11. Also, note that the schematic available for a previous version of a SF serial display backpack "v28" is not the same as for the LCD-09568. I have attached a schematic that shows the pinouts and connections of the 16F88 as best I can determine:

View attachment 151066
(I didn't bother to trace out all of the resistors.)
My plan is to make it work with the PIC 16F88/16F1826/27 chips. My suspicion is that the code uses fixed delays rather than monitoring the "busy" signal.

Questions:
1) I would love to hear from others who have significant experience with that display.
2) Is 38,400 baud a reasonable target for that controller?

Finally, I have absolutely financial motive in this and have told SparkFun that anything I develop will be open source. I paid full price plus shipping for the display I will be using for this project.

Regards, John
Are you going to write in C or .asm?
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
9,699
Yes, of course. All help is welcome.

I haven't even turned on my soldering iron yet. I do have code written for the 16F1829 in 4-bit mode and reading the busy bit. The main purpose of the post was to see whether someone else had "fixed" that display and share experiences with running it or similar Hitachi HD44780 controlled displays faster than 19200 baud.
 

joeyd999

Joined Jun 6, 2011
4,401
Yes, of course. All help is welcome.

I haven't even turned on my soldering iron yet. I do have code written for the 16F1829 in 4-bit mode and reading the busy bit. The main purpose of the post was to see whether someone else had "fixed" that display and share experiences with running it or similar Hitachi HD44780 controlled displays faster than 19200 baud.
Why do you believe the serial interface speed has any technical impact on the display performance?

They are two separate devices with their own (or should have their own) independent drivers. Ultimate performance will depend on how well they are integrated.
 

LesJones

Joined Jan 8, 2017
2,665
Hi John,
I have used 4 x 16 displays mainly with PIC16F628s. I have also used basically the same code with other 16F series PICs and I think with 4 X 20 displays. Here is an example of the code I used. I do test the busy flag rather than just waiting a fixed time.

Code:
;
;   Version Mod3  Reads two scale inputs.
;    Modified for 20 Mhz xtal
;    Modified for PIC16F628
;   Modified to detect scale sample mode (Normal / fast)
;   MODIFIED TO DETECT WAIT FOR DATA OF OVER 400 MS
;   Modified to put display buffers in page 1 ram (Instead of page 0)
;   Modified to output combined data

;   Display Scale 1  on top line
;   Display Scale 2  on second line
;   Sum of inputs on line 4
;   Add "No signal statup display"
;
;    Being modified to use RA5/MCLRE as input to disable fast sample mode as startup.

;Each 24 bit value is in ones compliment form (1 sign bit, 23 data bits). This allows for a 0 and a -0. The unit of measure is 1/20480th of an inch.

;The raw value is multiplied by 125/256 for inches or by 127/1024 for mm.



;PIC16F628, WDT OFF, POR ON, INTERNAL 20 MHz CLOCK  Register space in bank 0 0x20 - 0x6

   list P=16F628A
   #include p16f628a.inc
   __config _HS_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF & _MCLRE_OFF
;


;             ===============

; !!!!!!!!!!!! These timing values are based on a 20mhz Xtal !!!!!!!!!!!!

;       OUTPUT ROUTINE INFORMATION & Equates
; Three timer periods are required to generate the Chinese scale output.
; The periods are timed with timer1 which is set to run at the internal clock
; rate of Fosc/4 or 5MHz which is equal to a 200ns period.

; The time between number outputs is 20ms.  In timer1 ticks, this is calculated
; as 20ms / 200ns = 100,000.  Since timer1 is only  a 16 bit counter, this time
; is divided into two 10ms time intervals or 50,000 (0xc350) timer1 ticks each.
; Instruction overhead for the delay function call is ignored.
DELAY_10MS_L  equ  0x50
DELAY_10MS_H  equ  0xc3

; The time between the two numbers is 110us and the time before and after the
; number output is 55us.  The number of timer1 ticks is calculated as
; 55us / 200ns = 275.  Subtracting the delay function call overhead
; leaves 250 (0xfa) timer1 ticks.
DELAY_55US  equ  0xfa

; The clock period for the Chinese scale output is 13.2us.  The half period
; between transitions is 6.6us.  The number of timer1 ticks is calculated as
; 6.6us / 200ns = 33.  Subtracting the delay function call overhead
; leaves 10 (0x0a) timer1 ticks.
DELAY_6_6US  equ  0x0a

; Define variables at memory locations
;           ****** PAGE 0 *****

Delay     EQU   0x20
X_Delay     EQU   0x21
X_Delay2   EQU   0x22
Temp     EQU   0x23

Ctrl     EQU   0x24       ;Used in LCD routines
Char     EQU   0x25
Cmd     EQU   0x26
Flag     EQU   0x27       ;Bit '0' sign bit set for negative
           ;Bit '1' Display line bit - set for line 2
           ;Bit 2 set for invalid reading from getdata routines ie timeout has occured
           ;Bit 3 set for Z1 in fast mode
           ;Bit 4 set for Z2 in fast mode
TempByte2   EQU   0x28



;         Only the data in the second frame is used
Z1Data0     EQU   0x29     ;MSB     (Second frame)  (Relative)
Z1Data1     EQU   0x2A
Z1Data2     EQU   0x2B     ;LSB

Z1Data3     EQU   0x2C     ;MSB     (First frame) (Absolute)
Z1Data4     EQU   0x2D
Z1Data5     EQU   0x2E     ;LSB     First byte received from scale


Z2Data0     EQU   0x2F     ;MSB     (Second frame)  (Relative)
Z2Data1     EQU   0x30
Z2Data2     EQU   0x31     ;LSB

Z2Data3     EQU   0x32     ;MSB     (First frame) (Absolute)
Z2Data4     EQU   0x33
Z2Data5     EQU   0x34     ;LSB     First byte received from scale

rDataCnt   EQU   0x35
rBitCnt     EQU   0x36

rData0     EQU   0x37     ;     Byte 6           MSB  
rData1     EQU   0x38     ;     Byte 5
rData2     EQU   0x39     ;     Byte 4           LSB


ZCombData0   EQU   0x3A     ;MSB
ZCombData1   EQU   0x3B
ZCombData2   EQU   0x3C     ;LSB


         ;     rBin 4 Bytes
rBin     EQU   0x3D     ;     MSB
rBin1     EQU   0x3E
rBin2     EQU   0x3F
rBin3     EQU   0x40

         ;     rBCD 4 Bytes
rBCD     EQU   0x41     ;     MSB
rBCD1     EQU   0x42  
rBCD2     EQU   0x43  
rBCD3     EQU   0x44     ;     LSB


rCnt     EQU   0x45  
rPto     EQU   0x46     ;Output buffer pointer fo BCD to ACII routine
rPti     EQU   0x47     ;Input buffer pointer fo BCD to ACII routine
mFactor     EQU   0x48  
TempByte   EQU   0x49
Timeout     EQU   0x4A     ;Counter used in getdat routines to detect more than 400 ms wait for data
;spare     EQU   0x4B
TempW     EQU   0x4C
TempS     EQU   0x4D


param1     EQU   0x4E     ; parameter 1 for function calls  (Only seems to be used in delay routine)
param2     EQU   0x4F     ; parameter 2 for function calls  (Only seems to be used in delay routine)
temp1     EQU   0x50     ; temporary scratch variable
temp2     EQU   0x51     ; temporary scratch variable
temp3     EQU   0x52     ; temporary scratch variable
bits     EQU   0x53     ; bit iterator
bytes     EQU   0x54     ; byte iterator
frames     EQU   0x55     ; frame iterator
FormatPtr   EQU   0x56     ; Holds pointer to LCD buffer  (Used in format routine)
;Flag     EQU   0x57     ;Bit 0 Z1 negative flag
           ;Bit 1 Z2 negative flag

;     -------------------------------------------------------

;   PAGE 1

         ;rDisp Display buffer 64 bytes  NOW ON PAGE 1


;             ****** PAGE 1 *****
rDisp1     EQU   0xA0     ;Start of first line
rDisp2     EQU   0xB0     ;Start of second line
rDisp3     EQU   0xC0     ;Start of third line
rDisp4     EQU   0xD0     ;Start of fourth line



;   First available address with PIC16F628A is 0x20
;   Last available address with PIC16F628A is 0x6F (In bank 0)
;   Ram space 0xA0 to 0xEF  in bank 1
;   Ram space 0x120 to 0x14F  in bank 2


  
LCD_PORT   EQU   PORTB
LCD_TRIS   EQU   TRISB

#define   INH4051     PORTA, 0     ;Pin 17
#define   Clock1     PORTA, 1
#define   Data1     PORTA, 2

#define   Clock2     PORTA, 3
#define   Data2     PORTA, 4
#define Mode     PORTA, 5   ;Low to enable fast sample mode at startup

#define   Z_CombClock_out   PORTB,4
#define   Z_CombData_out   PORTB,5



;Display port useage
#define   Out_Enable   PORTB, 0  ;enables clock & data out to DRO350
#define   E     PORTB, 1
#define   RW     PORTB, 2
#define   RS     PORTB, 3

;LCD_Data4     PORTB, 4   ;Also clock out to 74LS00 enabled by RB0
;LCD_Data5     PORTB, 5   ;Also data out to 74LS00 enabled by RB0
;LCD_Data6     PORTB, 6   ;Also CD4051 select A (Pin 11)
;LCD_Data7     PORTB, 7   ;Also CD4051 select B (Pin 10)

#define     Sel_A   PORTB,6
#define     Sel_B   PORTB,7

;   CD4051 table
;Select A PORTB,5
;Select B PORTB,6
;Select C Grounded
;Inhibit PORTA,0
;
; Outputs     Pull scale clock & data to +1.5 V
;   X0   Scale 1 clock   pin 13
;   X1   Scale 1 data   Pin 14
;   X2   Scale 2 clock   pin 15
;   X3   Scale 2 data   pin 12
;   X4 to X7 not used
;   Common pin 3   +1.5 V

  
;     End of definitions

  

Start     ORG 0
     GOTO    Main

Interrupt   ORG 4       ;Should not get here !!!
     GOTO   Main  ;

Main           ;   Initialize PIC  
       movlw    0x07     ; activate PORTA for PC16F628 as digital
       movwf    CMCON
       BCF     INTCON,GIE   ;disable interrupts
       BCF     INTCON,INTF   ;disable interrupt from RB0
     CLRF   PORTA     ;
     CLRF   PORTB     ;
     BSF   STATUS, RP0   ;Select bank 1
     MOVLW   B'00000000'   ;Bit 0 input All others output
     MOVWF   TRISB     ;
    
     MOVLW   B'00111110'   ;Bits 1, 2, 3, 4 & 5 inputs. Others outputs.
     MOVWF   TRISA     ;
    
     MOVLW   B'00000000'   ;Could set bit 7 to disable weak pull-up's as they are not required.
     MOVWF   OPTION_REG   ;
     BCF   STATUS, RP0   ;Back to bank 0

           ; Initialize timer 1
     MOVLW   0x31     ;Timer on Prescaler 1:8
     MOVWF   T1CON

     BSF   INH4051     ;Set inhibit high



;             Initialize LCD
     MOVLW   D'200'     ;
     CALL   X_Delay100   ;
     MOVLW   0x30     ;(LCD bits 4 & 5) -  Set 4 bit mode command
     IORWF   LCD_PORT,F   ;
     CALL   Toggle_E   ;Send command
     MOVLW   D'50'     ;
     CALL   X_Delay100   ;
     CALL   Toggle_E   ;Send command again ?
     MOVLW   D'1'     ;
     CALL   X_Delay100   ;
     CALL   Toggle_E   ;
     MOVF   LCD_PORT, W   ;Get lower nibble of PORTB  ????? (R/W has not been changed for a read)
     ANDLW   0x0f     ; Clear high nibble (LCD data)
     MOVWF   Ctrl     ;
     MOVLW   0x20     ;
     IORWf   Ctrl,W     ; "or" in bit 5
     MOVWF   LCD_PORT   ;
     CALL   Toggle_E   ;
;     MOVLW   0x20     ;Set datalength to 4, 1 line, 5x7 font (Original setting)

     MOVLW   0x28     ;Set datalength to 4, 2 line, 5x8 font  (New setting)
     CALL   Send_Cmd   ;
     MOVLW   0x0c     ;Display on, Curser off
     CALL   Send_Cmd   ;

     MOVLW   0x06     ;Screen shifting mode on

;     MOVLW   0x04     ;Screen shifting mode off

     CALL   Send_Cmd   ;
     MOVLW   0x03     ;Return home command
     CALL   Send_Cmd   ;



;           move mm characters to display buffer Top line (Buffer now on page 1)
     bsf   STATUS,5     ;Select page 1
     MOVLW   ' '       ;
     MOVWF   rDisp1+0x0F     ;
     MOVWF   rDisp1+0x0E     ;
     MOVWF   rDisp1+0x0D     ;
     MOVWF   rDisp1+0x0C     ;
     MOVLW   'm'       ;
     MOVWF   rDisp1+0x0B   ;
     MOVLW   'm'       ;
     MOVWF   rDisp1+0x0A   ;
     MOVLW   ' '       ;
     MOVWF   rDisp1+0x09   ;


;           move mm characters to display buffer second line

     MOVLW   ' '       ;
     MOVWF   rDisp2+0x0F     ;
     MOVWF   rDisp2+0x0E     ;
     MOVWF   rDisp2+0x0D     ;
     MOVWF   rDisp2+0x0C     ;
     MOVLW   'm'       ;
     MOVWF   rDisp2+0x0B   ;
     MOVLW   'm'       ;
     MOVWF   rDisp2+0x0A   ;
     MOVLW   ' '       ;
     MOVWF   rDisp2+0x09   ;


;           move mm characters to display buffer fourth line


     MOVLW   ' '       ;
     MOVWF   rDisp4+0x0F     ;
     MOVWF   rDisp4+0x0E     ;
     MOVWF   rDisp4+0x0D     ;
     MOVWF   rDisp4+0x0C     ;
     MOVLW   'm'       ;
     MOVWF   rDisp4+0x0B   ;
     MOVLW   'm'       ;
     MOVWF   rDisp4+0x0A   ;
     MOVLW   ' '       ;
     MOVWF   rDisp4+0x09   ;
     BCF   STATUS,5     ;Select page 0


     MOVLW   B'00000000'   ;Disable all interrupts
     MOVWF   INTCON     ;Initialise INTCON register

     call   Version     ;Display Version and No Signal message
     movlw   D'200'     ;Delay 2 seconds
     call   X_Delay10ms
     movlw   D'200'     ;Delay 2 seconds
     call   X_Delay10ms
     movlw   D'200'     ;Delay 2 seconds
     call   X_Delay10ms
;
;         *********** START OF MAIN LOOP ********************8

     BTFSS   Mode     ;Test mode switch / jumper to see if fast mode is required
     GOTO   Normal

     CALL   Set_Fast1

     movlw   D'200'     ;Delay 2 seconds
     call   X_Delay10ms

     CALL   Set_Fast2
     GOTO   Repeat     ;Set_Done

Normal
     CALL   Set_Slow1

     movlw   D'200'     ;Delay 2 seconds
     call   X_Delay10ms

     CALL   Set_Slow2
     GOTO   Repeat     ;Set_Done

;   Need to add this code for 2 sec delay then clear 3 rd line
;Set_Done
;     movlw   D'200'     ;Delay 2 seconds
;     call   X_Delay10ms
;
;     CALL   Clear     ;Clear 3rd line

  

Repeat    
     CALL   GetData1       ;   Read data from scale 1
     CALL   GetData2       ;   Read data from scale 2

     CALL   ADD_24Bit

;       Copy Z1Data to rData
     movfw   Z1Data0
     MOVWF   rData0
     MOVFW   Z1Data1
     MOVWF   rData1
     MOVFW   Z1Data2
     MOVWF   rData2
;
     CALL   SetSign
     call   ConvertM     ; Convert to mm    Raw value * 127/1024
     call   BintoBCD
     MOVLW   rDisp1     ;Least significant digit to display on line 1
     CALL   FormatL     ;   Format line 1 data (Top line of display.

     MOVLW   0x80     ; Cursor at 0x00
     call   Send_Cmd

     MOVLW   0x010     ; Write 16 characters from display buffer to top line of display
     MOVWF   TempByte2   ;
     MOVLW   rDisp1     ;
     MOVWF   FSR     ;
     CALL   Send_next_Char   ;



;       Copy Z2Data  to rData
     movfw   Z2Data0
     MOVWF   rData0
     MOVFW   Z2Data1
     MOVWF   rData1
     MOVFW   Z2Data2
     MOVWF   rData2
;
     CALL   SetSign
     call   ConvertM     ; Convert to mm    Raw value * 127/1024
     call   BintoBCD
;     CALL   FormatL2     ;   Format line 2 data (Bottom line of display.

     MOVLW   rDisp2     ;Least significant digit to display on line 2
     CALL   FormatL     ;   Format line

     MOVLW   0xC0     ; Cursor at 0x40 (Start of second line)
     call   Send_Cmd

     MOVLW   0x010     ; Write 16 characters from display buffer to second line of display
     MOVWF   TempByte2   ;
     MOVLW   rDisp2   ;
     MOVWF   FSR     ;
     CALL   Send_next_Char   ;

     MOVLW   0x94     ; Cursor at 0x14 (Start of third line)
     call   Send_Cmd

;     MOVLW   0x010     ; Write 16 characters from display buffer to third line of display
;     MOVWF   TempByte2   ;
;     MOVLW   rDisp3   ;
;     MOVWF   FSR     ;
;     CALL   Send_next_Char   ;



;           Copy ZCombData to rData
     MOVFW   ZCombData0
     MOVWF   rData0
     MOVFW   ZCombData1
     MOVWF   rData1
     MOVFW   ZCombData2
     MOVWF   rData2

     CALL   SetSign
     call   ConvertM     ; Convert to mm    Raw value * 127/1024
     call   BintoBCD

     MOVLW   rDisp4     ;Least significant digit to display on line 4
     CALL   FormatL     ;   Format line

     MOVLW   0xD4     ; Cursor at 0x54 (Start of fourth line)
     call   Send_Cmd

     MOVLW   0x010     ; Write 16 characters from display buffer to fourth line of display
     MOVWF   TempByte2   ;
     MOVLW   rDisp4   ;
     MOVWF   FSR     ;
     CALL   Send_next_Char   ;

     CALL   Send_Frames   ;Output combined data

     GOTO   Repeat     ;
;             END OF MAIN PROGRAM LOOP
;
;
;       ----------------------------------------------------------------------------------
;             Subroutines from here
;         -----------------------------------------------

Send_next_Char     MOVF   INDF, W   ;
       CALL   Write_Char   ;
       INCF   FSR,F     ;
       DECFSZ   TempByte2,1   ;
       GOTO   Send_next_Char   ;
       RETURN

;         ------------------------------------------------

Write_Char
       MOVWF   Char     ; Character to be sent is in W
       CALL   Check_BF   ; Wait for LCD to be ready
       MOVF   LCD_PORT, W   ; Get lower nibble of PORTB
       ANDLW   0x0F
       MOVWF   Ctrl     ;
       MOVF   Char, W     ;
       ANDLW   0xF0     ; Get upper nibble
       IORWF   Ctrl,W     ; Combine data with Ctrl
       MOVWF   LCD_PORT   ;
       BCF   RW     ; Set LCD to write
       BSF   RS     ; Set LCD to data mode
       CALL   Toggle_E   ;
       MOVLW   0x0F     ;
       ANDWF   LCD_PORT,F   ;
       SWAPF   Char,W     ;
       ANDLW   0xF0     ; Get lower nibble
       IORWF   LCD_PORT,F
       CALL   Toggle_E
;       MOVF   Char, W     ;Could add this instruction so character is still in "W"
       RETURN

;         ------------------------------------------------

Clear_Display     MOVLW   0x01   ;
       CALL   Send_Cmd   ;
       RETURN

;         ------------------------------------------------

Send_Cmd     MOVWF   Cmd       ;Put command byte in Temporary variable
       CALL   Check_BF   ;Check if LCD is ready
       MOVF   LCD_PORT, W   ;Get lower nibble of PORTB
       ANDLW   0x0F     ;
       MOVWF   Ctrl     ;
       MOVF   Cmd, W     ;
       ANDLW   0xF0     ;Get upper nibble
       IORWF   Ctrl,W     ;Combine command with Ctrl
       MOVWF   LCD_PORT   ;
       BCF     RW       ;Clear is for a write to LCD
       BCF     RS       ;Clear is for command mode
       CALL   Toggle_E   ;
       MOVLW   0x0F     ;
       ANDWF   LCD_PORT,F   ;
       SWAPF   Cmd,W     ;Swap nibbles
       ANDLW   0xF0     ;
       IORWF   LCD_PORT,F   ;
       CALL   Toggle_E   ;
       RETURN

;           ---------------------------------------------
             ;Check LCD busy flag    
Check_BF     BSF   STATUS, RP0   ; Select bank 1
       MOVLW   0xF0     ; Make high nibble of PORTB output
       IORWF   LCD_TRIS,F
       BCF   STATUS, RP0   ; Select bank 0
       BCF   RS     ; Set LCD for command mode
       BSF   RW     ; Setup to read busy flag
       BSF   E     ;
       CALL   Delay100   ;
       MOVF   LCD_PORT, W   ;Read upper nibble busy flag
       BCF   E     ;
       ANDLW   0xF0     ; Mask out lower nibble
       MOVWF   Temp     ;
       CALL   Toggle_E   ; "read" lower nibble
       BTFSC   Temp, 7     ; Check busy flag, high = busy
       GOTO   Check_BF   ; If busy, check again
       BCF   RW
       BSF   STATUS, RP0   ; Select bank 1
       MOVLW   0x0F     ; Make high nibble of PORTB input
       ANDWF   LCD_TRIS,f
       BCF   STATUS, RP0   ; Select bank 0
       return
      

;           ---------------------------------------
;             Delay by 10 ms * value of 'W' at entry
X_Delay10ms     MOVWF   X_Delay2

X_Delay10ms_LOOP   movlw   D'100'
       CALL   X_Delay100   ;
       DECFSZ   X_Delay2,F
       GOTO   X_Delay10ms_LOOP
       RETURN  
        
;           ---------------------------------------
;             Delay by 100 us * value of 'W' at entry
X_Delay100     MOVWF   X_Delay

X_Delay100_LOOP     CALL   Delay100   ;
       DECFSZ   X_Delay,F
       GOTO   X_Delay100_LOOP
       RETURN  
      
;           ------------------------------------------------  

Delay100
       MOVLW   D'160'     ;100 microseconds timeDelay with 20 Mhz xtal
       MOVWF   Delay
Delay100_LOOP     DECFSZ   Delay,F
       GOTO   Delay100_LOOP
       RETURN  
        
;           -------------------------------------
;   *********************************************************************************
;    *  Function:  delay_cycles               *
;    *  Description: Delay a specified number of instruction cycles including   *
;  *  interrupt cycles.  The function call overhead adds between     *
;  *  13 and 16 cycles of delay on top of the specified value.     *
;   *   Timer 1 Prescaler 1:1 Clock Fosc/4  (5Mhz  / 200ns with 20 Mhz xtal)   *
;   *                     *
;    *  Parameters:  param1 - least significant byte of 16 bit cycle delay     *
;  *  param2 - most significant byte of 16 bit cycle delay     *
;    *  Returns:  None                 *
;   *********************************************************************************
delay_cycles:
      
         MOVLW   0x01       ; enable timer1  All other bits 0  1:1 Prescaler ,Internal clock Fosc/4
       MOVWF   T1CON

       comf  param1,F  ; negate the delay by complementing the
       comf  param2,F  ; low and high bytes
       bcf  T1CON,TMR1ON  ; stop timer 1
       movf  param1,W  ; move the low byte of the delay into
       movwf  TMR1L  ; timer 1
       movf  param2,W  ; move the high byte of the delay into
       movwf  TMR1H  ; timer 1
       bcf  PIR1,TMR1IF  ; clear the timer 1 rollover flag
       bsf  T1CON,TMR1ON  ; turn on timer 1
  
tmr1_check:  
       btfss  PIR1,TMR1IF  ; wait for the timer 1 rollover flag to
       goto  tmr1_check  ; trigger
       return
    
;           ----------------------------------

Toggle_E     BSF   E
       NOP
       BCF   E
       RETURN
;           -----------------------------------------

;           **** Get data from scale 1  ****
;           If no data within 420 ms return with Flag bit 2 set

GetData1
;       BSF   PORTB,0     ;!!!!!!!  TEST ONLY
       BCF   Flag,2     ;Clear invalid reading flag
       BCF   T1CON,0     ;Stop timer 1
       MOVLW   0x31     ;Timer on Prescaler 1:8
       MOVWF   T1CON
       CLRF   TMR1H     ;Clear timer 1 high (Clearing timer registers gives about 105 ms timeout
       CLRF   TMR1L     ;Crear timer 1 low
       BCF   PIR1,0     ;Clear timer 1 overflow flag
       MOVLW   0x04     ;"Timeout" will reach zero in 420 ms) (4 * 105 ms)
       MOVWF   Timeout
       BSF   T1CON,0     ;Start timer 1

       MOVLW   Z1Data5
       MOVWF   FSR
       MOVLW   6
       MOVWF   rDataCnt   ;Total number of bytes to get from scale (6)
       CLRF   rBitCnt
       MOVLW   0x24     ;For 20 Mhz xtal 36 decimal
Init1       MOVWF   TempByte

       BTFSS   PIR1,0     ;Test for timer overflow Should not have to wait for clock to go low
       GOTO   CountDown1
       BCF   PIR1,0     ;Clear timer 1 overflow flag
       BSF   Flag,2     ;Set a flag here to show invalid reading
       Return       ; **********Return from here if no signal

             ;   wait about 38 us to confirm we are in interrecord gap
CountDown1     BTFSC   Clock1     ; Check clock for low       0.8 us
       GOTO   Init1     ; Clock high, start over again
       DECFSZ    TempByte,F   ; Clock low, start countdown     0.4 us    
       GOTO   CountDown1   ;           0.8 us (while looping)

Get_Byte1     BSF   rBitCnt,3     ; Initialize bitcount to 8
       CLRF   TempByte   ; Initialize receiving byte


;   Loop waiting for start of clock pulse
Wait_Clock_High1   BTFSS   Clock1       ; Check clock for high
       GOTO   Wait_Clock_High1   ; Clock low, wait for clock to go high

;       BCF   PORTB,0     ;!!!! TEST ONLY  We are now at the start of the data frame

       BTFSS   PIR1,0     ;Test for timer overflow
       GOTO   Clock_Low1
       BCF   PIR1,0     ;Clear timer 1 overflow flag
       DECFSZ   Timeout,F     ;If Timeout reaches zero we have waited too long

       GOTO   Clock_Low1
       BSF   Flag,2     ;Set a flag here to show invalid reading
       Return       ; **********Return from here if no signal within 420 ms



Clock_Low1
           ;Could Enable clock 1 and global interrupt here
           ;to avoid being hung in this routine
       BTFSC   Clock1     ; Clock high, wait for clock to go low
       GOTO   Clock_Low1   ;

;This is the edge of the clock pulse we use to clock in data
       BTFSC   Data1     ; Check data signal for low
       BSF   STATUS,C   ; Set Carry bit
       RRF   TempByte,1   ; Shift in a bit
       DECFSZ   rBitCnt,1   ; Decrement bitcounter
       GOTO   Clock_High1   ; Repeat for next bit
       GOTO   Save_Byte1   ; 8 bits done
Clock_High1     BTFSS   Clock1     ; Check clock for high
       GOTO   Clock_High1   ; Clock high, wait for low
       GOTO   Clock_Low1   ; Repeat waiting for clock low

Save_Byte1     MOVF   TempByte, W   ; TempByte holds data
       MOVWF   0     ; Move data in Z1Data
       DECFSZ   FSR,1     ; Point to next byte in Z1Data
       DECFSZ   rDataCnt,1   ; Decrement datacounter
       GOTO   Get_Byte1   ; Get next byte
       RETURN
           ; All bytes done


;            ----------------------------------------------
;           **** Get data from scale 2  ****

GetData2
;       BSF   PORTB,0     ;!!!!!!!  TEST ONLY
       BCF   Flag,2     ;Clear invalid reading flag
       BCF   T1CON,0     ;Stop timer 1
       MOVLW   0x31     ;Timer on Prescaler 1:8
       MOVWF   T1CON
       CLRF   TMR1H     ;Clear timer 1 high (Clearing timer registers gives about 105 ms timeout
       CLRF   TMR1L     ;Crear timer 1 low
       BCF   PIR1,0     ;Clear timer 1 overflow flag
       MOVLW   0x04     ;"Timeout" will reach zero in 420 ms)
       MOVWF   Timeout
       BSF   T1CON,0     ;Start timer 1

       MOVLW   Z2Data5
       MOVWF   FSR
       MOVLW   6
       MOVWF   rDataCnt   ;Total number of bytes to get from scale (6)
       CLRF   rBitCnt
       MOVLW   0x24     ;For 20 Mhz xtal 36 decimal
Init2       MOVWF   TempByte

       BTFSS   PIR1,0     ;Test for timer overflow Should not have to wait for clock to go low
       GOTO   CountDown2
       BCF   PIR1,0     ;Clear timer 1 overflow flag
       BSF   Flag,2     ;Set a flag here to show invalid reading
       Return       ; **********Return from here if no signal

             ;wait about 38 us confirm it is still low
CountDown2     BTFSC   Clock2       ; Check clock for low       0.8 us
       GOTO   Init2     ; Clock high, start over again
       DECFSZ    TempByte,F   ; Clock low, start countdown     0.4 us    
       GOTO   CountDown2   ;           0.8 us (while looping)

Get_Byte2     BSF   rBitCnt,3     ; Initialize bitcount to 8
       CLRF   TempByte   ; Initialize receiving byte

;   Loop waiting for start of clock pulse
Wait_Clock_High2   BTFSS   Clock2       ; Check clock for high
       GOTO   Wait_Clock_High2   ; Clock low, wait for clock to go high

;       BCF   PORTB,0     ;!!!! TEST ONLY  We are now at the start of the data frame

       BTFSS   PIR1,0     ;Test for timer overflow
       GOTO   Clock_Low2
       BCF   PIR1,0     ;Clear timer 1 overflow flag
       DECFSZ   Timeout,F     ;If Timeout reaches zero we have waited too long

       GOTO   Clock_Low2
       BSF   Flag,2     ;Set a flag here to show invalid reading
       Return       ; **********Return from here if no signal within 420 ms



Clock_Low2     BTFSC   Clock2       ; Clock high, wait for clock to go low
       GOTO   Clock_Low2   ;

;This is the edge of the clock pulse we use to clock in data
       BTFSC   Data2     ; Check data signal for low
       BSF   STATUS,C     ; Set Carry bit
       RRF   TempByte,1     ; Shift in a bit (From top bit down)
       DECFSZ   rBitCnt,1   ; Decrement bitcounter
       GOTO   Clock_High2   ; Repeat for next bit
       GOTO   Save_Byte2   ; 8 bits done
Clock_High2     BTFSS   Clock2     ; Check clock for high
       GOTO   Clock_High2   ; Clock high, wait for low
       GOTO   Clock_Low2   ; Repeat waiting for clock low

Save_Byte2     MOVF   TempByte, W   ; TempByte holds data
       MOVWF   0     ; Move data in Z2Data
       DECFSZ   FSR,1     ; Point to next byte in Z2Data
       DECFSZ   rDataCnt,1   ; Decrement datacounter
       GOTO   Get_Byte2   ; Get next byte
   ;     BCF   PORTB,0     ;!!!!!!!  TEST ONLY
       RETURN
           ; All bytes done


;           ----------------------------------------------

SetSign     ;Check if negative - If so convert to positive and set sign bit

       CLRF    Flag   ; Assume positive
       BTFSS   rData0,7     ; Test if negative  (MSB of second frame)
       GOTO   Not_Neg     ; No...
                 ;
                 ; Sample is negative so make one's complement
       COMF   rData2,F   ;
       COMF   rData1,F   ;
       COMF   rData0,F     ;
       BSF   Flag,0     ; Flag negative

;Should really increment rData here to make 2's complemant or count will be wrong by 1

;       incfsz   rData2,f
;       goto   neg1
;       incfsz   rData1,f
;       goto   neg1
;       incfsz   rData0,f
;
Not_Neg  
       return

;           ----------------------------------------------

;********************************************************************************************************
;*                           *
;*                           *
;*           **** Add Z1Data to Z2Data  Subroutine ****       *
;*                           *
;*   Results in ZCombData0 to ZCombData2                 *
;********************************************************************************************************


ADD_24Bit
;           Copy Z1Data to ZCombData
       movfw   Z1Data2
       movwf   ZCombData2
       movfw   Z1Data1
       movwf   ZCombData1
       movfw   Z1Data0
       movwf   ZCombData0


;           Add Z2Data to ZCombData
       movfw   Z2Data2
       addwf   ZCombData2,f   ;Low byte
;
       movfw   Z2Data1
       btfsc   STATUS,C   ;Skip if no carry
       incfsz   Z2Data1,w
       addwf   ZCombData1,f   ;Middle byte
;
       movfw   Z2Data0
       btfsc   STATUS,C   ;Skip if no carry
       incfsz   Z2Data0,w
       addwf   ZCombData0,f   ;High byte
       return

;         --------------------------------------------------



;   *************************************************************************************************
;   *   Send Frames                     *
;   *  Function    Output the pair of 3 byte data frames to DRO           *
;   *  Data to be output is in locations ZCombData0, ZCombData1 & ZCombData2       *
;   *  !! NOTE both frames of the pair contain the same data            *
;   *************************************************************************************************
Send_Frames         ;Subroutine
         bcf  Z_CombClock_out  ; Clear the clock line
       bcf  Z_CombData_out  ; Clear the data line
       BSF   Out_Enable

       movlw  2  ; Set the word iterator to 2  (3 byte frames of data ?)
       movwf  frames  ;

  
next_word:
       movlw  ZCombData2    ; Load the file register pointer  (With LSB of frame)
       movwf  FSR  ; with the address of the data to be sent
  
       movlw  3  ; Set the byte iterator to 3  (3 bytes per frame)
       movwf  bytes  ;
  
       movlw  7  ; Set the bit iterator to 7 (To count bit shifts)
       movwf  bits  ;
  
       bsf  Z_CombClock_out  ; Rising clock edge  (Clock set high)


;           *** Output state of data bit ***
       btfsc  INDF,0  ; Output data bit 0
       bsf  Z_CombData_out     ; THIS LINE HAD BEEN ACCIDENTALLY DELETED IN COPY ON SCHUMATECH FORUM. !!!!!!!!!
       btfss  INDF,0
       bcf  Z_CombData_out
;

       rrf  INDF,F  ; Advance to next data bit
  
       movlw  DELAY_55US  ; Delay 55 microseconds    (Value = 0xFA)   (This is the start clock pulse)
       movwf  param1  ;
       clrf  param2  ;
       call  delay_cycles  ;
  
       bcf  Z_CombClock_out  ; Falling clock edge     (Set clock low)



;           *** Delay 1 bit time ***  
       movlw  DELAY_6_6US  ; Delay 6.6 microseconds  (Value = 0x0A) (Bit time)
       movwf  temp1
delay_1: 
       decfsz  temp1,F
       goto  delay_1
;           End of 6.6 us delay

;

       bsf  Z_CombClock_out  ; Rising clock edge  (Set clock high)

next_byte:
next_bit:
       btfsc  INDF,0  ; Output next data bit
       bsf  Z_CombData_out
       btfss  INDF,0
       bcf  Z_CombData_out
;

       rrf  INDF,F  ; Advance to next data bit
;
;           ; Delay 6.6 microseconds      
       movlw  DELAY_6_6US
       movwf  temp1
delay_2: 
       decfsz  temp1,F
       goto  delay_2
;           End of 6.6 us delay



       bcf  Z_CombClock_out  ; Falling clock edge     (Set clock low)

          ; Delay 6.6 microseconds  
       movlw  DELAY_6_6US
       movwf  temp1
delay_3: 
       decfsz  temp1,F
       goto  delay_3
;           End of 6.6 us delay


  
       bsf  Z_CombClock_out  ; Rising clock edge     (Set clock high)

       decfsz  bits,F  ; Decrement the bit iterator
       goto  next_bit  ; and continue if not zero
;

       rrf  INDF,F  ; Rotate to the first data bit  (Is this just to leave the byte unchanged ?)
       decf  FSR,F  ; Decrement the file register pointer
       movlw  8  ; Set the bit iterator to 8
       movwf  bits  ;
       decfsz  bytes,F  ; Decrement the byte iterator
       goto  next_byte  ; and continue if not zero


;               *** 55 us delay at end of frame ***  
       movlw  DELAY_55US  ; Delay 55 microseconds
       movwf  param1  ;
       clrf  param2  ;
       call  delay_cycles  ;
;               *** End of 55 us delay ***


       decfsz  frames,F  ; Decrement the word iterator  (I think word means frames !)
       goto  next_word  ; and continue if not zero

;               *** End of frame set clock & data low ***  
       bcf  Z_CombClock_out  ; Clear the clock line
       bcf  Z_CombData_out  ; Clear the data line
;               *** Frame has now been output ***
       BCF   Out_Enable
       return
;

;     -----------------------------------------------------------------------------

;   *************************************************************************************************
;   *         Convert to mm or inches siubroutine         *
;   *     Input value in 0, Rdata1 & rData2             *
;   *     Result in rBin, rBin+1 & rBin+2              *
;   *************************************************************************************************

ConvertI           ;Convert to inches

       movlw   D'125'     ; 125/256 - For Inches
       MOVWF   mFactor     ;
       movlw   D'8'     ; For Inches  (256 = 2^8)
       MOVWF   rBitCnt     ; Setup bit count (N)
       goto   Convert

ConvertM           ;Convert to milimeters
       MOVLW   D'127'     ; 127/1024 - MM
       MOVWF   mFactor     ;
       MOVLW   D'10'     ; For mm  (1024 = 2^10)
       MOVWF   rBitCnt     ; Setup bit count (N)
                 ; --- 24 * N (frac) multiply, fractional not calculated

Convert

       CLRF   rBin   ; Clear product
       CLRF   rBin+1   ;
       CLRF   rBin+2   ;
                 ;
MulNext       BCF   STATUS,C     ; Shift in a 0
       RRF   mFactor,F     ; Get the lsb of multiplier
       BTFSS   STATUS,C   ; Check the lsb
       GOTO   No_Add     ; Clear...
                 ; -- 24 bit add
       MOVF   rData2, W   ; LSB
       ADDWF   rBin+2,F;
                 ;
       MOVF   rData1, W   ; Middle byte
       BTFSC   STATUS,C   ;
       INCFSZ   rData1,W   ;
       ADDWF   rBin+1,F;
                 ;
       MOVF   rData0, W   ; MSB
       BTFSC   STATUS,C   ;
       INCFSZ   rData0,W     ;
       ADDWF   rBin,F   ;
                 ;
No_Add       RRF   rBin,F     ; Shift product
       RRF   rBin+1,F   ;
       RRF   rBin+2,F   ;
                 ;
       DECFSZ   rBitCnt,F   ; Dec bit count
       GOTO   MulNext     ;
       RETURN         ;

;       --------------------------------------------------------------
    

BintoBCD  
       BCF   STATUS,C
       MOVLW   D'24'     ; 24-Bits
       MOVWF   rBitCnt     ; Make cycle counter
       CLRF   rBCD     ; Clear result area
       CLRF   rBCD+1     ;
       CLRF   rBCD+2     ;
       CLRF   rBCD+3     ;

Loop       MOVLW   rBCD     ;Pointer to lowest BCD location
       MOVWF   FSR
       MOVLW   4
       MOVWF   rCnt

BintoBCD1    
       MOVLW   0x33
       ADDWF   INDF,F     ;
       BTFSC   INDF,3     ; top bit of low nibble ?  (If the original nibble was 5 or greater then adding 3
             ; will cause the top bit of nibble to be set)
       ANDLW   0xF0     ; Mask for bits 4 - 7 ?
       BTFSC   INDF,7     ; top bit of high nibble ? (If the original nibble was 5 or greater then adding 3
             ; will cause the top bit of nibble to be set)
       ANDLW   0x0F     ; Mask for bits 0 - 3
       SUBWF   INDF,F     ; Get back to the value befor the 3 was a added to each nibble
       INCF   FSR,F     ; Inc pointer to next byte
       DECFSZ   rCnt,F     ; Decrement counter  (Count to 4)
       GOTO   BintoBCD1   ; four times round this loop

       RLF   rBin+2,F   ; Get another bit
       RLF   rBin+1,F   ;
       RLF   rBin+0,F   ;
       RLF   rBCD+3,F   ; Put it into BCD
       RLF   rBCD+2,F   ;
       RLF   rBCD+1,F   ;
       RLF   rBCD+0,F   ;
       DECFSZ   rBitCnt,F   ; All done?
       GOTO   Loop     ; No, loop  (24 times round this loop)
       return

;             ----------------------------------

FormatL           ;Format for 1 line of display output
              
             ; --- CONVERT THE 10 BINARY CODED DIGITS (5 BYTES) STARTING AT rBcd+3
             ; <BCD> INTO AN ASCII STRING ALSO STARTING AT <BCD>. ORIGINAL
             ; BCD DIGITS ARE LOST.
             ; On entry 'W' has to be set to output buffer address


       MOVWF   FormatPtr   ;Save for later
       ADDLW   0x08     ;Add 8
       MOVWF   rPto     ; Destination pointer

       MOVLW   rBCD+3     ;
       MOVWF   rPti     ; Source pointer
       CALL   BCDtoASCII   ; On return rPto will be rDisp1+0x06 (ie decremented by 2)
             ;
       MOVLW   '.'     ;  decimal place
       MOVF   rPto,W     ; Get current output pointer
       MOVWF   FSR
       MOVLW   '.'     ;  decimal place
       MOVWF   INDF     ;Write to output buffer
       DECF   rPto,F     ; will now = sDisp1+05
                 ;
       MOVLW   rBCD+2     ;
       MOVWF   rPti     ; Source pointer
       MOVLW   3     ; 3 Bytes to process
       MOVWF   rCnt     ;
Next_BCD     CALL   BCDtoASCII   ;
       DECFSZ   rCnt,F     ;
       GOTO   Next_BCD   ;
       MOVFW   FormatPtr
       MOVWF   FSR     ;
       MOVLW   1     ;
       MOVWF   rCnt     ;
       MOVLW   ' '     ; space character ?
FillSpaces     MOVWF   INDF     ;
       INCF   FSR,F     ;
       DECFSZ   rCnt,1
       GOTO   FillSpaces   ; 4 space characters
    
       MOVLW   4       ;
       MOVWF   rCnt     ;
ReplaceZero     MOVLW   '0'       ;
       XORWF   INDF,W     ;is the character "0"
       BTFSS   STATUS,Z   ;
       GOTO   PutSign     ;
       MOVLW   ' '       ;
       MOVWF   INDF     ;
       INCF   FSR,F       ;
       DECFSZ   rCnt,1     ;
       GOTO    ReplaceZero   ;
PutSign  
;       BTFSS   Flag,0   ; Test if negative !!!!!! Original test.

       BTFSC   Flag,0   ; Test if negative  !!!!!! Changed to skip on clear

       GOTO   Done     ; No sign to put
       MOVLW   '-'       ; Put negative sign before
       DECF   FSR,F       ;
       MOVWF   INDF     ;
Done       RETURN


;           -------------------------------------


;         On entry pointer to input bytes in 'rPti'
;            Pointer to output buffer in 'rPto'
;            Input pointer (rPti) decremented by 1 on exit rdom entry value
;            Output pointer (rPto) decremented by 2 on exit from entry value
BCDtoASCII

       MOVF   rPti,W     ; Get current input pointer
       MOVWF   FSR       ;
       DECF   rPti,F     ; Prepare for next
       MOVF   INDF,W     ; Get 2 BCDS
       MOVWF   TempByte   ; Save for later
       MOVF   rPto,W     ; Get current output pointer
       MOVWF   FSR       ;
       DECF   rPto,F     ; Prepare for next
       DECF   rPto,F     ;
       MOVF   TempByte,W   ; Get digits back
       ANDLW   0x0F     ; Process LSD Low nibble
       MOVWF   INDF     ;
       MOVLW   '0'     ; Character zero (0x30) not value of 0
       ADDWF   INDF,F     ; To output
       DECF   FSR,F     ;
       SWAPF   TempByte,W   ; Process MSD Swap high ad low nibbles
       ANDLW   0x0F     ;
       MOVWF   INDF     ;
       MOVLW   '0'     ; Character zero (0x30) not value of 0
       ADDWF   INDF,F     ; To output
       RETLW   0

;           -------------------------------------
Version           ;Display version message
       movlw   0x80     ; Cursor at 0x00 Start of top line
       call   Send_Cmd

       movlw   ' '
       call   Write_Char
       movlw   'V'
       call   Write_Char
       movlw   '0'
       call   Write_Char
       movlw   '3'
       call   Write_Char
       movlw   'H'
       call   Write_Char
       movlw   '0'
       call   Write_Char
       movlw   '0'
       call   Write_Char

;       return



No_Signal         ;Display "No Signal message at startup"


       MOVLW   0xC0     ; Cursor at 0x40 Start of second line
       call   Send_Cmd

       movlw   'N'
       call   Write_Char
       movlw   'o'
       call   Write_Char
       movlw   ' '
       call   Write_Char
       movlw   'S'
       call   Write_Char
       movlw   'i'
       call   Write_Char
       movlw   'g'
       call   Write_Char
       movlw   'n'
       call   Write_Char
       movlw   'a'
       call   Write_Char
       movlw   'l'
       call   Write_Char
       return

Slow             ;Display slow message on third line
       MOVLW   0x94     ; Cursor at 0x40 Start of second line
       call   Send_Cmd

       movlw   'S'
       call   Write_Char
       movlw   'l'
       call   Write_Char
       movlw   'o'
       call   Write_Char
       movlw   'w'
       call   Write_Char
       movlw   ' '
       call   Write_Char
       movlw   ' '
       call   Write_Char
       MOVLW   '0'     ; Character zero (0x30) not value of 0
       ADDWF   Timeout,W
       CALL   Write_Char
       return

Fast             ;Display Fast message on third line
       MOVLW   0x94     ; Cursor at 0x40 Start of second line
       call   Send_Cmd

       movlw   'F'
       call   Write_Char
       movlw   'a'
       call   Write_Char
       movlw   's'
       call   Write_Char
       movlw   't'
       call   Write_Char
       movlw   ' '
       call   Write_Char
       movlw   ' '
       call   Write_Char
       MOVLW   '0'     ; Character zero (0x30) not value of 0
       ADDWF   Timeout,W
       CALL   Write_Char
       return
T_out             ;Display Tout message on third line
       MOVLW   0x94     ; Cursor at 0x40 Start of second line
       call   Send_Cmd

       movlw   'T'
       call   Write_Char
       movlw   '_'
       call   Write_Char
       movlw   'o'
       call   Write_Char
       movlw   'u'
       call   Write_Char
       movlw   't'
       call   Write_Char
       movlw   ' '
       call   Write_Char
       return

T_out2             ;Display Tout message on third line
       MOVLW   0x94     ; Cursor at 0x40 Start of second line
       call   Send_Cmd

       movlw   'T'
       call   Write_Char
       movlw   '2'
       call   Write_Char
       movlw   'o'
       call   Write_Char
       movlw   'u'
       call   Write_Char
       movlw   't'
       call   Write_Char
       movlw   ' '
       call   Write_Char
       return

Clear             ;Fill with spaces  third line
       MOVLW   0x94     ; Cursor at 0x40 Start of second line
       call   Send_Cmd

       movlw   ' '
       call   Write_Char
       movlw   ' '
       call   Write_Char
       movlw   ' '
       call   Write_Char
       movlw   ' '
       call   Write_Char
       movlw   ' '
       call   Write_Char
       movlw   ' '
       call   Write_Char
       movlw   ' '
       call   Write_Char
       movlw   ' '
       call   Write_Char
       movlw   ' '
       call   Write_Char
       movlw   ' '
       call   Write_Char
       movlw   ' '
       call   Write_Char
       movlw   ' '
       call   Write_Char
       return


;           -------------------------------------
;   Routine to see if scale 1 is in fast sample mode.
;   On exit Z  flag is set if in Fast mode
Mode_Test1
  
       CALL   GetData1
       BTFSS   Flag,2     ;Check for timeout
       GOTO   MT1a
       Call   T_out
       RETURN
MT1a
       MOVLW   0x0A     ;10 * 100 us = 1 ms
       CALL   X_Delay100   ;
       CALL   GetData1
       BTFSS   Flag,2     ;Check for timeout
       GOTO   MT1b
       Call   T_out2
       RETURN

MT1b
       MOVLW   0x04
       SUBWF   Timeout,w   ;If Timeout still equals 4 then must be in fast mode
       BTFSC   STATUS,Z
       GOTO   MT1c
       Call   Slow
       BCF   Flag,3
       RETURN

MT1c
       Call   Fast
       BSF   Flag,3
       RETURN

;           -------------------------------------
;   Routine to see if scale 2 is in fast sample mode.
;   On exit Z  flag is set if in Fast mode

Mode_Test2
  
       CALL   GetData2
       BTFSS   Flag,2     ;Check for timeout
       GOTO   MT2a
       Call   T_out2
       RETURN
MT2a
       MOVLW   0x0A     ;10 * 100 us = 1 ms
       CALL   X_Delay100   ;
       CALL   GetData2
       BTFSS   Flag,2     ;Check for timeout
       GOTO   MT2b
       Call   T_out2
       RETURN

MT2b
       MOVLW   0x04
       SUBWF   Timeout,w   ;If Timeout still equals 4 then must be in fast mode
       BTFSC   STATUS,Z
       GOTO   MT2c
       Call   Slow
       BCF   Flag,4
       RETURN

MT2c
       Call   Fast
       BSF   Flag,4
       RETURN

;           -------------------------------------
Set_Fast1
       CALL   Mode_Test1
     movlw   D'200'     ;Delay 2 seconds   ;!!!!!! TESTING ONLY
     call   X_Delay10ms  
       CALL   Clear
       BTFSC   Flag,3     ;If  flag,3 is set we are in fast sample mode
       RETURN       ;Allready in fast mode

       BSF   Sel_A     ;Select scale 1 data
       BCF   Sel_B
       BCF   INH4051     ;Remove inhibit ie pull data 1 high

       MOVLW   D'20'     ;Set for delay of 200 ms
       CALL   X_Delay10ms

    
       BSF   INH4051     ;Enable inhibit ie no clock or data pull ups
       CALL   Mode_Test1
     movlw   D'200'     ;Delay 2 seconds   ;!!!!!! TESTING ONLY
     call   X_Delay10ms
       BTFSC   Flag,3     ;If  flag,3 is set we are in fast sample mode  
       RETURN
       BCF   Sel_A     ;Select scale 1 clock
       BCF   Sel_B
       BCF   INH4051     ;Remove inhibit ie pull clock 1 high

       MOVLW   D'20'     ;Set for delay of 200 ms
       CALL   X_Delay10ms
      
       BSF   INH4051     ;Enable inhibit ie no clock or data pull up's
       RETURN    
;           -------------------------------------

Set_Fast2
       CALL   Mode_Test2
     movlw   D'200'     ;Delay 2 seconds   ;!!!!!! TESTING ONLY
     call   X_Delay10ms  
       CALL   Clear
       BTFSC   Flag,4     ;If  flag,4 is set we are in fast sample mode  
       RETURN       ;Allready in fast mode

       BSF   Sel_A     ;Select scale 2 data
       BSF   Sel_B
       BCF   INH4051     ;Remove inhibit ie pull data 1 high

       MOVLW   D'20'     ;Set for delay of 200 ms
       CALL   X_Delay10ms
      
       BSF   INH4051     ;Enable inhibit ie no clock or data pull ups
       CALL   Mode_Test2
     movlw   D'200'     ;Delay 2 seconds   ;!!!!!! TESTING ONLY
     call   X_Delay10ms
       BTFSC   Flag,4     ;If  flag,4 is set we are in fast sample mode  
       RETURN
       BCF   Sel_A     ;Select scale 2 clock
       BSF   Sel_B
       BCF   INH4051     ;Remove inhibit ie pull clock 1 high

       MOVLW   D'20'     ;Set for delay of 200 ms
       CALL   X_Delay10ms
      
       BSF   INH4051     ;Enable inhibit ie no clock or data pull up's
       RETURN    
;           -------------------------------------
Set_Slow1
       CALL   Mode_Test1
       BTFSS   Flag,3     ;If  flag,3 is set we are in fast sample mode  
       RETURN       ;Allready in slow mode

       BSF   Sel_A     ;Select scale 1 data
       BCF   Sel_B
       BCF   INH4051     ;Remove inhibit ie pull data 1 high

       MOVLW   D'20'     ;Set for delay of 200 ms
       CALL   X_Delay10ms
      
       BSF   INH4051     ;Enable inhibit ie no clock or data pull up's
       RETURN
;           -------------------------------------  
Set_Slow2
       CALL   Mode_Test2
       BTFSS   Flag,4     ;If  flag,4 is set we are in fast sample mode  
       RETURN       ;Allready in slow mode

       BSF   Sel_A     ;Select scale 2 data
       BSF   Sel_B
       BCF   INH4051     ;Remove inhibit ie pull data 1 high

       MOVLW   D'20'     ;Set for delay of 200 ms
       CALL   X_Delay10ms
      
       BSF   INH4051     ;Enable inhibit ie no clock or data pull up's
       RETURN
;           -------------------------------------    


       END
The subroutines for the display are at the start of the subroutine section. This particular program reads two digital scales/calipers, displays the output of each scale and also the sum of the readings. (This was for combining the Main Z axis with the quill position on a milling machine.)
It will only be the display initialization and subroutines that are of interest to you. I hope they are usefull to you.

Les.
 

MrChips

Joined Oct 2, 2009
21,325
Why do you need to go with 38,400 baud? Visual refresh rate is lower than 50Hz. I usually settle for 4800 or 9600 baud.

I have used 4 x 20 LCD extensively in the past. Sorry, all my code is for Motorola HC11 and Atmel AVR.

Typical hangups happen in the initialization stage when switching from 8-bit mode to 4-bit mode. There are specific guidelines on how to overcome this. I will try to dig this up. In essence, it requires having to hit the controller twice.
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
9,699
Why do you need to go with 38,400 baud? Visual refresh rate is lower than 50Hz. I usually settle for 4800 or 9600 baud.
I understand the limitations of visual perception. Simply put, as I struggle to make faster and/or more compact code, and then go to print something at 1 ms/character -- even though I may not be cognizant of the 10 to 20 ms to print a line -- I think what a waste of time. In other words, just a challenge.

As for the SF display, it got rid of the backpack and integrated the PIC MCU on the same PCB as the display controller COB's. It is nice looking. As to why I think baud speed may affect function, the SF display performed better at 9600 baud than it did at 38,400 baud; although, you could still get it to lock up. Of course, correlation does not prove causation, but it can be a clue.

John
 

BobaMosfet

Joined Jul 1, 2009
1,118
@jpanhalt-

You need to read a datasheet on the display or the chip behind the display. 1ms per character may be too fast-- It may expect 5ms or 1ms depending on command mode .v. display modes, etc. If you try to drive it too fast, you will get unexpected/unreliable results. An LCD has different drive considerations that have nothing to do with anything you're doing, but will prevent the display from reacting beyond a certain speed.
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
9,699
@jpanhalt-

You need to read a datasheet on the display or the chip behind the display. 1ms per character may be too fast--
Not at all, that is approximately 9600 baud. The Parallax displays work great at 19,200 baud.

One member here got a 2x16 display going well over 100,000 baud. I suspect you may be confusing the "write" time for the Hitachi controller (specified as about 37 to 40 us) with the bit time for characters using the 1-wire RS232-type interface, i.e., at 9600 baud the bit time works out to be 104 us, which for 10 bits works out to be 1.04 ms er character (1/9600 = 1.042 ms). The write time for the controller is per write, as I understand it, so that is either 4 or 8 bits per write.

Writing too quickly will cause the sort of failure the SF device displays. I will be reading the busy flag (DB7) to avoid that -- just a theory at this point. The SF Display is outsourced, and SF claimed not to have any information on it.
 

MMcLaren

Joined Feb 14, 2010
844
It is kind of neat that the 16F88 interface is part of the LCD board (no backpack required).

Here's the source listing from the Spark Fun product page. I'm curious if anyone can tell which compiler was being used (High Tech, CCS, etc.)?
C:
/*
    1-16-04
    Copyright Spark Fun Electronics© 2004
 
    Uses 16F688 to drive Parallel LCD with Serial Commands.
 
    For educational and hobbyist use only. NOT for commerical use.


    Thanks to Olimex for providing the original C test code for the PIC-MT-C w/ 16F873
 
    1-17-04 Started migrating 16F873A code to 16F630


    1-17-04pm Works well from user keyboard input. No busy flag checking yet.
              Ok, busy flag works.


    1-19-04 Got commands to pass through after decimal 254 is seen. The special 8-bit data
            mode command is also correctly ignored.
         
    1-30-04 v1.1 Added 16x4 line support
 
    2-10-04 v1.2 Added BL Control. Moved to direct RC0-RC3 = D4-D7 mapping. This significantly
            improved timing between characters.
 
    2-12-04 Added EEPROM recording of Backlight State. Also added Super Special
            Command input. Pass the '|' character to the LCD, then pass '1' to turn
            on/off backlight.
         
            Added universal support for any LCD upto 4 lines, 40 characters wide.
         
    2-16-04 New PCBs v1.1 - Replaced some if statements with EL1, EL2, etc, in the 4 line wrapping
            routine to try to speed things up.
         
    2-17-04 v1.3 Timing has now been nearly reduced to within one stop bit. In the past,
            wrapping characters were some times lost because of the time required to relocate
            the cursor. Now almost all characters are seen, every time, every location.
            There are stuff certain combinations that cause a character miss.
 
    3-3-04 New PCB - 1.1a. BL Control has been routed to PGC - bad idea. Causes problems with programming.
           Unit must be powered +5V while programming to get it to work. Will move this line on next revision.
         
    4-5-04 Customer reported problems with interface. Baud rate was too low. The test bed was running
           at much too low of a speed. The test bed has been corrected, the code must now be tweaked.
         
    4-14-04 Added a special command 8 that will re-initialize the LCD. There are certain circumstances (low
            power) where the PIC will keep running, but the LCD will shut down.


    6-1-04 Re-routed some pins for a smoother PCB layout. Firmware had to be updated.


    7-7-04 Added support for 40x2 displays
 
    12-15-04 New version - SerLCD v2 using 16F688
             Incoming characters buffered
             PWM of back light
             Turning off splash screen
             Higher data speeds
           
             We now use the 16F688 with onboard UART and internal 8MHz oscillator. The internal oscillator is much
             tighter tolerance and the UART interrupt is used. The incoming characters are automatically buffered,
             up to 80 characters can be stored at a time in case the LCD is busy doing other things.
           
             The backlight is now pulse width modulated. What this means is you can set how bright the backlight is
             from 0 (off) to 30 (completely on). This will greatly effect how much current the module uses while
             retaining part of the ability to see in low light conditions. The backlight defaults to maximum
             output (brightest) unless otherwise configured by the user. To control the backlight, send the 'special
             command' character (124) followed by 0b.100?.???? where ?? is the value from 0-30.
           
             You can now turn off the splash screen. Send the 'special command' character 124 followed by decimal 9.
             The splash screen option will toggle and will take effect the next time the LCD is initialized (power-up).
 
    3-30-05  Added busy checking before the LCD is init'd. SPBRG is set to 51 per datasheet for 16F688.
             Extended the delays a bit during LCD init to try to avoid hangups with 3% of units that report hanging.
 
    4/29/09  Changed code for integrated LCD with backpack, outsourced hardware. Control lines to LCD from PIC have changed.
 
*/


#define Clock_10MHz
#define Baud_9600
//#define DEBUG


#include "C:\Global\PIC\C\16F88.h"  // device dependent interrupt definitions
#include "C:\Global\PIC\C\int16CXX.H"



#pragma config|= 0x3F02
//#pragma config|= 0x2F8A   //10MHz xtal
//#pragma config|= 0x29D3     //int RC


//LCD Type
#define Display_16x2    0
#define Display_16x4    0


//Hardware port definitions
#define     RS              PORTB.4
#define     R_W             PORTB.1
#define     BL_Control      PORTA.4
#define     E               PORTB.0

//#define     STAT            PORTA.4


#define     D4              PORTA.0
#define     D5              PORTA.1
#define     D6              PORTA.2
#define     D7              PORTA.3


//Constant definitions
#define     FALSE       0
#define     TRUE        1


#define     LCD_BACKLIGHT_SETTING   0
#define     LCD_WIDTH_SETTING       1
#define     LCD_LINE_SETTING        2
#define     LCD_BRIGHTNESS_SETTING  3
#define     LCD_SPLASH_ENABLE       4
#define     LINE1_START             5
#define     LINE2_START             25
#define     BAUD_RATE               45




#define     CLR_DISP        0b.0000.0001 //Clear display


#define     CUR_HOME        0b.0000.0010    //Move cursor home and clear screen memory
#define     CUR_RIGHT       0b.0001.0100    //Move cursor one to right
#define     CUR_LEFT        0b.0001.0000    //Move cursor one to left


#define     SCROLL_RIGHT    0b.0001.1100    //Scroll entire screen right one space
#define     SCROLL_LEFT     0b.0001.1000    //Scroll entire screen left one space


#define     DISP_ON         0b.0000.1100    //Turn visible LCD on
#define     DISP_OFF        0b.0000.1000    //Turn visible LCD off


#define     UNDERLINE_ON    0b.0000.1110    //Turn on underlined cursor
#define     UNDERLINE_OFF   0b.0000.1100    //Turn off underlined cursor


#define     BLINKCUR_ON     0b.0000.1101    //Turn on blinking box cursor
#define     BLINKCUR_OFF    0b.0000.1100    //Turn off blinking box cursor


#define     DUALCUR_ON      0b.0000.1111    //Turn on blinking box and underline cursor
#define     DUALCUR_OFF     0b.0000.1100    //Turn off blinking box and underine cursor


#define     SET_CURSOR      0b.1000.0000    //SET_CURSOR + X : Sets cursor position to X


#define     ENTRY_INC       0b.0000.0110 //
#define     DD_RAM_ADDR     0b.1000.0000 //
#define     DD_RAM_ADDR2    0b.1100.0000 //
#define     CG_RAM_ADDR     0b.0100.0000 //


//Global variable declarations
uns8 letter;
static uns8 cursor_position;
uns8 splash_enable;


uns8 backlight_counter;
uns8 brightness_setting;
uns8 Backlight_Setting;


uns8 LCD_Type;
uns8 LCD_Width;
uns8 LCD_Lines;
uns8 EL1, EL2, EL3, EL4;
uns8 CP1, CP2, CP3, CP4;
uns8 baud;


uns8 RX_In;
uns8 Displayed_Letters;


#pragma rambank 1
#define BUFFER_SIZE 80
uns8 RX_Array[BUFFER_SIZE];

#pragma rambank 0
uns8 line1[20];
uns8 line2[20];


//End Global variable declarations



//Interrupt Vectors
#pragma origin 4
interrupt serverX( void)
{
    int_save_registers
    char sv_FSR = FSR;  // save FSR if required


    if(RCIF) //If we have received something from the computer store it in the RX_Array array
    {
     
        RX_In++;
        if(RX_In == BUFFER_SIZE) RX_In = 0;
     
        RX_Array[RX_In] = RCREG;
    }


    if(T0IF) //TMR0 Overflow Interrupt - Occurs every 1024us ~1ms
    {
        backlight_counter++;


        if(backlight_counter == 30)
        {
            backlight_counter = 0; //30ms pulse width period
         
            if (brightness_setting > 0) BL_Control = 0; //Kick on the back light only if brightness setting is greater than 0
        } 


        //Turn off back light after its assigned duration
        if(backlight_counter == brightness_setting) BL_Control = 1;
     
        T0IF = 0; //Clear Interrupt Flag
    }
 
    FSR = sv_FSR;               // restore FSR if saved
    int_restore_registers
}


//Function declarations
void boot_up(void);
void special_commands(void);
void delay_ms(uns16);
void delay_ms(uns16);
uns8 onboard_eeread(uns8 e_address);
void onboard_eewrite(uns8 e_data, uns8 e_address);
void set_baud_rate(void);


void send_char(uns8);
void send_cmd(uns8);
void send_string(const char* incoming_string);
void LCD_wait(void);
void init_lcd(void);


//#include "C:\Global\PIC\C\stdio.c"

//End Function declarations


void main(void)
{
    //Initialize LCD and PIC
    boot_up();
 
    while(1)
    {
        //sleep(); //Wait for incoming command to wake us up...
        //nop(); //Executes after wake-up and before INTF runs
     
        if(RX_In != Displayed_Letters)
        {
            Displayed_Letters++;
            if(Displayed_Letters == BUFFER_SIZE) Displayed_Letters = 0;


            letter = RX_Array[Displayed_Letters];



            //Check for special LCD command
            //===============================================================
            if (letter == 254)
            {
                //Wait for the next letter
                while(RX_In == Displayed_Letters);
             
                //Look at the next letter
                Displayed_Letters++;
                if(Displayed_Letters == BUFFER_SIZE) Displayed_Letters = 0;


                letter = RX_Array[Displayed_Letters];
             
                //Ignore the one command that will send the LCD into 8-bit mode
                if ( (letter >> 4) != 3 ) //If not 0b.0000.0011, then send it to LCD
                    send_cmd(letter);
                 
                //Correct cursor position variable if certain commands are received
                if (letter == CLR_DISP)
                    cursor_position = 0;
                else if (letter.7 == 1) //Move cursor command
                    cursor_position = letter & 0b.0111.1111; //Ignore first bit - obtain address
            } 
            //===============================================================


            //Super Special LCD Commands
            //===============================================================
            else if (letter == 124) //This is the '|' character above '\'
            {
                //Wait for the next letter
                while(RX_In == Displayed_Letters);


                //Look at the next letter
                Displayed_Letters++;
                if(Displayed_Letters == BUFFER_SIZE) Displayed_Letters = 0;


                letter = RX_Array[Displayed_Letters];


                special_commands();
            }
            //===============================================================
         
         
         
            //All else, just print it to the LCD and adjust the cursor position
            //===============================================================
            else
            { 
                #ifdef DEBUG
                    if (letter == '+') brightness_setting++;
                    if (brightness_setting > 30) brightness_setting = 0;
                 
                    if( letter == 'A')
                    {
                        letter = 0b.1000.0000 | 15;
                        special_commands();
                    }
                #endif
             
             
             
             
//Backspace============================================================================             
                if (letter == 8)
                {
                 
                    if(cursor_position == (LCD_Width + 64))        //from beginning of line 4
                    {
                        //send_cmd(CP3);
                        send_cmd(CP3 + LCD_Width - 1);
                        send_char(32);
                        send_cmd(CP3 + LCD_Width - 1);
                        cursor_position = EL3 - 1;
                     
                        //printf("%d\r\n",cursor_position);
                    }
                 
                    else if(cursor_position == LCD_Width)   //from beginning of line 3
                    {
                        //send_cmd(CP2);
                        send_cmd(CP2 + LCD_Width - 1);
                        send_char(32);
                        send_cmd(CP2 + LCD_Width - 1);
                        cursor_position = EL2 - 1;
                     
                        line2[cursor_position - 64] = 32;
                     
                        //printf("%d\r\n",cursor_position);
                    }
                 
                 
                    else if(cursor_position == 64)      //from beginning of line 2
                    {
                     
                        //send_cmd(CP1);
                        send_cmd(CP1 + LCD_Width - 1);
                        send_char(32);
                        send_cmd(CP1 + LCD_Width - 1);
                        cursor_position = EL1 - 1;
                     
                        line1[cursor_position] = 32;
                     
                        //printf("%d\r\n",cursor_position);
                 
                    }
                     
                    else if (cursor_position == 0)      //from beginning of line 1
                    {
                        if(LCD_Lines == 1)
                        {
                            send_cmd(CP1 + LCD_Width - 1);
                            send_char(32);
                            send_cmd(CP1 + LCD_Width - 1);
                            cursor_position = EL1 - 1;
                         
                            //printf("%d\r\n",cursor_position);
                        }
                     
                        else if (LCD_Lines == 2)                   
                        {
                            //send_cmd(CP2);
                            send_cmd(CP2 + LCD_Width - 1);
                            send_char(32);
                            send_cmd(CP2 + LCD_Width - 1);
                            cursor_position = EL2 - 1;
                         
                            //printf("%d\r\n",cursor_position);
                        }
                     
                        else    //4 line display
                        {
                            //send_cmd(CP4);
                            send_cmd(CP4 + LCD_Width - 1);
                            send_char(32);
                            send_cmd(CP4 + LCD_Width - 1);
                            cursor_position = EL4 - 1;
                         
                            //printf("%d\r\n",cursor_position);
                        }
                    }
                 
                    else
                    {
                        send_cmd(CUR_LEFT);
                        send_char(32);
                        send_cmd(CUR_LEFT);
                        cursor_position--;
                     
                        //make sure that the line arrays are correct
                        if (cursor_position < 16) line1[cursor_position] = 32;
                        if ((cursor_position > 63) && (cursor_position < 80)) line2[cursor_position - 64] = 32;
                     
                        //printf("%d\r\n",cursor_position);
                    }
                }
             
             
//Regular character send==========================================================================
                else
                {
                    send_char(letter);
                 
                    //keep the first 2 lines logged for splash screen changes
                    if (cursor_position < 20) line1[cursor_position] = letter;
                    if ((cursor_position > 63) && (cursor_position < 84)) line2[cursor_position - 64] = letter;
 
                    cursor_position++;
                 
                 
                    //When the cursor gets to the end of one line, it must
                    //advance to the next visual line
                    //===============================================================
                    if (cursor_position == EL1) //End of line one
                    {
                        if(LCD_Lines == 1)
                        {
                            cursor_position = 0; //Return to beginning of line 1
                            send_cmd(CP1);
                        }
                        else
                        {
                            cursor_position = 64; //Beginning of line 2
                            send_cmd(CP2);
                        }
                    }
                 
                    else if (cursor_position == EL2) //End of line 2
                    {
                        if(LCD_Lines == 2)
                        {
                            cursor_position = 0; //Return to line 1
                            send_cmd(CP1);
                        }
                        else
                        {
                            cursor_position = LCD_Width; //Beginning of line 3
                            send_cmd(CP3);
                        }
                    }
                 
                    else if (cursor_position == EL3) //End of line 3
                    {
                        cursor_position = EL2; //Beginning of line 4 is the end of line 2
                        send_cmd(CP4);
                    }
                 
                    else if (cursor_position == EL4) //End of line 4
                    {
                        cursor_position = 0; //Beginning of line 1
                        send_cmd(CP1);
                    }
                 
                 
             
                }
                //===============================================================
            }
            //===============================================================
             


        }//End regular character printing
         
   }//End infinite loop


}//End Main


//This is called when the control character is received.
//The control character is 124 or '|' (above the '\' on standard US layout keyboards)
void special_commands(void)
{
    uns8 x;

    //Backlight control
    if (letter == 1)
    {
        brightness_setting = 30;
        onboard_eewrite(brightness_setting, LCD_BRIGHTNESS_SETTING); //Record to EEPROM
        //Backlight_Setting = 0; //Turn on backlight setting
        //BL_Control = Backlight_Setting; //Turn on backlight
        //onboard_eewrite(Backlight_Setting, LCD_BACKLIGHT_SETTING); //Record to EEPROM
    }
    else if(letter == 2)
    {
        brightness_setting = 0;
        onboard_eewrite(brightness_setting, LCD_BRIGHTNESS_SETTING); //Record to EEPROM
        //Backlight_Setting = 1; //Turn off backlight setting
        //BL_Control = Backlight_Setting; //Turn off backlight
        //onboard_eewrite(Backlight_Setting, LCD_BACKLIGHT_SETTING); //Record to EEPROM
    }
 
    //LCD type commands
    else if(letter == 3)
    {
        LCD_Width = 20;
        onboard_eewrite(LCD_Width, 1);
    }
    else if(letter == 4)
    {
        LCD_Width = 16;
        onboard_eewrite(LCD_Width, 1);
    }
    else if(letter == 5)
    {
        LCD_Lines = 4;
        onboard_eewrite(LCD_Lines, 2);
    }
    else if(letter == 6)
    {
        LCD_Lines = 2;
        onboard_eewrite(LCD_Lines, 2);
    }
    else if(letter == 7)
    {
        LCD_Lines = 1;
        onboard_eewrite(LCD_Lines, 2);
    }
    else if(letter == 8) //Re-init command
    {
        init_lcd();
    }


    //Turn on/off splash screen
    else if(letter == 9)
    {
        if (splash_enable == 1) splash_enable = 0;
        else splash_enable = 1;
     
        onboard_eewrite(splash_enable, LCD_SPLASH_ENABLE); //Record to EEPROM
    }
 
    //Save alternate splash screen from line arrays (first two lines)
    else if(letter == 10)   //^j
    {
        for (x = 0; x < 20; x++)
        {
            onboard_eewrite(line1[x], LINE1_START + x); //Record to EEPROM
            onboard_eewrite(line2[x], LINE2_START + x);
        }
     
         
    }
 
    //set baud rate to 2400
    else if(letter == 11)   //^k
    {
        baud = 0;
        set_baud_rate();
        onboard_eewrite(baud, BAUD_RATE);
         
    }
 
 
    //set baud rate to 4800
    else if(letter == 12)   //^l
    {
        baud = 1;
        set_baud_rate();
        onboard_eewrite(baud, BAUD_RATE);
         
    }
 
 
    //set baud rate to 9600
    else if(letter == 13)   //^m
    {
        baud = 2;
        set_baud_rate();
        onboard_eewrite(baud, BAUD_RATE);
         
    }
 
 
    //set baud rate to 14400
    else if(letter == 14)   //^n
    {
        baud = 3;
        set_baud_rate();
        onboard_eewrite(baud, BAUD_RATE);
         
    }
 
 
    //set baud rate to 19200
    else if(letter == 15)   //^o
    {
        baud = 4;
        set_baud_rate();
        onboard_eewrite(baud, BAUD_RATE);
         
    }
 
 
    //set baud rate to 38400
    else if(letter == 16)   //^p
    {
        baud = 5;
        set_baud_rate();
        onboard_eewrite(baud, BAUD_RATE);
         
    }


    //Back light brightness settings
    else if(letter >= 128)
    {
        brightness_setting = letter & 0b.0001.1111; //Brightness is a value from 0-29 only (5-bit)
        onboard_eewrite(brightness_setting, LCD_BRIGHTNESS_SETTING); //Record to EEPROM
    }
 
 
    //LCD_Width is the number of seen characters
    //EL1 is End Line 1 - Normally LCD_Width
    //EL2 is End Line 2 - Normally 64 + LCD_Width
    //EL3 is End Line 3 - LCD_Width + LCD_Width
    //EL4 is End Line 4 - 64 + LCD_Width + LCD_Width


    EL1 = LCD_Width;
    EL2 = 64 + LCD_Width;
    EL3 = LCD_Width + LCD_Width;
    EL4 = 64 + LCD_Width + LCD_Width;
 
    //CP1 = Cursor Position 1 - 0 Always
    //CP1 = CP1 & 0b.1000.000 - this is to set the DRAM command
    //CP2 = 64 Always = 0b.1000.0000 | 64
    //CP3 = LCD_width = 0b.1000.0000 | LCD_Width
    //CP4 = 64 + LCD_Width = 0b.1000.0000 | (64 + LCD_Width)
 
    CP1 = 0b.1000.0000 | 0;
    CP2 = 0b.1000.0000 | 64;
    CP3 = 0b.1000.0000 | LCD_Width;
    CP4 = 0b.1000.0000 | (LCD_Width + 64);
 
}


//Initializes the various ports and interrupts
//Also inits the LCD
void boot_up(void)
{
    uns8 x;
    //OSCCON = 0b.0000.1000; //Setup internal oscillator for 8MHz
    //while(OSCCON.2 == 0); //Wait for frequency to stabilize
 
    //Setup Ports
    ANSEL = 0b.0000.0000; //Disable ADC on all pins
    //CMCON0 = 0b.0000.0111; //Turn off comparator on RA port


    PORTA = 0b.0001.0000;
    TRISA = 0b.1110.0000; //0 = Output, 1 = Input


    PORTB = 0b.0000.0000;
    TRISB = 0b.1110.1100; //0 = Output, 1 = Input (RX on RB2)


    //OPTION.7 = 0; //Enable weak pull-ups
 
    Displayed_Letters = 0;
    RX_In = 0;
 
    //Setup the hardware UART module and interrupts
    //=============================================================
    //baud = onboard_eeread(BAUD_RATE);
    //set_baud_rate();
    SPBRG = 64;

    TXSTA = 0b.0010.0100; //
    RCSTA = 0b.1001.0000; //Receive enable, 8-bit asych continous receive mode


    //BRG16 = 1;
    BRGH = 1;


    RCIF = 0;
    RCIE = 1;
    PEIE = 1;
    //=============================================================


    //Setup TMR0 and interrupts
    OPTION_REG = 0b.0101.0001; //Give Timer0 prescalar of 4
    T0IE = 1;


    GIE = 1; //Enable global interrupts
 
   //for (x = 0; x < 4; x++)
    //{
        //STAT = 0;
        //delay_ms(500);
        //STAT = 1;
        //delay_ms(1500);
    //}
 
    for (x = 0; x < 20; x++)//zero the line arrays
    {
        line1[x] = 0;
        line2[x] = 0;
    }
 
 
    //Init LCD
    init_lcd();
 
}


//Initializes the 4-bit parallel interface to the HD44780
void init_lcd(void)
{
    uns8 x;
 
    //Wait for LCD busy bit to clear
    LCD_wait();
 
    RS = 0;             
    R_W = 0;


    //Tell the LCD we are using 4bit data communication
    //===============================================================
    delay_ms(100);
    //PORTB = 0b.0000.0011;
    D4 = 1; D5 = 1;
    E = 1; E = 0;


    delay_ms(50);
    //PORTB = 0b.0000.0011;
    E = 1; E = 0;


    delay_ms(10);
    //PORTB = 0b.0000.0011;
    E = 1; E = 0;


    delay_ms(10);
    //PORTB = 0b.0000.0010;
    D4 = 0;
    E = 1; E = 0;


    send_cmd(DISP_ON);
    send_cmd(CLR_DISP);
    //===============================================================
    //LCD Init Complete
 
    //Retrieve last Backlight state
    //===============================================================
    Backlight_Setting = onboard_eeread(LCD_BACKLIGHT_SETTING);
    if (Backlight_Setting > 1) Backlight_Setting = 1; //Default
    onboard_eewrite(Backlight_Setting, LCD_BACKLIGHT_SETTING); //Record to EEPROM
    BL_Control = Backlight_Setting; //Turn on/off the backlight
    //===============================================================
 
    //Retrieve backlight brightness setting
    //===============================================================
    brightness_setting = onboard_eeread(LCD_BRIGHTNESS_SETTING);
    if (brightness_setting > 30) brightness_setting = 30; //Turn LCD backlight on to brightest by default
    onboard_eewrite(brightness_setting, LCD_BRIGHTNESS_SETTING); //Record to EEPROM
    //===============================================================


    //Retrieve last LCD Type
    //===============================================================
    LCD_Width = onboard_eeread(LCD_WIDTH_SETTING);
    if (LCD_Width > 80) LCD_Width = 20; //Default
    onboard_eewrite(LCD_Width, LCD_WIDTH_SETTING); //Record to EEPROM


    //Set end lines
    EL1 = LCD_Width;
    EL2 = 64 + LCD_Width;
    EL3 = LCD_Width + LCD_Width;
    EL4 = 64 + LCD_Width + LCD_Width;


    //Set cursor positions
    CP1 = 0b.1000.0000 | 0;
    CP2 = 0b.1000.0000 | 64;
    CP3 = 0b.1000.0000 | LCD_Width;
    CP4 = 0b.1000.0000 | (LCD_Width + 64);

 
    if (Display_16x2 == 1) LCD_Lines = 2;
    else if (Display_16x4 == 1) LCD_Lines = 4;
    else
    {
        LCD_Lines = onboard_eeread(LCD_LINE_SETTING);
        if (LCD_Lines > 8) LCD_Lines = 4; //Default
        onboard_eewrite(LCD_Lines, LCD_LINE_SETTING); //Record to EEPROM
    }
 
    //===============================================================
 
    //Display splash screen if enabled
    //===============================================================
    splash_enable = onboard_eeread(LCD_SPLASH_ENABLE);
    if (splash_enable > 1) splash_enable = 1; //Default to show splash screen
    onboard_eewrite(splash_enable, LCD_SPLASH_ENABLE); //Record to EEPROM
 
    if (splash_enable == 0)
    {
        delay_ms(1000);
     
        if(RX_In > 0)
        {
            x = RX_Array[1];
         
            if (x == 18)    //^r to reset the baud rate to 9600
            {
                baud = 2;
                set_baud_rate();
                onboard_eewrite(baud, BAUD_RATE);
             
                send_string("Reset to 9600");
                delay_ms(1000);
                send_cmd(CLR_DISP);
             
            }
         
            RX_In = 0;
        }
     
     
        else
        {
            baud = onboard_eeread(BAUD_RATE);
            set_baud_rate();
        }
    }
 


    else if (splash_enable == 1)
    {
        x = onboard_eeread(LINE1_START);
        if (x != 0xFF)
        {
            send_cmd(CP2);//sets cursor position to beginning of line 2 for 4-line displays
            for (x = 0; x < 20; x++)
            {
                send_char(onboard_eeread(LINE1_START + x));
             
            }
         
            send_cmd(CP3);
         
            for (x = 0; x < 20; x++)
            {
                send_char(onboard_eeread(LINE2_START + x));
             
            }
         
            delay_ms(1000);
            send_cmd(CLR_DISP);
        }
         
        else
        {
            //send_cmd(SET_CURSOR + LINE2_START);
            send_cmd(CP2);
            send_string("    SparkFun.com");
            //send_cmd(SET_CURSOR + LINE3_START);
            send_cmd(CP3);
            send_string("    SerLCD v2.5");
            delay_ms(1000);
            send_cmd(CLR_DISP);
        }
         
     
        if(RX_In > 0)
        {
            x = RX_Array[1];
         
            if (x == 18)    //^r to reset the baud rate to 9600
            {
                baud = 2;
                set_baud_rate();
                onboard_eewrite(baud, BAUD_RATE);
             
                send_string("Reset to 9600");
                delay_ms(1000);
                send_cmd(CLR_DISP);
             
            }
         
            RX_In = 0;
        }
     
     
        else
        {
            baud = onboard_eeread(BAUD_RATE);
            set_baud_rate();
        }
     
    }
 
    //===============================================================
 
    for (x = 0; x < 16; x++)
    {
        line1[x] = 32;  //init the line arrays to black spaces
        line2[x] = 32;  //
    }
 
    cursor_position = 0;
} 


//Checks the busy flag and waits until LCD is ready for next command
void LCD_wait(void)
{
    bit i = 1;
    uns8 j;
 
    TRISA |= 0b.0000.1111; //0 = Output, 1 = Input


    R_W = 1; //Tell LCD to output status
    RS = 0;             


    while(i == 1)
    {
        E = 1;
        //for (j = 0; j < 1; j++);
        i = D7; //Read data bit 7 - Busy Flag
        E = 0;
 
        E = 1;
        //for (j = 0; j < 1; j++);
        E = 0; //Toggle E to get the second four bits of the status byte - but we don't care
    }
 
    TRISA &= 0b.1111.0000; //0 = Output, 1 = Input
 
    //STAT = 0;
 
    //while (1);
}


//Sends an ASCII character to the LCD
void send_char(uns8 c)
{
    LCD_wait();
 
    R_W = 0; //set LCD to write
    RS = 1; //set LCD to data mode
 
    //PORTC = c >> 4;
    D4 = c.4; D5 = c.5; D6 = c.6; D7 = c.7;
    E = 1; E = 0;
 
    //PORTC = c;
    D4 = c.0; D5 = c.1; D6 = c.2; D7 = c.3;
    E = 1; E = 0; //Toggle the Enable Pin
}


//Sends an LCD command
void send_cmd(uns8 d)
{
    LCD_wait();


    //TRISC = 0b.0000.0000;   //0 = Output, 1 = Input


    R_W = 0; //set LCD to write


    //PORTC = d >> 4;
    D4 = d.4; D5 = d.5; D6 = d.6; D7 = d.7;
    E = 1; E = 0;


    //PORTC = d;
    D4 = d.0; D5 = d.1; D6 = d.2; D7 = d.3;
    E = 1; E = 0;
}


//Sends a given string to the LCD. Will start printing from
//current cursor position.
void send_string(const char *incoming_string)
{
    uns8 x;
 
    for(x = 0 ; incoming_string[x] != '\0' ; x++)
        send_char(incoming_string[x]);
}


void set_baud_rate(void)
{
    //changed for 4MHz
    if (baud == 0) SPBRG = 255;             //2400
    else if (baud == 1) SPBRG = 128;        //4800
    else if (baud == 2) SPBRG = 64;         //9600
    else if (baud == 3) SPBRG = 43;         //14400
    else if (baud == 4) SPBRG = 31;         //19200
    else if (baud == 5) SPBRG = 15;         //38400
 
    else
    {
        SPBRG = 64;
        onboard_eewrite(2, BAUD_RATE);
    }
 
 
}


//General short delay
void delay_ms(uns16 x)
{
    //Clocks out at 1006us per 1ms @ 8MHz
    uns8 y, z;
    for ( ; x > 0 ; x--)
        for ( y = 0 ; y < 4 ; y++)
            for ( z = 0 ; z < 69 ; z++);
}


//Reads e_data from the onboard eeprom at e_address
uns8 onboard_eeread(uns8 e_address)
{
    //Do a read
    EEADR = e_address; //Set the address to read
    EEPGD = 0; //Point to EEPROM Memory
    RD = 1; //Read it
 
    return(EEDATA); //Read that EEPROM value
} 


//Write e_data to the onboard eeprom at e_address
void onboard_eewrite(uns8 e_data, uns8 e_address)
{
    bit temp_GIE = GIE;
 
    EEIF = 0; //Clear the write completion intr flag
    EEADR = e_address; //Set the address
    EEDATA = e_data; //Give it the data
    EEPGD = 0; //Point to EEPROM data block?
    WREN = 1; //Enable EE Writes
    GIE = 0; //Disable Intrs
 
    //Specific EEPROM write steps
    EECON2 = 0x55;
    EECON2 = 0xAA;
    WR = 1;
    //Specific EEPROM write steps


    while(EEIF == 0); //Wait for write to complete
    WREN = 0; //Disable EEPROM Writes


    GIE = temp_GIE; //Set GIE to its original state
}
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
9,699
Hi Mike,
Thanks for your interest in this project.
I had that code, but this statement regarding the last revision threw me a little,"4/29/09 Changed code for integrated LCD with backpack, outsourced hardware. Control lines to LCD from PIC have changed." Here is the downloaded SF datasheet for backpack version 2.8(?) dated 12/14/09 (attached below).

That schematic did not agree with either the code (e.g., DB4..7 are actually PORTA<4..7> and the control lines are on PORTB) nor with the device I purchased (JP1 is 10-pin and the pinouts that I traced agreed with the code), so I redrew the schematic I posted above. That was no big deal.

In a conversation with a couple of the people at SF, I learned they had very little knowledge of the integrated board. They could not confirm the pinouts I had found (presumably the software developer did have access to the correct layout).

SF sells both a backpack version and an integrated version of essentially the same thing, but they are not identical. I suspect the code for the integrated version is accurate, and the schematic applies to the backpack version. I did not carefully review customer reviews of the backpack version, but clearly reviews of the integrated version indicate a problem with the display "locking up."

As I am completely C-challenged, rather than try to find a glitch in the code, I decided to start over from working code you helped me on a few years ago.

Regards, John
 

Attachments

BobaMosfet

Joined Jul 1, 2009
1,118
Not at all, that is approximately 9600 baud. The Parallax displays work great at 19,200 baud.

One member here got a 2x16 display going well over 100,000 baud. I suspect you may be confusing the "write" time for the Hitachi controller (specified as about 37 to 40 us) with the bit time for characters using the 1-wire RS232-type interface, i.e., at 9600 baud the bit time works out to be 104 us, which for 10 bits works out to be 1.04 ms er character (1/9600 = 1.042 ms). The write time for the controller is per write, as I understand it, so that is either 4 or 8 bits per write.

Writing too quickly will cause the sort of failure the SF device displays. I will be reading the busy flag (DB7) to avoid that -- just a theory at this point. The SF Display is outsourced, and SF claimed not to have any information on it.
Hi, yes, sorry. I was considering the enforced 1-5ms delay depending on data .v. command mode.
 

MMcLaren

Joined Feb 14, 2010
844
I've not heard of that timing requirement.

I use timing similar to that shown below from one of the many HD44780 compatible datasheets. BTW, you don't have to delay between nibbles when sending a byte in 4-bit interface mode.

HD44780 Timing.png
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
9,699
Two brief updates.

1) Now running with 16F1826 (ver. 7). If anyone does this, do not get 16F1826 ver. 4 or before (see errata), 16F1827 in early versions has fewer problems. Later versions (e.g, 7) are the same.
upload_2018-4-27_16-15-42.png

2) The Samsung ST7066U and Hitachi HD44780 are compatible, but not the same. The Samsung will POR by sending 0x02 (as recommended in its DS) or 0x03 (4-bit mode). The Hitachi will not properly boot with 0x02. Before pulling the 16F88 from the SF LCD, I did the firmware on a "cheap" LCD from China that had the Samsung chip and saw that difference. SF seems to be Hitachi or very similar.

John
 

MMcLaren

Joined Feb 14, 2010
844
Shame on you, John. Now you have me thinking about LCD Backpack designs when I really should be attending to other matters (grin). I realize you're concentrating on fixing your Sparkfun display but may I ask if you've ever considered designing your own Backpack? I believe there's a lot of potential capability being overlooked in current LCD Backpack designs.

How about a Backpack that will accept either an HD44780 LCD or a 4-digit 7-segment LED display? Several Backpacks can share the same Serial connection to support more LED displays. Here's a mock-up (circa 2011) that represents three backpacks. One Backpack with an LCD installed and two Backpacks with 4-digit LED displays installed (~9% PWM brightness level). Each Backpack has its own 16F1824 on this mock-up.

K8LH Ser LCD-LED Mockup.JPG

The 4-digit 7-segment LED displays are assigned DDRAM addresses in unused portions of the 128 byte LCD DDRAM address space. For example, one Backpack may have a 4-digit LED display using DDRAM addresses 0x28..0x2B while another Backpack with LED display may use addresses 0x2C..0x2F. One of the things I like about this concept is that your host software can be written to drive both LCD and LED displays at the appropriate DDRAM addresses and the end-user can decide whether to use an LCD display or an LED display (or both for that matter).

The mock-up assumes a 4x20 display with the following DDRAM memory map;

DDRAM Memory.jpg

There are many possibilities for some of these unused DDRAM addresses and plenty of processing power to implement them. Use one address for the LCD back light brightness setting, or a set of addresses for reading/setting a built-in RTC, or one address for the output from a built-in IR decoder... Supporting a keypad and/or a couple of rotary decoders would be relatively simple, too.

Food for thought.

Cheerful regards, Mike McLaren, K8LH
 
Last edited:

Thread Starter

jpanhalt

Joined Jan 18, 2008
9,699
Shame on you, John. Now you have me thinking about LCD Backpack designs when I really should be attending to other matters (grin). I realize you're concentrating on fixing your Sparkfun display but may I ask if you've ever considered designing your own Backpack? I believe there's a lot of potential capability being overlooked in current LCD Backpack designs.
Glad you have a sense of humor. I just can't resist trying to fix stuff. In brief, had I not bought the SF display, I would probably be doing something else.

Awhile back, I thought about making my own backpack, but lost interest when I found some $6 boards that fit my needs . I am not too interested in LED or other types of displays at present. For LCD's, I found a supplier that has a multi-interface backpack: https://www.ebay.com/sch/i.html?_from=R40&_trksid=m570.l1313&_nkw=Serial:UART/I2C/SPI+Adapters+for+1602/1604/2002/2004/4002+LCD+in+Arduino/PIC&_sacat=0 The boards have outputs for I2C,SPI and UART , have a 16F1829, and pogopin pads for programming. They look well made and shipping was fast, but I have not tried them. That seller also has a shipping location in Canada. Order to receipt speed was not that much faster from Canada. There are a slew of other suppliers that advertise what seem to be the same but they are not. I got one early on as it was a little cheaper, and it was only I2C, not the other interfaces.

This was just a project for rainy days, like today.

Regards, John
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
9,699
We have a week of good weather forecast, and I have a forest of dead ash trees to deal with... Here's a brief update. I will be pretty busy as long as that weather holds.

Sorted through the controls for the SF display (attached). They are all pretty standard (compared to Parallax) but use two entries for each. I am going to try using single entries (see: jpa column). That will give me 0xF0 thru 0xFF and 0xE8 thru 0xEF to play with for additional commands, I think. Software selected baud rate will require 4 or 5 of them. I haven't found the Parallax "Line feed" or "Carriage return" to be useful. Does anyone here feel differently? I am also considering an "Erase line" command that will erase the current line X and return to X,0. Does that appeal to anyone?

As for performance to date, I have tested up to 50,000 baud with no problems. My character/command generator is at 8 MHz, and the LCD is currently at 10 MHz, which is why I tested an oddball baudrate to keep the error low. I can fill the LCD screen with an ascii character and have not needed a ring buffer. Also found that the mandatory delay after clearing the display (command = 0x01) seems to be avoided by monitoring the busy flag. Since the clearing is accomplished by writing 0x20 (space) to all 80 spaces, I wasn't sure whether there would be 80 busy flags or just one. It seems to work just fine. That ought to save a few lines of code.

Looking forward to your input on the two questions or other suggested commands.

John
 

Attachments

Thread Starter

jpanhalt

Joined Jan 18, 2008
9,699
Update
Made a little progress yesterday and have got most of the individualized commands working. Right now, I am using a table like this:
Code:
Cmd1           ;bit<7> clear, hex value <0x20
     brw
     bra       Valid          ;0x00    ; Tested 05/07/18 and seems to erase screen w/normal operation*      
     bra       Valid          ;0x01 clear display
     bra       Valid          ;0x02 return home
     bra       _0x03          ;0x03 return home*#      erase line
     bra       Valid          ;0x04 entry mode
     bra       Valid          ;0x05 entry mode
     bra       Valid          ;0x06 entry mode
     bra       Valid          ;0x07 entry mode
     bra       _0x08          ;0x08 display off cursor**#    baud 9600
     bra       _0x09          ;0x09 display off cursor**#    baud 19200
     bra       _0x0A          ;0x0A display off cursor**#    baud 38400
     bra       _0x0B          ;0x0B display off cursor**#    baud 50000
<more code follows>
Does anyone know what a command instruction of "0x00" is supposed to do? (See: Table 6 of the Hitachi datasheet)

In brief, several of the instructions ignore the last one or two lsb's. In other words, the instructions for "return home" can be either a 0x02 or 0x03. I have repurposed 0x03 to go to a routine that erases the rest of a line or whole line depending on the index when called. There are about 13 such codes that can be repurposed without losing any of the original functionality.

As best I can tell, code 0x00 is valid and clears the screen, but that is not documented in the datasheet. However, I am shy of sending a code that is not documented.

Thanks,
John

ps: Baud = 50,000 is to accommodate having my sending unit at 8 or 32 MHz and my display receiver at 10 MHz. That will change. Display will probably be at 16 Mhz.
 
Top