From 7f25d1a0f9a8d32538f2f0c22c11b317834a4bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Ondr=C3=A1=C4=8Dek?= Date: Thu, 6 Feb 2025 01:06:20 +0100 Subject: [PATCH 1/2] Character_LCD_I2C: add support for PCF8574 I2C expander backpack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Roman Ondráček --- README.rst | 1 + adafruit_character_lcd/character_lcd_i2c.py | 72 +++++++++++++++------ requirements.txt | 1 + 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/README.rst b/README.rst index 8c1cd04..74a1827 100644 --- a/README.rst +++ b/README.rst @@ -55,6 +55,7 @@ This driver depends on: * `Adafruit CircuitPython `_ * `Adafruit CircuitPython BusDevice `_ * `Adafruit CircuitPython MCP230xx `_ +* `Adafruit CircuitPython PCF8574 `_ * `Adafruit CircuitPython 74HC595 `_ I2C & SPI displays also depend on: diff --git a/adafruit_character_lcd/character_lcd_i2c.py b/adafruit_character_lcd/character_lcd_i2c.py index af0676c..029b80e 100644 --- a/adafruit_character_lcd/character_lcd_i2c.py +++ b/adafruit_character_lcd/character_lcd_i2c.py @@ -38,6 +38,7 @@ pass from adafruit_mcp230xx.mcp23008 import MCP23008 +from adafruit_pcf8574 import PCF8574 from adafruit_character_lcd.character_lcd import Character_LCD_Mono @@ -45,6 +46,15 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_CharLCD.git" +class I2C_Expander: + """ + I2C Expander ICs + """ + + MCP23008 = "MCP23008" + PCF8574 = "PCF8574" + + class Character_LCD_I2C(Character_LCD_Mono): """Character LCD connected to I2C/SPI backpack using its I2C connection. This is a subclass of `Character_LCD_Mono` and implements all the same @@ -68,30 +78,56 @@ def __init__( lines: int, address: Optional[int] = None, backlight_inverted: bool = False, + expander: I2C_Expander = I2C_Expander.MCP23008, ) -> None: """Initialize character LCD connected to backpack using I2C connection on the specified I2C bus with the specified number of columns and lines on the display. Optionally specify if backlight is inverted. """ - if address: - self.mcp = MCP23008(i2c, address=address) - else: - self.mcp = MCP23008(i2c) - super().__init__( - self.mcp.get_pin(1), # reset - self.mcp.get_pin(2), # enable - self.mcp.get_pin(3), # data line 4 - self.mcp.get_pin(4), # data line 5 - self.mcp.get_pin(5), # data line 6 - self.mcp.get_pin(6), # data line 7 - columns, - lines, - backlight_pin=self.mcp.get_pin(7), - backlight_inverted=backlight_inverted, - ) + if expander == I2C_Expander.MCP23008: + if address: + self.expander = MCP23008(i2c, address=address) + else: + self.expander = MCP23008(i2c) + + super().__init__( + self.expander.get_pin(1), # reset + self.expander.get_pin(2), # enable + self.expander.get_pin(3), # data line 4 + self.expander.get_pin(4), # data line 5 + self.expander.get_pin(5), # data line 6 + self.expander.get_pin(6), # data line 7 + columns, + lines, + backlight_pin=self.expander.get_pin(7), + backlight_inverted=backlight_inverted, + ) + + elif expander == I2C_Expander.PCF8574: + if address: + self.expander = PCF8574(i2c, address=address) + else: + self.expander = PCF8574(i2c) + + super().__init__( + self.expander.get_pin(0), # reset + self.expander.get_pin(2), # enable + self.expander.get_pin(4), # data line 4 + self.expander.get_pin(5), # data line 5 + self.expander.get_pin(6), # data line 6 + self.expander.get_pin(7), # data line 7 + columns, + lines, + backlight_pin=self.expander.get_pin(3), + backlight_inverted=backlight_inverted, + ) def _write8(self, value: int, char_mode: bool = False) -> None: + if not isinstance(self.expander, MCP23008): + super()._write8(value, char_mode) + return + # Sends 8b ``value`` in ``char_mode``. # :param value: bytes # :param char_mode: character/data mode selector. False (default) for @@ -113,13 +149,13 @@ def _write8(self, value: int, char_mode: bool = False) -> None: backlight_bit = int(self.backlight ^ self.backlight_inverted) << 7 # Write char_mode and upper 4 bits of data, shifted to the correct position. - self.mcp.gpio = reset_bit | backlight_bit | ((value & 0xF0) >> 1) + self.expander.gpio = reset_bit | backlight_bit | ((value & 0xF0) >> 1) # do command self._pulse_enable() # Write char_mode and lower 4 bits of data, shifted to the correct position. - self.mcp.gpio = reset_bit | backlight_bit | ((value & 0x0F) << 3) + self.expander.gpio = reset_bit | backlight_bit | ((value & 0x0F) << 3) # do command self._pulse_enable() diff --git a/requirements.txt b/requirements.txt index c933bcb..aad84bd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ Adafruit-Blinka adafruit-circuitpython-mcp230xx +adafruit-circuitpython-pcf8574 adafruit-circuitpython-busdevice adafruit-circuitpython-74hc595 adafruit-circuitpython-typing~=1.5 From a42d2305428a3cab5e35d1e80f4d97d709efcbc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Ondr=C3=A1=C4=8Dek?= Date: Thu, 6 Feb 2025 14:19:22 +0100 Subject: [PATCH 2/2] Character_LCD_I2C: speed up PCF8574 by setting all GPIO bits at once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Roman Ondráček --- adafruit_character_lcd/character_lcd_i2c.py | 70 +++++++++++++-------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/adafruit_character_lcd/character_lcd_i2c.py b/adafruit_character_lcd/character_lcd_i2c.py index 029b80e..371864a 100644 --- a/adafruit_character_lcd/character_lcd_i2c.py +++ b/adafruit_character_lcd/character_lcd_i2c.py @@ -124,10 +124,6 @@ def __init__( ) def _write8(self, value: int, char_mode: bool = False) -> None: - if not isinstance(self.expander, MCP23008): - super()._write8(value, char_mode) - return - # Sends 8b ``value`` in ``char_mode``. # :param value: bytes # :param char_mode: character/data mode selector. False (default) for @@ -135,27 +131,47 @@ def _write8(self, value: int, char_mode: bool = False) -> None: # one ms delay to prevent writing too quickly. time.sleep(0.001) - # bits are, MSB (7) to LSB (0) - # backlight: bit 7 - # data line 7: bit 6 - # data line 6: bit 5 - # data line 5: bit 4 - # data line 4: bit 3 - # enable: bit 2 - # reset: bit 1 - # (unused): bit 0 - - reset_bit = int(char_mode) << 1 - backlight_bit = int(self.backlight ^ self.backlight_inverted) << 7 - - # Write char_mode and upper 4 bits of data, shifted to the correct position. - self.expander.gpio = reset_bit | backlight_bit | ((value & 0xF0) >> 1) - - # do command - self._pulse_enable() - - # Write char_mode and lower 4 bits of data, shifted to the correct position. - self.expander.gpio = reset_bit | backlight_bit | ((value & 0x0F) << 3) - - # do command + if isinstance(self.expander, MCP23008): + # bits are, MSB (7) to LSB (0) + # backlight: bit 7 + # data line 7: bit 6 + # data line 6: bit 5 + # data line 5: bit 4 + # data line 4: bit 3 + # enable: bit 2 + # reset: bit 1 + # (unused): bit 0 + + reset_bit = int(char_mode) << 1 + backlight_bit = int(self.backlight ^ self.backlight_inverted) << 7 + + # Write char_mode and upper 4 bits of data, shifted to the correct position. + self.__write_command(reset_bit | backlight_bit | ((value & 0xF0) >> 1)) + self.__write_command(reset_bit | backlight_bit | ((value & 0x0F) << 3)) + + elif isinstance(self.expander, PCF8574): + # bits are, MSB (7) to LSB (0) + # data line 7: bit 7 + # data line 6: bit 6 + # data line 5: bit 5 + # data line 4: bit 4 + # backlight: bit 3 + # enable: bit 2 + # write/read: bit 1 + # reset: bit 0 + reset_bit = int(char_mode) + backlight_bit = int(self.backlight ^ self.backlight_inverted) << 3 + + # Write char_mode and upper 4 bits of data, shifted to the correct position. + self.__write_command(reset_bit | backlight_bit | (value & 0xF0)) + self.__write_command(reset_bit | backlight_bit | (value << 4)) + + def __write_command(self, value: int) -> None: + # Write command bits to expander. + if isinstance(self.expander, MCP23008): + self.expander.gpio = value + elif isinstance(self.expander, PCF8574): + self.expander.write_gpio(value) + + # execute command self._pulse_enable()