-
Notifications
You must be signed in to change notification settings - Fork 0
/
gpioa.c
288 lines (236 loc) · 8.58 KB
/
gpioa.c
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
/* gpioa.c -- system layer
** Copyright (c) 2020 Renaud Fivet
**
** gpioa low level API and usleep()
** interrupt based serial transmission
** clocks configuration: HSI, HSE, PLL HSI, PLL HSE
** implements system.h interface: uptime, init(), kputc(), kputs(), yield()
** uptime = seconds elapsed since boot
** Serial tx, SysClck 8MHz HSI based, baudrate 9600, Busy wait transmission
** user LED toggled every second
** SysTick interrupt every second
*/
#include "system.h" /* implements system.h */
/** CORE **********************************************************************/
#define SYSTICK ((volatile unsigned long *) 0xE000E010)
#define SYSTICK_CSR SYSTICK[ 0]
#define SYSTICK_RVR SYSTICK[ 1]
#define SYSTICK_CVR SYSTICK[ 2]
#define NVIC ((volatile long *) 0xE000E100)
#define NVIC_ISER NVIC[ 0]
#define unmask_irq( idx) NVIC_ISER = 1 << idx
#define USART1_IRQ_IDX 27
/** PERIPH ********************************************************************/
#define CAT( a, b) a##b
#define RCC ((volatile long *) 0x40021000)
#define RCC_CR RCC[ 0]
#define RCC_CR_HSION 0x00000001 /* 1: Internal High Speed clock enable */
#define RCC_CR_HSEON 0x00010000 /* 16: External High Speed clock enable */
#define RCC_CR_HSERDY 0x00020000 /* 17: External High Speed clock ready flag */
#define RCC_CR_PLLON 0x01000000 /* 24: PLL enable */
#define RCC_CR_PLLRDY 0x02000000 /* 25: PLL clock ready flag */
#define RCC_CFGR RCC[ 1]
#define RCC_CFGR_SW_MSK 0x00000003 /* 1-0: System clock SWitch Mask */
#define RCC_CFGR_SW_HSE 0x00000001 /* 1-0: Switch to HSE as system clock */
#define RCC_CFGR_SW_PLL 0x00000002 /* 1-0: Switch to PLL as system clock */
#define RCC_CFGR_SWS_MSK 0x0000000C /* 3-2: System clock SWitch Status Mask */
#define RCC_CFGR_SWS_HSE 0x00000004 /* 3-2: HSE used as system clock */
#define RCC_CFGR_SWS_PLL 0x00000008 /* 3-2: PLL used as system clock */
#define RCC_CFGR_PLLSRC 0x00010000
#define RCC_CFGR_PLLSRC_HSI 0x00000000 /* HSI / 2 */
#define RCC_CFGR_PLLSRC_HSE 0x00010000 /* HSE */
#define RCC_CFGR_PLLXTPRE 0x00020000
#define RCC_CFGR_PLLXTPRE_DIV1 0x00000000 /* HSE */
#define RCC_CFGR_PLLXTPRE_DIV2 0x00020000 /* HSE / 2 */
#define RCC_CFGR_PLLMUL_MSK (0x00F << 18)
#define RCC_CFGR_PLLMUL( v) ((v - 2) << 18)
#define RCC_AHBENR RCC[ 5]
#define RCC_AHBENR_IOPn( n) (1 << (17 + n))
#define RCC_AHBENR_IOPh( h) RCC_AHBENR_IOPn( CAT( 0x, h) - 0xA)
#define RCC_APB2ENR RCC[ 6]
#define RCC_APB2ENR_USART1EN 0x00004000 /* 14: USART1 clock enable */
#define GPIOA ((volatile long *) 0x48000000)
#define GPIOB ((volatile long *) 0x48000400)
#define GPIO( x) CAT( GPIO, x)
#define MODER 0
#define IDR 4
#define ODR 5
#define AFRH 9
#define USART1 ((volatile long *) 0x40013800)
#define CR1 0 /* Config Register */
#define BRR 3 /* BaudRate Register */
#define ISR 7 /* Interrupt and Status Register */
#define TDR 10 /* Transmit Data Register*/
#define USART_CR1_TXEIE (1 << 7) /* 7: TDR Empty Interrupt Enable */
#define USART_CR1_TE 8 /* 3: Transmit Enable */
#define USART_CR1_RE 4 /* 2: Receive Enable */
#define USART_CR1_UE 1 /* 0: USART Enable */
#define USART_ISR_TXE (1 << 7) /* 7: Transmit Data Register Empty */
/* user LED ON when PA4 is low */
#define LED_IOP A
#define LED_PIN 4
#define LED_ON 0
/* 8MHz quartz, configure PLL at 24MHz */
#define HSE 8000000
#define PLL 6
#define BAUD 9600
#ifdef PLL
# ifdef HSE
# define CLOCK HSE / 2 * PLL
# else /* HSI */
# define CLOCK 8000000 / 2 * PLL
# endif
# if CLOCK < 16000000
# error PLL output below 16MHz
# endif
#elif defined( HSE)
# define CLOCK HSE
#else /* HSI */
# define CLOCK 8000000
#endif
#if CLOCK > 48000000
# error clock frequency exceeds 48MHz
#endif
#if CLOCK % BAUD
# warning baud rate not accurate at that clock frequency
#endif
static unsigned char txbuf[ 8] ; // best if size is a power of 2 for cortex-M0
#define TXBUF_SIZE (sizeof txbuf / sizeof txbuf[ 0])
static unsigned char txbufin ;
static volatile unsigned char txbufout ;
void USART1_Handler( void) {
if( txbufout == txbufin) {
/* Empty buffer => Disable TXEIE */
USART1[ CR1] &= ~USART_CR1_TXEIE ;
} else {
static unsigned char lastc ;
unsigned char c ;
c = txbuf[ txbufout] ;
if( c == '\n' && lastc != '\r')
c = '\r' ;
else
txbufout = (txbufout + 1) % TXBUF_SIZE ;
USART1[ TDR] = c ;
lastc = c ;
}
}
void kputc( unsigned char c) { /* character output */
int nextidx ;
/* Wait if buffer full */
nextidx = (txbufin + 1) % TXBUF_SIZE ;
while( nextidx == txbufout)
yield() ;
txbuf[ txbufin] = c ;
txbufin = nextidx ;
/* Trigger transmission by enabling interrupt */
USART1[ CR1] |= USART_CR1_TXEIE ;
}
int kputs( const char s[]) { /* string output */
int cnt = 0 ;
int c ;
while( (c = *s++) != 0) {
kputc( c) ;
cnt += 1 ;
}
return cnt ;
}
void yield( void) { /* give way */
__asm( "WFI") ; /* Wait for System Tick Interrupt */
}
volatile unsigned uptime ; /* seconds elapsed since boot */
#ifdef LED_ON
static void userLEDtoggle( void) {
GPIO( LED_IOP)[ ODR] ^= 1 << LED_PIN ; /* Toggle User LED */
}
#endif
void SysTick_Handler( void) {
uptime += 1 ;
#ifdef LED_ON
userLEDtoggle() ;
#endif
}
void usleep( unsigned usecs) { /* wait at least usec µs */
#if CLOCK / 8000000 < 1
# error HCLK below 8 MHz
#endif
usecs = SYSTICK_CVR - (CLOCK / 8000000 * usecs) ;
while( SYSTICK_CVR > usecs) ;
}
/* GPIOA low level API ********************************************************/
void gpioa_input( int pin) { /* Configure GPIOA pin as input */
GPIOA[ MODER] &= ~(3 << (pin * 2)) ; /* Apin as input [00] */
}
void gpioa_output( int pin) { /* Configure GPIOA pin as output */
GPIOA[ MODER] |= 1 << (pin * 2) ; /* Apin output (over [00]) */
}
iolvl_t gpioa_read( int pin) { /* Read level of GPIOA pin */
return LOW != (GPIOA[ IDR] & (1 << pin)) ;
}
int init( void) {
/* By default SYSCLK == HSI [8MHZ] */
#ifdef HSE
/* Start HSE clock (8 MHz external oscillator) */
RCC_CR |= RCC_CR_HSEON ;
/* Wait for oscillator to stabilize */
do {} while( (RCC_CR & RCC_CR_HSERDY) == 0) ;
#endif
#ifdef PLL
/* Setup PLL HSx/2 * 6 [24MHz] */
/* Default 0: PLL HSI/2 src, PLL MULL * 2 */
RCC_CFGR =
# ifdef HSE
RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_DIV2 |
# endif
RCC_CFGR_PLLMUL( PLL) ;
RCC_CR |= RCC_CR_PLLON ;
do {} while( (RCC_CR & RCC_CR_PLLRDY) == 0) ; /* Wait for PLL */
/* Switch to PLL as system clock SYSCLK == PLL [24MHz] */
RCC_CFGR = (RCC_CFGR & ~RCC_CFGR_SW_MSK) | RCC_CFGR_SW_PLL ;
do {} while( (RCC_CFGR & RCC_CFGR_SWS_MSK) != RCC_CFGR_SWS_PLL) ;
#elif defined( HSE)
/* Switch to HSE as system clock SYSCLK == HSE [8MHz] */
RCC_CFGR = (RCC_CFGR & ~RCC_CFGR_SW_MSK) | RCC_CFGR_SW_HSE ;
do {} while( (RCC_CFGR & RCC_CFGR_SWS_MSK) != RCC_CFGR_SWS_HSE) ;
#endif
#ifdef HSE
/* Switch off HSI */
RCC_CR &= ~RCC_CR_HSION ;
#endif
/* SYSTICK */
SYSTICK_RVR = CLOCK / 8 - 1 ; /* HBA / 8 */
SYSTICK_CVR = 0 ;
SYSTICK_CSR = 3 ; /* HBA / 8, Interrupt ON, Enable */
/* SysTick_Handler will execute every 1s from now on */
#ifdef LED_ON
/* User LED ON */
RCC_AHBENR |= RCC_AHBENR_IOPh( LED_IOP) ; /* Enable IOPx periph */
GPIO( LED_IOP)[ MODER] |= 1 << (LED_PIN * 2) ; /* LED_IO Output [01],
** over default 00 */
/* OTYPER Push-Pull by default */
/* Pxn output default LOW at reset */
# if LED_ON
userLEDtoggle() ;
# endif
#endif
/* USART1 9600 8N1 */
RCC_AHBENR |= RCC_AHBENR_IOPh( A) ; /* Enable GPIOA periph */
GPIOA[ MODER] |= 0x0A << (9 * 2) ; /* PA9-10 ALT 10, over default 00 */
GPIOA[ AFRH] |= 0x110 ; /* PA9-10 AF1 0001, over default 0000 */
RCC_APB2ENR |= RCC_APB2ENR_USART1EN ;
USART1[ BRR] = CLOCK / BAUD ; /* PCLK is default source */
USART1[ CR1] |= USART_CR1_UE | USART_CR1_TE ; /* Enable USART & Tx */
/* Unmask USART1 irq */
unmask_irq( USART1_IRQ_IDX) ;
kputs(
#ifdef PLL
"PLL"
#endif
#ifdef HSE
"HSE"
#else
"HSI"
#endif
"\n") ;
return 0 ;
}
/* end of gpioa.c */