自制J-Flash烧录工具——Qt调用jlinkARM.dll方式

这篇具有很好参考价值的文章主要介绍了自制J-Flash烧录工具——Qt调用jlinkARM.dll方式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景介绍

想必玩过STM32、GD32的同学都用过下面这个烧录工具吧,它就是J-Flash。通过它再配合我们购买的jlink、jlink-ob等烧录器,便能够非常方便的实现对cortext-M系列的单片机进行程序烧录。
jlinkarm.dll,QT学习,单片机学习,C++学习,qt,单片机
但是在产品的生产和应用过程中,我们通常都会有一些定制化需求,比如读取MCU的芯片ID,根据芯片ID计算出秘钥,烧录程序的同时将秘钥烧录进Flash的某个区域,从而实现软件加密等功能。

原理分析

经过网上大佬们的分析得到一个内部消息,J-Flash烧录工具的大部分接口都封装在安装目录下的JLinkARM.dll动态库中。
jlinkarm.dll,QT学习,单片机学习,C++学习,qt,单片机
知道这个消息后,我们可以使用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
jlinkarm.dll,QT学习,单片机学习,C++学习,qt,单片机
本文章仅供学习参考,不可用于商业用途,如侵权请联系删除。文章来源地址https://www.toymoban.com/news/detail-697690.html

到了这里,关于自制J-Flash烧录工具——Qt调用jlinkARM.dll方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • J-flash工具使用教程

    JLink_Windows_V754b_x86_64 JLINK驱动,集成了多种调试下载工具,也可以单独用来下载代码,并且效率高于串口下载;支持多种芯片和市面上大多数主流IDE环境,可作为J-link驱动,可独立使用其集成的十几款调试工具,非常方便。如图: 安装JLink_Windows_V754b_x86_64 免积分下载链接:

    2024年02月16日
    浏览(23)
  • 用VSCode玩STM32的烧录工具 CooCox Cortex Flash Programmer

    一、下载软件         经热心兄弟推荐的版本,不知道有没有版权,如有版权问题,请通知删除。         CSDN - 0积分下载:https://download.csdn.net/download/qq_49053936/88744187 二、生成bin文件         插件不同,方法有所不同,各施神通吧。         这里分享一下用keil生成

    2024年01月16日
    浏览(33)
  • J-flash 的简易使用教程

    1.打开J-FLash,配置target device为你的芯片类型文件,例如这里选的STM32F1,如果是其他类类型的芯片,要先在J -link安装路径下添加相关配置文件(一般这个文件在原厂的SDK里面都带有)。 2、然后点击File-Open data file...选择要烧录的hex文件,或者直接将文件拖入JFLash中; 3、选择烧录

    2024年02月17日
    浏览(24)
  • 为你的J-FLASH添加MCU型号

    1、首先你要有你需要添加的MCU对应的FLM算法文件。这里我以某大HC32L13x为例。 2、将FLM算法文件添加到JLINK目录下的Device文件夹下,我这里新建一个HDSC文件夹,并将FLM放在此目录下。 3、修改JLINK目录下的 JLinkDevices.xml  在xml文件中添加MCU器件信息。  对应你MCU的FLASH RAM信息修

    2024年02月06日
    浏览(31)
  • ESP32-C3烧录后打印报错invalid header: 0xffffffff【flash_download_tool_3.9.3.exe工具烧录】

    ESP32-C3没有一键下载电路的情况下是使用的乐鑫官方下载工具flash_download_tool_3.9.3.exe 【工具 | 乐鑫科技 (espressif.com.cn)】 在下载工具中,你选择你的下载地址非常重要。 如果你是ESP32其他系列,你需要选择你的bootloader.bin是0x1000开始 如果你是ESP32-C3系列,你需要选择你的bootlo

    2024年02月07日
    浏览(32)
  • 使用J-Flash下载Hex文件的方法

    请大家Follow我的Steps,你一定不会错过使用J-Flash下载程序方法。 1.打开J-Flash软件 直接点标红的×,关闭它。 2.File-New Project,弹出的对话框中点这3个点。  3.选择MCU型号,点OK。就关闭这一页面。 4.确定选择的MCU是正确的型号 5.打开Hex文件 ,File-Open Data File,选择目标Hex文件

    2024年02月06日
    浏览(27)
  • J-Flash J-Link解锁GD32单片机

    提示:本篇所使用的工具为J-Link V9,软件为J-Flash V6.48b 当给GD32单片机Flash上锁,即配置安全保护后,单片机无法再通过keil、J-Link工具J-Flash烧录程序, 可通过J-Link STM32 Unlock或者J-Flash两种方法都可以。 我们先来用J-Link命令行验证下是否是真的开启了安全保护的 打开J-Link Comm

    2024年02月21日
    浏览(33)
  • J-LINK J-FLASH 下载STM32单片机程序使用教程

    J-LINK J-FLASH 下载程序使用教程 -V1.0 2023.05.09 Introduction 本教程用于演示如何使用JLINK V9配套J-FLASH软件烧写更新MCU程序的教程 安装提供的 JLINK驱动程序。JLink_Windows_V722a.rar。全程默认安装,只能安装在C盘,不可安装于其它盘。 安装驱动完成后,将JLINK V9仿真器上USB口插上电脑,

    2024年02月11日
    浏览(34)
  • Qt动态调用(外部调用)SDK库(dll动态库)

    ​​​​​​​  之前的文章,介绍了Qt调用sdk库的方式,大家可以点击查看(Q调用SDK库(dll动态库))之前的文章,里面介绍的调用方式就是静态的调用方式。如下图所示,需要提供.h文件,dll文件和lib文件。  本篇文章主要实现通过一个dll文件,外部动态调用的方式来调用我

    2024年02月09日
    浏览(32)
  • QT笔记——vs + qt 创建一个带界面的 dll 和 调用带界面的dll

    用VS + qt 生成dll 和 调用生成的dll 环境: vs 2019 + qt 5.12 创建dll 上一次我们是直接使用的qt 为我们提供方便的类库创建 ,这次呢 我们自己写类库 第一步:创建一个QT应用程序 第二步,修改程序为 生成dll 并且 我修改了生成的所在路径 添加QT的模块 ,自己用了什么,就添加什

    2024年02月03日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包