toolchain 的使用已經不在困擾我, 困難的是 cpu arm 架構與硬體平台, 這可要看不少資料。通過開發環境的試鍊後, 我打算點亮 led 燈來做為第一個 c 語言的練習程式。
不過沒想到這支作業系統之前的程式就難倒我, 是有關 io 的部份, 不像 前一個程式, 這個程式會用到平台相關的程式碼, 點亮 led, 得先要查詢接腳資料, 而查詢接腳資料是很磨人的, 我從 這裡的範例程式修改而來。這是 st 公司提供的 library, 可以省下不少看 datasheet 的功夫, 也可幫助程式 port 到其他 st 平台, 是很重要的參考資料。
一樣化繁為簡, 將程式庫的部份抽取出來, 簡化成一個小的作業系統之前的程式。
2 #include "stm32f4xx_gpio.h"
3 #include "stm32f4xx.h"
stm32f4xx_gpio.h, stm32f4xx.h, 從範例程式裡頭挖出來小改一下。大部份是 gpio, rcc 的 macro。stm32.h 是
coretex-m3 之 stm32 嵌入式系統設計的範例。由於沒有 SDRAM 控制器, 省下這部份的程式碼, lucky!!
stm32.h
1 #ifndef STM32_H
2 #define STM32_H
3 #define GPIOB_CRL (*(volatile unsigned long *)0x40010c00)
4 #define GPIOB_BSRR (*(volatile unsigned long *)0x40010c10)
5 #define GPIOB_BRR (*(volatile unsigned long *)0x40010c14)
6 #define RCC_APB2ENR (*(volatile unsigned long *)0x40021018)
7 #define STACK_SIZE 64
8 extern unsigned long _etext;
9 extern unsigned long _data;
10 extern unsigned long _edata;
11 extern unsigned long _bss;
12 extern unsigned long _ebss;
13
14 void ResetISR(void)
15 {
16 unsigned long *pulSrc, *pulDest;
17
18 pulSrc = &_etext;
19 for (pulDest = &_data; pulDest < &_edata;)
20 *pulDest++ = *pulSrc++;
21 for (pulDest = &_bss; pulDest < &_ebss;)
22 *pulDest++ = 0;
23
24 main();
25 }
26
27 typedef void (*pfnISR)(void);
28 __attribute__((section(".stackares")))
29 static unsigned long pulStack[STACK_SIZE];
30
31
32 __attribute__((section(".isr_vector")))
33 pfnISR VectorTable[]=
34 {
35 (pfnISR)((unsigned long)pulStack+sizeof(pulStack)),
36 ResetISR
37 };
38
39 #endif
mygpio_led.c
1 #include "stm32.h"
2 #include "stm32f4xx_gpio.h"
3 #include "stm32f4xx.h"
4
5 /*!< Uncomment the following line if you need to relocate your vector Table in
6 Internal SRAM. */
7 /* #define VECT_TAB_SRAM */
8 #define VECT_TAB_OFFSET 0x0 /*!< Vector Table base offset field.
9 This value must be a multiple of 0x200. */
10
11
12
13 void Delay(uint32_t delay )
14 {
15 while(delay) delay--;
16 }
17
18
19 void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState)
20 {
21 /* Check the parameters */
22 //assert_param(IS_RCC_AHB1_CLOCK_PERIPH(RCC_AHB1Periph));
23
24 //assert_param(IS_FUNCTIONAL_STATE(NewState));
25 if (NewState != DISABLE)
26 {
27 RCC->AHB1ENR |= RCC_AHB1Periph;
28 }
29 else
30 {
31 RCC->AHB1ENR &= ~RCC_AHB1Periph;
32 }
33 }
34
35 #define RCC_AHB1Periph_GPIOD ((uint32_t)0x00000008)
36
37
38 void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
39 {
40 uint32_t pinpos = 0x00, pos = 0x00 , currentpin = 0x00;
41
42 /* -------------------------Configure the port pins---------------- */
43 /*-- GPIO Mode Configuration --*/
44 for (pinpos = 0x00; pinpos < 0x10; pinpos++)
45 {
46 pos = ((uint32_t)0x01) << pinpos;
47 /* Get the port pins position */
48 currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
49
50 if (currentpin == pos)
51 {
52 GPIOx->MODER &= ~(GPIO_MODER_MODER0 << (pinpos * 2));
53 GPIOx->MODER |= (((uint32_t)GPIO_InitStruct->GPIO_Mode) << (pinpos * 2));
54
55 if ((GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT) || (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_AF))
56 {
57 /* Check Speed mode parameters */
58 //assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
59
60 /* Speed mode configuration */
61 GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (pinpos * 2));
62 GPIOx->OSPEEDR |= ((uint32_t)(GPIO_InitStruct->GPIO_Speed) << (pinpos * 2));
63
64 /* Check Output mode parameters */
65 //assert_param(IS_GPIO_OTYPE(GPIO_InitStruct->GPIO_OType));
66
67 /* Output mode configuration*/
68 GPIOx->OTYPER &= ~((GPIO_OTYPER_OT_0) << ((uint16_t)pinpos)) ;
69 GPIOx->OTYPER |= (uint16_t)(((uint16_t)GPIO_InitStruct->GPIO_OType) << ((uint16_t)pinpos));
70 }
71
72 /* Pull-up Pull down resistor configuration*/
73 GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << ((uint16_t)pinpos * 2));
74 GPIOx->PUPDR |= (((uint32_t)GPIO_InitStruct->GPIO_PuPd) << (pinpos * 2));
75 }
76 }
77 }
78
79 void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
80 {
81 /* Check the parameters */
82 //assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
83 //assert_param(IS_GPIO_PIN(GPIO_Pin));
84
85 GPIOx->BSRRL = GPIO_Pin;
86 }
87
88
89 void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
90 {
91 /* Check the parameters */
92 //assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
93 //assert_param(IS_GPIO_PIN(GPIO_Pin));
94
95 GPIOx->BSRRH = GPIO_Pin;
96 }
97
98
99
100
101 int main(void)
102 {
103 //init_led();
104 GPIO_InitTypeDef GPIO_InitStructure;
105
106
107 /* GPIOD Periph clock enable */
108 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
109
110 /* Configure PD12, PD13, PD14 and PD15 in output pushpull mode */
111 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
112 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
113 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
114 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
115 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
116 GPIO_Init(GPIOD, &GPIO_InitStructure);
117
118
119 while (1)
120 {
121 /* PD12 to be toggled */
122 GPIO_SetBits(GPIOD, GPIO_Pin_12);
123
124 /* Insert delay */
125 Delay(0x3FFFFF);
126
127 #if 0
128 /* PD13 to be toggled */
129 GPIO_SetBits(GPIOD, GPIO_Pin_13);
130
131 /* Insert delay */
132 Delay(0x3FFFFF);
133 #endif
134 /* PD14 to be toggled */
135 GPIO_SetBits(GPIOD, GPIO_Pin_14);
136
137 /* Insert delay */
138 Delay(0x3FFFFF);
139 #if 0
140 /* PD15 to be toggled */
141 GPIO_SetBits(GPIOD, GPIO_Pin_15);
142 /* Insert delay */
143 Delay(0x5FFFFF);
144 #endif
145 GPIO_ResetBits(GPIOD, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
146
147 /* Insert delay */
148 Delay(0x6FFFFF);
149 }
150
151
152 }
stm32.ld
1 MEMORY
2 {
3 FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 1024K
4 SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 192K
5 }
6
7 SECTIONS
8 {
9 .text :
10 {
11 KEEP(*(.isr_vector .isr_vector.*))
12 *(.text .text.*)
13 *(.rodata .rodata*)
14 _etext = .;
15 } > FLASH
16 .data : AT (_etext)
17 {
18 _data = .;
19 *(.data .data.*)
20 _edata = .;
21 } > SRAM
22 .bss(NOLOAD) :
23 {
24 _bss = .;
25 *(.bss .bss.*)
26 *(COMMON)
27 . = ALIGN(4);
28 _ebss = .;
29 } > SRAM
30 .stackarea(NOLOAD) :
31 {
32 . = ALIGN(8);
33 *(.stackarea .stackares.*)
34 . = ALIGN(8);
35 }
36 . = ALIGN(4);
37 _end = .;
38 }
程式結果就是先閃綠燈, 閃紅燈在一起熄滅, 如此循環下去。
其他和 io 相關的 .h 檔案就不列出。我要解說的部份只和開機程式有關, IO 部份請自己搞定, 這不是文章的重點。和 x86 不同, cortex-m3 可以
完全使用 c 語言來寫開機程式, 而不像 x86 需要 inline 組合語言。一開機, 位址 0~3 存放的值會被填到 sp, 完成 stack 設定, 在 c 語言中完成最重要的設定。
再來是 bss section:
14 void ResetISR(void)
15 {
16 unsigned long *pulSrc, *pulDest;
17
18 pulSrc = &_etext;
19 for (pulDest = &_data; pulDest < &_edata;)
20 *pulDest++ = *pulSrc++;
21 for (pulDest = &_bss; pulDest < &_ebss;)
22 *pulDest++ = 0;
23
24 main();
25 }
這段 code 便是在初使化 .bss, .data, 就這樣完成 c startup code, 完全不需要組合語言介入, 厲害的設計。
也許你想知道 .bss, .data 為什麼要初始化, 這不是很容易寫在 blog 上, 用個例子簡單說明:
int a=6;
void func()
{
static int i;
}
a 位於 .data, i 位於 .bss, 在程式執行時, a 要是 6, 而 i 要是 0, 這不是憑空得來的, 初始化 .bss, .data 就是在完成這樣的工作, 我知道, 這只能解除你一半的疑惑, 你一定想知道更多吧!
那 x86 的 bss section 也可以這樣寫嗎?由於有名的 segment address, 你得先確定你指的位址 0x100 真的是絕對位址 0x100 嗎?這是很容易搞亂的。
而透過 link script, section(".isr_vector") 會被放到 0 開頭的位址, 達到 0~3 是 stack value, 4 ~ 7 是 reset handle 的目的, 進而透過 ResetISR call main 去執行 main()。
按照之前的慣例, 下一個應該是 c++ 的版本, 不過 ...
source code:
https://github.com/descent/stm32f4_prog
ref:
gpio gpio push-pull or open drain:
http://www.coactionos.com/embedded-design/98-gpio-output-pin-modes.html
http://chunyenchu0818.pixnet.net/blog/post/40596842-gpio%E8%A8%AD%E5%AE%9Aod-or-pp
介紹一些 arm 指令:
http://jnotes.googlecode.com/svn-history/r105/trunk/Notes/NotesOnCM3/stm32WithGCC.html
books: coretex-m3 之 stm32 嵌入式系統設計