轉自:https://www.jianshu.com/p/1461fc7486b7
內容目錄
一、理論
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,然後從這個值所對應的地址處取指。
圖 1.1 復位序列.jpg
在 0 地址處提供 MSP 的初始值,然後緊跟着就是向量表。向量表中的數值是 32位的地址,而不是跳轉指令。向量表的第一個條目指向復位後應執行的第一條指令。
圖 1.2 初始化MSP及PC初始化一個範例.jpg
因爲 Cortex-M 使用的是向下生長的滿棧,所以 MSP 的初始值必須是堆棧內存的末地址加1。舉例來說,如果你的堆棧區域在 0x20007C00-0x20007FFF 之間,那麼 MSP 的初始值就必須是 0x20008000。
向量表跟隨在 MSP 的初始值之後——也就是第 2 個表目。要注意因爲 cortex-m 是在Thumb 態下執行,所以向量表中的每個數值都必須把 LSB 置1(也就是奇數)。正是因爲這個原因,圖 1.2 中使用 0x101 來表達地址 0x100。當 0x100 處的指令得到執行後,就正式開始了程序的執行。在此之前初始化 MSP 是必需的,因爲可能第1條指令還沒來得及執行,就發生了 NMI 或是其它 fault。MSP 初始化好後就已經爲它們的服務例程準備好了堆棧。
2、 內存分佈
圖 1.3 flash 內存分配圖.jpg
需要修改工程中內存分配的腳本實現以上效果,這裏是使用兩個工程設計,一個爲 bootloader 工程,一個爲 app 工程。
二、 實戰 — boot
1、 工具:keil JLINK
使用 S32DS 開發也可以,但是 S32DS 使用 jlink 下載程序的時候,芯片容易鎖死,而且飛思卡爾的芯片鎖死之後,解鎖都比較麻煩,所以暫時選擇 keil 開發。
2、 新建工程
使用 keil 建立 s32k144 工程並不難,只是 s32k144 與其他的芯片稍微有點不一樣,除了有一個彙編文件的啓動文件之外,還有幾個源文件,源文件的作用主要是從 flash 複製代碼到 ram 中,初始化 bss 段,關閉看門狗,配置時鐘等。
圖 2.1 新建工程.jpg
圖 2.2 保存工程.jpg
圖 2.3 選擇芯片類型.jpg
圖 2.4 選擇固件以及啓動代碼.jpg
上圖紅色部分務必勾選。
圖 2.5 工程目錄結構.jpg
紅色框部分即使內存分配腳本,這是待會兒需要修改的。工程建立好了之後,還需要進行一些配置。
圖 2.6 內存配置.jpg
在搞 STM32 的時候,有時候修改的就是這裏,這裏可以修改內存分配,但是我們這次使用的是用戶腳本,不是默認內存配置,所以這裏不需要修改。當然還有生成 hex 文件、調試工具的選擇、輸出目錄等配置也需要進行,這裏就不一一列舉了,
圖 2.7 用戶腳本選擇.jpg
紅色部分不要勾選,紫色部分需要手動找到這個腳本,我們把代碼下載到 flash 就選擇 flash 腳本,下載到 ram 就選擇 ram 腳本。
圖 2.8 下載配置.jpg
紅色部分最好不勾選,因爲如果直接使用在 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 下載,這時候需要配置一下這個軟件:
圖 2.9 segger 配置.jpg
圖 2.10 選擇芯片.jpg
然後把編譯生成的 hex 文件,直接拖到頁面裏面就可以了,按 F5 就可以下載了,boot的下載沒有什麼特別的要求,兩個都可以下載。
圖 2.11 編譯完成.jpg
6、現象
下載之後,可以看到小燈閃了 5 下就不閃了,說明執行到了後面的跳轉程序。
三、 實戰 — app
1、 新建工程
建立工程和 boot 一樣的過程,這裏就不再贅述了。
圖 3.1 app 目錄結構.jpg
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 文件,內容如下:
圖 3.2 app 在線調試腳本.jpg
在 keil 配置裏面導入這個文件
圖 3.3 導入在線調試腳本.jpg
這樣就可以就行在線調試了,就和正常開發一樣方便了。
注意:
每個工程編譯完了之後,最好是查看 .map 文件,看一下內存分配是否都在劃定的內存裏面
圖 3.4 app 工程的 .map 文件.jpg
也算是一種調試信息手段吧。
問題:
這個 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
作者:Depthkernelcore
鏈接:https://www.jianshu.com/p/1461fc7486b7
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。