Skip to content

Adding I2C Micropython code #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ Giuseppe Cassibba wrote up a [tutorial](https://peppe8o.com/using-i2c-lcd-displa
| pyb_i2c_grove_rgb_lcd_test.py | Pyboard test using Grove I2C RGB LCD |
| pyb_i2c_lcd.py | Pyboard PCF8574 I2C HAL |
| pyb_i2c_lcd_test.py | Pyboard test using PCF8574 backpack |

| i2c_micropython_lcd.py | Micropython I2C |
| i2c_micropython_lcd_test.py | Micropython I2C test using ESP32 |

The files which end in **_test.py** are examples which show how the corresponding
file is used.
Expand Down
89 changes: 89 additions & 0 deletions lcd/i2c_micropython_lcd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""Implements a HD44780 character LCD connected via PCF8574 on I2C."""

from lcd_api import LcdApi
from machine import I2C
import time

# The PCF8574 has a jumper selectable address: 0x20 - 0x27
DEFAULT_I2C_ADDR = 0x27

# Defines shifts or masks for the various LCD line attached to the PCF8574

MASK_RS = 0x01
MASK_RW = 0x02
MASK_E = 0x04
SHIFT_BACKLIGHT = 3
SHIFT_DATA = 4


class I2cLcd(LcdApi):
"""Implements a HD44780 character LCD connected via PCF8574 on I2C."""

def __init__(self, i2c, i2c_addr, num_lines, num_columns):
self.i2c_addr = i2c_addr
self.i2c = i2c
self.i2c.writeto(self.i2c_addr, bytearray([0]))
time.sleep(0.020) # Allow LCD time to powerup
# Send reset 3 times
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
time.sleep(0.005) # need to delay at least 4.1 msec
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
time.sleep(0.001)
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
time.sleep(0.001)
# Put LCD into 4 bit mode
self.hal_write_init_nibble(self.LCD_FUNCTION)
time.sleep(0.001)
LcdApi.__init__(self, num_lines, num_columns)
cmd = self.LCD_FUNCTION
if num_lines > 1:
cmd |= self.LCD_FUNCTION_2LINES
self.hal_write_command(cmd)

def hal_write_init_nibble(self, nibble):
"""Writes an initialization nibble to the LCD.

This particular function is only used during initialization.
"""
byte = ((nibble >> 4) & 0x0f) << SHIFT_DATA
self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytearray([byte]))

def hal_backlight_on(self):
"""Allows the hal layer to turn the backlight on."""
self.i2c.writeto(self.i2c_addr, bytearray([1 << SHIFT_BACKLIGHT]))

def hal_backlight_off(self):
"""Allows the hal layer to turn the backlight off."""
self.i2c.writeto(self.i2c_addr, bytearray([0]))

def hal_write_command(self, cmd):
"""Writes a command to the LCD.

Data is latched on the falling edge of E.
"""
byte = ((self.backlight << SHIFT_BACKLIGHT) |
(((cmd >> 4) & 0x0f) << SHIFT_DATA))
self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytearray([byte]))
byte = ((self.backlight << SHIFT_BACKLIGHT) |
((cmd & 0x0f) << SHIFT_DATA))
self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytearray([byte]))
if cmd <= 3:
# The home and clear commands require a worst
# case delay of 4.1 msec
time.sleep(0.005)

def hal_write_data(self, data):
"""Write data to the LCD."""
byte = (MASK_RS |
(self.backlight << SHIFT_BACKLIGHT) |
(((data >> 4) & 0x0f) << SHIFT_DATA))
self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytearray([byte]))
byte = (MASK_RS |
(self.backlight << SHIFT_BACKLIGHT) |
((data & 0x0f) << SHIFT_DATA))
self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytearray([byte]))
65 changes: 65 additions & 0 deletions lcd/i2c_micropython_lcd_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""Implements a HD44780 character LCD connected via PCF8574 on I2C."""

from i2c_micropython_lcd import I2cLcd
import machine
import time

# The PCF8574 has a jumper selectable address: 0x20 - 0x27
DEFAULT_I2C_ADDR = 0x27

def test_main():
"""Test function for verifying basic functionality."""
sda = machine.Pin(18)
scl = machine.Pin(19)
i2c = machine.I2C(scl=scl, sda=sda, freq=100000)

lcd = I2cLcd(i2c, DEFAULT_I2C_ADDR, 4, 20)
lcd.blink_cursor_on()
lcd.putstr("It Works!\nSecond Line")
time.sleep(3)
lcd.clear()

# custom characters: battery icons - 5 wide, 8 tall
lcd.custom_char(0, bytearray([0x0E,0x1B,0x11,0x11,0x11,0x11,0x11,0x1F])) # 0% Empty
lcd.custom_char(1, bytearray([0x0E,0x1B,0x11,0x11,0x11,0x11,0x1F,0x1F])) # 16%
lcd.custom_char(2, bytearray([0x0E,0x1B,0x11,0x11,0x11,0x1F,0x1F,0x1F])) # 33%
lcd.custom_char(3, bytearray([0x0E,0x1B,0x11,0x11,0x1F,0x1F,0x1F,0x1F])) # 50%
lcd.custom_char(4, bytearray([0x0E,0x1B,0x11,0x1F,0x1F,0x1F,0x1F,0x1F])) # 66%
lcd.custom_char(5, bytearray([0x0E,0x1B,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F])) # 83%
lcd.custom_char(6, bytearray([0x0E,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F])) # 100% Full
lcd.custom_char(7, bytearray([0x0E,0x1F,0x1B,0x1B,0x1B,0x1F,0x1B,0x1F])) # ! Error
for i in range(8):
lcd.putchar(chr(i))
time.sleep(3)
lcd.clear()
lcd.blink_cursor_off()

count = 0
while True:
lcd.move_to(0, 0)
lcd.putstr("Time: " + str(time.time()))
time.sleep(1)
count += 1
if count % 10 == 3:
print("Turning backlight off")
lcd.backlight_off()
if count % 10 == 4:
print("Turning backlight on")
lcd.backlight_on()
if count % 10 == 5:
print("Turning display off")
lcd.display_off()
if count % 10 == 6:
print("Turning display on")
lcd.display_on()
if count % 10 == 7:
print("Turning display & backlight off")
lcd.backlight_off()
lcd.display_off()
if count % 10 == 8:
print("Turning display & backlight on")
lcd.backlight_on()
lcd.display_on()

if __name__ == "__main__":
test_main()