【STM32】STM32 IAP BootLoader

Emma1111發表於2024-12-03

1.IAP簡介

IAP,即線上應用程式設計,IAP是使用者自己的程式在執行過程中對User Flash的部分割槽域進行燒寫,目的是為了在產品釋出後,可以方便地透過遺留的通訊口對產品中的韌體程式進行更新升級。

通常在實現IAP功能時,即使用者程式執行中作自身的更新操作, 需要在設計韌體程式時編寫
兩個專案程式碼, 第一個專案程式不執行正常的功能操作, 而只是透過某種通訊方式(如 USB、 USART)接收程式或資料, 執行對第二部分程式碼的更新; 第二個專案程式碼才是真正的功能程式碼。

這兩部分專案程式碼都同時燒錄在 User Flash 中, 當晶片上電後, 首先是第一個專案程式碼開始執行, 它作如下操作:

1.檢查是否需要對第二部分程式碼進行更新

2.檢查不需要更新則轉到4

3.執行更新操作

4.跳轉到第二部分程式碼執行

第一部分程式碼必須透過其它手段, 如 JTAG 或 ISP 燒入; 第二部分程式碼可以使用第一部分程式碼 IAP 功能燒入, 也可以和第一部分程式碼一起燒入, 以後需要程式更新時再透過第一部分 IAP 程式碼更新。
第一個專案程式碼稱為bootloader,第二個專案程式碼稱為APP程式。

他們存放在 STM32 FLASH 的不同地址範圍, 一般從最低地址區開始存放 Bootloader, 緊跟其後的就是 APP 程式(注意, 如果 FLASH 容量足夠,是可以設計很多 APP 程式)。

2.STM32正常的程式執行流程

STM32的內部flash的地址起始於0x08000000,STM32內部透過一張"中斷向量表"來響應中斷

image

程式啟動後:

首先從中斷向量表中取出復位中斷向量執行復位中斷程式完成啟動, 這張“中斷向量表” 的起始地址是 0x08000004, 當中斷來臨, STM32 的內部硬體機制亦會自動將 PC 指標定位到“中斷向量表” 處, 並根據中斷源取出對應的中斷向量執行中斷服務程式。

在上圖中, STM32 在復位後, 先從 0X08000004 地址取出復位中斷向量的地址, 並跳轉到復位中斷服務程式, 如圖示號①所示; 在復位中斷服務程式執行完之後, 會跳轉到們的 main 函式, 如圖示號②所示; 而我們的 main 函式一般都是一個死迴圈, 在 main 函式執行過程中, 如果收到中斷請求(發生重中斷) ,此時 STM32 強制將 PC 指標指回中斷向量表處, 如圖示號③所示; 然後, 根據中斷源進入相應的中斷服務程式, 如圖示號④所示; 在執行完中斷服務程式以後,程式再次返回 main 函式執行, 如圖示號⑤所示。

3.加入IAP後程式的執行流程

image

在該流程圖中, STM32 復位後, 還是從 0X08000004 地址取出復位中斷向量的地址, 並跳轉到復位中斷服務程式, 在執行完復位中斷服務程式之後跳轉到IAP 的 main 函式, 如圖示號①所示, 此部分同前面 STM32 正常流程圖一樣; 在執行完 IAP 以後(即將新的 APP 程式碼寫入 STM32 的 FLASH, 灰底部分。 新程式的復位中斷向量起始地址為 0X08000004+N+M) , 跳轉至新寫入程式的復位向量表, 取出新程式的復位中斷向量的地址, 並跳轉執行新程式的復位中斷服務程式, 隨後跳轉至新程式的 main 函式, 如圖示號②和③所示, 同樣 main 函式為一個死迴圈, 到此時 STM32 的 FLASH, 在不同位置上, 共有兩個中斷向量表。

在 main 函式執行過程中, 如果 CPU 得到一箇中斷請求, PC 指標仍強制跳轉到地址 0X08000004 中斷向量表處, 而不是新程式的中斷向量表, 如圖示號④所示; 程式再根據我們設定的中斷向量表偏移量, 跳轉到對應中斷源新的中斷服務程式中, 如圖示號⑤所示; 在執行完中斷服務程式後, 程式返回 main 函式繼續執行, 如圖示號⑥所示。

透過以上兩個過程的分析, 我們知道 IAP 程式必須滿足兩個要求:
1.新程式必須在IAP程式之後的某個偏移量為x的地址開始
2.必須將新程式的中斷向量表相應的移動,移動的偏移量為x;

我們有 2 個 APP 程式, 一個為 FLASH 的 APP, 程式在 FLASH 中執行, 另外一個位 SRAM 的 APP, 程式執行在 SRAM 中, 上圖雖然是針對 FLASH APP來說的, 但是在 SRAM 裡面執行的過程和 FLASH 基本一致, 只是需要設定向量表的地址為 SRAM 的地址。

3.1 中斷向量表的偏移量設定方法

系統啟動的時候,會首先呼叫SystemInit函式初始化時鐘系統,同時完成中斷向量表的設定,程式碼如下

/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif

VTOR暫存器存放的是中斷向量表的起始地址。預設情況下VECT_TAB_SRAM是沒有定義的,所以執行SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
對於flash app,設定為FLASH_BASE+偏移量0x10000(具體flash app的地址空間配置見下章),所以需要再FLASH APP的main函式最開頭處新增如下程式碼實現中斷向量表的起始地址的重設

image

當使用SRAM APP的時候,我們設定起始地址為:SRAM_BASE + 0x1000,同樣新增到SRAM APP的main函式開始處,新增如下:

image

以上就完成了中斷向量表偏移量的設定,如果此處設定和後面程式空間的配置不匹配,就可能會出現跳轉不到正確程式起始地址導致app執行不起來的情況。

4.應用程式空間配置

我的主控晶片是STM32F4ZGT6,有1024KB的Flash和192KB的SRAM。

Flash程式和SRAM程式和普通程式一樣,只是IROM空間和IRAM空間需要特殊配置一下。

此處先介紹一下Keil MDK中IROM1、IROM2、IRAM1和IRAM2表示的意義:

1.IROM1:表示內部只讀儲存器區域1(Internal ROM 1)。這是一個用於存放程式程式碼的區域,通常是Flash記憶體的一部分。在Keil中,IROM1的配置包括起始地址和大小,用於指定程式碼儲存的具體位置和空間大小。

2.IROM2:表示內部只讀儲存器區域2(Internal ROM 2)。這通常用於指定另一個程式碼儲存區域,可能用於存放程式的第二部分或者其他不需要連續儲存的程式碼段。在某些應用中,比如有多個Flash區域的微控制器,IROM2可以用來指定第二個Flash區域的配置。

3.IRAM1:表示內部隨機訪問儲存器區域1(Internal RAM 1)。這是用於存放變數的RAM區域,通常是微控制器內部的SRAM。IRAM1的配置包括起始地址和大小,用於指定變數儲存的具體位置和空間大小。

4.IRAM2:表示內部隨機訪問儲存器區域2(Internal RAM 2)。這可能是微控制器內部的另一個RAM區域,用於存放特定的變數或者資料,以實現更快的訪問速度或者隔離不同功能的記憶體需求。在一些高階的微控制器中,IRAM2可能指的是特定用途的記憶體區域,比如CCM(Core Coupled Memory)。

4.1 IAP-Bootloader空間配置

此處IROM1size設定成64k,明確一下範圍

image

實際編譯出來的bin為27k,預留出64k是足夠的,還是看具體需求,只要確保APP起始地址在

image

BootLoader以後,而且偏移量為0x200的倍數即可(M3權威指南P104頁有介紹)

4.2 Flash APP空間配置

image

IROM1從Flash 64K地址位置起始,預留960k,如果有兩個以上的flash app還可以再行劃分IRAM1預留了128k的記憶體空間(0x20000)

4.3 SRAM_APP空間配置

image

SRAM app將儲存地址設定了SRAM上,即0x20001000儲存起始,尺寸大小為0x19000

SRAM app的執行範圍緊隨儲存地址後面,從0x2001A000,執行大小為0x6000

5.我的IAP BootLoader程式

5.1 IAP的main函式

主函式功能流程如下:

image

key0的功能是將RXBUF中的flash app資料複製到預設的flash地址:

iap_write_appbin(FLASH_APP1_ADDR,USART1_RX_BUF,applenth);//更新FLASH程式碼 

函式實現原型

//appxaddr:應用程式的起始地址
//appbuf:應用程式CODE.
//appsize:應用程式大小(位元組).
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
	u32 t;
	u16 i=0;
	u32 temp;
	u32 fwaddr=appxaddr;//當前寫入的地址
	u8 *dfu=appbuf;
	for(t=0;t<appsize;t+=4)
	{						   
		temp=(u32)dfu[3]<<24;   
		temp|=(u32)dfu[2]<<16;    
		temp|=(u32)dfu[1]<<8;
		temp|=(u32)dfu[0];	  
		dfu+=4;//偏移4個位元組
		iapbuf[i++]=temp;	    
		if(i==512)
		{
			i=0; 
			STM32_FLASH_Write(fwaddr,iapbuf,512);
			fwaddr+=2048;//偏移2048  512*4=2048
		}
	} 
	if(i)STM32_FLASH_Write(fwaddr,iapbuf,i);//將最後的一些內容位元組寫進去.  
}

key2的功能是執行flash app,就是直接跳轉到預設的flash地址

if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判斷是否為0X08XXXXXX.
{	 
    iap_load_app(FLASH_APP1_ADDR);//執行FLASH APP程式碼
}else 
{
    printf("非FLASH應用程式,無法執行!\r\n");
    LCD_ShowString(30,210,200,16,16,"Illegal FLASH APP!");	   
}	

函式實現:

typedef  void (*iapfun)(void);		   
iapfun jump2app; 
u32 iapbuf[512]; 	//512位元組快取 

//設定棧頂地址
//addr:棧頂地址
__asm void MSR_MSP(u32 addr) 
{
	MSR MSP, r0 			//set Main Stack value
	BX r14
}

//跳轉到應用程式段
//appxaddr:使用者程式碼起始地址.
void iap_load_app(u32 appxaddr)
{
	if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)	//檢查棧頂地址是否合法.
	{ 
		jump2app=(iapfun)*(vu32*)(appxaddr+4);		//使用者程式碼區第二個字為程式開始地址(復位地址)		
		MSR_MSP(*(vu32*)appxaddr);			//初始化APP堆疊指標(使用者程式碼區的第一個字用於存放棧頂地址)
		jump2app();					//跳轉到APP.
	}
}	

key3的功能是執行SRAM app程式碼,直接跳到SRAM的地址上即可:

if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000)//判斷是否為0X20XXXXXX.
{	 
    iap_load_app(0X20001000);//SRAM地址
}else 
{
    printf("非SRAM應用程式,無法執行!\r\n");
    LCD_ShowString(30,210,200,16,16,"Illegal SRAM APP!");	   
}		

5.2 USART驅動

usart採用中斷接受,將接受到的資料存在快取的buf裡

#define USART1_REC_LEN		55*1024 //定義最大接收位元組數 55K

u8 USART1_RX_BUF[USART1_REC_LEN] __attribute__ ((at(0X20001000)));//接收緩衝,最大USART_REC_LEN個位元組,起始地址為0X20001000. 


// usart的中斷服務子函式
void USART1_IRQHandler(void)                	//串列埠1中斷服務程式
{
	u8 r;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中斷
	{
		r =USART_ReceiveData(USART1);//(USART1->DR);	//讀取接收到的資料
		if(USART1_RX_CNT<USART1_REC_LEN)
		{
			USART1_RX_BUF[USART1_RX_CNT]=r;
			USART1_RX_CNT++;			 									     
		}	 	
	} 
} 	


6.功能測試

使用串列埠工具,開啟編譯好的flash_app的bin檔案,點選傳送檔案就可以編譯好的bin檔案燒錄到板子中。

image

然後按下key1,key2就可以執行起flash app了。

7.Q&A:

1.我編譯出的bin檔案大小為64628大小,透過usart燒寫flash app後,按下key1_up後,正常應該是顯示

image

但是實際顯示了Illegal FLASH APP!

image

猜測原因應該是資料沒接收全,排查IAP程式發現,usart的buf大小設定最大為55k,而我的程式

image

為64k,所以將這個buf大小改為64k應該就可以了, 實測確實是這個原因

2.當flash app或sram app執行有問題的話,有限排查IROM和IRAM的配置,檢查是否出現記憶體越界或衝突的情況

相關文章