作業系統之前的程式 for stm32f4discovery (2) - 點亮 led, c version

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 嵌入式系統設計

你可能感兴趣的:(作業系統之前的程式 for stm32f4discovery (2) - 點亮 led, c version)