我這里主要是記錄一下我所使用的方法,調(diào)試也花了兩天時間。 我所用的型號是STM32F103C8T6,這個IC有64KFlash和20K的RAM,也有小道說有后置隱藏的64K,也就是說其實是有128K,我一直也沒有測試,有空測測,有大神這樣說,估計是可以的。 這里重點記錄一下我寫的IAP思路和代碼以及細節(jié)和遇到坑的地方。先大體的概述一下,最后貼上我認為重點的代碼。 在概述之前先要解決一個問題,那就是sram空間和flash空間的問題,sram只有20K,flash有64k。解決的辦法有很多: 1)最常見的就是自己寫上位機軟件,通過分包發(fā)送,期間還可以加入加密算法,校驗等等。 2)使用環(huán)形隊列,簡單點說就是個環(huán)形數(shù)組,一邊接收上位機數(shù)據(jù),一邊往flash里面寫。 這里條件限制就采用第二種方法。所以即使是分給A和B的25K空間的flash空間,sram只有20K也是不能一次接收完所有的bin數(shù)據(jù)的,這里我只開辟了一個1K的BUF,使用尾插法寫入,我的測試應(yīng)用程序都在5-6K,用這樣的方法可以在9600波特率下測試穩(wěn)定,也試過57600的勉強可以的,115200就不行了。環(huán)形隊列代碼如下: 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; } //刪除一個環(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;//長度為空 } //獲取鏈表的長度 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--讀的個數(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));//比較能讀的個數(shù) memcpy(buf,p->head,copySz);//讀出數(shù)據(jù) p->head+=copySz;//頭指針加上讀取的個數(shù) returncopySz;//返回讀取的個數(shù) } else//頭大于等于了尾 { if(lenhead-p->buf))//讀的個數(shù)小于頭上面的數(shù)據(jù)量 { copySz=len;//讀出的個數(shù) memcpy(buf,p->head,copySz); p->head+=copySz; returncopySz; } else//讀的個數(shù)大于頭上面的數(shù)據(jù)量 { copySz=Get_Capacity(p)-(p->head-p->buf);//先讀出來頭上面的數(shù)據(jù) memcpy(buf,p->head,copySz); p->head=p->buf;//頭指針指向數(shù)組的首地址 //還要讀的個數(shù) copySz+=Read(p,(char*)buf+copySz,len-copySz);//接著讀剩余要讀的個數(shù) returncopySz; } } } //p--要寫的環(huán)形鏈表 //buf--寫出的數(shù)據(jù) //len--寫的個數(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)//個數(shù)小于等于尾上面剩余的空間 { memcpy(p->tail,buf,len);//拷貝數(shù)據(jù)到環(huán)形數(shù)組 p->tail+=len;//尾指針加上數(shù)據(jù)個數(shù) if(p->tail==p->buf+Get_Capacity(p))//正好寫到最后 { p->tail=p->buf;//尾指向數(shù)組的首地址 } returnlen;//返回寫入的數(shù)據(jù)個數(shù) } else { memcpy(p->tail,buf,tailAvailSz);//填入尾上面剩余的空間 p->tail=p->buf;//尾指針指向數(shù)組首地址 //剩余空間剩余數(shù)據(jù)的首地址剩余數(shù)據(jù)的個數(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個部分,第一部分是BootLoader,第二部分是程序A(APP1),第三部分是程序B(APP2),第四部分是用來存儲一些變量和標記的。下面是空間的分配情況。BootLoader程序可以用來更新程序A,而程序A又更新程序B,程序B可以更新程序A。 最開始的時候想的是程序A、B都帶更新了干嘛還多此一舉,其實這個Bootloader還是需要的。如果之后程序A、B和FLAG三部分,假設(shè)一種情況,在程序B中更新程序A中遇到問題,復(fù)位后直接成磚,因為程序A在其實地址,上電直接運行程序A,而程序A現(xiàn)在出問題了,那就沒招了。 所以加上BootLoader情況下,不管怎么樣BootLoader的程序是不會錯的,因為更新不會更新BootLoader,計時更新出錯了,還可以進入BootLoader重新更新應(yīng)用程序。我見也有另外一種設(shè)計方法的,就是應(yīng)用程序只有一個程序A,把程序B區(qū)域的flash當作緩存用,重啟的時候判斷B區(qū)域有沒有更新程序,有的話就把B拷貝到A,然后擦除B,我感覺這樣其實也一樣,反正不管怎么樣這部分空間是必須要預(yù)留出來的。![b8815812-d893-11ed-bfe3-dac502259ad0.png](https://file1.elecfans.com//web2/M00/99/BB/wKgZomTniY-AdvAlAAAT4U3nSWo301.png)
2、Bootloader部分
BootLoader的任務(wù)有兩個,一是在串口中斷接收BIN的數(shù)據(jù)和主循環(huán)內(nèi)判斷以及更新APP1的程序,二是在在程序開始的時候判斷有沒有可用的用戶程序進而跳轉(zhuǎn)到用戶程序(程序A或者程序B)。 簡單介紹下執(zhí)行流程: 系統(tǒng)上電首先肯定是執(zhí)行BootLoader程序的,因為它的起始地址就是0x08000000,首先是初始化,然后判斷按鍵是否手動升級程序,按鍵按下了就把FLAG部分的APP標記寫成0xFFFF(這里用的宏定義方式),再執(zhí)行執(zhí)行App_Check(),否則就直接執(zhí)行App_Check()。 App_Check函數(shù)是來判斷程序A和程序B的,最開始BootLoader是用swd方式下載的,下載的時候全片擦除,所以會執(zhí)行主循環(huán)的Update_Check函數(shù)。此時串口打印出“等待接收APP1的BIN”,這個時候發(fā)送APP1的BIN過去,等接受完了,會寫在FLAG區(qū)域?qū)憘€0xAAAA,代表程序A寫入了,下次啟動可以執(zhí)行程序A。 主要代碼部分#include"fy_includes.h" /* 晶振使用的是16M其他頻率在system_stm32f10x.c中修改 使用printf需要在fy_includes.h修改串口重定向為#definePRINTF_USARTUSART1 */ /* Bootloader程序 完成三個任務(wù) 步驟1.檢查是否有程序更新,如果有就擦寫flash進行更新,如果沒有進入步驟2 步驟2.判斷app1有沒有可執(zhí)行程序,如果有就執(zhí)行,如果沒有進入步驟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) { //獲取程序標號 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);//讀取兩個數(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("接收溢出,無法更新,請重試 "); SoftReset();//軟件復(fù)位 } else { printf(" 接收BIN文件完成,長度為%d ",applen); temp16=FLAG_APP1; STMFLASH_Write(FLASH_PARAM_ADDR,&temp16,1);//寫入標記 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;//更新長度 } 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時鐘 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)//開機按下keyup進行更新 { printf("主動更新,"); 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部分
這兩個都是用戶程序,這兩個程序都帶有更新程序功能,我這里用作測試的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修改串口重定向為#definePRINTF_USARTUSART1 */ /* APP1程序 完成兩個任務(wù) 1.執(zhí)行本身的app任務(wù),同時監(jiān)聽程序更新,監(jiān)聽到停止本身的任務(wù)進入到狀態(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);//讀取兩個數(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(" 接收溢出,請重新嘗試 "); SoftReset();//軟件復(fù)位 } printf(" 接收BIN文件完成,長度為%d ",applen); temp16=FLAG_APP2; STMFLASH_Write(FLASH_PARAM_ADDR,&temp16,1);//寫入標記 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;//更新長度 } 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("正在運行APP1 "); Led_Tog(); } } intmain(void) { SCB->VTOR=FLASH_APP1_ADDR; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//開啟AFIO時鐘 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空間部分
這里其實只是用來存儲2個變量,一個是程序運行標記,一個是接收到的程序長度,程序標記還有點把子用,程序長度其實要不要都無所謂。5、遇到的坑
最值得一說的就是更新部分,最開始程序沒有加入擦除flash,遇到的情況就是下載完BootLoader后發(fā)送app1沒問題,在app1中更新App2也沒問題,然后app2再更新app1就出問題了。直觀的結(jié)果就是循環(huán)隊列溢出,原因就是app2在更新app1前沒有去擦除app1所在的flash,所以在寫的時候就要去擦除,這樣就寫的很慢,然而串口接收是不停的收,所以就是寫不過來。
審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。
舉報投訴
-
FlaSh
+關(guān)注
關(guān)注
10文章
1643瀏覽量
148697 -
調(diào)試
+關(guān)注
關(guān)注
7文章
589瀏覽量
34066 -
串口
+關(guān)注
關(guān)注
14文章
1559瀏覽量
77084 -
IAP
+關(guān)注
關(guān)注
2文章
164瀏覽量
24393 -
bootloader
+關(guān)注
關(guān)注
2文章
235瀏覽量
45742
原文標題:基于串口環(huán)形隊列的IAP實現(xiàn)!
文章出處:【微信號:技術(shù)讓夢想更偉大,微信公眾號:技術(shù)讓夢想更偉大】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
STM32進階之串口環(huán)形緩沖區(qū)實現(xiàn)
實現(xiàn)吧:從隊列到串口緩沖區(qū)的實現(xiàn)串口環(huán)形緩沖區(qū)收發(fā):在很多入門級教程中,我們知道的
發(fā)表于 06-08 14:03
STM32串口環(huán)形緩沖區(qū)的實現(xiàn)
試試用代碼實現(xiàn)吧!從隊列到串口緩沖區(qū)的實現(xiàn): 串口環(huán)形緩沖區(qū)收發(fā):在很多入門級教程中,我們知道的
發(fā)表于 10-16 11:40
環(huán)形隊列在串口數(shù)據(jù)接收中的使用
前言??書接上回,前文主要介紹了環(huán)形隊列的實現(xiàn)原理以及C語言實現(xiàn)及測試過程,本文將回歸到嵌入式平臺的應(yīng)用中,話不多說,淦,上干貨!實驗?zāi)康腍AL庫下
發(fā)表于 12-06 06:27
如何使用隊列實現(xiàn)STM32串口環(huán)形緩沖?
串口環(huán)形緩沖的好處是什么?如何使用隊列實現(xiàn)STM32串口環(huán)形緩沖?
發(fā)表于 12-07 07:13
環(huán)形隊列的操作如何去實現(xiàn)呢
環(huán)形隊列結(jié)構(gòu)的定義是什么?環(huán)形隊列的操作如何去實現(xiàn)呢?
發(fā)表于 02-25 06:35
STM32串口環(huán)形緩沖--使用隊列實現(xiàn)(開放源碼)
串口隊列環(huán)形緩沖區(qū)隊列串口環(huán)形緩沖的好處代碼實現(xiàn)
發(fā)表于 12-24 19:04
?28次下載
![STM32<b class='flag-5'>串口</b><b class='flag-5'>環(huán)形</b>緩沖--使用<b class='flag-5'>隊列</b><b class='flag-5'>實現(xiàn)</b>(開放源碼)](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
基于STM32的串口環(huán)形隊列IAP調(diào)試心得
使用環(huán)形隊列,簡單點說就是個環(huán)形數(shù)組,一邊接收上位機數(shù)據(jù),一邊往flash里面寫。
發(fā)表于 02-08 15:22
?5次下載
![基于STM32的<b class='flag-5'>串口</b><b class='flag-5'>環(huán)形</b><b class='flag-5'>隊列</b><b class='flag-5'>IAP</b>調(diào)試心得](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
STM32進階之串口環(huán)形緩沖區(qū)實現(xiàn)
碼代碼的應(yīng)該學數(shù)據(jù)結(jié)構(gòu)都學過隊列。環(huán)形隊列是隊列的一種特殊形式,應(yīng)用挺廣泛的。因為有太多文章關(guān)于這方面的內(nèi)容,理論知識可以看別人的,下面寫得挺好的:STM32進階之
發(fā)表于 12-06 10:00
?3110次閱讀
嵌入式環(huán)形隊列和消息隊列的實現(xiàn)
嵌入式環(huán)形隊列和消息隊列是實現(xiàn)數(shù)據(jù)緩存和通信的常見數(shù)據(jù)結(jié)構(gòu),廣泛應(yīng)用于嵌入式系統(tǒng)中的通信協(xié)議和領(lǐng)域。
嵌入式環(huán)形隊列和消息隊列是如何去實現(xiàn)的?
嵌入式環(huán)形隊列和消息隊列是實現(xiàn)數(shù)據(jù)緩存和通信的常見數(shù)據(jù)結(jié)構(gòu),廣泛應(yīng)用于嵌入式系統(tǒng)中的通信協(xié)議和領(lǐng)域。
發(fā)表于 05-20 14:55
?1180次閱讀
嵌入式環(huán)形隊列與消息隊列的實現(xiàn)原理
嵌入式環(huán)形隊列,也稱為環(huán)形緩沖區(qū)或循環(huán)隊列,是一種先進先出(FIFO)的數(shù)據(jù)結(jié)構(gòu),用于在固定大小的存儲區(qū)域中高效地存儲和訪問數(shù)據(jù)。其主要特點包括固定大小的數(shù)組和兩個指針(頭指針和尾指針
評論