From 93e8a43b51b13ed3f9a5fb1a761deaa3aff9ba2d Mon Sep 17 00:00:00 2001 From: Maksim Levental Date: Tue, 2 Apr 2019 11:39:15 -0400 Subject: [PATCH 1/2] -move compat mode to base -implement for pigpio --- RPLCD/gpio.py | 16 +++------------- RPLCD/lcd.py | 23 ++++++++++++++++++++++- RPLCD/pigpio.py | 19 +++++++++++++++++-- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/RPLCD/gpio.py b/RPLCD/gpio.py index 1ef41b1..d2acbb7 100644 --- a/RPLCD/gpio.py +++ b/RPLCD/gpio.py @@ -36,9 +36,6 @@ else: from time import perf_counter as now -# Duration to rate-limit calls to _send -COMPAT_MODE_WAIT_TIME = 0.001 - PinConfig = namedtuple('PinConfig', 'rs rw e d0 d1 d2 d3 d4 d5 d6 d7 backlight mode') @@ -99,10 +96,6 @@ def __init__(self, numbering_mode=None, pin_rs=None, pin_rw=None, pin_e=None, pi :type compat_mode: bool """ - # Configure compatibility mode - self.compat_mode = compat_mode - if compat_mode: - self.last_send_event = now() # Set attributes if numbering_mode == GPIO.BCM or numbering_mode == GPIO.BOARD: @@ -136,7 +129,8 @@ def __init__(self, numbering_mode=None, pin_rs=None, pin_rw=None, pin_e=None, pi # Call superclass super(CharLCD, self).__init__(cols, rows, dotsize, charmap=charmap, - auto_linebreaks=auto_linebreaks) + auto_linebreaks=auto_linebreaks, + compat_mode=compat_mode) # Set backlight status if pin_backlight is not None: @@ -243,8 +237,4 @@ def _pulse_enable(self): GPIO.output(self.pins.e, 0) c.usleep(100) # commands need > 37us to settle - def _wait(self): - """Rate limit the number of send events.""" - end = self.last_send_event + COMPAT_MODE_WAIT_TIME - while now() < end: - pass + diff --git a/RPLCD/lcd.py b/RPLCD/lcd.py index 53a359c..8d2992b 100644 --- a/RPLCD/lcd.py +++ b/RPLCD/lcd.py @@ -27,7 +27,14 @@ from . import codecs from . import common as c from .compat import range +import sys +if sys.version_info.major < 3: + from time import clock as now +else: + from time import perf_counter as now +# Duration to rate-limit calls to _send +COMPAT_MODE_WAIT_TIME = 0.001 LCDConfig = namedtuple('LCDConfig', 'rows cols dotsize') @@ -38,7 +45,7 @@ class BaseCharLCD(object): # Init, setup, teardown - def __init__(self, cols=20, rows=4, dotsize=8, charmap='A02', auto_linebreaks=True): + def __init__(self, cols=20, rows=4, dotsize=8, charmap='A02', auto_linebreaks=True, compat_mode=False): """ Character LCD controller. Base class only, you should use a subclass. @@ -117,6 +124,8 @@ def __init__(self, cols=20, rows=4, dotsize=8, charmap='A02', auto_linebreaks=Tr else: raise ValueError('Invalid data bus mode: {}'.format(self.data_bus_mode)) + + # Write configuration to display self.command(c.LCD_FUNCTIONSET | displayfunction) c.usleep(50) @@ -137,6 +146,11 @@ def __init__(self, cols=20, rows=4, dotsize=8, charmap='A02', auto_linebreaks=Tr self.command(c.LCD_ENTRYMODESET | self._text_align_mode | self._display_shift_mode) c.usleep(50) + # Configure compatibility mode + self.compat_mode = compat_mode + if compat_mode: + self.last_send_event = now() + def close(self, clear=False): if clear: self.clear() @@ -239,6 +253,12 @@ def _set_cursor_mode(self, value): cursor_mode = property(_get_cursor_mode, _set_cursor_mode, doc='How the cursor should behave (``hide``, ``line`` or ``blink``).') + def _wait(self): + """Rate limit the number of send events.""" + end = self.last_send_event + COMPAT_MODE_WAIT_TIME + while now() < end: + pass + # High level commands def write_string(self, value): @@ -446,3 +466,4 @@ def lf(self): # type: () -> None def crlf(self): # type: () -> None """Write a line feed and a carriage return (``\\r\\n``) character to the LCD.""" self.write_string('\r\n') + diff --git a/RPLCD/pigpio.py b/RPLCD/pigpio.py index 20e3723..6105f87 100644 --- a/RPLCD/pigpio.py +++ b/RPLCD/pigpio.py @@ -31,6 +31,11 @@ from .lcd import BaseCharLCD from .compat import range +import sys +if sys.version_info.major < 3: + from time import clock as now +else: + from time import perf_counter as now # https://diarmuid.ie/blog/pwm-exponential-led-fading-on-arduino-or-other-platforms/ # p 101 .. maximum value of the PWM cycle @@ -52,7 +57,8 @@ def __init__(self, pi, contrast_pwm=None, contrast=0.5, cols=20, rows=4, dotsize=8, charmap='A02', - auto_linebreaks=True): + auto_linebreaks=True, + compat_mode=False): """ Character LCD controller. @@ -151,7 +157,8 @@ def __init__(self, pi, # Call superclass super(CharLCD, self).__init__(cols, rows, dotsize, charmap=charmap, - auto_linebreaks=auto_linebreaks) + auto_linebreaks=auto_linebreaks, + compat_mode=compat_mode) # Set backlight status if pin_backlight is not None: @@ -300,6 +307,10 @@ def _send(self, value, mode): """Send the specified value to the display with automatic 4bit / 8bit selection. The rs_mode is either ``RS_DATA`` or ``RS_INSTRUCTION``.""" + # Wait, if compatibility mode is enabled + if self.compat_mode: + self._wait() + # Assemble the parameters sent to the pigpio script params = [mode] params.extend([(value >> i) & 0x01 for i in range(8)]) @@ -316,6 +327,10 @@ def _send(self, value, mode): # Switch on pigpio's exceptions pigpio.exceptions = True + # Record the time for the tail-end of the last send event + if self.compat_mode: + self.last_send_event = now() + def _send_data(self, value): """Send data to the display. """ self._send(value, c.RS_DATA) From 61ed40bdd23df09883749aed4c3b8ec85c501f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Joz=C3=ADfek?= Date: Tue, 5 May 2020 17:08:20 +0200 Subject: [PATCH 2/2] Move wait implementation to base class, make sleep amount tune-able Remove busy-wait Fix initialization bug --- RPLCD/gpio.py | 24 ++++++++++-------------- RPLCD/lcd.py | 49 +++++++++++++++++++++++++++++++------------------ RPLCD/pigpio.py | 21 +++++++++------------ 3 files changed, 50 insertions(+), 44 deletions(-) diff --git a/RPLCD/gpio.py b/RPLCD/gpio.py index d2acbb7..8ed26da 100644 --- a/RPLCD/gpio.py +++ b/RPLCD/gpio.py @@ -30,12 +30,6 @@ from .lcd import BaseCharLCD from .compat import range -import sys -if sys.version_info.major < 3: - from time import clock as now -else: - from time import perf_counter as now - PinConfig = namedtuple('PinConfig', 'rs rw e d0 d1 d2 d3 d4 d5 d6 d7 backlight mode') @@ -46,7 +40,8 @@ def __init__(self, numbering_mode=None, pin_rs=None, pin_rw=None, pin_e=None, pi cols=20, rows=4, dotsize=8, charmap='A02', auto_linebreaks=True, - compat_mode=False): + compat_mode=False, + compat_mode_wait_time=0.001): """ Character LCD controller. @@ -94,6 +89,9 @@ def __init__(self, numbering_mode=None, pin_rs=None, pin_rw=None, pin_e=None, pi :param compat_mode: Whether to run additional checks to support older LCDs that may not run at the reference clock (or keep up with it). :type compat_mode: bool + :param compat_mode_wait_time: Minimum time to pass between sends. + if zero, turns off compat_mode Default: ``0.001`` seconds. + :type compat_mode_wait_time: float """ @@ -130,7 +128,8 @@ def __init__(self, numbering_mode=None, pin_rs=None, pin_rw=None, pin_e=None, pi super(CharLCD, self).__init__(cols, rows, dotsize, charmap=charmap, auto_linebreaks=auto_linebreaks, - compat_mode=compat_mode) + compat_mode=compat_mode, + compat_mode_wait_time=compat_mode_wait_time) # Set backlight status if pin_backlight is not None: @@ -184,9 +183,8 @@ def _set_backlight_enabled(self, value): def _send(self, value, mode): """Send the specified value to the display with automatic 4bit / 8bit selection. The rs_mode is either ``RS_DATA`` or ``RS_INSTRUCTION``.""" - # Wait, if compatibility mode is enabled - if self.compat_mode: - self._wait() + # Wait if compatibility mode is enabled + self._compat_mode_wait() # Choose instruction or data mode GPIO.output(self.pins.rs, mode) @@ -203,8 +201,7 @@ def _send(self, value, mode): self._write4bits(value) # Record the time for the tail-end of the last send event - if self.compat_mode: - self.last_send_event = now() + self._compat_mode_record_send_event() def _send_data(self, value): """Send data to the display. """ @@ -237,4 +234,3 @@ def _pulse_enable(self): GPIO.output(self.pins.e, 0) c.usleep(100) # commands need > 37us to settle - diff --git a/RPLCD/lcd.py b/RPLCD/lcd.py index 8d2992b..7aa6358 100644 --- a/RPLCD/lcd.py +++ b/RPLCD/lcd.py @@ -32,9 +32,7 @@ from time import clock as now else: from time import perf_counter as now - -# Duration to rate-limit calls to _send -COMPAT_MODE_WAIT_TIME = 0.001 +from time import sleep LCDConfig = namedtuple('LCDConfig', 'rows cols dotsize') @@ -45,7 +43,8 @@ class BaseCharLCD(object): # Init, setup, teardown - def __init__(self, cols=20, rows=4, dotsize=8, charmap='A02', auto_linebreaks=True, compat_mode=False): + def __init__(self, cols=20, rows=4, dotsize=8, charmap='A02', auto_linebreaks=True, compat_mode=False, + compat_mode_wait_time=0.001): """ Character LCD controller. Base class only, you should use a subclass. @@ -63,10 +62,21 @@ def __init__(self, cols=20, rows=4, dotsize=8, charmap='A02', auto_linebreaks=Tr auto_linebreaks: Whether or not to automatically insert line breaks. Default: True. - + compat_mode: + Whether to run additional checks to support older LCDs + that may not run at the reference clock (or keep up with it). + Default: False + compat_mode_wait_time: Minimum time to pass between sends. + if zero, turns off compat_mode + Default: ``0.001`` seconds. """ assert dotsize in [8, 10], 'The ``dotsize`` argument should be either 8 or 10.' + # Configure compatibility mode + self.compat_mode = compat_mode and compat_mode_wait_time > 0 + self.compat_mode_wait_time = compat_mode_wait_time + self._compat_mode_record_send_event() + # Initialize codec if charmap == 'A00': self.codec = codecs.A00Codec() @@ -124,8 +134,6 @@ def __init__(self, cols=20, rows=4, dotsize=8, charmap='A02', auto_linebreaks=Tr else: raise ValueError('Invalid data bus mode: {}'.format(self.data_bus_mode)) - - # Write configuration to display self.command(c.LCD_FUNCTIONSET | displayfunction) c.usleep(50) @@ -146,11 +154,6 @@ def __init__(self, cols=20, rows=4, dotsize=8, charmap='A02', auto_linebreaks=Tr self.command(c.LCD_ENTRYMODESET | self._text_align_mode | self._display_shift_mode) c.usleep(50) - # Configure compatibility mode - self.compat_mode = compat_mode - if compat_mode: - self.last_send_event = now() - def close(self, clear=False): if clear: self.clear() @@ -253,12 +256,6 @@ def _set_cursor_mode(self, value): cursor_mode = property(_get_cursor_mode, _set_cursor_mode, doc='How the cursor should behave (``hide``, ``line`` or ``blink``).') - def _wait(self): - """Rate limit the number of send events.""" - end = self.last_send_event + COMPAT_MODE_WAIT_TIME - while now() < end: - pass - # High level commands def write_string(self, value): @@ -467,3 +464,19 @@ def crlf(self): # type: () -> None """Write a line feed and a carriage return (``\\r\\n``) character to the LCD.""" self.write_string('\r\n') + def _is_compat_mode_on(self): + """Compat mode is on, when it's enabled and the wait time is > 0""" + return self.compat_mode and self.compat_mode_wait_time > 0 + + def _compat_mode_wait(self): + """Wait the specified amount, if the compat mode is on, to rate limit the data transmission""" + if self._is_compat_mode_on(): + end = self.last_send_event + self.compat_mode_wait_time + sleep_duration = end - now() + if sleep_duration > 0: + sleep(sleep_duration) + + def _compat_mode_record_send_event(self): + """Record when did the last send take place, so the rate limiting adapts to any slowdowns.""" + self.last_send_event = now() + diff --git a/RPLCD/pigpio.py b/RPLCD/pigpio.py index 6105f87..b7bc44d 100644 --- a/RPLCD/pigpio.py +++ b/RPLCD/pigpio.py @@ -31,12 +31,6 @@ from .lcd import BaseCharLCD from .compat import range -import sys -if sys.version_info.major < 3: - from time import clock as now -else: - from time import perf_counter as now - # https://diarmuid.ie/blog/pwm-exponential-led-fading-on-arduino-or-other-platforms/ # p 101 .. maximum value of the PWM cycle # m 100 .. number of steps the LED will fade over @@ -58,7 +52,8 @@ def __init__(self, pi, cols=20, rows=4, dotsize=8, charmap='A02', auto_linebreaks=True, - compat_mode=False): + compat_mode=False, + compat_mode_wait_time=0.001): """ Character LCD controller. @@ -124,6 +119,9 @@ def __init__(self, pi, :param auto_linebreaks: Whether or not to automatically insert line breaks. Default: ``True``. :type auto_linebreaks: bool + :param compat_mode_wait_time: Minimum time to pass between sends. + if zero, turns off compat_mode Default: ``0.001`` seconds. + :type compat_mode_wait_time: float """ @@ -158,7 +156,8 @@ def __init__(self, pi, super(CharLCD, self).__init__(cols, rows, dotsize, charmap=charmap, auto_linebreaks=auto_linebreaks, - compat_mode=compat_mode) + compat_mode=compat_mode, + compat_mode_wait_time=compat_mode_wait_time) # Set backlight status if pin_backlight is not None: @@ -308,8 +307,7 @@ def _send(self, value, mode): selection. The rs_mode is either ``RS_DATA`` or ``RS_INSTRUCTION``.""" # Wait, if compatibility mode is enabled - if self.compat_mode: - self._wait() + self._compat_mode_wait() # Assemble the parameters sent to the pigpio script params = [mode] @@ -328,8 +326,7 @@ def _send(self, value, mode): pigpio.exceptions = True # Record the time for the tail-end of the last send event - if self.compat_mode: - self.last_send_event = now() + self._compat_mode_record_send_event() def _send_data(self, value): """Send data to the display. """