-
Notifications
You must be signed in to change notification settings - Fork 1
/
equates-stm32f4.asm
265 lines (207 loc) · 9.39 KB
/
equates-stm32f4.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
;; skip the emacs mode line
;;; -*- Mode:Asm Mode:outline-minor-mode outline-regexp:";;;+" comment-start: "; " -*-
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; equates-stm32.asm
;; The purpose of this file (equates-stm32.asm) is to contain
;; all the code for Riscy Pygness that is specific to the
;; STM32 ARM M3 MCU.
;; This file (equates-stm32.asm) is included by the
;; board-specific include file (such as
;; olimex-stm32-p103.asm).
;;; NOTE
;; Four subroutines and 2 Forth primitives must be defined.
;; The following subroutines must be defined, even if they do nothing,
;; because riscy.asm will call them.
;;
;; setup_clocks
;; setup_ports
;; ledOnSub
;; ledOffSub
;; Additional, application-specific CODE words can be defined in
;; this file.
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.equ PLLCLKIN, 8000000
;; Main crystal clock frequency
;; above (8 MHz) is the speed of both the STM32 HSI (internal
;; RC) oscillator and the HSE (external crystal) oscillator on
;; the Olimex board.
.equ PLL_MULTIPLIER, 1 ; Multiplier must be 1 since we turn off PLL
.equ CPUDIVISOR, 1 ; (does the Cortex have a cpu clock divisor?)
;.equ PCLKDIVISOR, 4 ; must be 1, 2, or 4
.equ PCLKDIVISOR, 1 ; must be 1, 2, or 4
.equ TIMER0_PRESCALE_DIVISOR, 1
.equ BAUDRATE, 38400
.equ SPIDIVISOR, 128 ; slow it way down for testing
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Equates that do calculations and so are not likely to change
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Frequency is given in number of clocks per second
;; The Cortex M3 has two PCLKs
.equ PLLCLK, (PLLCLKIN * PLL_MULTIPLIER)
.equ CCLK, (PLLCLK / CPUDIVISOR)
;; The Cortex M3 terms would be HSI or HSE (for the external
;; or internal high-speed oscillators). After going through
;; (or bypassing) the PLL, we get a SYSCLK which I think is
;; the same thing as what we called the CCLK on the LPC chips.
.equ SYSCLK, CCLK
;; After the SYSCLK goes through the AHB prescaler
;; (/ 1,2,...512) it is called the HCLK. So, maybe HCLK is
;; the Cortex M3 term for what we had been calling CCLK?
;; Maybe not.
;; The Cortex M3 has PCLK1 and PCLK2.
;;
;; APB1 peripherals use PCLK1 (up to 36 MHz)
;; APB2 peripherals use PCLK2 (up to 72 MHz)
.equ PCLK, CCLK / PCLKDIVISOR ; most timing depends on PCLK
;; Cortex M3 has two peripheral clocks, PCLK1 and PCLK2. Set
;; them to the same value (hopefully their defaults are divide by 1)
.equ PCLK1, PCLK
.equ PCLK2, PCLK
;; So, to begin with, all the clocks stay at 8 MHz.
;; Following needs to be converted for the Cortex M3
.equ PCLK_TIMER0_DIVISOR, PCLKDIVISOR
.equ TIMER0FREQ, ((CCLK / PCLK_TIMER0_DIVISOR) / TIMER0_PRESCALE_DIVISOR)
.equ SPICLK, PCLK / SPIDIVISOR
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Clocks
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
setup_clocks:
;; Initialize STM32 Clocks
;; Ideally, we would just take the defaults to begin with and
;; do nothing. Because it is possible that HSI is not
;; accurate enough for the serial communication (USART2), we
;; will switch from the internal 8 MHz clock (HSI) to the
;; external 8 MHz clock (HSE).
ldr r6, = RCC_CR
mov r0, HSEON
str r0, [r6] ; turn on the external clock
awaitHSE:
ldr r0, [r6]
and r0, # HSERDY
beq awaitHSE ; hang here until external clock is stable
;; at this point, the HSE is running and stable but I suppose we have not yet
;; switched Sysclk to use it.
ldr r6, = RCC_CFGR
mov r0, # 1
str r0, [r6] ; switch to the external clock
;; Turn off the HSION bit
ldr r6, = RCC_CR
ldr r0, [r6]
and r0, 0xFFFFFFFE ; Zero the 0th bit
str r0, [r6]
mov pc, lr ; rts
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; I/O Ports and Peripherals
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
setup_ports:
;; STM32
;;;; Set up LED pin - green LED on PD12
;; Enable the Port D peripheral clock (for LED)
;; and the Port A peripheral clock (for USART)
ldr r6, = RCC_AHB1ENR
mov r0, BIT3 + BIT0
str r0, [r6]
;; Configure mode of LED pin: Port D, pin 12
ldr r6, = GPIOD_MODER
ldr r0, = BIT24 ; Output
str r0, [r6]
;; Configure pullup for LED pin: Port D, pin 12
ldr r6, = GPIOD_PUPDR
ldr r0, = BIT24 ; Pull-up
str r0, [r6]
;; STM32F4 port - still relevant? vvvvv
;; to turn on the LED, clear bit 12 of Port D
;; to turn off the LED, set bit 12 of Port D
;; by writing 0 or 0x1000 to GPIOD_ODR
;; (later, use bit banding)
;;;; UART2 Initialization
setup_uarts:
;; Configure mode of USART2 pins Port A (AF mode)
;; nbl3|nbl2|nbl1|nbl0
;; 0000|0000|1010|0000
;; 0 A 0
;; GPIOA_MODER has a nonzero reset value and JTAG stops
;; working if you set it to zero, so a little bit of ORRing
;; is required.
ldr r6, = GPIOA_MODER
;;ldr r0, = 0x2AA
ldr r5, [r6]
ldr r0, = 0xA0
orr r0, r5
str r0, [r6]
;; Configure pullups for USART2 pins on Port A
;; nbl3|nbl2|nbl1|nbl0
;; 0000|0000|0001|0000
;; 0 0 1 0
ldr r6, = GPIOA_PUPDR
;;ldr r0, = 0x114 ; All pins of USART2 pulled up
ldr r0, = 0x20 ; TX pin pulled up (necessary?)
str r0, [r6]
;; Set alternate function 7 to enable USART2 pins on Port A
ldr r6, = GPIOA_AFRL
ldr r0, = 0x77777 ; Alternate function 7 for pins of USART2 on PORTA
str r0, [r6]
;; On the Olimex STM32-P103 board, UART2 is connected to the
;; RS232 connector. UART2 uses Port A (PA2 for TX and PA3 for RX)
;; Enable the Port A peripheral clock by setting bit 2
;; See above where we enabled both Port C and Port A
;; PA2 is TX so it needs to be configured as 0b1010 (push-pull alternate function low frequency)
;; PA3 is RX and its default state of 0x0100 (floating) should
;; work. We could change it to a pull-up or pull-down?
;; bit3|bit2|bit1|bit0
;; 0100|1010|xxxx|xxxx
;;ldr r6, = GPIOA_CRL
;;ldr r0, [r6]
;;and r0, # 0xFFFFF0FF ; clear nibble for PA2
;;orr r0, # 0x00000A00 ; OR in new value for PA2 nibble to
;; ; configure as push-pull alternate function low frequency
;;str r0, [r6]
;; enable clock for USART2
ldr r6, = RCC_APB1ENR
ldr r0, = BIT17
str r0, [r6]
;; It appears that the actual formula for the USART2 baudrate
;; register value is BRRVALUE = (PCLK1 / BAUDRATE)
;; For example, with an 8 MHz SYSCLK (either from HSI or HSE)
;; and not turning on any clock dividers, the PCLK1 would also
;; be 8 MHz. If the desired baudrate is 38400 bps, then
;; BRRVALUE = 8000000 / 38400 = 0xD0.
enableuart:
;; set UE (usart enable) (bit 13), TE (transmit enable) (bit 3), and RE (receiver enable) (bit 2)
ldr r6, = USART2_CR1
;;.equ USART_UE_TE_RE, (BIT13 + BIT3 + BIT2)
ldr r0, = BIT13 ; UE bit - USART enable
str r0, [r6]
setbaud:
.equ BRRVALUE, (PCLK1 / BAUDRATE)
ldr r6, = USART2_BRR
;ldr r0, = 0x00D0 ; 38400 bps
ldr r0, = BRRVALUE ; BAUDRATE bps
str r0, [r6]
enabletxrx:
;; set TE (transmit enable) (bit 3), and RE (receiver enable) (bit 2)
;; this should cause an idle frame to be sent
ldr r6, = USART2_CR1
ldr r0, [r6]
orr r0, # 0x0000000C ; = BIT3 + BIT2
str r0, [r6]
mov pc, lr
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; LED Subroutines
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Note, these routines are specific to a board which has
;; which has an LED on Port D.
;; Consider changing over to bit banding.
ledOnSub:
ldr r6, = GPIOD_ODR
mov r1, # 0
str r1, [r6] ; clear LED bit (bit 12) to turn on the LED
mov pc, lr
ledOffSub:
ldr r6, = GPIOD_ODR
ldr r1, = LED_MASK ; LED bit
str r1, [r6] ; set LED bit (bit 12) to turn off the LED
mov pc, lr
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Additional application-specific Forth Primitives could go here.
;;;