BS2p driving I2C display problem

2»

Comments

  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2017-02-14 - 23:04:02
    Notice for characters it is sending the data out to LCDD (LCD Data Register) whereas commands are being sent out to LCDI (LCD Instruction Register). This is using 8-bit parallel mode on a Hitachi display, however the point is how the main code handles things, which I forgot to include. If you notice, the HL register pair is the address reference, so before I call PRINT I do something like this:
    KNIGHT	LD HL,KDATA
    	CALL PRINT
    	RET
    KDATA	.BYTE $0C,"PROGRAMMING COPYRIGHT 1994 CHRIS SAVAGE",$07,$00
    

    KDATA is a label and .byte is the equivalent of the BS2 DATA statement. The $00 is a null terminator. The $0C clears the screen, while the $07 beeps a piezo speaker. When I call (GOSUB) to KNIGHT, it loads the HL register pair with the address of the data at KDATA and then starts printing one character at a time until it hits the $00.

    You can make a subroutine to handle all this in PBASIC so that for a string you need only pass the address of the DATA block to it and the subroutine will send all the characters until it hits the NULL (0). With planning it could handle special characters as my routine did.
    Chris Savage | Engineering Tech | Main Office: (916) 624-8333 | Direct to Tech Support: (888) 997-8267 | Website | Twitter | Google+
  • kwinn wrote: »
    john_s wrote: »
    One more thing - will I be able to send "Hello world" as a single string when using I2COUT or do I have to brake it and send char after character instead?

    You can send a single string like "Hello world" using the string formatter. See page 232 of the "BASIC Stamp Syntax and Reference Manual Version 2.2"

    Actually in this case that won't work. If you create a subroutine to handle to individual writes, you can also create a string subroutine to take a zero-delimited string and send it one character at a time calling the data subroutine.

    Ah yes, forgot that this was going through the I2C to parallel interface to get to the LCD.
    Thanks for catching my mistake.
    In science there is no authority. There is only experiment.
    Life is unpredictable. Eat dessert first.
  • Here is a sample code I cobbled together that puts the I2C transfer operation into a subroutine. This code is by no means complete but you should be able to utilize some of it for your project. The compiler says the syntax is good, however, there may be a flaw in the logic which may crop up when uploading to your stamp. You may have to add some delay after the RETURN if the datasheet for the LCD requires it, such as after a clear display operation.
    ' PCF8574 I2C LCD test
    ' {$STAMP BS2p}
    ' {$PBASIC 2.5}
    
    sda     PIN 0
    slave   CON $27  ' or whatever your device address is
    
    char    VAR Byte  ' holds what you want to send
    upper   VAR char.HIGHNIB
    lower   VAR char.LOWNIB
    buffer  VAR Byte
    val     VAR buffer.HIGHNIB
    brs     VAR buffer.BIT0
    brw     VAR buffer.BIT1
    bcs     VAR buffer.BIT2
    bbl     VAR buffer.BIT3
    rs      VAR Bit   ' you set these three
    rw      VAR Bit   '
    bl      VAR Bit   '
    
    ' Your main program will need to place into variable 'char' the byte
    ' you want to transfer to the LCD. You also need to set up 3 additional
    ' bit variables as follows:
    
    ' rs = 0 for an instruction  ( clear the display, cursor movement, etc)
    ' rs = 1 for data transfer (stuff to display)
    
    ' rw = 0 for writing to LCD
    ' rw = 1 for reading from LCD
    
    ' bl = 0 for backlight OFF
    ' bl = 1 for backlight ON
    
    'Once these four variables have been set up then call the transfer
    ' subroutine to transfer the byte to the LCD. The subroutine will
    ' return to the following instruction after the call.
    
    main:
    ' do your thing here like the following...
    
      char = $31
      rs = 1
      rw = 0
      bl = 1
      GOSUB send2
    '
    ' do more of your thing here
    '
    '
      GOTO main
    
    send2:
      val = upper
      brs = rs
      brw = rw
      bcs = 0     ' number 0
      bbl = bl
      I2COUT sda, slave, [buffer]
      bcs = 1
      I2COUT sda, slave, [buffer]
      val = lower
      bcs = 0
      I2COUT sda, slave, [buffer]
      bcs = 1
      I2COUT sda, slave, [buffer]    ' LCD now has received the value you placed in 'char'
      RETURN
    
    
    Florida, between St. Petersburg and the Gulf of Mexico

    Do not look directly into laser with remaining good eye...
  • (Sorry for bad english)

    I find a regal one BS2px and I will use with TWI LCD (PCF8574T and 20*4 LCD).
    I have read all, what write here, but do't work. The I2C LCD with Arduino work fine.
    The address for PCF it set to $20.
    Can someone modify this prog for me to working?

    ' {$STAMP BS2px}
    ' {$PBASIC 2.5}
    
    
    #IF ($STAMP < BS2P) #THEN
      #ERROR "Program requires BS2p, BS2pe, or BS2px."
    #ENDIF
    
    SDA  PIN  8    ' SDA DATA line is connected TO BS2P PIN8; pullup 1k
    SCL  PIN  9    ' SCL clock line is connected to BS2p PIN9; pullup 1k
    
    addr CON  $20  ' unique SlaveID address; 0x27 for PCF8574T, 0x37 for PCF8574AT
    
    ' HD44780 control characters
    ' use Shift Left operator (<<) when in 4-bit mode
    '
    ClrLCD  CON     $01  ' clear display
    Cur_Hm  CON     $02  ' move cursor to Home position
    Set_8b  CON     $03  ' set 8-bit mode
    Cur_Lf  CON     $10  ' move cursor left
    Cur_Rt  CON     $14  ' move cursor right
    DispLf  CON     $18  ' shift displayed chars left
    DispRt  CON     $1C  ' shift displayed chars right
    DDRam   CON     $80  ' Display Data RAM control
    
    ' --- [Main] ---
    
    PAUSE 1000
    
    
    I2COUT SDA, addr, [$03<<4] ' set mode
    PAUSE 100
    I2COUT SDA, addr, [$03<<4] ' set mode
    PAUSE 100
    I2COUT SDA, addr, [$03<<4] ' set mode
    PAUSE 100
    
    I2COUT SDA, addr, [$02<<4] ' initial 4-bit interface
    PAUSE 100
    
    I2COUT SDA, addr, [$02<<4] ' 'Function Set'
    I2COUT SDA, addr, [$10<<4] ' I=1, N=1 (2-line display), F=0 (5x8 font)
    PAUSE 100
    
    I2COUT SDA, addr, [$00]    ' ...no delay needed between sending 2 groups
    I2COUT SDA, addr, [$08<<4] ' display ON/OFF control; D=0, C=0, B=0
    PAUSE 100
    
    I2COUT SDA, addr, [$00]    ' ...no delay between sending 2 groups
    I2COUT SDA, addr, [$01<<4] ' Clear Display
    PAUSE 100
    
    I2COUT SDA, addr, [$00]    ' ...no delay between sending 2 groups
    I2COUT SDA, addr, [$06<<4] ' Entry Mode Set (I/D=1, S=1)
    'I2COUT SDA, addr, [0 1 I/D S] ' Entry Mode Set (I/D and S as required)
    PAUSE 100
    
    ' *** Initialization END ****
    
    I2COUT SDA, addr, [$00]    ' ...no delay between sending 2 groups
    I2COUT SDA, addr, [$FF<<4] ' display ON, cursor ON and blinking, D=1, C=1, B=1
    PAUSE 100
    
    I2COUT SDA, addr, ["Test"]
    
    Even a heavy burden is light if lifted by many.
  • Hal AlbachHal Albach Posts: 747
    edited 2018-03-05 - 00:57:16
    For a Basic Stamp the address of the PCF8574 is an 8 bit address, $40. Arduino uses a 7 bit address, requiring $40 to be shifted right once for 0x20. Is this PCF8574 interface of your making or did you get it from Amazon or eBay? The ones you normally buy usually have pins 1, 2, & 3 of the PCF8574 tied to VCC or +5, which would make the address $4E.
    Also, please look over this thread and try to understand that once the LCD has been placed in 4-bit mode, each character you send to the LCD has to be sent in two parts, the upper nibble (4 bits) and the lower nibble. AND each nibble has to be sent twice - the first time with E low and the second time with E high - in other words toggling the E bit. So, each 8 bit character you wish to send to the LCD has to be sent in four transmissions over the I2C bus. The first four init characters are sent in 8-bit mode, but the LCD ignores the upper nibble. Each of the first four characters has to be sent twice to toggle the E bit.

    BTW, I have a BS2px I2C - LCD demo program I put together about a year ago, if you want it I will post it here. It displays three lines of text on a 4x20 LCD and on the 4th line it counts up to 65, 535.
    Florida, between St. Petersburg and the Gulf of Mexico

    Do not look directly into laser with remaining good eye...
  • Hal Albach wrote: »
    BTW, I have a BS2px I2C - LCD demo program I put together about a year ago, if you want it I will post it here. It displays three lines of text on a 4x20 LCD and on the 4th line it counts up to 65, 535.

    Thanks for You answer.
    If can You post Yours demo prog, It would be great, I would learn from it.
    Even a heavy burden is light if lifted by many.
  • Hal AlbachHal Albach Posts: 747
    edited 2018-03-05 - 17:21:20
    Here you go, make sure the connections are the same as indicated in the comments at the top.
    ' PCF8574 I2C LCD Demo with 32x2 LCD 4-bit
    ' {$STAMP BS2px}
    ' {$PBASIC 2.5}
    
    ' A small demonstration on using an I2c interfaced LCD with a BS2px
    '  series Basic Stamp.
    '  This is for a 20x4 LCD using an interface which uses a PCF8574 I2C
    '  Port expander chip and has A0, A1, & A2 tied to Vcc for a device
    '  address of $4E. If your interface has the PCF8574A chip then the
    '  address would be $7E. PBasic uses 8-bit addresses but it still
    '  controls the RW bit, bit 0. These 20x4 display modules have a
    '  backlight controlled by the bl bit defined below. Seems to work
    '  best by setting bl hi and then lo in the I2C_Send function for
    '  lowered intensity. Setting bl hi both times will yield a very bright
    '  backlight, drawing an additional 4 mA from the power source.
    
    ' Your main program will need to place into variable 'char' the byte
    '  you want to transfer to the LCD. You also need to set up 2 additional
    '  bit variables as follows:
    
    ' rs = 0 for an instruction
    ' rs = 1 for data transfer
    
    ' rw = 0 for writing to LCD, usually always 0
    ' rw = 1 for reading from LCD
    
    ' Once these two variables have been set up then call the transfer
    '  subroutine "send_byte" to transfer the byte to the LCD and takes care
    '  of sending the correct nibbles and toggling the E bit.
    
    '                     CONNECTIONS:
    '   BS2Px Board        PCF8574T            LCD
    '       +5 - - - - -  1, 2, 3, 16  - - - - 2
    '       Gnd  - - - - - - - 8 - - - - - - - 1
    '       P0 - - - SDA - -  15
    '       P1 - - - SCL - -  14
    '                          4  - -  E  - -  6
    '                          5  - - RS  - -  4
    '                          6  - - RW  - -  5
    '                          7  - - BL  - -  BL driver
    '                          9  - - DB4 - -  11
    '                         10  - - DB5 - -  12
    '                         11  - - DB6 - -  13
    '                         12  - - DB7 - -  14
    
    
    ' NOTE!
    ' At init time the E, RS, & R/W signals are established LOW per datasheet with
    '   an initial I2C transfer of $00 to the PCF8574.  With E low changes can be
    '   made to the RS, R/W, and data lines.  Then the E bit is set to HI and the
    '   entire byte is sent to the PCF8574, then E is brought LOW and the byte is
    '   again sent to the PCF8574. This procedure "toggles" the E bit.
    '   Instead of shifting nibbles around I took advantage of a PBasic feature that
    '   lets me re-define bits and nibbles of variables.
    
    ' DELAYS:
    ' The first 4 init operations use two I2C operations, all other data transfers
    '  to the LCD uses 4 I2COUT operations per character which slows things down to
    '  where operational delays required for Clear Screen or Home cursor are not
    '  needed.
    
    ' Let the show begin...
    '-------------------------------------------------------------------------------
    
    DATA $30, $30, $30, $20, $28, $08, $01, $06, $0C, 0  ' init sequence opcodes
    
    Msg1 DATA " BS2px-->I2C-->LCD  ", 0
    Msg2 DATA "   Demonstration    ", 0
    Msg3 DATA " Counting to 65,535 ", 0
    Msg4 DATA "                    ", 0
    
    SDA     PIN 0                    ' can be P0, P1, P8, or P9
    LCD     CON $4E                  ' For PCF8574T, (pins 1, 2, & 3 tied to +5)
    hi      CON 1
    lo      CON 0
    dat     CON 1                    ' RS data mode
    cmd     CON 0                    ' RS command mode
    
    '     LCD Line addresses
    L1      CON $80                  ' LCD top line
    L2      CON $C0                  ' LCD line 2
    L3      CON $94                  ' LCD line 3
    L4      CON $D4                  ' LCD line 4
    L4C     CON L4 + 7               ' location of count on display
    
    EndCnt  CON $FFFF                ' that's 65535
    TenK    CON 10000                ' factor Digit 5
    OneK    CON 1000                 ' factor Digit 4
    OneH    CON 100                  ' factor Digit 3
    Ten     CON 10                   ' factor Digit 2
    
    char    VAR Byte                 ' byte to send over bus
    upper   VAR char.HIGHNIB         ' upper nibble of char
    lower   VAR char.LOWNIB          ' lower nibble of char
    buff    VAR Byte                 ' sent to the PCF8574
    val     VAR buff.HIGHNIB         ' upper nibble of buff
    rs      VAR buff.BIT0            ' 0 = command, 1 = data
    rw      VAR buff.BIT1            ' 0 = write to LCD, 1 = read from LCD
    E       VAR buff.BIT2            ' handled by I2C_send subroutine
    bl      VAR buff.BIT3            ' backlight, 1 = on
    strAddr VAR Byte                 ' EEPROM string char address
    idx     VAR Byte                 ' FOR...NEXT index
    cnt     VAR Word                 ' counter
    temp    VAR Word                 ' for cacalations
    factor  VAR Word                 '  "      "
    
    '-------------------------------------------------------------------------------
    
    MAIN:
    GOSUB LCDINIT                    ' Sort of speaks for itself, no?
    GOSUB String_Out                 ' display EEPROM strings
    GOSUB counting                   ' do the count on line 4
    
    END                              ' Go to sleep, little Stamp
    
    '--------------------- send upper then lower nibble ----------------------------
    
    send_byte:
      val = upper                    ' val is buff high, upper is char high
      GOSUB I2C_send                 ' send buff down the line to PCF8574
      val = lower                    ' lower is the low nibble of char
      GOSUB I2C_send                 ' send buff again to PCF8574
    
    RETURN                           ' go back to where you came from
    
    '-------------------- toggle the E and bl line ---------------------------------
    
     ' NOTE!   RS, RW, & data must be properly set up before calling this sub
    
    I2C_send:                        ' send buff to PCF8574 & toggles E
      E = hi
      bl = hi                        ' backlight on
      I2COUT SDA, LCD, [buff]        ' now raise the E line
      E = lo
      bl = lo                        ' backlight off for half intensity
      I2COUT SDA, LCD, [buff]        ' now E is back to lo
    
    RETURN
    
    '------------------ Display stored strings -------------------------------------
    
     String_Out:
      strAddr = Msg1                 ' point to message 1
      char = L1                      ' top line
      GOSUB send_msg                 ' display first msg
    
      strAddr = Msg2                 ' point to message 2
      char = L2                      ' 2nd line
      GOSUB send_msg                 ' display 2nd msg
    
      strAddr = Msg3                 ' point to message 3
      char = L3                      ' 3rd line
      GOSUB send_msg                 ' display 3rd msg
    
      strAddr = Msg4                 ' point to message 4
      char = L4                      ' 4th line
      GOSUB send_msg                 ' erase 4th line
    
    RETURN
    
    '------------------ send out selected string -----------------------------------
    
    '  pointers to msg to send and to which line was set up in sub String_Out:
    
    send_msg:
      rs = cmd                       ' set RS to command
      GOSUB send_byte                ' position cursor at selected location
      rs = dat                       ' set RS to DATA
      DO
        READ strAddr, char           ' read next byte from string in EEPROM
        strAddr = strAddr + hi       ' increment string pointer
        IF char = lo THEN EXIT       ' if done with this string, EXIT
        GOSUB send_byte              ' NO! Display char on LCD
      LOOP                           ' repeat the do-While loop
    
    RETURN
    
    '------------------ count and display counter ----------------------------------
    
    counting:
    
    FOR cnt = hi TO EndCnt           ' a massive undertaking
     rs = cmd                        ' set RS to command
     char = L4C                      ' position cursor at line 4 col 7
     GOSUB send_byte                 ' execute the command
     rs = dat                        ' set RS to DATA
     temp = cnt                      ' temp will be our work space & preserves cnt
     factor = TenK                   ' start with leftmost digit
      DO                             ' start a 'do' loop
       IF cnt < factor THEN          ' suppress leading 0 at factor location
        char = " "                                 ' send a space instead of "0"
       ELSEIF factor = OneH AND cnt >= OneK THEN   ' do we need a comma?
        char = ","                                 ' place a comma between hundreds and thousands
        GOSUB send_byte              ' and send it to the LCD
        GOTO Skip_one                ' skip the next statement
       ELSE
    Skip_one:
       char = temp / factor + "0"    ' get digit and ASCII-fy
       ENDIF
      GOSUB send_byte                ' and display it
      temp = temp // factor          ' modulus division to remove msd
      factor = factor / Ten          ' point to next lower digit
      LOOP UNTIL factor = lo         ' our end condition
     NEXT
    
    RETURN
    
    '-------------------- Initialize the display -----------------------------------
    
    LCDINIT:
      PAUSE(15)                      ' power up pause
      buff = $00                     ' set all LCD pins low
      I2COUT SDA, LCD, [buff]        ' ensure RS, RW, E & BL are now low
    
    ' 44780 starts in 8 bit mode but ignores hi nibble for first 4 init instructions
    ' We now pull bytes from the first DATA stream defined at the beginning and
    '  send them to the LCD for initialization.
      FOR idx = 0 TO 3               ' 1st 4 ops are in 8-bit mode
        READ idx, char               ' fetch init byte from list in EEPROM
        val = upper                  ' val = buff high nib , upper = char high nib
        GOSUB I2C_send               ' transfer buff to LCD
      NEXT                           ' do this four times
    
    ' Now in 4 bit mode, finish init operation
    
      FOR idx = 4 TO 8               ' five more init steps
        READ idx, char               ' fetch next init byte from list in EEPROM
        GOSUB send_byte              ' transfer cmd to LCD
      NEXT                           ' do this five times
    
    RETURN                           ' init is finished
    
    '-------------------------------------------------------------------------------
    
    Florida, between St. Petersburg and the Gulf of Mexico

    Do not look directly into laser with remaining good eye...
  • Hal, Thanks, the prog, now working fine. If it's no problem, I will use the I2C part in my program.
    Even a heavy burden is light if lifted by many.
  • You're welcome, hopefully it will give you an insight on how to use the I2C interface between the Stamp and the LCD.
    Florida, between St. Petersburg and the Gulf of Mexico

    Do not look directly into laser with remaining good eye...
  • Interesting.Only with PCF8574 was a problem.
    With PCF 8583, PCF 8591 and 24C02 can I do all.
    Thanks again!
    Even a heavy burden is light if lifted by many.
Sign In or Register to comment.