STM32通过USB实现Bootlader/IAP功能
生活随笔
收集整理的這篇文章主要介紹了
STM32通过USB实现Bootlader/IAP功能
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
前沿:
最近在做STM32的USB Bootlader/IAP功能,也就是通過(guò)USB實(shí)現(xiàn)固件升級(jí),本文介紹下實(shí)現(xiàn)的基本思路,希望對(duì)實(shí)現(xiàn)IAP的同學(xué)一個(gè)參考,改方法已經(jīng)在產(chǎn)品中得到實(shí)際應(yīng)用并驗(yàn)證是比較合理,穩(wěn)定可靠的。
程序空間劃分:
在單片機(jī)的程序Flash中分兩個(gè)區(qū),分別存儲(chǔ)Bootloader代碼和App代碼,Bootloader放到代碼起始地址,也就是0x08000000,App放到0x8020000地址,中間預(yù)留了很多的地址空間,主要是為了用來(lái)存儲(chǔ)一些需要掉電保存的數(shù)據(jù),比如我在0x0800C000地址就存放了App程序運(yùn)行后寫(xiě)入該地址的標(biāo)志數(shù)據(jù)。
啟動(dòng)流程:
上電后自然是運(yùn)行Bootloader程序,Bootloader運(yùn)行后,做的第一件事情如下所示
[C]?純文本查看?復(fù)制代碼 ?
也就是判斷App運(yùn)行標(biāo)志是否有效,這個(gè)標(biāo)志是存放到EXE_FLAG_ADDR地址的,若有效就直接跳轉(zhuǎn)到App程序運(yùn)行,這個(gè)時(shí)間很短,所以用戶(hù)看不到有Bootloader執(zhí)行的效果,感覺(jué)就是直接運(yùn)行的App程序,進(jìn)入App程序后,App程序第一件事情如下
[C]?純文本查看?復(fù)制代碼 ?
也就是判斷App標(biāo)志是否有效,若有效則直接執(zhí)行后面的程序,若無(wú)效則需要在EXE_FLAG_ADDR地址寫(xiě)入執(zhí)行標(biāo)志。
Bootloader程序判斷App標(biāo)志若無(wú)效,那么Bootloader就不會(huì)直接跳轉(zhuǎn)到App,因?yàn)檫@個(gè)時(shí)候是需要進(jìn)行升級(jí)App的操作,所以程序就進(jìn)入Bootloader的正常工作流程,也就是等待升級(jí)App的各種命令,比如擦出固件,燒寫(xiě)固件,校驗(yàn)固件等。當(dāng)固件成功寫(xiě)入并校驗(yàn)通過(guò)之后,PC端就可以發(fā)送一個(gè)程序跳轉(zhuǎn)命令跳轉(zhuǎn)到App執(zhí)行。
PC端操作流程:
PC端和單片機(jī)是通過(guò)USB進(jìn)行數(shù)據(jù)交換的,當(dāng)然用其他方式也可以,基本流程都是差不多的。
PC程序首先當(dāng)然是掃描設(shè)備,打開(kāi)設(shè)備,然后調(diào)用獲取固件信息的函數(shù),調(diào)用該函數(shù)后可以得知當(dāng)前固件的名稱(chēng),版本號(hào),固件類(lèi)型(Bootloader還是App),若發(fā)現(xiàn)當(dāng)前固件不是Bootloader,那么就得通過(guò)USB給固件發(fā)送一個(gè)程序跳轉(zhuǎn)命令,也就是跳轉(zhuǎn)到Bootloader代碼執(zhí)行,當(dāng)然App在跳轉(zhuǎn)到Bootloader的時(shí)候必須把EXE_FLAG_ADDR地址的標(biāo)志數(shù)據(jù)擦出掉,這樣Bootloader才能進(jìn)入正常的升級(jí)流程。
控制固件程序進(jìn)入Bootloader之后,PC端程序?qū)⒋蜷_(kāi)App固件程序文件,然后根據(jù)文件大小,發(fā)送擦出App代碼存儲(chǔ)區(qū)域Flash的數(shù)據(jù),然后再分包將固件發(fā)送給單片機(jī),單片機(jī)端Bootlader程序接收到數(shù)據(jù)后將數(shù)據(jù)寫(xiě)入App的Flash區(qū)域,數(shù)據(jù)寫(xiě)完之后再進(jìn)行校驗(yàn),我是通過(guò)計(jì)算CRC16的方式進(jìn)行校驗(yàn)的,校驗(yàn)通過(guò)之后就可以發(fā)送跳轉(zhuǎn)命令控制程序跳轉(zhuǎn)到App運(yùn)行了,到此升級(jí)流程完畢。
PC端程序代碼:
[C]?純文本查看?復(fù)制代碼 ?
PC端程序運(yùn)行效果如下所示:
?
后記:
我是用STM32F4+USB3300高速USB實(shí)現(xiàn)IAP功能的,12K的App代碼幾乎瞬間下載完畢,整個(gè)程序測(cè)試了很多次,沒(méi)一次出問(wèn)題...
最近在做STM32的USB Bootlader/IAP功能,也就是通過(guò)USB實(shí)現(xiàn)固件升級(jí),本文介紹下實(shí)現(xiàn)的基本思路,希望對(duì)實(shí)現(xiàn)IAP的同學(xué)一個(gè)參考,改方法已經(jīng)在產(chǎn)品中得到實(shí)際應(yīng)用并驗(yàn)證是比較合理,穩(wěn)定可靠的。
程序空間劃分:
在單片機(jī)的程序Flash中分兩個(gè)區(qū),分別存儲(chǔ)Bootloader代碼和App代碼,Bootloader放到代碼起始地址,也就是0x08000000,App放到0x8020000地址,中間預(yù)留了很多的地址空間,主要是為了用來(lái)存儲(chǔ)一些需要掉電保存的數(shù)據(jù),比如我在0x0800C000地址就存放了App程序運(yùn)行后寫(xiě)入該地址的標(biāo)志數(shù)據(jù)。
啟動(dòng)流程:
上電后自然是運(yùn)行Bootloader程序,Bootloader運(yùn)行后,做的第一件事情如下所示
[C]?純文本查看?復(fù)制代碼 ?
| 01 02 03 | if((*((uint32_t *)EXE_FLAG_ADDR))==0x12345678){ ??JumpToApplication(APP_START_ADDR); } |
也就是判斷App運(yùn)行標(biāo)志是否有效,這個(gè)標(biāo)志是存放到EXE_FLAG_ADDR地址的,若有效就直接跳轉(zhuǎn)到App程序運(yùn)行,這個(gè)時(shí)間很短,所以用戶(hù)看不到有Bootloader執(zhí)行的效果,感覺(jué)就是直接運(yùn)行的App程序,進(jìn)入App程序后,App程序第一件事情如下
[C]?純文本查看?復(fù)制代碼 ?
| 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標(biāo)志是否有效,若有效則直接執(zhí)行后面的程序,若無(wú)效則需要在EXE_FLAG_ADDR地址寫(xiě)入執(zhí)行標(biāo)志。
Bootloader程序判斷App標(biāo)志若無(wú)效,那么Bootloader就不會(huì)直接跳轉(zhuǎn)到App,因?yàn)檫@個(gè)時(shí)候是需要進(jìn)行升級(jí)App的操作,所以程序就進(jìn)入Bootloader的正常工作流程,也就是等待升級(jí)App的各種命令,比如擦出固件,燒寫(xiě)固件,校驗(yàn)固件等。當(dāng)固件成功寫(xiě)入并校驗(yàn)通過(guò)之后,PC端就可以發(fā)送一個(gè)程序跳轉(zhuǎn)命令跳轉(zhuǎn)到App執(zhí)行。
PC端操作流程:
PC端和單片機(jī)是通過(guò)USB進(jìn)行數(shù)據(jù)交換的,當(dāng)然用其他方式也可以,基本流程都是差不多的。
PC程序首先當(dāng)然是掃描設(shè)備,打開(kāi)設(shè)備,然后調(diào)用獲取固件信息的函數(shù),調(diào)用該函數(shù)后可以得知當(dāng)前固件的名稱(chēng),版本號(hào),固件類(lèi)型(Bootloader還是App),若發(fā)現(xiàn)當(dāng)前固件不是Bootloader,那么就得通過(guò)USB給固件發(fā)送一個(gè)程序跳轉(zhuǎn)命令,也就是跳轉(zhuǎn)到Bootloader代碼執(zhí)行,當(dāng)然App在跳轉(zhuǎn)到Bootloader的時(shí)候必須把EXE_FLAG_ADDR地址的標(biāo)志數(shù)據(jù)擦出掉,這樣Bootloader才能進(jìn)入正常的升級(jí)流程。
控制固件程序進(jìn)入Bootloader之后,PC端程序?qū)⒋蜷_(kāi)App固件程序文件,然后根據(jù)文件大小,發(fā)送擦出App代碼存儲(chǔ)區(qū)域Flash的數(shù)據(jù),然后再分包將固件發(fā)送給單片機(jī),單片機(jī)端Bootlader程序接收到數(shù)據(jù)后將數(shù)據(jù)寫(xiě)入App的Flash區(qū)域,數(shù)據(jù)寫(xiě)完之后再進(jìn)行校驗(yàn),我是通過(guò)計(jì)算CRC16的方式進(jìn)行校驗(yàn)的,校驗(yàn)通過(guò)之后就可以發(fā)送跳轉(zhuǎn)命令控制程序跳轉(zhuǎn)到App運(yùn)行了,到此升級(jí)流程完畢。
PC端程序代碼:
[C]?純文本查看?復(fù)制代碼 ?
| 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; ????????//掃描查找設(shè)備 ????????ret = BT_ScanDevice(true); ????????if(ret <= 0){ ????????????????printf("No device connected!\n"); ????????????????return 0; ????????} ????????//打開(kāi)設(shè)備 ????????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); ????????//判斷當(dāng)前固件是否為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);//掃描查找設(shè)備 ????????????????????????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);//打開(kāi)設(shè)備 ????????????????????????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); ????????} ????????//打開(kāi)固件文件 ????????FILE *pFile=fopen("Project.bin","rb"); //獲取文件的指針 ????????fseek(pFile,0,SEEK_END); //把指針移動(dòng)到文件的結(jié)尾 ,獲取文件長(zhǎng)度 ????????int FileLen=ftell(pFile); //獲取文件長(zhǎng)度 ????????static char *pBuf = (char *)malloc(FileLen);? //定義文件指針 ????????if(pBuf == NULL){ ????????????????printf("malloc error\n"); ????????????????return 0; ????????} ????????rewind(pFile); //把指針移動(dòng)到文件開(kāi)頭 因?yàn)槲覀円婚_(kāi)始把指針移動(dòng)到結(jié)尾,如果不移動(dòng)回來(lái) 會(huì)出錯(cuò) ????????fread(pBuf,1,FileLen,pFile); //讀文件 ????????fclose(pFile); // 關(guān)閉文件 ????????//擦除之前的固件 ????????state = BT_EraseSectors(0,ApplicationAddress,ApplicationAddress+FileLen); ????????if(!state){ ????????????????printf("BT_EraseSectors error!\n"); ????????????????return 0; ????????}else{ ????????????????printf("BT_EraseSectors success\n"); ????????} ????????//循環(huán)寫(xiě)入固件數(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)校驗(yàn)數(shù)據(jù)是否寫(xiě)成功 ????????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); ????????//關(guān)閉設(shè)備 ????????BT_CloseDevice(0); ????????return 0; } |
PC端程序運(yùn)行效果如下所示:
?
后記:
我是用STM32F4+USB3300高速USB實(shí)現(xiàn)IAP功能的,12K的App代碼幾乎瞬間下載完畢,整個(gè)程序測(cè)試了很多次,沒(méi)一次出問(wèn)題...
總結(jié)
以上是生活随笔為你收集整理的STM32通过USB实现Bootlader/IAP功能的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 临界区问题的产生
- 下一篇: ucos中的三种临界区管理机制