引言
MM32F5微控制器基于Arm STAR-MC1微控制器,最高主頻可達120MHz,集成了FPU單元和DSP擴展指令集,有不錯的算力。但片內集成的128KB的RAM和256KB的FLASH,如果想支持代碼量比較大的軟件框架,就可能會力不從心,例如,TensorFlow Lite或者基于MicroPython的OpenMV這樣的應用就需要更多的內存空間做緩存。但MM32F5微控制器帶有FSMC接口和QSPI接口并支持基于QSPI的XIP(eXecute In Place,就地執行),可以分別外擴SRAM和FLASH存儲器,這就為擴展存儲資源提供了可能。在本文中,將介紹使用FSMC接口外接SRAM擴展內存的過程。在后續的文章中,在后續文章中,還會繼續介紹使用QSPI對接qspiflash存儲器實現外擴FLASH的過程。
硬件電路
MM32F5微控制器上集成了FSMC(Flexable Static Memory Controller)接口,可以外接并口的SRAM存儲器。
在PLUS-F5270開發板上,對應外擴了一個1MB大小的PSRAM存儲器作為擴展內存,如圖x所示。
圖x FSMC對接SRAM存儲器
軟件設計
使用FSMC接口外擴的SRAM存儲設備之前,必須先激活微控制器的FSMC接口,包括啟用對FSMC接口外設的訪問開關、配置FSMC接口對應的外部引腳,以及配置FSMC的時鐘源和工作模式等操作。基于這樣的使用前提,一般情況下使用外擴SRAM,都是在應用程序中激活FSMC硬件外設接口,之后通過在指定地址分配內存,或者訪問絕對地址的方式訪問新擴展出來的內存,但此時默認的主內存還是片內的SRAM。這種使用擴展SRAM的方式對于規模較小或者綁定具體應用的項目,因為涉及到對代碼的改動以及對存儲管理的工作量較小并且明確,在一定程度上是可以接受的。但對于移植已有現有的項目,或者是規模較大的框架性軟件,開發者通常不愿意(也不建議)深入到代碼庫中去人為指定每個可能的全局變量的絕對地址,僅將管理的目標地址區間從片內SRAM轉移到了外擴的SRAM而已,而希望能夠一如既往地讓編譯器自動管理內存的分配機制。
編譯器自動管理內存,就涉及到在芯片上電初始化過程中對編譯器運行時環境的初始化過程中對堆棧進行初始化,配置棧頂和棧底、堆底和堆頂指針等,也包括將內存中BSS段的數據清零,將DATA段數據的初值從FLASH搬運到SRAM中等。這些操作的過程,大多被封裝在集成開發環境自帶的庫中(例如Keil的__main
函數,經過一系列同編譯器相關的準備工作后才跳轉到用戶的main()
函數),不開放給用戶修改,而其中使用的和計算出的內存地址,也都是在編譯過程中預先定義的。
- 如果用戶強行在鏈接命令文件中指定默認的主內存空間為外擴存儲,那么在芯片啟動過程中,預定義的初始化運行時環境的操作,將會在未初始化好FSMC接口等硬件的時候直接訪問FSMC擴展出的內存空間,必然出錯。可能會提示的錯誤是hardfault,標記為訪問了無效的地址。此時,若是用戶在集成開發環境的
__main
函數之前的SystemInit()
函數中先激活FSMC等外擴SRAM相關的硬件也是可行的,但必須要注意,這個過程中,除了CPU中僅有的寄存器外,不能使用任何棧內存,因為此時燒寫在默認的中斷向量表首位的棧頂地址所指向的空間還是不可訪問的。具體來說,就需要用匯編命令完成對所有相關硬件外設的初始化操作,這確實是一個考驗人耐心的事情。這里簡單看一下SDK中的復位中斷服務程序中的啟動程序代碼,見代碼x。
代碼x SDK中的啟動程序代碼
Reset_Handler:
ldr r0, =__INITIAL_SP
msr psp, r0
ldr r0, =__STACK_LIMIT
msr msplim, r0
msr psplim, r0
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
ldr r0, =__STACK_SEAL
ldr r1, =0xFEF5EDA5U
strd r1,r1,[r0,#0]
#endif
bl SystemInit
bl __main
.fnend
.size Reset_Handler, . - Reset_Handler
- 一些技術高超的工程師可能會想到一些巧妙的做法,能不能先用缺省的片內SRAM支持編譯工具鏈的初始化過程,然后在應用程序中初始化FSMC外設(此時仍使用片內SRAM),然后再試圖重建內存管理系統,將芯片系統中內存相關的指針人為重建在外擴SRAM中呢?且不說這是一個極其麻煩的過程,需要把編譯工具鏈中的每個同內存相關的配置變量都翻出來重新人為計算并賦值一遍,一個明顯的限制在于,所有將要放在外擴的大SRAM中的數據必須在較小的片內SRAM中必須預先存放一份副本,之后在應用程序運行的過程中轉移到外擴SRAM。此時,在編譯階段,編譯器會限定整個程序能使用的存儲空間不能大于片內SRAM的大小,否則編譯器仍然會報錯并拒絕生成可執行文件。這就限制了能夠直接使用外擴SRAM的空間,同最常用的將外擴SRAM當成輔助存儲空間的做法沒有實質區別。
使用bootloader初始化硬件環境的思路
為了讓用戶的工程直接在一個可用的外擴SRAM上建立存儲管理系統,一個可行的設計,是使用額外使用一個bootloader工程(或者在芯片內部的電路實現上直接用一段ROM承載bootloader工程中的操作),在使用少量片內SRAM的條件下,通過常規的調用驅動API的方式(而不用匯編語句序列),先準備好使用FSMC的硬件環境,例如配置時鐘系統、引腳復用功能、FSMC接口外設等等。在bootloader工程最后的部分,直接跳轉到一個約定的、存放用戶application工程的地址,開始自行application工程。在application是一個完全獨立的工程,不用激活FSMC就可以直接使用外擴的SRAM,因此可以利用編譯工具鏈直接在外擴的SRAM上重建存儲管理系統。在application工程中,用戶將完全不用干預內存的分配情況,就像之前一樣完全交由編譯器自行管理;由于不再使用bootloader工程,片內的SRAM可以作為獨立的一塊可用的存儲空間(就像之前在片內SRAM看外擴SRAM一樣),繼續為應用程序提供存儲服務。
進一步分析,探討把bootloader工程放到ROM中的可能性。程序一旦寫入ROM中,就不能有任何改變了。但在配置外擴SRAM的時候,仍需要人為指定外擴SRAM映射的地址范圍(開始地址和空間大小),這個設定在不同應用場景中可能會不一樣,受成本和功能的權衡,可能有時候會用或大或小的存儲器設備,因具體選型不同配置參數也會隨之發生變化,因此不適合直接固化在ROM中。除非是對應合封的芯片,需要固定規格的SRAM芯片的晶元已經同微控制器一起被封在芯片內部,倒是不失為一種高集成的SOC解決方案。或者也可以用類似回調的方式,由用戶在某種基礎的協議下向ROM中的小程序提供外擴SRAM芯片專屬的配置,例如在手冊里說明在特定的用戶可編程的FLASH存儲區中存放了關于SRAM的配置信息,也是可行的,但處理過程就多了幾個步驟。沒有擴展多種SRAM的情況下,實在沒必要在芯片里設計這么一塊ROM。不過,這種方式在外擴FLASH的時候確實用到了,在后續的文章中將會提及,外擴FLASH的各廠家設計生產的nor FLASH型號芯片在使用上存在差異,在flashless的芯片中必須在ROM中設計程序首先識別外擴spiflash芯片的型號,從而使用對應的配置信息初始化spiflash芯片,到時也將會有一番細致地闡述。
接下來,將詳細介紹創建bootloader工程和用戶在application工程中開發應用的實現過程和應用要點。
創建bootloader工程
頂級執行流程函數main()
在實現最簡單功能的bootloader工程中,缺省使用片內的SRAM作為主內存設備,僅僅需要完成的工作包括:
- 初始化外擴SRAM的接口FSMC
- 在NVIC_VTOR寄存器中重定位中斷向量表的基地址。后續application工程中的中斷向量表將位于自己可執行二進制文件的最開始。application工程執行過程中,將通過NVIC_VTOR寄存器和中斷向量表項的偏移值確定實際的中斷服務程序入口地址。
- 為application工程的棧指針寄存器SP(MSP/PSP)賦初值。這個初值即為application工程中的中斷向量表的第一個表項中存放的數值,這個值是由application工程的鏈接器算出來的。
最后跳轉到application可執行程序的位置,后續執行application工程。
為了確定從芯片上電到執行application程序這段時間,bootloader確實按照預期正常工作,在本例實現的bootloader工程中使用了一個GPIO控制的小燈指示執行過程中可能出現的錯誤:
- 初始化配置指示燈亮
- 如果順利執行到跳轉到application的前一步,那么就可以熄滅指示燈,順利進入跳轉過程
- 如果在bootloader執行的過程中遇到任何問題,例如可以增加一個驗證外擴SRAM可以工作的檢測過程,就在原地等待,此時指示燈將保持常亮
本例創建的bootloader工程中的main()
函數,見代碼x。
代碼x bootloader工程中的main() 函數
/* define the memory range would be used in application firmware. */
#define BOARD_APP_EXEC_ROM_OFFSET (0x4000) /* 16KB. */
#define BOARD_APP_EXEC_ROM_BASE (0x08000000 + BOARD_APP_EXEC_ROM_OFFSET)
#define BOARD_APP_EXEC_ROM_LIMIT (0x08000000 + 0x80000)
#define BOARD_APP_EXEC_RAM_BASE (0x68000000) /* ext sram base address. */
#define BOARD_APP_EXEC_RAM_LIMIT (BOARD_APP_EXEC_RAM_BASE + 0x100000)
...
int main(void)
{
/* setup the boot clock, pins. */
BOARD_Init();
/* prepare a led to tell if everything is ok. */
app_led_init();
app_led_on();
/* setup the fsmc interface hardware for ext sram. */
app_init_sram();
/* check if the ext sram is ready. */
if (0u != app_check_image((void *)BOARD_APP_EXEC_ROM_BASE))
{
while (1); /* error: unavailable application firmware binary. */
}
/* restore as much as possible */
CLOCK_ResetToDefault();
/* turn off the led to tell every is finally ok. */
app_led_off();
/* jump to application. */
app_jump_to_image((void *)BOARD_APP_EXEC_ROM_BASE);
while (1); /* never run to this place. */
}
用戶實際開發application工程時,是不應該感受到這個附加的bootloader工程的,因此,bootloader的執行時間應盡量短,執行完畢后應盡量復原至芯片上電復位的狀態。在本例中,為了盡量加速bootloader工程的運行,在BOARD_Init()
函數中初始化啟用的PLL,從芯片內部的8MHz時鐘源倍頻到120MHz,用最快速度執行完bootloader的語句后,在臨跳轉到application工程之前,又將系統時鐘復原成原來上電缺省使用的8MHz內部時鐘,盡量還原到芯片剛上電后進入用戶程序的狀態。但同外擴內存相關的外設資源(引腳、時鐘等),則必須保持激活狀態。
驗證固件函數app_check_image()
關于驗證bootloader跳轉的硬件環境,本例做得比較簡單,僅在app_check_image()
函數中檢查即將在application中使用的棧頂指針值和復位向量入口(中斷向量表的前兩個表項)。如果希望做得更嚴謹一些,可以再把即將使用的外擴SRAM存儲空間都遍歷一遍,寫入數據之后再讀出來查看是否一致,以確認即將使用的SRAM也是有效的。本例創建的app_check_image()
函數,見代碼x。
代碼x 實現app_check_image()函數
void app_check_image(void * addr)
{
uint32_t * vectorTable = (uint32_t *)addr;
/* validate the addr for sp. */
if ((vectorTable[0] < BOARD_APP_EXEC_RAM_BASE) || (vectorTable[0] > BOARD_APP_EXEC_RAM_LIMIT ))
{
return 1u; /* unavailable sram area. */
}
/* validate the addr for pc. */
if ((vectorTable[1] < BOARD_APP_EXEC_ROM_BASE) || (vectorTable[1] > BOARD_APP_EXEC_ROM_LIMIT ))
{
return 2u; /* unavailable sram area. */
}
return 0u;
}
跳轉函數app_jump_to_image()
最關鍵的跳轉函數app_jump_to_image()
,見代碼x。
代碼x 實現app_jump_to_image()函數
typedef void(*func_0_t)(void);
volatile uint32_t sp_base;
volatile uint32_t pc_base;
void app_jump_to_image(void * addr)
{
uint32_t * vectorTable = (uint32_t *)addr;
sp_base = vectorTable[0];
pc_base = vectorTable[1];
/* set new MSP and PSP.
* when the SP is changed, the address of variables in stack would be remapped according to the new SP.
*/
__set_MSP(sp_base);
__set_PSP(sp_base);
#if __VTOR_PRESENT == 1
SCB- >VTOR = (uint32_t)addr; /* the func's param is kept in R1 register, which would not be changed per the SP update. */
#endif
/* jump to application. */
((func_0_t)(pc_base))();
//pc_func();
/* the code should never reach here. */
while (1)
{}
}
在app_jump_to_image()
函數中,通過傳入的即將跳轉到可執行二進制代碼區的首地址,提取位于可執行文件程序開始位置的中斷向量表的前兩個表項,分別為棧頂SP指針的初始值和PC指針的初始(復位中斷服務程序入口地址),然后用各自不同的方法將它們賦值到硬件寄存器中生效:MSP和PSP寄存器可以直接使用匯編語句賦值,而PC指針不能由程序直接操作,但通過函數跳轉命令實際可以載入新的PC值。
關于跳轉之前是否重新配置SP指針(微控制器內核中的MSP和PSP寄存器),這里也有一些考慮:
- 如果在application工程的啟動程序中,有重置棧指針的操作,那么在bootloader工程中就沒必要從application工程的文件中提取棧地址并重置棧頂指針了。但實際上大多數工程的啟動程序中都沒有這個步驟,而是依賴于微控制內核硬件的自動行為,從中斷向量表的第一個表項中提取棧頂地址作為內核棧指針的初值。
- 從完全模擬芯片啟動行為的角度上看,在進入application之前,仍然需要給application工程一個位于主內存空間中的缺省棧指針,就像bootloader工程中上電后執行的第一條指令時,硬件就已經自動從中斷向量表的第一個表項中提取了棧頂地址賦給棧指針。從bootloader跳轉到application的過程中,微控制器不會自動將application中斷向量表的第一個存放棧頂地址值,硬件自動為棧指針賦值的操作僅僅,只好由bootloader預先準備好。
關于重置SP指針的影響,這里也要特別說明。當在app_jump_to_image()
函數中執行__set_MSP(sp_base)
語句時,當前的棧指針就已經變了,此時,當前函數中使用的局部變量,還保存在原有的棧中,使用變化后的棧頂指針已經無法訪問原有棧中的內容了。因此,之后再使用的sp_base
和pc_base
變量都被定義成全局變量,存放在外部內存(仍位于片內SRAM中),而不是棧中。至于addr
變量,是來自于函數傳參,被存放在內核的Rn寄存器中,不受棧指針變化的影響。
通過把pc_base
賦值給PC寄存器,微控制器內核就轉而執行新的PC指針指向的程序,從而完成了跳轉到新程序的功能。
調試
改好代碼之后,編譯工程,就可以直接下載可執行文件到芯片中了。這個下載過程同正常下載工程沒有任何區別,還是將可執行文件的二進制代碼下載到片內FLASH存儲器上。
此時,如果片內FLASH中還沒有下載可用application文件到約定的地址上,有一定幾率被app_check_image()函數檢測為無效目標程序,直接卡在原地,并用指示燈常量警示用戶。也可能碰巧通過了檢測,bootloader工程最后跳轉之后將會“跑飛”。
如果片內FLASH中已經下載了可用的application工程文件,例如后續重新調試bootloader添加新功能的開發過程,處理器內核執行了bootloader的跳轉語句之后,就已經跳出了bootloader工程的控制范圍,進入了application工程的執行序列,屆時還需要配合application工程聯合調試。
約定分配bootloader和application工程的存儲空間
MM32F5微控制器內部存儲空間分布,如表x所示。
bootloader工程和application工程的可執行文件都存放在片內FLASH存儲器上。bootloader使用片內SRAM作為主SRAM,application使用外擴SRAM作為主SRAM,映射到FSMC Bank3的0x68000000 - 0x68100000的1MB大小的空間上。
bootloader工程的鏈接命令文件
bootloader工程和application工程的可執行文件都存放在片內FLASH存儲器上。芯片上電后,缺省先執行bootloader工程,故bootloader工程的程序位于片內FLASH存儲空間的首部,預留16KB。bootloader使用部分片內SRAM作為主SRAM,預留64KB,用戶也可以根據實際需要調整。
在bootloader工程的鏈接命令文件中有關于存放程序文件地址空間的定義。見代碼x。
代碼 x bootloader工程的鏈接命令文件
/*--------------------- FLASH Configuration ----------------------------------
; < h > FLASH Configuration
; < o0 > FLASH Base Address < 0x0-0xFFFFFFFF:8 >
; < o1 > FLASH Size (in Bytes) < 0x0-0xFFFFFFFF:8 >
; < /h >
*----------------------------------------------------------------------------*/
#define __ROM_BASE 0x08000000
#define __ROM_SIZE 0x00004000
/*--------------------- Embedded RAM Configuration ---------------------------
; < h > RAM Configuration
; < o0 > RAM Base Address < 0x0-0xFFFFFFFF:8 >
; < o1 > RAM Size (in Bytes) < 0x0-0xFFFFFFFF:8 >
; < /h >
*----------------------------------------------------------------------------*/
#define __RAM_BASE 0x30000000
#define __RAM_SIZE 0x00010000
application工程的鏈接命令文件
application工程的可執行文件也保存在片內FLASH上,位于bootloader程序文件之后。若在應用中沒有特別的需要,application工程可占用剩余的所有FLASH存儲空間。application使用外擴SRAM存儲作為主SRAM,映射到FSMC Bank3的0x68000000 - 0x68100000的1MB大小的空間上。
在application工程的鏈接命令文件中有關于存放程序文件地址空間的定義。見代碼x。
代碼x application工程的鏈接命令文件
/*--------------------- FLASH Configuration ----------------------------------
; < h > FLASH Configuration
; < o0 > FLASH Base Address < 0x0-0xFFFFFFFF:8 >
; < o1 > FLASH Size (in Bytes) < 0x0-0xFFFFFFFF:8 >
; < /h >
*----------------------------------------------------------------------------*/
#define __ROM_BASE 0x08004000
#define __ROM_SIZE 0x0003C000
/*--------------------- Embedded RAM Configuration ---------------------------
; < h > RAM Configuration
; < o0 > RAM Base Address < 0x0-0xFFFFFFFF:8 >
; < o1 > RAM Size (in Bytes) < 0x0-0xFFFFFFFF:8 >
; < /h >
*----------------------------------------------------------------------------*/
#define __RAM_BASE 0x68000000
#define __RAM_SIZE 0x00100000
在application工程中開發應用
試驗并查看分配在外擴SRAM的存儲空間
前文說到,用戶在application工程中開發應用,不需要專門配置外擴SRAM相關硬件的操作,即可直接使用外擴SRAM存儲作為主存儲器。不過,在application工程中仍需要調整一下鏈接文件,將FLASH存儲空間定義到片內FLASH存儲器上除了bootloader已經在首部占用的其余空間,將RAM空間定義到外擴存儲器映射的存儲空間中。這部分操作,已經在application工程的鏈接命令文件中配置好了,不需要用戶在代碼層面做任何特殊的設置。
在樣例工程application中定義全局變量 uint8_t ch;
見代碼x。
代碼x 在application工程中定義全局變量
#include "board_init.h"
uint8_t ch;
int main(void)
{
BOARD_Init();
printf("application.rn");
while (1)
{
ch = getchar();
putchar(ch);
}
}
編譯項目后,可以查看其中的project.map
文件中,編譯器自動為全局變量分配的內存位于外擴存儲的內存空間中。見代碼x。
代碼x 編譯application工程生成的project.map文件
Global Symbols
Symbol Name Value Ov Type Size Object(Section)
...
__stdin 0x68000000 Data 4 stdin.o(.data)
__stdout 0x68000004 Data 4 stdout.o(.data)
ch 0x68000008 Data 1 main.o(.bss.ch)
Image$$ARM_LIB_STACK$$ZI$$Base 0x680ff000 Number 0 anon$$obj.o ABSOLUTE
Image$$ARM_LIB_STACK$$ZI$$Limit 0x68100000 Number 0 anon$$obj.o ABSOLUTE
...
Execution Region RW_RAM (Exec base: 0x68000000, Load base: 0x08005724, Size: 0x0000000c, Max: 0x000fe000, ABSOLUTE)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x68000000 0x08005724 0x00000004 Data RW 310 .data mc_w.l(stdin.o)
0x68000004 0x08005728 0x00000004 Data RW 311 .data mc_w.l(stdout.o)
0x68000008 - 0x00000001 Zero RW 20 .bss.ch main.o
Execution Region ARM_LIB_HEAP (Exec base: 0x68000010, Load base: 0x0800572c, Size: 0x00001000, Max: 0x00001000, ABSOLUTE)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x68000010 - 0x00001000 Zero RW 1 ARM_LIB_HEAP.bss anon$$obj.o
Execution Region ARM_LIB_STACK (Exec base: 0x680ff000, Load base: 0x0800572c, Size: 0x00001000, Max: 0x00001000, ABSOLUTE)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x680ff000 - 0x00001000 Zero RW 2 ARM_LIB_STACK.bss anon$$obj.o
從代碼x中也可以看到,application工程的運行時全局變量數據區(RW_RAW)、堆空間(ARM_LIB_HEAP)、棧空間(ARM_LIB_STACK)也都位于0x6800_0000開始的外擴內存區間。
clock_init()
雖說在application工程中不需要用戶為使用外擴SRAM做任何特殊的設置,但由于使用了SDK代碼包,還是有一點編程要點要注意。SDK的編程規范里,有要求在芯片上電啟動過程中,用戶在使用硬件外設之前,要使用硬件復位操作復位將要使用外設模塊,以確保每次進入main()函數時,硬件外設的狀態都是從確定的初始狀態開始工作的。但在本例中,是通過bootloader引導進入的application,有一些已經激活的外設必須保持工作狀態,例如FSMC以及對應使用的GPIO等外設模塊,是不能在application工程中復位硬件的,否則,之前bootloader的準備工作就白費了,整個工程也不能正常工作。這里務必要再次確認clock_init.c文件中BOARD_InitBootClocks()函數中,關閉對FSMC和GPIO外設的復位操作,或者不要額外操作亦可。見代碼x。
代碼x application工程的BOARD_InitBootClocks()函數
void BOARD_InitBootClocks(void)
{
CLOCK_ResetToDefault();
CLOCK_BootToHSE120MHz();
/* UART1. */
RCC_EnableAPB2Periphs(RCC_APB2_PERIPH_UART1, true);
RCC_ResetAPB2Periphs(RCC_APB2_PERIPH_UART1);
/* GPIOA. */
//RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOA, true);
//RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOA);
/* GPIOB. */
//RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOB, true);
//RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOB);
/* GPIOC. */
//RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOC, true);
//RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOC);
/* GPIOD. */
//RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOD, true);
//RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOD);
/* GPIOE. */
//RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOE, true);
//RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOE);
/* GPIOF. */
//RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOF, true);
//RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOF);
/* GPIOG. */
//RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOG, true);
//RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOG);
}
調試
改好代碼之后,編譯工程,就可以直接下載可執行文件到芯片中了。這個下載過程同正常下載工程沒有任何區別,還是將可執行文件的二進制代碼下載到片內FLASH存儲器上。由于在設計鏈接命令文件時已經做好約定,application工程使用的片內FLASH存儲區同bootloader工程是錯開的,所以下載application工程的可執行文件到片內FLASH中不會沖掉之前下載的bootloader工程,但切記不要做全片擦除。
下載application工程后,可正常使用單步調試。這里可以理解一個要點:當集成開發環境中啟動調試模式時,會在本工程的main()函數或者復位服務程序的第一句下一個斷點,當用戶啟動“運行”操作后,才開始執行后續的程序。
- 在原來沒有bootloader引導的工程中,芯片上電后,直接進入用戶軟件的管轄范圍后,遇到預設的斷點就停了下來。
- 帶有bootloader引導的application工程中,芯片上電后,先執行bootloader代碼(沒有預設斷點),后來跳轉到application工程后,遇到了application中預設的斷點,才開始停下來。
這個過程是集成開發環境工具自動執行的,用戶在使用上不會有任何區別,只是可能會感受到啟動調試后到可以再次啟動“運行”操作中間等待的時間稍微長了一點。而這段等待的時間,正是bootloader在運行呢。
結論
本文探討了基于MM32F5微控制器的FSMC接口外接SRAM存儲器的用法,試圖尋找一種讓編譯器自動管理外擴內存的開發方法。使用bootloader工程引導application工程組合的方式,可以解決這個問題:在bootloader工程中初始化外擴SRAM的相關硬件,使得application工程可以在編譯過程中就可以將外擴SRAM用起來。最終用戶在application工程中開發自己的應用,可以直接使用外擴的大SRAM作為主內存,同時也可以將片內較小的SRAM作為輔助存儲繼續使用。
評論
查看更多