找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 9524|回復(fù): 5
打印 上一主題 下一主題
收起左側(cè)

LPC1768 IAP升級方法

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:90762 發(fā)表于 2018-10-8 05:52 | 只看該作者 回帖獎勵 |倒序?yàn)g覽 |閱讀模式
1、IAP介紹
IAP即“in applicatinprogramming”在應(yīng)用編程的縮寫,指MCU可以在系統(tǒng)中獲取新代碼并對自己重新編程,即改變應(yīng)用程序。它與我們所熟悉的ISP編程不同,      
  LPC1768 ISP編程接口為串口1,如果使用其他的串口或其他總線則不能對其進(jìn)行編程。而我們這里所說的IAP通過下載一段引導(dǎo)程序Bootloader程序,如果我們想要從串口2或網(wǎng)口更新應(yīng)用程序,在Bootloader中初始化相應(yīng)的串口或網(wǎng)口,使其接收應(yīng)用程序,將接收到的應(yīng)用程序?qū)懭氲?/font>Flash里面,IAP完成后跳轉(zhuǎn)到應(yīng)用程序入口執(zhí)行應(yīng)用程序。所以現(xiàn)在的IAP程序涉及到兩個(gè)概念:Bootloader和應(yīng)用程序。

Bootloader:BootLoader就是在操作系統(tǒng)內(nèi)核運(yùn)行之前運(yùn)行的一段小程序。通過這段小程序,我們可以初始化硬件設(shè)備、建立內(nèi)存空間映射圖,從而將系統(tǒng)的軟硬件環(huán)境帶到一個(gè)合適狀態(tài),以便為最終調(diào)用操作系統(tǒng)內(nèi)核準(zhǔn)備好正確的環(huán)境。這里我們所說的Bootloader也是系統(tǒng)開機(jī)前的一段小程序,其主要任務(wù)是用來初始化串口和IAP端口(網(wǎng)口CAN接口等)的,通過判斷狀態(tài)是否需要從IAP端口進(jìn)行更新應(yīng)用程序,若需要更新則從端口接收應(yīng)用程序,并存放到指定的Flash里面,更新完成后則跳入到指定的Flash里面執(zhí)行應(yīng)用程序。
應(yīng)用程序:即我們需要開發(fā)板實(shí)現(xiàn)功能的程序,其中應(yīng)用程序主要分為兩種:hex文件和bin文件。在我們經(jīng)常使用的KEIL中默認(rèn)編譯生成的可執(zhí)行文件(應(yīng)用程序)為hex格式的,若需要編譯生成bin格式需要做如下修改,加入“D:\Keil\ARM\ARMCC\bin\fromelf.exe --bin --output ./Obj/Can_Updata.bin ./Obj/test.axf”,重新編譯生成的Can_Updata.bin文件存放在Obj文件夾下。

2、bin格式文件與hex格式文件的區(qū)別
bin格式文件是純粹的二進(jìn)制文件,使用下載其將其下載到開發(fā)板時(shí)其內(nèi)容完全不變,所以對于IAP下載使用bin格式文件是比較方便的,如下圖是bin文件的內(nèi)容與寫入到開發(fā)板后使用仿真器觀察到Flash存放的內(nèi)容(這段程序當(dāng)然是可以執(zhí)行的)。

Hex格式文件:Hex全稱(Intel HEX)文件是由一行行符合Intel HEX文件格式的文本所構(gòu)成的ASCII文本文件。在Intel HEX文件中,每一行包含一個(gè)HEX記錄。這些記錄由對應(yīng)機(jī)器語言碼和/或常量數(shù)據(jù)的十六進(jìn)制數(shù)數(shù)字組成。如下圖是hex文件的部分?jǐn)?shù)據(jù),其組成由“:CCAAAARR...ZZ ”,CC=10代表長度為16字節(jié),AAAA=0000本條記錄中的數(shù)據(jù)在存儲區(qū)中的起始地址,RR=00,數(shù)據(jù)區(qū),ZZ=38為校驗(yàn),這里就不做仔細(xì)說明了。

3、LPC1768 IAP原理
LPC1768復(fù)位后開始執(zhí)行Boot代碼,Boot代碼可以執(zhí)行ISP程序或用戶的應(yīng)用代碼。發(fā)生硬件復(fù)位后,P2.10 引腳為低電平,這就被當(dāng)作啟動ISP命令處理器的外部硬件請求。假定在/RESET 引腳上出現(xiàn)上升沿時(shí),電源引腳出現(xiàn)正確的信號,那么在采樣P2.10 之前有3ms的時(shí)間決定是執(zhí)行用戶代碼還是ISP 處理程序。如果P2.10 為低電平且看門狗溢出標(biāo)志置位,那么忽略啟動ISP 命令處理器的外部硬件請求。在沒有ISP 命令處理器的請求(硬件復(fù)位后P2.10引腳為高電平)時(shí),將搜索有效的用戶程序。若發(fā)現(xiàn)有效的用戶程序,執(zhí)行控制權(quán)就被轉(zhuǎn)移給用戶程序。若沒有找到有效的用戶程序,就將調(diào)用自動波特率程序。這里不討論ISP下載及命令,有興趣的朋友可以查看LPC1768技術(shù)手冊第三十二章ISP命令。


在IAP升級中,程序正常執(zhí)行即用戶代碼(這里的用戶代碼是我們所說的IAP引導(dǎo)程序),如下是IAP升級流程圖,程序?qū)㈩A(yù)留端口(這里提供有串口和CAN總線接口兩種)接收到的APP程序bin文件,將接收到的數(shù)據(jù)寫入到指定的Flash區(qū)域(例程APP地址為0x0001 0000),程序通過IAP命令將數(shù)據(jù)寫入到Flash里面,LPC1768提供了一系列IAP命令對片內(nèi)Flash進(jìn)行擦除編寫等
4、IAP命令
LPC1768通過IAP函數(shù)對片內(nèi)Flash進(jìn)行操作,IAP函數(shù)是固化在0x1FFF1FF1處的一個(gè)有傳入?yún)?shù)和返回參數(shù)的一個(gè)函數(shù),在LPC1768技術(shù)手冊第三十二章IAP命令中有有詳細(xì)的說明。主要提供有如下命令:準(zhǔn)備下操作扇區(qū)、將RAM內(nèi)容復(fù)制到Flash、清除扇區(qū)、扇區(qū)查空、讀器件ID、讀boot版本、比較、重新調(diào)用ISP等。

5、串口IAP升級
本例程是根據(jù)官方提供的串口IAP更新圖片進(jìn)行修改而來,直接使用官方的IAP.c文件,該文件中提供了如上圖IAP命令的各種函數(shù),其具體參數(shù)可以參考IAP命令。根據(jù)官方例程里面將bmp圖片經(jīng)過串口采用Xmodem1K協(xié)議發(fā)送到開發(fā)板存放在地址0x0001 0000,如下圖是LPC1768 Flash分配地址,第16~21扇區(qū)為應(yīng)用程序存放空間。這里我們將要傳送的bmp圖片改為傳輸應(yīng)用程序bin文件

6、串口IAP程序分析
例程通過按鍵對開發(fā)板進(jìn)行控制,INT0鍵擦除Flash,確認(rèn)鍵等待串口IAP,向上鍵顯示菜單,向下鍵執(zhí)行應(yīng)用程序,使用LCD來開發(fā)板狀態(tài)

當(dāng)程序全部寫入到Flash后,按下向下按鍵,跳轉(zhuǎn)到應(yīng)用程序,首先修改中斷向量表然后進(jìn)入應(yīng)用程序
void Boot( void )
{
        SCB->VTOR = IMG_START_SECTOR & 0x1FFFFF80;        //修改中斷向量表
        JMP_Boot(IMG_START_SECTOR);
}
堆棧地址更新,PC地址更新
__asm void JMP_Boot( uint32_t address ){
   LDR SP, [R0]                ;堆棧地址更新
   LDR PC, [R0, #4]        ;進(jìn)入應(yīng)用程序
}
7、操作步驟及實(shí)驗(yàn)現(xiàn)象
1、下載“寶馬開發(fā)板串口IAP升級”例程,插上USB轉(zhuǎn)串口線,打開超級終端,復(fù)位開發(fā)板。
2、按下按鍵INT0按鍵--擦除扇區(qū)
3、按下方向鍵確認(rèn)鍵(即向下按)--等待接收串口程序
4、串口打印’C’字符等待接收數(shù)據(jù)
5、串口發(fā)送文件,選擇“1K Xmodem”協(xié)議,選擇要下載的應(yīng)用程序bin文件,這里使用DAC例程作為測試。

6、點(diǎn)擊“發(fā)送”,發(fā)送文件
7、發(fā)送完成
8、按下方向鍵向下鍵開始執(zhí)行應(yīng)用程序,這時(shí)我們可以使用示波器測試P0.26口輸出正弦波信號
bin文件生成方法及設(shè)置:
打開要更新應(yīng)用程序工程,這里使用“IAP升級DAC轉(zhuǎn)換”程序,設(shè)置ROM空間地址(程序下載到Flash的地址),這里也是我們應(yīng)用程序的入口地址0x10000
打開User選項(xiàng),利用Keil自帶的fromelf.exe生成bin文件,bin文件保存在Obj文件夾中,如下圖添加“D:\Keil\ARM\ARMCC\bin\fromelf.exe --bin --output ./Obj/app.bin ./Obj/app.axf”,輸入文件為app.axf,所以工程編譯生成輸出文件名設(shè)置為app,命令執(zhí)行生成app.bin文件
打開Asm選項(xiàng),定義“NO_CRP”,我們可以打開啟動文件,當(dāng)定義了“NO_CRP”后,那么我們后面的代碼也就不起作用了,所以在需要加密的時(shí)候前面就一定不能再定義了代碼讀保護(hù),也就是加密的關(guān)鍵字,經(jīng)過加密后芯片再也無法擦除,由于我們這里程序需要使用到IAP升級,因此添加此定義

編譯即可生成app.bin




lpc1768 IAP疑點(diǎn)全解釋
IAP簡介:
       IAP為在應(yīng)用編程的簡稱,其作用是用戶自己的程序在運(yùn)行過程中對用戶程序所在的部分區(qū)域進(jìn)行燒寫,目的是為了在產(chǎn)品發(fā)布后可以方便地通過預(yù)留的通信口對產(chǎn)品中的程序進(jìn)行更新升級。
Lpc1768存儲器空間分配:
整體Flash布局:     
地址范圍
地址說明
0x1000_0000 ~0x1000_7FFF
片上32K通用SRAM
0x2007_C000 ~0x2007_FFFF
片上16K以太網(wǎng)/USB靜態(tài)SRAM,可用作通用SRAM
0x2008_0000 ~0x2008_3FFF
片上16K以太網(wǎng)/USB靜態(tài)SRAM,可用作通用SRAM
0x1FFF_0000 ~0x1FFF_1FFF
片上 8K啟動代碼區(qū)
0x0000_0000 ~0x0007_FFFF
片上512K Flash存儲器
其他地址
APB、AHB等寄存器映射區(qū)
分散加載描述文件:
分散加載描述文件是arm連接器提供的可以將程序中的代碼段、數(shù)據(jù)段定位到flash中特定的物理地址的一種機(jī)制。通過此機(jī)制我們可以把給IAP程序和用戶程序分別分配一部分空間并制定各自的起始地址以保證IAP程序和用戶程序不會重疊。關(guān)于分散加載描述文件詳細(xì)文檔,請參考《RealView編譯工具----鏈接器參考指南》第三章,對于IAP涉及到的分散加載文件的知識,我們只需知道以下幾點(diǎn)。以本次IAP工程為例,我們給IAP升級代碼留32K的空間(0x0000_0000~0x0000_7FFFF),剩余的給用戶程序空間(即用戶程序從地址0x0000_8000開始)。對于IAP程序部分的分散加載文件不做修改,對于用戶程序部分修改如下:
我們僅僅對第5行和第6行做了修改,改動的地方做出了標(biāo)注,其具體表示意思是:
Line5:0x0000_8000表示加載域的起始地址,即放在flash0x0000_8000地址處開始放置,0x0008_0000表示代碼區(qū)、數(shù)據(jù)區(qū)的總共大小的最大值,程序文件超過此值將會報(bào)錯,這里取默認(rèn)值0x0008_0000即可。
Line6: 0x0000_8000表示執(zhí)行域的起始地址,即程序從0x0000_8000地址處開始執(zhí)行,0x0008_0000代表的意思參考上一行。
Line7:此行的作用是把中斷向量表定位在起始地址處(這里是0x0000_8000).
要使用此分散加載描述文件,還需要將Target Opitions…->Linker下的Use Memory Layout from Target Dialog前的“√”去掉。
IAP函數(shù)的使用
Iap函數(shù)是固化在Boot Rom中地址0x1FFF1FF1處的一個(gè)有傳入?yún)?shù)和返回參數(shù)的一個(gè)函數(shù)。對于不同的傳入?yún)?shù),iap函數(shù)實(shí)現(xiàn)不同的功能。關(guān)于這些功能的詳細(xì)介紹,《LPC1768 user manual》32章第8節(jié)IAP commands一節(jié)中有詳細(xì)介紹,這里不贅述。遠(yuǎn)程升級中我們常用到的幾個(gè)iap命令是:讀器件標(biāo)識號、準(zhǔn)備寫操作扇區(qū)、擦除扇區(qū)、扇區(qū)查空、將RAM內(nèi)容復(fù)制到Flash、比較<地址1><地址2><字節(jié)數(shù)>。
以準(zhǔn)備寫操作扇區(qū)為例說明iap函數(shù)的寫法。首先定義iap函數(shù)的入口地址:
1
#define  IAP_ENTER_ADR  0x1FFF1FF1/*IAP函數(shù)入口地址*/
接著聲明函數(shù)類型指針I(yè)AP_Entry:
1
void (*IAP_Entry)(uint32  paramin[ ],uint32  paramout[ ]) ;
初始化IAP函數(shù)指針使其指向IAP函數(shù)入口地址:
1
2
3
4
void  IAP_EntryInit(void)
{
   IAP_Entry  =( void(*)() )IAP_ENTER_ADR ;
}
由用戶手冊可知iap命令匯總?cè)缦聢D:
Iap狀態(tài)碼匯總?cè)缦聢D:
據(jù)此寫出IAP命令字和狀態(tài)碼宏定義如下:(PS:Command Code中的數(shù)字10表示十進(jìn)制,如:Read part ID的命令字為5410,表示其命令字為十進(jìn)制的54,見上圖)
準(zhǔn)備寫操作扇區(qū)的命令解釋如下圖:
由上圖可知準(zhǔn)備寫操作扇區(qū)需要三個(gè)參數(shù),分別是Command code、Param0、Param1,返回狀態(tài)碼的可能取值為CMD_SUCCESS、BUSY、INVALID_SECTOR。據(jù)此我們寫處準(zhǔn)備寫操作扇區(qū)的命令函數(shù)如下:
1
2
3
4
5
6
7
8
9
uint32  PreSector(uint8  arg1,uint8  arg2)
{
    paramin[0] = IAP_SELECTOR ;    //設(shè)置命令字
    paramin[1] = arg1 ;        //設(shè)置參數(shù)
    paramin[2] = arg2 ;
    (*IAP_Entry)(paramin, paramout) ; //調(diào)用IAP服務(wù)程序

    Return (paramout[0]) ;      // 返回狀態(tài)碼
}
其中paramin[]、paramout[]為定義的uint32型全局?jǐn)?shù)組。調(diào)用此函數(shù)時(shí)只需將起始扇區(qū)號傳給arg1,結(jié)束扇區(qū)號傳給arg2即可。其他命令的函數(shù)書寫于此大同小異。
這里給出一個(gè)遠(yuǎn)程升級的iap流程:讀取器件標(biāo)識碼確定是當(dāng)前芯片→確定待升級程序(用戶程序)占用的起始扇區(qū)號與結(jié)束扇區(qū)號→準(zhǔn)備需占用扇區(qū)→擦除需占用扇區(qū)→扇區(qū)查空確定需占用扇區(qū)已成功擦除→執(zhí)行將RAM復(fù)制到Flash命令將數(shù)據(jù)塊復(fù)制到Flash→執(zhí)行比較命令校驗(yàn)數(shù)據(jù)是否正確→如果正確執(zhí)行下一數(shù)據(jù)塊的復(fù)制。
需要說明的一點(diǎn)是:在還行IAP命令的時(shí)候,需要關(guān)閉中斷以保證IAP命令的正確執(zhí)行。幸運(yùn)的是Cortex-M3提供了關(guān)閉/打開中斷的指令CPSID I 和CPSIE I,而且在core_cm3.h中也提供這樣的開關(guān)中斷的函數(shù)__enable_irq()和__disable_irq(),需要的時(shí)候直接去調(diào)用就可以。
bootloaderUsrApp的跳轉(zhuǎn):
這里我們把引導(dǎo)cpu進(jìn)入用戶代碼區(qū)的程序稱作bootloader,把用戶實(shí)際實(shí)現(xiàn)相應(yīng)功能的程序稱作UsrApp,它們是一個(gè)完整的程序,下文提到的bootloader、UsrApp均指這些。
bootloader到UsrApp的跳轉(zhuǎn)需要熟知兩方面的知識:一個(gè)是中斷向量表的重映射,另一個(gè)是一段完整的程序的入口是如何定義的。
中斷向量表重映射
在LPC1768中,位于地址0xE000_ED08處有一個(gè)向量表偏移寄存器VTOR,通過修改此寄存器可以設(shè)定向量表基址位于Code區(qū)或是RAM區(qū)以及向量表的基址偏移域,以此達(dá)到中斷向量表重映射的目的。比如我們的UsrApp起始地址為0x8000;為使UsrApp在發(fā)生中斷行為時(shí)不會產(chǎn)生錯誤或異常,我們可以通過以下代碼段將中斷向量表重映射到地址0x8000處Code區(qū)。
1
SCB->VTOR = USR_APP_START_ADDR &0x1FFFFF80;
SCB->VTOR由core_cm3.c提供,對應(yīng)向量表偏移寄存器地址0xE000_ED08。USR_APP_START_ADDR為用戶代碼區(qū)的起始地址,和數(shù)值0x1FFFFF80按位與是保證寄存器的保留位為0和向量表基址位于Code區(qū)。此寄存器的詳細(xì)說明請參考《cortex-M3技術(shù)參考手冊》第八章第二節(jié)的NVIC寄存器描述或《LPC17XX User manual》英文版34.4.3.5章節(jié)。,此處不再貼出。對于用戶程序,在bootloader中的向量表偏移設(shè)置并不起作用,需要在用戶程序中重新設(shè)置向量表偏移寄存器。原因是CM3器件進(jìn)入main()函數(shù)即要求調(diào)用SystemInit(void)進(jìn)行系統(tǒng)初始化,系統(tǒng)初始化的時(shí)候?qū)⑾蛄勘砥萍拇嫫髑辶懔,所以需要在調(diào)用SystemInit()之后重新設(shè)置向量表偏移寄存器。有網(wǎng)文稱對于應(yīng)用了OS的用戶程序需要這樣做,其實(shí)對于開啟了中斷的的用戶程序都需要這樣,你的簡單的IAP測試程序之所以在沒有這樣做的情況下通過了測試,是因?yàn)槟愕挠脩舫绦驕y試代碼中并沒有用到中斷。
一個(gè)完整鏡像的程序入口:
對于在flash中存儲的一個(gè)完整的程序代碼,其起始部分應(yīng)該為向量表,向量表的內(nèi)容格式固定如下表(參考《cortex-M3權(quán)威指南》7.3節(jié))
上電后的向量表:
由表可知,對于起始存儲地址為0的一段完整程序,其首地址處存放的是MSP的初始值,偏移4字節(jié)的地址處存放的是PC指針的初始值,我們要運(yùn)行這段完整的程序,只需將這段完整程序的SP、PC初始值賦給SP和PC寄存器即可,具體實(shí)現(xiàn)的函數(shù)如下:
1
2
3
4
5
__asm void boot_jump(uint32 address)
{
   LDR  SP, [R0]
LDR  PC ,[R0, #4]
}
對于此函數(shù)的解釋:__asm是MDK的編譯器提供的嵌入?yún)R編的指令(RealView C編譯器3.0以上版本提供)。函數(shù)體中兩行匯編代碼的功能分別為:
LDR  SP, [R0]:把R0中的值作為地址,將此地址中的值賦給SP
LDR  PC ,[R0, #4]:把R0中的值加4作為地址,將此地址中的值賦給SP
這里涉及到一個(gè)問題,r0中的值是什么?我們根據(jù)ATPCS(ARM-THUMBprocedure call standard)可知,對于參數(shù)少于等于4的函數(shù),參數(shù)是通過R0~R3傳遞的,第一個(gè)參數(shù)放在R0中,依次類推。所以這里的R0存放的正式UsrApp的起始地址,回過頭再看前面的兩行匯編代碼,它們做的事正是將UsrApp的SP、PC初始值賦給相應(yīng)寄存器,達(dá)到開始運(yùn)行UsrApp的目的。
UsrApp的燒寫:
因?yàn)樵谄瑑?nèi)flash的起始地址處燒寫的是我們的iap處理程序,用戶程序的起始地址不是0x0000_0000,在通過keil用jlink下載程序的時(shí)候,還需做一個(gè)設(shè)置。在Option for Target→Debug→Settings→Flash Download中,設(shè)置下載程序時(shí)的起始地址為用戶程序的起始地址。如本例中用戶程序的起始地址是0x0000_8000,我們設(shè)置下載程序的起始地址為0x0000_8000,如下圖。如果不這么做的話,通過jlink下載程序的時(shí)候會從地址0x0000_0000開始擦除。
遺留的問題:
疑問1:《lpc1768 user manual》34.4.3.5章節(jié)指出中斷向量表偏移寄存器的向量表基址偏移域?yàn)閇28:8]位,為什么將偏移量實(shí)際賦值給此寄存器的時(shí)候沒有做’<<8’的處理而是直接賦值過來?PS:《Cortex-M3技術(shù)參考手冊》第8章的表8-15指出中斷向量偏移寄存器的向量表基址偏移域?yàn)閇28:7]
疑問2:手冊中指出使用iap的時(shí)候RAM頂部32字節(jié)空出,這該怎么理解?iap占用ram頂端32字節(jié),即使我不去刻意留出也不影響iap使用這32字節(jié)的空間,我能想到的唯一解釋是編譯器可能會把程序中的全局變量放到ram頂端32字節(jié)區(qū)域進(jìn)而調(diào)用iap函數(shù)會引起全局變量被修改,是這樣么?
  如果你讀到了這里并且你知道這些答案,敬請告知解惑。E-mail:arm6410@126.com
參考過的文獻(xiàn):
《arm匯編指令集》--------網(wǎng)文
《arm啟動代碼的探究-鄭遠(yuǎn)超》
《arm體系結(jié)構(gòu)與編程》------杜春雷(PS:重點(diǎn)第11章)
《map文件認(rèn)識初步》-------網(wǎng)文
《匯編器指南》------RealviewMDK
《鏈接器指南》------RealviewMDK
《中斷向量表重映射與復(fù)制》------網(wǎng)文
《cortex-m3技術(shù)參考手冊》









  之前說了stm32的iap編程,今天天氣真好,順手就來說說lpc1788的iap編程(沒看前面的請查看stm筆記下的內(nèi)容)
  首先是flash的算法,lpc1768并沒有寄存器來讓我們操作flash,他內(nèi)置了iap的flash算法,在技術(shù)手冊的525頁有如下說明
  
  其支持的iap命令有這些
這樣我們就能夠做出相關(guān)的flash讀寫借口呢(具體請查看lpc1768的技術(shù)手冊)
unsigned param_table[5];//傳遞參數(shù)列表
unsigned result_table[5];//返回結(jié)果列表
//調(diào)用iap命令
void iap_entry(unsigned param_tab[],unsigned result_tab[])
{
    void (*iap)(unsigned [],unsigned []);
    iap = (void (*)(unsigned [],unsigned []))IAP_ADDRESS;
    iap(param_tab,result_tab);
}
  通過這種手段就能夠調(diào)用iap命令,我們演示性的看一個(gè)命令
//扇區(qū)準(zhǔn)備好指令
//起始扇區(qū)號 結(jié)束扇區(qū)號 系統(tǒng)時(shí)鐘
void prepare_sector(unsigned start_sector,unsigned end_sector,unsigned cclk)
{
    param_table[0] = PREPARE_SECTOR_FOR_WRITE;
    param_table[1] = start_sector;
    param_table[2] = end_sector;
    param_table[3] = cclk;
    iap_entry(param_table,result_table);
}
  該指令在寫flash和擦除flash之前必須調(diào)用
具體的完整flash代碼請查看工程文件,會在文章末尾上傳
然后依舊是五個(gè)指令
"iap_down"
"iap_jump_app"
"iap_over"
"iap_set_flag"
"iap_clear_flag"
  功能和之前的stm32差不多,但是下載算法變化了,因?yàn)閟tm32支持的寫入是每次寫入一個(gè)十六位數(shù)據(jù),而lpc1768每次寫入8位數(shù)據(jù),而且每次寫入數(shù)據(jù)的量為128/256/512/1024/4096,正好沒有我們之前所用的2048,所以算法修改成如下的樣子

  1. u8 iapbuf[1024] = {0}; //用于緩存數(shù)據(jù)的數(shù)組
  2. u16 receiveDataCur = 0;  //當(dāng)前iapbuffer中已經(jīng)填充的數(shù)據(jù)長度,一次填充滿了之后寫入flash并清零
  3. u32 addrCur = FLASH_APP1_ADDR;         //當(dāng)前系統(tǒng)寫入地址,每次寫入之后地址增加2048

  4. #define vu32 volatile unsigned int

  5. //開始下載
  6. void iap_down_s(void)
  7. {
  8.     u16 i = 0;
  9.     u16 receiveCount;
  10.     if(erase_user_flash())
  11.     {
  12.        printf("error\r\n");
  13.        return;
  14.     }
  15.     printf("begin,wait data download\r\n");
  16.     receiveMode = 1;//串口進(jìn)入下載接收數(shù)據(jù)模式
  17.     while(1)
  18.     {
  19.        //循環(huán)接收數(shù)據(jù),每次必須要發(fā)128個(gè)數(shù)據(jù)下來,如果沒有128,說明這是最后一包數(shù)據(jù)
  20.        //接收到一包數(shù)據(jù)之后,返回一個(gè)小數(shù)點(diǎn),發(fā)送完成,系統(tǒng)編程完成之后返回一個(gè)iap_over
  21.        if(serial_Buffer_Length & 0x8000)
  22.        {
  23.            receiveCount = (u8)(serial_Buffer_Length&0x00ff);
  24.            if(receiveCount == 128)//滿足一包,填充并查看是否有了1024字節(jié),有了寫入閃存
  25.            {
  26.               for(i = 0; i < receiveCount; i++)
  27.               {
  28.                   iapbuf[receiveDataCur] = serial_Buffer[i];
  29.                   receiveDataCur++;//完成之后receiveDataCur++;
  30.               }
  31.               receiveExpectCount = 0;//清除期望接收模式
  32.               serial_Buffer_Length = 0;//清除串口滿標(biāo)志
  33.               printf(".");//每次接受一次數(shù)據(jù)打一個(gè)點(diǎn)
  34.               //此時(shí)需要檢測receiveDataCur的值,要是放滿了,就需要寫入
  35.               if(receiveDataCur == 1024)
  36.               {
  37.                   //寫入flash中
  38.                   if(write_flash(100000,addrCur,(unsigned*)iapbuf,1024))
  39.                   {
  40.                      receiveMode = 0;
  41.                      addrCur = FLASH_APP1_ADDR;
  42.                      receiveDataCur = 0;
  43.                      return;
  44.                   }
  45.                   addrCur += 1024;//地址+2048
  46.                   //寫完之后receiveDataCur要清零等待下一次傳輸
  47.                   receiveDataCur = 0;
  48.               }
  49.               else //有可能最后一包有128個(gè)數(shù)據(jù)但是最終沒有2048個(gè)數(shù)據(jù),此時(shí)擴(kuò)展一個(gè)指令用于完成最后一個(gè)的寫入
  50.               {
  51.                  
  52.               }
  53.               //還沒放滿,等待下一次數(shù)據(jù)過來
  54.            }
  55.            else   //不滿足一包,說明數(shù)據(jù)傳送這是最后一包,寫入閃存
  56.            {
  57.               //沒有一包也要傳送到緩存中
  58.               for(i = 0; i < receiveCount; i++)
  59.               {
  60.                   iapbuf[receiveDataCur] = serial_Buffer[i];
  61.                   receiveDataCur++;//完成之后receiveDataCur++;
  62.               }
  63.               receiveExpectCount = 0;//清除期望接收模式
  64.               serial_Buffer_Length = 0;//清除串口滿標(biāo)志
  65.               printf(".");//每次接受一次數(shù)據(jù)打一個(gè)點(diǎn)
  66.               //要將沒接收滿的數(shù)據(jù)變成0xff
  67.               for(i= receiveDataCur; i < 1024; i++)
  68.               {
  69.                   iapbuf[i] = 0xff;
  70.               }
  71.               //之后就要將這數(shù)據(jù)寫入到閃存中
  72.               if(write_flash(100000,addrCur,(unsigned*)iapbuf,1024))
  73.               {
  74.                   receiveMode = 0;
  75.                   addrCur = FLASH_APP1_ADDR;
  76.                   receiveDataCur = 0;
  77.                   return;
  78.               }
  79.               //printf("\r\nwrite addr %x,length %d\r\n",addrCur,receiveDataCur);
  80.               //寫完之后要把地址恢復(fù)到原來的位置
  81.               addrCur = FLASH_APP1_ADDR;
  82.               receiveDataCur = 0;
  83.               //寫完之后要退出下載循環(huán)并告訴上位機(jī),已經(jīng)下載完了
  84.               printf("download over\r\n");
  85.                //同時(shí),也要退出下載循環(huán)模式
  86.               receiveMode = 0;
  87.               return;
  88.            }
復(fù)制代碼

 因?yàn)閘pc1768比較獨(dú)特的扇區(qū)分區(qū),如下
我們要修改地址的定義,現(xiàn)在app代碼不能存放在0x08002000位置了,而是存儲在0x00003000的位置,我們將012三個(gè)扇區(qū)當(dāng)做iap代碼的存放區(qū)域,并將2號扇區(qū)的最后一個(gè)位定義成了app固化標(biāo)志存在的位置,寫入固化標(biāo)志的函數(shù)如下
unsigned char iapConfigBuffer[4096];//一個(gè)扇區(qū)4K
  1. //失敗返回1 成功返回0
  2. u8 Iap_Write_Config_Value(u8 value)
  3. {
  4.     u32 i = 0;
  5.     u8 *p;
  6.     p = (u8*)SECTOR_2_START;
  7.     //首先要將第三扇區(qū)的數(shù)據(jù)全部讀取到ram里面
  8.     for(i = 0; i < 4096; i++)
  9.     {
  10.        iapConfigBuffer[i] = *p;
  11.        p++;
  12.     }
  13.     //然后檢查最后一個(gè)數(shù)據(jù)和我們要設(shè)置的數(shù)據(jù)是否相等

  14.     if(iapConfigBuffer[4095] == value)//相等,不用設(shè)置了

  15.     {
  16.        return 0;
  17.     }
  18.     else
  19.     {
  20.        //不等,先擦除第2扇區(qū)
  21.        prepare_sector(2,2,100000);
  22.        erase_sector(2,2,100000);
  23.        if(result_table[0] != CMD_SUCCESS)
  24.        {
  25.            return 1;//擦除失敗
  26.        }
  27.        //將數(shù)組最后一個(gè)元素設(shè)置為指定值
  28.        iapConfigBuffer[4095] = value;
  29.        prepare_sector(2,2,100000);//根據(jù)地址找出應(yīng)當(dāng)準(zhǔn)備哪一個(gè)扇區(qū)
  30.        write_data(100000,SECTOR_2_START,(unsigned*)iapConfigBuffer,4096);
  31.        if(result_table[0] != CMD_SUCCESS)
  32.        {
  33.          return 1;
  34.        }
  35.        return 0;
  36.     }
  37. }
復(fù)制代碼


  擦除一個(gè)扇區(qū)的時(shí)候必須將扇區(qū)內(nèi)容保存下來,所以必須定義一個(gè)能存放4096數(shù)據(jù)的數(shù)組,寫入的時(shí)候先讀取,在擦除,防止數(shù)據(jù)丟失
Flash存放的地址修改成這樣
#define APP_CONFIG_ADDR     0X00002FFF //配置地址
#define APP_CONFIG_SET_VALUE    0X55       //設(shè)置值
#define APP_CONFIG_CLEAR_VALUE  0XFF       //清零值
#define FLASH_APP1_ADDR     0x00003000     //第一個(gè)應(yīng)用程序起始地址(存放在FLASH)
                                     //保留的空間為IAP使用
  相應(yīng)的配置就要改成如下
  當(dāng)然,在main函數(shù)中還是之前一樣的流程
  而對于app來說,改動的位置是這些
  還有在系統(tǒng)初始化函數(shù)SystemInit中的這個(gè)
  修改成
#define VECT_TAB_OFFSET  0x3000
  這樣基本就能完成了,突然發(fā)現(xiàn),這樣app是編譯不過去的,原因在于startup_LPC17XX.s文件中的110行有這么一段
該段匯編代碼在flash的0x000002fc位置存放了一個(gè)0xffffffff,但是app程序從0x00003000啟動,是不能操作0x000002fc處的flash的,我們來看看這個(gè)位置存放的是什么
  可以看到,這一段是進(jìn)行flash讀保護(hù)的,這一段我們可以注釋掉了,因?yàn)閕ap部分的啟動代碼我們沒有改啊,iap代碼中已經(jīng)設(shè)置了crp,那iap引導(dǎo)的app只要不去修改,crp就是我們在iap中定義了,放心大膽的注釋掉吧.
現(xiàn)在我們比一比相對于stm32的iap,lpc1788的iap有啥變化
  •                           flash擦寫算法變了
  •                           程序的存儲空間變了
  •                           中斷向量表的位置變了
  •                           Crpstm32沒有的,算是一個(gè)新東西
  •                           因?yàn)?/font>flash的擦寫算法變了,所以我們底層接收數(shù)據(jù)處理數(shù)據(jù)的方式變了
  綜上,協(xié)議沒變,我們依然可以按照之前的協(xié)議使用,那么,之前的上位機(jī)還算可以使用的
  對了,該上位機(jī)需要下載bin文件,需要在keil中將axf轉(zhuǎn)換成bin文件,設(shè)置如下
  Target的user界面
  注意啊,這是我的工程分布,我在工程文件所在目錄里面放了一個(gè)output目錄,axf生成到了output目錄中,如果你沒有output目錄,那么將output刪除,設(shè)置生成axf的位置和工程文件的位置位于同一目錄就可以了


完整的Word格式文檔51黑下載地址:
LPC1768 IAP升級.zip (2.07 MB, 下載次數(shù): 94)


評分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎勵!

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏2 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:6656 發(fā)表于 2018-12-10 14:23 | 只看該作者
多謝樓主分享資料
回復(fù)

使用道具 舉報(bào)

板凳
ID:225107 發(fā)表于 2019-3-2 16:13 | 只看該作者
謝謝樓主。很不錯的東西!
回復(fù)

使用道具 舉報(bào)

地板
ID:225107 發(fā)表于 2019-3-2 16:14 | 只看該作者
謝謝樓主。很不錯的資料!
回復(fù)

使用道具 舉報(bào)

5#
ID:135928 發(fā)表于 2019-7-11 17:04 | 只看該作者
下載看看 學(xué)習(xí)
回復(fù)

使用道具 舉報(bào)

6#
ID:157087 發(fā)表于 2020-3-9 16:53 | 只看該作者
謝謝樓主的資料
回復(fù)

使用道具 舉報(bào)

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表