From daa56fbade13348312ba2463b7770dc340ae7b22 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 | 73 ++++++++++++++++----- requirements.txt | 1 + 3 files changed, 57 insertions(+), 18 deletions(-) diff --git a/README.rst b/README.rst index 8db7546..f6bf078 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 346d0e3..45b9a2d 100644 --- a/adafruit_character_lcd/character_lcd_i2c.py +++ b/adafruit_character_lcd/character_lcd_i2c.py @@ -36,12 +36,23 @@ pass from adafruit_mcp230xx.mcp23008 import MCP23008 +from adafruit_pcf8574 import PCF8574 from adafruit_character_lcd.character_lcd import Character_LCD_Mono __version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_CharLCD.git" +class I2C_Expander: + # pylint: disable=too-few-public-methods + """ + I2C Expander ICs + """ + + MCP23008 = "MCP23008" + PCF8574 = "PCF8574" + + class Character_LCD_I2C(Character_LCD_Mono): # pylint: disable=too-few-public-methods """Character LCD connected to I2C/SPI backpack using its I2C connection. @@ -67,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 @@ -112,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 9cecf0c0e6021e572e7c23849dae4e742db784b3 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 45b9a2d..1b11c7b 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()