一步步學習操作系統(2)——在STM32上實現一個可動態加載kernel的"my-boot"


如果要做嵌入式Linux,我們首先要在板子上燒寫的往往不是kernel,而是u-boot,這時需要燒寫工具幫忙。當u-boot燒寫成功后,我們就可以用u-boot附帶的網絡功能來燒寫kernel了。每當板子上電時,u-boot一般會被加載到內存的前半段,如果我們的kernel之前就已經被燒寫到開發板了,那么u-boot會加載kernel到內存的后半段並跳轉到kernel的起始地址處執行(或者直接跳轉到kernel的起始地址處執行,如果kernel可以直接在flash上執行的話。)

如上圖所示,綠色部分為u-boot,紅色部分為kernel。

把loader(指u-boot)和kernel分離究竟有什么好處呢?

舉個極端的例子:沒有grub的話,我們就沒辦法做windows和linux雙系統了。這就是最大的好處。

然而對於嵌入式,我倒是說不出什么上得了台面的理由,根據個人喜好,我倒是有3點理由:

1、不用再求助燒寫工具了;

2、方便使用GNU交叉編譯工具;

3、擺脫Windows+linux虛擬機的工作平台。

現在,我的筆記本就可以輕松一下了,只需單開fedora/ubuntu就能工作啦!

以下是源碼和工程的下載鏈接:

kernel源碼及mdk5工程

kernel的arm-gcc源碼

my-boot源碼及mdk5工程

注:僅可使用在stm32f10x系列

 

接下來,我們將分為三部分敘述:

1、系統概述;

2、kernel;

3、“my-boot”;

4、先燒寫"my-boot“,然后用"my-boot”加載kernel——操作示例;

 

1、系統概述

接下來我們將建立兩個工程,一個是用來編譯kernel,一個用來編譯loader(姑且命名為“my-boot”)。首先,我們先把“my-boot”和kernel都編譯好,並通過燒寫工具把“my-boot”燒寫進stm32的flash中。然后,我們就可以重啟stm32,並使之運行“my-boot”。“my-boot”等待接收燒寫kernel的起始命令,當我們通過串口向“my-boot”發送了燒寫起始命令后,“my-boot”將把串口設置為DMA模式,並等待我們發送kernel的bin文件。接着,我們再通過串口傳送kernel的bin文件。傳送結束后,kernel也就被寫入stm32的RAM中了,同時“my-boot”把串口切換回通常的窗口通信模式。此時,芯片的控制權依舊被掌控在“my-boot”手中,不過,如果我們再向串口發送一條啟動kernel的指令,那么stm32將跳轉到kernel代碼處執行。至此,我們的目標達成。

 

2、kernel

我們的kernel很簡單,只有一個源文件,其功能就是不停的閃led。程序參考了博客http://www.cnblogs.com/sky1991/archive/2012/10/13/2722640.html的“例子一”,並加以修改與簡化,代碼給出如下:

  1 ;RCC寄存器地址映像
  2 RCC_BASE                EQU             0x40021000
  3 RCC_CR                  EQU             (RCC_BASE + 0x00)
  4 RCC_CFGR                EQU             (RCC_BASE + 0x04)
  5 RCC_CIR                 EQU             (RCC_BASE + 0x08)
  6 RCC_APB2RSTR            EQU             (RCC_BASE + 0x0C)
  7 RCC_APB1RSTR            EQU             (RCC_BASE + 0x10)
  8 RCC_AHBENR              EQU             (RCC_BASE + 0x14)
  9 RCC_APB2ENR             EQU             (RCC_BASE + 0x18)
 10 RCC_APB1ENR             EQU             (RCC_BASE + 0x1C)
 11 RCC_BDCR                EQU             (RCC_BASE + 0x20)
 12 RCC_CSR                 EQU             (RCC_BASE + 0x24)
 13 ;GPIO寄存器地址映像
 14 GPIOA_BASE              EQU             0x40010800
 15 GPIOA_CRL               EQU             (GPIOA_BASE + 0x00)
 16 GPIOA_CRH               EQU             (GPIOA_BASE + 0x04)
 17 GPIOA_IDR               EQU             (GPIOA_BASE + 0x08)
 18 GPIOA_ODR               EQU             (GPIOA_BASE + 0x0C)
 19 GPIOA_BSRR              EQU             (GPIOA_BASE + 0x10)
 20 GPIOA_BRR               EQU             (GPIOA_BASE + 0x14)
 21 GPIOA_LCKR              EQU             (GPIOA_BASE + 0x18)
 22 
 23 SETENA0                 EQU             0xE000E100
 24 SETENA1                 EQU             0xE000E104
 25 
 26 ;;FLASH緩沖寄存器地址映像
 27 FLASH_ACR               EQU             0x40022000
 28 
 29 ;-----------------
 30 MSP_TOP                 EQU             0x20005000              ;主堆棧起始值
 31 PSP_TOP                 EQU             0x20004E00              ;進程堆棧起始值
 32 
 33 DelayTime                EQU             13000000        ; to choose a better number to fit your cpu
 34 CLRPEND0                 EQU             0xE000E280    
 35 
 36 ;常數定義---------
 37 Bit0                    EQU             0x00000001
 38 Bit1                    EQU             0x00000002
 39 Bit2                    EQU             0x00000004
 40 Bit3                    EQU             0x00000008
 41 Bit4                    EQU             0x00000010
 42 Bit5                    EQU             0x00000020
 43 Bit6                    EQU             0x00000040
 44 Bit7                    EQU             0x00000080
 45 Bit8                    EQU             0x00000100
 46 Bit9                    EQU             0x00000200
 47 Bit10                   EQU             0x00000400
 48 Bit11                   EQU             0x00000800
 49 Bit12                   EQU             0x00001000
 50 Bit13                   EQU             0x00002000
 51 Bit14                   EQU             0x00004000
 52 Bit15                   EQU             0x00008000
 53 Bit16                   EQU             0x00010000
 54 Bit17                   EQU             0x00020000
 55 Bit18                   EQU             0x00040000
 56 Bit19                   EQU             0x00080000
 57 Bit20                   EQU             0x00100000
 58 Bit21                   EQU             0x00200000
 59 Bit22                   EQU             0x00400000
 60 Bit23                   EQU             0x00800000
 61 Bit24                   EQU             0x01000000
 62 Bit25                   EQU             0x02000000
 63 Bit26                   EQU             0x04000000
 64 Bit27                   EQU             0x08000000
 65 Bit28                   EQU             0x10000000
 66 Bit29                   EQU             0x20000000
 67 Bit30                   EQU             0x40000000
 68 Bit31                   EQU             0x80000000
 69     
 70 
 71 ;向量表*********************************************************************************
 72                 AREA            RESET, DATA, READONLY
 73 
 74                 DCD             MSP_TOP                   ;初始化主堆棧
 75                 DCD             Start                     ;復位向量
 76                 DCD             NMI_Handler               ;NMI Handler
 77                 DCD             HardFault_Handler         ;Hard Fault Handler
 78 ;***************************************************************************************
 79                 AREA            |.text|, CODE, READONLY
 80 ;主程序開始
 81                 ENTRY                           ;指示程序從這里開始執行
 82 Start
 83                 CPSID    I              ;關中斷
 84                 ldr     r0, =MSP_TOP        
 85                 msr        msp,    r0        ;重設MSP
 86                 mov     r0, #0            
 87                 msr     control, r0         ;切換MSP,並進入特權級
 88                 
 89                 mov     r0, #0
 90                 mov     r1, #0
 91                 mov     r2, #0
 92                 mov     r3, #0
 93                 mov     lr, #0
 94                 
 95                 ldr     r0, =CLRPEND0
 96                 ldr     r1, [r0]
 97                 orr     r1, #0xFFFFFFFF
 98                 str     r1, [r0]
 99                 
100                 
101 ;時鍾系統設置
102                 ;啟動外部8M晶振
103         
104                 ldr             r0,=RCC_CR
105                 ldr             r1,[r0]
106                 orr             r1,#Bit16
107                 str             r1,[r0]
108 ClkOk
109                 ldr             r1,[r0]
110                 ands            r1,#Bit17
111                 beq             ClkOk
112                 ldr             r1,[r0]
113                 orr             r1,#Bit17
114                 str             r1,[r0]                                                                                                                                                                                                                                                                              
115                 ;FLASH緩沖器
116                 ldr             r0,=FLASH_ACR
117                 mov             r1,#0x00000032
118                 str             r1,[r0]
119                 ;設置PLL鎖相環倍率為7,HSE輸入不分頻
120                 ldr             r0,=RCC_CFGR
121                 ldr             r1,[r0]
122                 orr             r1,#Bit18 | Bit19 | Bit20 | Bit16 | Bit14
123                 orr             r1,#Bit10
124                 str             r1,[r0]
125                 ;啟動PLL鎖相環
126                 ldr             r0,=RCC_CR
127                 ldr             r1,[r0]
128                 orr             r1,#Bit24
129                 str             r1,[r0]
130 PllOk
131                 ldr             r1,[r0]
132                 ands            r1,#Bit25
133                 beq             PllOk
134                 ;選擇PLL時鍾作為系統時鍾
135                 ldr             r0,=RCC_CFGR
136                 ldr             r1,[r0]
137                 orr             r1,#Bit18 | Bit19 | Bit20 | Bit16 | Bit14
138                 orr             r1,#Bit10
139                 orr             r1,#Bit1
140                 str             r1,[r0]
141                 ;其它RCC相關設置
142                 ldr             r0,=RCC_APB2ENR
143                 mov             r1,#Bit2
144                 str             r1,[r0]
145 ;IO端口設置
146                 ldr             r0,=GPIOA_CRH
147                 ldr             r1,[r0]
148                 orr             r1,#Bit0 | Bit1         ;PA.8輸出模式,最大速度50MHz 
149                 and             r1,#~Bit2 & ~Bit3       ;PA.8通用推挽輸出模式
150                 str             r1,[r0]
151 
152                 mov     r5, #0 ; led flag
153                 
154                 ;CPSIE    I
155 ;主循環=================================================================================
156 main
157                 bl                 Delay
158                 bl              LedFlas
159                 b               main
160 ;子程序**********************************************************************************
161 LedFlas
162                 push            {r0-r3}
163                 cmp             r5,#1
164                 beq             ONLED
165                 
166                 mov             r5, #1
167                 ;PA.8輸出1
168                 ldr             r0,=GPIOA_BRR
169                 ldr             r1,[r0]
170                 orr             r1,#Bit8
171                 str             r1,[r0]
172                 b               LedEx
173 ONLED
174                 mov             r5, #0
175                 ;PA.8輸出0
176                 ldr             r0,=GPIOA_BSRR
177                 ldr             r1,[r0]
178                 orr             r1,#Bit8
179                 str             r1,[r0]
180 LedEx
181                 pop            {r0-r3}
182                 bx              lr
183                 
184 Delay
185                 push  {r0-r3}
186                 
187                 ldr    r0, =DelayTime
188 Loop            CBZ    r0, LoopExit
189                 sub    r0, #1
190                 b        Loop
191 LoopExit        
192                 pop            {r0-r3}
193                 bx              lr
194 ;異常程序*******************************************************************************
195 NMI_Handler
196                 ;xxxxxxxxxxxxxxxxxx
197                 bx              lr
198 ;-----------------------------
199 HardFault_Handler
200                 ;xxxxxxxxxxxxxxxxxx
201                 bx              lr
202 ;***************************************************************************************
203                 ALIGN           ;通過用零或空指令NOP填充,來使當前位置與一個指定的邊界對齊
204 ;-----------------------------
205                 END

 

(1)主循環程序:

main

  bl Delay     // 延時
  bl LedFlas  // 翻轉led
  b main      // 跳轉會main開頭(即“延時”)

(2)延時程序:

Delay
  push {r0-r3}

  ldr r0, =DelayTime // r0 = DelayTime;
Loop

   CBZ r0, LoopExit // if(r0 != 0) {
  sub r0, #1    //   r0 -= 1;
  b Loop      //   goto Loop; }
LoopExit
  pop {r0-r3}
  bx lr

該延時程序是“C51式”的延時,就是純粹的讓CPU空跑n個周期,這里是“DelayTime=13000000“。“13000000”是隨便設的一個數,只是為了讓眼睛和耐性都能接受,時鍾頻率變化之后,這個數字可以自行的、隨性的去進行調整。

(3)LED翻轉程序:

LedFlas
  push {r0-r3}
  cmp r5,#1     // if(r5 == 1)
  beq ONLED     //   goto ONLED;

  mov r5, #1       //  r5 = 1;
  ;PA.8輸出1
  ldr r0,=GPIOA_BRR 
  ldr r1,[r0]       
  orr r1,#Bit8
  str r1,[r0]
  b LedEx
ONLED
  mov r5, #0       // r5 = 0;
  ;PA.8輸出0
  ldr r0,=GPIOA_BSRR
  ldr r1,[r0]
  orr r1,#Bit8
  str r1,[r0]
LedEx
  pop {r0-r3}
  bx lr

該LED翻轉程序以“r5”寄存器為標志,“r5”為0或1時,分別使PA.8輸出不同的電平(此處PA.8對應開發板上一個紅色LED)。


注:

一般MDK會生成hex文件,但不生成bin文件,所以我們還要給MDK加一些設置:

先找到fromelf.exe文件(一般在你的MDK安裝目錄里的bin目錄里),然后如下圖輸入,

如:

C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin --output kernel.bin kernel.axf

重新編譯之后,於是我們就得到kernel的bin文件了,即kernel.bin,留着備用。

此處的kernel是可以獨立運行的,所以不妨將該程序通過燒寫工具燒寫進開發板驗證一下。

Note:

如果要用arm-gcc的kernel,首先,你的Linux必須得有arm-gcc編譯工具。可使用目錄中提供的腳本build.sh直接編譯。此處我用的是“arm-none-eabi-as”等,如果是arm-linux-eabi-as等,需要簡單修改腳本中的“PREFIX”變量。

 

3、“my-boot”

我們知道,在kernel和loader之間,真正的主角是kernel,loader只是一個輔助工具罷了。然而,作為loader的"my-boot"在這里卻比kernel復雜許多。

“my-boot”以一步步學習操作系統(1)中的代碼為基礎,並將之整理了一下,把各個源文件分類到了不同的目錄。

如圖,除了obj目錄是存放編譯時所用的中間文件和hex文件外,其余4個目錄都存放源碼。

(1)arch目錄:其中的源碼均是和CPU架構相關,如中斷代碼、串口初始化、啟動代碼等;

(2)include目錄:所有的頭文件都在這里;

(3)kernel目錄:包含主函數、任務調度、延時相關的源碼;

(4)lib目錄:stm32f10x庫函數源碼及“printf”重定向至串口的輔助代碼(printf_to_serial)。

 

 

主程序一共建立3個任務:Task1, TaskBH, TaskDMA_Print。

 1 int main(void)
 2 {
 3     memset(SRAM_Buffer, 0, PAGE_SIZE);
 4     OSInit();
 5 
 6     OSTaskCreate(Task1, (void*)0, (OS_STK*)&Task1Stk[TASK_STACK_SIZE-1]);
 7     OSTaskCreate(TaskBH, (void*)0, (OS_STK*)&TaskBHStk[TASK_STACK_SIZE-1]);
 8     OSTaskCreate(TaskDMA_Print, (void*)0, (OS_STK*)&TaskDMA_PrintStk[TASK_STACK_SIZE-1]);
 9 
10     OSStart();
11 }

 

Task1:和kernel的功能一樣,也是不斷的閃led(最好是不同於kernel所使用的led),用來指示程序依舊正常運行,其功能很單純;

TaskBH:接受串口發送過來的相關命令,並向串口打印信息以提示命令發送成功。特別是當收到“startos”指令后,會置位變量“GotoKernelFlag”,以致后續代碼將跳轉到kernel運行,該任務是三個任務中最復雜的一個;

TaskDMA_Print:打印RAM中的kernel代碼。

 

其實以上三個任務的負擔並不重,身上擔子最重的時串口中斷程序:

串口通信遵循一個自定義的協議,協議內容如下:

 

將以下串口中斷程序與TaskBH結合着看,串口接受三種命令:

第一種:BURN命令:

如:

"BURN 0x08004000"

協議信息16進制表示為

57 41 4e 15 00 75 42 55 52 4e 20 30 78 30 38 30 30 34 30 30 30

該命令就是在通知開發板:“我要發送kernel了呦,趕緊准備接駕。”

這時,串口中斷程序會啟動串口的DMA模式,並開啟DMA中斷。

關於命令中的地址“0x08004000”,該值是設計為以后燒寫flash做准備的,但現在我們只將kernel寫入SRAM,所以現在還沒有特別的作用,任意值都可以。

這個命令發送之后就要小心了,緊跟着必須向串口發送kernel的bin文件。發送結束后,DMA中斷會被觸發,並且會調用“LED1TURN()”去翻轉另一個LED(不同於Task1的LED),用以指示kernel已經被寫入RAM。

 Note:看了以下代碼后,其實對於"BURN”這個命令來說,校驗和是形同虛設的,為了圖方便就偷了個懶……

 1 volatile void IRQ_Usart1(void)
 2 {
 3     
 4     RecvBuffer[Index] = serial_1;
 5 
 6     // Magic handling
 7     // Byte order: 0 1 2
 8     if(!MagicGotten) {
 9         if(0 == Index && 'W' == RecvBuffer[Index]) {
10             Index++;
11         }else if(1 == Index && 'A' == RecvBuffer[Index]) {
12             Index++;
13         }else if(2 == Index && 'N' == RecvBuffer[Index]) {
14             Index++;
15             MagicGotten = TRUE;
16         }else {
17             Index = 0;
18         }
19         return;
20     } 
21     
22     // Size handling
23     // byte order: 3 4
24     if(!SizeGotten) {
25         Index++;
26         if(5 == Index) {
27             SizeGotten = TRUE;
28             MsgSize = RecvBuffer[3] + (RecvBuffer[4] << 8);
29         }
30         if(SizeGotten && MsgSize > BUFSIZ) {
31             MagicGotten = FALSE;
32             SizeGotten = FALSE;
33             Index = 0;
34         }
35         return;
36     }
37 
38     // Checksum handling
39     // byte order: 5
40     if(!ChecksumGotten) {
41         Index++;
42         if(6 == Index) {
43             ChecksumGotten = TRUE;
44         }else {
45             MagicGotten = FALSE;
46             SizeGotten = FALSE;
47             Index = 0;
48         }
49         return;
50     }
51 
52     // Data handling:
53     // byte order: 6...
54     Index++;
55     if(Index >= MsgSize) {
56         MagicGotten = FALSE;
57         SizeGotten = FALSE;
58         ChecksumGotten = FALSE;
59         Index = 0;
60         MsgGotten = TRUE;
61         if(0 == strncmp((char *)RecvBuffer + 6, "BURN", 4)) {
62             USART_Cmd(USART1, DISABLE);
63             USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
64             USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); 
65             DMA1_Channel5->CNDTR = PAGE_SIZE;//re-load
66             DMA_Cmd(DMA1_Channel5, ENABLE);//re-open DMA
67             USART_Cmd(USART1, ENABLE);
68             LED1TURN();
69         }
70     }
71 }

 第二種:“startos”

協議信息16進制表示為

57 41 4e 0d 00 f8 73 74 61 72 74 6f 73

開發板接收到該命令后,TaskBH會將變量“GotoKernelFlag”設為1。之后,當SysTick中斷程序(如下)再次執行時,將會調用“ModifyPC()”(這里的“PC”不是指“Personal Computer”,而是指PC指令寄存器哦)。這個函數很難懂。如果能理解這個函數,那么loader加載kernel的原理也就等於理解了80%了。我們不妨來試着啃一啃這塊硬骨頭!

 1 volatile void IRQ_SysTick(void)
 2 {
 3      OS_ENTER_CRITICAL();
 4      if(GotoKernelFlag) ModifyPC();
 5      if((--TaskTimeSlice) == 0){
 6         TaskTimeSlice = TASK_TIME_SLICE;
 7         OSTaskSchedule();
 8     }
 9     TimeMS++;
10     
11     OS_EXIT_CRITICAL();
12 }

 

 “ModifyPC()”是嵌入C語言式的匯編代碼。其作用就是:

修改 PSP中存儲的“當前被SysTick中斷的任務”的 PC指針,使之等於kernel代碼的起始地址。當該任務再一次被調度時,由於PC被換成了kernel代碼的起始地址,所以就進入了kernel。

於是,兩個問題出現了:

(1)kernel的起始地址是什么?

(2)被SysTick中斷的任務的PC又在哪?

或許有人會認為:“kernel在DMA傳送時,被放進‘SRAM_Buffer’這個緩沖區了,那么kernel的起始地址不就是‘SRAM_Buffer’嗎?”(一開始我也是這么想的……)

可惜,真正的“起始地址”要比SRAM_Buffer在靠后一點點。

不妨在MDK5下,在kernel工程里打開Debug,接着再用二進制編輯器打開kernel.bin,這樣就能看出蹊蹺了。

stm32燒寫程序時,是將代碼燒至起始地址為0x08000000的flash中,並在開機運行時也是直接從flash啟動。

看到沒有,我們開機時的第一條命令是“CPSID I”,對應的指令地址為0x08000010,機器碼為“B672”。

再用二進制文件打開kernel.bin后,發現果然是“B672”(二進制文件為“小端法”表示,所以是“72 B6”)。

 

所以,我們的kernel代碼的起始地址,確切來說是第一條命令“CPSID I”的地址為“SRAM_Buffer + 0x10”。

Note:

“既然代碼燒寫進地址為0x08000000起始的地方,那么第一條指令為什么確實0x08000010呢?”

意味0x08000010 - 0x08000000 = 0x10 = 16 = 4*4,也即代碼開頭的“4個DCD”,每個DCD4字節。

 

第二個問題,“被SysTick中斷的任務”的PC到底在哪兒呢?

首先我們要知道,任務使用的是PSP(可參考“PendSV_Handler”的匯編代碼)。確認了這點之后,我們就可以繼續往下講了。

根據《Cortex-M3權威指南》--“chap09中斷的具體行為”--“入棧”,當SysTick中斷發生時,PSP會將發生如下圖的變化。

也就是說,當SysTick中斷發生時,CPU會自動將被中斷任務的R0-R3,R12,LR,PC,xPSR這8個寄存器裝載進PSP的后續存儲空間,並且PSP最后將指向被中斷任務R0寄存器的存儲地址。

那么被中斷任務的PC寄存器的存儲地址就找到啦:PSP+24!如果該任務再次被調度執行,其第一條指令就是地址“PSP+24”存儲的內容,如果我“偷偷的”把這個存儲內容換成kernel代碼的起始地址(確切來說,是第一條指令所在的地址),那么當該任務再次被調度時,原來的任務搖身一變,就成了kernel。

那么,ModifyPC()函數的代碼就比較容易理解了。

 

PCModifyPC偽代碼可寫為:

ModifyPC()

  PSP.PC = SRAM_Buffer+0x10 

1 __asm void ModifyPC(void) {
2     IMPORT SRAM_Buffer
3     MRS R0, PSP
4     LDR R1, =SRAM_Buffer
5     ADD R1, #0x10
6     STR R1, [R0, #24] 
7     BX LR
8     align 4
9 }

 

第三種:任意字符串

如:“ls”

協議信息16進制表示為

57 41 4e 08 00 31 6c 73

該命令將對TaskDMA_Print的行為產生影響(代碼如下)。

不難看出,只有當“ReadDMAFlag不為0時,該任務才會打印緩沖區SRAM_Buffer的內容。而在TaskBH中,上述命令會使變量“ReadDMAFlag”在0,1之間翻轉,所以該命令也就起到控制打印“SRAM_Buffer”內容的作用。

 1 void TaskDMA_Print(void *p_arg)
 2 {
 3     int i = 0;
 4     while(1) {
 5             delayMs(2000);
 6             if(!ReadDMAFlag) continue;
 7             printf("########DMA##########START\r\n");
 8             for(i = 0; i < PAGE_SIZE; i++) {
 9                 printf("%x ", SRAM_Buffer[i]);
10             }
11             printf("########DMA##########END\r\n");
12       
13     }
14 }

 “my-boot"中幾點注意事項:

(1)宏定義PAGE_SIZE

該宏在hardware.h中定義如下:

#define PAGE_SIZE 284

 

“284”?這個數字怎么這么莫名其妙?其實它表示的是kernel的大小(如下圖),同時它也決定了緩沖區SRAM_Buffer的大小。

如果我編譯了一個新的kernel,大小不再是284字節了怎么辦?

實在對不住!“my-boot”中的這個宏也要改成相應的數字。當然,這確實是個不合理的地方,但現在為使代碼盡可能簡潔,所以就未做完善這方面的工作了,暫且辛苦一下。

 

 

(2)預設宏定義:USE_STDPERIPH_DRIVER

為了使用stm32的函數庫,且避免編譯出錯,故定義該宏。具體內容可查詢“stm32f10x.h”第8296行附近的代碼。

 

 (3)庫函數文件:

如果stm32的型號不是stm32f10x系列的,需要自備相應的函數庫。

 

 4、先燒寫"my-boot“,然后用"my-boot”加載kernel——操作示例

(1)將“my-boot”燒進stm32開發板

(2)向stm32開發板發送燒寫命令:

BURN 0x08004000

16進制表示為
57 41 4e 15 00 75 42 55 52 4e 20 30 78 30 38 30 30 34 30 30 30

命令發送之后,串口工具會打印信息“Addr: 8004000”。而且還有一個變化,那就是另一個LED燈亮了/滅了(如果存在第二個led的話)。

注意,是16進制發送。

 

(3)發送kernel.bin

這時我們會發現,剛剛亮了/滅了的LED現在又滅了/亮了(如果存在第二個led的話)。

 

(4)打印剛剛燒進SRAM中的kernel命令(可選):

ls

16進制表示為

57 41 4e 08 00 31 6c 73

該命令發送一次,就會打印一次“###ls###”,並且跟后會打印SRAM中的內容。如果該命令只發送一次,那么SRAM中的打印將每隔2秒打印一次,直到再一次發送該命令為止。

所以圖中有2個“###ls###”,第二個就是終止打印的。

 

(5)啟動kernel:

startos

16進制表示為

57 41 4e 0d 00 f8 73 74 61 72 74 6f 73

這時你將會看到開發板在運行kernel的程序啦!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM