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)用就可以。 從bootloader到UsrApp的跳轉(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ù)會引起全局變量被修改,是這樣么? 參考過的文獻(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,所以算法修改成如下的樣子
- u8 iapbuf[1024] = {0}; //用于緩存數(shù)據(jù)的數(shù)組
- u16 receiveDataCur = 0; //當(dāng)前iapbuffer中已經(jīng)填充的數(shù)據(jù)長度,一次填充滿了之后寫入flash并清零
- u32 addrCur = FLASH_APP1_ADDR; //當(dāng)前系統(tǒng)寫入地址,每次寫入之后地址增加2048
-
- #define vu32 volatile unsigned int
-
- //開始下載
- void iap_down_s(void)
- {
- u16 i = 0;
- u16 receiveCount;
- if(erase_user_flash())
- {
- printf("error\r\n");
- return;
- }
- printf("begin,wait data download\r\n");
- receiveMode = 1;//串口進(jìn)入下載接收數(shù)據(jù)模式
- while(1)
- {
- //循環(huán)接收數(shù)據(jù),每次必須要發(fā)128個(gè)數(shù)據(jù)下來,如果沒有128,說明這是最后一包數(shù)據(jù)
- //接收到一包數(shù)據(jù)之后,返回一個(gè)小數(shù)點(diǎn),發(fā)送完成,系統(tǒng)編程完成之后返回一個(gè)iap_over
- if(serial_Buffer_Length & 0x8000)
- {
- receiveCount = (u8)(serial_Buffer_Length&0x00ff);
- if(receiveCount == 128)//滿足一包,填充并查看是否有了1024字節(jié),有了寫入閃存
- {
- for(i = 0; i < receiveCount; i++)
- {
- iapbuf[receiveDataCur] = serial_Buffer[i];
- receiveDataCur++;//完成之后receiveDataCur++;
- }
- receiveExpectCount = 0;//清除期望接收模式
- serial_Buffer_Length = 0;//清除串口滿標(biāo)志
- printf(".");//每次接受一次數(shù)據(jù)打一個(gè)點(diǎn)
- //此時(shí)需要檢測receiveDataCur的值,要是放滿了,就需要寫入
- if(receiveDataCur == 1024)
- {
- //寫入flash中
- if(write_flash(100000,addrCur,(unsigned*)iapbuf,1024))
- {
- receiveMode = 0;
- addrCur = FLASH_APP1_ADDR;
- receiveDataCur = 0;
- return;
- }
- addrCur += 1024;//地址+2048
- //寫完之后receiveDataCur要清零等待下一次傳輸
- receiveDataCur = 0;
- }
- else //有可能最后一包有128個(gè)數(shù)據(jù)但是最終沒有2048個(gè)數(shù)據(jù),此時(shí)擴(kuò)展一個(gè)指令用于完成最后一個(gè)的寫入
- {
-
- }
- //還沒放滿,等待下一次數(shù)據(jù)過來
- }
- else //不滿足一包,說明數(shù)據(jù)傳送這是最后一包,寫入閃存
- {
- //沒有一包也要傳送到緩存中
- for(i = 0; i < receiveCount; i++)
- {
- iapbuf[receiveDataCur] = serial_Buffer[i];
- receiveDataCur++;//完成之后receiveDataCur++;
- }
- receiveExpectCount = 0;//清除期望接收模式
- serial_Buffer_Length = 0;//清除串口滿標(biāo)志
- printf(".");//每次接受一次數(shù)據(jù)打一個(gè)點(diǎn)
- //要將沒接收滿的數(shù)據(jù)變成0xff
- for(i= receiveDataCur; i < 1024; i++)
- {
- iapbuf[i] = 0xff;
- }
- //之后就要將這數(shù)據(jù)寫入到閃存中
- if(write_flash(100000,addrCur,(unsigned*)iapbuf,1024))
- {
- receiveMode = 0;
- addrCur = FLASH_APP1_ADDR;
- receiveDataCur = 0;
- return;
- }
- //printf("\r\nwrite addr %x,length %d\r\n",addrCur,receiveDataCur);
- //寫完之后要把地址恢復(fù)到原來的位置
- addrCur = FLASH_APP1_ADDR;
- receiveDataCur = 0;
- //寫完之后要退出下載循環(huán)并告訴上位機(jī),已經(jīng)下載完了
- printf("download over\r\n");
- //同時(shí),也要退出下載循環(huán)模式
- receiveMode = 0;
- return;
- }
復(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 成功返回0
- u8 Iap_Write_Config_Value(u8 value)
- {
- u32 i = 0;
- u8 *p;
- p = (u8*)SECTOR_2_START;
- //首先要將第三扇區(qū)的數(shù)據(jù)全部讀取到ram里面
- for(i = 0; i < 4096; i++)
- {
- iapConfigBuffer[i] = *p;
- p++;
- }
- //然后檢查最后一個(gè)數(shù)據(jù)和我們要設(shè)置的數(shù)據(jù)是否相等
- if(iapConfigBuffer[4095] == value)//相等,不用設(shè)置了
- {
- return 0;
- }
- else
- {
- //不等,先擦除第2扇區(qū)
- prepare_sector(2,2,100000);
- erase_sector(2,2,100000);
- if(result_table[0] != CMD_SUCCESS)
- {
- return 1;//擦除失敗
- }
- //將數(shù)組最后一個(gè)元素設(shè)置為指定值
- iapConfigBuffer[4095] = value;
- prepare_sector(2,2,100000);//根據(jù)地址找出應(yīng)當(dāng)準(zhǔn)備哪一個(gè)扇區(qū)
- write_data(100000,SECTOR_2_START,(unsigned*)iapConfigBuffer,4096);
- if(result_table[0] != CMD_SUCCESS)
- {
- return 1;
- }
- return 0;
- }
- }
復(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擦寫算法變了
- 程序的存儲空間變了
- 中斷向量表的位置變了
- Crp是stm32沒有的,算是一個(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)
2018-10-8 05:48 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
|