原文地址:http://www.embed-net.com/thread-268-1-1.html
前沿:
最近在做STM32的USB Bootlader/IAP功能,也就是通过USB实现固件升级,本文介绍下实现的基本思路,希望对实现IAP的同学一个参考,改方法已经在产品中得到实际应用并验证是比较合理,稳定可靠的。程序空间划分:
在单片机的程序Flash中分两个区,分别存储Bootloader代码和App代码,Bootloader放到代码起始地址,也就是0x08000000,App放到0x8020000地址,中间预留了很多的地址空间,主要是为了用来存储一些需要掉电保存的数据,比如我在0x0800C000地址就存放了App程序运行后写入该地址的标志数据。
启动流程:
上电后自然是运行Bootloader程序,Bootloader运行后,做的第一件事情如下所示
[C]
纯文本查看
复制代码
01
02
03
|
if
((*((uint32_t *)EXE_FLAG_ADDR))==0x12345678){
JumpToApplication(APP_START_ADDR);
}
|
也就是判断App运行标志是否有效,这个标志是存放到EXE_FLAG_ADDR地址的,若有效就直接跳转到App程序运行,这个时间很短,所以用户看不到有Bootloader执行的效果,感觉就是直接运行的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标志是否有效,若有效则直接执行后面的程序,若无效则需要在EXE_FLAG_ADDR地址写入执行标志。
Bootloader程序判断App标志若无效,那么Bootloader就不会直接跳转到App,因为这个时候是需要进行升级App的操作,所以程序就进入Bootloader的正常工作流程,也就是等待升级App的各种命令,比如擦出固件,烧写固件,校验固件等。当固件成功写入并校验通过之后,PC端就可以发送一个程序跳转命令跳转到App执行。
PC端操作流程:
PC端和单片机是通过USB进行数据交换的,当然用其他方式也可以,基本流程都是差不多的。
PC程序首先当然是扫描设备,打开设备,然后调用获取固件信息的函数,调用该函数后可以得知当前固件的名称,版本号,固件类型(Bootloader还是App),若发现当前固件不是Bootloader,那么就得通过USB给固件发送一个程序跳转命令,也就是跳转到Bootloader代码执行,当然App在跳转到Bootloader的时候必须把EXE_FLAG_ADDR地址的标志数据擦出掉,这样Bootloader才能进入正常的升级流程。
控制固件程序进入Bootloader之后,PC端程序将打开App固件程序文件,然后根据文件大小,发送擦出App代码存储区域Flash的数据,然后再分包将固件发送给单片机,单片机端Bootlader程序接收到数据后将数据写入App的Flash区域,数据写完之后再进行校验,我是通过计算CRC16的方式进行校验的,校验通过之后就可以发送跳转命令控制程序跳转到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)){
//控制程序跳转到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);
//把指针移动到文件的结尾 ,获取文件长度
int
FileLen=
ftell
(pFile);
//获取文件长度
static
char
*pBuf = (
char
*)
malloc
(FileLen);
//定义文件指针
if
(pBuf == NULL){
printf
(
"malloc error\n"
);
return
0;
}
rewind
(pFile);
//把指针移动到文件开头 因为我们一开始把指针移动到结尾,如果不移动回来 会出错
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"
);
}
//循环写入固件数据到芯片Flash
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"
);
//循环校验数据是否写成功
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"
);
//执行固件
state = BT_ExcuteFirmware(0,ApplicationAddress);
printf
(
"BT_ExcuteFirmware state = %d\n"
,state);
//关闭设备
BT_CloseDevice(0);
return
0;
}
|
PC端程序运行效果如下所示:
![](http://www.embed-net.com/data/attachment/forum/201509/05/102401hm0g20vg0b0rqzrv.png)
后记:
我是用STM32F4+USB3300高速USB实现IAP功能的,12K的App代码几乎瞬间下载完毕,整个程序测试了很多次,没一次出问题...