From af0ae91379290135928b6254b7a66d606752abb3 Mon Sep 17 00:00:00 2001 From: Joseph Coffland Date: Fri, 12 May 2017 20:45:24 -0700 Subject: [PATCH] More robust LCD driver --- src/py/bbctrl/LCD.py | 126 +++++++++++++++++++++++++++++++++-------- src/py/lcd/__init__.py | 7 ++- 2 files changed, 108 insertions(+), 25 deletions(-) diff --git a/src/py/bbctrl/LCD.py b/src/py/bbctrl/LCD.py index 1910b4b..b21591d 100644 --- a/src/py/bbctrl/LCD.py +++ b/src/py/bbctrl/LCD.py @@ -1,6 +1,7 @@ import lcd import atexit import logging +import tornado.ioloop log = logging.getLogger('LCD') @@ -9,20 +10,92 @@ log = logging.getLogger('LCD') class LCD: def __init__(self, ctrl): self.ctrl = ctrl + + self.width = 20 + self.height = 4 + self.lcd = None + self.timeout = None + self.clear_next_write = False + + self.clear() + self.text('Loading', 6, 1) + self.clear_next_write = True + self.update_screen() + + # Redraw screen every 5 seconds + self.redraw_timer = tornado.ioloop.PeriodicCallback(self._redraw, 5000, + self.ctrl.ioloop) + self.redraw_timer.start() + atexit.register(self.goodbye) - self.connect() - def connect(self): + def clear(self): + self.screen = [[[' ', False] for x in range(self.width)] + for y in range(self.height)] + self.redraw = True + + + def _trigger_update(self): + if self.timeout is None: + self.timeout = self.ctrl.ioloop.call_later(0.25, self.update_screen) + + + def _redraw(self): + self.redraw = True + self._trigger_update() + + + def put(self, c, x, y): + if self.clear_next_write: + self.clear_next_write = False + self.clear() + + y += x // self.width + x %= self.width + y %= self.height + + if self.screen[y][x][0] != c: + self.screen[y][x] = [c, True] + self._trigger_update() + + + def text(self, s, x, y): + for c in s: + self.put(c, x, y) + x += 1 + + + def update_screen(self): + self.timeout = None + try: - self.lcd = None - self.lcd = lcd.LCD(self.ctrl.args.lcd_port, self.ctrl.args.lcd_addr) - self.lcd.clear() - self.lcd.display(1, 'Loading', lcd.JUSTIFY_CENTER) + if self.lcd is None: + self.lcd = lcd.LCD(self.ctrl.args.lcd_port, + self.ctrl.args.lcd_addr, + self.height, self.width) + + cursorX, cursorY = -1, -1 + + for y in range(self.height): + for x in range(self.width): + cell = self.screen[y][x] + + if self.redraw or cell[1]: + if cursorX != x or cursorY != y: + self.lcd.goto(x, y) + cursorX, cursorY = x, y + + self.lcd.put_char(cell[0]) + cursorX += 1 + cell[1] = False + + self.redraw = False except IOError as e: - log.error('Connect failed, retrying: %s' % e) - self.ctrl.ioloop.call_later(1, self.connect) + log.error('LCD communication failed, retrying: %s' % e) + self.redraw = True + self.timeout = self.ctrl.ioloop.call_later(1, self.update_screen) def update(self, msg): @@ -31,24 +104,31 @@ class LCD: state = v.get('x', 'INIT') if 'c' in v and state == 'RUNNING': state = v['c'] - self.lcd.text('%-9s' % state, 0, 0) + self.text('%-9s' % state, 0, 0) - if 'xp' in msg: self.lcd.text('% 10.4fX' % msg['xp'], 9, 0) - if 'yp' in msg: self.lcd.text('% 10.4fY' % msg['yp'], 9, 1) - if 'zp' in msg: self.lcd.text('% 10.4fZ' % msg['zp'], 9, 2) - if 'ap' in msg: self.lcd.text('% 10.4fA' % msg['ap'], 9, 3) - if 't' in msg: self.lcd.text('%2uT' % msg['t'], 6, 1) - if 'u' in msg: self.lcd.text('%s' % msg['u'], 0, 1) - if 'f' in msg: self.lcd.text('%8uF' % msg['f'], 0, 2) - if 's' in msg: self.lcd.text('%8dS' % msg['s'], 0, 3) + if 'xp' in msg: self.text('% 10.4fX' % msg['xp'], 9, 0) + if 'yp' in msg: self.text('% 10.4fY' % msg['yp'], 9, 1) + if 'zp' in msg: self.text('% 10.4fZ' % msg['zp'], 9, 2) + if 'ap' in msg: self.text('% 10.4fA' % msg['ap'], 9, 3) + if 't' in msg: self.text('%2uT' % msg['t'], 6, 1) + if 'u' in msg: self.text('%s' % msg['u'], 0, 1) + if 'f' in msg: self.text('%8uF' % msg['f'], 0, 2) + if 's' in msg: self.text('%8dS' % msg['s'], 0, 3) def goodbye(self): - if self.lcd is None: return + if self.timeout: + self.ctrl.ioloop.remove_timeout(self.timeout) + self.timeout = None - try: - self.lcd.clear() - self.lcd.display(1, 'Goodbye', lcd.JUSTIFY_CENTER) + if self.redraw_timer: + self.redraw_timer.stop() + self.redraw_timer = None - except IOError as e: - log.error('I2C communication failed: %s' % e) + if self.lcd is not None: + try: + self.lcd.clear() + self.lcd.display(1, 'Goodbye', lcd.JUSTIFY_CENTER) + + except IOError as e: + log.error('LCD communication failed: %s' % e) diff --git a/src/py/lcd/__init__.py b/src/py/lcd/__init__.py index 64648fe..5094eb9 100755 --- a/src/py/lcd/__init__.py +++ b/src/py/lcd/__init__.py @@ -148,11 +148,14 @@ class LCD: self.write(LCD_SET_DDRAM_ADDR | (0, 64, 20, 84)[y] + int(x)) + def put_char(self, c): + self.write(ord(c), REG_SELECT_BIT) + + def text(self, msg, x = None, y = None): if x is not None and y is not None: self.goto(x, y) - for c in msg: - self.write(ord(c), REG_SELECT_BIT) + for c in msg: self.put_char(c) def display(self, line, msg, justify = JUSTIFY_LEFT): -- 2.27.0