当前位置:网站首页>自制J-Flash烧录工具——Qt调用jlinkARM.dll方式
自制J-Flash烧录工具——Qt调用jlinkARM.dll方式
2022-07-06 14:29:00 【fangye945a】
背景介绍
想必玩过STM32、GD32的同学都用过下面这个烧录工具吧,它就是J-Flash。通过它再配合我们购买的jlink、jlink-ob等烧录器,便能够非常方便的实现对cortext-M系列的单片机进行程序烧录。
但是在产品的生产和应用过程中,我们通常都会有一些定制化需求,比如读取MCU的芯片ID,根据芯片ID计算出秘钥,烧录程序的同时将秘钥烧录进Flash的某个区域,从而实现软件加密等功能。
原理分析
经过网上大佬们的分析得到一个内部消息,J-Flash烧录工具的大部分接口都封装在安装目录下的JLinkARM.dll动态库中。
知道这个消息后,我们可以使用depends工具对dll库的进行分析, 获取到dll库中所有函数符号,然后在网上查找这些函数的参数类型和个数,然后定义一个函数指针,将指针指向该函数在dll中的地址,从而实现调用。
调用方法
理论上,只要是windows的程序都可以调用dll。
通过Visual Studio( C#)调用JLinkARM.dll,实现程序下载已经有大佬写过文章了。
在此我就以Qt( C++)为例,调用JLinkARM.dll,实现程序下载功能和芯片ID读取功能。调用方式是采用QLibrary类显式调用的方式。
示例程序
下面展示部分示例程序供大家参考,包括函数指针的定义、使用方法、设备连接、读取、烧录的步骤等。
宏定义及函数指针类型定义
//JLINK TIF
#define JLINKARM_TIF_JTAG 0
#define JLINKARM_TIF_SWD 1
#define JLINKARM_TIF_DBM3 2
#define JLINKARM_TIF_FINE 3
#define JLINKARM_TIF_2wire_JTAG_PIC32 4
//RESET TYPE
#define JLINKARM_RESET_TYPE_NORMAL 0
#define JLINKARM_RESET_TYPE_CORE 1
#define JLINKARM_RESET_TYPE_PIN 2
typedef BOOL (*JLINKARM_Open_Func_Ptr)(void); // 定义导出函数类型
typedef void (*JLINKARM_Close_Func_Ptr)(void);
typedef DWORD (*JLINKARM_TIF_Select_Func_Ptr)(int);
typedef void (*JLINKARM_SetSpeed_Func_Ptr)(int);
typedef void (*JLINKARM_Reset_Func_Ptr)(void);
typedef void (*JLINKARM_Go_Func_Ptr)(void);
typedef BOOL (*JLINKARM_IsOpen_Func_Ptr)(void);
typedef void (*JLINKARM_SetLogFile_Func_Ptr)(char *file);
typedef DWORD (*JLINKARM_GetDLLVersion_Func_Ptr)(void);
typedef DWORD (*JLINKARM_GetHardwareVersion_Func_Ptr)(void);
typedef DWORD (*JLINKARM_GetFirmwareString_Func_Ptr)(char *buff, int count);
typedef DWORD (*JLINKARM_GetSN_Func_Ptr)(void);
typedef BOOL (*JLINKARM_ExecCommand_Func_Ptr)(char* cmd, int a, int b);
typedef DWORD (*JLINKARM_TIF_Select_Func_Ptr)(int type);
typedef void (*JLINKARM_SetSpeed_Func_Ptr)(int speed);
typedef DWORD (*JLINKARM_GetSpeed_Func_Ptr)(void);
typedef DWORD (*JLINKARM_GetId_Func_Ptr)(void);
typedef DWORD (*JLINKARM_GetDeviceFamily_Func_Ptr)(void);
typedef BOOL (*JLINKARM_Open_Func_Ptr)(void);
typedef void (*JLINKARM_Close_Func_Ptr)(void);
typedef BOOL (*JLINKARM_IsOpen_Func_Ptr)(void);
typedef BOOL (*JLINKARM_Connect_Func_Ptr)(void);
typedef BOOL (*JLINKARM_IsConnected_Func_Ptr)(void);
typedef int (*JLINKARM_Halt_Func_Ptr)(void);
typedef BOOL (*JLINKARM_IsHalted_Func_Ptr)(void);
typedef void (*JLINKARM_SetResetType_Func_Ptr)(int type);
typedef void (*JLINKARM_Reset_Func_Ptr)(void);
typedef void (*JLINKARM_Go_Func_Ptr)(void);
typedef void (*JLINKARM_GoIntDis_Func_Ptr)(void);
typedef DWORD (*JLINKARM_ReadReg_Func_Ptr)(int index);
typedef int (*JLINKARM_WriteReg_Func_Ptr)(int index, DWORD data);
typedef int (*JLINKARM_ReadMem_Func_Ptr)(DWORD addr, int len, void *buf);
typedef int (*JLINKARM_WriteMem_Func_Ptr)(DWORD addr, int len, void *buf);
typedef int (*JLINKARM_WriteU8_Func_Ptr)(DWORD addr, BYTE data);
typedef int (*JLINKARM_WriteU16_Func_Ptr)(DWORD addr, WORD data);
typedef int (*JLINKARM_WriteU32_Func_Ptr)(DWORD addr, DWORD data);
typedef int (*JLINK_EraseChip_Func_Ptr)(void);
typedef int (*JLINKARM_DownloadFile_Func_Ptr)(LPCSTR file, DWORD addr);
typedef void (*JLINKARM_BeginDownload_Func_Ptr)(int index);
typedef void (*JLINKARM_EndDownload_Func_Ptr)(void);
函数指针定义
JLINKARM_GetDLLVersion_Func_Ptr JLINKARM_GetDLLVersion_Entry = NULL; //获取DLL版本
JLINKARM_Open_Func_Ptr JLINKARM_Open_Entry = NULL; //打开设备
JLINKARM_IsOpen_Func_Ptr JLINKARM_IsOpen_Entry = NULL; //是否已经打开
JLINKARM_Close_Func_Ptr JLINKARM_Close_Entry = NULL; //关闭设备
JLINKARM_TIF_Select_Func_Ptr JLINKARM_TIF_Select_Entry = NULL; //选择设备
JLINKARM_SetSpeed_Func_Ptr JLINKARM_SetSpeed_Entry = NULL; //设置JLINK接口速度 0为自动调整
JLINKARM_Reset_Func_Ptr JLINKARM_Reset_Entry = NULL; //复位系统
JLINKARM_Halt_Func_Ptr JLINKARM_Halt_Entry = NULL; //中断程序执行,进入停止状态
JLINKARM_Go_Func_Ptr JLINKARM_Go_Entry = NULL; //执行程序
JLINKARM_WriteMem_Func_Ptr JLINKARM_WriteMem_Entry = NULL; //写内存
JLINKARM_ReadMem_Func_Ptr JLINKARM_ReadMem_Entry = NULL; //读内存
函数入口加载
//构造函数初始化时,new QLibaray时传入使用的dll文件
QLibrary jlink_lib = new QLibrary("JLinkARM.dll");
void Widget::load_library_function()
{
if(jlink_lib->load()){
qDebug()<<"加载JLinkARM.dll成功, 开始解析函数";
JLINKARM_Open_Entry = (JLINKARM_Open_Func_Ptr)jlink_lib->resolve("JLINKARM_Open");
JLINKARM_IsOpen_Entry = (JLINKARM_IsOpen_Func_Ptr)jlink_lib->resolve("JLINKARM_IsOpen");
JLINKARM_Close_Entry = (JLINKARM_Close_Func_Ptr)jlink_lib->resolve("JLINKARM_Close");
JLINKARM_ExecCommand_Entry = (JLINKARM_ExecCommand_Func_Ptr)jlink_lib->resolve("JLINKARM_ExecCommand");
JLINKARM_GetDLLVersion_Entry = (JLINKARM_GetDLLVersion_Func_Ptr)jlink_lib->resolve("JLINKARM_GetDLLVersion");
JLINKARM_TIF_Select_Entry = (JLINKARM_TIF_Select_Func_Ptr)jlink_lib->resolve("JLINKARM_TIF_Select");
JLINKARM_SetSpeed_Entry = (JLINKARM_SetSpeed_Func_Ptr)jlink_lib->resolve("JLINKARM_SetSpeed");
JLINKARM_GetSpeed_Entry = (JLINKARM_GetSpeed_Func_Ptr)jlink_lib->resolve("JLINKARM_GetSpeed");
JLINKARM_Connect_Entry = (JLINKARM_Connect_Func_Ptr)jlink_lib->resolve("JLINKARM_Connect");
JLINKARM_IsConnected_Entry = (JLINKARM_IsConnected_Func_Ptr)jlink_lib->resolve("JLINKARM_IsConnected");
JLINKARM_GetId_Entry = (JLINKARM_GetId_Func_Ptr)jlink_lib->resolve("JLINKARM_GetId");
JLINKARM_GetSN_Entry = (JLINKARM_GetSN_Func_Ptr)jlink_lib->resolve("JLINKARM_GetSN");
JLINKARM_Reset_Entry = (JLINKARM_Reset_Func_Ptr)jlink_lib->resolve("JLINKARM_Reset");
JLINKARM_Halt_Entry = (JLINKARM_Halt_Func_Ptr)jlink_lib->resolve("JLINKARM_Halt");
JLINKARM_WriteMem_Entry = (JLINKARM_WriteMem_Func_Ptr)jlink_lib->resolve("JLINKARM_WriteMem");
JLINKARM_ReadMem_Entry = (JLINKARM_ReadMem_Func_Ptr)jlink_lib->resolve("JLINKARM_ReadMem");
JLINK_EraseChip_Entry = (JLINK_EraseChip_Func_Ptr)jlink_lib->resolve("JLINK_EraseChip");
qDebug()<<"解析函数完成";
}
else
{
qDebug()<<"加载JLinkARM.dll失败!!";
}
}
连接设备
所有操作前必需确保设备已经连接,如下为设备连接的示例程序。
bool Widget::connect_device()
{
if(JLINKARM_IsOpen())
{
qDebug()<<"JLINKARM was Opened!";
return true;
}
qDebug()<<"Try Open JLINKARM...";
JLINKARM_Open();
if(JLINKARM_IsOpen())
{
qDebug()<<"JLINKARM Open success!";
JLINKARM_ExecCommand("device = STM32F407IG", 0, 0);
JLINKARM_TIF_Select(JLINKARM_TIF_SWD);
JLINKARM_SetSpeed(4000); //设置下载速度
JLINKARM_Connect();
if(JLINKARM_IsConnected()){
return true;
}else
{
print_log("连接设备失败! 请检查设备连接..");
}
}
else {
qDebug()<<"JLINKARM Open fail!";
print_log("连接设备失败! 请检查烧录器连接..");
}
return false;
}
读取芯片ID
连接设备后我们可以通过JLINKARM_ReadMem接口,读取芯片ID,也就是读取CPUID寄存器地址的值。
QString Widget::get_cpu_id()
{
unsigned char cpuid[12]={
0};
char cpu_id_tmp[128]={
0};
JLINKARM_ReadMem(0x1FFF7A10, 12, cpuid);
JLINKARM_ReadMem(0x1FFF7A10, 12, cpuid); //多读一次解决读取错误的问题
sprintf(cpu_id_tmp, "%02X%02X%02X%02X-%02X%02X%02X%02X-%02X%02X%02X%02X",
cpuid[3],cpuid[2],cpuid[1],cpuid[0],
cpuid[7],cpuid[6],cpuid[5],cpuid[4],
cpuid[11],cpuid[10],cpuid[9],cpuid[8]);
for(int i=0; i<sizeof(cpuid); i++)
qDebug("cpuid[%d]=%02X", i, cpuid[i]);
qDebug("cpuid=%s",cpu_id_tmp);
return QString(cpu_id_tmp);
}
void Widget::on_pushButton_cpuid_clicked()
{
static int cpuid_reading_flag = 0;
if(cpuid_reading_flag)
{
print_log(QString("获取CPUID中,请稍后..."));
return;
}
if( connect_device()){
// qDebug("JLink Info:");
// qDebug("SN = %08u", JLINKARM_GetSN());
// qDebug("ID = %08X", JLINKARM_GetId());
// qDebug("VER = %u", JLINKARM_GetDLLVersion());
// qDebug("Speed = %u", JLINKARM_GetSpeed());
print_log(QString("获取CPUID中,请稍后..."));
cpu_id_str = get_cpu_id();
print_log(QString("获取CPUID成功: ")+cpu_id_str);
disconnect_device();
}
}
烧录程序
烧录程序考虑到会持续一段时间,可能会导致界面假死。所以使用了一个QTimer定时器来实现程序烧录过程,每次定时结束时烧录1KB数据,同时更新烧录进度条,直到烧录结束。但是在实际使用过程中,感觉并没有真正将程序烧录至MCU中去,只是传递至jlinkARM.dll接口的内存中去了。而在断开连接时,会自动触发jlinkARM.dll中烧录功能,会弹出一个JFLASH的小烧录窗口进行真正的烧录。
//构造函数初始化时,连接定时器超时信号与槽函数
connect(timer_burn, SIGNAL(timeout()),this, SLOT(on_timer_burn_timeout()));
void Widget::on_pushButton_burn_clicked()
{
if(burnning_flag)
{
print_log("正在加速烧录中,请稍后...");
return;
}
burn_bin_data.clear();
total_file_size = 0;
if(target_bin_path.isEmpty())
{
print_log("请选择要烧录的固件!");
return;
}
if( connect_device() ){
//连接设备
cpu_id_str = get_cpu_id();// 获取CPUID
bool ok = false; //检查起始地址信息
write_start_addr = ui->lineEdit_start_addr->text().trimmed().toInt(&ok, 16);
if(!ok)
{
print_log("烧录起始地址格式有误!");
disconnect_device();
return;
}
QFile burn_file;
burn_file.setFileName(target_bin_path);
burn_file.open(QIODevice::ReadOnly); //打开文件
if(burn_file.isOpen())
{
burn_bin_data = burn_file.readAll(); //将要烧录的数据读取到内存中
burn_file.close(); //关闭文件
if(burn_bin_data.size() > 1024*1024)
{
print_log("文件大小不允许超过1MB!");
burn_bin_data.clear();
disconnect_device();
return;
}
print_log("开始烧录固件, 请稍后...");
burnning_flag = 1; //正在烧录
timer_burn->start(BURN_DELAY);
}
else
{
print_log("打开固件失败, 请检查文件是否存在!");
disconnect_device();
}
}
}
void Widget::on_timer_burn_timeout()
{
if(timer_burn)
{
timer_burn->stop();
//烧写固件
if(burn_bin_data.isEmpty()) //烧录完成
{
ui->progressBar->setValue(100);
burnning_flag = 0;
disconnect_device();
print_log("烧录完成!");
return;
}
else //烧录的数据非空
{
if(burn_bin_data.size() > BURN_STEP_SIZE) //大小超过1K
{
int ret = JLINKARM_WriteMem(write_start_addr, BURN_STEP_SIZE, burn_bin_data.data());
// qDebug()<<"JLINKARM_WriteMem ret = "<<ret;
write_start_addr += BURN_STEP_SIZE; //烧写地址递增
burn_bin_data.remove(0, BURN_STEP_SIZE);
}
else //大小不到1K
{
int ret = JLINKARM_WriteMem(write_start_addr, burn_bin_data.size(), burn_bin_data.data());
// qDebug()<<"JLINKARM_WriteMem ret = "<<ret;
write_start_addr += burn_bin_data.size(); //烧写地址递增
burn_bin_data.clear();
}
unsigned int percent = 1.0*(total_file_size - burn_bin_data.size())/total_file_size*100;
// qDebug()<<"Burn progress: "<<percent;
ui->progressBar->setValue(percent);
timer_burn->start(BURN_DELAY);//5ms后继续烧录
}
}
}
学习参考传送门
在开发过程中,踩了很多坑,在此整理几个有借鉴意义的文章。
https://www.amobbs.com/thread-5670237-1-1.html
https://www.amobbs.com/thread-5718918-1-1.html
https://blog.csdn.net/qq446252221/article/details/89878996
免责声明
segger官网有配套的SDK,包含完整的头文件、库文件及PDF文档,有条件的同学建议支持正版。
https://www.segger.com/products/debug-probes/j-link/technology/j-link-sdk
本文章仅供学习参考,不可用于商业用途,如侵权请联系删除。
边栏推荐
- GPS from entry to abandonment (XVII), tropospheric delay
- Maximum product of three numbers in question 628 of Li Kou
- Problems in the process of opencv300 cmake generating project
- OpenCV300 CMake生成project在项目过程中的问题
- About the professional ethics of programmers, let's talk about it from the way of craftsmanship and neatness
- Spatial domain and frequency domain image compression of images
- 【sciter】: 基于 sciter 封装通知栏组件
- labelimg的安装与使用
- VIP case introduction and in-depth analysis of brokerage XX system node exceptions
- zabbix 代理服务器 与 zabbix-snmp 监控
猜你喜欢

The nearest common ancestor of binary (search) tree ●●

ResNet-RS:谷歌领衔调优ResNet,性能全面超越EfficientNet系列 | 2021 arxiv

Notes de développement du matériel (10): flux de base du développement du matériel, fabrication d'un module USB à RS232 (9): création de la Bibliothèque d'emballage ch340g / max232 SOP - 16 et Associa
![[Digital IC hand tearing code] Verilog burr free clock switching circuit | topic | principle | design | simulation](/img/2b/15b3d831bba6aa772ad83f3ac91d23.png)
[Digital IC hand tearing code] Verilog burr free clock switching circuit | topic | principle | design | simulation

重磅新闻 | Softing FG-200获得中国3C防爆认证 为客户现场测试提供安全保障

Unity3d Learning Notes 6 - GPU instantiation (1)

每日一题:力扣:225:用队列实现栈

HDR image reconstruction from a single exposure using deep CNN reading notes

0 basic learning C language - digital tube

小常识:保险中的“保全”是什么?
随机推荐
Memorabilia of domestic database in June 2022 - ink Sky Wheel
基于 QEMUv8 搭建 OP-TEE 开发环境
[sciter bug] multi line hiding
Wechat red envelope cover applet source code - background independent version - source code with evaluation points function
NetXpert XG2帮您解决“布线安装与维护”难题
[leetcode daily clock in] 1020 Number of enclaves
MariaDb数据库管理系统的学习(一)安装示意图
GPS from getting started to giving up (XX), antenna offset
墨西哥一架飞往美国的客机起飞后遭雷击 随后安全返航
About the professional ethics of programmers, let's talk about it from the way of craftsmanship and neatness
嵌入式常用计算神器EXCEL,欢迎各位推荐技巧,以保持文档持续更新,为其他人提供便利
[MySQL] online DDL details
GNN, please deepen your network layer~
(十八)LCD1602实验
Applet system update prompt, and force the applet to restart and use the new version
2020 Bioinformatics | GraphDTA: predicting drug target binding affinity with graph neural networks
Mongodb (III) - CRUD
Codeforces Round #274 (Div. 2) –A Expression
Force deduction question 500, keyboard line, JS implementation
GPS从入门到放弃(十七) 、对流层延时