Last active
December 30, 2021 15:31
-
-
Save EarlGray/f6a0c7ddf470d56b12aebb131620de39 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
This is a driver for Gravity 16x2 LCD (single-color, not RGB) for Raspberry Pi. | |
It is tested on Raspberry Pi Zero W with | |
- i2c_arm,baudrate=100000 | |
- i2c_arm,baudrate=400000 | |
- dtoverlay=i2c-gpio,bus=3,i2c_gpio_delay_us=1 | |
""" | |
from enum import IntFlag, IntEnum | |
import itertools | |
import time | |
import argparse | |
import smbus | |
class GravityBacklight: | |
REG_MODE1 = 0x00 | |
REG_MODE2 = 0x01 | |
REG_ONLY = 0x02 | |
REG_OUTPUT = 0x08 | |
def __init__(self, bus, light_addr=0x60): | |
self.bus = bus | |
self.addr = light_addr | |
def _cmd(self, reg, val): | |
self.bus.write_i2c_block_data(self.addr, reg, [val]) | |
def init(self, brightness=0): | |
self._cmd(self.REG_MODE1, 0x00) # backlight init | |
self._cmd(self.REG_OUTPUT, 0xff) # PWM | GRPPWM | |
self._cmd(self.REG_MODE2, 0x20) # blinkiness something | |
self.set_brightness(brightness) | |
def set_brightness(self, brightness): | |
assert brightness in range(256) | |
self._cmd(self.REG_ONLY, brightness) | |
def off(self): | |
self.set_brightness(0) | |
class EntryMode(IntEnum): | |
DECR = 0x00 | |
INCR = 0x02 | |
class LCDLines(IntEnum): | |
ONE = 0x00 | |
TWO = 0x08 | |
class Font(IntEnum): | |
_5x8 = 0x00 | |
_5x10 = 0x04 | |
class ShiftOf(IntEnum): | |
CURSOR = 0x00 | |
DISPLAY = 0x08 | |
class ShiftTo(IntEnum): | |
LEFT = 0x00 | |
RIGHT = 0x04 | |
class AIP31086L: | |
""" | |
An AIP31068L driver | |
See: https://seeeddoc.github.io/Grove-LCD_RGB_Backlight/res/JHD1214Y_YG_1.0.pdf | |
""" | |
ROW_0_OFFSET = 0x00 | |
ROW_1_OFFSET = 0x40 | |
def __init__(self, bus, addr=0x3e): | |
self.addr = addr | |
self.bus = bus | |
def init(self, lines: LCDLines): | |
self._do_function_set(lines=lines, font=Font._5x8) | |
self._do_display_control(on=True, cursor=False) | |
self._do_clear_display() | |
self._do_entry_mode(shift=False) | |
def close(self): | |
self._do_clear_display() | |
self._do_display_control(on=False) | |
def _cmd(self, code, co=False): | |
# RS=0 | |
cmd = (int(co) << 7) | |
self.bus.write_i2c_block_data(self.addr, cmd, [code]) | |
def _set(self, value, co=False): | |
# RS=1 | |
cmd = (int(co) << 7) | (1 << 6) | |
self.bus.write_i2c_block_data(self.addr, cmd, [value]) | |
def _get(self, co=False): | |
# RS=1 | |
return self.bus.read_byte(self.addr) | |
def _do_clear_display(self): | |
self._cmd(0x01) | |
time.sleep(0.0016) | |
def _do_cursor_home(self): | |
self._cmd(0x02) | |
time.sleep(0.0016) | |
def _do_entry_mode(self, shift: bool = True, mode: EntryMode = EntryMode.INCR): | |
self._cmd(0x04 | int(mode) | int(shift)) | |
# time.sleep(39usec) | |
def _do_display_control(self, on=True, cursor=True, blink=False): | |
self._cmd(0x08 | (int(on) << 2) | (int(cursor) << 1) | int(blink)) | |
# time.sleep(39usec) | |
def _do_set_shift(self, of=ShiftOf.DISPLAY, to=ShiftTo.RIGHT): | |
""" of='display'|'cursor', to='left'|'right' """ | |
self._cmd(0x10 | int(of) | int(to)) | |
# time.sleep(39usec) | |
def _do_function_set(self, lines=LCDLines.TWO, font=Font._5x8): | |
dl = 1 | |
self._cmd(0x20 | (int(dl) << 4) | int(lines) | int(font)) | |
# time.sleep(39usec) | |
def _do_cgram_addr(self, cgaddr): | |
self._cmd(0x40 | cgaddr) | |
def _do_ddram_addr(self, ddaddr): | |
self._cmd(0x80 | ddaddr) | |
class LCD: | |
def __init__(self, busno=1, addr=0x3e, light_addr=0x60): | |
bus = smbus.SMBus(bus=busno) | |
self.screen = AIP31086L(bus=bus, addr=addr) | |
self.screen.init(LCDLines.TWO) | |
self.light = GravityBacklight(bus=bus, light_addr=light_addr) | |
self.light.init(brightness=0x40) | |
def close(self): | |
self.screen.close() | |
self.light.off() | |
def clear(self): | |
self._do_clear_display() | |
self._do_cursor_home() | |
def cursor(self, on=True): | |
self.screen._do_display_control(on=True, cursor=on) | |
def cursor_forward(self): | |
self.screen._do_set_shift(ShiftOf.CURSOR, ShiftTo.RIGHT) | |
def cursor_back(self): | |
self.screen._do_set_shift(ShiftOf.CURSOR, ShiftTo.LEFT) | |
def cursor_home(self): | |
self.screen._do_cursor_home() | |
def scroll_forward(self): | |
self.screen._do_set_shift(ShiftOf.DISPLAY, ShiftTo.LEFT) | |
def scroll_back(self): | |
self.screen._do_set_shift(ShiftOf.DISPLAY, ShiftTo.RIGHT) | |
def __setitem__(self, key, value): | |
if isinstance(key, int): | |
if key == 0: | |
offset = AIP31086L.ROW_0_OFFSET | |
elif key == 1: | |
offset = AIP31086L.ROW_1_OFFSET | |
else: | |
raise ValueError('Only two lines supported') | |
for i, c in enumerate(itertools.islice(value, 40)): | |
if isinstance(c, str): | |
c = ord(c) | |
elif isinstance(c, int): | |
assert c in range(256) | |
self.screen._do_ddram_addr(offset+i) | |
self.screen._set(c) | |
else: | |
raise IndexError('Unknown key type %s' % type(key)) | |
def __enter__(self): | |
return self | |
def __exit__(self, exc_type, exc_value, traceback): | |
self.close() | |
return False | |
args = argparse.ArgumentParser(description='Gravity LCD driver test program') | |
args.add_argument( | |
'-b', '--bus', metavar='BUS', | |
type=int, default=1, | |
help="i2c bus") | |
args.add_argument( | |
'-a', '--addr', metavar='LCDADDR', | |
type=int, default=0x3E, | |
help="i2c address of LCD") | |
args.add_argument( | |
'-l', '--light-addr', metavar='LIGHTADDR', | |
type=int, default=0x60, | |
help="i2c address of backlight") | |
args.add_argument( | |
'-t', '--timeout', | |
type=int, default=3, | |
help="display text for TIMEOUT seconds") | |
args.add_argument( | |
'-B', '--brightness', | |
type=int, default=0x40, | |
help="backlight brightness (0..255)") | |
args.add_argument( | |
'line1', help='Text in the first line') | |
args.add_argument( | |
'line2', | |
nargs='?', default="", | |
help='Text in the second line') | |
if __name__ == '__main__': | |
flags = args.parse_args() | |
with LCD( | |
busno=flags.bus, | |
addr=flags.addr, | |
light_addr=flags.light_addr | |
) as lcd: | |
lcd.light.set_brightness(flags.brightness) | |
lcd[0] = flags.line1 | |
lcd[1] = flags.line2 | |
time.sleep(flags.timeout) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment