|
9 | 9 | .cpu cortex-m3
|
10 | 10 | .thumb
|
11 | 11 |
|
12 |
| -.word 0x20000400 |
13 |
| -.word 0x080000ed |
14 |
| -.space 0xe4 |
15 |
| - |
16 |
| -/***********************************************************************/ |
17 |
| -/* library: lib_gpio.s */ |
18 |
| -/* description: TODO Provides mathematical functions */ |
19 |
| -/* function mult_r0_by_r1: multiplies two numbers */ |
20 |
| -/* function div_r0_by_r1: divides one number by another */ |
21 |
| -/* depends on: - */ |
22 |
| -/***********************************************************************/ |
| 12 | +/************************************************************************/ |
| 13 | +/* library: lib_gpio.s */ |
| 14 | +/* description: Provides functions for handling GPIO of NUCLEO-F103RB */ |
| 15 | +/* function port_open: enables IO port clock */ |
| 16 | +/* function gpio_init: configures pin */ |
| 17 | +/* function gpio_set: sets output of pin */ |
| 18 | +/* depends on: - */ |
| 19 | +/************************************************************************/ |
23 | 20 |
|
24 | 21 | .global port_open
|
25 | 22 | port_open:
|
26 |
| - /********************************************************************************/ |
27 |
| - /* function: mult_r0_by_r1 */ |
28 |
| - /* description: multiplies the values in r0 and r1 and returns the result in r0 */ |
29 |
| - /********************************************************************************/ |
30 |
| - /* input: r0: multiplier (how many times to add) */ |
31 |
| - /* r1: multiplicand (what to add) */ |
32 |
| - /* output: r0: product of multiplicand and multiplier */ |
33 |
| - /* helper: r2 (no need to save, is saved on the stack in this function) */ |
34 |
| - /********************************************************************************/ |
| 23 | + /**************************************************************/ |
| 24 | + /* function: port_open */ |
| 25 | + /* description: enables the clock for the specified IO port */ |
| 26 | + /**************************************************************/ |
| 27 | + /* input: r0: port (A: 0, B: 1, ...), must be >= 0 and <= 4 */ |
| 28 | + /* otherwise this function has no effect */ |
| 29 | + /* output: r0: preserves input */ |
| 30 | + /**************************************************************/ |
| 31 | + /* check parameter */ |
| 32 | + push {lr} |
| 33 | + bl check_port @ failed check will return early |
| 34 | + pop {lr} |
| 35 | + |
| 36 | + /* begin function */ |
35 | 37 | push {r1-r3}
|
| 38 | + |
36 | 39 | /*
|
37 | 40 | * configure APB2 peripheral clock enable register (RCC_APB2ENR)
|
38 |
| - * |
39 |
| - * base RCC address: 0x40021000 |
| 41 | + */ |
| 42 | + |
| 43 | + /* |
| 44 | + * base reset and clock control RCC address: 0x40021000 |
40 | 45 | * RCC_APB2ENR offset: 0x18
|
41 | 46 | * -> 0x40021018
|
42 | 47 | */
|
43 |
| - ldr r1, =0x40021018 @ base address |
44 |
| - ldr r2, [r1] |
45 |
| - mov r3, #0b100 |
46 |
| - lsl r3, r3, r0 |
47 |
| - orr r2, r2, r3 |
48 |
| - str r2, [r1] |
| 48 | + ldr r1, =0x40021018 @ RCC_APB2ENR address |
| 49 | + ldr r2, [r1] @ load previous register value |
| 50 | + |
| 51 | + mov r3, #0b100 @ bitmask, ports start at bit 2 |
| 52 | + lsl r3, r0 @ shift mask by port number |
| 53 | + orr r2, r3 @ enable clock for port |
49 | 54 |
|
| 55 | + str r2, [r1] @ store new register value |
| 56 | + |
| 57 | + /* end function and return */ |
50 | 58 | pop {r1-r3}
|
51 | 59 | bx lr
|
52 | 60 |
|
53 | 61 |
|
54 | 62 |
|
55 | 63 | .global gpio_init
|
56 | 64 | gpio_init:
|
57 |
| - push {r3-r5} |
| 65 | + /***************************************************************************/ |
| 66 | + /* function: gpio_init */ |
| 67 | + /* description: configures the specified GPIO pin as an input/output pin */ |
| 68 | + /***************************************************************************/ |
| 69 | + /* input: r0: port the pin belongs to (A: 0, B: 1, ...), */ |
| 70 | + /* must be >= 0 and <= 4 otherwise this function has no effect */ |
| 71 | + /* r1: pin, must be >= 0 and <= 15 */ |
| 72 | + /* otherwise this function has no effect */ |
| 73 | + /* r2: mode, 0 for floating input, anything else for general */ |
| 74 | + /* purpose output push-pull, max speed 50 MHz */ |
| 75 | + /* output: r0: preserves input */ |
| 76 | + /* r1: preserves input */ |
| 77 | + /* r2: preserves input */ |
| 78 | + /***************************************************************************/ |
| 79 | + /* check parameters */ |
| 80 | + push {lr} |
| 81 | + bl check_port |
| 82 | + bl check_pin @ failed check will return early |
| 83 | + pop {lr} |
| 84 | + |
| 85 | + /* begin function */ |
| 86 | + push {r0-r5} |
58 | 87 |
|
59 |
| - /* Port offset -> Ports are 0x400 apart */ |
60 |
| - mov r3, #0x400 |
61 |
| - mul r4, r0, r3 @ r0 is Port number (A = 0, ...) |
62 | 88 | /*
|
63 |
| - * configure Port configuration register low/high (GPIOx_CRL/GPIOx_CRH) |
64 |
| - * |
65 |
| - * base GPIO Port A address: 0x40010800 |
66 |
| - * -> add Port offset in r4 and optional high offset |
| 89 | + * configure port configuration register low/high |
| 90 | + * (GPIOx_CRL/GPIOx_CRH) |
67 | 91 | */
|
68 |
| - ldr r3, =0x40010800 @ base address |
69 |
| - add r3, r3, r4 |
70 |
| - /* if Pin number > 8 high else low */ |
71 |
| - cmp r1, #8 |
72 |
| - blt skip_high |
73 |
| - add r3, r3, #0x04 @ extra high offset |
74 |
| -skip_high: |
75 |
| - /* final GPIOx_CRL/GPIOx_CRH address is in r3 */ |
76 | 92 |
|
77 | 93 | /*
|
78 |
| - * low/high is set (stored in r3) |
79 |
| - * -> only care about last 3 bits of Pin number |
80 |
| - * -> multiply by 4, use as shift distance |
| 94 | + * base GPIO port A address: 0x40010800 |
| 95 | + * -> add port offset and optional GPIOx_CRH offset (0x04) |
81 | 96 | */
|
82 |
| - and r1, r1, #0b111 |
83 |
| - mov r5, #4 |
84 |
| - mul r1, r1, r5 |
85 |
| - |
86 |
| - ldr r4, [r3] @ load previous register value |
| 97 | + ldr r3, =0x40010800 @ base GPIO port A address |
87 | 98 |
|
88 |
| - mvn r0, #0b1111 @ invert mask |
89 |
| - mov r5, #32 |
90 |
| - sub r5, r5, r1 @ 32 - bits to rotate left |
91 |
| - ror r0, r0, r5 @ to make left rotate with ror |
| 99 | + mov r4, #0x400 @ ports are 0x400 bytes apart |
| 100 | + mul r4, r0 @ times port number -> offset |
| 101 | + add r3, r4 @ add port offset to base |
92 | 102 |
|
93 |
| - and r4, r4, r0 @ reset CNF and MODE for Pin |
| 103 | + cmp r1, #8 @ pin >= 8 -> high else low |
| 104 | + it ge @ if high |
| 105 | + addge r3, #0x04 @ add GPIOx_CRH offset to base |
| 106 | + /* final GPIOx_CRL/GPIOx_CRH address is now in r3 */ |
94 | 107 |
|
95 |
| - cmp r2, #0 @ 0 = input, 1 = output |
96 |
| -input: |
97 |
| - bne output |
98 |
| - mov r0, #0b0100 |
99 |
| - b change_cnf_and_mode |
100 |
| -output: |
101 |
| - mov r0, #0b0001 |
| 108 | + /* |
| 109 | + * we now know whether we have a low or high pin (address in r3) |
| 110 | + * -> only care about last 3 bits of pin number from here on |
| 111 | + * (low and high configuration registers both have |
| 112 | + * configurations for 8 = 2^3 pins) |
| 113 | + * -> multiply by 4 to use as shift distance later |
| 114 | + * (each pin has 4 configuration bits) |
| 115 | + */ |
| 116 | + and r1, #0b111 @ only keep 3 bits |
| 117 | + mov r0, #4 |
| 118 | + mul r1, r0 @ times 4 for use in shifts |
102 | 119 |
|
103 |
| -change_cnf_and_mode: |
104 |
| - lsl r0, r0, r1 @ shift CNF and MODE to position |
105 |
| - orr r4, r4, r0 |
| 120 | + /* load previous register value */ |
| 121 | + ldr r4, [r3] |
106 | 122 |
|
107 |
| - str r4, [r3] @ store new register value |
| 123 | + /* |
| 124 | + * reset previous CNF and MODE for pin by using a bitmask with |
| 125 | + * 4 unset bits rotated into position using ror with adjusted |
| 126 | + * shift distance to simulate left rotation |
| 127 | + * left shift distance is r1 |
| 128 | + */ |
| 129 | + mvn r0, #0b1111 @ all bits set except last 4 |
| 130 | + mov r5, #32 |
| 131 | + sub r5, r1 @ 32 - bits to rotate left |
| 132 | + ror r0, r5 @ -> left rotation with ror |
| 133 | + and r4, r0 @ reset CNF and MODE for pin |
108 | 134 |
|
109 |
| - pop {r3-r5} |
| 135 | + /* |
| 136 | + * set new CNF and MODE for pin based on function parameter r2 |
| 137 | + * r2 | CNF | MODE |
| 138 | + * input -> floating input | 01 | 00 |
| 139 | + * output -> general purpose output push-pull, | 00 | 11 |
| 140 | + * max speed 50 MHz | | |
| 141 | + */ |
| 142 | + cmp r2, #0 @ 0 -> input else output |
| 143 | + ite eq @ if input |
| 144 | + moveq r0, #0b0100 @ input configuration |
| 145 | + movne r0, #0b0011 @ else output configuration |
| 146 | + lsl r0, r1 @ shift CNF and MODE to position |
| 147 | + orr r4, r0 @ set new CNF and MODE for pin |
| 148 | + |
| 149 | + /* store new register value */ |
| 150 | + str r4, [r3] |
| 151 | + |
| 152 | + /* end function and return */ |
| 153 | + pop {r0-r5} |
110 | 154 | bx lr
|
111 | 155 |
|
112 | 156 |
|
113 | 157 |
|
114 | 158 | .global gpio_set
|
115 | 159 | gpio_set:
|
116 |
| - push {r3-r5} |
| 160 | + /***************************************************************************/ |
| 161 | + /* function: gpio_set */ |
| 162 | + /* description: sets the output value of the specified GPIO pin */ |
| 163 | + /***************************************************************************/ |
| 164 | + /* input: r0: port the pin belongs to (A: 0, B: 1, ...), */ |
| 165 | + /* must be >= 0 and <= 4 otherwise this function has no effect */ |
| 166 | + /* r1: pin, must be >= 0 and <= 15 */ |
| 167 | + /* otherwise this function has no effect */ |
| 168 | + /* r2: value, 0 to reset, anything else to set */ |
| 169 | + /* output: r0: preserves input */ |
| 170 | + /* r1: preserves input */ |
| 171 | + /* r2: preserves input */ |
| 172 | + /***************************************************************************/ |
| 173 | + /* check parameters */ |
| 174 | + push {lr} |
| 175 | + bl check_port |
| 176 | + bl check_pin @ failed check will return early |
| 177 | + pop {lr} |
| 178 | + |
| 179 | + /* begin function */ |
| 180 | + push {r0-r2} |
| 181 | + |
| 182 | + /* |
| 183 | + * update pin output value using port bit set/reset register |
| 184 | + * (GPIOx_BSRR) |
| 185 | + */ |
| 186 | + |
| 187 | + /* |
| 188 | + * adjust pin number by 16 if pin should be reset |
| 189 | + * (reset bits in GPIOx_BSRR have an offset of 16) |
| 190 | + */ |
| 191 | + cmp r2, #0 @ 0 -> reset else set |
| 192 | + it eq @ if reset |
| 193 | + addeq r1, #16 @ add 16 to pin number |
| 194 | + |
| 195 | + /* port offset */ |
| 196 | + mov r2, #0x400 @ ports are 0x400 bytes apart |
| 197 | + mul r0, r2 @ times port number -> offset |
117 | 198 |
|
118 |
| - /* Port offset -> Ports are 0x400 apart */ |
119 |
| - mov r3, #0x400 |
120 |
| - mul r4, r0, r3 @ r0 is Port number (A = 0, ...) |
121 | 199 | /*
|
122 |
| - * change Port bit set/reset register (GPIOx_BSRR) |
123 |
| - * |
124 |
| - * base GPIO Port A address: 0x40010800 |
| 200 | + * base GPIO port A address: 0x40010800 |
125 | 201 | * GPIOx_BSRR offset: 0x10
|
126 | 202 | * -> 0x40010810
|
127 |
| - * -> add Port offset in r4 |
| 203 | + * -> add port offset |
128 | 204 | */
|
129 |
| - ldr r3, =0x40010810 @ base address |
130 |
| - add r3, r3, r4 |
131 |
| - /* final GPIOx_BSRR address is in r3 */ |
| 205 | + ldr r2, =0x40010810 @ base adjusted address |
| 206 | + add r2, r0 @ add port offset to base |
| 207 | + /* final GPIOx_BSRR address is now in r3 */ |
132 | 208 |
|
133 |
| - ldr r4, [r3] @ load previous register value |
| 209 | + /* |
| 210 | + * write to BSx/BRx, no need to save previous values, |
| 211 | + * GPIOx_BSRR is a write-only register |
| 212 | + */ |
| 213 | + mov r0, #1 |
| 214 | + lsl r0, r1 @ shift by adjusted pin number |
| 215 | + str r0, [r2] @ set/reset pin |
134 | 216 |
|
135 |
| - mvn r0, 0x10001 @ invert mask |
136 |
| - mov r5, #32 |
137 |
| - sub r5, r5, r1 @ 32 - bits to rotate left |
138 |
| - ror r0, r0, r5 @ to make left rotate with ror |
| 217 | + /* end function and return */ |
| 218 | + pop {r0-r2} |
| 219 | + bx lr |
139 | 220 |
|
140 |
| - and r4, r4, r0 @ set and reset bit to 0 for Pin |
141 | 221 |
|
142 |
| - mov r0, #1 |
143 |
| - mov r0, r0, lsl r1 @ shift to Pin number |
144 |
| - cmp r2, #0 @ 0 = reset, 1 = set |
145 |
| - bne change_set_or_reset @ no extra shift for set |
146 |
| - lsl r0, r0, #16 @ extra 16 bit shift for reset |
147 | 222 |
|
148 |
| -change_set_or_reset: |
149 |
| - orr r4, r4, r0 |
| 223 | +check_port: |
| 224 | + /* port has to be in range 0-4 (A-E) */ |
150 | 225 |
|
151 |
| - str r4, [r3] @ store new register value |
| 226 | + cmp r0, #0 @ port less than 0? |
| 227 | + itt lt @ | |
| 228 | + poplt {lr} @ v |
| 229 | + bxlt lr @ return early from caller |
152 | 230 |
|
153 |
| - pop {r3-r5} |
154 |
| - bx lr |
| 231 | + cmp r0, #4 @ port greater than 4? |
| 232 | + itt gt @ | |
| 233 | + popgt {lr} @ v |
| 234 | + bxgt lr @ return early from caller |
| 235 | + |
| 236 | + bx lr @ ok |
| 237 | + |
| 238 | +check_pin: |
| 239 | + /* pin has to be in range 0-15 */ |
| 240 | + |
| 241 | + cmp r1, #0 @ pin less than 0? |
| 242 | + itt lt @ | |
| 243 | + poplt {lr} @ v |
| 244 | + bxlt lr @ return early from caller |
| 245 | + |
| 246 | + cmp r1, #15 @ pin greater than 15? |
| 247 | + itt gt @ | |
| 248 | + popgt {lr} @ v |
| 249 | + bxgt lr @ return early from caller |
| 250 | + |
| 251 | + bx lr @ ok |
0 commit comments