IAP技術原理


IAP技術原理

更新記錄

version status description date author
V1.0 C Create Document 2018.10.17 John Wan
V2.0 M 對中斷向量表的理解有誤,以及理清判斷程序是否下載的原理 2018.10.19 John Wan

status:
C―― Create,
A—— Add,
M—— Modify,
D—— Delete。


IAP與ISP的概念及原理

ISP簡介

ISP:In System Programming 在系統中編程,通過芯片專用的串行編程接口對其內部的程序存儲器進行擦寫。

ISP原理

通過專用的程序下載器或程序仿真器(JTAG/ST-Link等)連接到目標板的調試接口,並通過PC端的軟件操作目標板的程序存儲器。

IAP簡介

IAP:In Application Programming 在應用中編程,通過調用特定的bootloader程序,對程序存儲器的指定段進行讀/寫操作,從而實現對目標板的程序的修改。

IAP原理

從軟件層面上將芯片的程序存儲器划分為兩個區域,其中區域①使用ISP的方式燒錄特定的bootloader程序,該程序只具備
兩種與業務無關的功能:

  • 1)通過某種通信方式(USB、USART、CAN等)接收程序或數據的功能;
  • 2)對存儲器的擦寫功能;

區域②則存放真正的項目代碼(app),而該區域的代碼則是通過區域①進行更改。

注:只要程序存儲器足夠,可以存放多個APP程序

IAP優勢

方便遠程進行app更新。

IAP的設計

硬件平台:STM32F103ZET6 ,Flash:512K,內置SRAM:64K

1、程序啟動流程

存儲器的啟動配置

lip_image002

圖1 參考《STM32F10xxx中文參考手冊》 2.4節 P33

以典型的Flash啟動為例

捕獲

圖2 參考《STM32F1開發指南-庫函數版本_V3.1》 五十二章 P713

  圖1的三種啟動方式,從系統存儲器啟動項,里面固化廠家出廠的代碼,不可重寫。
  結合圖1與圖2,代碼區始終從地址0x0000 0000開始,那么設置成Flash啟動之后,Flash的起始地址0x0800 0000就映射到了0x0000 0000,那么原本保存在0x0000 0000處的“中斷向量表”也相應可以通過0x0800 0000訪問中斷向量表是在芯片的啟動文件中,隨着Keil MDK的編譯,將中斷向量表放在執行文件的開始(可查看.hex.bin),又因為代碼執行的開始地址與Flash的起始地址形成了映射,所以才能在代碼執行的開始直接進入中斷向量表)。基於該原理,其它地址啟動的app程序首先要做的就是在程序的開始進行向量表的映射
  “中斷向量表”的功能主要是用來存儲相應中斷服務程序入口地址,當中斷來臨時,PC指針會從該表中尋找對應中斷服務程序入口地址。
  向量表的第一個4字節保存的是用戶程序堆棧空間的棧頂地址。程序啟動時,先從0x0800 0004地址處去取復位中斷向量的地址,並跳轉到復位中斷服務程序,執行完之后跳轉到IAP的main函數,如圖表①所示。如果在IAP過程接收到中斷請求,則PC指針強制指向中斷向量表處,根據中斷源進入相應的中斷服務程序。執行完之后,再次返回到main函數。
  在執行完IAP后,跳轉至新寫入程序的復位向量表,取出新程序的復位中斷向量的地址,並跳轉執行新程序的復位中斷服務程序,隨后跳轉至新程序的main函數,如圖標號②和③所示。
  在APP的main函數運行過程中,如果接收到中斷請求,PC指針仍強制跳轉到0x0800 0004(第一個中斷向量表),如圖標號④所示;程序再根據設置的中斷向量表偏移量,跳轉到對應中斷源新的中斷服務程序中,如圖標號⑤所示;執行完之后,返回main函數繼續運行,如圖標號⑥所示。

相應的其它啟動配置,例如內置SRAM啟動,確保映射了向量表,其余流程與之相同

2、中斷向量表的重定位

通過可編程的向量表偏移寄存器(VTOR)設定該程序的向量表所處的位置

向量偏移量寄存器

圖3 參考《Cortex-M3權威指南CnR2》 第8章 P116

注:該書翻譯的是Cortex-M3的r2p0版本

可通過ST的官方固件庫(CMSIS)控制

CMSIS 3.5, misc.c文件

/**
  * @brief  Sets the vector table location and Offset.
  * @param  NVIC_VectTab: specifies if the vector table is in RAM or FLASH memory.
  *   This parameter can be one of the following values:
  *     @arg NVIC_VectTab_RAM
  *     @arg NVIC_VectTab_FLASH
  * @param  Offset: Vector Table base offset field. This value must be a multiple 
  *         of 0x200.
  * @retval None
  */
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset)
{ 
  /* Check the parameters */
  assert_param(IS_NVIC_VECTTAB(NVIC_VectTab));
  assert_param(IS_NVIC_OFFSET(Offset));  
  
  SCB->VTOR = NVIC_VectTab | (Offset & (uint32_t)0x1FFFFF80);
}

在這里注意兩點:

  1. 向量表的起始地址要求

向量表的大小擴展為下一個 2 的整數次冪,且新向量表的起始地址必須對齊該值

  向量表中每個向量占用4個字節,然后乘上系統中的向量數,再將總字節數向上擴展到2的整數次冪,以stm32f103vet6為例,共有60(中斷) + 16(系統異常) = 76,向上擴展為2的整數次冪為128,從而向量表的起始地址必須是能被 128 * 4 = 512 = 0x200 整除,即 0x0,0x200,0x400......之類地址。

  1. 固件庫中 (Offset & (uint32_t)0x1FFFFF80)的由來?
  • 首先看VTOR寄存器

VTOR寄存器

圖4 參考《ARM Cortex-M3與Cortex-M4權威指南CnR3》 第7章 P179

r2p1版本VTOR寄存器

圖5 參考《Cortex_M3_Devices_Generic_User_Guide_UG0552》的4.3.4節

  了解到r2p1版本VTOR寄存器有效的位為bit[7:31],其中bit[29]決定的是向量表位於代碼(0)或SRAM(1)中,bit[7:28]決定的是向量表的起始地址,因此上面提到的地址理應為0x1FFFF FF80

3、IAP跳轉APP函數分析

3.1 執行流程:

  1. 判斷用戶是否已經下載程序
  2. 中斷配置初始化
  3. Flash加鎖
  4. 設置MSP指針
  5. 進行跳轉

3.2 完整代碼

#define  APP_FLASH_ADDR         0x08004000

/*********************************************************************
 *函數名:iap_jump_to_app
 *描述  :執行跳入APP
 *輸入  :appAddr
 *輸出  :無
 *返回值:無
 *說明:
***********************************************************************/
u8 iap_jump_to_app(u32 appAddr)
{
    u32 app_msp_addr;           //
    u32 app_jump_addr;
    void  (*pAppFun)(void);     //定義一個函數指針,用於指向app程序入口
    
    app_msp_addr = (*(vu32 *)APP_FLASH_ADDR);           //取棧頂地址保存的數據
    app_jump_addr = (*(vu32 *)(APP_FLASH_ADDR + 4));    //取出APP程序復位中斷向量的地址

    if ((app_msp_addr & 0x2FFE0000) != 0x20000000)      //檢查app棧頂地址是否合法,判斷是否已經下載程序
        return 1;
    
    nvic_reset();               //恢復NVIC為復位狀態,使中斷不再發生
    FLASH_Lock();

    __set_MSP(app_msp_addr);    //設置MSP指針指向app向量表的棧頂地址保存的地址
    pAppFun = (void (*)(void))app_jump_addr;        //生成跳轉函數
    (*pAppFun)();               //跳轉,PC指針執行復位
    
    return 0;
}

3.3 問題解析:判斷程序已經下載?

  從棧頂地址取出的數據即用戶代碼的堆棧地址,堆棧地址指向RAM,而RAM的地址在下面解釋。

STM32F10xxx SRAM存儲器映像

圖6 參考《STM32F10xxx中文參考手冊》的2.3.1節 P29

Cortex-M3存儲器映射

圖7 參考《Cortex_M3_Devices_Generic_User_Guide_UG0552》的2.2節

  參考圖7,Cortex-M3將 [0x2000 0000, 0x3FFF FFFF] 地址划定為SRAM區域,而在STM32F10xxx中SRAM的區域為64K,那么SRAM的實際有效地址就為 [0x2000 0000, 0x2000 FFFF];因此檢測棧頂地址是否合法及判斷棧頂地址是否在規定的SRAM地址區域內。有效位的檢查只需要看后面4位。即

上式中:
    if ((app_msp_addr & 0x2FFE0000) != 0x20000000)      //檢查app棧頂地址是否合法,判斷是否已經下載程序
        return 1;

准確的應修改為:
    if ((app_msp_addr & 0xFFFF0000) != 0x20000000)
        return 1;
  1. 為什么棧頂地址的區域是在SRAM中?

  SRAM:靜態隨機存取存儲器,Static Random-Access Memory,相當於內存的作用。代碼就是在該區域跑。因此代碼的堆棧也是在該區域。在Keil MDK進行設置時,也就是處於Target選項右下角的IRAM1內容,這里決定了編譯生成的執行文件的變量地址及堆棧地址所能存在的區域范圍

  1. 多種檢查方式判定app程序的下載狀態
  • 檢查接收到的app程序的棧頂地址是否符合要求 —— 從而判斷程序是否下載。
      那么就是從app程序的向量表中,獲取棧頂地址,然后判斷該棧頂地址是否在設定的區域內。例如上面的Flash啟動例子,從 0x0800 0000處取得棧頂地址,判定該地址是否在Keil MDK里面設置的SRAM區域。

  • 檢查接收到的app程序的中斷復位向量地址是否符合要求 —— 從而判斷接收到程序是Flash啟動類型還是SRAM啟動類型
      可以從接收到的程序的向量表的第二個4字節獲取中斷復位向量的地址,然后判斷該向量地址所處的區域是在Flash還是SRAM中,從而確定該程序應該存放的地方。

3.4 問題解析:NVIC的復位

防止在執行跳轉的過程中,被其它中斷打斷。
也可使用:

位於 core_cm3.c的
__set_FAULTMASK(1);       //屏蔽所有中斷

函數原型:

/**
 * @brief  Set the Fault Mask value
 *
 * @param  faultMask  faultMask value
 *
 * Set the fault mask register
 */
void __set_FAULTMASK(uint32_t faultMask)
{
  __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) );
}

FAULTMASK寄存器

圖8 參考《Cortex-M3權威指南CnR2》的3.2.2節 P39

另:參考《ARM Cortex-M3與Cortex-M4權威指南CnR3》的7.10.2節 P183FAULTMASK相比於另外兩個寄存器,它實際上會將當前優先級修改為-1,這樣甚至是HardFault處理也會被屏蔽,當其置位時,只有優先級比它更高的NMI異常處理才能執行。

3.5 “__set_MSP(app_msp_addr);”的解析

  該函數的原型如下:

位於 core_cm3.c

/**
 * @brief  Set the Main Stack Pointer
 *
 * @param  topOfMainStack  Main Stack Pointer
 *
 * Assign the value mainStackPointer to the MSP 
 * (main stack pointer) Cortex processor register
 */
__ASM void __set_MSP(uint32_t mainStackPointer)
{
  msr msp, r0
  bx lr
}

MSR指令的格式為:

MSR{條件}     程序狀態寄存器(CPSR戒SPSR)_<域>,操作數

MSR指令用於將操作數的內容傳送到程序狀態寄存器的特定域中。其中,操作數可以為通用寄存器或立即數。<域>用於設置程序狀態寄存器中需要操作的位,32位的程序狀態寄存器可分為4個域:

位[31:24]為條件標志位域,用f表示;
位[23:16]為狀態位域,用s表示;
位[15:8]為擴展位域,用x表示;
位[7:0]為控制位域,用c表示;

該指令通常用於恢復或改變程序狀態寄存器的內容,在使用時,一般要在MSR指令中指明將要操作的域。

指令示例:
MSR CPSR,R0	;傳送R0的內容到CPSR
MSR SPSR,R0	;傳送R0的內容到SPSR
MSR CPSR_c,R0    ;傳送R0的內容到SPSR,但僅僅修改CPSR中的控制位域

“bx lr”:等同於“MOV pc,lr” 即跳轉到lr中存放的地址處。
連接寄存器r14(LR),在ARM體系結構中有兩種特殊用途:

  1. 保存子程序返回地址。使用BLBLX時,跳轉指令自動把返回地址放入r14中;子程序通過把r14復制到PC來實現返回,通常用下列指令之一:
    MOV PC, LR
    BX LR
  1. 當異常發生時,異常模式的r14用來保存異常返回地址,將r14如棧可以處理嵌套中斷。LR中保存的值等於異常發生時PC的值減4(或者減2),因此在各種異常模式下可以根據LR的值返回到異常發生前的相應位置繼續執行。

4、APP跳轉IAP函數分析

4.1 完整代碼

#define  IAP_ADDR           0x08000000

/********************************************************************
函數: 運行IAP程序.		//
輸入: 無
返回: 無.不再返回.
說明:
    由於APP是在IAP的基礎上運行的,因此,IAP一定是有效的,這里不再作IAP有效性檢查.
    APP跳IAP
************************************************************************/
void app_jump_to_iap(void)
{
    u32  IapSpInitVal;           //IAP程序的SP初值.
    u32	 IapJumpAddr;            //IAP程序的跳轉地址.即,IAP程序的入口.
    void (*pIapFun)(void);       //定義一個函數指針.用於指向APP程序入口.

    //__set_FAULTMASK(1);       //屏蔽所有中斷
    //NVIC_SystemReset();       //軟件復位

    RCC_DeInit();
    nvic_reset();               //恢復NVIC為復位狀態.使中斷不再發生.

    __set_CONTROL(0);                       //將PSP指針切換為MSP指針

    IapSpInitVal = *(vu32 *)IAP_ADDR;       //取APP的SP初值.
    IapJumpAddr = *(vu32 *)(IAP_ADDR + 4);  //取程序入口.

	__set_MSP(IapSpInitVal);                     //設置SP.

    pIapFun = (void (*)(void))IapJumpAddr;       //生成跳轉函數.
    (*pIapFun)();                                //跳轉.不再返回.
}

4.2 基本的流程與IAP跳轉APP一致。

4.3 “_set_CONTROL(0);”的作用:

  該函數的原型如下:

位於 core_cm3.c

/**
 * @brief  Set the Control Register value
 *
 * @param  control  Control value
 *
 * Set the control register
 */
__ASM void __set_CONTROL(uint32_t control)
{
  msr control, r0
  bx lr
}

CONTROL寄存器:

CONTROL寄存器

圖9 參考《ARM Cortex-M3與Cortex-M4權威指南CnR3》的4.2.3節 P60

  環境:APP程序用了UCOSII系統,IAP程序是裸機
  參考《ARM Cortex-M3與Cortex-M4權威指南CnR3》的 3.2.7節 與 10.2節:
  其中:

主棧指針(MSP)為默認指針,用於處理模式,也可用於線程模式。
進程棧指針(PSP),只能用於線程模式,專門為了支持OS,保護進程安全。

不帶OS程序運行時,默認的狀態是特權模式,使用MSP指針;
而ucosii運行在特權模式下,線程使用的PSP指針.

  因為APP的程序是上OS的,而IAP程序是裸機的,那么在APP程序運行時,由於處於線程模式則使用的是PSP指針,而要跳轉的IAP程序由於沒上OS,理應使用的是MSP指針,因此在這需要進行使用指針的切換

  如果沒有"__set_CONTROL(0);" 跳轉進入IAP時,運行IAP的代碼狀態是特權模式,使用PSP指針,在配置NVIC寄存器是,出現hardfault,因此要么在跳進IAP之前在APP的代碼里面使用該語句將指針切換為MSP,要么在IAP代碼的起始處調用該語句,將指針切換為MSP,才能夠正常來回跳轉。

4.4 "NVIC_SystemReset();"的功能

  軟件的方式觸發自復位

/**
 * @brief  Initiate a system reset request.
 *
 * Initiate a system reset request to reset the MCU
 */
static __INLINE void NVIC_SystemReset(void)
{
  SCB->AIRCR  = ((0x5FA << SCB_AIRCR_VECTKEY_Pos)      | 
                 (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | 
                 SCB_AIRCR_SYSRESETREQ_Msk);                   /* Keep priority group unchanged */
  __DSB();                                                     /* Ensure completion of memory access */              
  while(1);                                                    /* wait until reset */
}

寄存器的參考《Cortex_M3_Devices_Generic_User_Guide_UG0552_A_》 4.3.5節
功能說明參考《ARM Cortex-M3與Cortex-M4權威指南CnR3》 13.4節

上式執行的是SYSRESETREQ類型的復位,在Cortex-M3里描述為產生到微控制器系統復位控制邏輯的系統復位請求,而系統復位控制邏輯又不屬於處理器設計,得根據微處理器的廠家來定,然后查詢
參考《STM32F10xxx_20xxx_21xxx_L1xxx_Programming_Mannual_PM0056_ENV6》 4.4.5節
STM32將該復位請求設置為復位除調試邏輯以外的所有

5、Keil MDK的設置

5.1 地址的設置

以上面Flash的例子IAP的程序占用16K,APP的地址從0x0800 4000開始,Keil MDK的設置

Keil MDK的設置

圖10 Keil MDK IROM1、IRAM1設置

類似的如果是以SRAM啟動,則相應的修改左邊的IROM1區域為啟動地址以及區域大小。

IROM1區域決定代碼的存儲區域,IRAM1區域決定運行內存和棧的存儲區域,

如果使用Jlink進行調試,那么在Jlink的Flash Download設置項,start 與 size 根據想要的調試的程序地址進行更改。

5.2 ".bin"文件

5.2.1 ".bin"文件與".hex"文件的區別

最大的區別是hex文件帶地址,bin文件不帶地址,因此bin文件的體積較小。具體的差異百度。

5.2.2 ".bin"文件的生成

使用Keil MDK里面的fromelf.exe工具,位於ARM\ARMCC\bin文件夾下。
具體的語法查看正點原子的《MDK如何生成bin文件》.doc

使用fromelf.exe工具的目的是將生成.axf文件轉換為.bin文件,
總體分為四個步驟:

  1. 選擇fromelf.exe所在的目錄,例如:F:\tool\Keil_v5\ARM\ARMCC\bin\fromelf.exe
  2. 配置fromelf.exe的語法選項,例如:--bin -o
  3. 選擇生成的文件路徑及文件名,例如:..\OBJ\RTC.bin;
  4. 選擇可執行文件路徑及文件名,例如:..\OBK\RTC.axf

因此整體的語句為:
F:\tool\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o ..\OBJ\RTC.bin ..\OBK\RTC.axf

注意:

  1. 整體語句是填在MDKOptions for target -> User -> After Build/Rebuild -> Run #1里面,並勾選;
  2. 文件的路徑:如果是..\表示項目文件所在目錄的上一級目錄下查找,也可輸入完整路徑以固定路徑形式查找;
  3. 文件的名字:可執行文件.axf的名字與路徑與項目編譯所生成文件的名字與路徑一致,即MDKOptions for target -> Output的設置項-> Name of Executable-> Select Folder for Objects生成的文件.bin路徑及文件名不作要求;
  4. 生成的文件可執行文件兩者的語法位置順序是根據fromelf.exe的語法來的,上面的整體語句也可寫成:
    F:\tool\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin ..\OBK\RTC.axf -o ..\OBJ\RTC.bin

使用fromelf反匯編keil生成的AXF文件

參考資料

官網資料:

  1. 《Cortex_M3_Devices_Generic_User_Guide_UG0552_A_》
  2. 《Cortex_M3_Processor_Technical_Reference_Mannual_I_r2p1》
  3. 《STM32F10xxx中文參考手冊_V10》
  4. 《STM32F10x_StdPeriph_Lib_V3.5.0》庫文件

書籍:

  1. 《STM32F1開發指南-庫函數版本_V3.1》——正點原子 第五十二章 串口IAP實驗 P711
  2. 《ARM Cortex-M3與Cortex-M4權威指南CnR3》——Joseph Yiu著 吳常玉 譯
  3. 《ARM Cortex-M3權威指南CnR2》——Joseph Yiu著 宋岩 譯

博客:
IAP升級功能編寫初期的一些困惑與疑問---完成功能后的總結
STM32IAP升級-----編寫IAP升級遇到的問題總結

STM32F4-IAP學習筆記(一)
STM32F4-IAP學習筆記(二)


免責聲明!

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



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