News:

Let's find out together what makes a PIC Tick!

Main Menu

PCF8574 I2C Lcd Code Examples

Started by dnaci, Sep 11, 2021, 05:08 PM

Previous topic - Next topic

dnaci

Codes that operate 2-4 line LCDs with PCF8574 module or IC:

Device = 18F46K20
Declare Xtal = 8
Declare All_Digital = true
Declare Watchdog = On
Declare Slow_Bus On
Clear

Include "I2CPrintHardCommand.inc"
TRISA=%11111111
TRISB=%00000000
TRISC=%10011111
TRISD=%00000000
TRISE=%00000011
PORTA=0
PORTB=0
PORTC=0
PORTD=0
PORTE=0
Dim lights As Byte
lights = 0
DelayMS 500 'Short delay to stabilize the controller
Main:
Cls
If lights = 0 Then
Print $FE, 3 'Backlight on
EndIf

DelayMS 500

Print At 1,1, "  PCF8574T DISPLAY  "
DelayMS 5
Print At 2,1, "TEST TEST  TEST TEST"
DelayMS 5
Print At 3,1, "20 x 4 lines display"
DelayMS 5
Print At 4,1, "  PROTON BASIC    "
DelayMS 500

lights = 1
If lights = 1 Then
Print $FE, 4 'Backlight off
EndIf
DelayMS 500
lights = 0
GoTo Main

Config_Start
FOSC = HS ;HS oscillator
FCMEN = On ;Fail-Safe Clock Monitor enabled
IESO = OFF ;Oscillator Switchover mode disabled
PWRT = On ;PWRT enabled
BOREN = SBORDIS ;Brown-out Reset enabled in hardware only (SBOREN is disabled)
BORV = 18 ;VBOR set to 1.8 V nominal
WDTEN = On ;WDT is controlled by SWDTEN bit of the WDTCON register
WDTPS = 8192 ;1:8192 işlemci çekirdeği 8 saniye çalışmazsa sistem resetlenir.
CCP2MX = PORTC ;CCP2 input/output is multiplexed with RC1
PBADEN = OFF ;PORTB<4:0> pins are configured as analog input channels on Reset
LPT1OSC = OFF ;Timer1 configured for higher power operation
HFOFST = OFF ;HFINTOSC starts clocking the CPU without waiting for the oscillator to stablize.
MCLRE = On ;MCLR pin enabled; RE3 input pin disabled
STVREN = On ;Stack full/underflow will cause Reset
LVP = OFF ;Single-Supply ICSP enabled
XINST = OFF ;Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
Debug = OFF ;Background debugger disabled, RB6 and RB7 configured as general purpose I/O pins
Cp0 = OFF ;Block 0 (000800-003FFFh) not code-protected
CP1 = OFF ;Block 1 (004000-007FFFh) not code-protected
CP2 = OFF ;Block 2 (008000-00BFFFh) not code-protected
CP3 = OFF ;Block 3 (00C000-00FFFFh) not code-protected
CPB = OFF ;Boot block (000000-0007FFh) not code-protected
CPD = OFF ;Data EEPROM not code-protected
WRT0 = OFF ;Block 0 (000800-003FFFh) not write-protected
WRT1 = OFF ;Block 1 (004000-007FFFh) not write-protected
WRT2 = OFF ;Block 2 (008000-00BFFFh) not write-protected
WRT3 = OFF ;Block 3 (00C000h-00FFFFh) not write-protected
WRTC = OFF ;Configuration registers (300000-3000FFh) not write-protected
WRTB = OFF ;Boot Block (000000-0007FFh) not write-protected
WRTD = OFF ;Data EEPROM not write-protected
EBTR0 = OFF ;Block 0 (000800-003FFFh) not protected from table reads executed in other blocks
EBTR1 = OFF ;Block 1 (004000-007FFFh) not protected from table reads executed in other blocks
EBTR2 = OFF ;Block 2 (008000-00BFFFh) not protected from table reads executed in other blocks
EBTR3 = OFF ;Block 3 (00C000-00FFFFh) not protected from table reads executed in other blocks
EBTRB = OFF ;Boot Block (000000-0007FFh) not protected from table reads executed in other blocks
Config_End

;**** End of Fuse Configurator Settings ****
;-------------------------------------------------------------------------------
End

I2CPrintHardCommand.inc files:

#Disable Print                          ;Disable the Proton Print command   

  ;***************************************************************************************************************
  ; Configure items to suit your LCD and I2C hardware, I2CPrint_DataOnHighBits remmed if data is on low bits    *
  ;                                                                                                              *
  Symbol I2CPrint_address        = %01001110  ' PCF8574 Slave Address 0,1,1,1,A2,A1,A0,RW  where W=0          *
  Symbol I2CPrint_InitForLCDLines = $28        ' $28 for two lines or 4 lines                                              *
  Symbol I2CPrint_RS              = %00000001  '                                                                *
  Symbol I2CPrint_RW              = %00000010  '                                                                *
  Symbol I2CPrint_E              = %00000100  '                                                                *
  Symbol I2CPrint_BackLight      = %00001000  '                                                                *
  $define I2CPrint_DataOnHighBits              '                                                                *
  ;***************************************************************************************************************
 
  Symbol I2CPrint_RS_E          = I2CPrint_RS | I2CPrint_E
 
  $ifdef I2CPrint_DataOnHighBits
    Symbol I2CPrint_Init1          = %00110000
    Symbol I2CPrint_Init1_E        = %00110000 | I2CPrint_E
    Symbol I2CPrint_Init2          = %00110000
    Symbol I2CPrint_Init2_E        = %00110000 | I2CPrint_E
    Symbol I2CPrint_Init3          = %00100000
    Symbol I2CPrint_Init3_E        = %00100000 | I2CPrint_E
  $else
    Symbol I2CPrint_Init1          = %00000011
    Symbol I2CPrint_Init1_E        = %00000011 | I2CPrint_E
    Symbol I2CPrint_Init2          = %00000011
    Symbol I2CPrint_Init2_E        = %00000011 | I2CPrint_E
    Symbol I2CPrint_Init3          = %00000010
    Symbol I2CPrint_Init3_E        = %00000010 | I2CPrint_E
  $endif

  Dim BPF As Byte System
  Symbol I2CPrint_tComOrData = BPF.0
  Symbol I2CPrint_tInitialised = BPF.1

  Dim I2CPrint_PrintStore As Byte
  Dim I2CPrint_PrintData As Byte
  Dim I2CPrint_HighData As Byte
  Dim I2CPrint_LowData As Byte
  Dim I2CPrint_dummy As Byte
  Dim I2CPrint_RSFlag As Byte
  Dim Backlight_State As Byte

  GoTo I2CPrintCommand_End

  @Print                                              ; assembler Print redirect
  Wreg_Byte I2CPrint_PrintStore                      ; save the W Register

  If I2CPrint_tInitialised = 0 Then                  ; Has LCD been inititalised?
    DelayMS 100                                      ; Power-on delay for slow display types
    HBusOut I2CPrint_address, [I2CPrint_Init1_E]      ; E hi and send command nibble 0011 (3)   
    HBusOut I2CPrint_address, [I2CPrint_Init1]        ; E lo   
    DelayMS 20
    HBusOut I2CPrint_address, [I2CPrint_Init2_E]      ; E hi and send command nibble 0011 (3)   
    HBusOut I2CPrint_address, [I2CPrint_Init2]        ; E lo   
    DelayMS 20
    HBusOut I2CPrint_address, [I2CPrint_Init3_E]      ; E hi and send command nibble 0010 (2)   
    HBusOut I2CPrint_address, [I2CPrint_Init3]        ; E lo   
    DelayMS 20
    I2CPrint_PrintData = I2CPrint_InitForLCDLines    ; 4 bit mode and n lines
    GoSub I2CPrint_SendControl
    I2CPrint_PrintData = $0C                          ; enable display, cursor and blink off
    GoSub I2CPrint_SendControl
    I2CPrint_PrintData = $06                          ; move cursor after each write
    GoSub I2CPrint_SendControl                   
    I2CPrint_PrintData = $80                          ; goto column 1 line 1
    GoSub I2CPrint_SendControl
    Set I2CPrint_tInitialised                        ; Indicate that the LCD is initialised
  EndIf

  I2CPrint_PrintData = I2CPrint_PrintStore
  If I2CPrint_PrintData = 0xFE Then                  ; it is a command header
    I2CPrint_RSFlag = 0                              ; clear a flag so we know the next byte will be a command
  Else
    If I2CPrint_RSFlag = 0 Then                      ; this is a command byte
      If I2CPrint_PrintData = 03 Then
        Backlight_State = I2CPrint_BackLight
      ElseIf I2CPrint_PrintData = 04 Then
        Backlight_State = 0
      Else
        GoSub I2CPrint_SendControl
      End If
    Else
      GoSub I2CPrint_SendData
    EndIf
    I2CPrint_RSFlag = 1                              ; set the flag so the next byte will be send as Data
  EndIf                                   
  Byte_Wreg I2CPrint_PrintStore                      ; restore the W register
  Return

' ****************************************************************************
' **                            S U B R O U T I N E S                      **
' ****************************************************************************

I2CPrint_SendData:
  $ifdef I2CPrint_DataOnHighBits
    I2CPrint_HighData = I2CPrint_PrintData & %11110000            ; mask LSBs
    I2CPrint_LowData = I2CPrint_PrintData << 4                    ; shift left
  $else
    I2CPrint_LowData = I2CPrint_PrintData & %00001111            ; mask MSBs
    I2CPrint_HighData = I2CPrint_PrintData >> 4                  ; shift Right
  $endif

  I2CPrint_dummy = I2CPrint_RS_E | I2CPrint_HighData              ; data and e hi
  I2CPrint_dummy = I2CPrint_dummy | Backlight_State              ; data and e hi and bls
  HBusOut I2CPrint_address, [I2CPrint_dummy]           
  I2CPrint_dummy = I2CPrint_RS | I2CPrint_HighData                ; data and e lo
  I2CPrint_dummy = I2CPrint_dummy | Backlight_State              ; data and e lo and bls
  HBusOut I2CPrint_address, [I2CPrint_dummy]             
  I2CPrint_dummy = I2CPrint_RS | Backlight_State                  ; data and e hi
  HBusOut I2CPrint_address, [I2CPrint_dummy]                      ; Delay, RS on throughout
  I2CPrint_dummy = I2CPrint_RS_E | I2CPrint_LowData              ; data and e hi
  I2CPrint_dummy = I2CPrint_dummy | Backlight_State              ; data and e hi and bls
  HBusOut I2CPrint_address, [I2CPrint_dummy]             
  I2CPrint_dummy = I2CPrint_RS | I2CPrint_LowData                ; data and e lo
  I2CPrint_dummy = I2CPrint_dummy | Backlight_State              ; data and e lo and bls
  HBusOut I2CPrint_address, [I2CPrint_dummy]
  Return
 
I2CPrint_SendControl:
  $ifdef I2CPrint_DataOnHighBits
    I2CPrint_HighData = I2CPrint_PrintData & %11110000            ; mask LSBs
    I2CPrint_LowData = I2CPrint_PrintData << 4                    ; shift left
  $else
    I2CPrint_LowData = I2CPrint_PrintData & %00001111            ; mask MSBs
    I2CPrint_HighData = I2CPrint_PrintData >> 4                  ; shift Right
  $endif

  I2CPrint_dummy = I2CPrint_E | I2CPrint_HighData                ; data and e hi
  I2CPrint_dummy = I2CPrint_dummy | Backlight_State              ; data and e hi and bls
  HBusOut I2CPrint_address, [I2CPrint_dummy]             
  I2CPrint_dummy = I2CPrint_HighData | Backlight_State            ; data and e lo and bls
  HBusOut I2CPrint_address, [I2CPrint_dummy]             
  I2CPrint_dummy = Backlight_State                                ;
  HBusOut I2CPrint_address, [I2CPrint_dummy]                      ; Delay
  I2CPrint_dummy = I2CPrint_E | I2CPrint_LowData                  ; data and e hi
  I2CPrint_dummy = I2CPrint_dummy | Backlight_State              ; data and e hi and bls
  HBusOut I2CPrint_address, [I2CPrint_dummy]             
  I2CPrint_dummy = I2CPrint_LowData | Backlight_State            ; data and e lo and bls
  HBusOut I2CPrint_address, [I2CPrint_dummy]       
  Return

I2CPrintCommand_End:

John Drew

Hello Dnaci,
It's worth putting on the WIKI so it will be easy to find in future.
Regards
John

See_Mos

I am using compiler version 4.0.0.7 and compiling the code causes several error 113.

See post 5 of this thread for the fix

https://protoncompiler.com/index.php/topic,53.msg193.html#msg193

Thanks Bob.

chris_cb_uk

Great post.  I've just tried this and updated the Hbusout to i2cout commands to get over a "legacy" warning
2 Questions for this. 

1st question
In the include file how / where do I adjust the widths of the screen. Issue currently is I'm using a 4x16 screen.  The first 2 lines show fine, but line 3 and 4 are both offset by 4 blocks.  I feel there is something shifting too far?
2nd question Would it be possible to add more screens running on i2c and be able to address them in program for example print1 at 1,1,"Screen 1" and then print2 at 1,1,"screen 2"? I am imagining just setting another @__print_ section with the IC2 address as the 2nd screen but how do these kinds of commands "Print" get treated within the inner workings?
Thanks.

Stephen Moss

Quote from: chris_cb_uk on Nov 01, 2021, 03:36 PM2nd question Would it be possible to add more screens running on i2c and be able to address them in program for example print1 at 1,1,"Screen 1" and then print2 at 1,1,"screen 2"? I am imagining just setting another @__print_ section with the IC2 address as the 2nd screen but how do these kinds of commands "Print" get treated within the inner workings?
dnaci can probably answer this better than me but looking at the code for the include file you will see...
  Symbol I2CPrint_address        = %01001110  ' PCF8574 Slave Address 0,1,1,1,A2,A1,A0,RW  where W=0  If you attach each screen to an separate 8574 each with a unique address I would think it should just be a matter of changing the code from symbol (a fixed value) to DIM I2CPrint_Address as Byte, create a symbol for each display, i.e
Display_1 = %01001110
Display_2 = %01101110 and so on for the appropriate addresses.
Then when you want to send data to a specific display your use
I2CPrint_address = Display_2 to send the data to display 2 or use
I2CPrint_address = Display_1 to send the data to display 1 and so on

chris_cb_uk

Thanks Stephen, I had thought about doing similar while in the shower this morning.  It's my place of thought!
I could certainly see this working. 

Dompie

Changing the software address of the I2C is indeed not that difficult, but you must also be able to give the hardware of the module the same I2C address and that is often not possible or at most for one other address. I myself solved this problem with an I2C-Mux TCA9548A between the PIC and the I2C-LCDs. You do not have to change hardware addresses and your PIC uses one hardware address for all the LCDs, only you switch the outputs of the I2C-MUX to the correct I2C-LCD.

Johan

Edwin

Has anybody compiled this with version 4.0.1.7 because by me it wont work.
Asm 113 error __print not defined.

with old version 3 it works

Thanks

top204

Change the:

@Print

to

__Print_:

The new assembler for the latest devices did not like the "@" character, so I changed the library subroutines names quite a few years ago. And at the same time, I changed the Print command's library subroutine name, so it did not have to be preceded with the @ character, and could be issued in the BASIC code as a BASIC label name.

keytapper

I'd suggest for the new versions to add a changelog. But I'm estimating just little percentage of readers.
Ignorance comes with a cost