-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUART.s
376 lines (359 loc) · 19.3 KB
/
UART.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
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
; UART.s
; Runs on LM4F120/TM4C123
; Use UART0 to implement bidirectional data transfer to and from a
; computer running HyperTerminal. This time, interrupts and FIFOs
; are used.
; This file is named "UART2" because it is the second UART example.
; It is not related to the UART2 module on the microcontroller.
; Daniel Valvano
; September 12, 2013
; This example accompanies the book
; "Embedded Systems: Real Time Interfacing to Arm Cortex M Microcontrollers",
; ISBN: 978-1463590154, Jonathan Valvano, copyright (c) 2015
; Program 5.11 Section 5.6, Program 3.10
;
;Copyright 2015 by Jonathan W. Valvano, [email protected]
; You may use, edit, run or distribute this file
; as long as the above copyright notice remains
;THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
;OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
;MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
;VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
;OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
;For more information about my classes, my research, and my books, see
;http://users.ece.utexas.edu/~valvano/
; U0Rx (VCP receive) connected to PA0
; U0Tx (VCP transmit) connected to PA1
NVIC_EN0_INT5 EQU 0x00000020 ; Interrupt 5 enable
NVIC_EN0_R EQU 0xE000E100 ; IRQ 0 to 31 Set Enable Register
NVIC_PRI1_R EQU 0xE000E404 ; IRQ 4 to 7 Priority Register
GPIO_PORTA_AFSEL_R EQU 0x40004420
GPIO_PORTA_DEN_R EQU 0x4000451C
GPIO_PORTA_AMSEL_R EQU 0x40004528
GPIO_PORTA_PCTL_R EQU 0x4000452C
UART0_DR_R EQU 0x4000C000
UART0_FR_R EQU 0x4000C018
UART0_IBRD_R EQU 0x4000C024
UART0_FBRD_R EQU 0x4000C028
UART0_LCRH_R EQU 0x4000C02C
UART0_CTL_R EQU 0x4000C030
UART0_IFLS_R EQU 0x4000C034
UART0_IM_R EQU 0x4000C038
UART0_RIS_R EQU 0x4000C03C
UART0_ICR_R EQU 0x4000C044
UART_FR_RXFF EQU 0x00000040 ; UART Receive FIFO Full
UART_FR_TXFF EQU 0x00000020 ; UART Transmit FIFO Full
UART_FR_RXFE EQU 0x00000010 ; UART Receive FIFO Empty
UART_LCRH_WLEN_8 EQU 0x00000060 ; 8 bit word length
UART_LCRH_FEN EQU 0x00000010 ; UART Enable FIFOs
UART_CTL_UARTEN EQU 0x00000001 ; UART Enable
UART_IFLS_RX1_8 EQU 0x00000000 ; RX FIFO >= 1/8 full
UART_IFLS_TX1_8 EQU 0x00000000 ; TX FIFO <= 1/8 full
UART_IM_RTIM EQU 0x00000040 ; UART Receive Time-Out Interrupt
; Mask
UART_IM_TXIM EQU 0x00000020 ; UART Transmit Interrupt Mask
UART_IM_RXIM EQU 0x00000010 ; UART Receive Interrupt Mask
UART_RIS_RTRIS EQU 0x00000040 ; UART Receive Time-Out Raw
; Interrupt Status
UART_RIS_TXRIS EQU 0x00000020 ; UART Transmit Raw Interrupt
; Status
UART_RIS_RXRIS EQU 0x00000010 ; UART Receive Raw Interrupt
; Status
UART_ICR_RTIC EQU 0x00000040 ; Receive Time-Out Interrupt Clear
UART_ICR_TXIC EQU 0x00000020 ; Transmit Interrupt Clear
UART_ICR_RXIC EQU 0x00000010 ; Receive Interrupt Clear
SYSCTL_RCGCGPIO_R EQU 0x400FE608
SYSCTL_RCGCUART_R EQU 0x400FE618
IMPORT DisableInterrupts ; Disable interrupts
IMPORT EnableInterrupts ; Enable interrupts
IMPORT StartCritical ; previous I bit, disable interrupts
IMPORT EndCritical ; restore I bit to previous value
IMPORT WaitForInterrupt ; low power mode
; properties from FIFO.c
; size of the FIFOs (must be power of 2)
FIFOSIZE EQU 16 ; (copy this value from both places in FIFO.s)
FIFOSUCCESS EQU 1 ; return value on success
FIFOFAIL EQU 0 ; return value on failure
; functions from FIFO.s
IMPORT TxFifo_Init
IMPORT TxFifo_Put
IMPORT TxFifo_Get
IMPORT TxFifo_Size
IMPORT RxFifo_Init
IMPORT RxFifo_Put
IMPORT RxFifo_Get
IMPORT RxFifo_Size
; standard ASCII symbols
CR EQU 0x0D
LF EQU 0x0A
BS EQU 0x08
ESC EQU 0x1B
SPA EQU 0x20
DEL EQU 0x7F
AREA |.text|, CODE, READONLY, ALIGN=2
THUMB
EXPORT UART_Init
EXPORT UART_InChar
EXPORT UART_OutChar
EXPORT UART0_Handler
; require C function calls to preserve the 8-byte alignment of 8-byte data objects
PRESERVE8
;------------UART_Init------------
; Initialize UART0
; Baud rate is 115200 bits/sec
; Input: none
; Output: none
; Modifies: R0, R1
; Assumes: 50 MHz system clock
UART_Init
PUSH {LR} ; save current value of LR
BL DisableInterrupts ; disable all interrupts (critical section)
; activate clock for UART0
LDR R1, =SYSCTL_RCGCUART_R ; R1 = &SYSCTL_RCGCUART_R
LDR R0, [R1] ; R0 = [R1]
ORR R0, R0, #0x01 ; enable UART0
STR R0, [R1] ; [R1] = R0
; activate clock for port A
LDR R1, =SYSCTL_RCGCGPIO_R ; R1 = &SYSCTL_RCGCGPIO_R
LDR R0, [R1] ; R0 = [R1]
ORR R0, R0, #0x01 ; enable Port A
STR R0, [R1] ; [R1] = R0
; initialize empty FIFOs
BL RxFifo_Init
BL TxFifo_Init
; disable UART
LDR R1, =UART0_CTL_R ; R1 = &UART0_CTL_R
LDR R0, [R1] ; R0 = [R1]
BIC R0, R0, #UART_CTL_UARTEN ; R0 = R0&~UART_CTL_UARTEN (disable UART)
STR R0, [R1] ; [R1] = R0
; set the baud rate (equations on p845 of datasheet)
LDR R1, =UART0_IBRD_R ; R1 = &UART0_IBRD_R
MOV R0, #162 ; R0 = IBRD = int(50,000,000 / (16 * 19,200)) = int(162,7604) mesto
STR R0, [R1] ; [R1] = R0
LDR R1, =UART0_FBRD_R ; R1 = &UART0_FBRD_R
MOV R0, #53 ; R0 = FBRD = int(0.7604 * 64 + 0.5) = 53 modulo
STR R0, [R1] ; [R1] = R0
; configure Line Control Register settings
LDR R1, =UART0_LCRH_R ; R1 = &UART0_LCRH_R
LDR R0, [R1] ; R0 = [R1]
BIC R0, R0, #0xFF ; R0 = R0&~0xFF (clear all fields)
; 8 bit word length, no parity bits, one stop bit, FIFOs
ADD R0, R0, #(UART_LCRH_WLEN_8+UART_LCRH_FEN)
STR R0, [R1] ; [R1] = R0
; configure Interrupt FIFO Level Select Register settings
LDR R1, =UART0_IFLS_R ; R1 = &UART0_IFLS_R
LDR R0, [R1] ; R0 = [R1]
BIC R0, R0, #0x3F ; R0 = R0&~0x3F (clear TX and RX interrupt FIFO level fields)
; configure interrupt for TX FIFO <= 1/8 full
; configure interrupt for RX FIFO >= 1/8 full
ADD R0, R0, #(UART_IFLS_TX1_8+UART_IFLS_RX1_8)
STR R0, [R1] ; [R1] = R0
; enable interrupts to be requested upon certain conditions
; TX FIFO interrupt: when TX FIFO <= 2 elements (<= 1/8 full, configured above)
; RX FIFO interrupt; when RX FIFO >= 2 elements (>= 1/8 full, configured above)
; RX time-out interrupt: receive FIFO not empty and no more data received in next 32-bit timeframe
; (this causes an interrupt after each keystroke, rather than every other keystroke)
LDR R1, =UART0_IM_R ; R1 = &UART0_IM_R
LDR R0, [R1] ; R0 = [R1]
; enable TX and RX FIFO interrupts and RX time-out interrupt
ORR R0, R0, #(UART_IM_RXIM+UART_IM_TXIM+UART_IM_RTIM)
STR R0, [R1] ; [R1] = R0
; enable UART
LDR R1, =UART0_CTL_R ; R1 = &UART0_CTL_R
LDR R0, [R1] ; R0 = [R1]
ORR R0, R0, #UART_CTL_UARTEN ; R0 = R0|UART_CTL_UARTEN (enable UART)
STR R0, [R1] ; [R1] = R0
; enable alternate function
LDR R1, =GPIO_PORTA_AFSEL_R ; R1 = &GPIO_PORTA_AFSEL_R
LDR R0, [R1] ; R0 = [R1]
ORR R0, R0, #0x03 ; R0 = R0|0x03 (enable alt funct on PA1-0)
STR R0, [R1] ; [R1] = R0
; enable digital port
LDR R1, =GPIO_PORTA_DEN_R ; R1 = &GPIO_PORTA_DEN_R
LDR R0, [R1] ; R0 = [R1]
ORR R0, R0, #0x03 ; R0 = R0|0x03 (enable digital I/O on PA1-0)
STR R0, [R1] ; [R1] = R0
; configure as UART
LDR R1, =GPIO_PORTA_PCTL_R ; R1 = &GPIO_PORTA_PCTL_R
LDR R0, [R1] ; R0 = [R1]
BIC R0, R0, #0x000000FF ; R0 = R0&~0x000000FF (clear port control field for PA1-0)
ADD R0, R0, #0x00000011 ; R0 = R0+0x00000011 (configure PA1-0 as UART)
STR R0, [R1] ; [R1] = R0
; disable analog functionality
LDR R1, =GPIO_PORTA_AMSEL_R ; R1 = &GPIO_PORTA_AMSEL_R
MOV R0, #0 ; R0 = 0 (disable analog functionality on PA)
STR R0, [R1] ; [R1] = R0
; set the priority of the UART interrupt
LDR R1, =NVIC_PRI1_R ; R1 = &NVIC_PRI1_R
LDR R0, [R1] ; R0 = [R1]
BIC R0, R0, #0x0000FF00 ; R0 = R0&~0xFFFF00FF (clear NVIC priority field for UART0 interrupt)
ADD R0, R0, #0x00004000 ; R0 = R0+0x00004000 (UART0 = priority 2; stored in bits 13-15)
STR R0, [R1] ; [R1] = R0
; enable interrupt 5 in NVIC
LDR R1, =NVIC_EN0_R ; R1 = &NVIC_EN0_R
LDR R0, =NVIC_EN0_INT5 ; R0 = NVIC_EN0_INT5 (zeros written to enable register have no effect)
STR R0, [R1] ; [R1] = R0
BL EnableInterrupts ; enable all interrupts (end of critical section)
POP {PC} ; restore previous value of LR into PC (return)
; private helper subroutine
; copy from hardware RX FIFO to software RX FIFO
; stop when hardware RX FIFO is empty or software RX FIFO is full
; Modifies: R0, R1
copyHardwareToSoftware
PUSH {LR} ; save current value of LR
h2sloop
; repeat the loop while (hardware receive FIFO not empty) and (software receive FIFO not full)
LDR R1, =UART0_FR_R ; R1 = &UART0_FR_R
LDR R0, [R1] ; R0 = [R1]
AND R0, R0, #UART_FR_RXFE ; R0 = R0&UART_FR_RXFE
CMP R0, #UART_FR_RXFE ; is R0 (UART0_FR_R&UART_FR_RXFE) == UART_FR_RXFE? (is hardware receive FIFO empty?)
BEQ h2sdone ; if so, skip to 'h2sdone'
BL RxFifo_Size
CMP R0, #(FIFOSIZE - 1) ; is R0 (RxFifo_Size()) == (FIFOSIZE - 1)? (is software receive FIFO full?)
BEQ h2sdone ; if so, skip to 'h2sdone'
; read a character from the hardware FIFO
LDR R1, =UART0_DR_R ; R1 = &UART0_DR_R
LDR R0, [R1] ; R0 = [R1]
; store R0 (UART0_DR_R) in software receive FIFO
BL RxFifo_Put
B h2sloop ; unconditional branch to 'h2sloop'
h2sdone
POP {PC} ; restore previous value of LR into PC (return)
; private helper subroutine
; copy from software TX FIFO to hardware TX FIFO
; stop when software TX FIFO is empty or hardware TX FIFO is full
copySoftwareToHardware
PUSH {LR} ; save current value of LR
s2hloop
; repeat the loop while (hardware transmit FIFO not full) and (software transmit FIFO not empty)
LDR R1, =UART0_FR_R ; R1 = &UART0_FR_R
LDR R0, [R1] ; R0 = [R1]
AND R0, R0, #UART_FR_TXFF ; R0 = R0&UART_FR_TXFF
CMP R0, #UART_FR_TXFF ; is R0 (UART0_FR_R&UART_FR_TXFF) == UART_FR_TXFF? (is hardware transmit FIFO full?)
BEQ s2hdone ; if so, skip to 's2hdone'
BL TxFifo_Size
CMP R0, #0 ; is R0 (TxFifo_Size()) == 0? (is software transmit FIFO empty?)
BEQ s2hdone ; if so, skip to 's2hdone'
; read a character from the software FIFO
PUSH {R0} ; allocate local variable
MOV R0, SP ; R0 = SP (R0 points to local variable)
BL TxFifo_Get ; get from software transmit FIFO into pointer R0
POP {R0} ; pop data into R0
; store R0 (data from TxFifo_Get()) in hardware transmit FIFO
LDR R1, =UART0_DR_R ; R1 = &UART0_DR_R
STR R0, [R1] ; [R1] = R0
B s2hloop ; unconditional branch to 'h2sloop'
s2hdone
POP {PC} ; restore previous value of LR into PC (return)
;------------UART_InChar------------
; input ASCII character from UART
; spin if RxFifo is empty
; Input: none
; Output: R0 character in from UART
; Very Important: The UART0 interrupt handler automatically
; empties the hardware receive FIFO into the software FIFO as
; the hardware gets data. If the UART0 interrupt is
; disabled, the software receive FIFO may become empty, and
; this function will stall forever.
; Ensure that the UART0 module is initialized and its
; interrupt is enabled before calling this function. Do not
; use UART I/O functions within a critical section of your
; main program.
UART_InChar
MOV R0, #0 ; initialize local variable
PUSH {R0, LR} ; save current value of LR and allocate local variable
inCharLoop
MOV R0, SP ; R0 = SP (R0 points to local variable)
BL RxFifo_Get ; get from software receive FIFO into pointer R0
CMP R0, #FIFOFAIL ; is R0 (RxFifo_Get()) == FIFOFAIL (value returned when FIFO empty)?
BEQ inCharLoop ; if so, skip to 'inCharLoop' (spin until receive a character)
POP {R0, PC} ; pop data into R0 and restore LR into PC (return)
;------------UART_OutChar------------
; output ASCII character to UART
; spin if TxFifo is full
; Input: R0 character out to UART
; Output: none
; Modifies: R0, R1
; Very Important: The UART0 interrupt handler automatically
; empties the software transmit FIFO into the hardware FIFO as
; the hardware sends data. If the UART0 interrupt is
; disabled, the software transmit FIFO may become full, and
; this function will stall forever.
; Ensure that the UART0 module is initialized and its
; interrupt is enabled before calling this function. Do not
; use UART I/O functions within a critical section of your
; main program.
UART_OutChar
PUSH {R4, LR} ; save current value of R4 and LR
MOV R4, R0 ; R4 = R0 (save the output character)
outCharLoop
MOV R0, R4 ; R0 = R4 (recall the output character)
BL TxFifo_Put ; store R0 (output character) in software transmit FIFO
CMP R0, #FIFOFAIL ; is R0 (TxFifo_Put()) == FIFOFAIL (value returned when FIFO full)?
BEQ outCharLoop ; if so, skip to 'outCharLoop' (spin until space in software transmit FIFO)
LDR R4, =UART0_IM_R ; R4 = &UART0_IM_R
LDR R0, [R4] ; R0 = [R4]
BIC R0, R0, #UART_IM_TXIM ; R0 = R0&~UART_IM_TXIM (disable TX FIFO interrupt)
STR R0, [R4] ; [R4] = R0
BL copySoftwareToHardware ; private helper subroutine
LDR R0, [R4] ; R0 = [R4]
ORR R0, R0, #UART_IM_TXIM ; R0 = R0|UART_IM_TXIM (enable TX FIFO interrupt)
STR R0, [R4] ; [R4] = R0
POP {R4, PC} ; restore previous value of R4 into R4 and LR into PC (return)
;------------UART0_Handler------------
; at least one of three things has happened:
; hardware TX FIFO goes from 3 to 2 or less items
; hardware RX FIFO goes from 1 to 2 or more items
; UART receiver has timed out
UART0_Handler
PUSH {LR} ; save current value of LR
; check the flags to determine which interrupt condition occurred
handlerCheck0
LDR R1, =UART0_RIS_R ; R1 = &UART0_RIS_R
LDR R0, [R1] ; R0 = [R1]
AND R0, R0, #UART_RIS_TXRIS ; R0 = R0&UART_RIS_TXRIS
CMP R0, #UART_RIS_TXRIS ; is R0 (UART0_RIS_R&UART_RIS_TXRIS) == UART_RIS_TXRIS? (does hardware TX FIFO have <= 2 items?)
BNE handlerCheck1 ; if not, skip to 'handlerCheck1' and check the next flag
; acknowledge TX FIFO interrupt
LDR R1, =UART0_ICR_R ; R1 = &UART0_ICR_R
LDR R0, =UART_ICR_TXIC ; R0 = UART_ICR_TXIC (zeros written to interrupt clear register have no effect)
STR R0, [R1] ; [R1] = R0
; copy from software TX FIFO to hardware TX FIFO
BL copySoftwareToHardware ; private helper subroutine
; if the software transmit FIFO is now empty, disable TX FIFO interrupt
; UART_OutChar() will re-enable the TX FIFO interrupt when it is needed
BL TxFifo_Size
CMP R0, #0 ; is R0 (TxFifo_Size()) == 0? (is software transmit FIFO empty?)
BNE handlerCheck1 ; if not, skip to 'handlerCheck1'
LDR R1, =UART0_IM_R ; R1 = &UART0_IM_R
LDR R0, [R1] ; R0 = [R1]
BIC R0, R0, #UART_IM_TXIM ; R0 = R0&~UART_IM_TXIM (disable TX FIFO interrupt)
STR R0, [R1] ; [R1] = R0
handlerCheck1
LDR R1, =UART0_RIS_R ; R1 = &UART0_RIS_R
LDR R0, [R1] ; R0 = [R1]
AND R0, R0, #UART_RIS_RXRIS ; R0 = R0&UART_RIS_RXRIS
CMP R0, #UART_RIS_RXRIS ; is R0 (UART0_RIS_R&UART_RIS_RXRIS) == UART_RIS_RXRIS? (does hardware RX FIFO have >= 2 items?)
BNE handlerCheck2 ; if not, skip to 'handlerCheck2' and check the next flag
; acknowledge RX FIFO interrupt
LDR R1, =UART0_ICR_R ; R1 = &UART0_ICR_R
LDR R0, =UART_ICR_RXIC ; R0 = UART_ICR_RXIC (zeros written to interrupt clear register have no effect)
STR R0, [R1] ; [R1] = R0
; copy from hardware RX FIFO to software RX FIFO
BL copyHardwareToSoftware ; private helper subroutine
handlerCheck2
LDR R1, =UART0_RIS_R ; R1 = &UART0_RIS_R
LDR R0, [R1] ; R0 = [R1]
AND R0, R0, #UART_RIS_RTRIS ; R0 = R0&UART_RIS_RTRIS
CMP R0, #UART_RIS_RTRIS ; is R0 (UART0_RIS_R&UART_RIS_RTRIS) == UART_RIS_RTRIS? (did the receiver timeout?)
BNE handlerDone ; if not, skip to 'handlerDone'
; acknowledge receiver timeout interrupt
LDR R1, =UART0_ICR_R ; R1 = &UART0_ICR_R
LDR R0, =UART_ICR_RTIC ; R0 = UART_ICR_RTIC (zeros written to interrupt clear register have no effect)
STR R0, [R1] ; [R1] = R0
; copy from hardware RX FIFO to software RX FIFO
BL copyHardwareToSoftware ; private helper subroutine
handlerDone
POP {PC} ; restore previous value of LR into PC (return from interrupt)
ALIGN ; make sure the end of this section is aligned
END ; end of file