generated from ImperialCollegeLondon/MicroprocessorsLab
-
Notifications
You must be signed in to change notification settings - Fork 0
/
LCD.asm
285 lines (256 loc) · 6.44 KB
/
LCD.asm
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
#include p18f87k22.inc
; Globals section
global LCD_setup, LCD_Write_Message, LCD_Write_Message_TBLPTR, LCD_Send_Byte_D, LCD_goto_pos, LCD_clear, m16L, m16H, LCD_hextodec
; Reserving space in RAM
acs0 udata_acs ; named variables in access ram
LCD_cnt_l res 1 ; reserve 1 byte for variable LCD_cnt_l
LCD_cnt_h res 1 ; reserve 1 byte for variable LCD_cnt_h
LCD_cnt_ms res 1 ; reserve 1 byte for ms counter
LCD_tmp res 1 ; reserve 1 byte for temporary use
LCD_counter res 1 ; reserve 1 byte for counting through message
; Variables for multiplication
m8 res 1
m8_2 res 1
m16L res 1
m16H res 1
m24U res 1
m24H res 1
m24L res 1
m32L res 1
m32H res 1
m32U res 1
m32UU res 1
; Constants
constant LCD_E=5 ; LCD enable bit
constant LCD_RS=4 ; LCD register select bit
; Put this somewhere in Program memory
LCD code
LCD_setup
clrf LATB
movlw b'11000000' ; RB0:5 all outputs
movwf TRISB
movlw .40
call LCD_delay_ms ; wait 40ms for LCD to start up properly
movlw b'00110000' ; Function set 4-bit
call LCD_Send_Byte_I
movlw .10 ; wait 40us
call LCD_delay_x4us
movlw b'00101000' ; 2 line display 5x8 dot characters
call LCD_Send_Byte_I
movlw .10 ; wait 40us
call LCD_delay_x4us
movlw b'00101000' ; repeat, 2 line display 5x8 dot characters
call LCD_Send_Byte_I
movlw .10 ; wait 40us
call LCD_delay_x4us
movlw b'00001111' ; display on, cursor on, blinking on
call LCD_Send_Byte_I
movlw .10 ; wait 40us
call LCD_delay_x4us
movlw b'00000001' ; display clear
call LCD_Send_Byte_I
movlw .2 ; wait 2ms
call LCD_delay_ms
movlw b'00000110' ; entry mode incr by 1 no shift
call LCD_Send_Byte_I
movlw .10 ; wait 40us
call LCD_delay_x4us
return
LCD_Write_Message ; Message stored at FSR2, length stored in W
movwf LCD_counter
LCD_Loop_message
movf POSTINC2, W
call LCD_Send_Byte_D
decfsz LCD_counter
bra LCD_Loop_message
return
LCD_Write_Message_TBLPTR ; Message stored at TBLPTR, length stored in W
movwf LCD_counter
LCD_Loop_message_TBLPTR
tblrd*+
movf TABLAT, W
call LCD_Send_Byte_D
decfsz LCD_counter
bra LCD_Loop_message_TBLPTR
return
LCD_goto_pos
movwf LCD_tmp
movlw b'10000000'
iorwf LCD_tmp, W ; Command for position is 1ppppppp, where p are bits describing position address
call LCD_Send_Byte_I
movlw .10 ; wait 40us
call LCD_delay_x4us
return
LCD_clear
movlw b'00000001'
call LCD_Send_Byte_I
movlw .2 ; wait 2ms
call LCD_delay_ms
return
LCD_Send_Byte_I ; Transmits byte stored in W to instruction reg
movwf LCD_tmp
swapf LCD_tmp,W ; swap nibbles, high nibble goes first
andlw 0x0f ; select just low nibble
movwf LATB ; output data bits to LCD
bcf LATB, LCD_RS ; Instruction write clear RS bit
call LCD_Enable ; Pulse enable Bit
movf LCD_tmp,W ; swap nibbles, now do low nibble
andlw 0x0f ; select just low nibble
movwf LATB ; output data bits to LCD
bcf LATB, LCD_RS ; Instruction write clear RS bit
call LCD_Enable ; Pulse enable Bit
return
LCD_Send_Byte_D ; Transmits byte stored in W to data reg
movwf LCD_tmp
swapf LCD_tmp,W ; swap nibbles, high nibble goes first
andlw 0x0f ; select just low nibble
movwf LATB ; output data bits to LCD
bsf LATB, LCD_RS ; Data write set RS bit
call LCD_Enable ; Pulse enable Bit
movf LCD_tmp,W ; swap nibbles, now do low nibble
andlw 0x0f ; select just low nibble
movwf LATB ; output data bits to LCD
bsf LATB, LCD_RS ; Data write set RS bit
call LCD_Enable ; Pulse enable Bit
movlw .10 ; delay 40us
call LCD_delay_x4us
return
LCD_Enable ; pulse enable bit LCD_E for 500ns
nop
nop
nop
nop
nop
nop
nop
nop
bsf LATB, LCD_E ; Take enable high
nop
nop
nop
nop
nop
nop
nop
bcf LATB, LCD_E ; Writes data to LCD
return
; ** a few delay routines below here as LCD timing can be quite critical ****
LCD_delay_ms ; delay given in ms in W
movwf LCD_cnt_ms
lcdlp2 movlw .250 ; 1 ms delay
call LCD_delay_x4us
decfsz LCD_cnt_ms
bra lcdlp2
return
LCD_delay_x4us ; delay given in chunks of 4 microsecond in W
movwf LCD_cnt_l ; now need to multiply by 16
swapf LCD_cnt_l,F ; swap nibbles
movlw 0x0f
andwf LCD_cnt_l,W ; move low nibble to W
movwf LCD_cnt_h ; then to LCD_cnt_h
movlw 0xf0
andwf LCD_cnt_l,F ; keep high nibble in LCD_cnt_l
call LCD_delay
return
LCD_delay ; delay routine 4 instruction loop == 250ns
movlw 0x00 ; W=0
lcdlp1 decf LCD_cnt_l,F ; no carry when 0x00 -> 0xff
subwfb LCD_cnt_h,F ; no carry when 0x00 -> 0xff
bc lcdlp1 ; carry, then loop again
return ; carry reset so return
; Hex to decimal conversion multiplication helper functions
; Those mostly involve using mulwf and many temporary variables
; Multiply as 16 bit number by an 8 bit one
mul8x16
movf m8,W
mulwf m16L
movff PRODL,m24L
movff PRODH,m24H
mulwf m16H
movf PRODL,W
addwf m24H,f
movff PRODH,m24U
movlw 0
addwfc m24U,f
return
; Multiply two 16 bit numbers
mul16x16
call mul8x16
movff m24L,m32L
movff m24H,m32H
movff m24U,m32U
movff m8_2,m8
call mul8x16
movf m24L,W
addwf m32H,f
movf m24H,W
addwfc m32U,f
movff m24U,m32UU
movlw 0
addwfc m32UU,f
return
; Multiply a 24 bit bumber by an 8 bit one
mul8x24
movf m8,W
mulwf m24L
movff PRODL,m32L
movff PRODH,m32H
mulwf m24H
movf PRODL,W
addwf m32H,f
movff PRODH,m32U
movlw 0
addwfc m32U,f
movf m8,W
mulwf m24U
movf PRODL,W
addwf m32U
movff PRODH,m32UU
movlw 0
addwfc m32UU,f
return
; Convert and display a hexadecimal number in decimal
; Value to convert and display is stored in m16L and m16H
LCD_hextodec
movlw 0x41
movwf m8_2
movlw 0x8A
movwf m8
; Multiply the hex number by 16778 to get the first decimal digit
call mul16x16
movf m32UU,W
; We don't need the first digit for this project (max decimal length is 3)
;call LCD_display_digit
movlw 0x0A
movwf m8
movff m32U,m24U
movff m32H,m24H
movff m32L,m24L
; Multiply by 10 repeatedly to get and display further digits
call mul8x24
movf m32UU,W
call LCD_display_digit
movlw 0x0A
movwf m8
movff m32U,m24U
movff m32H,m24H
movff m32L,m24L
call mul8x24
movf m32UU,W
call LCD_display_digit
movlw 0x0A
movwf m8
movff m32U,m24U
movff m32H,m24H
movff m32L,m24L
call mul8x24
movf m32UU,W
call LCD_display_digit
return
; Display a number (0-9) stored in W on the screen
LCD_display_digit
; To go from int 1 to char "1" (etc) we need to add a 0x30 offset
addlw 0x30
call LCD_Send_Byte_D
return
end