前提
想來做iap升級了,應該不是什么新手。
下面的程序需要用到一些簡單的功能
串口收發數據
開關總中斷
雖然本文標題是實現遠程固件更新,但是具體遠程方案本文不做詳細說明,重點在於介紹mcu接收到新的固件后怎么保存更新,以及更新失敗回滾等。下面簡單說明一下遠程的事情。
stm32的通信方式有串口,spi,iic,以及sdio等。也就是說我們的固件可以通過這些方式傳輸到mcu,不過普遍常用的是串口或者用sdio(外接sd卡)這兩種方式。個人覺得用sd卡來回copy也不怎么方便。簡單點還是再加一個串口網絡模塊,然后把固件存到服務器,經由串口網絡模塊透傳到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,然后取指令執行。但是現在我們程序可以理解成分成了兩部分。
app就是我們實際實現各種功能的固件,BootLoader為控制更新的固件。可以看到加入iap升級功能后我們app的起始地址變了,所以對應工程也要做這部分修改
如圖,我這里把地址偏移了0x20000,同時在Linker中把“Use Memory Layout from Target Dialog”勾選,讓我們的修改生效。
如此設置以后就一些ok了
圖中上半部分是起始地址為0x800 0000 下半部分為起始地址0x802 0000,可以看到復位中斷地址,以及下面一系列入口地址都相應變化了。
關於固件有一點需要注意,因為起始地址修改了,所以導致我們的中斷向量表也整體偏移了,所以需要在app程序起始添加一行代碼,本文是偏移0x20000,根據實際使用做相應改動
NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x2000);
固件保存
下載到板子的方式自選,下到板子后,我們需要把固件保存到內置flash對應的地址,
int writeToFlash(unsigned char *data, unsigned int len,unsigned int baseAddress )
{
unsigned char i = 0;
unsigned char pageNum = 0;
FLASH_Status FLASHStatus;
pageNum = len/FLASH_PAGE_SIZE+1;//求出總頁數
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
for (i=0; i < pageNum; i++)
{
FLASHStatus = FLASH_ErasePage(baseAddress + FLASH_PAGE_SIZE*i);
if (FLASHStatus != FLASH_COMPLETE)
{
FLASH_Lock();
return -1;
}
}
pageNum = (len+1)>> 1;
for (i=0; i < pageNum; i++)
{
FLASHStatus = FLASH_ProgramHalfWord(baseAddress+i*2, *((unsigned short*)data+i));
if (FLASHStatus != FLASH_COMPLETE)
{
FLASH_Lock();
return -1;
}
}
FLASH_Lock();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
本文上面設置的偏移是0x2 0000,所以此處寫入flash的地址也必須是0x802 0000(0x800 0000 + 0x2 0000)
除了寫入外,還應該加一些必要的文件完整性檢查,比如使用校驗等方式,這部分自行處理。然后在flash特定區域立一個flag,通知BootLoader程序更新固件。
固件更新
現在萬事具備了,接下來就是更新的事情了,先簡單說一下更新的思路。上電啟動后運行BootLoader程序,在bootloader中檢查是否是否需要更新,不需要的話就引導之前的app程序運行,需要更新就引導新的app程序。引導步驟大體就是重置棧頂指針,強制跳轉app的reset復位中斷。
#define APPLICATION_ADDRESS 0x08020000
typedef void (*pFunction)(void);
int main(void)
{
SystemInit();
BootLoad_Jump();
}
__asm void MSR_MSP(u32 addr) //設置堆棧指針
{
MSR MSP, r0
BX r14
}
void BootLoad_Jump(void)
{
u32 JumpAddress;
pFunction Jump_To_Application;
/* Check Vector Table: Test if user code is programmed starting from address
"APPLICATION_ADDRESS" */
//d_printfhex32((*(__IO uint32_t*)APPLICATION_ADDRESS));d_printf("\n");
if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
{
__disable_irq();
JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS +4);
//d_printfhex32(JumpAddress);d_printf("\n");
Delay(100);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
MSR_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
Jump_To_Application();
}
}