From bae21e5e602dd1fef09f47080bd9daa47245bb8d Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 17 Dec 2024 19:33:23 -0500 Subject: [PATCH] set all the MCP23008 gpio bits at once to speed up I2C --- .pre-commit-config.yaml | 8 +-- .pylintrc | 55 ++++---------------- adafruit_character_lcd/character_lcd.py | 29 +++++------ adafruit_character_lcd/character_lcd_i2c.py | 56 +++++++++++++++++---- 4 files changed, 71 insertions(+), 77 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 43d1385..559b96b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,21 +4,21 @@ repos: - repo: https://github.com/python/black - rev: 20.8b1 + rev: 23.3.0 hooks: - id: black - repo: https://github.com/fsfe/reuse-tool - rev: v0.12.1 + rev: v1.1.1 hooks: - id: reuse - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.3.0 + rev: v4.4.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: v2.11.1 + rev: v3.3.1 hooks: - id: pylint name: pylint (library code) diff --git a/.pylintrc b/.pylintrc index cfd1c41..1d28459 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries # # SPDX-License-Identifier: Unlicense @@ -9,11 +9,11 @@ # run arbitrary code extension-pkg-whitelist= -# Add files or directories to the blacklist. They should be base names, not +# Add files or directories to the ignore-list. They should be base names, not # paths. ignore=CVS -# Add files or directories matching the regex patterns to the blacklist. The +# Add files or directories matching the regex patterns to the ignore-list. The # regex matches against base names, not paths. ignore-patterns= @@ -26,7 +26,7 @@ jobs=1 # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. -load-plugins= +load-plugins=pylint.extensions.no_self_use # Pickle collected data for later comparisons. persistent=yes @@ -54,8 +54,8 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error,bad-continuation,unspecified-encoding +# disable=import-error,raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,deprecated-str-translate-call +disable=raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,import-error,pointless-string-statement,unspecified-encoding # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option @@ -225,12 +225,6 @@ max-line-length=100 # Maximum number of lines in a module max-module-lines=1000 -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma,dict-separator - # Allow the body of a class to be on the same line as the declaration if body # contains single statement. single-line-class-stmt=no @@ -252,43 +246,27 @@ ignore-docstrings=yes ignore-imports=yes # Minimum lines number of a similarity. -min-similarity-lines=4 +min-similarity-lines=12 [BASIC] -# Naming hint for argument names -argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct argument names argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ -# Naming hint for attribute names -attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct attribute names attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - # Regular expression matching correct class attribute names class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ -# Naming hint for class names -# class-name-hint=[A-Z_][a-zA-Z0-9]+$ -class-name-hint=[A-Z_][a-zA-Z0-9_]+$ - # Regular expression matching correct class names # class-rgx=[A-Z_][a-zA-Z0-9]+$ class-rgx=[A-Z_][a-zA-Z0-9_]+$ -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - # Regular expression matching correct constant names const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ @@ -296,9 +274,6 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ # ones are exempt. docstring-min-length=-1 -# Naming hint for function names -function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct function names function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ @@ -309,21 +284,12 @@ good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ # Include a hint for the correct naming format with invalid-name include-naming-hint=no -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - # Regular expression matching correct inline iteration names inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ -# Naming hint for method names -method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct method names method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - # Regular expression matching correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ @@ -339,9 +305,6 @@ no-docstring-rgx=^_ # to this list to register other decorators that produce valid properties. property-classes=abc.abstractproperty -# Naming hint for variable names -variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct variable names variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ @@ -398,7 +361,7 @@ valid-metaclass-classmethod-first-arg=mcs [DESIGN] # Maximum number of arguments for function / method -max-args=5 +max-args=13 # Maximum number of attributes for a class (see R0902). # max-attributes=7 @@ -433,4 +396,4 @@ min-public-methods=1 # Exceptions that will emit a warning when being caught. Defaults to # "Exception" -overgeneral-exceptions=Exception +overgeneral-exceptions=builtins.Exception diff --git a/adafruit_character_lcd/character_lcd.py b/adafruit_character_lcd/character_lcd.py index 042888d..e6353bc 100644 --- a/adafruit_character_lcd/character_lcd.py +++ b/adafruit_character_lcd/character_lcd.py @@ -96,7 +96,7 @@ def _map(xval, in_min, in_max, out_min, out_max): return ret -# pylint: disable-msg=too-many-instance-attributes +# pylint: disable=too-many-instance-attributes class Character_LCD: """Base class for character LCD. @@ -114,9 +114,8 @@ class Character_LCD: LEFT_TO_RIGHT = const(0) RIGHT_TO_LEFT = const(1) - # pylint: disable-msg=too-many-arguments + # pylint: disable=too-many-positional-arguments,invalid-name def __init__(self, rs, en, d4, d5, d6, d7, columns, lines): - self.columns = columns self.lines = lines # save pin numbers @@ -148,9 +147,9 @@ def __init__(self, rs, en, d4, d5, d6, d7, columns, lines): self._write8(_LCD_ENTRYMODESET | self.displaymode) self.clear() - self._message = None - self._enable = None - self._direction = None + self._message = "" + self._backlight_on = False + self._direction = self.LEFT_TO_RIGHT # track row and column used in cursor_position # initialize to 0,0 self.row = 0 @@ -484,6 +483,7 @@ def create_char(self, location, pattern): self._write8(pattern[i], char_mode=True) def _write8(self, value, char_mode=False): + print(f"bit-by-bit: {char_mode=}, {value=:x}") # Sends 8b ``value`` in ``char_mode``. # :param value: bytes # :param char_mode: character/data mode selector. False (default) for @@ -520,7 +520,7 @@ def _pulse_enable(self): # pylint: enable-msg=too-many-instance-attributes -# pylint: disable-msg=too-many-instance-attributes +# pylint: disable=too-many-instance-attributes class Character_LCD_Mono(Character_LCD): """Interfaces with monochromatic character LCDs. @@ -539,7 +539,7 @@ class Character_LCD_Mono(Character_LCD): """ - # pylint: disable-msg=too-many-arguments + # pylint: disable=too-many-positional-arguments def __init__( self, rs, @@ -553,7 +553,6 @@ def __init__( backlight_pin=None, backlight_inverted=False, ): - # Backlight pin and inversion self.backlight_pin = backlight_pin self.backlight_inverted = backlight_inverted @@ -591,15 +590,12 @@ def backlight(self): time.sleep(5) """ - return self._enable + return self._backlight_on @backlight.setter def backlight(self, enable): - self._enable = enable - if enable: - self.backlight_pin.value = not self.backlight_inverted - else: - self.backlight_pin.value = self.backlight_inverted + self._backlight_on = enable + self.backlight_pin.value = enable ^ self.backlight_inverted class Character_LCD_RGB(Character_LCD): @@ -621,7 +617,7 @@ class Character_LCD_RGB(Character_LCD): """ - # pylint: disable-msg=too-many-arguments + # pylint: disable=too-many-positional-arguments,invalid-name def __init__( self, rs, @@ -637,7 +633,6 @@ def __init__( blue, read_write=None, ): - # Define read_write (rw) pin self.read_write = read_write diff --git a/adafruit_character_lcd/character_lcd_i2c.py b/adafruit_character_lcd/character_lcd_i2c.py index b0d85d5..0009d26 100644 --- a/adafruit_character_lcd/character_lcd_i2c.py +++ b/adafruit_character_lcd/character_lcd_i2c.py @@ -28,6 +28,8 @@ """ +import time + from adafruit_mcp230xx.mcp23008 import MCP23008 from adafruit_character_lcd.character_lcd import Character_LCD_Mono @@ -36,7 +38,7 @@ class Character_LCD_I2C(Character_LCD_Mono): - # pylint: disable=too-few-public-methods, too-many-arguments + # pylint: disable=too-few-public-methods """Character LCD connected to I2C/SPI backpack using its I2C connection. This is a subclass of `Character_LCD_Mono` and implements all of the same functions and functionality. @@ -52,6 +54,7 @@ class Character_LCD_I2C(Character_LCD_Mono): lcd = Character_LCD_I2C(i2c, 16, 2) """ + # pylint: disable=too-many-positional-arguments def __init__(self, i2c, columns, lines, address=None, backlight_inverted=False): """Initialize character LCD connected to backpack using I2C connection on the specified I2C bus with the specified number of columns and @@ -59,18 +62,51 @@ def __init__(self, i2c, columns, lines, address=None, backlight_inverted=False): """ if address: - mcp = MCP23008(i2c, address=address) + self.mcp = MCP23008(i2c, address=address) else: - mcp = MCP23008(i2c) + self.mcp = MCP23008(i2c) super().__init__( - mcp.get_pin(1), - mcp.get_pin(2), - mcp.get_pin(3), - mcp.get_pin(4), - mcp.get_pin(5), - mcp.get_pin(6), + 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=mcp.get_pin(7), + backlight_pin=self.mcp.get_pin(7), backlight_inverted=backlight_inverted, ) + + def _write8(self, value, char_mode=False): + # Sends 8b ``value`` in ``char_mode``. + # :param value: bytes + # :param char_mode: character/data mode selector. False (default) for + # data only, True for character bits. + # 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.mcp.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) + + # do command + self._pulse_enable()