暴力更改hex文件实现自动烧写stm32程序
STM32自动化烧写目的与适用范围项目已经固定,每次烧写程序只会更改很少量的信息。比如设备编号,日期等信息,但每台设备这些信息都是不相同的。第次更改设备必须打开keil更改编号,再次编译,烧写,过程繁琐。于是就想可不可以直接更改hex文件,改好后直接烧写进板子拉?使用到工具pythonpylink(python实现的一个stm32模块)JLinkARM库(这个是C/C++实现的底层库)步骤找到不同
·
STM32自动化烧写
目的与适用范围
- 项目已经固定,每次烧写程序只会更改很少量的信息。比如设备编号,日期等信息,但每台设备这些信息都是不相同的。
- 每次更改设备必须打开keil更改编号,再次编译,烧写,过程繁琐。于是就想可不可以直接更改hex文件,改好后直接烧写进板子拉?
使用到工具
- python
- pylink(python实现的一个stm32模块)
- JLinkARM库(这个是C/C++实现的底层库)
步骤
- 找到不同设备号更改时hex改变位置
- 使用python实现自动更改
- 使用pylink烧写进去
具体操作
- 找出hex文件更改位置
- keil编译生成两个不同设备号的hex文件,并使用比较命令找出更改位点。这里说下hex文件保存位置
项目根目录\Output\
下面会有一个.hex文件,这个文件就是要下载到STM32的程序。复制出来保存到你喜欢的文件夹下。
- keil编译生成两个不同设备号的hex文件,并使用比较命令找出更改位点。这里说下hex文件保存位置
/* 第一个文件编号如下 */
#define ID_Number 2018080120
/* 保存的hex文件命名为old.hex */
/* 第二个文件编号 */
#define ID_Number 2018080300
/* 保存为new.hex */
/* 更改编号后一定要点编译,不然两个文件都完全一样了 */
/* linux 比较两个文件 */
diff old.hex new.hex
1329c1329
< :1052F0006846FCF7F1FB1CBD2C7649781CB50446CA
---
> :1052F0006846FCF7F1FB1CBD787549781CB504467F
/* 显示1329行不同并分别列出了不同的行 */
/* windows CMD 比较两个文件 */
FC new.hex old.hex
***** new.hex
:1052E0002088ADF800006088ADF802000248019007
:1052F0006846FCF7F1FB1CBD2C7649781CB50446CA
:105300002088ADF800006088ADF802000548B0F8CC
***** OLD.HEX
:1052E0002088ADF800006088ADF802000248019007
:1052F0006846FCF7F1FB1CBD787549781CB504467F
:105300002088ADF800006088ADF802000548B0F8CC
*****
/* window FC命令给出了不同行和上下各一行所以是3行,但没有说明是那一行,可以复制搜索下,也能确认是第1329行 */
/* 仔细数一下可以更加明确的那几列不同,因为我们到时候更改hex文件必须定位到那一行,那一列 */
- 使用python更改
- 分析下hex文件那几列不同
linux的diff比较结果很容易的看出来2C76和7875是不同的,行尾两个留后面说。使用计算器进制转化计算下我们的设备编号十六进制是什么?
2018080120对应0x78497578,2018080300对应0x7849762C
可以对比下发现最后四位数字不同7578和762C刚好和我们用diff比较出来结果对上只是前后两位对换,这种对换应该是因为计算机存储的关系。再对比下文件就可以发现2C764978对应2018080300, 78754978对应2018080120。这里的存储顺序需要特别留意下,因为使用python暴力更改文件必须一一对应。
比较下可以发现除这个数字不同外最后两位也是不一样的,这又是什么鬼?
intel hex文件格式这里做了比较细的描述,总结下就是最后两位是对每一行前面计算得到的,用于检测这一行有没有错漏。 - 更改hex文件
- 分析下hex文件那几列不同
import os
# 定义一些变量
file_name = "data.hex" # 写入hex文件文件名
row = 1329 # 编号行
line = 23 # 编号列
No_row_str = list() # 编号行字符串
current_No = 0 # 当前编号十进制
next_No = 0 # 下台设备号
# 打开文件
if not os.access(file_name, os.F_OK|os.R_OK|os.W_OK):
print(file_name,"文件权限受限\n")
print(file_name,"加载中......")
while 1 :
file_ptr = open(file_name, "r+")
# 获取当前设备号
def get_current_No(file):
# 定位编号位置,包含行,起始列
global No_row_str
i = row
while i != 0:
read_line = file.readline();
i = i - 1;
No_row_str = list(read_line)
# 定位到上一行,方便后而写入
size = len(read_line)
file.seek((file.tell() - size - 1), 0)
return x_to_10(read_line[25:33])
# 16进制转化为10进制
def x_to_10(str):
global current_No
inpu = ['0', 'x']
list_str = list(str)
inpu = inpu + list_str[6:8] + list_str[4:6] + list_str[2:4] + list_str[0:2]
No_str = ''.join(inpu)
current_No = int(No_str, 16);
# 将文件中的设备编号更新为新设备编号
def update_No(new_No):
global No_row_str
#get new no hex string list;
no_hex = list(hex(new_No))
#update string to the No cur str;
No_row_str[line + 2 + 6] = no_hex[2];
No_row_str[line + 2 + 7] = no_hex[3];
No_row_str[line + 2 + 4] = no_hex[4];
No_row_str[line + 2 + 5] = no_hex[5];
No_row_str[line + 2 + 2] = no_hex[6];
No_row_str[line + 2 + 3] = no_hex[7];
No_row_str[line + 2 + 0] = no_hex[8];
No_row_str[line + 2 + 1] = no_hex[9];
ver_str = tohex(vertify(No_row_str))
No_row_str[41] = ver_str[2]
No_row_str[42] = ver_str[3]
No_row_str[43] = '\r'
return (''.join(No_row_str))
# python有一个十进制转16进制的hex不能直接用,因为数字转化的位数不确定会根据数字不同长度不同,这里表现就是有的转化后会是0xa1,有的又会是0xf。后面的会少一位,需要的是0x0f这样的形式。因此自己写一个.
def tohex(number):
rst = ['0','x']
rst.append(hex(int(number / 16))[2])
rst.append(hex(number % 16)[2])
return rst
# 计算验证码
def vertify(row_line):
if row_line == "":
print("不能验证空行")
return
size = len(row_line)
sum = 0
vertify = 0
for cur_pos in range(1, size-3, 2):
temp = ''.join(No_row_str[cur_pos:(cur_pos + 2)])
sum = sum + int(temp, 16)
vertify = 256 - (sum % 256)
return vertify
实现效果:
当前设备编号: 2020080301
默认下台设备编号: 2020080302
下台设备编号(使用默认编号直接按Enter)
正在更改设备编号为 2020080302 ......
当前设备编号: 2020080302
默认下台设备编号: 2020080303
下台设备编号(使用默认编号直接按Enter)
正在更改设备编号为 2020080303 ......
当前设备编号: 2020080303
默认下台设备编号: 2020080304
下台设备编号(使用默认编号直接按Enter)
正在更改设备编号为 2020080304 ......
当前设备编号: 2020080304
默认下台设备编号: 2020080305
下台设备编号(使用默认编号直接按Enter)
正在更改设备编号为 2020080305 ......
- 写入板子
使用pylink写入。但pylink国内使用的还真不多,于是又是各种找用法,本来是想用人家提供API自己写,但无耐文档是真的坑,只能直接使用提供的命令行。说说遇到的坑:
1. 开门不利,安装。要求我安装Microsoft Visual C++ 14.0,更神奇的是给我网页是认我安装visual studio。我安装一个python部件你叫我安装一个IDE,只想骂人。装了最新的你还提示找不到。只能找网友帮忙了。安装模块报错,因为我是psutil安装不上,直接<Ctrl + f>搜索psutil,下载安装OK。
2. pylink.JLink()错误连不上,百度搜的文档我压根儿都没看到JLinkARM啥事。连又连接不上,没法只能上github看看。github上说的很明确JLinkARM.dll要放在指定目录才可以,于是把keil下面的JLinkARM放windows,sys32,sys,当前目录,但没有一个可用的。仔细阅读github说明,要在这里下载JLinkARM.dll。下载下来写的有三个目录可以放,当前目录、windows目录、windows/sys32目录,我放在sys32好像是可以了,也不知道是因为放在sys32下面还是因为path中加入新安装软件的运行目录的原因。反正就是可以用了
3. 打开设备pylink.JLink().open()。要一个参数,这参数文档也不写。自己摸索发现win10设备管理器里面的事件里会有一行"已配置设备 USB\VID_1366&PID_0101\000000123456。",使用123456试试,还可以。
4. 打开了准备用flash_file写进去说我没有连接target,好吧只能用connect连接,连接又要用到设备名,这个设备名又不用有说只能自己摸索。这次使用设备管理器救不了我了,keil才能帮助我,keil的每个项目都要选择一个Device类型,比如STM32F103RC,现在想来感觉当时傻不知道用这个,但文档不说我怎么知道用那一串字符表示啊。
5. 到这里我其实还是没有成功运行connect,还是会提示pylink.errors.JLinkException: J-Link DLL is not open.
,但我发现一个好东西,可以完全放弃它的API,使用命令行就可以了。pylink提供了一个pylink命令,所以转而使用他的命令就可以了,我又不用管什么效率,它API也不一定带来什么效率。
6. 命令行就友好太多了想办法填满见个参数就好了。我确定设备名就是使用命令行确定STM32F103RC可用的
$ pylink emulator -s STM32F103RC
Device Name: STM32F103RC
Core ID: 1000342647
Flash Address: 134217728
Flash Size: 262144 bytes
RAM Address: 536870912
RAM Size: 49152 bytes
Manufacturer: ST
# 对于不可用的设备只会显示
STM32F103R is not supported :(
说说烧写命令
pylink flash -a 8000000 -t SWD -d STM32F103RC -s 123456 data.hex
这里还是有坑,-a/address是写入地址,这个可以keil里面项目中找到一般都0x8000000或以上,但都知道0x8000000是一个16进制数,我还特意计算了下它的十进制数,用不了。文档中写要用int数。试试直接8000000,可以用。好吧没啥说的。
-t 是下载方式就两种JTA和SWD和你板子的接线有关
-s/serial 就是设备管理器”事件“里面查到的那个
-d/device 就是设备名,keil项目里面有
最后是hex文件
有了以上命令就可以使用python写入了,但还有一点要注意写入前hex文件要关闭状态,pylink会去读hex文件,所以pylink写必须在file.close()后面。
使用脚本烧写的好处
- 自动完成自加操作,懒人必备
- 防止输入出错,写入重复编号
- 可以自己加入日志功能,实现自动记录烧写日期,烧写编号
- 还可以加入导出到excl功能,可定制程度相当高
更多推荐
已为社区贡献1条内容
所有评论(0)