STM32通过USB实现Bootlader/IAP功能
生活随笔
收集整理的這篇文章主要介紹了
STM32通过USB实现Bootlader/IAP功能
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
前沿:
最近在做STM32的USB Bootlader/IAP功能,也就是通過USB實現(xiàn)固件升級,本文介紹下實現(xiàn)的基本思路,希望對實現(xiàn)IAP的同學一個參考,改方法已經(jīng)在產(chǎn)品中得到實際應用并驗證是比較合理,穩(wěn)定可靠的。
程序空間劃分:
在單片機的程序Flash中分兩個區(qū),分別存儲Bootloader代碼和App代碼,Bootloader放到代碼起始地址,也就是0x08000000,App放到0x8020000地址,中間預留了很多的地址空間,主要是為了用來存儲一些需要掉電保存的數(shù)據(jù),比如我在0x0800C000地址就存放了App程序運行后寫入該地址的標志數(shù)據(jù)。
啟動流程:
上電后自然是運行Bootloader程序,Bootloader運行后,做的第一件事情如下所示
[C]?純文本查看?復制代碼 ?
也就是判斷App運行標志是否有效,這個標志是存放到EXE_FLAG_ADDR地址的,若有效就直接跳轉(zhuǎn)到App程序運行,這個時間很短,所以用戶看不到有Bootloader執(zhí)行的效果,感覺就是直接運行的App程序,進入App程序后,App程序第一件事情如下
[C]?純文本查看?復制代碼 ?
也就是判斷App標志是否有效,若有效則直接執(zhí)行后面的程序,若無效則需要在EXE_FLAG_ADDR地址寫入執(zhí)行標志。
Bootloader程序判斷App標志若無效,那么Bootloader就不會直接跳轉(zhuǎn)到App,因為這個時候是需要進行升級App的操作,所以程序就進入Bootloader的正常工作流程,也就是等待升級App的各種命令,比如擦出固件,燒寫固件,校驗固件等。當固件成功寫入并校驗通過之后,PC端就可以發(fā)送一個程序跳轉(zhuǎn)命令跳轉(zhuǎn)到App執(zhí)行。
PC端操作流程:
PC端和單片機是通過USB進行數(shù)據(jù)交換的,當然用其他方式也可以,基本流程都是差不多的。
PC程序首先當然是掃描設備,打開設備,然后調(diào)用獲取固件信息的函數(shù),調(diào)用該函數(shù)后可以得知當前固件的名稱,版本號,固件類型(Bootloader還是App),若發(fā)現(xiàn)當前固件不是Bootloader,那么就得通過USB給固件發(fā)送一個程序跳轉(zhuǎn)命令,也就是跳轉(zhuǎn)到Bootloader代碼執(zhí)行,當然App在跳轉(zhuǎn)到Bootloader的時候必須把EXE_FLAG_ADDR地址的標志數(shù)據(jù)擦出掉,這樣Bootloader才能進入正常的升級流程。
控制固件程序進入Bootloader之后,PC端程序?qū)⒋蜷_App固件程序文件,然后根據(jù)文件大小,發(fā)送擦出App代碼存儲區(qū)域Flash的數(shù)據(jù),然后再分包將固件發(fā)送給單片機,單片機端Bootlader程序接收到數(shù)據(jù)后將數(shù)據(jù)寫入App的Flash區(qū)域,數(shù)據(jù)寫完之后再進行校驗,我是通過計算CRC16的方式進行校驗的,校驗通過之后就可以發(fā)送跳轉(zhuǎn)命令控制程序跳轉(zhuǎn)到App運行了,到此升級流程完畢。
PC端程序代碼:
[C]?純文本查看?復制代碼 ?
PC端程序運行效果如下所示:
?
后記:
我是用STM32F4+USB3300高速USB實現(xiàn)IAP功能的,12K的App代碼幾乎瞬間下載完畢,整個程序測試了很多次,沒一次出問題...
最近在做STM32的USB Bootlader/IAP功能,也就是通過USB實現(xiàn)固件升級,本文介紹下實現(xiàn)的基本思路,希望對實現(xiàn)IAP的同學一個參考,改方法已經(jīng)在產(chǎn)品中得到實際應用并驗證是比較合理,穩(wěn)定可靠的。
程序空間劃分:
在單片機的程序Flash中分兩個區(qū),分別存儲Bootloader代碼和App代碼,Bootloader放到代碼起始地址,也就是0x08000000,App放到0x8020000地址,中間預留了很多的地址空間,主要是為了用來存儲一些需要掉電保存的數(shù)據(jù),比如我在0x0800C000地址就存放了App程序運行后寫入該地址的標志數(shù)據(jù)。
啟動流程:
上電后自然是運行Bootloader程序,Bootloader運行后,做的第一件事情如下所示
[C]?純文本查看?復制代碼 ?
| 01 02 03 | if((*((uint32_t *)EXE_FLAG_ADDR))==0x12345678){ ??JumpToApplication(APP_START_ADDR); } |
也就是判斷App運行標志是否有效,這個標志是存放到EXE_FLAG_ADDR地址的,若有效就直接跳轉(zhuǎn)到App程序運行,這個時間很短,所以用戶看不到有Bootloader執(zhí)行的效果,感覺就是直接運行的App程序,進入App程序后,App程序第一件事情如下
[C]?純文本查看?復制代碼 ?
| 01 02 03 04 05 06 07 | if((*((uint32_t *)EXE_FLAG_ADDR))==0xFFFFFFFF){ ??uint32_t ExeFlag = 0x12345678; ??__set_PRIMASK(1);//禁止全局中斷 ??FLASH_Unlock(); ??ProgramDatatoFlash(EXE_FLAG_ADDR,(uint8_t*)(&ExeFlag),4); ??FLASH_Lock(); } |
也就是判斷App標志是否有效,若有效則直接執(zhí)行后面的程序,若無效則需要在EXE_FLAG_ADDR地址寫入執(zhí)行標志。
Bootloader程序判斷App標志若無效,那么Bootloader就不會直接跳轉(zhuǎn)到App,因為這個時候是需要進行升級App的操作,所以程序就進入Bootloader的正常工作流程,也就是等待升級App的各種命令,比如擦出固件,燒寫固件,校驗固件等。當固件成功寫入并校驗通過之后,PC端就可以發(fā)送一個程序跳轉(zhuǎn)命令跳轉(zhuǎn)到App執(zhí)行。
PC端操作流程:
PC端和單片機是通過USB進行數(shù)據(jù)交換的,當然用其他方式也可以,基本流程都是差不多的。
PC程序首先當然是掃描設備,打開設備,然后調(diào)用獲取固件信息的函數(shù),調(diào)用該函數(shù)后可以得知當前固件的名稱,版本號,固件類型(Bootloader還是App),若發(fā)現(xiàn)當前固件不是Bootloader,那么就得通過USB給固件發(fā)送一個程序跳轉(zhuǎn)命令,也就是跳轉(zhuǎn)到Bootloader代碼執(zhí)行,當然App在跳轉(zhuǎn)到Bootloader的時候必須把EXE_FLAG_ADDR地址的標志數(shù)據(jù)擦出掉,這樣Bootloader才能進入正常的升級流程。
控制固件程序進入Bootloader之后,PC端程序?qū)⒋蜷_App固件程序文件,然后根據(jù)文件大小,發(fā)送擦出App代碼存儲區(qū)域Flash的數(shù)據(jù),然后再分包將固件發(fā)送給單片機,單片機端Bootlader程序接收到數(shù)據(jù)后將數(shù)據(jù)寫入App的Flash區(qū)域,數(shù)據(jù)寫完之后再進行校驗,我是通過計算CRC16的方式進行校驗的,校驗通過之后就可以發(fā)送跳轉(zhuǎn)命令控制程序跳轉(zhuǎn)到App運行了,到此升級流程完畢。
PC端程序代碼:
[C]?純文本查看?復制代碼 ?
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | // USB2XXXTest.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <stdlib.h> #include "../../USB2XXX/source/bootloader.h" int _tmain(int argc, _TCHAR* argv[]) { ????????int PackSize = 1024*10; ????????int TimeOut = 0; ????????int ApplicationAddress = 0x08020000; ????????int BootAddress = 0x08000000; ????????FW_INFO FwInfo; ????????bool state; ????????int ret; ????????//掃描查找設備 ????????ret = BT_ScanDevice(true); ????????if(ret <= 0){ ????????????????printf("No device connected!\n"); ????????????????return 0; ????????} ????????//打開設備 ????????state = BT_OpenDevice(0); ????????if(!state){ ????????????????printf("Open device error!\n"); ????????????????return 0; ????????} ????????//獲取固件信息 ????????BT_GetFirmwareInfo(0,&FwInfo); ????????printf("Firmware Name:%s\n",FwInfo.FirmwareName); ????????printf("Firmware Functions:%08X\n",FwInfo.Functions); ????????//判斷當前固件是否為Bootloader固件 ????????while(!(FwInfo.Functions&FUNCTION_BOOTLOADER)){ ????????????????//控制程序跳轉(zhuǎn)到Bootloader ????????????????state = BT_ExcuteFirmware(0,BootAddress); ????????????????printf("BT_ExcuteFirmware state = %d\n",state); ????????????????BT_CloseDevice(0); ????????? ????????????????do{ ????????????????????????Sleep(100); ????????????????????????ret = BT_ScanDevice(true);//掃描查找設備 ????????????????????????if((ret <= 0)&&(TimeOut > 50)){ ????????????????????????????????printf("No device connected!\n"); ????????????????????????????????return 0; ????????????????????????}else if(ret > 0){ ????????????????????????????????break; ????????????????????????} ????????????????????????TimeOut++; ????????????????}while(ret<=0); ????????????????TimeOut = 0; ????????????????do{ ????????????????????????Sleep(100); ????????????????????????state = BT_OpenDevice(0);//打開設備 ????????????????????????if((!state)&&(TimeOut > 50)){ ????????????????????????????????printf("Open device error!\n"); ????????????????????????????????return 0; ????????????????????????}else if(state){ ????????????????????????????????break; ????????????????????????} ????????????????????????TimeOut++; ????????????????}while(!state); ????????????????//獲取固件信息 ????????????????BT_GetFirmwareInfo(0,&FwInfo); ????????????????printf("Firmware Name:%s\n",FwInfo.FirmwareName); ????????????????printf("Firmware Functions:%08X\n",FwInfo.Functions); ????????} ????????//打開固件文件 ????????FILE *pFile=fopen("Project.bin","rb"); //獲取文件的指針 ????????fseek(pFile,0,SEEK_END); //把指針移動到文件的結(jié)尾 ,獲取文件長度 ????????int FileLen=ftell(pFile); //獲取文件長度 ????????static char *pBuf = (char *)malloc(FileLen);? //定義文件指針 ????????if(pBuf == NULL){ ????????????????printf("malloc error\n"); ????????????????return 0; ????????} ????????rewind(pFile); //把指針移動到文件開頭 因為我們一開始把指針移動到結(jié)尾,如果不移動回來 會出錯 ????????fread(pBuf,1,FileLen,pFile); //讀文件 ????????fclose(pFile); // 關閉文件 ????????//擦除之前的固件 ????????state = BT_EraseSectors(0,ApplicationAddress,ApplicationAddress+FileLen); ????????if(!state){ ????????????????printf("BT_EraseSectors error!\n"); ????????????????return 0; ????????}else{ ????????????????printf("BT_EraseSectors success\n"); ????????} ????????//循環(huán)寫入固件數(shù)據(jù)到芯片F(xiàn)lash ????????int PackIndex = 0; ????????for(PackIndex=0;PackIndex<FileLen/PackSize;PackIndex++){ ????????????????state = BT_WriteData(0,ApplicationAddress+PackIndex*PackSize,(unsigned char *)(&pBuf[PackIndex*PackSize]),PackSize,0); ????????????????if(!state){ ????????????????????????printf("BT_WriteData Error\n"); ????????????????????????return 0; ????????????????} ????????} ????????if(FileLen%PackSize){ ????????????????state = BT_WriteData(0,ApplicationAddress+PackIndex*PackSize,(unsigned char *)(&pBuf[PackIndex*PackSize]),FileLen%PackSize,0); ????????????????if(!state){ ????????????????????????printf("BT_WriteData Error\n"); ????????????????????????return 0; ????????????????} ????????} ????????printf("BT_WriteData Success\n"); ????????//循環(huán)校驗數(shù)據(jù)是否寫成功 ????????for(PackIndex=0;PackIndex<FileLen/PackSize;PackIndex++){ ????????????????state = BT_VerifyData(0,ApplicationAddress+PackIndex*PackSize,(unsigned char *)(&pBuf[PackIndex*PackSize]),PackSize); ????????????????if(!state){ ????????????????????????printf("BT_VerifyData Error\n"); ????????????????????????return 0; ????????????????} ????????} ????????if(FileLen%PackSize){ ????????????????state = BT_VerifyData(0,ApplicationAddress+PackIndex*PackSize,(unsigned char *)(&pBuf[PackIndex*PackSize]),FileLen%PackSize); ????????????????if(!state){ ????????????????????????printf("BT_VerifyData Error\n"); ????????????????????????return 0; ????????????????} ????????} ????????printf("BT_VerifyData Success\n"); ????????//執(zhí)行固件 ????????state = BT_ExcuteFirmware(0,ApplicationAddress); ????????printf("BT_ExcuteFirmware state = %d\n",state); ????????//關閉設備 ????????BT_CloseDevice(0); ????????return 0; } |
PC端程序運行效果如下所示:
?
后記:
我是用STM32F4+USB3300高速USB實現(xiàn)IAP功能的,12K的App代碼幾乎瞬間下載完畢,整個程序測試了很多次,沒一次出問題...
總結(jié)
以上是生活随笔為你收集整理的STM32通过USB实现Bootlader/IAP功能的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 临界区问题的产生
- 下一篇: ucos中的三种临界区管理机制