From 2a73750034263012e067f156b9b5724a80ea2013 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sat, 10 Jun 2017 16:56:11 -0400 Subject: [PATCH 1/3] Minimal functionality: backlight on, no cursor, no shifting, etc. --- lcd/i2c_pcf8574_interface.py | 24 ++--------- lcd/lcd.py | 80 ++++-------------------------------- 2 files changed, 10 insertions(+), 94 deletions(-) diff --git a/lcd/i2c_pcf8574_interface.py b/lcd/i2c_pcf8574_interface.py index adcc622..763aa01 100644 --- a/lcd/i2c_pcf8574_interface.py +++ b/lcd/i2c_pcf8574_interface.py @@ -24,16 +24,13 @@ import board from adafruit_bus_device.i2c_device import I2CDevice -from .lcd import LCD_4BITMODE, LCD_BACKLIGHT, LCD_NOBACKLIGHT, PIN_ENABLE +from .lcd import PIN_ENABLE, LCD_BACKLIGHT MICROSECOND = 1e-6 MILLISECOND = 1e-3 class I2CPCF8574Interface: - # Bit values to turn backlight on/off. Indexed by a boolean. - _BACKLIGHT_VALUES = (LCD_NOBACKLIGHT, LCD_BACKLIGHT) - def __init__(self, address): """ CharLCD via PCF8574 I2C port expander. @@ -47,8 +44,6 @@ def __init__(self, address): """ self.address = address - self._backlight_pin_state = LCD_BACKLIGHT - self.i2c = busio.I2C(board.SCL, board.SDA) self.i2c_device = I2CDevice(self.i2c, self.address) self.data_buffer = bytearray(1) @@ -56,26 +51,13 @@ def __init__(self, address): def deinit(self): self.i2c.deinit() - @property - def data_bus_mode(self): - return LCD_4BITMODE - - @property - def backlight(self): - return self._backlight_pin_state == LCD_BACKLIGHT - - @backlight.setter - def backlight(self, value): - self._backlight_pin_state = _BACKLIGHT_VALUES[value] - self._i2c_write(self._backlight_pin_state) - # Low level commands def send(self, value, rs_mode): """Send the specified value to the display in 4-bit nibbles. The rs_mode is either ``_RS_DATA`` or ``_RS_INSTRUCTION``.""" - self._write4bits(rs_mode | (value & 0xF0) | self._backlight_pin_state) - self._write4bits(rs_mode | ((value << 4) & 0xF0) | self._backlight_pin_state) + self._write4bits(rs_mode | (value & 0xF0) | LCD_BACKLIGHT) + self._write4bits(rs_mode | ((value << 4) & 0xF0) | LCD_BACKLIGHT) def _write4bits(self, value): """Pulse the `enable` flag to process value.""" diff --git a/lcd/lcd.py b/lcd/lcd.py index 4788841..b5cd38f 100644 --- a/lcd/lcd.py +++ b/lcd/lcd.py @@ -26,16 +26,12 @@ _LCD_RETURNHOME = const(0x02) _LCD_ENTRYMODESET = const(0x04) _LCD_DISPLAYCONTROL = const(0x08) -_LCD_CURSORSHIFT = const(0x10) _LCD_FUNCTIONSET = const(0x20) _LCD_SETCGRAMADDR = const(0x40) _LCD_SETDDRAMADDR = const(0x80) # Flags for display entry mode -_LCD_ENTRYRIGHT = const(0x00) _LCD_ENTRYLEFT = const(0x02) -_LCD_ENTRYSHIFTINCREMENT = const(0x01) -_LCD_ENTRYSHIFTDECREMENT = const(0x00) # Flags for display on/off control _LCD_DISPLAYON = const(0x04) @@ -45,18 +41,10 @@ _LCD_BLINKON = const(0x01) _LCD_BLINKOFF = const(0x00) -# Flags for display/cursor shift -_LCD_DISPLAYMOVE = const(0x08) -_LCD_CURSORMOVE = const(0x00) -_LCD_MOVERIGHT = const(0x04) -_LCD_MOVELEFT = const(0x00) - # Flags for function set -_LCD_8BITMODE = const(0x10) LCD_4BITMODE = const(0x00) _LCD_2LINE = const(0x08) _LCD_1LINE = const(0x00) -_LCD_5x10DOTS = const(0x04) _LCD_5x8DOTS = const(0x00) # Flags for backlight control @@ -72,32 +60,21 @@ PIN_READ_WRITE = const(0x2) PIN_REGISTER_SELECT = const(0x1) -class CursorMode: - HIDE = const(_LCD_CURSOROFF | _LCD_BLINKOFF) - LINE = const(_LCD_CURSORON | _LCD_BLINKOFF) - BLINK = const(_LCD_CURSOROFF | _LCD_BLINKON) - MICROSECOND = 1e-6 MILLISECOND = 1e-3 class LCD(object): - def __init__(self, interface, num_cols=20, num_rows=4, char_height=8): + def __init__(self, interface, num_cols=20, num_rows=4): """ Character LCD controller. :param interface: Communication interface, such as I2CInterface :param num_rows: Number of display rows (usually 1, 2 or 4). Default: 4. :param num_cols: Number of columns per row (usually 16 or 20). Default 20. - :param char_height: Some 1 line displays allow a font height of 10px. - Allowed: 8 or 10. Default: 8. """ self.interface = interface - if char_height not in (8, 10): - raise ValueError('The ``char_height`` argument should be either 8 or 10.') - self.char_height = char_height - self.num_rows = num_rows self.num_cols = num_cols @@ -105,35 +82,22 @@ def __init__(self, interface, num_cols=20, num_rows=4, char_height=8): self._row_offsets = (0x00, 0x40, self.num_cols, 0x40 + self.num_cols) # Setup initial display configuration - displayfunction = self.interface.data_bus_mode | _LCD_5x8DOTS + displayfunction = LCD_4BITMODE | _LCD_5x8DOTS if self.num_rows == 1: displayfunction |= _LCD_1LINE elif self.num_rows in (2, 4): # LCD only uses two lines on 4 row displays displayfunction |= _LCD_2LINE - if self.char_height == 10: - # For some 1 line displays you can select a 10px font. - displayfunction |= _LCD_5x10DOTS - # Choose 4 or 8 bit mode + # Assume 4-bit mode self.command(0x03) time.sleep(4.5*MILLISECOND) self.command(0x03) time.sleep(4.5*MILLISECOND) self.command(0x03) - if self.interface.data_bus_mode == LCD_4BITMODE: - # Hitachi manual page 46 - time.sleep(100*MICROSECOND) - self.command(0x02) - elif self.interface.data_bus_mode == _LCD_8BITMODE: - # Hitachi manual page 45 - self.command(0x30) - time.sleep(4.5*MILLISECOND) - self.command(0x30) - time.sleep(100*MICROSECOND) - self.command(0x30) - else: - raise ValueError('Invalid data bus mode: {}'.format(self.interface.data_bus_mode)) + # Hitachi manual page 46 + time.sleep(100*MICROSECOND) + self.command(0x02) # Write configuration to display self.command(_LCD_FUNCTIONSET | displayfunction) @@ -144,9 +108,7 @@ def __init__(self, interface, num_cols=20, num_rows=4, char_height=8): time.sleep(50*MICROSECOND) # Configure display mode. Define internal fields. - self._display_mode = _LCD_DISPLAYON - self._cursor_mode = CursorMode.HIDE - self.command(_LCD_DISPLAYCONTROL | self._display_mode | self._cursor_mode) + self.command(_LCD_DISPLAYCONTROL | _LCD_DISPLAYON | _LCD_CURSOROFF | _LCD_BLINKOFF) time.sleep(50*MICROSECOND) self.clear() @@ -154,19 +116,6 @@ def __init__(self, interface, num_cols=20, num_rows=4, char_height=8): def close(self): self.interface.deinit() - def set_backlight(self, value): - self.interface.backlight = value - - def set_display_enabled(self, value): - self._display_mode = _LCD_DISPLAYON if value else _LCD_DISPLAYOFF - self.command(_LCD_DISPLAYCONTROL | self._display_mode | self._cursor_mode) - time.sleep(50*MICROSECOND) - - def set_cursor_mode(self, value): - self._cursor_mode = value - self.command(_LCD_DISPLAYCONTROL | self._display_mode | self._cursor_mode) - time.sleep(50*MICROSECOND) - def cursor_pos(self): """The cursor position as a 2-tuple (row, col).""" return (self._row, self._col) @@ -212,16 +161,6 @@ def home(self): self._col = 0 time.sleep(2*MILLISECOND) - def shift_display(self, amount): - """Shift the display. Use negative amounts to shift left and positive - amounts to shift right.""" - if amount == 0: - return - direction = _LCD_MOVERIGHT if amount > 0 else _LCD_MOVELEFT - for i in range(abs(amount)): - self.command(_LCD_CURSORSHIFT | _LCD_DISPLAYMOVE | direction) - time.sleep(50*MICROSECOND) - def create_char(self, location, bitmap): """Create a new character. @@ -253,11 +192,6 @@ def create_char(self, location, bitmap): >>> lcd.create_char(0, smiley) """ - if not (0 <= location <= 7): - raise ValueError('Only locations 0-7 are valid.') - if len(bitmap) != 8: - raise ValueError('Bitmap should have exactly 8 rows.') - # Store previous position save_row = self._row save_col = self._col From c0d30d7d47e52c8b2b8cc7c41c053d427d208399 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 15 Oct 2018 17:30:17 -0400 Subject: [PATCH 2/3] Code cleanup; add proper delays so it works on faster processors (e.g., M4) --- lcd/i2c_pcf8574_interface.py | 15 ++++----- lcd/lcd.py | 64 ++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/lcd/i2c_pcf8574_interface.py b/lcd/i2c_pcf8574_interface.py index 763aa01..0cf28a0 100644 --- a/lcd/i2c_pcf8574_interface.py +++ b/lcd/i2c_pcf8574_interface.py @@ -18,19 +18,17 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -import time +"""Low-level interface to PCF8574.""" import busio import board +import microcontroller from adafruit_bus_device.i2c_device import I2CDevice from .lcd import PIN_ENABLE, LCD_BACKLIGHT -MICROSECOND = 1e-6 -MILLISECOND = 1e-3 - class I2CPCF8574Interface: - + """Write to PCF8574.""" def __init__(self, address): """ CharLCD via PCF8574 I2C port expander. @@ -49,6 +47,7 @@ def __init__(self, address): self.data_buffer = bytearray(1) def deinit(self): + """Done using this interface.""" self.i2c.deinit() # Low level commands @@ -63,12 +62,12 @@ def _write4bits(self, value): """Pulse the `enable` flag to process value.""" with self.i2c_device: self._i2c_write(value & ~PIN_ENABLE) - time.sleep(MICROSECOND) + microcontroller.delay_us(1) self._i2c_write(value | PIN_ENABLE) - time.sleep(MICROSECOND) + microcontroller.delay_us(1) self._i2c_write(value & ~PIN_ENABLE) # Wait for command to complete. - time.sleep(100*MICROSECOND) + microcontroller.delay_us(100) def _i2c_write(self, value): self.data_buffer[0] = value diff --git a/lcd/lcd.py b/lcd/lcd.py index b5cd38f..273022c 100644 --- a/lcd/lcd.py +++ b/lcd/lcd.py @@ -20,6 +20,7 @@ import time from micropython import const +import microcontroller # Commands _LCD_CLEARDISPLAY = const(0x01) @@ -60,27 +61,26 @@ PIN_READ_WRITE = const(0x2) PIN_REGISTER_SELECT = const(0x1) -MICROSECOND = 1e-6 -MILLISECOND = 1e-3 - class LCD(object): def __init__(self, interface, num_cols=20, num_rows=4): """ Character LCD controller. - + :param interface: Communication interface, such as I2CInterface :param num_rows: Number of display rows (usually 1, 2 or 4). Default: 4. :param num_cols: Number of columns per row (usually 16 or 20). Default 20. """ self.interface = interface - + self.num_rows = num_rows self.num_cols = num_cols - + + self._row = self._col = 0 + # get row addresses (varies based on display size) self._row_offsets = (0x00, 0x40, self.num_cols, 0x40 + self.num_cols) - + # Setup initial display configuration displayfunction = LCD_4BITMODE | _LCD_5x8DOTS if self.num_rows == 1: @@ -89,27 +89,30 @@ def __init__(self, interface, num_cols=20, num_rows=4): # LCD only uses two lines on 4 row displays displayfunction |= _LCD_2LINE + # Wait at least 40ms on power-up + time.sleep(0.050) # Assume 4-bit mode self.command(0x03) - time.sleep(4.5*MILLISECOND) + time.sleep(0.0045) self.command(0x03) - time.sleep(4.5*MILLISECOND) + time.sleep(0.0045) self.command(0x03) # Hitachi manual page 46 - time.sleep(100*MICROSECOND) + microcontroller.delay_us(150) self.command(0x02) + time.sleep(0.003) # Write configuration to display self.command(_LCD_FUNCTIONSET | displayfunction) - time.sleep(50*MICROSECOND) + microcontroller.delay_us(50) # Configure entry mode. Define internal fields. self.command(_LCD_ENTRYMODESET | _LCD_ENTRYLEFT) - time.sleep(50*MICROSECOND) + microcontroller.delay_us(50) # Configure display mode. Define internal fields. self.command(_LCD_DISPLAYCONTROL | _LCD_DISPLAYON | _LCD_CURSOROFF | _LCD_BLINKOFF) - time.sleep(50*MICROSECOND) + microcontroller.delay_us(50) self.clear() @@ -121,18 +124,18 @@ def cursor_pos(self): return (self._row, self._col) def set_cursor_pos(self, row, col): - if not (0 <= row < self.num_rows): - raise ValueError('row should be in range 0-{}'.format(self.num_rows - 1)) - if not (0 <= col < self.num_cols): - raise ValueError('col should be in range 0-{}'.format(self.num_cols - 1)) + if not 0 <= row < self.num_rows: + raise ValueError('row not in range 0-{}'.format(self.num_rows - 1)) + if not 0 <= col < self.num_cols: + raise ValueError('col not in range 0-{}'.format(self.num_cols - 1)) self._row = row self._col = col self.command(_LCD_SETDDRAMADDR | self._row_offsets[row] + col) - time.sleep(50*MICROSECOND) + microcontroller.delay_us(50) - def print(self, string): + def print(self, *strings): """ - Write the specified unicode string to the display. + Write the specified unicode strings to the display. A newline ('\n') will advance to the left side of the next row. Lines that are too long automatically continue on next line. @@ -140,18 +143,19 @@ def print(self, string): supported. """ - for char in string: - if char == '\n': - # Advance to next row, at left side. Wrap around to top row if at bottom. - self.set_cursor_pos((self._row + 1) % self.num_rows, 0) - else: - self.write(ord(char)) + for string in strings: + for char in string: + if char == '\n': + # Advance to next row, at left side. Wrap around to top row if at bottom. + self.set_cursor_pos((self._row + 1) % self.num_rows, 0) + else: + self.write(ord(char)) def clear(self): """Overwrite display with blank characters and reset cursor position.""" self.command(_LCD_CLEARDISPLAY) - time.sleep(2*MILLISECOND) + time.sleep(0.003) self.home() def home(self): @@ -159,7 +163,7 @@ def home(self): self.command(_LCD_RETURNHOME) self._row = 0 self._col = 0 - time.sleep(2*MILLISECOND) + time.sleep(0.002) def create_char(self, location, bitmap): """Create a new character. @@ -200,6 +204,7 @@ def create_char(self, location, bitmap): self.command(_LCD_SETCGRAMADDR | location << 3) for row in bitmap: self.interface.send(row, _RS_DATA) + microcontroller.delay_us(50) # Restore cursor pos self.set_cursor_pos(save_row, save_col) @@ -207,10 +212,12 @@ def create_char(self, location, bitmap): def command(self, value): """Send a raw command to the LCD.""" self.interface.send(value, _RS_INSTRUCTION) + microcontroller.delay_us(50) def write(self, value): """Write a raw character byte to the LCD.""" self.interface.send(value, _RS_DATA) + microcontroller.delay_us(50) if self._col < self.num_cols - 1: # Char was placed on current line. No need to reposition cursor. self._col += 1 @@ -220,4 +227,3 @@ def write(self, value): self._col = 0 self.set_cursor_pos(self._row, self._col) - From 0a4da76a7fcd01e5eaf35fa14c304e0329736fe0 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 19 May 2021 09:04:42 -0400 Subject: [PATCH 3/3] add i2c arg to constructor; fix brightness setter --- lcd/i2c_pcf8574_interface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lcd/i2c_pcf8574_interface.py b/lcd/i2c_pcf8574_interface.py index 0cf28a0..923770d 100644 --- a/lcd/i2c_pcf8574_interface.py +++ b/lcd/i2c_pcf8574_interface.py @@ -29,7 +29,7 @@ class I2CPCF8574Interface: """Write to PCF8574.""" - def __init__(self, address): + def __init__(self, i2c, address): """ CharLCD via PCF8574 I2C port expander. @@ -42,7 +42,7 @@ def __init__(self, address): """ self.address = address - self.i2c = busio.I2C(board.SCL, board.SDA) + self.i2c = i2c self.i2c_device = I2CDevice(self.i2c, self.address) self.data_buffer = bytearray(1) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy