stm32-IAP(在線升級程序)

第一章 背景知識

什麼是IAP?

IAP的知識網上的各種資料也說的比較明白,在此簡單介紹一下。IAP( In Application Programming)即在線應用編程,也就是用戶可以使用自己的程序對單片機的User Flash的某一區域(一般爲存放自己程序的區域)進行燒寫。在真正的工作中產品發佈後,可以很方便的使用預留的通信接口(串口、USB、網口、藍牙等)來完成程序的升級,從而避免了把機器拆開使用下載器燒寫程序。
要實現IAP功能一般要設計兩部分代碼,

  • 一是BootLoader程序,這部分程序存儲在FLASH的某一位置,主要用來引導、升級App程序;
  • 二是App程序,這個程序纔是實現產品的功能程序。通過BootLoader來完成對App程序的更新升級,這就是IAP功能。

最終要實現的是:

  • 單片機每次上電會先運行Boot程序,檢查標誌位如果標誌位爲FLAG_TO_APP則直接跳轉到App程序運行,如果標誌位爲FLAG_TO_BOOT,則運行Boot程序準備升級。
  • App程序在運行時,正常情況下正常完成其功能,當接收到升級的指令後(升級指令一般來自某個外設的中斷,如上位機從串口發來升級指令,或者上位機從網絡發來升級指令)會在FLASH中的某處空間(參數區)寫下升級的標誌位FLAG_TO_BOOT,並且加載Boot程序,Boot程序會接受新的程序文件並且存儲在相應的FLASH空間裏,完成升級後會在標誌位的空間寫下FLAG_TO_APP,並且運行新的程序。

一、stm32的內存映射

參考博文:STM32 IAP 在線升級詳解
操作前我們先來說一下內存映射:
下圖在stm32f100芯片手冊的29頁,我們只截取關鍵部分
在這裏插入圖片描述
在這裏插入圖片描述
注意: 根據啓動方式不同,地址空間中從0x0000 0000到0x07FF FFFF這段空間,可以是flash空間或system memory的映射(別名)。

一般而言,flash內存的開始地址是0x0800 0000.
在這裏插入圖片描述

所以我們需要先查看一下misc.h文件中的中斷向量表的初始位置宏定義爲 NVIC_VectTab_Flash 0x0800 0000

還要設置編譯器keil 中的 options for target 的target選項中的 IROM1地址 爲0x0800 0000 大小爲 0x20000即128K; IRAM1地址爲0x2000 0000 大小爲0x2000;
在這裏插入圖片描述
(提示:這一項IROM1 地址 即爲當前程序下載到flash的地址的起始位置)

二、解析STM32的啓動方式

具體的啓動文件和解讀,請見另一篇博文:stm32–啓動文件(.s)與啓動過程
當前的嵌入式應用程序開發過程裏,並且C語言成爲了絕大部分場合的最佳選擇。如此一來main函數似乎成爲了理所當然的起點——因爲C程序往往從main函數開始執行。但一個經常會被忽略的問題是:微控制器(單片機)上電後,是如何尋找到並執行main函數的呢?很顯然微控制器無法從硬件上定位main函數的入口地址,因爲使用C語言作爲開發語言後,變量/函數的地址便由編譯器在編譯時自行分配,這樣一來main函數的入口地址在微控制器的內部存儲空間中不再是絕對不變的。相信讀者都可以回答這個問題,答案也許大同小異,但肯定都有個關鍵詞,叫“啓動文件”,用英文單詞來描述是“Bootloader”。

無論性能高下,結構簡繁,價格貴賤,每一種微控制器(處理器)都必須有啓動文件,啓動文件的作用便是負責執行微控制器從“復位”到“開始執行main函數”中間這段時間(稱爲啓動過程)所必須進行的工作。最爲常見的51,AVR或MSP430等微控制器當然也有對應啓動文件,但開發環境往往自動完整地提供了這個啓動文件,不需要開發人員再行干預啓動過程,只需要從main函數開始進行應用程序的設計即可。

話題轉到STM32微控制器,無論是keil uvision4還是IAR EWARM開發環境,ST公司都提供了現成的直接可用的啓動文件,程序開發人員可以直接引用啓動文件後直接進行C應用程序的開發。這樣能大大減小開發人員從其它微控制器平臺跳轉至STM32平臺,也降低了適應STM32微控制器的難度(對於上一代ARM的當家花旦ARM9,啓動文件往往是第一道難啃卻又無法逾越的坎)。

相對於ARM上一代的主流ARM7/ARM9內核架構,新一代Cortex內核架構的啓動方式有了比較大的變化。ARM7/ARM9內核的控制器在復位後,CPU會從存儲空間的絕對地址0x000000取出第一條指令執行復位中斷服務程序的方式啓動,即固定了復位後的起始地址爲0x000000(PC = 0x000000)同時中斷向量表的位置並不是固定的。
而Cortex-M3內核則正好相反,有3種情況:

  • 1、 通過boot引腳設置可以將中斷向量表定位於SRAM區,即起始地址爲0x2000000,同時復位後PC指針位於0x2000000處;
  • 2、 通過boot引腳設置可以將中斷向量表定位於FLASH區,即起始地址爲0x8000000,同時復位後PC指針位於0x8000000處(最常用的方式);
  • 3、 通過boot引腳設置可以將中斷向量表定位於內置Bootloader區(system memory:0x1FFF F000—0x1FFF F7FF),本文不對這種情況做論述;
    而Cortex-M3內核規定,起始地址必須存放堆頂指針,而第二個地址則必須存放復位中斷入口向量地址,這樣在Cortex-M3內核復位後,會自動從起始地址的下一個32位空間取出復位中斷入口向量,跳轉執行復位中斷服務程序。對比ARM7/ARM9內核,Cortex-M3內核則是固定了中斷向量表的位置而起始地址是可變化的。
    在這裏插入圖片描述

三、stm32的啓動過程

(1)沒有IAP,只有APP時的正常啓動流程:
STM32的FLASH地址起始於0x08000000,程序文件就從此地址開始寫入。此外STM32內部通過“中斷向量表”來響應中斷,程序啓動後,將首先從“中斷向量表”取出復位中斷向量執行復位中斷程序完成啓動,而“中斷向量表”的起始地址是0x08000004,當中斷來臨,STM32的內部硬件機制亦會自動將PC指針定位到“中斷向量表”處,並根據中斷源取出對應的中斷向量執行中斷服務程序。
在這裏插入圖片描述
根據上圖分析啓動和運行過程,

① STM32在復位後,先從0X08000004地址取出復位中斷向量的地址,並跳轉到復位中斷服務程序,
② 在復位中斷服務程序執行完之後,會跳轉到的main函數(如使用KEIL MDK調試時一下載進程序,會發現需要運行幾次下一步纔會跳轉到main函數的位置)
③ main函數一般都是超循環體(while(1)死循環),在main函數執行過程中,如果收到中斷請求(發生重中斷),此時STM32強制將PC指針指回中斷向量表處
④ 根據中斷源進入相應的中斷服務程序,
⑤ 在執行完中斷服務程序以後,程序再次返回main函數執行。

(2)加入IAP後的啓動流程
在這裏插入圖片描述

根據上圖分析加入IAP後的起動和運行過程

① STM32復位後,還是從0X08000004地址取出復位中斷向量的地址,並跳轉到復位中斷服務程序,在運行完復位中斷服務程序之後跳轉到IAP的main函數,如將IAP看作是一個APP的話,那麼此部分和正常起動是一樣的。(此步=執行復位中斷服務程序+跳轉main,即將正常運行的①和②合併了)。

② 在執行完IAP以後(固件升級或直接跳轉),跳轉至APP的復位向量表(APP的復位中斷向量起始地址爲0X08000004+N+M)。

③ 取出APP的復位中斷向量的地址,並跳轉執行新程序的復位中斷服務程序,隨後跳轉至APP的main函數(此步=執行復位中斷服務程序+跳轉main)

④ 同樣main函數爲一個超循環,並且注意到此時STM32的FLASH,在不同位置上,共有兩個中斷向量表。在main函數執行過程中,如果CPU得到一箇中斷請求,PC指針仍強制跳轉到地址0X08000004中斷向量表處,而不是APP程序的中斷向量表。

⑤ 程序再根據我們設置的中斷向量表偏移量,跳轉到對應中斷源的APP的中斷服務程序中,

⑥ 在執行完中斷服務程序後,程序返回main函數繼續運行。

四、IAP時flash的分配

IAP時,flash空間如何分佈?
例子1:
我們在設計程序時把FLASH分成3部分,第一部分從0x08000000到0x0800FFFF共64K來存放BootLoader程序,第二部分爲0x08010000 到0x0802FFFF共128K來存放App程序,第三部分從0x08030000開始到0x803FFFF共64K來存放程序運行的標誌位和其他,如下所示:
在這裏插入圖片描述

例子2:
參考:如何使用STM32F4的BootLoader和APP程序
怎麼使用stm32寫IAP的bootloader和APP
在這裏插入圖片描述

  • 假設我用的是stm32f103c8t6,它的flash的大小是64k,所以把它分成如上所示:
    0x08000000 —0x0800 33FF分配給bootloader使用,大小是13k
    0x0800 3400----0x080097FF分配給第一個APP的使用,大小是25k
    0x08009800----0x0800 FBFF分配給第二個APP(或者緩存遠程下載的新的代碼文件)的使用,大小是25k
    0x0800FC00----0x0800 FFFF 分配給user_flag和其它標誌使用,大小是1k

  • 在keil中設置rom的大小(因爲bootloader和每個app都是單獨的工程,所以要分別設置其rom的大小)
    A、bootloader中rom大小的設置
    在這裏插入圖片描述
    B、APP1中rom大小的設置
    在這裏插入圖片描述
    C、APP2(緩衝區)中rom大小的設置
    在這裏插入圖片描述

第二章 具體步驟

參考:STM32具備升級功能的bootloader及APP/IAP的實現
附件:示例工程鏈接
https://github.com/zengzhaorong/stm32_IAP-demo

一、整體規劃

整體上,在flash上燒寫2個程序,bootloader和APP。
bootloader程序位於0x80000000處,即默認的程序啓動地址;
APP程序則位於bootloader程序的往後某地址,空間大小需自行定義。

flash空間規劃
STM32F103RCT6的flash大小爲256K。如我flash空間分配如下:
在這裏插入圖片描述
在這裏插入圖片描述

二、bootloader

bootloader的主要功能:校驗數據、啓動APP、升級APP。
bootloader的工作流程如下:

1、基礎功能初始化(時鐘、外設等);
主要進行一些BSP板級初始化:(僅供參考,因工程而異)

/******************************************************************/
/**
 * BSP初始化函數\n
 *
 */
/******************************************************************/
void BSP_Init(void)
{	
	/* MCU Configuration--------------------------------------------------------*/
	
	/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
	HAL_Init();	
	/* Configure the system clock */
	SystemClock_Config();	
	/* Initialize all configured peripherals */	
	/* Initialize GPIO */
	MX_GPIO_Init();	
	/* Initialize usart */
	MX_UART_Init(); 
	/* Initialize timer */
	Timer_ParamInit();
	MX_TIM3_Init();
	MX_TIM4_Init(); 
}

2、數據校驗(參數區信息、APP程序的檢驗)
此步驟爲後續啓動或升級過程讀取一些基礎參數(存於參數區),以及校驗數據的準確性等。

流程:先讀取參數,判斷下一步是直接啓動APP還是留在bootloader等待升級。若是啓動APP則校驗APP程序數據是否正常。

  • 讀取參數區的參數:如我將升級相關的參數寫在此分區(目前僅是啓動標誌,具體可自行定義),根據此標誌來判斷下一步該如何走。
/* flash參數區信息結構 */
struct param_info
{
	UINT16		usStartFlag;	// 啓動標誌 0x0A-跳至APP 0x0B-等待升級 0x0F-已強制啓動過
}
  • 校驗數據:如我將校驗APP程序區的數據是否正常,採用CRC校驗。

3、跳轉APP或升級APP。

3.1 如何跳轉至APP呢?
跳轉函數:

/*****************************************************************/
/**
 * 加載APP \n
 * 
 */
/******************************************************************/
void loadAPP(INT32U unLoadAddr)
{
	void (*fnJump2APP)(void);
	INT32U	unJumpAddr;
	
	if(((*(__IO INT32U *)unLoadAddr) & 0x2FFE0000) == 0x20000000)	/* 檢查棧頂地址是否合法 */
	{
		printf("%s: ----------------------> run APP addr: 0x%x\r\n", __FUNCTION__, unLoadAddr); 
		/* 用戶代碼區第5~8字節爲程序開始地址(復位地址) */
		unJumpAddr = *(__IO INT32U *)(unLoadAddr + 4);
		fnJump2APP = (void (*)(void))unJumpAddr;
		/* 初始化APP堆棧指針(用戶代碼區的前4個字節用於存放棧頂地址) */
		__set_MSP(*(__IO INT32U *)unLoadAddr);
		fnJump2APP();
	}
	else
	{
		printf("ERROR: %s: Stack top address is not valid! Can not run func!\r\n", __FUNCTION__);
		while(1);
	}
}

關於跳轉部分的代碼的理解(轉)
這裏重點說一下幾句經典且非常重要的代碼:
第一句: if ((((__IO uint32_t)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000) //判斷棧定地址值是否在0x2000 0000 - 0x 2000 2000之間
怎麼理解呢? (1)在程序裏#define ApplicationAddress 0x8003000 ,那麼*(__IO uint32_t*)ApplicationAddress) 即取0x8003000開始到0x8003003 的4個字節的值,(2) 因爲我們的應用程序APP中設置把 中斷向量表 放置在0x08003000 開始的位置;而中斷向量表裏第一個放的就是棧頂地址的值。
也就是說,這句話即通過判斷棧頂地址值是否正確(是否在0x2000 0000 - 0x 2000 2000之間,因爲棧頂地址就是整個地址空間中SRAM所在的位置) 來判斷是否應用程序已經下載了,因爲應用程序的啓動文件剛開始就去初始化棧空間,如果棧頂值對了,說應用程已經下載了並且啓動文件的初始化也執行了;
第二句: JumpAddress = (__IO uint32_t) (ApplicationAddress + 4); [ common.c文件第18行定義了: pFunction Jump_To_Application;]
ApplicationAddress + 4 即爲0x0800 3004 ,裏面放的是中斷向量表的第二項“復位地址” JumpAddress = (__IO uint32_t) (ApplicationAddress + 4); 之後此時JumpAddress
第三句: Jump_To_Application = (pFunction) JumpAddress;
此時Jump_To_Application指向了復位函數所在的地址。
( startup_stm32f10x_md_lv. 文件中別名 typedef void (pFunction)(void);
void (pFunction)(void); 是聲明一個函數指針,加上一個typedef 之後 pFunction只不過是類型 void ()(void) 的一個別名)
第四 、五句: __set_MSP(
(__IO uint32_t*) ApplicationAddress); \設置主函數棧指針
Jump_To_Application(); \執行復位函數

3.2 升級APP
升級,即是將新的程序數據替換舊的程序數據,因此,只需在程序數據所在區域擦除舊數據再寫上新數據即可。

具體而言,無非就是一包一包地接收數據,然後一包一包地寫入flash。
最好分爲兩個階段,防止升級文件傳輸不完整或損壞,導致變磚:

  • 文件下發階段:接收到的升級包不要直接寫入flash的APP區域,先存到另外的flash空間或外部存儲區(如SD卡);
  • 更新階段:等全部接收完成,再一次性將外部存儲空間的數據寫入flash的APP區域。

其中,通信是通過串口,自己定義協議傳送數據,將程序數據拆成N個包,一包一包地傳輸,例如設定3條協議,分別是:開始升級、發送升級數據、升級完成確認(自定義啦)。
在這裏插入圖片描述
程序數據位置CPU內部flash區,因此需要以flash的讀寫擦等函數操作爲基礎。

/****************************************************************
* Func 	:
* Desc	:	讀取CPU內部flash
* Input	:
* Output:
* Return:
*****************************************************************/
INT32 cpuflash_read(UINT32 unStartAddr, UINT8 *pData, UINT16 usSize)
{
 
	if(pData == NULL)
		return -1;
 
	memcpy(pData, (INT8U *)unStartAddr, usSize);
 
	return 0;
}
 
/****************************************************************
* Func 	:
* Desc	:	寫入CPU內部flash (要先erase才能寫)
* Input	:
* Output:
* Return:
*****************************************************************/
INT32 cpuflash_write(UINT32 unStartAddr, UINT8 *pData, UINT16 usSize)
{
	INT32 	i = 0;
    INT32 	nRet = 0;
    UINT16 	usTemp1 = 0;
    UINT16 	usTemp2 = 0;
    UINT16 	usTempALL = 0;
 
	
    if(usSize%2 != 0)
    {
        usSize += 1;
    }
 
	HAL_FLASH_Unlock();		// unlock
	
    for(i=0; i<usSize/2; i++)
    {
		usTemp1 = *pData;
		usTemp2 = *(pData+1);
		usTempALL = ((usTemp1&0X00FF) | ((usTemp2<<8)&0XFF00));
		//usTemp = ((*pData>>8)&0X00FF) | (*(pData+1)&0XFF00);
		//usTemp = *(INT16U *)pData;/*這個會導致硬件崩潰*/
		nRet = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, unStartAddr, usTempALL);
		if(nRet != HAL_OK)
		{
			HAL_FLASH_Lock();		// lock
			printf("ERROR: %s: program[%d %d] failed-code[%d]\n", __FUNCTION__, usTemp1, usTemp2, nRet);
			return -1;
		}
	
		unStartAddr += 2;
		pData += 2;
	}
 
    HAL_FLASH_Lock();		// lock
 
	return 0;
}
 
/****************************************************************
* Func 	:
* Desc	:	擦除CPU內部flash(整頁)
* Input	:
* Output:
* Return:
*****************************************************************/
INT32 cpuflash_erase(UINT32 unStartAddr, UINT32 unEndAddr)
{
	FLASH_EraseInitTypeDef	stEraseInit;
	UINT32		ucPageErr = 0;
    UINT32  	unTempAddr = 0;
	INT32		nRet = 0;
 
 
	HAL_FLASH_Unlock();		// unlock
 
	for(unTempAddr=unStartAddr; unTempAddr<=unEndAddr; unTempAddr+=FLASH_PAGE_SIZE)
	{
		stEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
		stEraseInit.PageAddress = unTempAddr;
		stEraseInit.NbPages = 1;
		nRet = HAL_FLASHEx_Erase(&stEraseInit, &ucPageErr);
		if(nRet != HAL_OK)
		{
			HAL_FLASH_Lock();
			return -1;
		}
		GPIO_feedDog();
	}
 
    HAL_FLASH_Lock();		// lock
 
	return 0;
}

順便說下,STM32內部flash庫的保護問題,若不加保護,則內部程序可輕易被J-Flash等工具讀出。因此,常用的措施是:對內部flash添加讀寫保護機制。鎖定與解除函數如下:

/****************************************************************
* Func 	:
* Desc	:	使能讀保護函數
* Input	:
* Output:
* Return:
*****************************************************************/
void cpuflash_enableReadProtect(void)
{
  FLASH_OBProgramInitTypeDef OBInit;
  
  __HAL_FLASH_PREFETCH_BUFFER_DISABLE();
  
  HAL_FLASHEx_OBGetConfig(&OBInit);
  if(OBInit.RDPLevel == OB_RDP_LEVEL_0)
  {
  	printf("%s: ------------ set ----------\n", __FUNCTION__);
    OBInit.OptionType = OPTIONBYTE_RDP;
    OBInit.RDPLevel = OB_RDP_LEVEL_1;
    HAL_FLASH_Unlock();
    HAL_FLASH_OB_Unlock();
    HAL_FLASHEx_OBProgram(&OBInit);
    HAL_FLASH_OB_Lock();
    HAL_FLASH_Lock();
	//HAL_FLASH_OB_Launch();
  }
  __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
 
}
 
/****************************************************************
* Func 	:
* Desc	:	失能讀保護函數
* Input	:
* Output:
* Return:
*****************************************************************/
void cpuflash_disableReadProtect(void)
{
  FLASH_OBProgramInitTypeDef OBInit;
  
  __HAL_FLASH_PREFETCH_BUFFER_DISABLE();
  
  HAL_FLASHEx_OBGetConfig(&OBInit);
  if(OBInit.RDPLevel == OB_RDP_LEVEL_1)
  {
	  printf("%s: ------------ set ----------\n", __FUNCTION__);
    OBInit.OptionType = OPTIONBYTE_RDP;
    OBInit.RDPLevel = OB_RDP_LEVEL_0;
    HAL_FLASH_Unlock();
    HAL_FLASH_OB_Unlock();
    HAL_FLASHEx_OBProgram(&OBInit);
    HAL_FLASH_OB_Lock();
    HAL_FLASH_Lock();
	//HAL_FLASH_OB_Launch();
  }
  __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
 
}

三、APP程序的編寫

APP的主要功能: 1、除升級功能外的所有應用功能, 2、跳轉至bootloader準備升級。

  • 中斷向量表重映射:由於APP程序的起始地址的變化,所以導致我們的中斷向量表也整體偏移了,所以需要在app程序起始添加一行代碼,否則程序異常。
    例如偏移0x10000(根據實際使用做相應改動)
    NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x1000);
    位於APP程序的main函數第一句
    在這裏插入圖片描述
  • 跳轉至bootloader,有兩種方式:
    1、用類似bootloader的跳轉函數,
    2、直接重啓reboot(軟重啓)。
void SoftReset(void)
{
    __set_FAULTMASK(1);      // ¹Ø±ÕËùÓÐÖжË
    NVIC_SystemReset();      // ¸´Î»
}	

關於APP與IAP互跳之間的中斷處理問題
跳轉時中斷問題還是一個比較棘手的問題。。經常跳轉之後無法進入中斷,自己理解大概是,跳轉時只是強制改變了PC指正的位置,但是裏面的中斷寄存器什麼的都沒有變,這樣中斷存在,但是中斷函數什麼的都沒有了,造成程序死掉。。我在寫的過程中也遇到了問題,第一次從iap跳到app正常,但是從app跳回iap的時候由於殘留的中斷太多,在iap中程序死了。
處理方式(具體可參考文章http://dzdesigned80.blog.163.com/blog/static/203259238201272425313152/):
1、把app中的跳轉命令換成了系統復位NVIC_SystemReset();(不同的固件庫可能函數名不同)
2、跳轉之前復位或者關閉所有打開的中斷
3、跳轉後在初始化時加入RCC_DeInit();,,NVIC_DeInit ();等讓中斷恢復默認值。

第三章 如何生成、下載、保存和更新app的固件(IDE界面操作)

參考博文:stm32實現iap遠程固件更新
雖然本文標題是實現遠程固件更新,但是具體遠程方案本文不做詳細說明,重點在於介紹mcu接收到新的固件後怎麼保存更新,以及更新失敗回滾等。
下面簡單說明一下遠程的事情。
stm32的通信方式有串口,spi,iic,以及sdio等。也就是說我們的固件可以通過這些方式傳輸到mcu,不過普遍常用的是串口或者用sdio(外接sd卡)這兩種方式。簡單點還是再加一個串口網絡模塊,然後把固件存到服務器,經由串口網絡模塊透傳到mcu。比如用http協議把固件發送下來。遠程下載就這麼簡單一說。

接下來重點分析更新的事情。

固件生成

遠程更新使用的固件和我們平時燒錄程序用的固件格式有點區別,我們需要用二進制格式(.bin)文件。生成方式以mdk爲例介紹一下,只需要添加一條命令行。
在這裏插入圖片描述

在mdk工程配置選項選擇User,這個頁面是讓我們添加自定義命令行的,我們要添加的命令添加到第三個選項,即在編譯完成後執行。
下面是命令內容,需要注意的是 bin前面兩個-,app1.bin就是生成的固件,名字可以自定義,**.axf是你工程實際的.axf文件,路徑要正確。不知道你的axf在那在output頁面查看。

fromelf.exe --bin -o ../app1.bin ./**.axf

現在我們就可以生成bin文件了,但是還差一點步驟。
一般使用下mcu啓動後會自動把0x0800 0000映射到地址0x0000 0000,然後取指令執行。但是現在我們程序可以理解成分成了兩部分。
在這裏插入圖片描述
可以看到加入iap升級功能後我們app的起始地址變了,所以對應工程也要做這部分修改
在這裏插入圖片描述

如圖,我這裏把地址偏移了0x20000,同時在Linker中把“Use Memory Layout from Target Dialog”勾選,讓我們的修改生效。
如此設置以後就一切ok了
在這裏插入圖片描述

關於固件有一點需要注意,因爲起始地址修改了,所以導致我們的中斷向量表也整體偏移了,所以需要在app程序起始添加一行代碼,本文是偏移0x20000,根據實際使用做相應改動

NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x2000);

固件保存

第一步:把固件下載到板子的緩存區,緩存區可以是SD卡或者flash的某塊專用區域(該區域不是bootloader和APP的區域)

第二步:下到板子後,我們需要把固件保存到內置flash(APP區域)對應的地址。
本步驟可分爲三個部分:

  • 寫入之前,還應該加一些必要的文件完整性檢查,比如使用校驗等方式。
  • 寫入flash
  • 然後在flash特定區域(參數區)立一個flag,通知BootLoader程序更新固件。

如何讀寫flash第二章的代碼:

cpuflash_write{
...
}

本文上面設置的偏移是0x2 0000,所以此處寫入flash的地址也必須是0x802 0000(0x800 0000 + 0x2 0000)

固件更新

現在萬事具備了,接下來就是更新的事情了,簡單說一下更新的思路。
上電啓動後運行BootLoader程序,在bootloader中檢查是否是否需要更新,不需要的話就引導之前的app程序運行,需要更新就引導新的app程序。
引導步驟大體就是重置棧頂指針,強制跳轉app的reset復位中斷。
代碼見第二章的

void loadAPP(INT32U unLoadAddr)
{
...
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章