本帖最后由 一路向北lm 于 2020-12-10 17:47 编辑
#申请原创#
前段时间研究了一下STM32内部的BootLoader ,也就是STM32内嵌的一段程序,搞明白原理后,使用Qt编写的一个 测试小工具,展示效果如下:
可能你还不知道: STM32中内嵌了一段自举程序,可能很多人不知道。那段自举程序存放在System memory(系统存储器)中,我们通过配置启动,选择启动系统存储器就可以运行这段程序。
STM32有三种启动模式:
01.Main Flash memory:主FLASH
02.System memory:系统存储器
03.Embedded SRAM:内置SRAM
三种模式是通过不同配置来实现,一般通过BOOT引脚和BOOT位来配置启动模式。不同的芯片配置有差异,有些芯片没有BOOT1引脚,会结合BOOT位来实现。
内嵌的自举程序
STM32内嵌的自举程序存放在系统存储区,由ST在生产线上写入,用户不能修改。我们选择System memory(系统存储器)启动模式,就会进入系统存储区执行自举程序,内嵌的自举程序主要用于通过串行、IIC、SPI、USB、CAN等接口与外部通讯。不同型号MCU支持的串行接口不一样,具体请参看应用笔记 AN2606。
自举程序中UART协议
通信就会牵涉到协议,这里也说一下自举程序中 USART 协议。
USART 自举程序命令集:(具体参考官方文档AN3155)
4.介绍几个简单的命令流程4.1 Get 命令
用户通过 Get 命令可获取自举程序的版本及支持的命令。自举程序接收到 Get 命令后,会将
自举程序版本和支持命令的代码发送给主机 具体如下:
4.2Get Version & Read Protection Status 命令
GetVersion & Read Protection Status 命令用于获取自举程序版本及读保护状态。自举程序
接收到此命令后,会将如下信息(版本、使能和禁止读保护的次数)发送给主机。 具体如下:
4.3Get ID 命令
Get ID命令用于获取芯片 ID(标识)的版本。自举程序接收到此命令后,会将产品 ID 发送
给主机。 具体如下:
4.4 Read Memory 命令
ReadMemory 命令用于从RAM、 Flash 和信息块(系统存储器或选项字节区域)中的任何
有效存储器地址(参见注释)读取数据。(读的前提是Flash没有加读保护操作) 具体如下:(读取flash 0x 08000000 开始的16个字节的数据)
4.5 Go 命令
Go 命令用于从应用程序指定的地址开始执行已下载的代码或其它任何代码。自举程序接收
到 Go 命令后,会将 ACK 字节发送到应用程序。发送 ACK 字节后,自举程序会等待一个地
址(4 个字节,字节 1 表示地址 MSB,字节 4 表示 LSB)和一个校验和字节,然后检查接
收到的地址。如果接收到的地址有效且校验和正确,则自举程序将发送一个ACK字节,否
则将发送一个 NACK 字节并中止此命令。 具体如下:使用go 跳转到 flash地址处执行(0x08000000),此时LED已经闪烁 命令太多,其它的就不再介绍了!
5.自举程序上位机(自己使用Qt编写的,主要用于测试)01. 在自举模式下可以简单实现对内部flash的数据的读出(flash不加锁) 02. 在自举模式下可以简单实现对内部sram的数据的读出(flash不加锁) 03.可以生成对应的.hex文件,使用Qt自己设计算法实现。 04.下载部分暂且没有做。
读取Hex文件 和拼Hex文件代码开源给大家 // 读取芯片内部flash数据
// 地址范围: 0x0800 0000 ~ 0x0801 ffff 128K Byte
// 循环读取,每次读取16个字节,读取128K Byte /16Byte = 8000 次
int base = 0x08000000;
for(int i =0;i<8000;i++)
{
//1.发送读内存命令
UartSendHex("11ee");
Sleep(5);
//2.发送地址和校验和
int offer = base + i*16;
QString offer_str = QString::number(offer,16);
if(offer_str.size()<8)
offer_str.insert(0,"0");
QString check_str;
check_str = getXORresult(offer_str.mid(0,2), offer_str.mid(2,2));
check_str = getXORresult(check_str, offer_str.mid(4,2));
check_str = getXORresult(check_str, offer_str.mid(6,2));
UartSendHex(offer_str+check_str);
Sleep(5);
//3.发送要读取的字节数和效验和
QByteArray ByteNum = "0ff0";
UartSendHex(ByteNum);
Sleep(5);
ui->progressBar->setFormat(QString::fromLocal8Bit("%1%").arg(QString::number((i+1)/10, 'f', 1)));
ui->progressBar->setValue((i+1)/10);
}
// Qt flash数据 转 hex文件算法
int t = 0,g = 0;
QString text = ui->textEdit_Recv->toPlainText();
QStringList number_list = text.split("\n");
QStringList result_list;
// 遍历字符串列表,滤除全ffff.........结尾,记录行数
for(int i = 0; i < number_list.size(); ++i)
{
if(number_list.at(i) == "ffffffffffffffffffffffffffffffff")
{
t = i;
break;
}
}
// 遍历字符串列表斩断行,滤除改行的 ffff.........
text.clear();
text = number_list.at(t-1);
for(int i =0;i<text.size();i++)
{
if((text.at(i) == 'f')&&(text.at(i+1) == 'f')&&(i%2 == 0))
{
g = i;
break;
}
}
// 拼接新的全数据的字符串列表
for(int i =0;i<t-1;i++)
{
result_list<<number_list.at(i);
}
if(g!=0)
{
text = text.remove(g,32-g);
result_list<<text;
}
//生成HEX文件
//1.添加 10 字节数
text.clear();
QStringList result_list1;
for(int i = 0; i < t; ++i)
{
text = result_list.at(i);
if(i!=t-1)
text = text.insert(0,"10");
else
{
QString str = QString::number(text.size()/2,16);
if(str.size()<2)
str.insert(0,"0");
text = text.insert(0,str);
}
result_list1 << text;
}
//2.添加地址偏移
QStringList result_list2;
int offer = 0x0000;
QString offer_str;
text.clear();
for(int i = 0; i < t; ++i)
{
offer = i*16;
offer_str = QString::number(offer,16);
if(offer_str.size()<4)
{
if(offer_str.size() == 1)
offer_str.insert(0,"000");
if(offer_str.size() == 2)
offer_str.insert(0,"00");
if(offer_str.size() == 3)
offer_str.insert(0,"0");
}
text = result_list1.at(i);
text = text.insert(2,offer_str);
result_list2 << text;
}
//3.添加效验和
// 以020000040003F7为例子
//具体算法为 0x100-((0x02+0x00+0x00+0x04+0x00+0x03)%256)= 0xF7
text.clear();
bool ok;
int check;
QString check_str;
QStringList result_list3;
for(int i = 0; i < t; ++i)
{
text = result_list2.at(i);
check = 0;
for(int j=0;j<text.size();j++)
{
check += text.mid(j*2,2).toInt(&ok,16);
}
check = (0x100 - check%256);
check_str = QString::number(check,16);
if(check_str.size()<2)
check_str = check_str.insert(0,"0");
text = text.insert(text.size(),check_str);
result_list3<<text;
}
//4.添加冒号 :
QStringList result_list4;
text.clear();
for(int i = 0; i < t; ++i)
{
text = result_list3.at(i);
text = text.insert(0,":");
result_list4<<text;
}
//5.添加HEX头部
QString hear=":020000040800f2";
//QStringList result_list5;
result_list4.insert(0,hear);
//6.添加HEX尾部
QString tail1=":04000005080001cd21";
QString tail2=":00000001ff";
result_list4.insert(t+2,tail1);
result_list4.insert(t+3,tail2);
//7.保存数据为.hex文件
...............
|