基于IAP和Keil MDK的遠程升級設計
3.3.1.2 選擇扇區(qū)
本文引用地址:http://m.butianyuan.cn/article/201612/341760.htm在任何擦除和編程Flash之前,必須選中扇區(qū),可以選中一個或多個。
/******************************************************************
* 名稱:SelSector()
* 功能:IAP操作扇區(qū)選擇,命令代碼50。
* 入口參數:sec1 起始扇區(qū)
* sec2 終止扇區(qū)
* 出口參數:IAP返回值(paramout緩沖區(qū)) CMD_SUCCESS,BUSY,INVALID_SECTOR
*********************************************************************/
void SelSector(uint8 sec1, uint8 sec2)
{
paramin[0] = IAP_SELSECTOR; // 設置命令字
paramin[1] = sec1; // 設置參數
paramin[2] = sec2;
iap_entry(paramin, paramout); // 調用IAP服務程序
}
代碼3-2 選擇扇區(qū)
3.3.1.3 擦除扇區(qū)
在編程Flash前必須執(zhí)行擦除操作,如果某個扇區(qū)已經擦除,就不需要再次擦除??梢砸淮尾脸粋€或多個扇區(qū)。
/******************************************************************
* 名稱:EraseSector()
* 功能:扇區(qū)擦除,命令代碼52。
* 入口參數:sec1 起始扇區(qū)
* sec2 終止扇區(qū)
* 出口參數:IAP返回值(paramout緩沖區(qū)) CMD_SUCCESS,BUSY,INVALID_SECTOR ************************************************************************/
void EraseSector(uint8 sec1, uint8 sec2)
{ paramin[0] = IAP_ERASESECTOR; // 設置命令字
paramin[1] = sec1; // 設置參數
paramin[2] = sec2;
paramin[3] = Fosc/1000; // 當不使用PLL功能時,Fcclk=Fosc
iap_entry(paramin, paramout); // 調用IAP服務程序
代碼3-3 擦除扇區(qū)
3.3.1.4 編程扇區(qū)
通過這個過程,數據可以從RAM中編程到片內Flash中。
注:
1. 數據只能從片內SRAM編程到片內Flash。
2. 片內Flash的地址必須512字節(jié)對齊。
3. 片內RAM應位于局部總線,即USB或以太網的SRAM不可以使用。
4. 每一次編程字節(jié)應該是512、1024、4096、8192中的一個。
/*********************************************************************
* 名稱:RamToFlash()
* 功能:復制RAM的數據到FLASH,命令代碼51。
* 入口參數:dst 目標地址,即FLASH起始地址。以512字節(jié)為分界
* src 源地址,即RAM地址。地址必須字對齊
* no 復制字節(jié)個數,為512/1024/4096/8192
* 出口參數:IAP返回值(paramout緩沖區(qū)) CMD_SUCCESS,SRC_ADDR_ERROR,DST_ADDR_ERROR,
SRC_ADDR_NOT_MAPPED,DST_ADDR_NOT_MAPPED,COUNT_ERROR,BUSY,未選擇扇區(qū)
********************************************************************/
void RamToFlash(uint32 dst, uint32 src, uint32 no)
{ paramin[0] = IAP_RAMTOFLASH; // 設置命令字
paramin[1] = dst; // 設置參數
paramin[2] = src;
paramin[3] = no;
paramin[4] = Fosc/1000; // 當不使用PLL功能時,Fcclk=Fosc
iap_entry(paramin, paramout); // 調用IAP服務程序
}
代碼3-4 編程扇區(qū)
3.3.1.5 比較數據
通過這個函數,可以檢查寫入Flash中的數據和RAM中的是否相同。
注意源地址、目標地址和字節(jié)數必須是4的倍數??墒褂?a class="contentlabel" href="http://m.butianyuan.cn/news/listbylabel/label/Keil">Keil MDK提供的關鍵字__align(n) 來指定n字節(jié)對齊。
/********************************************************************
* 名稱:Compare()
* 功能:校驗數據,命令代碼56。
* 入口參數:dst 目標地址,即RAM/FLASH起始地址。地址必須字對齊
* src 源地址,即FLASH/RAM地址。地址必須字對齊
* no 復制字節(jié)個數,必須能被4整除
* 出口參數:IAP返回值(paramout緩沖區(qū)) CMD_SUCCESS,COMPARE_ERROR,ADDR_ERROR
******************************************************************/
void Compare(uint32 dst, uint32 src, uint32 no)
{ paramin[0] = IAP_COMPARE; // 設置命令字
paramin[1] = dst; // 設置參數
paramin[2] = src;
paramin[3] = no;
iap_entry(paramin, paramout); // 調用IAP服務程序
代碼3-5 比較數據
3.3.2 IAP編程期間的中斷管理
LPC2114片上Flash在擦除/編程期間絕不可被中斷打斷。但Bootloader中定時和串口接收又使用了中斷,因此必須在擦除/編程之前禁止總中斷,待操作完成后再使能總中斷。Bootloader運行在用戶模式下,不具有禁止/使能中斷的權力,所以在本設計中使用軟中斷禁止/使能總中斷。Keil MDK提供了關鍵字__svc來觸發(fā)軟中斷。
軟中斷函數聲明:
__svc(0x00) void EnableIrq(void); //使能中斷,軟中斷0
__svc(0x01) void DisableIrq(void); //禁止中斷,軟中斷1
軟中斷函數代碼:
/*
*********************************************************************
* 功 能:禁止中斷
* 描 述:利用軟中斷實現在用戶模式下調用函數關中斷
*********************************************************************/
void DisableIrqFunc(void)
{
int temp;
__asm
{
MRS temp,SPSR
ORR temp,temp,#0x80
MSR SPSR_c,temp
}
}
/*
********************************************************************
* 功 能:使能中斷
* 描 述:利用軟中斷實現在用戶模式下調用函數開中斷
********************************************************************
*/
void EnableIrqFunc(void)
{
int temp;
__asm
{
MRS temp,SPSR
BIC temp,temp,#0x80
MSR SPSR_c,temp
}
}
代碼3-6 禁止/使能總中斷
更改啟動代碼,掛接軟中斷入口:
;軟中斷入口
EXPORT SWI_Handler
extern EnableIrq1
extern DisableIrq1
SWI_Handler
STMFD SP!, {R0,R12,LR} ;入棧
LDR R0, [LR,#-4] ;取軟中斷指令,軟中斷號就包含其中
BIC R0,R0,#0xFF000000
CMP R0,#0 ;判斷是否軟中斷0
BLEQ EnableIrqFunc
BLNE DisableIrqFunc
LDMFD SP!,{R0,R12,PC}^
代碼3-7 掛接軟中斷入口
在程序中,如果想禁止中斷,只需使用DisableIrq();若是能中斷,只需使用EnableIrq()。
3.3.3 使用分散加載機制精確定位入口地址
應用程序接收到升級指令后,會跳轉到0x00000400處執(zhí)行Bootloader升級程序。因此Bootloader程序的入口地址必須精確定位到0x00000400處。這可以使用Keil MDK提供的分散加載機制來完成。
分散加載代碼見代碼3-8.
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x00000400 0x00001C00 { ; load region size_region
ER_IROM1 0x00000400 0x00001C00 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x40000040 0x00003FA0 { ; RW data
.ANY (+RW +ZI)
}
}
代碼3-8 分散加載代碼
這段代碼顯示出Bootloader程序從0x00000400處開始執(zhí)行,最多占用0x1C00字節(jié)的Flash空間。另外,該程序的RAM從0x40000040開始,長度為0x3FA0個字節(jié)。這樣RAM的低64字節(jié)保留給中斷向量映射使用,高32字節(jié)保留給IAP編程使用。
3.3.4 中斷向量的重映射
Bootloader的起始地址位于0x00000400,中斷向量也從這一地址開始存儲。但默認情況下ARM發(fā)生異常時,會跳轉到0x00000000處的64字節(jié)中斷向量區(qū)域執(zhí)行相應操作,所以為了使Bootloader能相應中斷,必須將位于0x00000400開始的64字節(jié)中斷向量表重映射到RAM的低區(qū)。LPC2114使用向寄存器MEMMAP寫入0x02來完成這一過程。
代碼3-9 描述了中斷向量重映射的過程。
; Copy Exception Vectors to Internal RAM ---------------------------------------
ADR R8, Vectors ; 源地址
LDR R9, =RAM_BASE ; 目標地址,這里是0x40000000
LDMIA R8!, {R0-R7} ; 裝載向量表
STMIA R9!, {R0-R7} ; 存儲向量表
LDMIA R8!, {R0-R7} ; 裝載處理程序地址
STMIA R9!, {R0-R7} ; 存儲處理程序地址
; Memory Mapping (when Interrupt Vectors are in RAM)
MEMMAP EQU 0xE01FC040 ; Memory Mapping Control
IF :DEF:REMAP
LDR R0, =MEMMAP
IF :DEF:EXTMEM_MODE
MOV R1, #3
ELIF :DEF:RAM_MODE
MOV R1, #2
ELSE
MOV R1, #1
ENDIF
STR R1, [R0]
ENDIF
代碼3-9 中斷向量重映射
由于Keil MDK提供的啟動代碼中使用條件編譯指令,所以,要想正確的執(zhí)行中斷向量重映射,還需要在Keil MDK編譯器工程設置Options for target“你的工程目標名”下的Asm標簽中找到Define編輯框,在編輯框中鍵入“REMAP RAM_MODE”。如圖3-2所示
圖3-2
注意:在擦除/編程Flash的時候還應該禁止PLL、存儲器加速模塊。
3.4 用戶程序的設計
用戶程序運行在高區(qū)(扇區(qū)8~13)或者低區(qū)(扇區(qū)1~7),用于實現數據的采集、處理和上傳等等,用戶程序除本身功能的要求外,還需要注意:
? 使用分散加載機制,將程序入口精確定位到0x00010000(高區(qū))或0x00008000(低區(qū))。
? 進行中斷向量重映射,映射到RAM最底處。
4.1 Intel的hex格式
Intel hex文件是記錄文本行的ASCII文本文件,在Intel HEX文件中,每一行是一個HEX記錄,由十六進制數組成的機器碼或者數據常量。一個數據記錄以一個回車和一個換行結束。
一個Intel HEX文件可以包含任意多的十六進制記錄,每條記錄有五個域,下面是一個記錄的格式.
: LL AAAA TT [DD...] CC
每一組字母是獨立的一域,每一個字母是一個十六進制數字,每一域至少由兩個十六進制數字組成,下面是字節(jié)的描述.
:冒號 是每一條Intel HEX記錄的開始
LL 是這條記錄的長度域,他表示數據(dd)的字節(jié)數目.
AAAA 是地址域,他
表示數據的起始地址
TT 這個域表示這條HEX記錄的類型,他有可能是下面這幾種類型
00 ----數據記錄
01 ----文件結束記錄
02 ----擴展段地址記錄
04 ----擴展線性地址記錄
DD 是數據域,表示一個字節(jié)的數據,一個記錄可能有多個數據字節(jié),字節(jié)數目可以查看LL域的說明。
評論