我這里主要是記錄一下我所使用的方法,調(diào)試也花了兩天時(shí)間。 我所用的型號(hào)是STM32F103C8T6,這個(gè)IC有64KFlash和20K的RAM,也有小道說有后置隱藏的64K,也就是說其實(shí)是有128K,我一直也沒有測(cè)試,有空測(cè)測(cè),有大神這樣說,估計(jì)是可以的。 這里重點(diǎn)記錄一下我寫的IAP思路和代碼以及細(xì)節(jié)和遇到坑的地方。先大體的概述一下,最后貼上我認(rèn)為重點(diǎn)的代碼。 在概述之前先要解決一個(gè)問題,那就是sram空間和flash空間的問題,sram只有20K,flash有64k。解決的辦法有很多: 1)最常見的就是自己寫上位機(jī)軟件,通過分包發(fā)送,期間還可以加入加密算法,校驗(yàn)等等。 2)使用環(huán)形隊(duì)列,簡(jiǎn)單點(diǎn)說就是個(gè)環(huán)形數(shù)組,一邊接收上位機(jī)數(shù)據(jù),一邊往flash里面寫。 這里條件限制就采用第二種方法。所以即使是分給A和B的25K空間的flash空間,sram只有20K也是不能一次接收完所有的bin數(shù)據(jù)的,這里我只開辟了一個(gè)1K的BUF,使用尾插法寫入,我的測(cè)試應(yīng)用程序都在5-6K,用這樣的方法可以在9600波特率下測(cè)試穩(wěn)定,也試過57600的勉強(qiáng)可以的,115200就不行了。環(huán)形隊(duì)列代碼如下: C文件:
#include"fy_looplist.h" #include"fy_includes.h" #ifndefNULL #defineNULL0 #endif #ifndefmin #definemin(a,b)(a)<(b)?(a):(b)?// #endif #defineDEBUG_LOOP1 staticintCreate(_loopList_s*p,unsignedchar*buf,unsignedintlen); staticvoidDelete(_loopList_s*p); staticintGet_Capacity(_loopList_s*p); staticintGet_CanRead(_loopList_s*p); staticintGet_CanWrite(_loopList_s*p); staticintRead(_loopList_s*p,void*buf,unsignedintlen); staticintWrite(_loopList_s*p,constvoid*buf,unsignedintlen); struct_typdef_LoopList_list= { Create, Delete, Get_Capacity, Get_CanRead, Get_CanWrite, Read, Write }; //初始化環(huán)形緩沖區(qū) staticintCreate(_loopList_s*p,unsignedchar*buf,unsignedintlen) { if(NULL==p) { #ifDEBUG_LOOP printf("ERROR:inputlistisNULL "); #endif return0; } p->capacity=len; p->buf=buf; p->head=p->buf;//頭指向數(shù)組首地址 p->tail=p->buf;//尾指向數(shù)組首地址 return1; } //刪除一個(gè)環(huán)形緩沖區(qū) staticvoidDelete(_loopList_s*p) { if(NULL==p) { #ifDEBUG_LOOP printf("ERROR:inputlistisNULL "); #endif return; } p->buf=NULL;//地址賦值為空 p->head=NULL;//頭地址為空 p->tail=NULL;//尾地址尾空 p->capacity=0;//長(zhǎng)度為空 } //獲取鏈表的長(zhǎng)度 staticintGet_Capacity(_loopList_s*p) { if(NULL==p) { #ifDEBUG_LOOP printf("ERROR:inputlistisNULL "); #endif return-1; } returnp->capacity; } //返回能讀的空間 staticintGet_CanRead(_loopList_s*p) { if(NULL==p) { #ifDEBUG_LOOP printf("ERROR:inputlistisNULL "); #endif return-1; } if(p->head==p->tail)//頭與尾相遇 { return0; } if(p->headtail)//尾大于頭 { returnp->tail-p->head; } returnGet_Capacity(p)-(p->head-p->tail);//頭大于尾 } //返回能寫入的空間 staticintGet_CanWrite(_loopList_s*p) { if(NULL==p) { #ifDEBUG_LOOP printf("ERROR:inputlistisNULL "); #endif return-1; } returnGet_Capacity(p)-Get_CanRead(p);//總的減去已經(jīng)寫入的空間 } //p--要讀的環(huán)形鏈表 //buf--讀出的數(shù)據(jù) //count--讀的個(gè)數(shù) staticintRead(_loopList_s*p,void*buf,unsignedintlen) { intcopySz=0; if(NULL==p) { #ifDEBUG_LOOP printf("ERROR:inputlistisNULL "); #endif return-1; } if(NULL==buf) { #ifDEBUG_LOOP printf("ERROR:inputbufisNULL "); #endif return-2; } if(p->headtail)//尾大于頭 { copySz=min(len,Get_CanRead(p));//比較能讀的個(gè)數(shù) memcpy(buf,p->head,copySz);//讀出數(shù)據(jù) p->head+=copySz;//頭指針加上讀取的個(gè)數(shù) returncopySz;//返回讀取的個(gè)數(shù) } else//頭大于等于了尾 { if(lenhead-p->buf))//讀的個(gè)數(shù)小于頭上面的數(shù)據(jù)量 { copySz=len;//讀出的個(gè)數(shù) memcpy(buf,p->head,copySz); p->head+=copySz; returncopySz; } else//讀的個(gè)數(shù)大于頭上面的數(shù)據(jù)量 { copySz=Get_Capacity(p)-(p->head-p->buf);//先讀出來頭上面的數(shù)據(jù) memcpy(buf,p->head,copySz); p->head=p->buf;//頭指針指向數(shù)組的首地址 //還要讀的個(gè)數(shù) copySz+=Read(p,(char*)buf+copySz,len-copySz);//接著讀剩余要讀的個(gè)數(shù) returncopySz; } } } //p--要寫的環(huán)形鏈表 //buf--寫出的數(shù)據(jù) //len--寫的個(gè)數(shù) staticintWrite(_loopList_s*p,constvoid*buf,unsignedintlen) { inttailAvailSz=0;//尾部剩余空間 if(NULL==p) { #ifDEBUG_LOOP printf("ERROR:listisempty "); #endif return-1; } if(NULL==buf) { #ifDEBUG_LOOP printf("ERROR:bufisempty "); #endif return-2; } if(len>=Get_CanWrite(p))//如果剩余的空間不夠 { #ifDEBUG_LOOP printf("ERROR:nomemory "); #endif return-3; } if(p->head<=?p->tail)//頭小于等于尾 { tailAvailSz=Get_Capacity(p)-(p->tail-p->buf);//查看尾上面剩余的空間 if(len<=?tailAvailSz)//個(gè)數(shù)小于等于尾上面剩余的空間 { memcpy(p->tail,buf,len);//拷貝數(shù)據(jù)到環(huán)形數(shù)組 p->tail+=len;//尾指針加上數(shù)據(jù)個(gè)數(shù) if(p->tail==p->buf+Get_Capacity(p))//正好寫到最后 { p->tail=p->buf;//尾指向數(shù)組的首地址 } returnlen;//返回寫入的數(shù)據(jù)個(gè)數(shù) } else { memcpy(p->tail,buf,tailAvailSz);//填入尾上面剩余的空間 p->tail=p->buf;//尾指針指向數(shù)組首地址 //剩余空間剩余數(shù)據(jù)的首地址剩余數(shù)據(jù)的個(gè)數(shù) returntailAvailSz+Write(p,(char*)buf+tailAvailSz,len-tailAvailSz);//接著寫剩余的數(shù)據(jù) } } else//頭大于尾 { memcpy(p->tail,buf,len); p->tail+=len; returnlen; } } /*********************************************ENDOFFILE********************************************/
1、整體思路
把64K的flash空間分成了4個(gè)部分,第一部分是BootLoader,第二部分是程序A(APP1),第三部分是程序B(APP2),第四部分是用來存儲(chǔ)一些變量和標(biāo)記的。下面是空間的分配情況。BootLoader程序可以用來更新程序A,而程序A又更新程序B,程序B可以更新程序A。 最開始的時(shí)候想的是程序A、B都帶更新了干嘛還多此一舉,其實(shí)這個(gè)Bootloader還是需要的。如果之后程序A、B和FLAG三部分,假設(shè)一種情況,在程序B中更新程序A中遇到問題,復(fù)位后直接成磚,因?yàn)槌绦駻在其實(shí)地址,上電直接運(yùn)行程序A,而程序A現(xiàn)在出問題了,那就沒招了。 所以加上BootLoader情況下,不管怎么樣BootLoader的程序是不會(huì)錯(cuò)的,因?yàn)楦虏粫?huì)更新BootLoader,計(jì)時(shí)更新出錯(cuò)了,還可以進(jìn)入BootLoader重新更新應(yīng)用程序。我見也有另外一種設(shè)計(jì)方法的,就是應(yīng)用程序只有一個(gè)程序A,把程序B區(qū)域的flash當(dāng)作緩存用,重啟的時(shí)候判斷B區(qū)域有沒有更新程序,有的話就把B拷貝到A,然后擦除B,我感覺這樣其實(shí)也一樣,反正不管怎么樣這部分空間是必須要預(yù)留出來的。
2、Bootloader部分
BootLoader的任務(wù)有兩個(gè),一是在串口中斷接收BIN的數(shù)據(jù)和主循環(huán)內(nèi)判斷以及更新APP1的程序,二是在在程序開始的時(shí)候判斷有沒有可用的用戶程序進(jìn)而跳轉(zhuǎn)到用戶程序(程序A或者程序B)。 簡(jiǎn)單介紹下執(zhí)行流程: 系統(tǒng)上電首先肯定是執(zhí)行BootLoader程序的,因?yàn)樗钠鹗嫉刂肪褪?x08000000,首先是初始化,然后判斷按鍵是否手動(dòng)升級(jí)程序,按鍵按下了就把FLAG部分的APP標(biāo)記寫成0xFFFF(這里用的宏定義方式),再執(zhí)行執(zhí)行App_Check(),否則就直接執(zhí)行App_Check()。 App_Check函數(shù)是來判斷程序A和程序B的,最開始BootLoader是用swd方式下載的,下載的時(shí)候全片擦除,所以會(huì)執(zhí)行主循環(huán)的Update_Check函數(shù)。此時(shí)串口打印出“等待接收APP1的BIN”,這個(gè)時(shí)候發(fā)送APP1的BIN過去,等接受完了,會(huì)寫在FLAG區(qū)域?qū)憘€(gè)0xAAAA,代表程序A寫入了,下次啟動(dòng)可以執(zhí)行程序A。 主要代碼部分#include"fy_includes.h" /* 晶振使用的是16M其他頻率在system_stm32f10x.c中修改 使用printf需要在fy_includes.h修改串口重定向?yàn)?definePRINTF_USARTUSART1 */ /* Bootloader程序 完成三個(gè)任務(wù) 步驟1.檢查是否有程序更新,如果有就擦寫flash進(jìn)行更新,如果沒有進(jìn)入步驟2 步驟2.判斷app1有沒有可執(zhí)行程序,如果有就執(zhí)行,如果沒有進(jìn)入步驟3 步驟3.串口等待接收程序固件 */ #defineFLAG_UPDATE_APP10xBBAA #defineFLAG_UPDATE_APP20xAABB #defineFLAG_APP10xAAAA #defineFLAG_APP20xBBBB #defineFLAG_NONE0xFFFF _loopList_slist1; u8rxbuf[1024]; u8temp8[2]; u16temp16; u32rxlen=0; u32applen=0; u32write_addr; u8overflow=0; u32now_tick=0; u8_cnt_10ms=0; staticvoidApp_Check(void) { //獲取程序標(biāo)號(hào) STMFLASH_Read(FLASH_PARAM_ADDR,&temp16,1); if(temp16==FLAG_APP1)//執(zhí)行程序A { if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//可執(zhí)行? { printf("執(zhí)行程序A... "); IAP_RunApp(FLASH_APP1_ADDR); } else { printf("程序A不可執(zhí)行,擦除APP1程序所在空間... "); for(u8i=10;i<35;i++) { STMFLASH_Erase(FLASH_BASE+i*STM_SECTOR_SIZE,512); } printf("程序A所在空間擦除完成... "); printf("將執(zhí)行程序B... "); if(((*(vu32*)(FLASH_APP2_ADDR+4))&0xFF000000)==0x08000000)//可執(zhí)行? { printf("執(zhí)行程序B... "); IAP_RunApp(FLASH_APP2_ADDR); } else { printf("程序B不可執(zhí)行,擦除APP2程序所在空間... "); for(u8i=35;i<60;i++) { STMFLASH_Erase(FLASH_BASE+i*STM_SECTOR_SIZE,512); } printf("程序B所在空間擦除完成... "); } } } if(temp16==FLAG_APP2)//執(zhí)行程序B { if(((*(vu32*)(FLASH_APP2_ADDR+4))&0xFF000000)==0x08000000)//可執(zhí)行? { printf("執(zhí)行程序B... "); IAP_RunApp(FLASH_APP2_ADDR); } else { printf("程序B不可執(zhí)行,擦除APP2程序所在空間... "); for(u8i=35;i<60;i++) { STMFLASH_Erase(FLASH_BASE+i*STM_SECTOR_SIZE,512); } printf("程序B所在空間擦除完成... "); printf("將執(zhí)行程序A... "); if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//可執(zhí)行? { printf("執(zhí)行程序A... "); IAP_RunApp(FLASH_APP1_ADDR); } else { printf("程序A不可執(zhí)行,擦除APP1程序所在空間... "); for(u8i=10;i<35;i++) { STMFLASH_Erase(FLASH_BASE+i*STM_SECTOR_SIZE,512); } printf("程序A所在空間擦除完成... "); } } } if(temp16==FLAG_NONE) { printf("擦除App1程序所在空間... "); for(u8i=10;i<35;i++) { STMFLASH_Erase(FLASH_BASE+i*STM_SECTOR_SIZE,512); } printf("程序A所在空間擦除完成... "); } } staticvoidUpdate_Check(void) { if(_list.Get_CanRead(&list1)>1) { _list.Read(&list1,&temp8,2);//讀取兩個(gè)數(shù)據(jù) temp16=(u16)(temp8[1]<<8)|temp8[0]; STMFLASH_Write(write_addr,&temp16,1); write_addr+=2; } if(GetSystick_ms()-now_tick>10)//10ms { now_tick=GetSystick_ms(); _cnt_10ms++; if(applen==rxlen&&rxlen)//接收完成 { if(overflow) { printf("接收溢出,無法更新,請(qǐng)重試 "); SoftReset();//軟件復(fù)位 } else { printf(" 接收BIN文件完成,長(zhǎng)度為%d ",applen); temp16=FLAG_APP1; STMFLASH_Write(FLASH_PARAM_ADDR,&temp16,1);//寫入標(biāo)記 temp16=(u16)(applen>>16); STMFLASH_Write(FLASH_PARAM_ADDR+2,&temp16,1); temp16=(u16)(applen); STMFLASH_Write(FLASH_PARAM_ADDR+4,&temp16,1); SoftReset();//軟件復(fù)位 } }elseapplen=rxlen;//更新長(zhǎng)度 } if(_cnt_10ms>=50) { _cnt_10ms=0; Led_Tog(); if(!rxlen) { printf("等待接收App1的BIN文件 "); } } } intmain(void) { NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//開啟AFIO時(shí)鐘 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁止JTAG保留SWD Systick_Configuration(); Led_Configuration(); Key_Configuration(); Usart1_Configuration(9600); USART_ITConfig(USART1,USART_IT_IDLE,DISABLE);//關(guān)閉串口空閑中斷 printf("thisisbootloader! "); if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==SET) { Delay_ms(100); if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==SET)//開機(jī)按下keyup進(jìn)行更新 { printf("主動(dòng)更新,"); temp16=FLAG_NONE; STMFLASH_Write(FLASH_PARAM_ADDR,&temp16,1); } else { } } App_Check(); printf("執(zhí)行BootLoader程序... "); _list.Create(&list1,rxbuf,sizeof(rxbuf)); write_addr=FLASH_APP1_ADDR; while(1) { Update_Check(); } } //USART1串口中斷函數(shù) voidUSART1_IRQHandler(void) { if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) { u8temp=USART1->DR; if(_list.Write(&list1,&temp,1)<=0) { overflow=1; } rxlen++; } }
其中的宏://FLASH起始地址 #defineSTM32_FLASH_BASE0x08000000//STM32FLASH的起始地址 #defineFLASH_APP1_ADDRSTM32_FLASH_BASE+0x2800//偏移10K #defineFLASH_APP2_ADDRSTM32_FLASH_BASE+0x8c00//偏移35K #defineFLASH_PARAM_ADDRSTM32_FLASH_BASE+0xF000//偏移60K
3、程序A和程序B部分
這兩個(gè)都是用戶程序,這兩個(gè)程序都帶有更新程序功能,我這里用作測(cè)試的A和B程序大體都差不多,不同的地方就是程序A接收的BIN用來更新程序B,程序B接收的BIN用來更新A,還有就是中斷向量表便宜不同以及打印輸出不同。 應(yīng)用程序部分沒什么說的,程序A和B很類似,這里貼上A的代碼#include"fy_includes.h" /* 晶振使用的是16M其他頻率在system_stm32f10x.c中修改 使用printf需要在fy_includes.h修改串口重定向?yàn)?definePRINTF_USARTUSART1 */ /* APP1程序 完成兩個(gè)任務(wù) 1.執(zhí)行本身的app任務(wù),同時(shí)監(jiān)聽程序更新,監(jiān)聽到停止本身的任務(wù)進(jìn)入到狀態(tài)2 2.等待接收完成,完成后復(fù)位重啟 */ #defineFLAG_UPDATE_APP10xBBAA #defineFLAG_UPDATE_APP20xAABB #defineFLAG_APP10xAAAA #defineFLAG_APP20xBBBB #defineFLAG_NONE0xFFFF _loopList_slist1; u8rxbuf[1024]; u8temp8[2]; u16temp16; u32rxlen=0; u32applen=0; u32write_flsh_addr; u8update=0; u8overflow=0; u32now_tick; u8_cnt_10ms=0; staticvoidUpdate_Check(void) { if(update)//監(jiān)聽到有更新程序 { write_flsh_addr=FLASH_APP2_ADDR;//App1更新App2的程序 overflow=0; rxlen=0; _list.Create(&list1,rxbuf,sizeof(rxbuf)); printf("擦除APP2程序所在空間... "); for(u8i=35;i<60;i++)//擦除APP2所在空間程序 { STMFLASH_Erase(FLASH_BASE+i*STM_SECTOR_SIZE,512); } printf("程序B所在空間擦除完成... "); while(1) { if(_list.Get_CanRead(&list1)>1) { _list.Read(&list1,&temp8,2);//讀取兩個(gè)數(shù)據(jù) temp16=(u16)(temp8[1]<<8)|temp8[0]; STMFLASH_Write(write_flsh_addr,&temp16,1); write_flsh_addr+=2; } if(GetSystick_ms()-now_tick>10)//10ms { now_tick=GetSystick_ms(); _cnt_10ms++; if(applen==rxlen&&rxlen)//接收完成 { if(overflow) { printf(" 接收溢出,請(qǐng)重新嘗試 "); SoftReset();//軟件復(fù)位 } printf(" 接收BIN文件完成,長(zhǎng)度為%d ",applen); temp16=FLAG_APP2; STMFLASH_Write(FLASH_PARAM_ADDR,&temp16,1);//寫入標(biāo)記 temp16=(u16)(applen>>16); STMFLASH_Write(FLASH_PARAM_ADDR+2,&temp16,1); temp16=(u16)(applen); STMFLASH_Write(FLASH_PARAM_ADDR+4,&temp16,1); printf("系統(tǒng)將重啟.... "); SoftReset();//軟件復(fù)位 }elseapplen=rxlen;//更新長(zhǎng)度 } if(_cnt_10ms>=50) { _cnt_10ms=0; Led_Tog(); if(!rxlen) { printf("等待接收App2的BIN文件 "); } } }//while(1) } } staticvoidApp_Task(void) { if(GetSystick_ms()-now_tick>500) { now_tick=GetSystick_ms(); printf("正在運(yùn)行APP1 "); Led_Tog(); } } intmain(void) { SCB->VTOR=FLASH_APP1_ADDR; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//開啟AFIO時(shí)鐘 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁止JTAG保留SWD Systick_Configuration(); Led_Configuration(); Usart1_Configuration(9600); printf("thisisAPP1! "); Delay_ms(500); while(1) { Update_Check(); App_Task(); } } //USART1串口中斷函數(shù) voidUSART1_IRQHandler(void) { if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) { u8temp=USART1->DR; if(update) { if(_list.Write(&list1,&temp,1)<=?0) { overflow=1; } } else { rxbuf[rxlen]=temp; } rxlen++; } if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET) { u8temp=USART1->DR; temp=USART1->SR; if(strstr((char*)rxbuf,"AppUpdate")&&rxlen) { update=1; USART_ITConfig(USART1,USART_IT_IDLE,DISABLE);//關(guān)閉串口空閑中斷 } else { Usart1_SendBuf(rxbuf,rxlen); } rxlen=0; } }
這里如果要移植需要注意的就是向量表的偏移以及更新擦寫的區(qū)域。4、剩余的4Kflash空間部分
這里其實(shí)只是用來存儲(chǔ)2個(gè)變量,一個(gè)是程序運(yùn)行標(biāo)記,一個(gè)是接收到的程序長(zhǎng)度,程序標(biāo)記還有點(diǎn)把子用,程序長(zhǎng)度其實(shí)要不要都無所謂。5、遇到的坑
最值得一說的就是更新部分,最開始程序沒有加入擦除flash,遇到的情況就是下載完BootLoader后發(fā)送app1沒問題,在app1中更新App2也沒問題,然后app2再更新app1就出問題了。直觀的結(jié)果就是循環(huán)隊(duì)列溢出,原因就是app2在更新app1前沒有去擦除app1所在的flash,所以在寫的時(shí)候就要去擦除,這樣就寫的很慢,然而串口接收是不停的收,所以就是寫不過來。-
FlaSh
+關(guān)注
關(guān)注
10文章
1656瀏覽量
150597 -
調(diào)試
+關(guān)注
關(guān)注
7文章
602瀏覽量
34406 -
串口
+關(guān)注
關(guān)注
14文章
1580瀏覽量
78327 -
IAP
+關(guān)注
關(guān)注
2文章
164瀏覽量
24738 -
bootloader
+關(guān)注
關(guān)注
2文章
238瀏覽量
46327
原文標(biāo)題:基于串口環(huán)形隊(duì)列的IAP實(shí)現(xiàn)!
文章出處:【微信號(hào):技術(shù)讓夢(mèng)想更偉大,微信公眾號(hào):技術(shù)讓夢(mèng)想更偉大】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
STM32串口環(huán)形緩沖區(qū)的實(shí)現(xiàn)
請(qǐng)問串口接受用環(huán)形隊(duì)列,發(fā)送也能用嗎?
環(huán)形隊(duì)列在串口數(shù)據(jù)接收中的使用
如何使用隊(duì)列實(shí)現(xiàn)STM32串口環(huán)形緩沖?
實(shí)現(xiàn)隊(duì)列環(huán)形緩沖的方法
環(huán)形隊(duì)列的操作如何去實(shí)現(xiàn)呢
深度解析數(shù)據(jù)結(jié)構(gòu)與算法篇之隊(duì)列及環(huán)形隊(duì)列的實(shí)現(xiàn)
STM32串口環(huán)形緩沖--使用隊(duì)列實(shí)現(xiàn)(開放源碼)

基于STM32的串口環(huán)形隊(duì)列IAP調(diào)試心得

評(píng)論