STM32 Primer - Use the Standard Peripheral Library

http://pandafruits.com/stm32_primer/stm32_primer_lib.php

As I mentioned in the last section we need to do a bit more work to the infrastructure to make it more suitable for more realistic application development. While still making the same LED flashing, in this section I'll:

  • make the linker script more complete
  • use a startup file
  • use a timer interrupt to flash the LED
  • use some functions from ST's standard peripheral library

The files involved in this section are:

  • an application source file - "main.c" file
  • s startup file - "startup.c"
  • a liker script - "timer_led.ld"
  • a makefile - "Makefile"
  • an OpenOcd configratrion file - "openocd.cfg"
  • a small bash script - "write_bin.sh"
  • STM32F10x standard peripheral library directory that can be downloaded here

This time we look at the linker script first:

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
/***************************************
 * stm32 timer led example timer_led.ld
 ***************************************/
 
/* Memory layout for an STM32F103RB */
MEMORY
{
   FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
   RAM  (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}
 
STACK_SIZE = 1024;
_estack = ORIGIN(RAM) + LENGTH(RAM);
_sstack = _estack - STACK_SIZE;
 
/* Output sections */
SECTIONS
{
    .text :
    {
        /* All unused bytes set to 0xFF. */
        FILL (0xff)
 
        /* ISR Vector Table first */
        _isr_vectors_offs = . - 0x08000000;
        KEEP(*(.isr_vector))
 
        . = ALIGN(4);
        *(.text)        /* Program code */
        *(.text.*)
        *(.rodata)      /* Constants */
        *(.fini)   
        *(.rodata*)
        *(.glue_7) 
        *(.glue_7t)
        . = ALIGN(4);
        _etext = .;
 
        _sidata = _etext;
         
    } > FLASH
 
    /* Initialized global and static variables */
    .data :
    {
        FILL (0xff)
         
        . = ALIGN(4);
        _sdata = .;    
        *(.data)
        *(.data.*)
        . = ALIGN(4);
        _edata = .;
         
    } > RAM AT> FLASH
 
    /* Uninitialized global and static variables */
    .bss :
    {
        . = ALIGN(4);
        _sbss = .;
        *(.bss)
        *(.bss.*)
        *(COMMON)
        . = ALIGN(4);
 
        _ebss = .;
         
    } > RAM
 
    /* Stack */
    . = _sstack;
    .stack :
    {
        . = . + STACK_SIZE;
    } > RAM
}

Compared to last section, this linker script has more stuff in it. It now specifies the stack to be 1 Kbytes. It also has separate sections for initialized and uninitialized global and static variables. Before application starts execution, the initialized data in section '.data' will be copied from Flash to RAM, wherease RAM required by the the uninitialized data in section '.bss' will be allocated and zeroed - in the startup file:

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
/************************************
 * stm32 timer led example startup.c
 ************************************/
 
#include "stm32f10x.h"
 
typedef void( *const intfunc )( void );
 
#define WEAK __attribute__ ((weak))
 
extern unsigned long _etext;
extern unsigned long _sidata;
extern unsigned long _sdata;
extern unsigned long _edata;
extern unsigned long _sbss;
extern unsigned long _ebss;
extern unsigned long _estack;
  
 
void Reset_Handler(void) __attribute__((__interrupt__));
void __Init_Data(void);
void SystemInit (void);
void Default_Handler(void);
 
extern int main(void);
 
/* Init Data
 * Loads data from addresses defined in linker file into RAM
 * Zero bss (statically allocated uninitialized variables)
 */
void __Init_Data(void) {
    unsigned long *src, *dst;
    /* copy the data segment into ram */
    src = &_sidata;
    dst = &_sdata;
    if (src != dst)
        while(dst < &_edata)
            *(dst++) = *(src++);
 
    /* zero the bss segment */
    dst = &_sbss;
    while(dst < &_ebss)
        *(dst++) = 0;
}
 
/* This function is straight from the system_stm32f10x.c library file and
 * is called within the startup file:
 * 1. After each device reset the HSI is used as System clock source.
 * 2. This function assumes that an external 8MHz crystal is used to drive the System clock.
 */
void SystemInit (void)
{
    /* Reset the RCC clock configuration to the default reset state */
    /* Set HSION bit */
    RCC->CR |= (uint32_t)0x00000001;
    /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
    RCC->CFGR &= (uint32_t)0xF8FF0000;
    /* Reset HSEON, CSSON and PLLON bits */
    RCC->CR &= (uint32_t)0xFEF6FFFF;
    /* Reset HSEBYP bit */
    RCC->CR &= (uint32_t)0xFFFBFFFF;
    /* Disable all interrupts and clear pending bits  */
    RCC->CIR = 0x009F0000;
    /* Enable HSE */
    RCC->CR |= ((uint32_t)RCC_CR_HSEON);
    /* Wait till HSE is ready */
    do
    {
    } while ((RCC->CR & RCC_CR_HSERDY) == RESET);
    /* Configure the Flash Latency cycles and enable prefetch buffer */
    FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_2;
    /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
    /* HCLK = SYSCLK, PCLK2 = HCLK, PCLK1 = HCLK / 2
     * If crystal is 16MHz, add in PLLXTPRE flag to prescale by 2
     */
    RCC->CFGR = (uint32_t)( RCC_CFGR_HPRE_DIV1  |
                            RCC_CFGR_PPRE2_DIV1 |
                            RCC_CFGR_PPRE1_DIV2 |
                            RCC_CFGR_PLLSRC_HSE |
                            RCC_CFGR_PLLMULL9 );
    /* Enable PLL */
    RCC->CR |= RCC_CR_PLLON;
    /* Wait till PLL is ready */
    while ((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
    /* Select PLL as system clock source */
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
    /* Wait till PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
    {
    }
}
 
void Reset_Handler(void) {
    /* Initialize data and bss */
    __Init_Data();
    extern uint32_t _isr_vectors_offs;
    SCB->VTOR = 0x08000000 | ((uint32_t)&_isr_vectors_offs & (uint32_t)0x1FFFFF80);
    SystemInit();
    while(1)
    {
      __Init_Data();
      main();
    }
    while(1) {}
}
 
void WEAK NMI_Handler(void);
void WEAK HardFault_Handler(void);
void WEAK MemManage_Handler(void);
void WEAK BusFault_Handler(void);
void WEAK UsageFault_Handler(void);
void WEAK MemManage_Handler(void);
void WEAK SVC_Handler(void);
void WEAK DebugMon_Handler(void);
void WEAK PendSV_Handler(void);
void WEAK SysTick_Handler(void);
 
void WEAK WWDG_IRQHandler(void);
void WEAK PVD_IRQHandler(void);
void WEAK TAMPER_IRQHandler(void);
void WEAK RTC_IRQHandler(void);
void WEAK FLASH_IRQHandler(void);
void WEAK RCC_IRQHandler(void);
void WEAK EXTI0_IRQHandler(void);
void WEAK EXTI1_IRQHandler(void);
void WEAK EXTI2_IRQHandler(void);
void WEAK EXTI3_IRQHandler(void);
void WEAK EXTI4_IRQHandler(void);
void WEAK DMA1_Channel1_IRQHandler(void);
void WEAK DMA1_Channel2_IRQHandler(void);
void WEAK DMA1_Channel3_IRQHandler(void);
void WEAK DMA1_Channel4_IRQHandler(void);
void WEAK DMA1_Channel5_IRQHandler(void);
void WEAK DMA1_Channel6_IRQHandler(void);
void WEAK DMA1_Channel7_IRQHandler(void);
void WEAK ADC1_2_IRQHandler(void);
void WEAK USB_HP_CAN1_TX_IRQHandler(void);
void WEAK USB_LP_CAN1_RX0_IRQHandler(void);
void WEAK CAN1_RX1_IRQHandler(void);
void WEAK CAN1_SCE_IRQHandler(void);
void WEAK EXTI9_5_IRQHandler(void);
void WEAK TIM1_BRK_IRQHandler(void);
void WEAK TIM1_UP_IRQHandler(void);
void WEAK TIM1_TRG_COM_IRQHandler(void);
void WEAK TIM1_CC_IRQHandler(void);
void WEAK TIM2_IRQHandler(void);
void WEAK TIM3_IRQHandler(void);
void WEAK TIM4_IRQHandler(void);
void WEAK I2C1_EV_IRQHandler(void);
void WEAK I2C1_ER_IRQHandler(void);
void WEAK I2C2_EV_IRQHandler(void);
void WEAK I2C2_ER_IRQHandler(void);
void WEAK SPI1_IRQHandler(void);
void WEAK SPI2_IRQHandler(void);
void WEAK USART1_IRQHandler(void);
void WEAK USART2_IRQHandler(void);
void WEAK USART3_IRQHandler(void);
void WEAK EXTI15_10_IRQHandler(void);
void WEAK RTCAlarm_IRQHandler(void);
void WEAK USBWakeUp_IRQHandler(void);
void WEAK TIM8_BRK_IRQHandler(void);
void WEAK TIM8_UP_IRQHandler(void);
void WEAK TIM8_TRG_COM_IRQHandler(void);
void WEAK TIM8_CC_IRQHandler(void);
void WEAK ADC3_IRQHandler(void);
void WEAK FSMC_IRQHandler(void);
void WEAK SDIO_IRQHandler(void);
void WEAK TIM5_IRQHandler(void);
void WEAK SPI3_IRQHandler(void);
void WEAK UART4_IRQHandler(void);
void WEAK UART5_IRQHandler(void);
void WEAK TIM6_IRQHandler(void);
void WEAK TIM7_IRQHandler(void);
void WEAK DMA2_Channel1_IRQHandler(void);
void WEAK DMA2_Channel2_IRQHandler(void);
void WEAK DMA2_Channel3_IRQHandler(void);
void WEAK DMA2_Channel4_5_IRQHandler(void);
 
__attribute__ ((section(".isr_vector")))
void (* const g_pfnVectors[])(void) = {
    (intfunc)((unsigned long)&_estack), /* The stack pointer after relocation */
    Reset_Handler,              /* Reset Handler */
    NMI_Handler,                /* NMI Handler */
    HardFault_Handler,          /* Hard Fault Handler */
    MemManage_Handler,          /* MPU Fault Handler */
    BusFault_Handler,           /* Bus Fault Handler */
    UsageFault_Handler,         /* Usage Fault Handler */
    0,                          /* Reserved */
    0,                          /* Reserved */
    0,                          /* Reserved */
    0,                          /* Reserved */
    SVC_Handler,                /* SVCall Handler */
    DebugMon_Handler,           /* Debug Monitor Handler */
    0,                          /* Reserved */
    PendSV_Handler,             /* PendSV Handler */
    SysTick_Handler,            /* SysTick Handler */
 
    /* External Interrupts */
    WWDG_IRQHandler,            /* Window Watchdog */
    PVD_IRQHandler,             /* PVD through EXTI Line detect */
    TAMPER_IRQHandler,          /* Tamper */
    RTC_IRQHandler,             /* RTC */
    FLASH_IRQHandler,           /* Flash */
    RCC_IRQHandler,             /* RCC */
    EXTI0_IRQHandler,           /* EXTI Line 0 */
    EXTI1_IRQHandler,           /* EXTI Line 1 */
    EXTI2_IRQHandler,           /* EXTI Line 2 */
    EXTI3_IRQHandler,           /* EXTI Line 3 */
    EXTI4_IRQHandler,           /* EXTI Line 4 */
    DMA1_Channel1_IRQHandler,   /* DMA1 Channel 1 */
    DMA1_Channel2_IRQHandler,   /* DMA1 Channel 2 */
    DMA1_Channel3_IRQHandler,   /* DMA1 Channel 3 */
    DMA1_Channel4_IRQHandler,   /* DMA1 Channel 4 */
    DMA1_Channel5_IRQHandler,   /* DMA1 Channel 5 */
    DMA1_Channel6_IRQHandler,   /* DMA1 Channel 6 */
    DMA1_Channel7_IRQHandler,   /* DMA1 Channel 7 */
    ADC1_2_IRQHandler,          /* ADC1 & ADC2 */
    USB_HP_CAN1_TX_IRQHandler,  /* USB High Priority or CAN1 TX */
    USB_LP_CAN1_RX0_IRQHandler, /* USB Low  Priority or CAN1 RX0 */
    CAN1_RX1_IRQHandler,        /* CAN1 RX1 */
    CAN1_SCE_IRQHandler,        /* CAN1 SCE */
    EXTI9_5_IRQHandler,         /* EXTI Line 9..5 */
    TIM1_BRK_IRQHandler,        /* TIM1 Break */
    TIM1_UP_IRQHandler,         /* TIM1 Update */
    TIM1_TRG_COM_IRQHandler,    /* TIM1 Trigger and Commutation */
    TIM1_CC_IRQHandler,         /* TIM1 Capture Compare */
    TIM2_IRQHandler,            /* TIM2 */
    TIM3_IRQHandler,            /* TIM3 */
    TIM4_IRQHandler,            /* TIM4 */
    I2C1_EV_IRQHandler,         /* I2C1 Event */
    I2C1_ER_IRQHandler,         /* I2C1 Error */
    I2C2_EV_IRQHandler,         /* I2C2 Event */
    I2C2_ER_IRQHandler,         /* I2C2 Error */
    SPI1_IRQHandler,            /* SPI1 */
    SPI2_IRQHandler,            /* SPI2 */
    USART1_IRQHandler,          /* USART1 */
    USART2_IRQHandler,          /* USART2 */
    USART3_IRQHandler,          /* USART3 */
    EXTI15_10_IRQHandler,       /* EXTI Line 15..10 */
    RTCAlarm_IRQHandler,        /* RTC Alarm through EXTI Line */
    USBWakeUp_IRQHandler,       /* USB Wakeup from suspend */
    TIM8_BRK_IRQHandler,
    TIM8_UP_IRQHandler,
    TIM8_TRG_COM_IRQHandler,
    TIM8_CC_IRQHandler,
    ADC3_IRQHandler,
    FSMC_IRQHandler,
    SDIO_IRQHandler,
    TIM5_IRQHandler,
    SPI3_IRQHandler,
    UART4_IRQHandler,
    UART5_IRQHandler,
    TIM6_IRQHandler,
    TIM7_IRQHandler,
    DMA2_Channel1_IRQHandler,
    DMA2_Channel2_IRQHandler,
    DMA2_Channel3_IRQHandler,
    DMA2_Channel4_5_IRQHandler,
};
 
#pragma weak MMI_Handler                = Default_Handler
#pragma weak MemManage_Handler          = Default_Handler
#pragma weak BusFault_Handler           = Default_Handler
#pragma weak UsageFault_Handler         = Default_Handler
#pragma weak SVC_Handler                = Default_Handler
#pragma weak DebugMon_Handler           = Default_Handler
#pragma weak PendSV_Handler             = Default_Handler
#pragma weak SysTick_Handler            = Default_Handler
#pragma weak WWDG_IRQHandler            = Default_Handler
#pragma weak PVD_IRQHandler             = Default_Handler
#pragma weak TAMPER_IRQHandler          = Default_Handler
#pragma weak RTC_IRQHandler             = Default_Handler
#pragma weak FLASH_IRQHandler           = Default_Handler
#pragma weak RCC_IRQHandler             = Default_Handler
#pragma weak EXTI0_IRQHandler           = Default_Handler
#pragma weak EXTI1_IRQHandler           = Default_Handler
#pragma weak EXTI2_IRQHandler           = Default_Handler
#pragma weak EXTI3_IRQHandler           = Default_Handler
#pragma weak EXTI4_IRQHandler           = Default_Handler
#pragma weak DMA1_Channel1_IRQHandler   = Default_Handler
#pragma weak DMA1_Channel2_IRQHandler   = Default_Handler
#pragma weak DMA1_Channel3_IRQHandler   = Default_Handler
#pragma weak DMA1_Channel4_IRQHandler   = Default_Handler
#pragma weak DMA1_Channel5_IRQHandler   = Default_Handler
#pragma weak DMA1_Channel6_IRQHandler   = Default_Handler
#pragma weak DMA1_Channel7_IRQHandler   = Default_Handler
#pragma weak ADC1_2_IRQHandler          = Default_Handler
#pragma weak USB_HP_CAN1_TX_IRQHandler  = Default_Handler
#pragma weak USB_LP_CAN1_RX0_IRQHandler = Default_Handler
#pragma weak CAN1_RX1_IRQHandler        = Default_Handler
#pragma weak CAN1_SCE_IRQHandler        = Default_Handler
#pragma weak EXTI9_5_IRQHandler         = Default_Handler
#pragma weak TIM1_BRK_IRQHandler        = Default_Handler
#pragma weak TIM1_UP_IRQHandler         = Default_Handler
#pragma weak TIM1_TRG_COM_IRQHandler    = Default_Handler
#pragma weak TIM1_CC_IRQHandler         = Default_Handler
#pragma weak TIM2_IRQHandler            = Default_Handler
#pragma weak TIM3_IRQHandler            = Default_Handler
#pragma weak TIM4_IRQHandler            = Default_Handler
#pragma weak I2C1_EV_IRQHandler         = Default_Handler
#pragma weak I2C1_ER_IRQHandler         = Default_Handler
#pragma weak I2C2_EV_IRQHandler         = Default_Handler
#pragma weak I2C2_ER_IRQHandler         = Default_Handler
#pragma weak SPI1_IRQHandler            = Default_Handler
#pragma weak SPI2_IRQHandler            = Default_Handler
#pragma weak USART1_IRQHandler          = Default_Handler
#pragma weak USART2_IRQHandler          = Default_Handler
#pragma weak USART3_IRQHandler          = Default_Handler
#pragma weak EXTI15_10_IRQHandler       = Default_Handler
#pragma weak RTCAlarm_IRQHandler        = Default_Handler
#pragma weak USBWakeUp_IRQHandler       = Default_Handler
#pragma weak TIM8_BRK_IRQHandler        = Default_Handler
#pragma weak TIM8_UP_IRQHandler         = Default_Handler
#pragma weak TIM8_TRG_COM_IRQHandler    = Default_Handler
#pragma weak TIM8_CC_IRQHandler         = Default_Handler
#pragma weak ADC3_IRQHandler            = Default_Handler
#pragma weak FSMC_IRQHandler            = Default_Handler
#pragma weak SDIO_IRQHandler            = Default_Handler
#pragma weak TIM5_IRQHandler            = Default_Handler
#pragma weak SPI3_IRQHandler            = Default_Handler
#pragma weak UART4_IRQHandler           = Default_Handler
#pragma weak UART5_IRQHandler           = Default_Handler
#pragma weak TIM6_IRQHandler            = Default_Handler
#pragma weak TIM7_IRQHandler            = Default_Handler
#pragma weak DMA2_Channel1_IRQHandler   = Default_Handler
#pragma weak DMA2_Channel2_IRQHandler   = Default_Handler
#pragma weak DMA2_Channel3_IRQHandler   = Default_Handler
#pragma weak DMA2_Channel4_5_IRQHandler = Default_Handler
 
void Default_Handler(void) {
    while (1) {}
}

The startup file is actually not as complicated as it looks. Basically it does the following:

  • Set up system clocks in 'SystemInit'
  • Copy initialized global and static variables from Flash to RAM, and allocate and zero uninitialized global and static variables in RAM in function '__Init_Data'
  • Assign all but the reset interrupt to the default handler 'Default_Handler', which does nothing and can be overridden in application code by virtue of the "#pragma weak' directive

 

Note also that I've included the header 'stm32f10x.h' for all those register name definitions.

The 'openocd.cfg' file and the 'write_bin.sh' file are the same as the previous section so I won't list them here. The Makefile, of course, is different because we have two C files now:

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
####################################
# stm32 timer led example Makefile
####################################
 
PROGRAM = timer_led
 
ENTRY_FILE = startup
 
ENTRY_CFILE = $(ENTRY_FILE).c
ENTRY_OFILE = $(ENTRY_FILE).o
 
ALL_OFILES = main.o $(ENTRY_OFILE)
 
TARGET_BIN = $(PROGRAM).bin
TARGET_ELF = $(PROGRAM).elf
 
CC = arm-none-eabi-gcc
LD = arm-none-eabi-ld
CP = arm-none-eabi-objcopy
 
LKR_SCRIPT = $(PROGRAM).ld
 
FAMILY = STM32F10X_MD
 
DEFINES = -D$(FAMILY)
INCLUDES = -I./Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/ \
            -I./Libraries/CMSIS/CM3/CoreSupport/ \
            -I./Libraries/STM32F10x_StdPeriph_Driver/inc
 
CFLAGS  = -c -march=armv7-m -mcpu=cortex-m3 -mthumb \
          -fno-common -nostdlib -fno-builtin -ffreestanding \
          -Wall -O0 -g  \
          $(DEFINES)
           
LFLAGS  = -nostartfiles -T$(LKR_SCRIPT)
CPFLAGS = -Obinary
 
.PHONY: all clean write
 
all: $(TARGET_BIN)
 
$(TARGET_BIN): $(TARGET_ELF)
    $(CP) $(CPFLAGS) $< $@
     
$(TARGET_ELF): $(ALL_OFILES)
    $(LD) $(LFLAGS) $(ALL_OFILES) -o $@
     
main.o: main.c
    $(CC) $(INCLUDES) $(CFLAGS) main.c -o $@
 
$(ENTRY_OFILE): $(ENTRY_CFILE)
    $(CC) $(INCLUDES) $(CFLAGS) $(ENTRY_CFILE) -o $@
 
clean:
    rm -rf *.o *.elf *.bin
 
write:
    ./write_bin.sh openocd.cfg $(TARGET_ELF)

Also note that we need to specify the inclusion path for the library files in the compiler options. Finally, here's the main application code:

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
/*********************************
 * stm32 timer led example main.c
 ********************************/
 
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_tim.h"
 
/* User defined function prototypes */
void GPIOA_Init(void);
void TIM2_CC1_Init(void);
void led_toggle(void);
 
/* Prototypes for func. from stm32f10x lib. */
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
 
int main(void)
{
    /* Initialize GPIOA PIN8 */
    GPIOA_Init();
     
    /* Initialize TIM2 Capture/Compare 1 in output compare mode */
    TIM2_CC1_Init();
     
    /* Toggle LED forever */
    while(1)
    {
        /* Do nothing, all happens in ISR */
    }
}  
 
/* Initialize GPIOA PIN8 */
void GPIOA_Init(void)
{
    /* Configuration info. for PORTA PIN8:
     * - Speed = 50MHz
     * - Push-pull output mode
     */
    GPIO_InitTypeDef gpioa_pin8_config = { GPIO_Pin_8,
                                           GPIO_Speed_50MHz,
                                           GPIO_Mode_Out_PP };
                                             
    /* Enable PORT A clock */
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; 
    /* Configure PORTA PIN 8 */
    GPIO_Init(GPIOA, &gpioa_pin8_config);  
     
    /* Turn off LED to start with */
    GPIOA->BSRR = (uint32_t)1 << 8;   
}
 
/* Toggle LED */
void led_toggle(void)
{
    /* If PORTA BIT 8 clear, set it */
    if((GPIOA->ODR & (uint32_t)1<<8) == 0)
    {
        GPIOA->BSRR = (uint32_t)1 << 8;
    }
    /* If PORTA BIT 8 set, clear it */
    else
    {
        GPIOA->BRR = (uint32_t)1 << 8;
    }
}
 
/* Configure TIM2 Capture/Compare 1 to work in output compare mode
 * so that the red LED flashes at 1HZ (toggle every 0.5s) */
void TIM2_CC1_Init(void)
{
    /* Enable TIM2 clock */
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
    /* Enable TIM2 global interrupt */
    NVIC->ISER[0] |= 0x10000000;
     
    /* Frequency after prescalar = 72MHz/(7199+1) = 10KHz.
     * Compare register = 5000 so a compare match occurs every 0.5s.
     */
    TIM2->PSC = (uint16_t)7199;
    TIM2->CCR1 = (uint16_t)5000;
     
    /* Enable Capture/Compare 1 interrupt */
    TIM2->DIER |= (uint16_t)0x0002;
         
    /* Enable TIM2 counter (in upcount mode) */
    TIM2->CR1 |= (uint16_t)0x0001;  
}
 
/* Timer 2 Interrupt Service Routine */
void TIM2_IRQHandler(void)
{
    /* Toggle LED if TIM2's Capture/Compare 1 interrupt occurred. */
    if(TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
    {
        led_toggle();
        /* Clear TIM2's Capture/Compare 1 interrupt pending bit. */
        TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
        /* Increment compare register by 5000 so next interrupt
         * occurs in 0.5s */
        TIM2->CCR1 += (uint16_t)5000;
    }  
     
    /* ===== Other TIM2 interrupt types can go below ======
     * .........
     */
}
 
/* This function comes straight from stm32f10x_gpio.c. All it does is
 * configure a GPIO pin's speed and mode etc. but seems so complicated.
 */
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;
  /* Check the parameters */
  /*
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
  assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin)); 
  */
   
/*---------------------------- GPIO Mode Configuration -----------------------*/
  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  {
    /* Check the parameters */
    /* assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed)); */
    /* Output mode */
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  }
/*---------------------------- GPIO CRL Configuration ------------------------*/
  /* Configure the eight low port pins */
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
    tmpreg = GPIOx->CRL;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = ((uint32_t)0x01) << pinpos;
      /* Get the port pins position */
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        /* Clear the corresponding low control register bits */
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);
        }
        else
        {
          /* Set the corresponding ODR bit */
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
          }
        }
      }
    }
    GPIOx->CRL = tmpreg;
  }
/*---------------------------- GPIO CRH Configuration ------------------------*/
  /* Configure the eight high port pins */
  if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  {
    tmpreg = GPIOx->CRH;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = (((uint32_t)0x01) << (pinpos + 0x08));
      /* Get the port pins position */
      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        /* Clear the corresponding high control register bits */
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
        /* Set the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
    GPIOx->CRH = tmpreg;
  }
}
 
/* This function comes straight from stm32f10x_tim.c. It checks
 * whether interrupt TIM_IT in TIMx has occurred or not.
 */
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT)
{
  ITStatus bitstatus = RESET; 
  uint16_t itstatus = 0x0, itenable = 0x0;
  /* Check the parameters */
  //assert_param(IS_TIM_ALL_PERIPH(TIMx));
  //assert_param(IS_TIM_GET_IT(TIM_IT));
    
  itstatus = TIMx->SR & TIM_IT;
   
  itenable = TIMx->DIER & TIM_IT;
  if ((itstatus != (uint16_t)RESET) && (itenable != (uint16_t)RESET))
  {
    bitstatus = SET;
  }
  else
  {
    bitstatus = RESET;
  }
  return bitstatus;
}
 
/* This function comes straight from stm32f10x_tim.c.
 * It clears the TIMx's TIM_IT interrupt pending bits.
 */
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
{
  /* Check the parameters */
  //assert_param(IS_TIM_ALL_PERIPH(TIMx));
  //assert_param(IS_TIM_IT(TIM_IT));
  /* Clear the IT pending Bit */
  TIMx->SR = (uint16_t)~TIM_IT;
}

I have not compiled any library C files in this project. All the '#include' are just for peripheral register name definitions. Instead, I simply copy and pasted the functions I need for this application into the main file so that I could focus on what exactly is required to get the code working. To build and program:

me@pandafruits:~$ make
me@pandafruits:~$ make write

The LED should flash at exactly 1Hz now. I'll do one more example with the UART next.

posted @ 2020-11-02 19:57  wdliming  阅读(185)  评论(0编辑  收藏  举报