一.關於跳轉部分的代碼的理解(轉)app
這裏重點說一下幾句經典且很是重要的代碼:函數
第一句: if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000) //判斷棧定地址值是否在0x2000 0000 - 0x 2000 2000之間ui
怎麼理解呢? (1),在程序裏#define ApplicationAddress 0x8003000 ,*(__IO uint32_t*)ApplicationAddress) 即取0x8003000開始到0x8003003 的4個字節的值, 由於咱們的應用程序APP中設置把 中斷向量表 放置在0x08003000 開始的位置;而中斷向量表裏第一個放的就是棧頂地址的值spa
也就是說,這句話即經過判斷棧頂地址值是否正確(是否在0x2000 0000 - 0x 2000 2000之間) 來判斷是否應用程序已經下載了,由於應用程序的啓動文件剛開始就去初始化化棧空間,若是棧頂值對了,說應用程已經下載了啓動文件的初始化也執行了;.net
第二句: JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4); [ common.c文件第18行定義了: pFunction Jump_To_Application;]
debug
ApplicationAddress + 4 即爲0x0800 3004 ,裏面放的是中斷向量表的第二項「復位地址」 JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4); 以後此時JumpAddress指針
第三句: Jump_To_Application = (pFunction) JumpAddress;
startup_stm32f10x_md_lv. 文件中別名 typedef void (*pFunction)(void); 這個看上去有點奇怪;正常第一個整型變量 typedef int a; 就是給整型定義一個別名 acode
void (*pFunction)(void); 是聲明一個函數指針,加上一個typedef 以後 pFunction只不過是類型 void (*)(void) 的一個別名;例如:blog
[cpp] view plaincopy索引
因此,Jump_To_Application = (pFunction) JumpAddress; 此時Jump_To_Application指向了復位函數所在的地址;
第四 、五句: __set_MSP(*(__IO uint32_t*) ApplicationAddress); \\設置主函數棧指針
Jump_To_Application(); \\執行復位函數
Jump_To_Application()是把用戶代碼的復位地址付給PC指針,我看到Jump_To_Application()這句代碼debug的時候對應的彙編代碼是
LDR r0,[pc,#12] ;相對PC的數據加載,去函數指針的地址
LDR r0,[r0,#00] ;R0作索引,無偏移,數據裝載到R0,這個內容就是函數指針指向的內容,也就是函數的地址了,用戶程序的起始地址;
BLX r0 ;這個不解釋,說了是跳轉
正點原子代碼裏是這麼實現的:
//appxaddr:用戶代碼起始地址. void iap_load_app(u32 appxaddr) { if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)//檢查棧頂地址是否合法. { jump2app=(iapfun)*(vu32*)(appxaddr+4);//用戶代碼區第二個字爲程序開始地址(復位地址) MSR_MSP(*(vu32*)appxaddr);//初始化APP堆棧指針(用戶代碼區的第一個字用於存放棧頂地址) jump2app(); //跳轉到APP. } }