TOPS
原創版權,轉載請注明出處!
內容目錄
一、理論
1、 復位流程
2、 內存分布
二、 實戰 — boot
1、 工具:keil JLINK
2、 新建工程
3、 修改腳本
4、 修改代碼
5、 編譯下載
6、現象
三、 實戰 — app
1、 新建工程
2、 修改腳本
3、 修改代碼
4、 編譯下載
5、 在線仿真
注意:
問題:
總結
參考文獻:
一、理論
1、 復位流程
在離開復位狀態后,Cortex-M 做的第一件事就是讀取下列兩個 32 位整數的值:
1、從地址 0x0000,0000 處取出 MSP 的初始值。</li>
2、從地址 0x0000,0004 處取出 PC 的初始值——這個值是復位向量,LSB 必須是1,然后從這個值所對應的地址處取指。
在 0 地址處提供 MSP 的初始值,然后緊跟著就是向量表。向量表中的數值是 32位的地址,而不是跳轉指令。向量表的第一個條目指向復位后應執行的第一條指令。
因為 Cortex-M 使用的是向下生長的滿棧,所以 MSP 的初始值必須是堆棧內存的末地址加1。舉例來說,如果你的堆棧區域在 0x20007C00-0x20007FFF 之間,那么 MSP 的初始值就必須是 0x20008000。
向量表跟隨在 MSP 的初始值之后——也就是第 2 個表目。要注意因為 cortex-m 是在Thumb 態下執行,所以向量表中的每個數值都必須把 LSB 置1(也就是奇數)。正是因為這個原因,圖 3.18 中使用 0x101 來表達地址 0x100。當 0x100 處的指令得到執行后,就正式開始了程序的執行。在此之前初始化 MSP 是必需的,因為可能第1條指令還沒來得及執行,就發生了 NMI 或是其它 fault。MSP 初始化好后就已經為它們的服務例程準備好了堆棧。
2、 內存分布
需要修改工程中內存分配的腳本實現以上效果,這里是使用兩個工程設計,一個為 bootloader 工程,一個為 app 工程。
二、 實戰 — boot
1、 工具:keil JLINK
使用 S32DS 開發也可以,但是 S32DS 使用 jlink 下載程序的時候,芯片容易鎖死,而且飛思卡爾的芯片鎖死之后,解鎖都比較麻煩,所以暫時選擇 keil 開發。
2、 新建工程
使用 keil 建立 s32k144 工程并不難,只是 s32k144 與其他的芯片稍微有點不一樣,除了有一個匯編文件的啟動文件之外,還有幾個源文件,源文件的作用主要是從 flash 復制代碼到 ram 中,初始化 bss 段,關閉看門狗,配置時鐘等。
上圖紅色部分務必勾選。
紅色框部分即使內存分配腳本,這是待會兒需要修改的。工程建立好了之后,還需要進行一些配置。
在搞 STM32 的時候,有時候修改的就是這里,這里可以修改內存分配,但是我們這次使用的是用戶腳本,不是默認內存配置,所以這里不需要修改。當然還有生成 hex 文件、調試工具的選擇、輸出目錄等配置也需要進行,這里就不一一列舉了,
紅色部分不要勾選,紫色部分需要手動找到這個腳本,我們把代碼下載到 flash 就選擇 flash 腳本,下載到 ram 就選擇 ram 腳本。
紅色部分最好不勾選,因為如果直接使用在 keil 下載的話,這個選項在下載 app 代碼的時候,會全部擦除 flash,這時候 boot 會遭殃,會導致起不來,配置基本配置好了,接下來就是修改腳本了。
3、 修改腳本
Boot 的腳本基本不用怎么修改,就是修改一下 Boot 結束地址,這里開辟給 boot 的大小是 0x4000,相當于 16K 的大小。
#if (defined(__ram_vector_table__))
#define __ram_vector_table_size__ 0x00000400
#else
#define __ram_vector_table_size__ 0x00000000
#endif
#define m_interrupts_start 0x00000000
#define m_interrupts_size 0x00000400
#define m_flash_config_start 0x00000400
#define m_flash_config_size 0x00000010
#define m_text_start 0x00000410
//#define m_text_size 0x0007FBF0
#define m_text_size 0x00002000
#define m_interrupts_ram_start 0x1FFF8000
#define m_interrupts_ram_size __ram_vector_table_size__
#define m_data_start (m_interrupts_ram_start+m_interrupts_ram_size)
#define m_data_size (0x00008000 - m_interrupts_ram_size)
#define m_data_2_start 0x20000000
#define m_data_2_size 0x00007000
4、 修改代碼
代碼的修改主要是集中在,跳轉函數,需要定義一個函數指針類型,還要注意 Cortex-m 復位跳轉序列。
#include "S32K144.h" /* include peripheral declarations S32K144 */
#include "clocks_and_modes.h"
#include "gpio_led.h"
#define APP_ADDR 0x00004000
/******************************************************************************
*Local variables
******************************************************************************/
typedef void (*bootloader_fun)(void);
bootloader_fun jump2app;
void delay(volatile int cycles)
{
/* Delay function - do nothing for a number of cycles */
while(cycles--);
}
int main(void)
{
SOSC_init_8MHz(); /* Initialize system oscilator for 8 MHz xtal */
SPLL_init_160MHz(); /* Initialize SPLL to 160 MHz with 8 MHz SOSC */
NormalRUNmode_80MHz(); /* Init clocks: 80 MHz sysclk & core, 40 MHz bus, 20 MHz flash */
LED_PORT_init();
for(;;){
for(int i = 0;i < 5;i++){
led_triggle(2,1); /* turn on red LED */
led_triggle(1,0); /* turn on green LED */
led_triggle(0,0); /* turn on blue LED */
delay(720000);
delay(720000);
led_triggle(2,0); /* turn on red LED */
led_triggle(1,0); /* turn on green LED */
led_triggle(0,0); /* turn on blue LED */
delay(720000);
delay(720000);
}
jump2app = (bootloader_fun)*(uint32_t*)(APP_ADDR + 4);
jump2app();
}
return 0;
}
重點應該就是這句話:
jump2app = (bootloader_fun)(uint32_t)(APP_ADDR + 4);
APP 地址加上 4 ,就是因為 CORTEX-M 復位序列的 PC 指針位置的偏移量就是偏移 4,(uint32_t)(APP_ADDR + 4) 進行強制類型轉換,(bootloader_fun)(uint32_t*)(APP_ADDR+4) 取址,相當于取 0x4004 地址的值,放到 PC 指針,可能需要主義的還有一個就是 C 語言的語法,C 語言里面,函數名稱就是函數的入口地址,也就是說函數名稱其實也是一個指針,所以后面一句:jump2app();就是相當于執行一個函數。這樣就可以跳轉了。
5、 編譯下載
編譯就沒啥問題,下載就有兩種方式,一種是直接在keil 上下載,這個很簡單了,例外一個就是單獨打開 segger 下載,這時候需要配置一下這個軟件:
然后把編譯生成的 hex 文件,直接拖到頁面里面就可以了,按 F5 就可以下載了,boot的下載沒有什么特別的要求,兩個都可以下載。
6、現象
下載之后,可以看到小燈閃了 5 下就不閃了,說明執行到了后面的跳轉程序。
三、 實戰 — app
1、 新建工程
建立工程和 boot 一樣的過程,這里就不再贅述了。
2、 修改腳本
腳本位置:$(工程目錄)144_app\RTE\Device\S32K144UAxxxLLx 下
#if (defined(__ram_vector_table__))
#define __ram_vector_table_size__ 0x00000400
#else
#define __ram_vector_table_size__ 0x00000000
#endif
//#define m_interrupts_start 0x00000000
#define m_interrupts_start 0x00004000
#define m_interrupts_size 0x00000400
//#define m_flash_config_start 0x00000400
#define m_flash_config_start 0x00004400
#define m_flash_config_size 0x00000010
//#define m_text_start 0x00000410
#define m_text_start 0x00004410
//#define m_text_size 0x0007FBF0
#define m_text_size 0x0002FBF0
#define m_interrupts_ram_start 0x1FFF8000
#define m_interrupts_ram_size __ram_vector_table_size__
#define m_data_start (m_interrupts_ram_start + m_interrupts_ram_size)
#define m_data_size (0x00008000 - m_interrupts_ram_size)
#define m_data_2_start 0x20000000
#define m_data_2_size 0x00007000
App 腳本的修改稍微多一點,每個對應的偏移地址都需要改,向量表的地址,text 段的地址等。
3、 修改代碼
代碼基本就是正常的代碼,不用做任何的操作就可以了。
4、 編譯下載
編譯和 boot 一樣,下載的話,如果沒勾選 erase full chip ,可以直接下載,也可以使用 segger 下載。
5、 在線仿真
如果 app 的代碼只是下載,那每次調試都會比較麻煩,都得編譯成 hex 文件再下載才能驗證,因為腳本修改了向量表的地址,直接在線調試是不行的,這樣就很不方便開發了,網路上有網友通過修改一個腳本,讓 keil 識別到我們已經修改過的向量表的地址,在工程目錄新建 flashoffset.ini 文件,內容如下:
在 keil 配置里面導入這個文件
這樣就可以就行在線調試了,就和正常開發一樣方便了。
注意:
每個工程編譯完了之后,最好是查看 .map 文件,看一下內存分配是否都在劃定的內存里面
也算是一種調試信息手段吧。
問題:
這個 bootloader 整個流程雖然可以走通,但是還有問題,如果 app 工程開啟了中斷,app 的程序會跑飛。原因是什么呢?這個就是第一節說的 cortex-m 復位序列。
1、 boot 中斷未關;
2、接收升級文件驅動未提供;
3、Flash 驅動未提供;
4、App 程序重新定位向量表的基地址未修改。
總結
參考文獻:
《Cortex-M3 權威指南》
代碼下載:
https://pan.baidu.com/s/1fF3N_qpvMNtZOFGcB-HuIQ 密碼: 3ouv
https://pan.baidu.com/s/1TsZtd38C8hcLsVjJ113G5w 密碼:3tqg
微信公眾號:depthkernel
關注可了解更多嵌入式的教程。問題或建議,請公眾號留言;
QQ 群:135924744
如果你覺得對你有幫助,歡迎關注公眾號