-
Notifications
You must be signed in to change notification settings - Fork 0
/
lcd.S
170 lines (145 loc) · 3.76 KB
/
lcd.S
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include <avr/io.h>
#include "macros.i"
; Two shift registers control inputs to the LCD:
; - The first is the flag register. See the flag constants below.
; - The second is the address register. The MSB is the only LCD pin that is
; readable by the MCU, and is available on PB0.
#define LCD_BUSY_PIN 0
#define SER_DATA_PIN 1
#define SER_CLK_PIN 2
#define SER_LATCH_PIN 3
; == LCD FLAG REGISTER FLAGS ==
; LCD_E is the enable bit for sending data to the LCD. Reads are triggered by a
; rising edge, writes are triggered by a falling edge.
defconst "LCD_E",,LCD_E,0x80
; When LCD_RW_READ is set, puts the LCD into a read state. Unset = write.
; Setting the LCD_RW_READ bit also turns off the address register outputs so
; the pins are in a high impedence state and don't short :P
defconst "LCD_RW_READ",,LCD_RW_READ,0x40
; When LCD_RS_DR is set, selects the data register (DR). When unset, selects
; the instruction register (IR).
defconst "LCD_RS_DR",,LCD_RS_DR,0x20
; Drives an LED when set.
defconst "LCD_LED",,LCD_LED,0x10
; Must be called before any LCD operations.
defcode "SERINIT",,SERINIT
cbi _SFR_IO_ADDR(DDRB), LCD_BUSY_PIN
sbi _SFR_IO_ADDR(PORTB), LCD_BUSY_PIN
; CLK, LATCH, and OUT are always outputs.
sbi _SFR_IO_ADDR(DDRB), SER_CLK_PIN
sbi _SFR_IO_ADDR(DDRB), SER_LATCH_PIN
sbi _SFR_IO_ADDR(DDRB), SER_DATA_PIN
; Init all to low.
cbi _SFR_IO_ADDR(PORTB), SER_CLK_PIN
cbi _SFR_IO_ADDR(PORTB), SER_LATCH_PIN
cbi _SFR_IO_ADDR(PORTB), SER_DATA_PIN
rjmp NEXT
defcode "SERLATCH",,SERLATCH
cbi _SFR_IO_ADDR(PORTB), SER_LATCH_PIN
sbi _SFR_IO_ADDR(PORTB), SER_LATCH_PIN
rjmp NEXT
; Blink latch pin indefinitely.
; Useful to call from ASM when the kernel might be dead.
defcode "LATCHBLINK",,LATCHBLINK
ldi r26, 0
ldi r27, 0
ldi r18, 0
1:
adiw r26, 1
brne 1b ; loop if not 0
inc r18
sbrs r18, 3
rjmp 2f
sbi _SFR_IO_ADDR(PORTB), SER_LATCH_PIN
rjmp 1b
2:
cbi _SFR_IO_ADDR(PORTB), SER_LATCH_PIN
rjmp 1b
; Must set the LCD to LCD_RW_READ first.
defcode "LCDBUSY",,LCDBUSY
ldi r16, 0
sbic _SFR_IO_ADDR(PINB), LCD_BUSY_PIN
ldi r16, 0xFF ; port is busy
mov r17, r16
PUSHDSP 16
rjmp NEXT
defcode "B>SER",,BYTETOSER
POPDSP 0
; Load the byte into the data register.
out _SFR_IO_ADDR(USIDR), r0
; Reset the clock counter.
ldi r16, _BV(USIOIF)
out _SFR_IO_ADDR(USISR), r16
; Pulse clock until sent.
ldi r16, _BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC)
1:
out _SFR_IO_ADDR(USICR), r16
sbis _SFR_IO_ADDR(USISR), USIOIF
rjmp 1b
; Turn off USI.
ldi r16, 0
out _SFR_IO_ADDR(USICR), r16
rjmp NEXT
; Writes a word to the shift registers.
defword ">SER",,TOSER
; High byte first.
fw DUP
fw SWIZZLE
fw BYTETOSER
; Then the low byte.
fw BYTETOSER
fw SERLATCH
fw EXIT
defword "LCDWAIT",,LCDWAIT
1:
fw LCD_RW_READ
fw TOSER
fw LCD_RW_READ
fw LCD_E
fw OR
fw TOSER
fw LCDBUSY
fw ZEQU
zbranch 1b
; Bring LCD_E low so it's in a consistent state.
fw LCD_RW_READ
fw TOSER
fw EXIT
; Writes an LCD command to the shift register and handles the clocking of E.
defword "LCDCMD",,LCDCMD
fw LCDWAIT ; Wait for free.
fw DUP
fw LCD_E
fw OR
fw TOSER ; Clock E high first (should already be low).
fw LCD_E
fw INVERT
fw AND
fw TOSER ; Then low.
fw EXIT
defword "LCDINIT",,LCDINIT
; 8 bits, 2 lines, 5x8
lit 0x3800
fw LCDCMD
; clear the screen
lit 0x0100
fw LCDCMD
; Enable screen, cursor, and blinking
lit 0x0F00
fw LCDCMD
fw LCDDISABLE
fw EXIT
; Hold the address lines high so the busy pin can be used by other devices.
defword "LCDDISABLE",,LCDDISABLE
lit 0xFF00
fw TOSER
fw EXIT
defword "LCDCLEAR",,LCDCLEAR
lit 0x0100
fw LCDCMD
fw EXIT
; Move the cursor to the beginning of the second line.
defword "LCDLINE",,LCDLINE
lit 0xC000
fw LCDCMD
fw EXIT