70 lines
1.9 KiB
Python
70 lines
1.9 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
|
|
|
|
LCD_E_CLOCK_STATUS = 0xF200
|
|
LCD_E_CLOCK_DATA = 0xF201
|
|
|
|
LCD_LINE_WIDTH = 16
|
|
LCD_LINE_STARTS = (0x00, 0x40, 0x10, 0x50)
|
|
LCD_DDRAM_SIZE = 0x80
|
|
|
|
|
|
@dataclass
|
|
class LCD:
|
|
ddram: bytearray = field(default_factory=lambda: bytearray([0x20] * LCD_DDRAM_SIZE))
|
|
cursor: int = 0
|
|
data_latch: int = 0
|
|
command_latch: int = 0
|
|
|
|
def read_status(self) -> int:
|
|
return self.cursor & 0x7F
|
|
|
|
def read_data(self) -> int:
|
|
return self.data_latch & 0xFF
|
|
|
|
def write_command(self, value: int) -> None:
|
|
value &= 0xFF
|
|
self.command_latch = value
|
|
if value & 0x80:
|
|
self.cursor = value & 0x7F
|
|
elif value == 0x01:
|
|
self.ddram[:] = bytes([0x20]) * LCD_DDRAM_SIZE
|
|
self.cursor = 0
|
|
elif value == 0x02:
|
|
self.cursor = 0
|
|
|
|
def write_data(self, value: int) -> None:
|
|
value &= 0xFF
|
|
self.data_latch = value
|
|
if 0 <= self.cursor < LCD_DDRAM_SIZE:
|
|
self.ddram[self.cursor] = value
|
|
self.cursor = (self.cursor + 1) & 0x7F
|
|
|
|
def line_text(self, line: int, width: int = LCD_LINE_WIDTH) -> str:
|
|
if not 0 <= line < len(LCD_LINE_STARTS):
|
|
raise ValueError(f"LCD line out of range: {line}")
|
|
start = LCD_LINE_STARTS[line]
|
|
return "".join(_display_char(self.ddram[(start + offset) & 0x7F]) for offset in range(width))
|
|
|
|
def display_lines(self, lines: int = 4, width: int = LCD_LINE_WIDTH) -> list[str]:
|
|
return [self.line_text(line, width) for line in range(lines)]
|
|
|
|
def display_text(self, lines: int = 4, width: int = LCD_LINE_WIDTH) -> str:
|
|
return " | ".join(self.display_lines(lines, width))
|
|
|
|
|
|
def _display_char(value: int) -> str:
|
|
return chr(value) if 0x20 <= value <= 0x7E else "."
|
|
|
|
|
|
__all__ = [
|
|
"LCD",
|
|
"LCD_DDRAM_SIZE",
|
|
"LCD_E_CLOCK_DATA",
|
|
"LCD_E_CLOCK_STATUS",
|
|
"LCD_LINE_STARTS",
|
|
"LCD_LINE_WIDTH",
|
|
]
|