-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhd44780.py
More file actions
141 lines (119 loc) · 5.14 KB
/
Copy pathhd44780.py
File metadata and controls
141 lines (119 loc) · 5.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
class HD44780:
# Memory layout (172 bytes total):
# Offset 0: BF (bit 7) + AC (bits 6-0)
# Offset 1: Entry mode: ------IS (I/D=bit1, S=bit0)
# Offset 2: Display control: -----DCB (D=bit2, C=bit1, B=bit0)
# Offset 3: Function set: -----NF- (N=bit2, F=bit1) (DL always 8-bit)
# Offset 4-107: DDRAM (104 bytes, covers 4-line addrs 0x00-0x67)
# Offset 108-171: CGRAM (64 bytes)
# Internal register offsets
_REG_BF_AC = 0
_REG_ENTRY_MODE = 1
_REG_DISPLAY_CTRL = 2
_REG_FUNCTION_SET = 3
_REG_DDRAM_START = 4
_REG_CGRAM_START = 108 # _REG_DDRAM_START + _DDRAM_SIZE
# Sizes
_DDRAM_SIZE = 104
_CGRAM_SIZE = 64
_TOTAL_SIZE = 172 # 4 registers + 104 DDRAM + 64 CGRAM
# BF/AC register masks
_MASK_BF = 0x80
_MASK_AC = 0x7F
# Entry mode bits (register 1)
_BIT_ID = 0x02 # Increment/Decrement: 1=increment, 0=decrement
_BIT_S = 0x01 # Shift display on write
# Display control bits (register 2)
_BIT_D = 0x04 # Display on/off
_BIT_C = 0x02 # Cursor on/off
_BIT_B = 0x01 # Cursor blink on/off
# Function set bits (register 3)
_BIT_N = 0x04 # Number of lines: 1=2 lines, 0=1 line
_BIT_F = 0x02 # Font: 1=5x10, 0=5x8
# Instruction detection masks (high bits)
_INST_CLEAR = 0x01
_INST_HOME = 0x02
_INST_ENTRY_MODE = 0x04
_INST_DISPLAY_CTRL = 0x08
_INST_SHIFT = 0x10
_INST_FUNCTION_SET = 0x20
_INST_SET_CGRAM = 0x40
_INST_SET_DDRAM = 0x80
# DDRAM line start addresses (for 4-line display)
_DDRAM_LINE_ADDR = [0x00, 0x40, 0x14, 0x54]
# Display dimensions
LINES = 4
COLS = 20
def __init__(self):
self._mem = bytearray(self._TOTAL_SIZE)
# Power-on defaults per HD44780 datasheet:
# - Display off, cursor off, blink off
# - Entry mode: increment, no shift
# - Function set: 8-bit, 1-line, 5x8 font (we override to 2-line for 4-line displays)
# - AC = 0
# - DDRAM filled with spaces (0x20)
self._mem[self._REG_BF_AC] = 0x00 # BF=0, AC=0
self._mem[self._REG_ENTRY_MODE] = self._BIT_ID # Increment mode
self._mem[self._REG_DISPLAY_CTRL] = 0x00 # Display off
self._mem[self._REG_FUNCTION_SET] = self._BIT_N # 2-line mode for 4-line display
# Fill DDRAM with spaces
for i in range(self._DDRAM_SIZE):
self._mem[self._REG_DDRAM_START + i] = 0x20
# CGRAM starts zeroed (blank custom chars)
# Track whether we're addressing CGRAM or DDRAM
self._addressing_cgram = False
def get_display_lines(self):
for ddram_addr in self._DDRAM_LINE_ADDR:
addr_data = self._mem[self._REG_DDRAM_START + ddram_addr:
self._REG_DDRAM_START + ddram_addr + 20]
print(bytes(addr_data).decode('ascii'))
def write_data(self, value):
ac_value = self._mem[self._REG_BF_AC] & self._MASK_AC
if self._addressing_cgram:
self._mem[self._REG_CGRAM_START + ac_value] = value
else:
self._mem[self._REG_DDRAM_START + ac_value] = value
if self._mem[self._REG_ENTRY_MODE] & self._BIT_ID:
ac_value += 1
else:
ac_value -= 1
self._mem[self._REG_BF_AC] = (
self._mem[self._REG_BF_AC] & self._MASK_BF
) | (ac_value & self._MASK_AC)
def execute_instructions(self, command):
if command & 0x80: # Set DDRAM address
new_address = command & 0x7F
self._mem[self._REG_BF_AC] = (
self._mem[self._REG_BF_AC] & self._MASK_BF
) | new_address
self._addressing_cgram = False
elif command & 0x40: # Set CGRAM address
new_address = command & 0x3F
self._mem[self._REG_BF_AC] = (
self._mem[self._REG_BF_AC] & self._MASK_BF
) | new_address
self._addressing_cgram = True
elif command & 0x20: # Function set — N=bit3, F=bit2 of command
self._mem[self._REG_FUNCTION_SET] = (command & 0x0C) >> 1
elif command & 0x10: # Cursor/display shift
right = bool(command & 0x04)
display_shift = bool(command & 0x08)
if not display_shift:
ac = self._mem[self._REG_BF_AC] & self._MASK_AC
ac = (ac + (1 if right else -1)) & self._MASK_AC
self._mem[self._REG_BF_AC] = (
self._mem[self._REG_BF_AC] & self._MASK_BF
) | ac
elif command & 0x08: # Display on/off control
self._mem[self._REG_DISPLAY_CTRL] = command & 0x07
elif command & 0x04: # Entry mode set
self._mem[self._REG_ENTRY_MODE] = command & 0x03
elif command & 0x02: # Return home
self._mem[self._REG_BF_AC] = 0x00
self._addressing_cgram = False
elif command == 0x01: # Clear display
for i in range(self._DDRAM_SIZE):
self._mem[self._REG_DDRAM_START + i] = 0x20
self._mem[self._REG_BF_AC] &= self._MASK_BF
self._mem[self._REG_ENTRY_MODE] |= self._BIT_ID
self._addressing_cgram = False