Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 11 additions & 25 deletions RPLCD/gpio.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')


Expand All @@ -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.

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand All @@ -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. """
Expand Down Expand Up @@ -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
40 changes: 37 additions & 3 deletions RPLCD/lcd.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this Python 2 compat code, I think it's time to drop Python 2 support in the next release. However, for now, let's keep it since it still works.

from time import sleep

LCDConfig = namedtuple('LCDConfig', 'rows cols dotsize')

Expand All @@ -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.

Expand All @@ -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.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since compat_mode=False and compat_mode_wait_time=0 are essentially the same, can we merge the two parameters?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'd like to. But there are two problems with having just the wait amount.
We'll lose backwards compatibility. That's ok. I can do an assert that tells would be users what to do instead.
There would be no default for compat_mode "on". The default amount would have to be sourced from something like DEFAULT_COMPAT_WAIT_TIME constant or the docs.
It's undoubtedly cleaner and gets rid of invalid configs like compat_mode=False with a high compat_mode_wait_time value. Your pick 😉

"""
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()
Expand Down Expand Up @@ -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()

18 changes: 15 additions & 3 deletions RPLCD/pigpio.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.

Expand Down Expand Up @@ -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

"""

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)])
Expand All @@ -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)
Expand Down