串口IAP实验
IAP,即在應(yīng)用編程。很多單片機(jī)都支持這個功能,STM32也不例外。在之前的FLASH模擬EEPROM實驗里面,我們學(xué)習(xí)了STM32的FLASH自編程,本章我們將結(jié)合FLASH自編程的知識,通過STM32的串口實現(xiàn)一個簡單的IAP功能。fficeffice" />
48.1 IAP簡介???
IAP(In Application Programming)即在應(yīng)用編程,IAP是用戶自己的程序在運(yùn)行過程中對User Flash的部分區(qū)域進(jìn)行燒寫,目的是為了在產(chǎn)品發(fā)布后可以方便地通過預(yù)留的通信口對產(chǎn)品中的固件程序進(jìn)行更新升級。 通常實現(xiàn)IAP功能時,即用戶程序運(yùn)行中作自身的更新操作,需要在設(shè)計固件程序時編寫兩個項目代碼,
第一個項目程序不執(zhí)行正常的功能操作,而只是通過某種通信方式(如USB、USART)接收程序或數(shù)據(jù),執(zhí)行對第二部分代碼的更新;第二個項目代碼才是真正的功能代碼。這兩部分項目代碼都同時燒錄在User Flash中,當(dāng)芯片上電后,首先是第一個項目代碼開始運(yùn)行,它作如下操作:
1)檢查是否需要對第二部分代碼進(jìn)行更新
2)如果不需要更新則轉(zhuǎn)到4)
3)執(zhí)行更新操作
4)跳轉(zhuǎn)到第二部分代碼執(zhí)行
第一部分代碼必須通過其它手段,如JTAG或ISP燒入;第二部分代碼可以使用第一部分代碼IAP功能燒入,也可以和第一部分代碼一起燒入,以后需要程序更新是再通過第一部分IAP代碼更新。
我們將第一個項目代碼稱之為Bootloader程序,第二個項目代碼稱之為APP程序,他們存放在STM32 FLASH的不同地址范圍,一般從最低地址區(qū)開始存放Bootloader,
緊跟其后的就是APP程序(注意,如果FLASH容量足夠,是可以設(shè)計很多APP程序的,本章我們只討論一個APP程序的情況)。這樣我們就是要實現(xiàn)2個程序:Bootloader和APP。
STM32的APP程序不僅可以放到FLASH里面運(yùn)行,也可以放到SRAM里面運(yùn)行,本章,我們將制作兩個APP,一個用于FLASH運(yùn)行,一個用于SRAM運(yùn)行。
我們先來看看STM32正常的程序運(yùn)行流程,如圖48.1.1所示:
?
圖48.1.1 STM32正常運(yùn)行流程圖
?????? STM32的內(nèi)部閃存(FLASH)地址起始于0x08000000,一般情況下,程序文件就從此地址開始寫入。此外STM32是基于Cortex-M3內(nèi)核的微控制器,其內(nèi)部通過一張“中斷向量表”
來響應(yīng)中斷,程序啟動后,將首先從“中斷向量表”取出復(fù)位中斷向量執(zhí)行復(fù)位中斷程序完成啟動,而這張“中斷向量表”的起始地址是0x08000004,當(dāng)中斷來臨,STM32的內(nèi)部硬
件機(jī)制亦會自動將PC指針定位到“中斷向量表”處,并根據(jù)中斷源取出對應(yīng)的中斷向量執(zhí)行中斷服務(wù)程序。
?????? 在圖48.1.1中,STM32在復(fù)位后,先從0X08000004地址取出復(fù)位中斷向量的地址,并跳轉(zhuǎn)到復(fù)位中斷服務(wù)程序,如圖標(biāo)號①所示;在復(fù)位中斷服務(wù)程序執(zhí)行完之后,會跳轉(zhuǎn)到我們
的main函數(shù),如圖標(biāo)號②所示;而我們的main函數(shù)一般都是一個死循環(huán),在main函數(shù)執(zhí)行過程中,如果收到中斷請求(發(fā)生重中斷),此時STM32強(qiáng)制將PC指針指回中斷向量表處,如圖標(biāo)號③所示;然后,根據(jù)中斷源進(jìn)入相應(yīng)的中斷服務(wù)程序,如圖標(biāo)號④所示;在執(zhí)行完中斷服務(wù)程序以后,程序再次返回main函數(shù)執(zhí)行,如圖標(biāo)號⑤所示。
?????? 當(dāng)加入IAP程序之后,程序運(yùn)行流程如圖48.1.2所示:
?
?????? 在圖48.1.2所示流程中,STM32復(fù)位后,還是從0X08000004地址取出復(fù)位中斷向量的地址,并跳轉(zhuǎn)到復(fù)位中斷服務(wù)程序,在運(yùn)行完復(fù)位中斷服務(wù)程序之后跳轉(zhuǎn)到IAP的main函數(shù),
如圖標(biāo)號①所示,此部分同圖48.1.1一樣;在執(zhí)行完IAP以后(即將新的APP代碼寫入STM32的FLASH,灰底部分。新程序的復(fù)位中斷向量起始地址為0X08000004+N+M),跳轉(zhuǎn)至
新寫入程序的復(fù)位向量表,取出新程序的復(fù)位中斷向量的地址,并跳轉(zhuǎn)執(zhí)行新程序的復(fù)位中斷服務(wù)程序,隨后跳轉(zhuǎn)至新程序的main函數(shù),如圖標(biāo)號②和③所示,同樣main函數(shù)為一個
死循環(huán),并且注意到此時STM32的FLASH,在不同位置上,共有兩個中斷向量表。
?????? 在main函數(shù)執(zhí)行過程中,如果CPU得到一個中斷請求,PC指針仍強(qiáng)制跳轉(zhuǎn)到地址0X08000004中斷向量表處,而不是新程序的中斷向量表,如圖標(biāo)號④所示;程序再根據(jù)我們設(shè)置
的中斷向量表偏移量,跳轉(zhuǎn)到對應(yīng)中斷源新的中斷服務(wù)程序中,如圖標(biāo)號⑤所示;在執(zhí)行完中斷服務(wù)程序后,程序返回main函數(shù)繼續(xù)運(yùn)行,如圖標(biāo)號⑥所示。
?????? 通過以上兩個過程的分析,我們知道IAP程序必須滿足兩個要求:
本章,我們有2個APP程序,一個為FLASH的APP,程序在FLASH中運(yùn)行,另外一個位SRAM的APP,程序運(yùn)行在SRAM中,圖48.1.2雖然是針對FLASH APP來說的,但是在
SRAM里面運(yùn)行的過程和FLASH基本一致,只是需要設(shè)置向量表的地址為SRAM的地址。
1.APP程序起始地址設(shè)置方法
隨便打開一個之前的實例工程,點擊Options for TargetàTarget選項卡,如圖48.1.3所示:
圖48.1.3 FLASH APP Target選項卡設(shè)置
?????? 默認(rèn)的條件下,圖中IROM1的起始地址(Start)一般為0X08000000,大小(Size)為0X80000,即從0X08000000開始的512K空間為我們的程序存儲(因為我們的STM32F103ZET6的
FLASH大小是512K)。而圖中,我們設(shè)置起始地址(Start)為0X08010000,即偏移量為0X10000(64K字節(jié)),因而,留給APP用的FLASH空間(Size)只有0X80000-0X10000=0X70000
(448K字節(jié))大小了。設(shè)置好Start和Szie,就完成APP程序的起始地址設(shè)置。
?????? 這里的64K字節(jié),需要大家根據(jù)Bootloader程序大小進(jìn)行選擇,比如我們本章的Bootloader程序為22K左右,理論上我們只需要確保APP起始地址在Bootloader之后,
并且偏移量為0X200的倍數(shù)即可(相關(guān)知識,請參考:http://www.openedv.com/posts/list/392.htm)。這里我們選擇64K(0X10000)字節(jié),留了一些余量,方便Bootloader以后的升級修改。
?????? 這是針對FLASH APP的起始地址設(shè)置,如果是SRAM APP,那么起始地址設(shè)置如圖48.1.4所示:
?
圖48.1.4 SRAM APP Target選項卡設(shè)置
?????? 這里我們將IROM1的起始地址(Start)定義為:0X20001000,大小為0XA000(40K字節(jié)),即從地址0X20000000偏移0X1000開始,存放APP代碼。因為整個STM32F103ZET6的
SRAM大小為64K字節(jié),所以IRAM1(SRAM)的起始地址變?yōu)?X2000B000(0x20001000+0xA000=0X2000B000),大小只有0X5000(20K字節(jié))。這樣,整個STM32F103ZET6的SRAM
分配情況為:最開始的4K給Bootloader程序使用,隨后的40K存放APP程序,最后20K,用作APP程序的內(nèi)存。這個分配關(guān)系大家可以根據(jù)自己的實際情況修改,不一定和我們這里的設(shè)
置一模一樣,不過也需要注意,保證偏移量為0X200的倍數(shù)(我們這里為0X1000)。
?????? 2.中斷向量表的偏移量設(shè)置方法
?????? 之前我們講解過,在系統(tǒng)啟動的時候,會首先調(diào)用systemInit函數(shù)初始化時鐘系統(tǒng),同時systemInit還完成了中斷向量表的設(shè)置,我們可以打開systemInit函數(shù),看看函數(shù)體的結(jié)尾處有這樣幾行代碼:
#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寄存器存放的是中斷向量表的起始地址。默認(rèn)的情況VECT_TAB_SRAM是沒有定義,所以執(zhí)行SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; ?
對于FLASH APP,我們設(shè)置為FLASH_BASE+偏移量0x10000,所以我們可以在FLASH APP的main函數(shù)最開頭處添加如下代碼實現(xiàn)中斷向量表的起始地址的重設(shè):
SCB->VTOR = FLASH_BASE | 0x10000;
?????? 以上是FLASH APP的情況,當(dāng)使用SRAM APP的時候,我們設(shè)置起始地址為:SRAM_bASE+0x1000,同樣的方法,我們在SRAM APP的main函數(shù)最開始處,添加下面代碼:
SCB->VTOR = SRAM_BASE | 0x1000;
?????? 這樣,我們就完成了中斷向量表偏移量的設(shè)置。
通過以上兩個步驟的設(shè)置,我們就可以生成APP程序了,只要APP程序的FLASH和SRAM大小不超過我們的設(shè)置即可。不過MDK默認(rèn)生成的文件是.hex文件,并不方便我
們用作IAP更新,我們希望生成的文件是.bin文件,這樣可以方便進(jìn)行IAP升級(至于為什么,請大家自行百度HEX和BIN文件的區(qū)別!)。這里我們通過MDK自帶的格式轉(zhuǎn)換
工具fromelf.exe,來實現(xiàn).axf文件到.bin文件的轉(zhuǎn)換。該工具在MDK的安裝目錄\ARM\BIN40文件夾里面。
fromelf.exe轉(zhuǎn)換工具的語法格式為:fromelf [options] input_file。其中options有很多選項可以設(shè)置,詳細(xì)使用請參考光盤《mdk如何生成bin文件.pdf》.
本章,我們通過在MDK點擊Options for TargetàUser選項卡,在Run User Programs After Build/Rebuild 欄,勾選Run#1和DOS16,并寫入:D:\Keil3.80a\ARM\BIN40\fromelf.exe? --bin -o? ..\OBJ\TEST.bin ..\OBJ\TEST.axf ,如圖48.1.6所示:
?
?????? 通過這一步設(shè)置,我們就可以在MDK編譯成功之后,調(diào)用fromelf.exe(注意,我的MDK是安裝在D:\Keil3.80A文件夾下,如果你是安裝在其他目錄,請根據(jù)你
自己的目錄修改fromelf.exe的路徑),根據(jù)當(dāng)前工程的TEST.axf(如果是其他的名字,請記住修改,這個文件存放在OBJ目錄下面,格式為xxx.axf),生成一個
TEST.bin的文件。并存放在axf文件相同的目錄下,即工程的OBJ文件夾里面。在得到.bin文件之后,我們只需要將這個bin文件傳送給單片機(jī),即可執(zhí)行IAP升級。
?????? 最后再來APP程序的生成步驟:
對于在FLASH里面運(yùn)行的APP程序,我們可以按照圖48.1.3的設(shè)置。對于SRAM里面運(yùn)行的APP程序,我們可以參考圖48.1.4的設(shè)置。
這一步按照上面講解,重新設(shè)置SCB->VTOR的值即可。
通過在User選項卡,設(shè)置編譯后調(diào)用fromelf.exe,根據(jù).axf文件生成.bin文件,用于IAP更新。?
以上3個步驟,我們就可以得到一個.bin的APP程序,通過Bootlader程序即可實現(xiàn)更新。
大家可以打開我們光盤的兩個APP工程,熟悉這些設(shè)置。
48.2 硬件設(shè)計
本章實驗(Bootloader部分)功能簡介:開機(jī)的時候先顯示提示信息,然后等待串口輸入接收APP程序(無校驗,一次性接收),在串口接收到APP程序
之后,即可執(zhí)行IAP。如果是SRAM APP,通過按下KEY0即可執(zhí)行這個收到的SRAM APP程序。如果是FLASH APP,則需要先按下WK_UP按鍵,將串口接
收到的APP程序存放到STM32的FLASH,之后再按KEY2既可以執(zhí)行這個FLASH APP程序。通過KEY1按鍵,可以手動清除串口接收到的APP程序。DS0用于指示程序運(yùn)行狀態(tài)。
本實驗用到的資源如下:
這些用到的硬件,我們在之前都已經(jīng)介紹過,這里就不再介紹了。
48.3 軟件設(shè)計
本章,我們總共需要3個程序:1,Bootloader;2,FLASH APP;3)SRAM APP;其中,我們選擇之前做過的RTC實驗(在第二十章介紹)來做為FLASH APP程序
(起始地址為0X08010000),選擇觸摸屏實驗(在第三十一章介紹)來做SRAM APP程序(起始地址為0X20001000)。Bootloader則是通過TFTLCD顯示實驗(在第
十八章介紹)修改得來。本章,關(guān)于SRAM APP和FLASH APP的生成比較簡單,我們就不細(xì)說,請大家結(jié)合光盤源碼,以及48.1節(jié)的介紹,自行理解。本章軟件設(shè)計僅針對Bootloader程序。
打開本實驗工程,可以看到我們增加了IAP組,在組下面添加了iap.c文件以及其頭文件isp.h。打開iap.c, 代碼如下:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "stmflash.h"
#include "iap.h"???? ?
iapfun jump2app;
u16 iapbuf[1024];
//appxaddr:應(yīng)用程序的起始地址
//appbuf:應(yīng)用程序CODE.
//appsize:應(yīng)用程序大小(字節(jié)).
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
?????? u16 t;
?????? u16 i=0;
?????? u16 temp;
?????? u32 fwaddr=appxaddr;//當(dāng)前寫入的地址
?????? u8 *dfu=appbuf;
?????? for(t=0;t<appsize;t+=2)
?????? {??????????????????????????????????????? ???
????????????? temp=(u16)dfu[1]<<8;
????????????? temp+=(u16)dfu[0];????? ?
????????????? dfu+=2;//偏移2個字節(jié)
????????????? iapbuf[i++]=temp;? ???
????????????? if(i==1024)
????????????? {
???????????????????? i=0;
???????????????????? STMFLASH_Write(fwaddr,iapbuf,1024);???
???????????????????? fwaddr+=2048;//偏移2048? 16=2*8.所以要乘以2.
????????????? }
?????? }
?????? if(i)STMFLASH_Write(fwaddr,iapbuf,i);//將最后的一些內(nèi)容字節(jié)寫進(jìn)去.?
}
//跳轉(zhuǎn)到應(yīng)用程序段
//appxaddr:用戶代碼起始地址.
void iap_load_app(u32 appxaddr)
{
?????? if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)????? //檢查棧頂?shù)刂肥欠窈戏?
?????? {
????????????? jump2app=(iapfun)*(vu32*)(appxaddr+4);????????
//用戶代碼區(qū)第二個字為程序開始地址(復(fù)位地址)?????????
????????????? MSR_MSP(*(vu32*)appxaddr);???????????????????????????????
//初始化APP堆棧指針(用戶代碼區(qū)的第一個字用于存放棧頂?shù)刂?
????????????? jump2app();??? //跳轉(zhuǎn)到APP.
?????? }
}
?????? 該文件總共只有2個函數(shù),其中,iap_write_appbin函數(shù)用于將存放在串口接收buf里面的APP程序?qū)懭氲紽LASH。iap_load_app函數(shù),則用于跳轉(zhuǎn)到APP程序運(yùn)行,
其參數(shù)appxaddr為APP程序的起始地址,程序先判斷棧頂?shù)刂肥欠窈戏?#xff0c;在得到合法的棧頂?shù)刂泛?#xff0c;通過MSR_MSP函數(shù)(該函數(shù)在sys.c文件)設(shè)置棧頂?shù)刂?#xff0c;
最后通過一個虛擬的函數(shù)(jump2app)跳轉(zhuǎn)到APP程序執(zhí)行代碼,實現(xiàn)IAPàAPP的跳轉(zhuǎn)。
?????? 打開iap.h代碼如下:
#ifndef __IAP_H__
#define __IAP_H__
#include "sys.h"???
typedef? void (*iapfun)(void);???? //定義一個函數(shù)類型的參數(shù).??
#define FLASH_APP1_ADDR??????????? 0x080010000? ????
//第一個應(yīng)用程序起始地址(存放在FLASH)
//保留0X08000000~0X0800FFFF的空間為Bootloader使用?? ??
void iap_load_app(u32 appxaddr);???????????????????????????????????????????????? //跳轉(zhuǎn)到APP程序執(zhí)行
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 applen);?? //在指定地址開始,寫入bin
#endif
?????? 這部分代碼比較簡單,。本章,我們是通過串口接收APP程序的,我們將usart.c和usart.h做了稍微修改,在usart.h中,我們定義USART_REC_LEN為55K字節(jié),
也就是串口最大一次可以接收55K字節(jié)的數(shù)據(jù),這也是本Bootloader程序所能接收的最大APP程序大小。然后新增一個USART_RX_CNT的變量,用于記錄接收到
的文件大小,而USART_RX_STA不再使用。打開usart.c,可以看到我們修改USART1_IRQHandler部分代碼如下:
//串口1中斷服務(wù)程序
//注意,讀取USARTx->SR能避免莫名其妙的錯誤?? ????
u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));
//接收緩沖,最大USART_REC_LEN個字節(jié),起始地址為0X20001000.???
//接收狀態(tài)
//bit15, 接收完成標(biāo)志
//bit14, 接收到0x0d
//bit13~0,???? 接收到的有效字節(jié)數(shù)目
u16 USART_RX_STA=0;?????? ?????? //接收狀態(tài)標(biāo)記???? ?
u16 USART_RX_CNT=0;?????????????????? //接收的字節(jié)數(shù)???? ?
void USART1_IRQHandler(void)
{
?????? u8 res;????
#ifdef OS_CRITICAL_METHOD
//如果OS_CRITICAL_METHOD定義了,說明使用ucosII了.
?????? OSIntEnter();???
#endif
?????? if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//接收到數(shù)據(jù)
?????? {????
????????????? res=USART_ReceiveData(USART1);
????????????? if(USART_RX_CNT<USART_REC_LEN)
????????????? {
???????????????????? USART_RX_BUF[USART_RX_CNT]=res;
???????????????????? USART_RX_CNT++;????????????????? ???????????????????????????????????????????????????????????? ?????
????????????? }
?????? }
#ifdef OS_CRITICAL_METHOD ?????
//如果OS_CRITICAL_METHOD定義了,說明使用ucosII了.
?????? OSIntExit();? ???????????????????????????????????????????????????????????????????????????? ?
#endif
}
?????? 這里,我們指定USART_RX_BUF的地址是從0X20001000開始,該地址也就是SRAM APP程序的起始地址!然后在USART1_IRQHandler函數(shù)里面,將串口發(fā)
送過來的數(shù)據(jù),全部接收到USART_RX_BUF,并通過USART_RX_CNT計數(shù)。代碼比較簡單,我們就不多說了。
最后我們看看main函數(shù)如下:
int main(void)
{???????????
?????? u8 t,key;
?????? u16 oldcount=0;???? //老的串口接收數(shù)據(jù)值
?????? u16 applenth=0;???? //接收到的app代碼長度
?????? u8 clearflag=0;
?????? uart_init(256000);? ??? //串口初始化為256000
?????? delay_init();??? ?? ? ????? //延時初始化
?????? LCD_Init();??? ??????? //液晶初始化
?????? LED_Init();?????????? ? ??? //初始化與LED連接的硬件接口
????? KEY_Init();????????????????? //按鍵初始化 ??????
POINT_COLOR=RED;//設(shè)置字體為紅色
?????? LCD_ShowString(60,50,200,16,16,"Warship STM32");????
?????? LCD_ShowString(60,70,200,16,16,"IAP TEST");?????
?????? LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
?????? LCD_ShowString(60,110,200,16,16,"2012/9/24");?
?????? LCD_ShowString(60,130,200,16,16,"WK_UP:Copy APP2FLASH");
?????? LCD_ShowString(60,150,200,16,16,"KEY1:Erase SRAM APP");
?????? LCD_ShowString(60,170,200,16,16,"KEY0:Run SRAM APP");
?????? LCD_ShowString(60,190,200,16,16,"KEY2:Run FLASH APP");
?????? POINT_COLOR=BLUE;
?????? //顯示提示信息
?????? POINT_COLOR=BLUE;//設(shè)置字體為藍(lán)色 ?
?????? while(1)
?????? {
?????? ????? if(USART_RX_CNT)
????????????? {
???????????????????? if(oldcount==USART_RX_CNT)
//新周期內(nèi),沒有收到任何數(shù)據(jù),認(rèn)為本次數(shù)據(jù)接收完成.
???????????????????? {
??????????????????????????? applenth=USART_RX_CNT;
??????????????????????????? oldcount=0;
??????????????????????????? USART_RX_CNT=0;
??????????????????????????? printf("用戶程序接收完成!\r\n");
??????????????????????????? printf("代碼長度:%dBytes\r\n",applenth);
???????????????????? }else oldcount=USART_RX_CNT;????????????????????
????????????? }
????????????? t++; delay_ms(10);
????????????? if(t==30)
????????????? {
???????????????????? LED0=!LED0; t=0;
???????????????????? if(clearflag)
???????????????????? {
??????????????????????????? clearflag--;
??????????????????????????? if(clearflag==0)LCD_Fill(60,210,240,210+16,WHITE);//清除顯示
???????????????????? }
????????????? }???? ? ??? ?
????????????? key=KEY_Scan(0);
????????????? if(key==KEY_UP)
????????????? {
???????????????????? if(applenth)
???????????????????? {
??????????????????????????? printf("開始更新固件...\r\n");?????
??????????????????????????? LCD_ShowString(60,210,200,16,16,"Copying APP2FLASH...");
?????????????????????????? if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)
//判斷是否為0X08XXXXXX.
??????????????????????????? {???? ??????
iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,
applenth); //更新FLASH代碼??
?????????????????????????????????? LCD_ShowString(60,210,200,16,16,"Copy APP Successed!!");
?????????????????????????????????? printf("固件更新完成!\r\n");??????
??????????????????????????? }else
??????????????????????????? {
?????????????????????????????????? LCD_ShowString(60,210,200,16,16,"Illegal FLASH APP!? ");?????? ??
?????????????????????????????????? printf("非FLASH應(yīng)用程序!\r\n");
??????????????????????????? }
??????????????????? }else
???????????????????? {
??????????????????????????? printf("沒有可以更新的固件!\r\n");
??????????????????????????? LCD_ShowString(60,210,200,16,16,"No APP!");
???????????????????? }
???????????????????? clearflag=7;//標(biāo)志更新了顯示,并且設(shè)置7*300ms后清除顯示??????? ?
????????????? }
????????????? if(key==KEY_DOWN)
????????????? {
???????????????????? if(applenth)
???????????????????? {?????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????? printf("固件清除完成!\r\n");???
??????????????????????????? LCD_ShowString(60,210,200,16,16,"APP Erase Successed!");
??????????????????????????? applenth=0;
???????????????????? }else
???????????????????? {
??????????????????????????? printf("沒有可以清除的固件!\r\n");
??????????????????????????? LCD_ShowString(60,210,200,16,16,"No APP!");
???????????????????? }
???????????????????? clearflag=7;//標(biāo)志更新了顯示,并且設(shè)置7*300ms后清除顯示????????????????????????????????????????????????????????
????????????? }
????????????? if(key==KEY_LEFT)
????????????? {
???????????????????? printf("開始執(zhí)行FLASH用戶代碼!!\r\n");
???????????????????? if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)
//判斷是否為0X08XXXXXX.
???????????????????? {????
??????????????????????????? iap_load_app(FLASH_APP1_ADDR);//執(zhí)行FLASH APP代碼
???????????????????? }else
???????????????????? {
??????????????????????????? printf("非FLASH應(yīng)用程序,無法執(zhí)行!\r\n");
??????????????????????????? LCD_ShowString(60,210,200,16,16,"Illegal FLASH APP!");??? ??
???????????????????? }????????????????????????????????????????????????????????????
???????????????????? clearflag=7;//標(biāo)志更新了顯示,并且設(shè)置7*300ms后清除顯示 ?
????????????? }
????????????? if(key==KEY_RIGHT)
????????????? {
???????????????????? printf("開始執(zhí)行SRAM用戶代碼!!\r\n");
???????????????????? if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000)
//判斷是否為0X20XXXXXX.
???????????????????? {????
??????????????????????????? iap_load_app(0X20001000);//SRAM地址
???????????????????? }else
???????????????????? {
??????????????????????????? printf("非SRAM應(yīng)用程序,無法執(zhí)行!\r\n");
??????????????????????????? LCD_ShowString(60,210,200,16,16,"Illegal SRAM APP!");???? ??
???????????????????? }????????????????????????????????????????????????????????????
???????????????????? clearflag=7;//標(biāo)志更新了顯示,并且設(shè)置7*300ms后清除顯示
????????????? }????????????????????????? ??
????????????? ?
?????? }?? ?????? ???
}
?????? 該段代碼,實現(xiàn)了串口數(shù)據(jù)處理,以及IAP更新和跳轉(zhuǎn)等各項操作。Bootloader程序就設(shè)計完成了,但是一般要求bootloader程序越小越好(給APP省空間嘛),
所以,本章我們把一些不需要用到的.c文件全部去掉,最后得到工程截圖如圖48.3.1所示:
?
?
圖48.3.1 Bootloader 工程截圖
?????? 從上圖可以看出,雖然去掉了一些不用的.c文件,但是Bootloader大小還是有22K左右,比較大,主要原因是液晶驅(qū)動和printf占用了比較多的flash,如果大家
想進(jìn)一步刪減,可以去掉LCD顯示和printf等,不過我們在本章為了演示效果,所以保留了這些代碼。
????? 至此,本實驗的軟件設(shè)計部分結(jié)束。
?????? FLASH APP和SRAM APP兩部分代碼,我們在實驗?zāi)夸浵绿峁┝藘蓚€實驗供大家參考,不過要提醒大家,根據(jù)我們的設(shè)置,FLASH APP的起始地址必須是0X08010000,而SRAM APP的起始地址必須是0X20001000。
48.4 下載驗證
在代碼編譯成功之后,我們下載代碼到ALIENTEK戰(zhàn)艦STM32開發(fā)板上,得到,如圖48.4.1所示:
?
圖48.4.1 IAP程序界面
此時,我們可以通過串口,發(fā)送FLASH APP或者SRAM APP到戰(zhàn)艦STM32開發(fā)板,如圖48.4.2所示:
圖48.4.2 串口發(fā)送APP程序界面
?????? 先用串口調(diào)試助手的打開文件按鈕(如圖標(biāo)號1所示),找到APP程序生成的.bin文件,然后設(shè)置波特率為256000(為了提高速度,Bootloader程序?qū)⒉ㄌ芈时辉O(shè)置為256000了),
最后點擊發(fā)送文件(圖中標(biāo)號3所示),將.bin文件發(fā)送給戰(zhàn)艦STM32開發(fā)板。
?????? 在收到APP程序之后,我們就可以通過KEY0/KEY2運(yùn)行這個APP程序了(如果是FLASH APP,則先需要通過WK_UP將其存入對應(yīng)
總結(jié)