diff --git a/RPLCD/gpio.py b/RPLCD/gpio.py index 1ef41b1..8ed26da 100644 --- a/RPLCD/gpio.py +++ b/RPLCD/gpio.py @@ -30,15 +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 - -# 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') @@ -49,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. @@ -97,12 +89,11 @@ 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 """ - # 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 +127,9 @@ 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, + compat_mode_wait_time=compat_mode_wait_time) # Set backlight status if pin_backlight is not None: @@ -190,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) @@ -209,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. """ @@ -243,8 +234,3 @@ 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..7aa6358 100644 --- a/RPLCD/lcd.py +++ b/RPLCD/lcd.py @@ -27,7 +27,12 @@ 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 +from time import sleep LCDConfig = namedtuple('LCDConfig', 'rows cols dotsize') @@ -38,7 +43,8 @@ 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, + compat_mode_wait_time=0.001): """ Character LCD controller. Base class only, you should use a subclass. @@ -56,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() @@ -446,3 +463,20 @@ 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') + + 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 20e3723..b7bc44d 100644 --- a/RPLCD/pigpio.py +++ b/RPLCD/pigpio.py @@ -31,7 +31,6 @@ from .lcd import BaseCharLCD from .compat import range - # 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 @@ -52,7 +51,9 @@ 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, + compat_mode_wait_time=0.001): """ Character LCD controller. @@ -118,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 """ @@ -151,7 +155,9 @@ 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, + compat_mode_wait_time=compat_mode_wait_time) # Set backlight status if pin_backlight is not None: @@ -300,6 +306,9 @@ 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 + self._compat_mode_wait() + # Assemble the parameters sent to the pigpio script params = [mode] params.extend([(value >> i) & 0x01 for i in range(8)]) @@ -316,6 +325,9 @@ 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 + self._compat_mode_record_send_event() + def _send_data(self, value): """Send data to the display. """ self._send(value, c.RS_DATA)