SparkFun 4x20 LCD Redo

Discussion in 'Embedded Systems and Microcontrollers' started by jpanhalt, Apr 22, 2018.

  1. jpanhalt

    Thread Starter Expert

    Jan 18, 2008
    6,377
    1,219
    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: Apr 22, 2018
  2. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    3,971
    5,374
    Are you going to write in C or .asm?
     
  3. jpanhalt

    Thread Starter Expert

    Jan 18, 2008
    6,377
    1,219
    Assembly
    MPLab 8.92

    John
     
  4. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    3,971
    5,374
    Can I help?!
     
  5. jpanhalt

    Thread Starter Expert

    Jan 18, 2008
    6,377
    1,219
    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.
     
  6. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    3,971
    5,374
    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.
     
  7. LesJones

    Well-Known Member

    Jan 8, 2017
    1,488
    352
    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 (Text):
    1.  
    2. ;
    3. ;   Version Mod3  Reads two scale inputs.
    4. ;    Modified for 20 Mhz xtal
    5. ;    Modified for PIC16F628
    6. ;   Modified to detect scale sample mode (Normal / fast)
    7. ;   MODIFIED TO DETECT WAIT FOR DATA OF OVER 400 MS
    8. ;   Modified to put display buffers in page 1 ram (Instead of page 0)
    9. ;   Modified to output combined data
    10.  
    11. ;   Display Scale 1  on top line
    12. ;   Display Scale 2  on second line
    13. ;   Sum of inputs on line 4
    14. ;   Add "No signal statup display"
    15. ;
    16. ;    Being modified to use RA5/MCLRE as input to disable fast sample mode as startup.
    17.  
    18. ;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.
    19.  
    20. ;The raw value is multiplied by 125/256 for inches or by 127/1024 for mm.
    21.  
    22.  
    23.  
    24. ;PIC16F628, WDT OFF, POR ON, INTERNAL 20 MHz CLOCK  Register space in bank 0 0x20 - 0x6
    25.  
    26.    list P=16F628A
    27.    #include p16f628a.inc
    28.    __config _HS_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF & _MCLRE_OFF
    29. ;
    30.  
    31.  
    32. ;             ===============
    33.  
    34. ; !!!!!!!!!!!! These timing values are based on a 20mhz Xtal !!!!!!!!!!!!
    35.  
    36. ;       OUTPUT ROUTINE INFORMATION & Equates
    37. ; Three timer periods are required to generate the Chinese scale output.
    38. ; The periods are timed with timer1 which is set to run at the internal clock
    39. ; rate of Fosc/4 or 5MHz which is equal to a 200ns period.
    40.  
    41. ; The time between number outputs is 20ms.  In timer1 ticks, this is calculated
    42. ; as 20ms / 200ns = 100,000.  Since timer1 is only  a 16 bit counter, this time
    43. ; is divided into two 10ms time intervals or 50,000 (0xc350) timer1 ticks each.
    44. ; Instruction overhead for the delay function call is ignored.
    45. DELAY_10MS_L  equ  0x50
    46. DELAY_10MS_H  equ  0xc3
    47.  
    48. ; The time between the two numbers is 110us and the time before and after the
    49. ; number output is 55us.  The number of timer1 ticks is calculated as
    50. ; 55us / 200ns = 275.  Subtracting the delay function call overhead
    51. ; leaves 250 (0xfa) timer1 ticks.
    52. DELAY_55US  equ  0xfa
    53.  
    54. ; The clock period for the Chinese scale output is 13.2us.  The half period
    55. ; between transitions is 6.6us.  The number of timer1 ticks is calculated as
    56. ; 6.6us / 200ns = 33.  Subtracting the delay function call overhead
    57. ; leaves 10 (0x0a) timer1 ticks.
    58. DELAY_6_6US  equ  0x0a
    59.  
    60. ; Define variables at memory locations
    61. ;           ****** PAGE 0 *****
    62.  
    63. Delay     EQU   0x20
    64. X_Delay     EQU   0x21
    65. X_Delay2   EQU   0x22
    66. Temp     EQU   0x23
    67.  
    68. Ctrl     EQU   0x24       ;Used in LCD routines
    69. Char     EQU   0x25
    70. Cmd     EQU   0x26
    71. Flag     EQU   0x27       ;Bit '0' sign bit set for negative
    72.            ;Bit '1' Display line bit - set for line 2
    73.            ;Bit 2 set for invalid reading from getdata routines ie timeout has occured
    74.            ;Bit 3 set for Z1 in fast mode
    75.            ;Bit 4 set for Z2 in fast mode
    76. TempByte2   EQU   0x28
    77.  
    78.  
    79.  
    80. ;         Only the data in the second frame is used
    81. Z1Data0     EQU   0x29     ;MSB     (Second frame)  (Relative)
    82. Z1Data1     EQU   0x2A
    83. Z1Data2     EQU   0x2B     ;LSB
    84.  
    85. Z1Data3     EQU   0x2C     ;MSB     (First frame) (Absolute)
    86. Z1Data4     EQU   0x2D
    87. Z1Data5     EQU   0x2E     ;LSB     First byte received from scale
    88.  
    89.  
    90. Z2Data0     EQU   0x2F     ;MSB     (Second frame)  (Relative)
    91. Z2Data1     EQU   0x30
    92. Z2Data2     EQU   0x31     ;LSB
    93.  
    94. Z2Data3     EQU   0x32     ;MSB     (First frame) (Absolute)
    95. Z2Data4     EQU   0x33
    96. Z2Data5     EQU   0x34     ;LSB     First byte received from scale
    97.  
    98. rDataCnt   EQU   0x35
    99. rBitCnt     EQU   0x36
    100.  
    101. rData0     EQU   0x37     ;     Byte 6           MSB  
    102. rData1     EQU   0x38     ;     Byte 5
    103. rData2     EQU   0x39     ;     Byte 4           LSB
    104.  
    105.  
    106. ZCombData0   EQU   0x3A     ;MSB
    107. ZCombData1   EQU   0x3B
    108. ZCombData2   EQU   0x3C     ;LSB
    109.  
    110.  
    111.          ;     rBin 4 Bytes
    112. rBin     EQU   0x3D     ;     MSB
    113. rBin1     EQU   0x3E
    114. rBin2     EQU   0x3F
    115. rBin3     EQU   0x40
    116.  
    117.          ;     rBCD 4 Bytes
    118. rBCD     EQU   0x41     ;     MSB
    119. rBCD1     EQU   0x42  
    120. rBCD2     EQU   0x43  
    121. rBCD3     EQU   0x44     ;     LSB
    122.  
    123.  
    124. rCnt     EQU   0x45  
    125. rPto     EQU   0x46     ;Output buffer pointer fo BCD to ACII routine
    126. rPti     EQU   0x47     ;Input buffer pointer fo BCD to ACII routine
    127. mFactor     EQU   0x48  
    128. TempByte   EQU   0x49
    129. Timeout     EQU   0x4A     ;Counter used in getdat routines to detect more than 400 ms wait for data
    130. ;spare     EQU   0x4B
    131. TempW     EQU   0x4C
    132. TempS     EQU   0x4D
    133.  
    134.  
    135. param1     EQU   0x4E     ; parameter 1 for function calls  (Only seems to be used in delay routine)
    136. param2     EQU   0x4F     ; parameter 2 for function calls  (Only seems to be used in delay routine)
    137. temp1     EQU   0x50     ; temporary scratch variable
    138. temp2     EQU   0x51     ; temporary scratch variable
    139. temp3     EQU   0x52     ; temporary scratch variable
    140. bits     EQU   0x53     ; bit iterator
    141. bytes     EQU   0x54     ; byte iterator
    142. frames     EQU   0x55     ; frame iterator
    143. FormatPtr   EQU   0x56     ; Holds pointer to LCD buffer  (Used in format routine)
    144. ;Flag     EQU   0x57     ;Bit 0 Z1 negative flag
    145.            ;Bit 1 Z2 negative flag
    146.  
    147. ;     -------------------------------------------------------
    148.  
    149. ;   PAGE 1
    150.  
    151.          ;rDisp Display buffer 64 bytes  NOW ON PAGE 1
    152.  
    153.  
    154. ;             ****** PAGE 1 *****
    155. rDisp1     EQU   0xA0     ;Start of first line
    156. rDisp2     EQU   0xB0     ;Start of second line
    157. rDisp3     EQU   0xC0     ;Start of third line
    158. rDisp4     EQU   0xD0     ;Start of fourth line
    159.  
    160.  
    161.  
    162. ;   First available address with PIC16F628A is 0x20
    163. ;   Last available address with PIC16F628A is 0x6F (In bank 0)
    164. ;   Ram space 0xA0 to 0xEF  in bank 1
    165. ;   Ram space 0x120 to 0x14F  in bank 2
    166.  
    167.  
    168.  
    169. LCD_PORT   EQU   PORTB
    170. LCD_TRIS   EQU   TRISB
    171.  
    172. #define   INH4051     PORTA, 0     ;Pin 17
    173. #define   Clock1     PORTA, 1
    174. #define   Data1     PORTA, 2
    175.  
    176. #define   Clock2     PORTA, 3
    177. #define   Data2     PORTA, 4
    178. #define Mode     PORTA, 5   ;Low to enable fast sample mode at startup
    179.  
    180. #define   Z_CombClock_out   PORTB,4
    181. #define   Z_CombData_out   PORTB,5
    182.  
    183.  
    184.  
    185. ;Display port useage
    186. #define   Out_Enable   PORTB, 0  ;enables clock & data out to DRO350
    187. #define   E     PORTB, 1
    188. #define   RW     PORTB, 2
    189. #define   RS     PORTB, 3
    190.  
    191. ;LCD_Data4     PORTB, 4   ;Also clock out to 74LS00 enabled by RB0
    192. ;LCD_Data5     PORTB, 5   ;Also data out to 74LS00 enabled by RB0
    193. ;LCD_Data6     PORTB, 6   ;Also CD4051 select A (Pin 11)
    194. ;LCD_Data7     PORTB, 7   ;Also CD4051 select B (Pin 10)
    195.  
    196. #define     Sel_A   PORTB,6
    197. #define     Sel_B   PORTB,7
    198.  
    199. ;   CD4051 table
    200. ;Select A PORTB,5
    201. ;Select B PORTB,6
    202. ;Select C Grounded
    203. ;Inhibit PORTA,0
    204. ;
    205. ; Outputs     Pull scale clock & data to +1.5 V
    206. ;   X0   Scale 1 clock   pin 13
    207. ;   X1   Scale 1 data   Pin 14
    208. ;   X2   Scale 2 clock   pin 15
    209. ;   X3   Scale 2 data   pin 12
    210. ;   X4 to X7 not used
    211. ;   Common pin 3   +1.5 V
    212.  
    213.  
    214. ;     End of definitions
    215.  
    216.  
    217.  
    218. Start     ORG 0
    219.      GOTO    Main
    220.  
    221. Interrupt   ORG 4       ;Should not get here !!!
    222.      GOTO   Main  ;
    223.  
    224. Main           ;   Initialize PIC  
    225.        movlw    0x07     ; activate PORTA for PC16F628 as digital
    226.        movwf    CMCON
    227.        BCF     INTCON,GIE   ;disable interrupts
    228.        BCF     INTCON,INTF   ;disable interrupt from RB0
    229.      CLRF   PORTA     ;
    230.      CLRF   PORTB     ;
    231.      BSF   STATUS, RP0   ;Select bank 1
    232.      MOVLW   B'00000000'   ;Bit 0 input All others output
    233.      MOVWF   TRISB     ;
    234.    
    235.      MOVLW   B'00111110'   ;Bits 1, 2, 3, 4 & 5 inputs. Others outputs.
    236.      MOVWF   TRISA     ;
    237.    
    238.      MOVLW   B'00000000'   ;Could set bit 7 to disable weak pull-up's as they are not required.
    239.      MOVWF   OPTION_REG   ;
    240.      BCF   STATUS, RP0   ;Back to bank 0
    241.  
    242.            ; Initialize timer 1
    243.      MOVLW   0x31     ;Timer on Prescaler 1:8
    244.      MOVWF   T1CON
    245.  
    246.      BSF   INH4051     ;Set inhibit high
    247.  
    248.  
    249.  
    250. ;             Initialize LCD
    251.      MOVLW   D'200'     ;
    252.      CALL   X_Delay100   ;
    253.      MOVLW   0x30     ;(LCD bits 4 & 5) -  Set 4 bit mode command
    254.      IORWF   LCD_PORT,F   ;
    255.      CALL   Toggle_E   ;Send command
    256.      MOVLW   D'50'     ;
    257.      CALL   X_Delay100   ;
    258.      CALL   Toggle_E   ;Send command again ?
    259.      MOVLW   D'1'     ;
    260.      CALL   X_Delay100   ;
    261.      CALL   Toggle_E   ;
    262.      MOVF   LCD_PORT, W   ;Get lower nibble of PORTB  ????? (R/W has not been changed for a read)
    263.      ANDLW   0x0f     ; Clear high nibble (LCD data)
    264.      MOVWF   Ctrl     ;
    265.      MOVLW   0x20     ;
    266.      IORWf   Ctrl,W     ; "or" in bit 5
    267.      MOVWF   LCD_PORT   ;
    268.      CALL   Toggle_E   ;
    269. ;     MOVLW   0x20     ;Set datalength to 4, 1 line, 5x7 font (Original setting)
    270.  
    271.      MOVLW   0x28     ;Set datalength to 4, 2 line, 5x8 font  (New setting)
    272.      CALL   Send_Cmd   ;
    273.      MOVLW   0x0c     ;Display on, Curser off
    274.      CALL   Send_Cmd   ;
    275.  
    276.      MOVLW   0x06     ;Screen shifting mode on
    277.  
    278. ;     MOVLW   0x04     ;Screen shifting mode off
    279.  
    280.      CALL   Send_Cmd   ;
    281.      MOVLW   0x03     ;Return home command
    282.      CALL   Send_Cmd   ;
    283.  
    284.  
    285.  
    286. ;           move mm characters to display buffer Top line (Buffer now on page 1)
    287.      bsf   STATUS,5     ;Select page 1
    288.      MOVLW   ' '       ;
    289.      MOVWF   rDisp1+0x0F     ;
    290.      MOVWF   rDisp1+0x0E     ;
    291.      MOVWF   rDisp1+0x0D     ;
    292.      MOVWF   rDisp1+0x0C     ;
    293.      MOVLW   'm'       ;
    294.      MOVWF   rDisp1+0x0B   ;
    295.      MOVLW   'm'       ;
    296.      MOVWF   rDisp1+0x0A   ;
    297.      MOVLW   ' '       ;
    298.      MOVWF   rDisp1+0x09   ;
    299.  
    300.  
    301. ;           move mm characters to display buffer second line
    302.  
    303.      MOVLW   ' '       ;
    304.      MOVWF   rDisp2+0x0F     ;
    305.      MOVWF   rDisp2+0x0E     ;
    306.      MOVWF   rDisp2+0x0D     ;
    307.      MOVWF   rDisp2+0x0C     ;
    308.      MOVLW   'm'       ;
    309.      MOVWF   rDisp2+0x0B   ;
    310.      MOVLW   'm'       ;
    311.      MOVWF   rDisp2+0x0A   ;
    312.      MOVLW   ' '       ;
    313.      MOVWF   rDisp2+0x09   ;
    314.  
    315.  
    316. ;           move mm characters to display buffer fourth line
    317.  
    318.  
    319.      MOVLW   ' '       ;
    320.      MOVWF   rDisp4+0x0F     ;
    321.      MOVWF   rDisp4+0x0E     ;
    322.      MOVWF   rDisp4+0x0D     ;
    323.      MOVWF   rDisp4+0x0C     ;
    324.      MOVLW   'm'       ;
    325.      MOVWF   rDisp4+0x0B   ;
    326.      MOVLW   'm'       ;
    327.      MOVWF   rDisp4+0x0A   ;
    328.      MOVLW   ' '       ;
    329.      MOVWF   rDisp4+0x09   ;
    330.      BCF   STATUS,5     ;Select page 0
    331.  
    332.  
    333.      MOVLW   B'00000000'   ;Disable all interrupts
    334.      MOVWF   INTCON     ;Initialise INTCON register
    335.  
    336.      call   Version     ;Display Version and No Signal message
    337.      movlw   D'200'     ;Delay 2 seconds
    338.      call   X_Delay10ms
    339.      movlw   D'200'     ;Delay 2 seconds
    340.      call   X_Delay10ms
    341.      movlw   D'200'     ;Delay 2 seconds
    342.      call   X_Delay10ms
    343. ;
    344. ;         *********** START OF MAIN LOOP ********************8
    345.  
    346.      BTFSS   Mode     ;Test mode switch / jumper to see if fast mode is required
    347.      GOTO   Normal
    348.  
    349.      CALL   Set_Fast1
    350.  
    351.      movlw   D'200'     ;Delay 2 seconds
    352.      call   X_Delay10ms
    353.  
    354.      CALL   Set_Fast2
    355.      GOTO   Repeat     ;Set_Done
    356.  
    357. Normal
    358.      CALL   Set_Slow1
    359.  
    360.      movlw   D'200'     ;Delay 2 seconds
    361.      call   X_Delay10ms
    362.  
    363.      CALL   Set_Slow2
    364.      GOTO   Repeat     ;Set_Done
    365.  
    366. ;   Need to add this code for 2 sec delay then clear 3 rd line
    367. ;Set_Done
    368. ;     movlw   D'200'     ;Delay 2 seconds
    369. ;     call   X_Delay10ms
    370. ;
    371. ;     CALL   Clear     ;Clear 3rd line
    372.  
    373.  
    374.  
    375. Repeat    
    376.      CALL   GetData1       ;   Read data from scale 1
    377.      CALL   GetData2       ;   Read data from scale 2
    378.  
    379.      CALL   ADD_24Bit
    380.  
    381. ;       Copy Z1Data to rData
    382.      movfw   Z1Data0
    383.      MOVWF   rData0
    384.      MOVFW   Z1Data1
    385.      MOVWF   rData1
    386.      MOVFW   Z1Data2
    387.      MOVWF   rData2
    388. ;
    389.      CALL   SetSign
    390.      call   ConvertM     ; Convert to mm    Raw value * 127/1024
    391.      call   BintoBCD
    392.      MOVLW   rDisp1     ;Least significant digit to display on line 1
    393.      CALL   FormatL     ;   Format line 1 data (Top line of display.
    394.  
    395.      MOVLW   0x80     ; Cursor at 0x00
    396.      call   Send_Cmd
    397.  
    398.      MOVLW   0x010     ; Write 16 characters from display buffer to top line of display
    399.      MOVWF   TempByte2   ;
    400.      MOVLW   rDisp1     ;
    401.      MOVWF   FSR     ;
    402.      CALL   Send_next_Char   ;
    403.  
    404.  
    405.  
    406. ;       Copy Z2Data  to rData
    407.      movfw   Z2Data0
    408.      MOVWF   rData0
    409.      MOVFW   Z2Data1
    410.      MOVWF   rData1
    411.      MOVFW   Z2Data2
    412.      MOVWF   rData2
    413. ;
    414.      CALL   SetSign
    415.      call   ConvertM     ; Convert to mm    Raw value * 127/1024
    416.      call   BintoBCD
    417. ;     CALL   FormatL2     ;   Format line 2 data (Bottom line of display.
    418.  
    419.      MOVLW   rDisp2     ;Least significant digit to display on line 2
    420.      CALL   FormatL     ;   Format line
    421.  
    422.      MOVLW   0xC0     ; Cursor at 0x40 (Start of second line)
    423.      call   Send_Cmd
    424.  
    425.      MOVLW   0x010     ; Write 16 characters from display buffer to second line of display
    426.      MOVWF   TempByte2   ;
    427.      MOVLW   rDisp2   ;
    428.      MOVWF   FSR     ;
    429.      CALL   Send_next_Char   ;
    430.  
    431.      MOVLW   0x94     ; Cursor at 0x14 (Start of third line)
    432.      call   Send_Cmd
    433.  
    434. ;     MOVLW   0x010     ; Write 16 characters from display buffer to third line of display
    435. ;     MOVWF   TempByte2   ;
    436. ;     MOVLW   rDisp3   ;
    437. ;     MOVWF   FSR     ;
    438. ;     CALL   Send_next_Char   ;
    439.  
    440.  
    441.  
    442. ;           Copy ZCombData to rData
    443.      MOVFW   ZCombData0
    444.      MOVWF   rData0
    445.      MOVFW   ZCombData1
    446.      MOVWF   rData1
    447.      MOVFW   ZCombData2
    448.      MOVWF   rData2
    449.  
    450.      CALL   SetSign
    451.      call   ConvertM     ; Convert to mm    Raw value * 127/1024
    452.      call   BintoBCD
    453.  
    454.      MOVLW   rDisp4     ;Least significant digit to display on line 4
    455.      CALL   FormatL     ;   Format line
    456.  
    457.      MOVLW   0xD4     ; Cursor at 0x54 (Start of fourth line)
    458.      call   Send_Cmd
    459.  
    460.      MOVLW   0x010     ; Write 16 characters from display buffer to fourth line of display
    461.      MOVWF   TempByte2   ;
    462.      MOVLW   rDisp4   ;
    463.      MOVWF   FSR     ;
    464.      CALL   Send_next_Char   ;
    465.  
    466.      CALL   Send_Frames   ;Output combined data
    467.  
    468.      GOTO   Repeat     ;
    469. ;             END OF MAIN PROGRAM LOOP
    470. ;
    471. ;
    472. ;       ----------------------------------------------------------------------------------
    473. ;             Subroutines from here
    474. ;         -----------------------------------------------
    475.  
    476. Send_next_Char     MOVF   INDF, W   ;
    477.        CALL   Write_Char   ;
    478.        INCF   FSR,F     ;
    479.        DECFSZ   TempByte2,1   ;
    480.        GOTO   Send_next_Char   ;
    481.        RETURN
    482.  
    483. ;         ------------------------------------------------
    484.  
    485. Write_Char
    486.        MOVWF   Char     ; Character to be sent is in W
    487.        CALL   Check_BF   ; Wait for LCD to be ready
    488.        MOVF   LCD_PORT, W   ; Get lower nibble of PORTB
    489.        ANDLW   0x0F
    490.        MOVWF   Ctrl     ;
    491.        MOVF   Char, W     ;
    492.        ANDLW   0xF0     ; Get upper nibble
    493.        IORWF   Ctrl,W     ; Combine data with Ctrl
    494.        MOVWF   LCD_PORT   ;
    495.        BCF   RW     ; Set LCD to write
    496.        BSF   RS     ; Set LCD to data mode
    497.        CALL   Toggle_E   ;
    498.        MOVLW   0x0F     ;
    499.        ANDWF   LCD_PORT,F   ;
    500.        SWAPF   Char,W     ;
    501.        ANDLW   0xF0     ; Get lower nibble
    502.        IORWF   LCD_PORT,F
    503.        CALL   Toggle_E
    504. ;       MOVF   Char, W     ;Could add this instruction so character is still in "W"
    505.        RETURN
    506.  
    507. ;         ------------------------------------------------
    508.  
    509. Clear_Display     MOVLW   0x01   ;
    510.        CALL   Send_Cmd   ;
    511.        RETURN
    512.  
    513. ;         ------------------------------------------------
    514.  
    515. Send_Cmd     MOVWF   Cmd       ;Put command byte in Temporary variable
    516.        CALL   Check_BF   ;Check if LCD is ready
    517.        MOVF   LCD_PORT, W   ;Get lower nibble of PORTB
    518.        ANDLW   0x0F     ;
    519.        MOVWF   Ctrl     ;
    520.        MOVF   Cmd, W     ;
    521.        ANDLW   0xF0     ;Get upper nibble
    522.        IORWF   Ctrl,W     ;Combine command with Ctrl
    523.        MOVWF   LCD_PORT   ;
    524.        BCF     RW       ;Clear is for a write to LCD
    525.        BCF     RS       ;Clear is for command mode
    526.        CALL   Toggle_E   ;
    527.        MOVLW   0x0F     ;
    528.        ANDWF   LCD_PORT,F   ;
    529.        SWAPF   Cmd,W     ;Swap nibbles
    530.        ANDLW   0xF0     ;
    531.        IORWF   LCD_PORT,F   ;
    532.        CALL   Toggle_E   ;
    533.        RETURN
    534.  
    535. ;           ---------------------------------------------
    536.              ;Check LCD busy flag    
    537. Check_BF     BSF   STATUS, RP0   ; Select bank 1
    538.        MOVLW   0xF0     ; Make high nibble of PORTB output
    539.        IORWF   LCD_TRIS,F
    540.        BCF   STATUS, RP0   ; Select bank 0
    541.        BCF   RS     ; Set LCD for command mode
    542.        BSF   RW     ; Setup to read busy flag
    543.        BSF   E     ;
    544.        CALL   Delay100   ;
    545.        MOVF   LCD_PORT, W   ;Read upper nibble busy flag
    546.        BCF   E     ;
    547.        ANDLW   0xF0     ; Mask out lower nibble
    548.        MOVWF   Temp     ;
    549.        CALL   Toggle_E   ; "read" lower nibble
    550.        BTFSC   Temp, 7     ; Check busy flag, high = busy
    551.        GOTO   Check_BF   ; If busy, check again
    552.        BCF   RW
    553.        BSF   STATUS, RP0   ; Select bank 1
    554.        MOVLW   0x0F     ; Make high nibble of PORTB input
    555.        ANDWF   LCD_TRIS,f
    556.        BCF   STATUS, RP0   ; Select bank 0
    557.        return
    558.      
    559.  
    560. ;           ---------------------------------------
    561. ;             Delay by 10 ms * value of 'W' at entry
    562. X_Delay10ms     MOVWF   X_Delay2
    563.  
    564. X_Delay10ms_LOOP   movlw   D'100'
    565.        CALL   X_Delay100   ;
    566.        DECFSZ   X_Delay2,F
    567.        GOTO   X_Delay10ms_LOOP
    568.        RETURN  
    569.        
    570. ;           ---------------------------------------
    571. ;             Delay by 100 us * value of 'W' at entry
    572. X_Delay100     MOVWF   X_Delay
    573.  
    574. X_Delay100_LOOP     CALL   Delay100   ;
    575.        DECFSZ   X_Delay,F
    576.        GOTO   X_Delay100_LOOP
    577.        RETURN  
    578.      
    579. ;           ------------------------------------------------  
    580.  
    581. Delay100
    582.        MOVLW   D'160'     ;100 microseconds timeDelay with 20 Mhz xtal
    583.        MOVWF   Delay
    584. Delay100_LOOP     DECFSZ   Delay,F
    585.        GOTO   Delay100_LOOP
    586.        RETURN  
    587.        
    588. ;           -------------------------------------
    589. ;   *********************************************************************************
    590. ;    *  Function:  delay_cycles               *
    591. ;    *  Description: Delay a specified number of instruction cycles including   *
    592. ;  *  interrupt cycles.  The function call overhead adds between     *
    593. ;  *  13 and 16 cycles of delay on top of the specified value.     *
    594. ;   *   Timer 1 Prescaler 1:1 Clock Fosc/4  (5Mhz  / 200ns with 20 Mhz xtal)   *
    595. ;   *                     *
    596. ;    *  Parameters:  param1 - least significant byte of 16 bit cycle delay     *
    597. ;  *  param2 - most significant byte of 16 bit cycle delay     *
    598. ;    *  Returns:  None                 *
    599. ;   *********************************************************************************
    600. delay_cycles:
    601.      
    602.          MOVLW   0x01       ; enable timer1  All other bits 0  1:1 Prescaler ,Internal clock Fosc/4
    603.        MOVWF   T1CON
    604.  
    605.        comf  param1,F  ; negate the delay by complementing the
    606.        comf  param2,F  ; low and high bytes
    607.        bcf  T1CON,TMR1ON  ; stop timer 1
    608.        movf  param1,W  ; move the low byte of the delay into
    609.        movwf  TMR1L  ; timer 1
    610.        movf  param2,W  ; move the high byte of the delay into
    611.        movwf  TMR1H  ; timer 1
    612.        bcf  PIR1,TMR1IF  ; clear the timer 1 rollover flag
    613.        bsf  T1CON,TMR1ON  ; turn on timer 1
    614.  
    615. tmr1_check:  
    616.        btfss  PIR1,TMR1IF  ; wait for the timer 1 rollover flag to
    617.        goto  tmr1_check  ; trigger
    618.        return
    619.    
    620. ;           ----------------------------------
    621.  
    622. Toggle_E     BSF   E
    623.        NOP
    624.        BCF   E
    625.        RETURN
    626. ;           -----------------------------------------
    627.  
    628. ;           **** Get data from scale 1  ****
    629. ;           If no data within 420 ms return with Flag bit 2 set
    630.  
    631. GetData1
    632. ;       BSF   PORTB,0     ;!!!!!!!  TEST ONLY
    633.        BCF   Flag,2     ;Clear invalid reading flag
    634.        BCF   T1CON,0     ;Stop timer 1
    635.        MOVLW   0x31     ;Timer on Prescaler 1:8
    636.        MOVWF   T1CON
    637.        CLRF   TMR1H     ;Clear timer 1 high (Clearing timer registers gives about 105 ms timeout
    638.        CLRF   TMR1L     ;Crear timer 1 low
    639.        BCF   PIR1,0     ;Clear timer 1 overflow flag
    640.        MOVLW   0x04     ;"Timeout" will reach zero in 420 ms) (4 * 105 ms)
    641.        MOVWF   Timeout
    642.        BSF   T1CON,0     ;Start timer 1
    643.  
    644.        MOVLW   Z1Data5
    645.        MOVWF   FSR
    646.        MOVLW   6
    647.        MOVWF   rDataCnt   ;Total number of bytes to get from scale (6)
    648.        CLRF   rBitCnt
    649.        MOVLW   0x24     ;For 20 Mhz xtal 36 decimal
    650. Init1       MOVWF   TempByte
    651.  
    652.        BTFSS   PIR1,0     ;Test for timer overflow Should not have to wait for clock to go low
    653.        GOTO   CountDown1
    654.        BCF   PIR1,0     ;Clear timer 1 overflow flag
    655.        BSF   Flag,2     ;Set a flag here to show invalid reading
    656.        Return       ; **********Return from here if no signal
    657.  
    658.              ;   wait about 38 us to confirm we are in interrecord gap
    659. CountDown1     BTFSC   Clock1     ; Check clock for low       0.8 us
    660.        GOTO   Init1     ; Clock high, start over again
    661.        DECFSZ    TempByte,F   ; Clock low, start countdown     0.4 us    
    662.        GOTO   CountDown1   ;           0.8 us (while looping)
    663.  
    664. Get_Byte1     BSF   rBitCnt,3     ; Initialize bitcount to 8
    665.        CLRF   TempByte   ; Initialize receiving byte
    666.  
    667.  
    668. ;   Loop waiting for start of clock pulse
    669. Wait_Clock_High1   BTFSS   Clock1       ; Check clock for high
    670.        GOTO   Wait_Clock_High1   ; Clock low, wait for clock to go high
    671.  
    672. ;       BCF   PORTB,0     ;!!!! TEST ONLY  We are now at the start of the data frame
    673.  
    674.        BTFSS   PIR1,0     ;Test for timer overflow
    675.        GOTO   Clock_Low1
    676.        BCF   PIR1,0     ;Clear timer 1 overflow flag
    677.        DECFSZ   Timeout,F     ;If Timeout reaches zero we have waited too long
    678.  
    679.        GOTO   Clock_Low1
    680.        BSF   Flag,2     ;Set a flag here to show invalid reading
    681.        Return       ; **********Return from here if no signal within 420 ms
    682.  
    683.  
    684.  
    685. Clock_Low1
    686.            ;Could Enable clock 1 and global interrupt here
    687.            ;to avoid being hung in this routine
    688.        BTFSC   Clock1     ; Clock high, wait for clock to go low
    689.        GOTO   Clock_Low1   ;
    690.  
    691. ;This is the edge of the clock pulse we use to clock in data
    692.        BTFSC   Data1     ; Check data signal for low
    693.        BSF   STATUS,C   ; Set Carry bit
    694.        RRF   TempByte,1   ; Shift in a bit
    695.        DECFSZ   rBitCnt,1   ; Decrement bitcounter
    696.        GOTO   Clock_High1   ; Repeat for next bit
    697.        GOTO   Save_Byte1   ; 8 bits done
    698. Clock_High1     BTFSS   Clock1     ; Check clock for high
    699.        GOTO   Clock_High1   ; Clock high, wait for low
    700.        GOTO   Clock_Low1   ; Repeat waiting for clock low
    701.  
    702. Save_Byte1     MOVF   TempByte, W   ; TempByte holds data
    703.        MOVWF   0     ; Move data in Z1Data
    704.        DECFSZ   FSR,1     ; Point to next byte in Z1Data
    705.        DECFSZ   rDataCnt,1   ; Decrement datacounter
    706.        GOTO   Get_Byte1   ; Get next byte
    707.        RETURN
    708.            ; All bytes done
    709.  
    710.  
    711. ;            ----------------------------------------------
    712. ;           **** Get data from scale 2  ****
    713.  
    714. GetData2
    715. ;       BSF   PORTB,0     ;!!!!!!!  TEST ONLY
    716.        BCF   Flag,2     ;Clear invalid reading flag
    717.        BCF   T1CON,0     ;Stop timer 1
    718.        MOVLW   0x31     ;Timer on Prescaler 1:8
    719.        MOVWF   T1CON
    720.        CLRF   TMR1H     ;Clear timer 1 high (Clearing timer registers gives about 105 ms timeout
    721.        CLRF   TMR1L     ;Crear timer 1 low
    722.        BCF   PIR1,0     ;Clear timer 1 overflow flag
    723.        MOVLW   0x04     ;"Timeout" will reach zero in 420 ms)
    724.        MOVWF   Timeout
    725.        BSF   T1CON,0     ;Start timer 1
    726.  
    727.        MOVLW   Z2Data5
    728.        MOVWF   FSR
    729.        MOVLW   6
    730.        MOVWF   rDataCnt   ;Total number of bytes to get from scale (6)
    731.        CLRF   rBitCnt
    732.        MOVLW   0x24     ;For 20 Mhz xtal 36 decimal
    733. Init2       MOVWF   TempByte
    734.  
    735.        BTFSS   PIR1,0     ;Test for timer overflow Should not have to wait for clock to go low
    736.        GOTO   CountDown2
    737.        BCF   PIR1,0     ;Clear timer 1 overflow flag
    738.        BSF   Flag,2     ;Set a flag here to show invalid reading
    739.        Return       ; **********Return from here if no signal
    740.  
    741.              ;wait about 38 us confirm it is still low
    742. CountDown2     BTFSC   Clock2       ; Check clock for low       0.8 us
    743.        GOTO   Init2     ; Clock high, start over again
    744.        DECFSZ    TempByte,F   ; Clock low, start countdown     0.4 us    
    745.        GOTO   CountDown2   ;           0.8 us (while looping)
    746.  
    747. Get_Byte2     BSF   rBitCnt,3     ; Initialize bitcount to 8
    748.        CLRF   TempByte   ; Initialize receiving byte
    749.  
    750. ;   Loop waiting for start of clock pulse
    751. Wait_Clock_High2   BTFSS   Clock2       ; Check clock for high
    752.        GOTO   Wait_Clock_High2   ; Clock low, wait for clock to go high
    753.  
    754. ;       BCF   PORTB,0     ;!!!! TEST ONLY  We are now at the start of the data frame
    755.  
    756.        BTFSS   PIR1,0     ;Test for timer overflow
    757.        GOTO   Clock_Low2
    758.        BCF   PIR1,0     ;Clear timer 1 overflow flag
    759.        DECFSZ   Timeout,F     ;If Timeout reaches zero we have waited too long
    760.  
    761.        GOTO   Clock_Low2
    762.        BSF   Flag,2     ;Set a flag here to show invalid reading
    763.        Return       ; **********Return from here if no signal within 420 ms
    764.  
    765.  
    766.  
    767. Clock_Low2     BTFSC   Clock2       ; Clock high, wait for clock to go low
    768.        GOTO   Clock_Low2   ;
    769.  
    770. ;This is the edge of the clock pulse we use to clock in data
    771.        BTFSC   Data2     ; Check data signal for low
    772.        BSF   STATUS,C     ; Set Carry bit
    773.        RRF   TempByte,1     ; Shift in a bit (From top bit down)
    774.        DECFSZ   rBitCnt,1   ; Decrement bitcounter
    775.        GOTO   Clock_High2   ; Repeat for next bit
    776.        GOTO   Save_Byte2   ; 8 bits done
    777. Clock_High2     BTFSS   Clock2     ; Check clock for high
    778.        GOTO   Clock_High2   ; Clock high, wait for low
    779.        GOTO   Clock_Low2   ; Repeat waiting for clock low
    780.  
    781. Save_Byte2     MOVF   TempByte, W   ; TempByte holds data
    782.        MOVWF   0     ; Move data in Z2Data
    783.        DECFSZ   FSR,1     ; Point to next byte in Z2Data
    784.        DECFSZ   rDataCnt,1   ; Decrement datacounter
    785.        GOTO   Get_Byte2   ; Get next byte
    786.    ;     BCF   PORTB,0     ;!!!!!!!  TEST ONLY
    787.        RETURN
    788.            ; All bytes done
    789.  
    790.  
    791. ;           ----------------------------------------------
    792.  
    793. SetSign     ;Check if negative - If so convert to positive and set sign bit
    794.  
    795.        CLRF    Flag   ; Assume positive
    796.        BTFSS   rData0,7     ; Test if negative  (MSB of second frame)
    797.        GOTO   Not_Neg     ; No...
    798.                  ;
    799.                  ; Sample is negative so make one's complement
    800.        COMF   rData2,F   ;
    801.        COMF   rData1,F   ;
    802.        COMF   rData0,F     ;
    803.        BSF   Flag,0     ; Flag negative
    804.  
    805. ;Should really increment rData here to make 2's complemant or count will be wrong by 1
    806.  
    807. ;       incfsz   rData2,f
    808. ;       goto   neg1
    809. ;       incfsz   rData1,f
    810. ;       goto   neg1
    811. ;       incfsz   rData0,f
    812. ;
    813. Not_Neg  
    814.        return
    815.  
    816. ;           ----------------------------------------------
    817.  
    818. ;********************************************************************************************************
    819. ;*                           *
    820. ;*                           *
    821. ;*           **** Add Z1Data to Z2Data  Subroutine ****       *
    822. ;*                           *
    823. ;*   Results in ZCombData0 to ZCombData2                 *
    824. ;********************************************************************************************************
    825.  
    826.  
    827. ADD_24Bit
    828. ;           Copy Z1Data to ZCombData
    829.        movfw   Z1Data2
    830.        movwf   ZCombData2
    831.        movfw   Z1Data1
    832.        movwf   ZCombData1
    833.        movfw   Z1Data0
    834.        movwf   ZCombData0
    835.  
    836.  
    837. ;           Add Z2Data to ZCombData
    838.        movfw   Z2Data2
    839.        addwf   ZCombData2,f   ;Low byte
    840. ;
    841.        movfw   Z2Data1
    842.        btfsc   STATUS,C   ;Skip if no carry
    843.        incfsz   Z2Data1,w
    844.        addwf   ZCombData1,f   ;Middle byte
    845. ;
    846.        movfw   Z2Data0
    847.        btfsc   STATUS,C   ;Skip if no carry
    848.        incfsz   Z2Data0,w
    849.        addwf   ZCombData0,f   ;High byte
    850.        return
    851.  
    852. ;         --------------------------------------------------
    853.  
    854.  
    855.  
    856. ;   *************************************************************************************************
    857. ;   *   Send Frames                     *
    858. ;   *  Function    Output the pair of 3 byte data frames to DRO           *
    859. ;   *  Data to be output is in locations ZCombData0, ZCombData1 & ZCombData2       *
    860. ;   *  !! NOTE both frames of the pair contain the same data            *
    861. ;   *************************************************************************************************
    862. Send_Frames         ;Subroutine
    863.          bcf  Z_CombClock_out  ; Clear the clock line
    864.        bcf  Z_CombData_out  ; Clear the data line
    865.        BSF   Out_Enable
    866.  
    867.        movlw  2  ; Set the word iterator to 2  (3 byte frames of data ?)
    868.        movwf  frames  ;
    869.  
    870.  
    871. next_word:
    872.        movlw  ZCombData2    ; Load the file register pointer  (With LSB of frame)
    873.        movwf  FSR  ; with the address of the data to be sent
    874.  
    875.        movlw  3  ; Set the byte iterator to 3  (3 bytes per frame)
    876.        movwf  bytes  ;
    877.  
    878.        movlw  7  ; Set the bit iterator to 7 (To count bit shifts)
    879.        movwf  bits  ;
    880.  
    881.        bsf  Z_CombClock_out  ; Rising clock edge  (Clock set high)
    882.  
    883.  
    884. ;           *** Output state of data bit ***
    885.        btfsc  INDF,0  ; Output data bit 0
    886.        bsf  Z_CombData_out     ; THIS LINE HAD BEEN ACCIDENTALLY DELETED IN COPY ON SCHUMATECH FORUM. !!!!!!!!!
    887.        btfss  INDF,0
    888.        bcf  Z_CombData_out
    889. ;
    890.  
    891.        rrf  INDF,F  ; Advance to next data bit
    892.  
    893.        movlw  DELAY_55US  ; Delay 55 microseconds    (Value = 0xFA)   (This is the start clock pulse)
    894.        movwf  param1  ;
    895.        clrf  param2  ;
    896.        call  delay_cycles  ;
    897.  
    898.        bcf  Z_CombClock_out  ; Falling clock edge     (Set clock low)
    899.  
    900.  
    901.  
    902. ;           *** Delay 1 bit time ***  
    903.        movlw  DELAY_6_6US  ; Delay 6.6 microseconds  (Value = 0x0A) (Bit time)
    904.        movwf  temp1
    905. delay_1:
    906.        decfsz  temp1,F
    907.        goto  delay_1
    908. ;           End of 6.6 us delay
    909.  
    910. ;
    911.  
    912.        bsf  Z_CombClock_out  ; Rising clock edge  (Set clock high)
    913.  
    914. next_byte:
    915. next_bit:
    916.        btfsc  INDF,0  ; Output next data bit
    917.        bsf  Z_CombData_out
    918.        btfss  INDF,0
    919.        bcf  Z_CombData_out
    920. ;
    921.  
    922.        rrf  INDF,F  ; Advance to next data bit
    923. ;
    924. ;           ; Delay 6.6 microseconds      
    925.        movlw  DELAY_6_6US
    926.        movwf  temp1
    927. delay_2:
    928.        decfsz  temp1,F
    929.        goto  delay_2
    930. ;           End of 6.6 us delay
    931.  
    932.  
    933.  
    934.        bcf  Z_CombClock_out  ; Falling clock edge     (Set clock low)
    935.  
    936.           ; Delay 6.6 microseconds  
    937.        movlw  DELAY_6_6US
    938.        movwf  temp1
    939. delay_3:
    940.        decfsz  temp1,F
    941.        goto  delay_3
    942. ;           End of 6.6 us delay
    943.  
    944.  
    945.  
    946.        bsf  Z_CombClock_out  ; Rising clock edge     (Set clock high)
    947.  
    948.        decfsz  bits,F  ; Decrement the bit iterator
    949.        goto  next_bit  ; and continue if not zero
    950. ;
    951.  
    952.        rrf  INDF,F  ; Rotate to the first data bit  (Is this just to leave the byte unchanged ?)
    953.        decf  FSR,F  ; Decrement the file register pointer
    954.        movlw  8  ; Set the bit iterator to 8
    955.        movwf  bits  ;
    956.        decfsz  bytes,F  ; Decrement the byte iterator
    957.        goto  next_byte  ; and continue if not zero
    958.  
    959.  
    960. ;               *** 55 us delay at end of frame ***  
    961.        movlw  DELAY_55US  ; Delay 55 microseconds
    962.        movwf  param1  ;
    963.        clrf  param2  ;
    964.        call  delay_cycles  ;
    965. ;               *** End of 55 us delay ***
    966.  
    967.  
    968.        decfsz  frames,F  ; Decrement the word iterator  (I think word means frames !)
    969.        goto  next_word  ; and continue if not zero
    970.  
    971. ;               *** End of frame set clock & data low ***  
    972.        bcf  Z_CombClock_out  ; Clear the clock line
    973.        bcf  Z_CombData_out  ; Clear the data line
    974. ;               *** Frame has now been output ***
    975.        BCF   Out_Enable
    976.        return
    977. ;
    978.  
    979. ;     -----------------------------------------------------------------------------
    980.  
    981. ;   *************************************************************************************************
    982. ;   *         Convert to mm or inches siubroutine         *
    983. ;   *     Input value in 0, Rdata1 & rData2             *
    984. ;   *     Result in rBin, rBin+1 & rBin+2              *
    985. ;   *************************************************************************************************
    986.  
    987. ConvertI           ;Convert to inches
    988.  
    989.        movlw   D'125'     ; 125/256 - For Inches
    990.        MOVWF   mFactor     ;
    991.        movlw   D'8'     ; For Inches  (256 = 2^8)
    992.        MOVWF   rBitCnt     ; Setup bit count (N)
    993.        goto   Convert
    994.  
    995. ConvertM           ;Convert to milimeters
    996.        MOVLW   D'127'     ; 127/1024 - MM
    997.        MOVWF   mFactor     ;
    998.        MOVLW   D'10'     ; For mm  (1024 = 2^10)
    999.        MOVWF   rBitCnt     ; Setup bit count (N)
    1000.                  ; --- 24 * N (frac) multiply, fractional not calculated
    1001.  
    1002. Convert
    1003.  
    1004.        CLRF   rBin   ; Clear product
    1005.        CLRF   rBin+1   ;
    1006.        CLRF   rBin+2   ;
    1007.                  ;
    1008. MulNext       BCF   STATUS,C     ; Shift in a 0
    1009.        RRF   mFactor,F     ; Get the lsb of multiplier
    1010.        BTFSS   STATUS,C   ; Check the lsb
    1011.        GOTO   No_Add     ; Clear...
    1012.                  ; -- 24 bit add
    1013.        MOVF   rData2, W   ; LSB
    1014.        ADDWF   rBin+2,F;
    1015.                  ;
    1016.        MOVF   rData1, W   ; Middle byte
    1017.        BTFSC   STATUS,C   ;
    1018.        INCFSZ   rData1,W   ;
    1019.        ADDWF   rBin+1,F;
    1020.                  ;
    1021.        MOVF   rData0, W   ; MSB
    1022.        BTFSC   STATUS,C   ;
    1023.        INCFSZ   rData0,W     ;
    1024.        ADDWF   rBin,F   ;
    1025.                  ;
    1026. No_Add       RRF   rBin,F     ; Shift product
    1027.        RRF   rBin+1,F   ;
    1028.        RRF   rBin+2,F   ;
    1029.                  ;
    1030.        DECFSZ   rBitCnt,F   ; Dec bit count
    1031.        GOTO   MulNext     ;
    1032.        RETURN         ;
    1033.  
    1034. ;       --------------------------------------------------------------
    1035.    
    1036.  
    1037. BintoBCD  
    1038.        BCF   STATUS,C
    1039.        MOVLW   D'24'     ; 24-Bits
    1040.        MOVWF   rBitCnt     ; Make cycle counter
    1041.        CLRF   rBCD     ; Clear result area
    1042.        CLRF   rBCD+1     ;
    1043.        CLRF   rBCD+2     ;
    1044.        CLRF   rBCD+3     ;
    1045.  
    1046. Loop       MOVLW   rBCD     ;Pointer to lowest BCD location
    1047.        MOVWF   FSR
    1048.        MOVLW   4
    1049.        MOVWF   rCnt
    1050.  
    1051. BintoBCD1    
    1052.        MOVLW   0x33
    1053.        ADDWF   INDF,F     ;
    1054.        BTFSC   INDF,3     ; top bit of low nibble ?  (If the original nibble was 5 or greater then adding 3
    1055.              ; will cause the top bit of nibble to be set)
    1056.        ANDLW   0xF0     ; Mask for bits 4 - 7 ?
    1057.        BTFSC   INDF,7     ; top bit of high nibble ? (If the original nibble was 5 or greater then adding 3
    1058.              ; will cause the top bit of nibble to be set)
    1059.        ANDLW   0x0F     ; Mask for bits 0 - 3
    1060.        SUBWF   INDF,F     ; Get back to the value befor the 3 was a added to each nibble
    1061.        INCF   FSR,F     ; Inc pointer to next byte
    1062.        DECFSZ   rCnt,F     ; Decrement counter  (Count to 4)
    1063.        GOTO   BintoBCD1   ; four times round this loop
    1064.  
    1065.        RLF   rBin+2,F   ; Get another bit
    1066.        RLF   rBin+1,F   ;
    1067.        RLF   rBin+0,F   ;
    1068.        RLF   rBCD+3,F   ; Put it into BCD
    1069.        RLF   rBCD+2,F   ;
    1070.        RLF   rBCD+1,F   ;
    1071.        RLF   rBCD+0,F   ;
    1072.        DECFSZ   rBitCnt,F   ; All done?
    1073.        GOTO   Loop     ; No, loop  (24 times round this loop)
    1074.        return
    1075.  
    1076. ;             ----------------------------------
    1077.  
    1078. FormatL           ;Format for 1 line of display output
    1079.              
    1080.              ; --- CONVERT THE 10 BINARY CODED DIGITS (5 BYTES) STARTING AT rBcd+3
    1081.              ; <BCD> INTO AN ASCII STRING ALSO STARTING AT <BCD>. ORIGINAL
    1082.              ; BCD DIGITS ARE LOST.
    1083.              ; On entry 'W' has to be set to output buffer address
    1084.  
    1085.  
    1086.        MOVWF   FormatPtr   ;Save for later
    1087.        ADDLW   0x08     ;Add 8
    1088.        MOVWF   rPto     ; Destination pointer
    1089.  
    1090.        MOVLW   rBCD+3     ;
    1091.        MOVWF   rPti     ; Source pointer
    1092.        CALL   BCDtoASCII   ; On return rPto will be rDisp1+0x06 (ie decremented by 2)
    1093.              ;
    1094.        MOVLW   '.'     ;  decimal place
    1095.        MOVF   rPto,W     ; Get current output pointer
    1096.        MOVWF   FSR
    1097.        MOVLW   '.'     ;  decimal place
    1098.        MOVWF   INDF     ;Write to output buffer
    1099.        DECF   rPto,F     ; will now = sDisp1+05
    1100.                  ;
    1101.        MOVLW   rBCD+2     ;
    1102.        MOVWF   rPti     ; Source pointer
    1103.        MOVLW   3     ; 3 Bytes to process
    1104.        MOVWF   rCnt     ;
    1105. Next_BCD     CALL   BCDtoASCII   ;
    1106.        DECFSZ   rCnt,F     ;
    1107.        GOTO   Next_BCD   ;
    1108.        MOVFW   FormatPtr
    1109.        MOVWF   FSR     ;
    1110.        MOVLW   1     ;
    1111.        MOVWF   rCnt     ;
    1112.        MOVLW   ' '     ; space character ?
    1113. FillSpaces     MOVWF   INDF     ;
    1114.        INCF   FSR,F     ;
    1115.        DECFSZ   rCnt,1
    1116.        GOTO   FillSpaces   ; 4 space characters
    1117.    
    1118.        MOVLW   4       ;
    1119.        MOVWF   rCnt     ;
    1120. ReplaceZero     MOVLW   '0'       ;
    1121.        XORWF   INDF,W     ;is the character "0"
    1122.        BTFSS   STATUS,Z   ;
    1123.        GOTO   PutSign     ;
    1124.        MOVLW   ' '       ;
    1125.        MOVWF   INDF     ;
    1126.        INCF   FSR,F       ;
    1127.        DECFSZ   rCnt,1     ;
    1128.        GOTO    ReplaceZero   ;
    1129. PutSign  
    1130. ;       BTFSS   Flag,0   ; Test if negative !!!!!! Original test.
    1131.  
    1132.        BTFSC   Flag,0   ; Test if negative  !!!!!! Changed to skip on clear
    1133.  
    1134.        GOTO   Done     ; No sign to put
    1135.        MOVLW   '-'       ; Put negative sign before
    1136.        DECF   FSR,F       ;
    1137.        MOVWF   INDF     ;
    1138. Done       RETURN
    1139.  
    1140.  
    1141. ;           -------------------------------------
    1142.  
    1143.  
    1144. ;         On entry pointer to input bytes in 'rPti'
    1145. ;            Pointer to output buffer in 'rPto'
    1146. ;            Input pointer (rPti) decremented by 1 on exit rdom entry value
    1147. ;            Output pointer (rPto) decremented by 2 on exit from entry value
    1148. BCDtoASCII
    1149.  
    1150.        MOVF   rPti,W     ; Get current input pointer
    1151.        MOVWF   FSR       ;
    1152.        DECF   rPti,F     ; Prepare for next
    1153.        MOVF   INDF,W     ; Get 2 BCDS
    1154.        MOVWF   TempByte   ; Save for later
    1155.        MOVF   rPto,W     ; Get current output pointer
    1156.        MOVWF   FSR       ;
    1157.        DECF   rPto,F     ; Prepare for next
    1158.        DECF   rPto,F     ;
    1159.        MOVF   TempByte,W   ; Get digits back
    1160.        ANDLW   0x0F     ; Process LSD Low nibble
    1161.        MOVWF   INDF     ;
    1162.        MOVLW   '0'     ; Character zero (0x30) not value of 0
    1163.        ADDWF   INDF,F     ; To output
    1164.        DECF   FSR,F     ;
    1165.        SWAPF   TempByte,W   ; Process MSD Swap high ad low nibbles
    1166.        ANDLW   0x0F     ;
    1167.        MOVWF   INDF     ;
    1168.        MOVLW   '0'     ; Character zero (0x30) not value of 0
    1169.        ADDWF   INDF,F     ; To output
    1170.        RETLW   0
    1171.  
    1172. ;           -------------------------------------
    1173. Version           ;Display version message
    1174.        movlw   0x80     ; Cursor at 0x00 Start of top line
    1175.        call   Send_Cmd
    1176.  
    1177.        movlw   ' '
    1178.        call   Write_Char
    1179.        movlw   'V'
    1180.        call   Write_Char
    1181.        movlw   '0'
    1182.        call   Write_Char
    1183.        movlw   '3'
    1184.        call   Write_Char
    1185.        movlw   'H'
    1186.        call   Write_Char
    1187.        movlw   '0'
    1188.        call   Write_Char
    1189.        movlw   '0'
    1190.        call   Write_Char
    1191.  
    1192. ;       return
    1193.  
    1194.  
    1195.  
    1196. No_Signal         ;Display "No Signal message at startup"
    1197.  
    1198.  
    1199.        MOVLW   0xC0     ; Cursor at 0x40 Start of second line
    1200.        call   Send_Cmd
    1201.  
    1202.        movlw   'N'
    1203.        call   Write_Char
    1204.        movlw   'o'
    1205.        call   Write_Char
    1206.        movlw   ' '
    1207.        call   Write_Char
    1208.        movlw   'S'
    1209.        call   Write_Char
    1210.        movlw   'i'
    1211.        call   Write_Char
    1212.        movlw   'g'
    1213.        call   Write_Char
    1214.        movlw   'n'
    1215.        call   Write_Char
    1216.        movlw   'a'
    1217.        call   Write_Char
    1218.        movlw   'l'
    1219.        call   Write_Char
    1220.        return
    1221.  
    1222. Slow             ;Display slow message on third line
    1223.        MOVLW   0x94     ; Cursor at 0x40 Start of second line
    1224.        call   Send_Cmd
    1225.  
    1226.        movlw   'S'
    1227.        call   Write_Char
    1228.        movlw   'l'
    1229.        call   Write_Char
    1230.        movlw   'o'
    1231.        call   Write_Char
    1232.        movlw   'w'
    1233.        call   Write_Char
    1234.        movlw   ' '
    1235.        call   Write_Char
    1236.        movlw   ' '
    1237.        call   Write_Char
    1238.        MOVLW   '0'     ; Character zero (0x30) not value of 0
    1239.        ADDWF   Timeout,W
    1240.        CALL   Write_Char
    1241.        return
    1242.  
    1243. Fast             ;Display Fast message on third line
    1244.        MOVLW   0x94     ; Cursor at 0x40 Start of second line
    1245.        call   Send_Cmd
    1246.  
    1247.        movlw   'F'
    1248.        call   Write_Char
    1249.        movlw   'a'
    1250.        call   Write_Char
    1251.        movlw   's'
    1252.        call   Write_Char
    1253.        movlw   't'
    1254.        call   Write_Char
    1255.        movlw   ' '
    1256.        call   Write_Char
    1257.        movlw   ' '
    1258.        call   Write_Char
    1259.        MOVLW   '0'     ; Character zero (0x30) not value of 0
    1260.        ADDWF   Timeout,W
    1261.        CALL   Write_Char
    1262.        return
    1263. T_out             ;Display Tout message on third line
    1264.        MOVLW   0x94     ; Cursor at 0x40 Start of second line
    1265.        call   Send_Cmd
    1266.  
    1267.        movlw   'T'
    1268.        call   Write_Char
    1269.        movlw   '_'
    1270.        call   Write_Char
    1271.        movlw   'o'
    1272.        call   Write_Char
    1273.        movlw   'u'
    1274.        call   Write_Char
    1275.        movlw   't'
    1276.        call   Write_Char
    1277.        movlw   ' '
    1278.        call   Write_Char
    1279.        return
    1280.  
    1281. T_out2             ;Display Tout message on third line
    1282.        MOVLW   0x94     ; Cursor at 0x40 Start of second line
    1283.        call   Send_Cmd
    1284.  
    1285.        movlw   'T'
    1286.        call   Write_Char
    1287.        movlw   '2'
    1288.        call   Write_Char
    1289.        movlw   'o'
    1290.        call   Write_Char
    1291.        movlw   'u'
    1292.        call   Write_Char
    1293.        movlw   't'
    1294.        call   Write_Char
    1295.        movlw   ' '
    1296.        call   Write_Char
    1297.        return
    1298.  
    1299. Clear             ;Fill with spaces  third line
    1300.        MOVLW   0x94     ; Cursor at 0x40 Start of second line
    1301.        call   Send_Cmd
    1302.  
    1303.        movlw   ' '
    1304.        call   Write_Char
    1305.        movlw   ' '
    1306.        call   Write_Char
    1307.        movlw   ' '
    1308.        call   Write_Char
    1309.        movlw   ' '
    1310.        call   Write_Char
    1311.        movlw   ' '
    1312.        call   Write_Char
    1313.        movlw   ' '
    1314.        call   Write_Char
    1315.        movlw   ' '
    1316.        call   Write_Char
    1317.        movlw   ' '
    1318.        call   Write_Char
    1319.        movlw   ' '
    1320.        call   Write_Char
    1321.        movlw   ' '
    1322.        call   Write_Char
    1323.        movlw   ' '
    1324.        call   Write_Char
    1325.        movlw   ' '
    1326.        call   Write_Char
    1327.        return
    1328.  
    1329.  
    1330. ;           -------------------------------------
    1331. ;   Routine to see if scale 1 is in fast sample mode.
    1332. ;   On exit Z  flag is set if in Fast mode
    1333. Mode_Test1
    1334.  
    1335.        CALL   GetData1
    1336.        BTFSS   Flag,2     ;Check for timeout
    1337.        GOTO   MT1a
    1338.        Call   T_out
    1339.        RETURN
    1340. MT1a
    1341.        MOVLW   0x0A     ;10 * 100 us = 1 ms
    1342.        CALL   X_Delay100   ;
    1343.        CALL   GetData1
    1344.        BTFSS   Flag,2     ;Check for timeout
    1345.        GOTO   MT1b
    1346.        Call   T_out2
    1347.        RETURN
    1348.  
    1349. MT1b
    1350.        MOVLW   0x04
    1351.        SUBWF   Timeout,w   ;If Timeout still equals 4 then must be in fast mode
    1352.        BTFSC   STATUS,Z
    1353.        GOTO   MT1c
    1354.        Call   Slow
    1355.        BCF   Flag,3
    1356.        RETURN
    1357.  
    1358. MT1c
    1359.        Call   Fast
    1360.        BSF   Flag,3
    1361.        RETURN
    1362.  
    1363. ;           -------------------------------------
    1364. ;   Routine to see if scale 2 is in fast sample mode.
    1365. ;   On exit Z  flag is set if in Fast mode
    1366.  
    1367. Mode_Test2
    1368.  
    1369.        CALL   GetData2
    1370.        BTFSS   Flag,2     ;Check for timeout
    1371.        GOTO   MT2a
    1372.        Call   T_out2
    1373.        RETURN
    1374. MT2a
    1375.        MOVLW   0x0A     ;10 * 100 us = 1 ms
    1376.        CALL   X_Delay100   ;
    1377.        CALL   GetData2
    1378.        BTFSS   Flag,2     ;Check for timeout
    1379.        GOTO   MT2b
    1380.        Call   T_out2
    1381.        RETURN
    1382.  
    1383. MT2b
    1384.        MOVLW   0x04
    1385.        SUBWF   Timeout,w   ;If Timeout still equals 4 then must be in fast mode
    1386.        BTFSC   STATUS,Z
    1387.        GOTO   MT2c
    1388.        Call   Slow
    1389.        BCF   Flag,4
    1390.        RETURN
    1391.  
    1392. MT2c
    1393.        Call   Fast
    1394.        BSF   Flag,4
    1395.        RETURN
    1396.  
    1397. ;           -------------------------------------
    1398. Set_Fast1
    1399.        CALL   Mode_Test1
    1400.      movlw   D'200'     ;Delay 2 seconds   ;!!!!!! TESTING ONLY
    1401.      call   X_Delay10ms  
    1402.        CALL   Clear
    1403.        BTFSC   Flag,3     ;If  flag,3 is set we are in fast sample mode
    1404.        RETURN       ;Allready in fast mode
    1405.  
    1406.        BSF   Sel_A     ;Select scale 1 data
    1407.        BCF   Sel_B
    1408.        BCF   INH4051     ;Remove inhibit ie pull data 1 high
    1409.  
    1410.        MOVLW   D'20'     ;Set for delay of 200 ms
    1411.        CALL   X_Delay10ms
    1412.  
    1413.    
    1414.        BSF   INH4051     ;Enable inhibit ie no clock or data pull ups
    1415.        CALL   Mode_Test1
    1416.      movlw   D'200'     ;Delay 2 seconds   ;!!!!!! TESTING ONLY
    1417.      call   X_Delay10ms
    1418.        BTFSC   Flag,3     ;If  flag,3 is set we are in fast sample mode  
    1419.        RETURN
    1420.        BCF   Sel_A     ;Select scale 1 clock
    1421.        BCF   Sel_B
    1422.        BCF   INH4051     ;Remove inhibit ie pull clock 1 high
    1423.  
    1424.        MOVLW   D'20'     ;Set for delay of 200 ms
    1425.        CALL   X_Delay10ms
    1426.      
    1427.        BSF   INH4051     ;Enable inhibit ie no clock or data pull up's
    1428.        RETURN    
    1429. ;           -------------------------------------
    1430.  
    1431. Set_Fast2
    1432.        CALL   Mode_Test2
    1433.      movlw   D'200'     ;Delay 2 seconds   ;!!!!!! TESTING ONLY
    1434.      call   X_Delay10ms  
    1435.        CALL   Clear
    1436.        BTFSC   Flag,4     ;If  flag,4 is set we are in fast sample mode  
    1437.        RETURN       ;Allready in fast mode
    1438.  
    1439.        BSF   Sel_A     ;Select scale 2 data
    1440.        BSF   Sel_B
    1441.        BCF   INH4051     ;Remove inhibit ie pull data 1 high
    1442.  
    1443.        MOVLW   D'20'     ;Set for delay of 200 ms
    1444.        CALL   X_Delay10ms
    1445.      
    1446.        BSF   INH4051     ;Enable inhibit ie no clock or data pull ups
    1447.        CALL   Mode_Test2
    1448.      movlw   D'200'     ;Delay 2 seconds   ;!!!!!! TESTING ONLY
    1449.      call   X_Delay10ms
    1450.        BTFSC   Flag,4     ;If  flag,4 is set we are in fast sample mode  
    1451.        RETURN
    1452.        BCF   Sel_A     ;Select scale 2 clock
    1453.        BSF   Sel_B
    1454.        BCF   INH4051     ;Remove inhibit ie pull clock 1 high
    1455.  
    1456.        MOVLW   D'20'     ;Set for delay of 200 ms
    1457.        CALL   X_Delay10ms
    1458.      
    1459.        BSF   INH4051     ;Enable inhibit ie no clock or data pull up's
    1460.        RETURN    
    1461. ;           -------------------------------------
    1462. Set_Slow1
    1463.        CALL   Mode_Test1
    1464.        BTFSS   Flag,3     ;If  flag,3 is set we are in fast sample mode  
    1465.        RETURN       ;Allready in slow mode
    1466.  
    1467.        BSF   Sel_A     ;Select scale 1 data
    1468.        BCF   Sel_B
    1469.        BCF   INH4051     ;Remove inhibit ie pull data 1 high
    1470.  
    1471.        MOVLW   D'20'     ;Set for delay of 200 ms
    1472.        CALL   X_Delay10ms
    1473.      
    1474.        BSF   INH4051     ;Enable inhibit ie no clock or data pull up's
    1475.        RETURN
    1476. ;           -------------------------------------  
    1477. Set_Slow2
    1478.        CALL   Mode_Test2
    1479.        BTFSS   Flag,4     ;If  flag,4 is set we are in fast sample mode  
    1480.        RETURN       ;Allready in slow mode
    1481.  
    1482.        BSF   Sel_A     ;Select scale 2 data
    1483.        BSF   Sel_B
    1484.        BCF   INH4051     ;Remove inhibit ie pull data 1 high
    1485.  
    1486.        MOVLW   D'20'     ;Set for delay of 200 ms
    1487.        CALL   X_Delay10ms
    1488.      
    1489.        BSF   INH4051     ;Enable inhibit ie no clock or data pull up's
    1490.        RETURN
    1491. ;           -------------------------------------    
    1492.  
    1493.  
    1494.        END
    1495.  
    1496.  
    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.
     
  8. MrChips

    Moderator

    Oct 2, 2009
    17,078
    5,282
    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.
     
  9. jpanhalt

    Thread Starter Expert

    Jan 18, 2008
    6,377
    1,219
    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
     
  10. BobaMosfet

    Senior Member

    Jul 1, 2009
    396
    86
    @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.
     
  11. jpanhalt

    Thread Starter Expert

    Jan 18, 2008
    6,377
    1,219
    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.
     
  12. MMcLaren

    Distinguished Member

    Feb 14, 2010
    837
    158
    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.)?
    Code (C):
    1.  
    2. /*
    3.     1-16-04
    4.     Copyright Spark Fun Electronics© 2004
    5.  
    6.     Uses 16F688 to drive Parallel LCD with Serial Commands.
    7.  
    8.     For educational and hobbyist use only. NOT for commerical use.
    9.  
    10.  
    11.     Thanks to Olimex for providing the original C test code for the PIC-MT-C w/ 16F873
    12.  
    13.     1-17-04 Started migrating 16F873A code to 16F630
    14.  
    15.  
    16.     1-17-04pm Works well from user keyboard input. No busy flag checking yet.
    17.               Ok, busy flag works.
    18.  
    19.  
    20.     1-19-04 Got commands to pass through after decimal 254 is seen. The special 8-bit data
    21.             mode command is also correctly ignored.
    22.          
    23.     1-30-04 v1.1 Added 16x4 line support
    24.  
    25.     2-10-04 v1.2 Added BL Control. Moved to direct RC0-RC3 = D4-D7 mapping. This significantly
    26.             improved timing between characters.
    27.  
    28.     2-12-04 Added EEPROM recording of Backlight State. Also added Super Special
    29.             Command input. Pass the '|' character to the LCD, then pass '1' to turn
    30.             on/off backlight.
    31.          
    32.             Added universal support for any LCD upto 4 lines, 40 characters wide.
    33.          
    34.     2-16-04 New PCBs v1.1 - Replaced some if statements with EL1, EL2, etc, in the 4 line wrapping
    35.             routine to try to speed things up.
    36.          
    37.     2-17-04 v1.3 Timing has now been nearly reduced to within one stop bit. In the past,
    38.             wrapping characters were some times lost because of the time required to relocate
    39.             the cursor. Now almost all characters are seen, every time, every location.
    40.             There are stuff certain combinations that cause a character miss.
    41.  
    42.     3-3-04 New PCB - 1.1a. BL Control has been routed to PGC - bad idea. Causes problems with programming.
    43.            Unit must be powered +5V while programming to get it to work. Will move this line on next revision.
    44.          
    45.     4-5-04 Customer reported problems with interface. Baud rate was too low. The test bed was running
    46.            at much too low of a speed. The test bed has been corrected, the code must now be tweaked.
    47.          
    48.     4-14-04 Added a special command 8 that will re-initialize the LCD. There are certain circumstances (low
    49.             power) where the PIC will keep running, but the LCD will shut down.
    50.  
    51.  
    52.     6-1-04 Re-routed some pins for a smoother PCB layout. Firmware had to be updated.
    53.  
    54.  
    55.     7-7-04 Added support for 40x2 displays
    56.  
    57.     12-15-04 New version - SerLCD v2 using 16F688
    58.              Incoming characters buffered
    59.              PWM of back light
    60.              Turning off splash screen
    61.              Higher data speeds
    62.            
    63.              We now use the 16F688 with onboard UART and internal 8MHz oscillator. The internal oscillator is much
    64.              tighter tolerance and the UART interrupt is used. The incoming characters are automatically buffered,
    65.              up to 80 characters can be stored at a time in case the LCD is busy doing other things.
    66.            
    67.              The backlight is now pulse width modulated. What this means is you can set how bright the backlight is
    68.              from 0 (off) to 30 (completely on). This will greatly effect how much current the module uses while
    69.              retaining part of the ability to see in low light conditions. The backlight defaults to maximum
    70.              output (brightest) unless otherwise configured by the user. To control the backlight, send the 'special
    71.              command' character (124) followed by 0b.100?.???? where ?? is the value from 0-30.
    72.            
    73.              You can now turn off the splash screen. Send the 'special command' character 124 followed by decimal 9.
    74.              The splash screen option will toggle and will take effect the next time the LCD is initialized (power-up).
    75.  
    76.     3-30-05  Added busy checking before the LCD is init'd. SPBRG is set to 51 per datasheet for 16F688.
    77.              Extended the delays a bit during LCD init to try to avoid hangups with 3% of units that report hanging.
    78.  
    79.     4/29/09  Changed code for integrated LCD with backpack, outsourced hardware. Control lines to LCD from PIC have changed.
    80.  
    81. */
    82.  
    83.  
    84. #define Clock_10MHz
    85. #define Baud_9600
    86. //#define DEBUG
    87.  
    88.  
    89. #include "C:\Global\PIC\C\16F88.h"  // device dependent interrupt definitions
    90. #include "C:\Global\PIC\C\int16CXX.H"
    91.  
    92.  
    93.  
    94. #pragma config|= 0x3F02
    95. //#pragma config|= 0x2F8A   //10MHz xtal
    96. //#pragma config|= 0x29D3     //int RC
    97.  
    98.  
    99. //LCD Type
    100. #define Display_16x2    0
    101. #define Display_16x4    0
    102.  
    103.  
    104. //Hardware port definitions
    105. #define     RS              PORTB.4
    106. #define     R_W             PORTB.1
    107. #define     BL_Control      PORTA.4
    108. #define     E               PORTB.0
    109.  
    110. //#define     STAT            PORTA.4
    111.  
    112.  
    113. #define     D4              PORTA.0
    114. #define     D5              PORTA.1
    115. #define     D6              PORTA.2
    116. #define     D7              PORTA.3
    117.  
    118.  
    119. //Constant definitions
    120. #define     FALSE       0
    121. #define     TRUE        1
    122.  
    123.  
    124. #define     LCD_BACKLIGHT_SETTING   0
    125. #define     LCD_WIDTH_SETTING       1
    126. #define     LCD_LINE_SETTING        2
    127. #define     LCD_BRIGHTNESS_SETTING  3
    128. #define     LCD_SPLASH_ENABLE       4
    129. #define     LINE1_START             5
    130. #define     LINE2_START             25
    131. #define     BAUD_RATE               45
    132.  
    133.  
    134.  
    135.  
    136. #define     CLR_DISP        0b.0000.0001 //Clear display
    137.  
    138.  
    139. #define     CUR_HOME        0b.0000.0010    //Move cursor home and clear screen memory
    140. #define     CUR_RIGHT       0b.0001.0100    //Move cursor one to right
    141. #define     CUR_LEFT        0b.0001.0000    //Move cursor one to left
    142.  
    143.  
    144. #define     SCROLL_RIGHT    0b.0001.1100    //Scroll entire screen right one space
    145. #define     SCROLL_LEFT     0b.0001.1000    //Scroll entire screen left one space
    146.  
    147.  
    148. #define     DISP_ON         0b.0000.1100    //Turn visible LCD on
    149. #define     DISP_OFF        0b.0000.1000    //Turn visible LCD off
    150.  
    151.  
    152. #define     UNDERLINE_ON    0b.0000.1110    //Turn on underlined cursor
    153. #define     UNDERLINE_OFF   0b.0000.1100    //Turn off underlined cursor
    154.  
    155.  
    156. #define     BLINKCUR_ON     0b.0000.1101    //Turn on blinking box cursor
    157. #define     BLINKCUR_OFF    0b.0000.1100    //Turn off blinking box cursor
    158.  
    159.  
    160. #define     DUALCUR_ON      0b.0000.1111    //Turn on blinking box and underline cursor
    161. #define     DUALCUR_OFF     0b.0000.1100    //Turn off blinking box and underine cursor
    162.  
    163.  
    164. #define     SET_CURSOR      0b.1000.0000    //SET_CURSOR + X : Sets cursor position to X
    165.  
    166.  
    167. #define     ENTRY_INC       0b.0000.0110 //
    168. #define     DD_RAM_ADDR     0b.1000.0000 //
    169. #define     DD_RAM_ADDR2    0b.1100.0000 //
    170. #define     CG_RAM_ADDR     0b.0100.0000 //
    171.  
    172.  
    173. //Global variable declarations
    174. uns8 letter;
    175. static uns8 cursor_position;
    176. uns8 splash_enable;
    177.  
    178.  
    179. uns8 backlight_counter;
    180. uns8 brightness_setting;
    181. uns8 Backlight_Setting;
    182.  
    183.  
    184. uns8 LCD_Type;
    185. uns8 LCD_Width;
    186. uns8 LCD_Lines;
    187. uns8 EL1, EL2, EL3, EL4;
    188. uns8 CP1, CP2, CP3, CP4;
    189. uns8 baud;
    190.  
    191.  
    192. uns8 RX_In;
    193. uns8 Displayed_Letters;
    194.  
    195.  
    196. #pragma rambank 1
    197. #define BUFFER_SIZE 80
    198. uns8 RX_Array[BUFFER_SIZE];
    199.  
    200. #pragma rambank 0
    201. uns8 line1[20];
    202. uns8 line2[20];
    203.  
    204.  
    205. //End Global variable declarations
    206.  
    207.  
    208.  
    209. //Interrupt Vectors
    210. #pragma origin 4
    211. interrupt serverX( void)
    212. {
    213.     int_save_registers
    214.     char sv_FSR = FSR;  // save FSR if required
    215.  
    216.  
    217.     if(RCIF) //If we have received something from the computer store it in the RX_Array array
    218.     {
    219.      
    220.         RX_In++;
    221.         if(RX_In == BUFFER_SIZE) RX_In = 0;
    222.      
    223.         RX_Array[RX_In] = RCREG;
    224.     }
    225.  
    226.  
    227.     if(T0IF) //TMR0 Overflow Interrupt - Occurs every 1024us ~1ms
    228.     {
    229.         backlight_counter++;
    230.  
    231.  
    232.         if(backlight_counter == 30)
    233.         {
    234.             backlight_counter = 0; //30ms pulse width period
    235.          
    236.             if (brightness_setting > 0) BL_Control = 0; //Kick on the back light only if brightness setting is greater than 0
    237.         }
    238.  
    239.  
    240.         //Turn off back light after its assigned duration
    241.         if(backlight_counter == brightness_setting) BL_Control = 1;
    242.      
    243.         T0IF = 0; //Clear Interrupt Flag
    244.     }
    245.  
    246.     FSR = sv_FSR;               // restore FSR if saved
    247.     int_restore_registers
    248. }
    249.  
    250.  
    251. //Function declarations
    252. void boot_up(void);
    253. void special_commands(void);
    254. void delay_ms(uns16);
    255. void delay_ms(uns16);
    256. uns8 onboard_eeread(uns8 e_address);
    257. void onboard_eewrite(uns8 e_data, uns8 e_address);
    258. void set_baud_rate(void);
    259.  
    260.  
    261. void send_char(uns8);
    262. void send_cmd(uns8);
    263. void send_string(const char* incoming_string);
    264. void LCD_wait(void);
    265. void init_lcd(void);
    266.  
    267.  
    268. //#include "C:\Global\PIC\C\stdio.c"
    269.  
    270. //End Function declarations
    271.  
    272.  
    273. void main(void)
    274. {
    275.     //Initialize LCD and PIC
    276.     boot_up();
    277.  
    278.     while(1)
    279.     {
    280.         //sleep(); //Wait for incoming command to wake us up...
    281.         //nop(); //Executes after wake-up and before INTF runs
    282.      
    283.         if(RX_In != Displayed_Letters)
    284.         {
    285.             Displayed_Letters++;
    286.             if(Displayed_Letters == BUFFER_SIZE) Displayed_Letters = 0;
    287.  
    288.  
    289.             letter = RX_Array[Displayed_Letters];
    290.  
    291.  
    292.  
    293.             //Check for special LCD command
    294.             //===============================================================
    295.             if (letter == 254)
    296.             {
    297.                 //Wait for the next letter
    298.                 while(RX_In == Displayed_Letters);
    299.              
    300.                 //Look at the next letter
    301.                 Displayed_Letters++;
    302.                 if(Displayed_Letters == BUFFER_SIZE) Displayed_Letters = 0;
    303.  
    304.  
    305.                 letter = RX_Array[Displayed_Letters];
    306.              
    307.                 //Ignore the one command that will send the LCD into 8-bit mode
    308.                 if ( (letter >> 4) != 3 ) //If not 0b.0000.0011, then send it to LCD
    309.                     send_cmd(letter);
    310.                  
    311.                 //Correct cursor position variable if certain commands are received
    312.                 if (letter == CLR_DISP)
    313.                     cursor_position = 0;
    314.                 else if (letter.7 == 1) //Move cursor command
    315.                     cursor_position = letter & 0b.0111.1111; //Ignore first bit - obtain address
    316.             }
    317.             //===============================================================
    318.  
    319.  
    320.             //Super Special LCD Commands
    321.             //===============================================================
    322.             else if (letter == 124) //This is the '|' character above '\'
    323.             {
    324.                 //Wait for the next letter
    325.                 while(RX_In == Displayed_Letters);
    326.  
    327.  
    328.                 //Look at the next letter
    329.                 Displayed_Letters++;
    330.                 if(Displayed_Letters == BUFFER_SIZE) Displayed_Letters = 0;
    331.  
    332.  
    333.                 letter = RX_Array[Displayed_Letters];
    334.  
    335.  
    336.                 special_commands();
    337.             }
    338.             //===============================================================
    339.          
    340.          
    341.          
    342.             //All else, just print it to the LCD and adjust the cursor position
    343.             //===============================================================
    344.             else
    345.             {
    346.                 #ifdef DEBUG
    347.                     if (letter == '+') brightness_setting++;
    348.                     if (brightness_setting > 30) brightness_setting = 0;
    349.                  
    350.                     if( letter == 'A')
    351.                     {
    352.                         letter = 0b.1000.0000 | 15;
    353.                         special_commands();
    354.                     }
    355.                 #endif
    356.              
    357.              
    358.              
    359.              
    360. //Backspace============================================================================            
    361.                 if (letter == 8)
    362.                 {
    363.                  
    364.                     if(cursor_position == (LCD_Width + 64))        //from beginning of line 4
    365.                     {
    366.                         //send_cmd(CP3);
    367.                         send_cmd(CP3 + LCD_Width - 1);
    368.                         send_char(32);
    369.                         send_cmd(CP3 + LCD_Width - 1);
    370.                         cursor_position = EL3 - 1;
    371.                      
    372.                         //printf("%d\r\n",cursor_position);
    373.                     }
    374.                  
    375.                     else if(cursor_position == LCD_Width)   //from beginning of line 3
    376.                     {
    377.                         //send_cmd(CP2);
    378.                         send_cmd(CP2 + LCD_Width - 1);
    379.                         send_char(32);
    380.                         send_cmd(CP2 + LCD_Width - 1);
    381.                         cursor_position = EL2 - 1;
    382.                      
    383.                         line2[cursor_position - 64] = 32;
    384.                      
    385.                         //printf("%d\r\n",cursor_position);
    386.                     }
    387.                  
    388.                  
    389.                     else if(cursor_position == 64)      //from beginning of line 2
    390.                     {
    391.                      
    392.                         //send_cmd(CP1);
    393.                         send_cmd(CP1 + LCD_Width - 1);
    394.                         send_char(32);
    395.                         send_cmd(CP1 + LCD_Width - 1);
    396.                         cursor_position = EL1 - 1;
    397.                      
    398.                         line1[cursor_position] = 32;
    399.                      
    400.                         //printf("%d\r\n",cursor_position);
    401.                  
    402.                     }
    403.                      
    404.                     else if (cursor_position == 0)      //from beginning of line 1
    405.                     {
    406.                         if(LCD_Lines == 1)
    407.                         {
    408.                             send_cmd(CP1 + LCD_Width - 1);
    409.                             send_char(32);
    410.                             send_cmd(CP1 + LCD_Width - 1);
    411.                             cursor_position = EL1 - 1;
    412.                          
    413.                             //printf("%d\r\n",cursor_position);
    414.                         }
    415.                      
    416.                         else if (LCD_Lines == 2)                  
    417.                         {
    418.                             //send_cmd(CP2);
    419.                             send_cmd(CP2 + LCD_Width - 1);
    420.                             send_char(32);
    421.                             send_cmd(CP2 + LCD_Width - 1);
    422.                             cursor_position = EL2 - 1;
    423.                          
    424.                             //printf("%d\r\n",cursor_position);
    425.                         }
    426.                      
    427.                         else    //4 line display
    428.                         {
    429.                             //send_cmd(CP4);
    430.                             send_cmd(CP4 + LCD_Width - 1);
    431.                             send_char(32);
    432.                             send_cmd(CP4 + LCD_Width - 1);
    433.                             cursor_position = EL4 - 1;
    434.                          
    435.                             //printf("%d\r\n",cursor_position);
    436.                         }
    437.                     }
    438.                  
    439.                     else
    440.                     {
    441.                         send_cmd(CUR_LEFT);
    442.                         send_char(32);
    443.                         send_cmd(CUR_LEFT);
    444.                         cursor_position--;
    445.                      
    446.                         //make sure that the line arrays are correct
    447.                         if (cursor_position < 16) line1[cursor_position] = 32;
    448.                         if ((cursor_position > 63) && (cursor_position < 80)) line2[cursor_position - 64] = 32;
    449.                      
    450.                         //printf("%d\r\n",cursor_position);
    451.                     }
    452.                 }
    453.              
    454.              
    455. //Regular character send==========================================================================
    456.                 else
    457.                 {
    458.                     send_char(letter);
    459.                  
    460.                     //keep the first 2 lines logged for splash screen changes
    461.                     if (cursor_position < 20) line1[cursor_position] = letter;
    462.                     if ((cursor_position > 63) && (cursor_position < 84)) line2[cursor_position - 64] = letter;
    463.  
    464.                     cursor_position++;
    465.                  
    466.                  
    467.                     //When the cursor gets to the end of one line, it must
    468.                     //advance to the next visual line
    469.                     //===============================================================
    470.                     if (cursor_position == EL1) //End of line one
    471.                     {
    472.                         if(LCD_Lines == 1)
    473.                         {
    474.                             cursor_position = 0; //Return to beginning of line 1
    475.                             send_cmd(CP1);
    476.                         }
    477.                         else
    478.                         {
    479.                             cursor_position = 64; //Beginning of line 2
    480.                             send_cmd(CP2);
    481.                         }
    482.                     }
    483.                  
    484.                     else if (cursor_position == EL2) //End of line 2
    485.                     {
    486.                         if(LCD_Lines == 2)
    487.                         {
    488.                             cursor_position = 0; //Return to line 1
    489.                             send_cmd(CP1);
    490.                         }
    491.                         else
    492.                         {
    493.                             cursor_position = LCD_Width; //Beginning of line 3
    494.                             send_cmd(CP3);
    495.                         }
    496.                     }
    497.                  
    498.                     else if (cursor_position == EL3) //End of line 3
    499.                     {
    500.                         cursor_position = EL2; //Beginning of line 4 is the end of line 2
    501.                         send_cmd(CP4);
    502.                     }
    503.                  
    504.                     else if (cursor_position == EL4) //End of line 4
    505.                     {
    506.                         cursor_position = 0; //Beginning of line 1
    507.                         send_cmd(CP1);
    508.                     }
    509.                  
    510.                  
    511.              
    512.                 }
    513.                 //===============================================================
    514.             }
    515.             //===============================================================
    516.              
    517.  
    518.  
    519.         }//End regular character printing
    520.          
    521.    }//End infinite loop
    522.  
    523.  
    524. }//End Main
    525.  
    526.  
    527. //This is called when the control character is received.
    528. //The control character is 124 or '|' (above the '\' on standard US layout keyboards)
    529. void special_commands(void)
    530. {
    531.     uns8 x;
    532.  
    533.     //Backlight control
    534.     if (letter == 1)
    535.     {
    536.         brightness_setting = 30;
    537.         onboard_eewrite(brightness_setting, LCD_BRIGHTNESS_SETTING); //Record to EEPROM
    538.         //Backlight_Setting = 0; //Turn on backlight setting
    539.         //BL_Control = Backlight_Setting; //Turn on backlight
    540.         //onboard_eewrite(Backlight_Setting, LCD_BACKLIGHT_SETTING); //Record to EEPROM
    541.     }
    542.     else if(letter == 2)
    543.     {
    544.         brightness_setting = 0;
    545.         onboard_eewrite(brightness_setting, LCD_BRIGHTNESS_SETTING); //Record to EEPROM
    546.         //Backlight_Setting = 1; //Turn off backlight setting
    547.         //BL_Control = Backlight_Setting; //Turn off backlight
    548.         //onboard_eewrite(Backlight_Setting, LCD_BACKLIGHT_SETTING); //Record to EEPROM
    549.     }
    550.  
    551.     //LCD type commands
    552.     else if(letter == 3)
    553.     {
    554.         LCD_Width = 20;
    555.         onboard_eewrite(LCD_Width, 1);
    556.     }
    557.     else if(letter == 4)
    558.     {
    559.         LCD_Width = 16;
    560.         onboard_eewrite(LCD_Width, 1);
    561.     }
    562.     else if(letter == 5)
    563.     {
    564.         LCD_Lines = 4;
    565.         onboard_eewrite(LCD_Lines, 2);
    566.     }
    567.     else if(letter == 6)
    568.     {
    569.         LCD_Lines = 2;
    570.         onboard_eewrite(LCD_Lines, 2);
    571.     }
    572.     else if(letter == 7)
    573.     {
    574.         LCD_Lines = 1;
    575.         onboard_eewrite(LCD_Lines, 2);
    576.     }
    577.     else if(letter == 8) //Re-init command
    578.     {
    579.         init_lcd();
    580.     }
    581.  
    582.  
    583.     //Turn on/off splash screen
    584.     else if(letter == 9)
    585.     {
    586.         if (splash_enable == 1) splash_enable = 0;
    587.         else splash_enable = 1;
    588.      
    589.         onboard_eewrite(splash_enable, LCD_SPLASH_ENABLE); //Record to EEPROM
    590.     }
    591.  
    592.     //Save alternate splash screen from line arrays (first two lines)
    593.     else if(letter == 10)   //^j
    594.     {
    595.         for (x = 0; x < 20; x++)
    596.         {
    597.             onboard_eewrite(line1[x], LINE1_START + x); //Record to EEPROM
    598.             onboard_eewrite(line2[x], LINE2_START + x);
    599.         }
    600.      
    601.          
    602.     }
    603.  
    604.     //set baud rate to 2400
    605.     else if(letter == 11)   //^k
    606.     {
    607.         baud = 0;
    608.         set_baud_rate();
    609.         onboard_eewrite(baud, BAUD_RATE);
    610.          
    611.     }
    612.  
    613.  
    614.     //set baud rate to 4800
    615.     else if(letter == 12)   //^l
    616.     {
    617.         baud = 1;
    618.         set_baud_rate();
    619.         onboard_eewrite(baud, BAUD_RATE);
    620.          
    621.     }
    622.  
    623.  
    624.     //set baud rate to 9600
    625.     else if(letter == 13)   //^m
    626.     {
    627.         baud = 2;
    628.         set_baud_rate();
    629.         onboard_eewrite(baud, BAUD_RATE);
    630.          
    631.     }
    632.  
    633.  
    634.     //set baud rate to 14400
    635.     else if(letter == 14)   //^n
    636.     {
    637.         baud = 3;
    638.         set_baud_rate();
    639.         onboard_eewrite(baud, BAUD_RATE);
    640.          
    641.     }
    642.  
    643.  
    644.     //set baud rate to 19200
    645.     else if(letter == 15)   //^o
    646.     {
    647.         baud = 4;
    648.         set_baud_rate();
    649.         onboard_eewrite(baud, BAUD_RATE);
    650.          
    651.     }
    652.  
    653.  
    654.     //set baud rate to 38400
    655.     else if(letter == 16)   //^p
    656.     {
    657.         baud = 5;
    658.         set_baud_rate();
    659.         onboard_eewrite(baud, BAUD_RATE);
    660.          
    661.     }
    662.  
    663.  
    664.     //Back light brightness settings
    665.     else if(letter >= 128)
    666.     {
    667.         brightness_setting = letter & 0b.0001.1111; //Brightness is a value from 0-29 only (5-bit)
    668.         onboard_eewrite(brightness_setting, LCD_BRIGHTNESS_SETTING); //Record to EEPROM
    669.     }
    670.  
    671.  
    672.     //LCD_Width is the number of seen characters
    673.     //EL1 is End Line 1 - Normally LCD_Width
    674.     //EL2 is End Line 2 - Normally 64 + LCD_Width
    675.     //EL3 is End Line 3 - LCD_Width + LCD_Width
    676.     //EL4 is End Line 4 - 64 + LCD_Width + LCD_Width
    677.  
    678.  
    679.     EL1 = LCD_Width;
    680.     EL2 = 64 + LCD_Width;
    681.     EL3 = LCD_Width + LCD_Width;
    682.     EL4 = 64 + LCD_Width + LCD_Width;
    683.  
    684.     //CP1 = Cursor Position 1 - 0 Always
    685.     //CP1 = CP1 & 0b.1000.000 - this is to set the DRAM command
    686.     //CP2 = 64 Always = 0b.1000.0000 | 64
    687.     //CP3 = LCD_width = 0b.1000.0000 | LCD_Width
    688.     //CP4 = 64 + LCD_Width = 0b.1000.0000 | (64 + LCD_Width)
    689.  
    690.     CP1 = 0b.1000.0000 | 0;
    691.     CP2 = 0b.1000.0000 | 64;
    692.     CP3 = 0b.1000.0000 | LCD_Width;
    693.     CP4 = 0b.1000.0000 | (LCD_Width + 64);
    694.  
    695. }
    696.  
    697.  
    698. //Initializes the various ports and interrupts
    699. //Also inits the LCD
    700. void boot_up(void)
    701. {
    702.     uns8 x;
    703.     //OSCCON = 0b.0000.1000; //Setup internal oscillator for 8MHz
    704.     //while(OSCCON.2 == 0); //Wait for frequency to stabilize
    705.  
    706.     //Setup Ports
    707.     ANSEL = 0b.0000.0000; //Disable ADC on all pins
    708.     //CMCON0 = 0b.0000.0111; //Turn off comparator on RA port
    709.  
    710.  
    711.     PORTA = 0b.0001.0000;
    712.     TRISA = 0b.1110.0000; //0 = Output, 1 = Input
    713.  
    714.  
    715.     PORTB = 0b.0000.0000;
    716.     TRISB = 0b.1110.1100; //0 = Output, 1 = Input (RX on RB2)
    717.  
    718.  
    719.     //OPTION.7 = 0; //Enable weak pull-ups
    720.  
    721.     Displayed_Letters = 0;
    722.     RX_In = 0;
    723.  
    724.     //Setup the hardware UART module and interrupts
    725.     //=============================================================
    726.     //baud = onboard_eeread(BAUD_RATE);
    727.     //set_baud_rate();
    728.     SPBRG = 64;
    729.  
    730.     TXSTA = 0b.0010.0100; //
    731.     RCSTA = 0b.1001.0000; //Receive enable, 8-bit asych continous receive mode
    732.  
    733.  
    734.     //BRG16 = 1;
    735.     BRGH = 1;
    736.  
    737.  
    738.     RCIF = 0;
    739.     RCIE = 1;
    740.     PEIE = 1;
    741.     //=============================================================
    742.  
    743.  
    744.     //Setup TMR0 and interrupts
    745.     OPTION_REG = 0b.0101.0001; //Give Timer0 prescalar of 4
    746.     T0IE = 1;
    747.  
    748.  
    749.     GIE = 1; //Enable global interrupts
    750.  
    751.    //for (x = 0; x < 4; x++)
    752.     //{
    753.         //STAT = 0;
    754.         //delay_ms(500);
    755.         //STAT = 1;
    756.         //delay_ms(1500);
    757.     //}
    758.  
    759.     for (x = 0; x < 20; x++)//zero the line arrays
    760.     {
    761.         line1[x] = 0;
    762.         line2[x] = 0;
    763.     }
    764.  
    765.  
    766.     //Init LCD
    767.     init_lcd();
    768.  
    769. }
    770.  
    771.  
    772. //Initializes the 4-bit parallel interface to the HD44780
    773. void init_lcd(void)
    774. {
    775.     uns8 x;
    776.  
    777.     //Wait for LCD busy bit to clear
    778.     LCD_wait();
    779.  
    780.     RS = 0;            
    781.     R_W = 0;
    782.  
    783.  
    784.     //Tell the LCD we are using 4bit data communication
    785.     //===============================================================
    786.     delay_ms(100);
    787.     //PORTB = 0b.0000.0011;
    788.     D4 = 1; D5 = 1;
    789.     E = 1; E = 0;
    790.  
    791.  
    792.     delay_ms(50);
    793.     //PORTB = 0b.0000.0011;
    794.     E = 1; E = 0;
    795.  
    796.  
    797.     delay_ms(10);
    798.     //PORTB = 0b.0000.0011;
    799.     E = 1; E = 0;
    800.  
    801.  
    802.     delay_ms(10);
    803.     //PORTB = 0b.0000.0010;
    804.     D4 = 0;
    805.     E = 1; E = 0;
    806.  
    807.  
    808.     send_cmd(DISP_ON);
    809.     send_cmd(CLR_DISP);
    810.     //===============================================================
    811.     //LCD Init Complete
    812.  
    813.     //Retrieve last Backlight state
    814.     //===============================================================
    815.     Backlight_Setting = onboard_eeread(LCD_BACKLIGHT_SETTING);
    816.     if (Backlight_Setting > 1) Backlight_Setting = 1; //Default
    817.     onboard_eewrite(Backlight_Setting, LCD_BACKLIGHT_SETTING); //Record to EEPROM
    818.     BL_Control = Backlight_Setting; //Turn on/off the backlight
    819.     //===============================================================
    820.  
    821.     //Retrieve backlight brightness setting
    822.     //===============================================================
    823.     brightness_setting = onboard_eeread(LCD_BRIGHTNESS_SETTING);
    824.     if (brightness_setting > 30) brightness_setting = 30; //Turn LCD backlight on to brightest by default
    825.     onboard_eewrite(brightness_setting, LCD_BRIGHTNESS_SETTING); //Record to EEPROM
    826.     //===============================================================
    827.  
    828.  
    829.     //Retrieve last LCD Type
    830.     //===============================================================
    831.     LCD_Width = onboard_eeread(LCD_WIDTH_SETTING);
    832.     if (LCD_Width > 80) LCD_Width = 20; //Default
    833.     onboard_eewrite(LCD_Width, LCD_WIDTH_SETTING); //Record to EEPROM
    834.  
    835.  
    836.     //Set end lines
    837.     EL1 = LCD_Width;
    838.     EL2 = 64 + LCD_Width;
    839.     EL3 = LCD_Width + LCD_Width;
    840.     EL4 = 64 + LCD_Width + LCD_Width;
    841.  
    842.  
    843.     //Set cursor positions
    844.     CP1 = 0b.1000.0000 | 0;
    845.     CP2 = 0b.1000.0000 | 64;
    846.     CP3 = 0b.1000.0000 | LCD_Width;
    847.     CP4 = 0b.1000.0000 | (LCD_Width + 64);
    848.  
    849.  
    850.     if (Display_16x2 == 1) LCD_Lines = 2;
    851.     else if (Display_16x4 == 1) LCD_Lines = 4;
    852.     else
    853.     {
    854.         LCD_Lines = onboard_eeread(LCD_LINE_SETTING);
    855.         if (LCD_Lines > 8) LCD_Lines = 4; //Default
    856.         onboard_eewrite(LCD_Lines, LCD_LINE_SETTING); //Record to EEPROM
    857.     }
    858.  
    859.     //===============================================================
    860.  
    861.     //Display splash screen if enabled
    862.     //===============================================================
    863.     splash_enable = onboard_eeread(LCD_SPLASH_ENABLE);
    864.     if (splash_enable > 1) splash_enable = 1; //Default to show splash screen
    865.     onboard_eewrite(splash_enable, LCD_SPLASH_ENABLE); //Record to EEPROM
    866.  
    867.     if (splash_enable == 0)
    868.     {
    869.         delay_ms(1000);
    870.      
    871.         if(RX_In > 0)
    872.         {
    873.             x = RX_Array[1];
    874.          
    875.             if (x == 18)    //^r to reset the baud rate to 9600
    876.             {
    877.                 baud = 2;
    878.                 set_baud_rate();
    879.                 onboard_eewrite(baud, BAUD_RATE);
    880.              
    881.                 send_string("Reset to 9600");
    882.                 delay_ms(1000);
    883.                 send_cmd(CLR_DISP);
    884.              
    885.             }
    886.          
    887.             RX_In = 0;
    888.         }
    889.      
    890.      
    891.         else
    892.         {
    893.             baud = onboard_eeread(BAUD_RATE);
    894.             set_baud_rate();
    895.         }
    896.     }
    897.  
    898.  
    899.  
    900.     else if (splash_enable == 1)
    901.     {
    902.         x = onboard_eeread(LINE1_START);
    903.         if (x != 0xFF)
    904.         {
    905.             send_cmd(CP2);//sets cursor position to beginning of line 2 for 4-line displays
    906.             for (x = 0; x < 20; x++)
    907.             {
    908.                 send_char(onboard_eeread(LINE1_START + x));
    909.              
    910.             }
    911.          
    912.             send_cmd(CP3);
    913.          
    914.             for (x = 0; x < 20; x++)
    915.             {
    916.                 send_char(onboard_eeread(LINE2_START + x));
    917.              
    918.             }
    919.          
    920.             delay_ms(1000);
    921.             send_cmd(CLR_DISP);
    922.         }
    923.          
    924.         else
    925.         {
    926.             //send_cmd(SET_CURSOR + LINE2_START);
    927.             send_cmd(CP2);
    928.             send_string("    SparkFun.com");
    929.             //send_cmd(SET_CURSOR + LINE3_START);
    930.             send_cmd(CP3);
    931.             send_string("    SerLCD v2.5");
    932.             delay_ms(1000);
    933.             send_cmd(CLR_DISP);
    934.         }
    935.          
    936.      
    937.         if(RX_In > 0)
    938.         {
    939.             x = RX_Array[1];
    940.          
    941.             if (x == 18)    //^r to reset the baud rate to 9600
    942.             {
    943.                 baud = 2;
    944.                 set_baud_rate();
    945.                 onboard_eewrite(baud, BAUD_RATE);
    946.              
    947.                 send_string("Reset to 9600");
    948.                 delay_ms(1000);
    949.                 send_cmd(CLR_DISP);
    950.              
    951.             }
    952.          
    953.             RX_In = 0;
    954.         }
    955.      
    956.      
    957.         else
    958.         {
    959.             baud = onboard_eeread(BAUD_RATE);
    960.             set_baud_rate();
    961.         }
    962.      
    963.     }
    964.  
    965.     //===============================================================
    966.  
    967.     for (x = 0; x < 16; x++)
    968.     {
    969.         line1[x] = 32;  //init the line arrays to black spaces
    970.         line2[x] = 32;  //
    971.     }
    972.  
    973.     cursor_position = 0;
    974. }
    975.  
    976.  
    977. //Checks the busy flag and waits until LCD is ready for next command
    978. void LCD_wait(void)
    979. {
    980.     bit i = 1;
    981.     uns8 j;
    982.  
    983.     TRISA |= 0b.0000.1111; //0 = Output, 1 = Input
    984.  
    985.  
    986.     R_W = 1; //Tell LCD to output status
    987.     RS = 0;            
    988.  
    989.  
    990.     while(i == 1)
    991.     {
    992.         E = 1;
    993.         //for (j = 0; j < 1; j++);
    994.         i = D7; //Read data bit 7 - Busy Flag
    995.         E = 0;
    996.  
    997.         E = 1;
    998.         //for (j = 0; j < 1; j++);
    999.         E = 0; //Toggle E to get the second four bits of the status byte - but we don't care
    1000.     }
    1001.  
    1002.     TRISA &= 0b.1111.0000; //0 = Output, 1 = Input
    1003.  
    1004.     //STAT = 0;
    1005.  
    1006.     //while (1);
    1007. }
    1008.  
    1009.  
    1010. //Sends an ASCII character to the LCD
    1011. void send_char(uns8 c)
    1012. {
    1013.     LCD_wait();
    1014.  
    1015.     R_W = 0; //set LCD to write
    1016.     RS = 1; //set LCD to data mode
    1017.  
    1018.     //PORTC = c >> 4;
    1019.     D4 = c.4; D5 = c.5; D6 = c.6; D7 = c.7;
    1020.     E = 1; E = 0;
    1021.  
    1022.     //PORTC = c;
    1023.     D4 = c.0; D5 = c.1; D6 = c.2; D7 = c.3;
    1024.     E = 1; E = 0; //Toggle the Enable Pin
    1025. }
    1026.  
    1027.  
    1028. //Sends an LCD command
    1029. void send_cmd(uns8 d)
    1030. {
    1031.     LCD_wait();
    1032.  
    1033.  
    1034.     //TRISC = 0b.0000.0000;   //0 = Output, 1 = Input
    1035.  
    1036.  
    1037.     R_W = 0; //set LCD to write
    1038.  
    1039.  
    1040.     //PORTC = d >> 4;
    1041.     D4 = d.4; D5 = d.5; D6 = d.6; D7 = d.7;
    1042.     E = 1; E = 0;
    1043.  
    1044.  
    1045.     //PORTC = d;
    1046.     D4 = d.0; D5 = d.1; D6 = d.2; D7 = d.3;
    1047.     E = 1; E = 0;
    1048. }
    1049.  
    1050.  
    1051. //Sends a given string to the LCD. Will start printing from
    1052. //current cursor position.
    1053. void send_string(const char *incoming_string)
    1054. {
    1055.     uns8 x;
    1056.  
    1057.     for(x = 0 ; incoming_string[x] != '\0' ; x++)
    1058.         send_char(incoming_string[x]);
    1059. }
    1060.  
    1061.  
    1062. void set_baud_rate(void)
    1063. {
    1064.     //changed for 4MHz
    1065.     if (baud == 0) SPBRG = 255;             //2400
    1066.     else if (baud == 1) SPBRG = 128;        //4800
    1067.     else if (baud == 2) SPBRG = 64;         //9600
    1068.     else if (baud == 3) SPBRG = 43;         //14400
    1069.     else if (baud == 4) SPBRG = 31;         //19200
    1070.     else if (baud == 5) SPBRG = 15;         //38400
    1071.  
    1072.     else
    1073.     {
    1074.         SPBRG = 64;
    1075.         onboard_eewrite(2, BAUD_RATE);
    1076.     }
    1077.  
    1078.  
    1079. }
    1080.  
    1081.  
    1082. //General short delay
    1083. void delay_ms(uns16 x)
    1084. {
    1085.     //Clocks out at 1006us per 1ms @ 8MHz
    1086.     uns8 y, z;
    1087.     for ( ; x > 0 ; x--)
    1088.         for ( y = 0 ; y < 4 ; y++)
    1089.             for ( z = 0 ; z < 69 ; z++);
    1090. }
    1091.  
    1092.  
    1093. //Reads e_data from the onboard eeprom at e_address
    1094. uns8 onboard_eeread(uns8 e_address)
    1095. {
    1096.     //Do a read
    1097.     EEADR = e_address; //Set the address to read
    1098.     EEPGD = 0; //Point to EEPROM Memory
    1099.     RD = 1; //Read it
    1100.  
    1101.     return(EEDATA); //Read that EEPROM value
    1102. }
    1103.  
    1104.  
    1105. //Write e_data to the onboard eeprom at e_address
    1106. void onboard_eewrite(uns8 e_data, uns8 e_address)
    1107. {
    1108.     bit temp_GIE = GIE;
    1109.  
    1110.     EEIF = 0; //Clear the write completion intr flag
    1111.     EEADR = e_address; //Set the address
    1112.     EEDATA = e_data; //Give it the data
    1113.     EEPGD = 0; //Point to EEPROM data block?
    1114.     WREN = 1; //Enable EE Writes
    1115.     GIE = 0; //Disable Intrs
    1116.  
    1117.     //Specific EEPROM write steps
    1118.     EECON2 = 0x55;
    1119.     EECON2 = 0xAA;
    1120.     WR = 1;
    1121.     //Specific EEPROM write steps
    1122.  
    1123.  
    1124.     while(EEIF == 0); //Wait for write to complete
    1125.     WREN = 0; //Disable EEPROM Writes
    1126.  
    1127.  
    1128.     GIE = temp_GIE; //Set GIE to its original state
    1129. }
    1130.  
     
  13. jpanhalt

    Thread Starter Expert

    Jan 18, 2008
    6,377
    1,219
    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
     
  14. BobaMosfet

    Senior Member

    Jul 1, 2009
    396
    86
    Hi, yes, sorry. I was considering the enforced 1-5ms delay depending on data .v. command mode.
     
  15. MMcLaren

    Distinguished Member

    Feb 14, 2010
    837
    158
    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
     
    jpanhalt likes this.
  16. jpanhalt

    Thread Starter Expert

    Jan 18, 2008
    6,377
    1,219
    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
     
  17. MMcLaren

    Distinguished Member

    Feb 14, 2010
    837
    158
    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: Apr 28, 2018
  18. jpanhalt

    Thread Starter Expert

    Jan 18, 2008
    6,377
    1,219
    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?_fr...04/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
     
  19. jpanhalt

    Thread Starter Expert

    Jan 18, 2008
    6,377
    1,219
    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
     
  20. jpanhalt

    Thread Starter Expert

    Jan 18, 2008
    6,377
    1,219
    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 (ASM):
    1.  
    2. Cmd1           ;bit<7> clear, hex value <0x20
    3.      brw
    4.      bra       Valid          ;0x00    ; Tested 05/07/18 and seems to erase screen w/normal operation*      
    5.      bra       Valid          ;0x01 clear display
    6.      bra       Valid          ;0x02 return home
    7.      bra       _0x03          ;0x03 return home*#      erase line
    8.      bra       Valid          ;0x04 entry mode
    9.      bra       Valid          ;0x05 entry mode
    10.      bra       Valid          ;0x06 entry mode
    11.      bra       Valid          ;0x07 entry mode
    12.      bra       _0x08          ;0x08 display off cursor**#    baud 9600
    13.      bra       _0x09          ;0x09 display off cursor**#    baud 19200
    14.      bra       _0x0A          ;0x0A display off cursor**#    baud 38400
    15.      bra       _0x0B          ;0x0B display off cursor**#    baud 50000
    16. <more code follows>
    17.  
    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.
     
Loading...