IAR環(huán)境下STM32+IAP方案的實(shí)現(xiàn)
小弟近日看到一篇極好的文章,在此轉(zhuǎn)載分享給大家也是給自己做個(gè)備份吧?
**********************************分割線(xiàn)*************************************
--基于STM32F103ZET6的UART通訊實(shí)現(xiàn)
一、什么是IAP,為什么要IAP??????IAP即為In Application Programming(在應(yīng)用中編程),一般情況下,以STM32F10x系列芯片為主控制器的設(shè)備在出廠(chǎng)時(shí)就已經(jīng)使用J-Link仿真器將應(yīng)用代碼燒錄了,如果在設(shè)備使用過(guò)程中需要進(jìn)行應(yīng)用代碼的更換、升級(jí)等操作的話(huà),則可能需要將設(shè)備返回原廠(chǎng)并拆解出來(lái)再使用J-Link重新燒錄代碼,這就增加了很多不必要的麻煩。站在用戶(hù)的角度來(lái)說(shuō),就是能讓用戶(hù)自己來(lái)更換設(shè)備里邊的代碼程序而廠(chǎng)家這邊只需要提供給用戶(hù)一個(gè)代碼文件即可。??????而IAP卻能很好的解決掉這個(gè)難題,一片STM32芯片的Code(代碼)區(qū)內(nèi)一般只有一個(gè)用戶(hù)程序。而IAP方案則是將代碼區(qū)劃分為兩部分,兩部分區(qū)域各存放一個(gè)程序,一個(gè)叫bootloader(引導(dǎo)加載程序),另一個(gè)較user application(用戶(hù)應(yīng)用程序)。bootloader在出廠(chǎng)時(shí)就固定下來(lái)了,在需要變更user application時(shí)只需要通過(guò)觸發(fā)bootloader對(duì)userapplication的擦除和重新寫(xiě)入即可完成用戶(hù)應(yīng)用的更換。如圖1-1所示圖 1-1??????在程序執(zhí)行初始進(jìn)入bootloader,在bootloader里面檢測(cè)條件是否被觸發(fā)(可通過(guò)按鍵是否被按下、串口是否接收到特定的數(shù)據(jù)、U盤(pán)是否插入等等),如果有則進(jìn)行對(duì)user application進(jìn)行擦除和重新寫(xiě)入操作,如果沒(méi)有則直接跳轉(zhuǎn)到user application執(zhí)行應(yīng)用;如果有則進(jìn)行擦除用戶(hù)代碼并重新寫(xiě)入新的用戶(hù)代碼。?二、STM32F103ZET6硬件條件??????STM32F103ZET6的啟動(dòng)方式有三種:內(nèi)置FLASH啟動(dòng)、內(nèi)置SRAM啟動(dòng)、系統(tǒng)存儲(chǔ)器ROM啟動(dòng),通過(guò)BOOT0和BOOT1引腳的設(shè)置可以選擇從哪中方式啟動(dòng),這里選擇內(nèi)置的FLASH啟動(dòng)。其FLASH的地址為0x08000000—0x0807ffff,共512KB,這些都能從芯片數(shù)據(jù)手冊(cè)中直接得到。而這里首要的一個(gè)問(wèn)題是中斷的問(wèn)題。正常情況下發(fā)生中斷的過(guò)程為:發(fā)生中斷(中斷請(qǐng)求)à到中斷向量表查找中斷函數(shù)入口地址à跳轉(zhuǎn)到中斷函數(shù)à執(zhí)行中斷函數(shù)à中斷返回。也就是說(shuō)在STM32的內(nèi)置的Flash中有一個(gè)中斷向量表來(lái)存放各個(gè)中斷服務(wù)函數(shù)的入口地址,內(nèi)置Flash的分配情況大致如下圖2-1。圖2-1?在只有一個(gè)程序的情況下,程序執(zhí)行的走向應(yīng)該如圖2-2所示(借用網(wǎng)友的原圖)。圖2-2??????STM32F10x有一個(gè)中斷向量表,這個(gè)中斷向量表存放在代碼開(kāi)始部分的后4個(gè)字節(jié)處(即0x08000004),代碼開(kāi)始的4個(gè)字節(jié)存放的是堆棧棧頂?shù)牡刂?,?dāng)發(fā)生中斷后程序通過(guò)查找該表得到相應(yīng)的中斷服務(wù)程序入口地址,然后再跳到相應(yīng)的中斷服務(wù)程序中執(zhí)行。上電后從0x08000004處取出復(fù)位中斷向量的地址,然后跳轉(zhuǎn)到復(fù)位中斷程序的入口(標(biāo)號(hào)①所示),執(zhí)行結(jié)束后跳轉(zhuǎn)到main函數(shù)中(標(biāo)號(hào)②所示)。在執(zhí)行main函數(shù)的過(guò)程中發(fā)生中斷,則STM32強(qiáng)制將PC指針指回中斷向量表處(標(biāo)號(hào)③所示),從中斷向量表中找到相應(yīng)的中斷函數(shù)入口地址,跳轉(zhuǎn)到相應(yīng)的中斷服務(wù)函數(shù)(標(biāo)號(hào)④所示),執(zhí)行完中斷函數(shù)后再返回到main函數(shù)中來(lái)(標(biāo)號(hào)⑤所示)。??????若在STM32F103x中使用IAP方案,則內(nèi)置的Flash分配情況大致如下圖2-3。圖2-3在內(nèi)置的Flash里面添加一個(gè)BootLoader程序,BootLoader程序和user?application各有一個(gè)中斷向量表,假設(shè)BootLoader程序占用的空間為N+M字節(jié),則程序的走向應(yīng)該如圖2-2所示(借用網(wǎng)友的原圖并做改動(dòng),其中虛線(xiàn)部分為原圖步驟④⑤的走向,本人改為指向灰色部分)。圖2-2??????上電初始程序依然從0x08000004處取出復(fù)位中斷向量地址,執(zhí)行復(fù)位中斷函數(shù)后跳轉(zhuǎn)到IAP的main(標(biāo)號(hào)①所示),在IAP的main函數(shù)執(zhí)行完成后強(qiáng)制跳轉(zhuǎn)到0x08000004+N+M處(標(biāo)號(hào)②所示),最后跳轉(zhuǎn)到新的main函數(shù)中來(lái)(標(biāo)號(hào)③所示),當(dāng)發(fā)生中斷請(qǐng)求后,程序跳轉(zhuǎn)到新的中斷向量表中取出新的中斷函數(shù)入口地址,再跳轉(zhuǎn)到新的中斷服務(wù)函數(shù)中執(zhí)行(標(biāo)號(hào)④⑤所示),執(zhí)行完中斷函數(shù)后再返回到main函數(shù)中來(lái)(標(biāo)號(hào)⑥所示)。??????對(duì)于步驟④⑤,網(wǎng)友認(rèn)為是:“在main執(zhí)行的過(guò)程中,如果CPU得到一個(gè)中斷請(qǐng)求,PC指針仍強(qiáng)制跳轉(zhuǎn)到地址0x08000004中斷向量表處,而不是新的中斷向量表,如圖標(biāo)號(hào)④所示,程序再根據(jù)我們?cè)O(shè)置的中斷向量表偏移量,跳轉(zhuǎn)到對(duì)應(yīng)中斷源新的中斷服務(wù)程序中,如圖標(biāo)號(hào)⑤所示”。我對(duì)此的理解是:“當(dāng)發(fā)生中斷后,程序從0x08000004(舊)處的中斷向量表中得到相應(yīng)的中斷服務(wù)函數(shù)入口地址,繼而跳轉(zhuǎn)到相應(yīng)的中斷服務(wù)程序”。但是舊的中斷向量列表里邊存放的是IAP程序中斷函數(shù)的入口地址,它是如何得到user程序中斷函數(shù)的入口地址呢?所以我覺(jué)得此種說(shuō)法是錯(cuò)誤的?!爱?dāng)發(fā)生中斷時(shí)PC指針強(qiáng)制會(huì)跳轉(zhuǎn)到0x08000004處”這種說(shuō)法并沒(méi)有錯(cuò),只是忽略了后續(xù)的一些知識(shí)要點(diǎn)而導(dǎo)致這個(gè)說(shuō)法出現(xiàn)矛盾。??????對(duì)于步驟④⑤我認(rèn)為的是,在main函數(shù)的執(zhí)行過(guò)程中,如果CPU得到一個(gè)中斷請(qǐng)求,PC指針本來(lái)應(yīng)該跳轉(zhuǎn)到0x08000004處的中斷向量表,由于我們?cè)O(shè)置了中斷向量表偏移量為N+M,因此PC指針被強(qiáng)制跳轉(zhuǎn)到0x08000004+N+M處的中斷向量表中得到相應(yīng)的中斷函數(shù)地址(待求證),再跳轉(zhuǎn)到相應(yīng)新的中斷服務(wù)函數(shù),執(zhí)行結(jié)束后返回到main函數(shù)中來(lái)。?三、實(shí)現(xiàn)過(guò)程??????STM32F103ZET6的Flash地址為0x08000000—0x0807ffff共512KB,把這512KB的空間分為兩塊,第一塊大小為32KB存放BootLoader程序,剩余的空間存放用戶(hù)程序(根據(jù)實(shí)際情況分配這兩塊空間的大小,BootLoader程序占用的空間越小越好,則BootLoader地址為0x08000000—0x08007fff,用戶(hù)程序地址為0x08008000—0x0807ffff。BootLoader流程圖大致應(yīng)該如下:1、初始化時(shí)鐘。2、初始化中斷向量表地址。3、初始化按鍵。??????(使用按鍵觸發(fā)方式,上電時(shí)如果按鍵被按下則進(jìn)行用戶(hù)程序更新操作)4、初始化串口。5、檢測(cè)按鍵是否被按下,是則執(zhí)行步驟6,否則執(zhí)行步驟10。6、擦除用戶(hù)程序(擦除0x08008000—0x0807ffff地址空間Flash)。7、從串口讀取新的用戶(hù)代碼數(shù)據(jù),把代碼寫(xiě)入用戶(hù)程序空間。8、檢測(cè)串口數(shù)據(jù)接收完畢?是則執(zhí)行步驟9,否則跳回步驟7。9、用戶(hù)程序更新完畢,等待重新上電或硬件復(fù)位。10、跳轉(zhuǎn)到用戶(hù)程序(強(qiáng)制將PC指針跳轉(zhuǎn)到0x08008000+4處)。?????到這里首先要解決的問(wèn)題就有:1、如何進(jìn)行對(duì)STM32的Flash進(jìn)行擦除和寫(xiě)入操作。2、中斷向量表偏移如何設(shè)置。3、如何改變代碼存放的地址空間(因?yàn)锽ootLoader要存放在0x08000000處,用戶(hù)程序要存放在0x08008000處,而默認(rèn)的代碼存放的地址空間為0x08000000)。4、怎么進(jìn)行PC指針的強(qiáng)制跳轉(zhuǎn),跳轉(zhuǎn)時(shí)需要做些什么。5、串口接收的用戶(hù)代碼數(shù)據(jù)是什么樣的代碼數(shù)據(jù),是一種什么樣的文件。?問(wèn)題的解決:1、使用STM32的固件庫(kù)函數(shù),只需調(diào)用幾個(gè)庫(kù)函數(shù)即可輕松解決,使用的固件庫(kù)為stm32f10x_flash.c文件,對(duì)Flash的操作過(guò)程簡(jiǎn)要為:Flash解鎖àFlash擦除àFlash寫(xiě)入àFlash上鎖。(對(duì)Flash編程的更詳細(xì)操作參考STM32F10xxx閃存編程手冊(cè))①解鎖:FLASH_Unlock();????????????//解鎖FlashFLASH_SetLatency(FLASH_Latency_2);????????????//因?yàn)橄到y(tǒng)時(shí)鐘為72M所以要設(shè)置兩個(gè)時(shí)鐘周期的延時(shí)②擦除:for(i=0;i<240;i++){??if(FLASH_ErasePage(FLASH_ADDR+i*2048)?!= FLASH_COMPLETE)??????//一定要判斷是否擦除成功????return?ERROR;}說(shuō)明:FLASH_ErasePage(uint32_t Page_Address)即為Flash擦除操作,按頁(yè)擦除,每頁(yè)2KB,Page_Address為頁(yè)的起始地址,如0x08000000是第一頁(yè)起始地址,0x08000800為第二頁(yè)起始地址,這里的操作擦除了0x08008000—0x0807ffff地址空間的Flash。③寫(xiě)入:unsigned?char buf[1024];????????????//假設(shè)待寫(xiě)入的代碼數(shù)據(jù)unsigned short temp;????????????//臨時(shí)數(shù)據(jù)for(i=0;i<512;i++){??????temp = (buf[2*i+1]<<8) | buf[2*i];????????????//2個(gè)字節(jié)整合為1個(gè)半字??????if(FLASH_ProgramHalfWord(ADDR,temp) != FLASH_COMPLETE)??????//判斷是否寫(xiě)入成功??????{????????????Return ERROR;??????}ADDR +=2;??????//地址要加2,因?yàn)槊看螌?xiě)入的是2個(gè)字節(jié)(1個(gè)半字)}說(shuō)明:因?yàn)镾TM32的Flash寫(xiě)入為雙字節(jié)(1個(gè)半字)寫(xiě)入,F(xiàn)LASH_ProgramHalfWord(uint32_t Address, uint16_t Data)函數(shù)即為對(duì)地址為Address寫(xiě)入1個(gè)半字的Data,每次寫(xiě)入完成后地址要加2。④上鎖:FLASH_Lock();??????//Flash?上鎖,一個(gè)固件庫(kù)函數(shù)即可實(shí)現(xiàn)。?2、關(guān)于中斷向量表的偏移設(shè)置,對(duì)于BootLoader程序只需設(shè)置中斷向量表的指向在0x08000000處,對(duì)于用戶(hù)程序需要設(shè)置中斷向量表的指向在0x08008000處即可。①在BootLoader程序的中斷向量表指向設(shè)置中應(yīng)有這么一句:NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);??????//設(shè)置中斷向量表指向其中NVIC_VectTab_FLASH是個(gè)宏定義,的值為0x08000000。②在用戶(hù)程序的中斷向量表指向設(shè)置用應(yīng)有這么一句:NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x8000);??????//設(shè)置中斷向量表指向?3、確認(rèn)代碼存放的地址空間,在IAR和在Keil中的設(shè)置是不同的,網(wǎng)上有在Keil中設(shè)置的方法,設(shè)立介紹在IAR軟件環(huán)境下的設(shè)置方法。①在固件庫(kù)目錄STM32F10x_StdPeriph_Lib_V3.5.0ProjectSTM32F10x_StdPeriph_TemplateEWARM下找到一個(gè)stm32f10x_flash.icf文件,將其復(fù)制到工程目錄中來(lái),在打開(kāi)IAR工程,將配置文件添加到工程中,如下圖3-2所示圖3-1②在工程中打開(kāi)stm32f10x_flash.icf該文件,修改兩個(gè)參數(shù)即可改變代碼存放的地址空間,圖下圖3-2所示。圖3-2?4、關(guān)于PC指針的強(qiáng)制跳轉(zhuǎn),想在BootLoader程序中將PC指針跳轉(zhuǎn)到用戶(hù)代碼處,可選擇下面的操作typedef????????void (*pFunction)(void);?????pFunction???????Jump_To_Application;uint32_t???????JumpAddress;#define???????ApplicationAddress???????0x08008000??if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)???????//--------①{??JumpAddress?= *(__IO uint32_t*) (ApplicationAddress + 4);??????????????????//--------②??Jump_To_Application?= (pFunction) JumpAddress;??????????????????????????????//--------③??__set_MSP(*(__IO?uint32_t*) ApplicationAddress);?????????????????????????????//--------④??Jump_To_Application();?????????????????????????????????????????????????????????????????//--------⑤}①因?yàn)橛脩?hù)程序開(kāi)始位置(0x08008000處)的前4個(gè)字節(jié)存放的是堆棧的地址,堆棧地址必定是指向RAM空間的,而STM32的RAM空間起始地址為0x20000000,所以要進(jìn)行判斷。②程序跳轉(zhuǎn)地址的確認(rèn),前面已經(jīng)說(shuō)過(guò)0x08008004處的4個(gè)字節(jié)存放的是復(fù)位函數(shù)的入口地址,該句的意思為獲得(ApplicationAddress + 4)地址處的數(shù)據(jù),即為獲得新的復(fù)位函數(shù)入口地址。③令Jump_To_Application這個(gè)函數(shù)指針指向復(fù)位函數(shù)入口地址。④堆棧的初始化,重新設(shè)定棧頂代地址,把棧頂?shù)刂吩O(shè)置為用戶(hù)代碼指向的棧頂?shù)刂贰"萏D(zhuǎn)到新的復(fù)位函數(shù)。?5、通過(guò)串口來(lái)接收代碼數(shù)據(jù),就是PC機(jī)通過(guò)串口將代碼數(shù)據(jù)發(fā)送到STM32中去。這里就涉及到兩個(gè)問(wèn)題:①數(shù)據(jù)怎么得來(lái)。②數(shù)據(jù)傳輸?shù)倪^(guò)程需要遵循的協(xié)議,什么時(shí)候開(kāi)始,什么時(shí)候結(jié)束。解決①:一般我們就將*.hex文件使用JFlash-ARM打開(kāi)再通過(guò)Jlink仿真器燒錄到STM32芯片中,但是*.hex文件里邊包含的數(shù)據(jù)不純粹是代碼數(shù)據(jù)還有一些別的東西,而*.bin文件數(shù)據(jù)就全部是代碼數(shù)據(jù)。在IAR軟件環(huán)境中打開(kāi)一個(gè)用戶(hù)工程,先設(shè)置好中斷向量表偏移和代碼存放的地址空間后(前面已介紹過(guò)這兩種方法)。設(shè)置工程如下圖3-3所示,確認(rèn)后重新編譯工程,在工程的DebugExe目錄下會(huì)相應(yīng)生成一個(gè)xxx.bin文件,這就是所需要的代碼文件。圖3-3②數(shù)據(jù)通過(guò)串口來(lái)傳輸文件常用的協(xié)議有XModem、YModem、ZModem這三種協(xié)議,在PC端使用這些協(xié)議傳輸文件只需要PC的超級(jí)終端或者終端工具SecureCRT即可,但是在STM32這邊的編程會(huì)增加一些困難(因?yàn)橐热プx懂、解析這些協(xié)議,在通過(guò)編程來(lái)實(shí)現(xiàn))。也可選擇自己定義一套簡(jiǎn)單的傳輸協(xié)議,但同樣會(huì)有一些困難(因?yàn)橐赑C端進(jìn)行文件和串口編程)??傊还芡ㄟ^(guò)什么辦法都行,只要能將xxx.bin文件數(shù)據(jù)通過(guò)串口全部發(fā)送到STM32并且STM32能夠全部接收到這些數(shù)據(jù)并寫(xiě)入Flash即可(我選擇后者,自定義傳輸協(xié)議并用VC進(jìn)行文件和串口編程)。?四、結(jié)束語(yǔ)??????總的來(lái)說(shuō)STM32的IAP方案實(shí)現(xiàn)需要在進(jìn)行用戶(hù)程序之前加一段Bootloader程序,BootLoader程序的作用就是:①什么都不做,直接跳轉(zhuǎn)到用戶(hù)程序。②刪除原有的用戶(hù)程序,讀取*.bin文件數(shù)據(jù)并將數(shù)據(jù)重新寫(xiě)入新的用戶(hù)程序。對(duì)于用戶(hù)程序相比普通的編程只需要做三步改動(dòng)即可①改變中斷向量表。②改變代碼存放的地址空間③修改生成*.bin文件?????使用通過(guò)UART的IAP方案并不是很好的選擇,這只是IAP方案的一個(gè)機(jī)制,因?yàn)槟苁褂肞C機(jī)通過(guò)串口升級(jí)程序,同樣能通過(guò)Jlink燒寫(xiě)程序,并且自定義的串口通訊協(xié)議在沒(méi)有校CRC校驗(yàn)的情況下不能及時(shí)發(fā)現(xiàn)數(shù)據(jù)傳輸過(guò)程發(fā)生的錯(cuò)誤。這里推薦使用SD卡(或U盤(pán))進(jìn)行用戶(hù)程序更新,將*.bin文件復(fù)制到SD卡(或U盤(pán))中,STM32再通過(guò)讀取SD卡(或U盤(pán))的*.bin文件進(jìn)行用戶(hù)程序更新,這也避免了STM32與PC笨重的通訊,只需插一個(gè)SD卡(或U盤(pán))更顯得人性化一些,但需要去弄懂STM32如何與SD卡(或U盤(pán))的通訊。





