-
Notifications
You must be signed in to change notification settings - Fork 0
/
Lab 3
365 lines (300 loc) · 12.9 KB
/
Lab 3
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
;*----------------------------------------------------------------------------
;/*
;*Created on: 2020
;*Author: Fatin Sarker
;*Copyright © 2019 Fatin Sarker. All rights reserved
;*Computer Engineering
;*University of Waterloo
; */
;*----------------------------------------------------------------------------*/
;************************** DO NOT CHANGE THIS CODE **************************
; Set up the MCU for work and enables peripherals for STM32F401RE and match
; startup_stm32f401xe.s file
THUMB ; Thumb instruction set
AREA My_code, CODE, READONLY
EXPORT __MAIN
ENTRY
__MAIN
;*************************** Constant declarations ***************************
;----- GPIO Declarations
RCC_AHB1ENR EQU 0x40023830 ; address of enable register for AHB1
GPIOA_MODER EQU 0x40020000 ; base address of GPIOA
GPIOB_MODER EQU 0x40020400 ; base address of GPIOB
GPIOC_MODER EQU 0x40020800 ; base address of GPIOC
;Offset for GPIOs are the same accross the whole family (neat)
GPIO_OTYPER EQU 0x04 ; Output TYpE Register
GPIO_OSPEEDR EQU 0X08 ; Output SPEED Register
GPIO_PUPDR EQU 0x0C ; Pull UP/Down (control) Register
GPIO_IDR EQU 0x10 ; Input Data Register
GPIO_ODR EQU 0x14 ; Output Data Register
;----- Other constants
RAND_SEED EQU 0x2F1
DELAY_CNT EQU 8000 ;8000
;********************* Static variable declarations ***************************
RAND_DATA EQU 0x20000000 ;Reserve address for random number
LDR R0, =RAND_SEED ; load the seed number
LDR R1, =RAND_DATA ; load the address of the static variable
STR R0,[R1] ; store the random number in memory
BCD_TEST EQU 0x1234
;***************************** PERIPHERAL SETUP *******************************
; We will use a technique called Read/Modify/Write Back. This is commonly
; used when programming embedded application. Note that just writing our
; wanted configuration may ruin the configuration for other pins/peripherals.
; Reading what is in the peripheral register before modifying it allows us to
; set our configuration whilst keeping previous configurations that shouldn't
; be changed.
; We encorage you to follow this practice, it will help you avoid crashes.
LDR R1, =RCC_AHB1ENR ;Set the pointer to RCC_AHB1ENR register
LDR R0, [R1] ; Read: RCC_AHB1ENR from mapped memory
ORR R0, #0x05 ; Modify: Enable the clock for GPIOA and GPIOC
STR R0, [R1] ; Write Back: Write back to RCC_AHB1ENR
; GPIOA,C are now enable but more stuff must be configured
; Deep dive: Comment out the Write Back operation and see what happens to
; the MCU. HINT: You will end up in the hard fault handler, look the RCC_AHB1ENR
; register in the datasheet and try to understand why the system fails
;---- GPIOA configuration
; We do not use the read/modify/write back in some cases because the whole register can
; be overwritten wothout affecting the proper operation of the system
LDR R1,=GPIOA_MODER ;Load pointer for GPIOA mode. This is the base register
MOV R0, #0x55555555 ; Set ALL pins of GPIOA as output
STR R0, [R1] ; Write mode
MOV R0, #0 ; Set GPIOA_PIN5 as a push-pull output
STR R0, [R1, #GPIO_OTYPER] ; Write back the output type
;---- GPIOC configuration
; We use the three bits from GPIOC to control the RGB-LED
LDR R1,=GPIOC_MODER ;Load pointer for GPIOB mode. This is the base register
LDR R0,[R1] ; Load Output Data Register
MOV32 R2,#0x15 ; Set PC0..PC2 of GPIOC as outputs all other as inputs
ORR R0,R0,R2
STR R0, [R1] ; Write mode
MOV R0, #0x0 ; Set GPIOC_PIN[2..0] as a push-pull outputs
STR R0, [R1, #GPIO_OTYPER] ; Write back the output type
MOV R0, #0
STR R0, [R1, #GPIO_OSPEEDR] ; Low speed operation
MOV R0, #0x00000000
STR R0, [R1, #GPIO_PUPDR] ; No internal pull-ups
;---- Testing GPIOs. To see this happening you must debug the lines that
; follow step by step
;--GPIOA: seven-segement display
LDR R1,=GPIOA_MODER
MOV R0, #BCD_TEST ; Write 1234 to display
STR R0, [R1, #GPIO_ODR] ; Write the data to output data register
;--GPIOC: RGB-LED
LDR R1,=GPIOC_MODER
MOV R0, #0x0 ; Turn on three LEDs (LED will shine white)
STR R0, [R1, #GPIO_ODR] ; Write back the data to output data register
MOV R0, #0x6 ; RGB RED/ON
STR R0, [R1, #GPIO_ODR] ; Write back the data to output data register
NOP
MOV R0, #0x5 ; RGB GREEN/ON
STR R0, [R1, #GPIO_ODR] ; Write back the data to output data register
NOP
MOV R0, #0x3 ; RGB BLUE/ON
STR R0, [R1, #GPIO_ODR] ; Write back the data to output data register
NOP
MOV R0, #0x0 ; RGB ALL OFF
STR R0, [R1, #GPIO_ODR] ; Write back the data to output data register
NOP
;----------------------------- END OF SETUP CODE ------------------------------
;************************* USER CODE STARTS HERE ******************************
main_loop
;NOP; Your code goes here
;stage1 red LED and display AAAA
LDR R1,=GPIOC_MODER
MOV R0, #0x6 ; RGB RED/ON
STR R0, [R1, #GPIO_ODR] ; Write back the data to output data
LDR R1,=GPIOA_MODER
MOV R0, #0xAAAA ; Write AAAA to display
STR R0, [R1, #GPIO_ODR] ; Write the data to output data register
;loop Rand to check in the range is between 2000 to 10000
check
BL Rand ;branch to generate random number for delay
MOV R2, #0x07D0 ;decimal 2000 ;0x07D0
MOV R3, #0x2710 ;decimal 10000 ;0x2710
SUBS R4,R8,R2
BLT check1 ;if R4<0 and R8<R2/2000
check1
SUBS R6,R8,R3
BGT check ;if R6>0 and R8>R3/10000
;B check1
;B check
MOV R0,R8
BL DelayN ;Delay
;------------------------------------------------------------
;stage2 after the delay turn the LED to green and poll
LDR R1,=GPIOC_MODER
MOV R0, #0x5 ; RGB GREEN/ON
STR R0, [R1, #GPIO_ODR] ; Write back the data to output data register
;subroutine poll to check for botton pushed and display the time taken
MOV R5,#0x0 ;counter initialization
MOV R4,#0x2000 ;mask for 1 on 14th bit
Poll
MOV R0,#1 ;delay for 1ms
BL DelayN
ADD R5,R5,#1 ;increment counter
LDR R1, =GPIOC_MODER ;read button input
LDR R0,[R1, #GPIO_IDR]
ANDS R3,R4,R0 ;check the flag for 0
BNE Poll
;stage3 ;detects the button down and turns on LED to blue
LDR R1,=GPIOC_MODER
MOV R0, #0x3 ; RGB BLUE/ON
STR R0, [R1, #GPIO_ODR] ; Write back the data to output data register
MOV R0,R5
BL BinToBCD ;R5 had the reflex time to BCD
MOV R6,R8 ;move output R8 from BinToBCD to R4
;display
LDR R1,=GPIOA_MODER
MOV R0, R6 ; Write to display
STR R0, [R1, #GPIO_ODR] ; Write the data to output data register
MOV R0, #0x1388 ;delay 5 seconds, 5000ms
BL DelayN
B main_loop ;Also end of main program
;************************* Pseudo Random Num Gen ******************************
; Input: NA
; Output: R8 Contains the random number.
; Description: Rand generates a pseudo random number by using the Linear
; Feedback Shift Register (LFSR) method, for details on the theory
; of LFSR and its applications you can start at:
; https://en.wikipedia.org/wiki/Linear-feedback_shift_register
; The seed is initialized at the start of the program and saved in
; memory for use within the subroutine. The subroutine does not take
; any argument because it saves it using static memory allocation.
; That is we are using something like:
; static int RandNumber = 0x03FA; //equivalent C code
; To achieve the static allocation we first need to reserve a space
; in RAM memory and then load it with the seed value. Later Rand
; will only need to read the value at the start of the call an store
; the value for future use in RAM
;
; WARNING: I've changed the code from previous versions of this lab that reserve
; R11 for exclusive use of Rand. You know from Lab2 that reserving
; registers for exclusive use of subroutines is REALLY BAD practice.
; We now use R8 as per convention.
; WARNING: DO NOT MODIFY this subroutine to circumvent the rule about register
; reservations you will get a failed grade for Lab3.
Rand
STMFD R13!,{R0-R3, R14}
; Hint: Let's try some C/Assembly mixing, look at the C statement below
; static int RandNumber = 0x03FA;
; Something as simple as the previous line in C, requires the compiler to
; produce code in two different places:
; 1) In the init section of the program the compiler will reserve memory to place
; the int and will also assign the initial value. Check init section...
; 2) Every time the value is read from memory there has to be code to load the
; address into a register and then load the value. Within the subroutine
; we can cheat a little bit and reserve a register to hold the address but
; we must load the address in the subroutine code or we will be in violation
; of the non exclusive reservation rule.
LDR R0, =RAND_DATA ;Load random number address
LDR R8, [R0] ;Load random number
;Random generation is a traightforward sequence of logic operations
AND R1, R8, #0x2000
AND R2, R8, #0x0400
LSR R1, #2
EOR R3, R1, R2 ;first tab
AND R1, R8, #0x0400
LSR R3, #2
EOR R3, R3, R1 ;second tab
AND R1, R8, #0x0040
LSR R3, #4
EOR R3, R3, R1 ;third tab
LSR R3, #6 ;shift XORED bit to b0
LSL R8, #1 ;shit current one to the lefr
ORR R8, R8, R3 ;assemble new randdom number
MOV R1, #0x7FFF ;clear up mask for unwanted bits
AND R8, R1 ;clear up
STR R8,[R0] ;save random number in RAM
LDMFD R13!,{R0-R3, R15}
;------------------------------------------------------------------------------
;******************************** Binary to BCD *******************************
; Input: R0 binary number to convert to BCD. The number is assumed to be positive
; and smaller than 0x270F = 9999dec.
; Output: R8 the packed Binary Coded Decimal (BCD) code for the input number. If
; the number is greater than 9999 the subroutine will return 0xEEEE
; Hint: For details on the BCD number system you can start at:
; https://en.wikipedia.org/wiki/Binary-coded_decimal
; Note: This is one of those subroutines that seems very long but is indeed very
; fast compared to most of its C counterparts. You can find C implemenations
; that take a couple of lines of code. However, it requires the use of division
; and the modulus (%) operand. If the MCU does not have support for
; multiplication and division, then the ASM counterpart based on subtraction
; ALWAYS wins the race. BCD conversion is one of those things that can drag
; performance down without triggering any alarm.
BinToBCD
STMFD R13!,{R0-R3, R14}
MOV R8,#0 ; Initialize return value
if_BCD_logic_1 ; Equivalent C is if(R0 > 9999) {R8=0xEEEE; return;}
MOV R1,#9999 ; We check the logic statement by subtracting R0 from
SUBS R1,R1,R0 ; 9999, if the result is negative then R0 > 9999 and
BPL if_BCD_end_1 ; the BCD code is ERROR. The branch that terminates
if_BCD_then_1 ; the if statement occurs when the result is positive
MOV R8,#0xEEEE ; or zero, meaning R0 \in [0,9999]
B BCD_return ;return
if_BCD_end_1
; Binary number in range [0,9999] convert to BCD
; ----- Thousands column
; The equivalent C code for the loop is:
; for(i=R0; i>0; i-R1) {R3++}
MOV R3,#0
MOV R1,#1000 ; We use decimal notation. Let the assembler do its job!
for_thousands
SUBS R2,R0,R1
BMI for_thousands_end ;terminate the loop R0 < 0
MOV R0,R2 ; Update R0 iff subtraction zero or positive
ADD R3,#1 ; R3++
B for_thousands
for_thousands_end
; The unpacked BCD is now in R3, we need to pack by shifting and ORing
; in C this is equivalent to: R8 = R8 | (R3 << 12);
LSL R3,#12
ORR R8,R8,R3
; ----- Hundreds column
MOV R3,#0
MOV R1,#100 ; We use decimal notation. Let the assembler do its job!
for_hundreds
SUBS R2,R0,R1
BMI for_hundreds_end ;terminate the loop R0 < 0
MOV R0,R2 ; Update R0 iff subtraction zero or positive
ADD R3,#1 ; R3++
B for_hundreds
for_hundreds_end
; The unpacked BCD is now in R3, we need to pack by shifting and ORing
; in C this is equivalent to: R8 = R8 | (R3 << 8);
LSL R3,#8
ORR R8,R8,R3
; ----- Tens column
MOV R3,#0
MOV R1,#10 ; We use decimal notation. Let the assembler do its job!
for_tens
SUBS R2,R0,R1
BMI for_tens_end ;terminate the loop R0 < 0
MOV R0,R2 ; Update R0 iff subtraction zero or positive
ADD R3,#1 ; R3++
B for_tens
for_tens_end
; The unpacked BCD is now in R3, we need to pack by shifting and ORing
; in C this is equivalent to: R8 = R8 | (R3 << 4);
LSL R3,#4
ORR R8,R8,R3
; ---- Ones column
;Whatever remains in R0 is the ones column
ORR R8,R8,R0
BCD_return
LDMFD R13!,{R0-R3, R15}
;------------------------------------------------------------------------------
;polling
;------------------------------------------------------------------------------
;Delay of R0 times with 1ms tick
DelayN
STMFD R13!,{R2,R14} ;R0 input parameter and R5 to cause delay branching
TEQ R0, #0 ;statement for branching
loop1
LDR R2, =DELAY_CNT ;value can be changed to control the delay
loop2
SUBS R2,#1 ;counter decrement
BNE loop2
SUBS R0,#1 ;decrement R0
BNE loop1
exitDelayN LDMFD R13!,{R2,R15}
END