I set up the Black Pill to drive a 1602 LCD with a PC8574T I2C backpack board.

I wrote a little MicroPython code for hardware I2C and... it didn't work. I beat my head against that wall for a long time. In the end, as far as I can find out from lots of online searching and trying a long series of code variations, it seems that the library for that is currently broken. They're working on it. Great.

EDIT: Nope. I was doing it wrong. After many hours of searching and futzing around I finally found enough info to get hardware I2C working. I was pretty close, but close doesn't count in programming. Rejigged my code to use it and it works perfect. And it's WAY faster than my bitbang code, though I'm sure that could be sped up some. Scroll down past the bitbang source to find my hardware I2C source.

In this photo I'm supplying separate 5V power to the LCD, rather than running it off the USB 5V as in the lower photo.

So I found an old C bitbang I2C source file that I'd done some years ago and ported it over. Not too terribly difficult. Had some weird bugs that took forever to find, but fixed all those and it works fine.

This pic shows the LCD running off the 5V from USB. It works ok, but if you run much else off the USB the Black Pill processor will brown out. Found that out the hard way with other hardware - constant crashes and errors until I supplied separate power for external stuff.

Bitbang I2C source code. Nothin fancy - just a simple demo. (Scroll way down for hardware I2C source)
# ghmicro.com
# 1602 LCD w/PC8574T I2C backpack - bitbang I2c

from pyb import Pin
import math
import time

CLK = Pin('B6',Pin.OUT)     #set low when output
DIO = Pin('B7',Pin.OUT)
CLK.low()
DIO.low()
CLK = Pin('B6',Pin.IN)      #idle high when input
DIO = Pin('B7',Pin.IN)
backlight = 0x08            # backlight on
tbuff = [1,2,3,4]
buff = [1,2,3,4]

#I2C functions
#*************
def start():                    #claim the bus
    DIO = Pin('B7',Pin.OUT)     #dio low
    time.sleep_us(10)
    CLK = Pin('B6',Pin.OUT)     #clk low
    time.sleep_us(10)

def restart():
    DIO = Pin('B7',Pin.IN)      #release data
    time.sleep_us(10)
    CLK = Pin('B6',Pin.IN)      #clk high
    time.sleep_us(10)
    DIO = Pin('B7',Pin.OUT)     #dio low
    time.sleep_us(10)
    CLK = Pin('B6',Pin.OUT)     #clk low
    time.sleep_us(10)

def stop():                     #release the bus
    DIO = Pin('B7',Pin.OUT)     #dio low
    time.sleep_us(10)
    CLK = Pin('B6',Pin.IN)      #clk high
    time.sleep_us(10)
    DIO = Pin('B7',Pin.IN)      #dio high
    time.sleep_us(10)

def i2c_clock():                #cycle clock to latch a bit
    CLK = Pin('B6',Pin.IN)      #clock high
    time.sleep_us(10)           #wait a bit
    CLK = Pin('B6',Pin.OUT)     #clock low
    time.sleep_us(10)

def byteout(dat):                    #clock out byte
    time.sleep_us(10)
    for x in range(8):
        DIO = Pin('B7',Pin.OUT)      #set dio low
        if dat & 0x80:               #but if output bit is high
            DIO = Pin('B7',Pin.IN)   #then set dio high
        i2c_clock()                  #clock it out
        dat = dat << 1               #shift next bit into position

    DIO = Pin('B7',Pin.IN)           #set dio to be input
    DIO.low()                        #clear data bit
    CLK = Pin('B6',Pin.IN)           #clock high
    time.sleep_us(5)                 #wait half a clock pulse
    if DIO == True:                  #check the data bit
        return 1                     #nack error
    time.sleep_us(5)                 #ack good, wait other half of clock pulse
    CLK = Pin('B6',Pin.OUT)          #set clock low
    time.sleep_us(10)
    DIO = Pin('B7',Pin.IN)           #set dio high
    return 0

# LCD functions
#**************
def lcd_string(buff):
    pointer = 0
    for ch in buff:
        lcd_char(buff[pointer])
        pointer += 1

def lcd_cmd(letter):
    temp = letter
    temp = temp & 0xf0          #clear lower nybble
    temp = temp | 0b00000100    #set E, RW = write, RS = command
    temp = temp | backlight
    byteout(temp)               #send upper nybble
    temp = temp & 0b11110000    #clear E to latch data and resend
    temp = temp | backlight
    byteout(temp)

    temp = letter
    temp = temp & 0x0f          #clear upper nybble
    temp = temp << 4            #shift left 4 bits
    temp = temp | 0b00000100    #send lower nybble
    temp = temp | backlight
    byteout(temp)
    temp = temp & 0b11110000
    temp = temp | backlight
    byteout(temp)

def lcd_char(letter):
    temp = ord(letter)
    temp = temp & 0xf0          #clear lower nybble
    temp = temp | 0b00000101    #set E, RW = write, RS = data
    temp = temp | backlight
    byteout(temp)               #send upper nybble
    temp = temp & 0b11110001    #clear E to latch data and resend
    temp = temp | backlight
    byteout(temp)

    temp = ord(letter)
    temp = temp & 0x0f          #clear upper nybble
    temp = temp << 4            #shift left 4 bits
    temp = temp | 0b00000101    #send lower nybble
    temp = temp | backlight
    byteout(temp)
    temp = temp & 0b11110001
    temp = temp | backlight
    byteout(temp)

def lcd_nybble(nyb):
    nyb = nyb << 4
    nyb = nyb | 0b00001100
    byteout(nyb)
    nyb = nyb & 0b11111000
    byteout(nyb)

def lcd_init():
    start()
    byteout(0x4e)       #send lcd slave address & write bit
    pyb.delay(50)
    lcd_nybble(0x03)
    pyb.delay(5)
    lcd_nybble(0x03)
    pyb.delay(160)
    lcd_nybble(0x03)
    pyb.delay(160)
    lcd_nybble(0x02)
    pyb.delay(50)
    lcd_cmd(0x28)       #set 4-bit mode & 2 lines
    pyb.delay(50)
    lcd_cmd(0x10)       #cursor move & shift left
    pyb.delay(50)
    lcd_cmd(0x06)       #entry mode = increment
    pyb.delay(50)
    lcd_cmd(0b00001100) #display on - cursor & blink off
    pyb.delay(50)
    lcd_cmd(0x01)       #clear display
    stop()
    pyb.delay(50)

#------------------------------------------------------
# Begin here
#------------------------------------------------------

lcd_init()

start()
byteout(0x4e)            #send lcd slave address & write bit
lcd_cmd(0x80)            #line 1
lcd_string('Black Pill')
lcd_cmd(0xc0)            #line 2
for i in range(100000):
    string = str("%06d" % i)
    lcd_string(string)
    lcd_cmd(0xc0)        #go to start of line
    pyb.delay(10)
stop()
Hardware I2C source code. Nothin fancy - just a simple demo.
# ghmicro.com
# 1602 LCD w/PC8574T I2C backpack - hardware I2c

import time, machine
from machine import I2C, Pin

i2c = machine.I2C(1)

backlight = 0x08                #backlight on
byt = bytearray(1)

# LCD functions
#**************
def lcd_string(buff):
    pointer = 0
    for ch in buff:
        lcd_char(buff[pointer])
        pointer += 1

def lcd_cmd(letter):
    temp = letter
    temp = temp & 0xf0          #clear lower nybble
    temp = temp | 0b00000100    #set E, RW = write, RS = command
    temp = temp | backlight
    byt[0] = temp
    i2c.writeto(0x27,byt)       #send upper nybble
    temp = temp & 0b11110000    #clear E to latch data and resend
    temp = temp | backlight
    byt[0] = temp
    i2c.writeto(0x27,byt)

    temp = letter
    temp = temp & 0x0f          #clear upper nybble
    temp = temp << 4            #shift left 4 bits
    temp = temp | 0b00000100    #send lower nybble
    temp = temp | backlight
    byt[0] = temp
    i2c.writeto(0x27,byt)
    temp = temp & 0b11110000
    temp = temp | backlight
    byt[0] = temp
    i2c.writeto(0x27,byt)

def lcd_char(letter):
    temp = ord(letter)
    temp = temp & 0xf0          #clear lower nybble
    temp = temp | 0b00000101    #set E, RW = write, RS = data
    temp = temp | backlight
    byt[0] = temp
    i2c.writeto(0x27,byt)       #send upper nybble
    temp = temp & 0b11110001    #clear E to latch data and resend
    temp = temp | backlight
    byt[0] = temp
    i2c.writeto(0x27,byt)

    temp = ord(letter)
    temp = temp & 0x0f          #clear upper nybble
    temp = temp << 4            #shift left 4 bits
    temp = temp | 0b00000101    #send lower nybble
    temp = temp | backlight
    byt[0] = temp
    i2c.writeto(0x27,byt)
    temp = temp & 0b11110001
    temp = temp | backlight
    byt[0] = temp
    i2c.writeto(0x27,byt)

def lcd_nybble(nyb):
    nyb = nyb << 4
    nyb = nyb | 0b00001100
    byt[0] = nyb
    i2c.writeto(0x27,byt)
    nyb = nyb & 0b11111000
    byt[0] = nyb
    i2c.writeto(0x27,byt)

def lcd_init():
    pyb.delay(50)
    lcd_nybble(0x03)
    pyb.delay(5)
    lcd_nybble(0x03)
    pyb.delay(160)
    lcd_nybble(0x03)
    pyb.delay(160)
    lcd_nybble(0x02)
    pyb.delay(50)
    lcd_cmd(0x28)       #set 4-bit mode & 2 lines
    pyb.delay(50)
    lcd_cmd(0x10)       #cursor move & shift left
    pyb.delay(50)
    lcd_cmd(0x06)       #entry mode = increment
    pyb.delay(50)
    lcd_cmd(0b00001100) #display on - cursor & blink off
    pyb.delay(50)
    lcd_cmd(0x01)       #clear display
    pyb.delay(50)

#------------------------------------------------------
# Begin here
#------------------------------------------------------

lcd_init()

lcd_cmd(0x80)            #line 1
lcd_string('Black Pill')
lcd_cmd(0xc0)            #line 2
while(1):
    for i in range(1000000):
        string = str("%06d" % i)
        lcd_string(string)
        lcd_cmd(0xc0)        #go to start of line
        pyb.delay(100)

Next Post Previous Post