概述
作者一直有一个想法,就是写一个功能强大的桌面小工具,里面集成各种平时开发要用的工具。例如:串口助手,网络助手,下载工具等。那么如何也带来几个问题:
问题1:那么如何呈现在桌面上也是一个非常重要的问题 -- 桌面悬浮窗。
问题2:工具的名字 -- rtool(米饭工具)
问题3:是否贡献整个工具 -- 分为两个版本:开源版本和公司项目版本(已经发布了v1.0版本)。
本篇文章介绍rtool的jlink烧录小工具,那为什么要在rtool中集成jlink的烧录工具呢?原因:
像mcu,我们如果使用gcc构建我们的程序后,没有ide的支撑,就需要使用jflash这样的工具进行烧录,这个操作流程还是挺多步骤的。
方便我们对固件进行动手术,如对固件进行加密处理,对芯片ram,flash进行随心所欲的操作。
原理说明
我们在使用jflash烧录工具时,实际jflash是通过调用jlinkarm.dll动态库提供的接口进行操作的。那么我们可以通过dependency walker对jlinkarm.dll进行分析。获取到dll库中所有函数符号。
qt提供了qlibrary类可以动态加载dll,所以结合获取的函数符号,我们可以定义一些列函数指针指向对应的符号。
开发流程
ui设计,实际可以很简单,目的也是简化jflash的操作流程:
定义对接动态库jlinkarm.dll的一系列函数指针,头文件rjlinkarm.h:
#ifndef rjlinkarmh#define rjlinkarmh//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 2typedef bool (*rjlinkopenfunc)(void);typedef void (*rjlinkclosefunc)(void);typedef bool (*rjlinkisopenfunc)(void);typedef unsigned int (*rjlinktifselectfunc)(int);typedef void (*rjlinksetspeedfunc)(int);typedef unsigned int (*rjlinkgetspeedfunc)(void);typedef void (*rjlinkresetfunc)(void);typedef int (*rjlinkhaltfunc)(void);typedef void (*rjlinkgofunc)(void);typedef int (*rjlinkreadmemfunc)(unsigned int addr, int len, void *buf);typedef int (*rjlinkwritememfunc)(unsigned int addr, int len, void *buf);typedef int (*rjlinkwriteu8func)(unsigned int addr, unsigned char data);typedef int (*rjlinkwriteu16func)(unsigned int addr, unsigned short data);typedef int (*rjlinkwriteu32func)(unsigned int addr, unsigned int data);typedef int (*rjlinkerasechipfunc)(void);typedef int (*rjlinkdownloadfilefunc)(const char *file, unsigned int addr);typedef void (*rjlinkbegindownloadfunc)(int index);typedef void (*rjlinkenddownloadfunc)(void);typedef bool (*rjlinkexeccommandfunc)(const char* cmd, int a, int b);typedef unsigned int (*rjlinkreadregfunc)(int index);typedef int (*rjlinkwriteregfunc)(int index, unsigned int data);typedef void (*rjlinksetlogfilefunc)(char *file);typedef unsigned int (*rjlinkgetdllversionfunc)(void);typedef unsigned int (*rjlinkgethardwareversionfunc)(void);typedef unsigned int (*rjlinkgetfirmwarestringfunc)(char *buff, int count);typedef unsigned int (*rjlinkgetsnfunc)(void);typedef unsigned int (*rjlinkgetidfunc)(void);typedef bool (*rjlinkconnectfunc)(void);typedef bool (*rjlinkisconnectedfunc)(void);#endif // rjlinkarmh
通过qt提供了qlibrary类加载dll,然后函数指针指向对应的函数符号:
通过头文件rjlinkarm.h定义的函数指针类型定义对应的变量:
private: rjlinkopenfunc rjlinkopenfuncptr = null; rjlinkclosefunc rjlinkclosefuncptr = null; rjlinkisopenfunc rjlinkisopenfuncptr = null; rjlinktifselectfunc rjlinktifselectfuncptr = null; rjlinksetspeedfunc rjlinksetspeedfuncptr = null; rjlinkgetspeedfunc rjlinkgetspeedfuncptr = null; rjlinkresetfunc rjlinkresetfuncptr = null; rjlinkhaltfunc rjlinkhaltfuncptr = null; rjlinkgofunc rjlinkgofuncptr = null; rjlinkreadmemfunc rjlinkreadmemfuncptr = null; rjlinkwritememfunc rjlinkwritememfuncptr = null; rjlinkwriteu8func rjlinkwriteu8funcptr = null; rjlinkwriteu16func rjlinkwriteu16funcptr = null; rjlinkwriteu32func rjlinkwriteu32funcptr = null; rjlinkerasechipfunc rjlinkerasechipfuncptr = null; rjlinkdownloadfilefunc rjlinkdownloadfilefuncptr = null; rjlinkbegindownloadfunc rjlinkbegindownloadfuncptr = null; rjlinkenddownloadfunc rjlinkenddownloadfuncptr = null; rjlinkexeccommandfunc rjlinkexeccommandfuncptr = null; rjlinkreadregfunc rjlinkreadregfuncptr = null; rjlinkwriteregfunc rjlinkwriteregfuncptr = null; rjlinksetlogfilefunc rjlinksetlogfilefuncptr = null; rjlinkgetdllversionfunc rjlinkgetdllversionfuncptr = null; rjlinkgethardwareversionfunc rjlinkgethardwareversionfuncptr = null; rjlinkgetfirmwarestringfunc rjlinkgetfirmwarestringfuncptr = null; rjlinkgetsnfunc rjlinkgetsnfuncptr = null; rjlinkgetidfunc rjlinkgetidfuncptr = null; rjlinkconnectfunc rjlinkconnectfuncptr = null; rjlinkisconnectedfunc rjlinkisconnectedfuncptr = null;
通过动态库(jlinkarm.dll)获取对应的函数指针
void rjlinkview::jlinklibloadhandle(void){ jlinklib = new qlibrary(jlinkarm.dll); if(jlinklib->load()) { rjlinkopenfuncptr = (rjlinkopenfunc)jlinklib->resolve(jlinkarm_open); // 打开设备 rjlinkclosefuncptr = (rjlinkclosefunc)jlinklib->resolve(jlinkarm_close); // 关闭设备 rjlinkisopenfuncptr = (rjlinkisopenfunc)jlinklib->resolve(jlinkarm_isopen); // 判断设备是否打开 rjlinktifselectfuncptr = (rjlinktifselectfunc)jlinklib->resolve(jlinkarm_tif_select); // 选择设备 rjlinksetspeedfuncptr = (rjlinksetspeedfunc)jlinklib->resolve(jlinkarm_setspeed); // 设置烧录速度 rjlinkgetspeedfuncptr = (rjlinkgetspeedfunc)jlinklib->resolve(jlinkarm_getspeed); // 获取烧录速度 rjlinkresetfuncptr = (rjlinkresetfunc)jlinklib->resolve(jlinkarm_reset); // 复位设备 rjlinkhaltfuncptr = (rjlinkhaltfunc)jlinklib->resolve(jlinkarm_halt); // 中断程序执行 rjlinkreadmemfuncptr = (rjlinkreadmemfunc)jlinklib->resolve(jlinkarm_readmem); // 读取内存 rjlinkwritememfuncptr = (rjlinkwritememfunc)jlinklib->resolve(jlinkarm_writemem); // 写入内存 rjlinkerasechipfuncptr = (rjlinkerasechipfunc)jlinklib->resolve(jlink_erasechip); // 擦除芯片 rjlinkexeccommandfuncptr = (rjlinkexeccommandfunc)jlinklib->resolve(jlinkarm_execcommand); // 执行命令 rjlinkgetdllversionfuncptr = (rjlinkgetdllversionfunc)jlinklib->resolve(jlinkarm_getdllversion); // 获取dll版本号 rjlinkgetsnfuncptr = (rjlinkgetsnfunc)jlinklib->resolve(jlinkarm_getsn); // 获取sn号 rjlinkgetidfuncptr = (rjlinkgetidfunc)jlinklib->resolve(jlinkarm_getid); // 获取id rjlinkconnectfuncptr = (rjlinkconnectfunc)jlinklib->resolve(jlinkarm_connect); // 连接设备 rjlinkisconnectedfuncptr = (rjlinkisconnectedfunc)jlinklib->resolve(jlinkarm_isconnected); // 判断是否连接设备 }}
上述的函数指针,其实对访问操作一点也不友好,所以通过类方法重新对其封装,也避免的空指针的访问:
bool rjlinkview::jlinkopen(void){ if(rjlinkopenfuncptr){ return rjlinkopenfuncptr(); } return false;}void rjlinkview::jlinkclose(void){ if(rjlinkclosefuncptr){ rjlinkclosefuncptr(); }}bool rjlinkview::jlinkisopen(void){ if(rjlinkisopenfuncptr){ return rjlinkisopenfuncptr(); } return false;}unsigned int rjlinkview::jlinktifselectfunc(int type){ if(rjlinktifselectfuncptr){ return rjlinktifselectfuncptr(type); } return false;}void rjlinkview::jlinksetspeedfunc(unsigned int speed){ if(rjlinksetspeedfuncptr){ rjlinksetspeedfuncptr(speed); }}unsigned int rjlinkview::jlinkgetspeedfunc(void){ if(rjlinkgetspeedfuncptr){ return rjlinkgetspeedfuncptr(); } return 0;}void rjlinkview::jlinkresetfunc(void){ if(rjlinkresetfuncptr){ rjlinkresetfuncptr(); }}int rjlinkview::jlinkhaltfunc(void){ if(rjlinkhaltfuncptr){ return rjlinkhaltfuncptr(); } return 0;}int rjlinkview::jlinkreadmemfunc(unsigned int addr, int len, void *buf){ if(rjlinkreadmemfuncptr){ return rjlinkreadmemfuncptr(addr, len, buf); } return 0;}int rjlinkview::jlinkwritememfunc(unsigned int addr, int len, void *buf){ if(rjlinkwritememfuncptr){ return rjlinkwritememfuncptr(addr, len, buf); } return 0;}int rjlinkview::jlinkerasechipfunc(void){ if(rjlinkerasechipfuncptr){ return rjlinkerasechipfuncptr(); } return 0;}bool rjlinkview::jlinkexeccommandfunc(const char *cmd, int a, int b){ if(rjlinkexeccommandfuncptr){ return rjlinkexeccommandfuncptr(cmd, a, b); } return false;}unsigned int rjlinkview::jlinkgetdllversionfunc(void){ if(rjlinkgetdllversionfuncptr){ return rjlinkgetdllversionfuncptr(); } return 0;}unsigned int rjlinkview::jlinkgetsnfunc(void){ if(rjlinkgetsnfuncptr){ return rjlinkgetsnfuncptr(); } return 0;}unsigned int rjlinkview::jlinkgetidfunc(void){ if(rjlinkgetidfuncptr){ return rjlinkgetidfuncptr(); } return 0;}bool rjlinkview::jlinkconnectfunc(void){ if(rjlinkconnectfuncptr){ return rjlinkconnectfuncptr(); } return false;}bool rjlinkview::jlinkisconnectedfunc(void){ if(rjlinkisconnectedfuncptr){ return rjlinkisconnectedfuncptr(); } return false;}
下载信息窗体的显示格式设置,为了方便预览我们的操作步骤,所以规定一个显示格式,增加时间戳的显示:
void rjlinkview::infoshowhandle(qstring info){ qstring timestamp = qdatetime::currentdatetime().tostring([hhss.zzz]==> ); ui->burninfotextbrowser->append(timestamp + info);}
连接芯片设备,以下是按照stm32f407ig为例,下载方式采用swd,下载速度未4000khz:
bool rjlinkview::jlinkconnecthandle(void){ if(jlinkisopen()) { infoshowhandle(tr(设备连接成功)); return true; } jlinkopen(); if(jlinkisopen()) { jlinkexeccommandfunc(device = stm32f407ig, 0, 0); jlinktifselectfunc(jlinkarm_tif_swd); jlinksetspeedfunc(4000); jlinkconnectfunc(); if(jlinkisconnectedfunc()){ infoshowhandle(tr(设备连接成功)); return true; }else { infoshowhandle(tr(连接设备失败! 请检查设备连接...)); } } else { infoshowhandle(tr(连接设备失败! 请检查烧录器连接...)); } return false;}
断开芯片设备:
void rjlinkview::jlinkdisconnecthandle(void){ jlinkclose(); if(!jlinkisopen()) { infoshowhandle(tr(断开设备成功!)); } else { infoshowhandle(tr(断开设备失败...)); }}
获取cpu id,原理其实很简单,通过读取对应uuid寄存器,就可以获取过去到芯片的uuid
qstring rjlinkview::jlinkgetcpuidhandle(void){ unsigned char cpuid[12]={0}; char cpuidtemp[128]={0}; jlinkreadmemfunc(0x1fff7a10, 12, cpuid); sprintf(cpuidtemp, %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]); return qstring(cpuidtemp);}
选择固件按钮实现,这里指定了文件后缀为.bin格式:
void rjlinkview::on_fwfilepathselectpushbutton_clicked(){ qstring fwfilename = qfiledialog::getopenfilename(this, firmware file, qcoreapplication::applicationdirpath(), fw file(*.bin)); ui->fwfilepathlineedit->settext(fwfilename);}
清除信息显示窗体按钮实现:
void rjlinkview::on_cleaninfopushbutton_clicked(){ ui->burninfotextbrowser->clear();}
获取芯片id按钮实现,需要先连接上设备,然后调用jlinkgetcpuidhandle方案获取id之后,断开连接:
void rjlinkview::on_getcpuidpushbutton_clicked(){ if(jlinkconnecthandle()) { infoshowhandle(tr(获取cpu id中,请稍后...)); infoshowhandle(tr(获取cpuid成功: ) + jlinkgetcpuidhandle()); jlinkdisconnecthandle(); }}
同理,擦除flash也是要先连接上设备,然后通过调用jlinkerasechipfunc方法进行擦除之后,断开连接:
void rjlinkview::on_clearflashpushbutton_clicked(){ if(jlinkconnecthandle()) { infoshowhandle(tr(擦除flash中,请稍后...)); jlinkerasechipfunc(); infoshowhandle(tr(擦除flash成功!)); jlinkdisconnecthandle(); }}
一键烧录按钮原理,获取固件内容存储到一个缓冲区中,然后启动一个定时器,然后将固件内容搬运到对应的flash区域:
void rjlinkview::on_burnpushbutton_clicked(){ bool burnaddrisok = false; qfile burnfile; burnfilesize = 0; burnfilecontent.clear(); if(ui->fwfilepathlineedit->text() == tr()) { infoshowhandle(tr(请选择要烧录的固件!)); return; } if(jlinkconnecthandle()) { burnaddr = ui->burnaddrlineedit->text().trimmed().toint(&burnaddrisok, 16); if(!burnaddrisok) { infoshowhandle(tr(烧录起始地址格式有误,请正确输入地址...)); jlinkdisconnecthandle(); return; } burnfile.setfilename(ui->fwfilepathlineedit->text()); burnfile.open(qiodevice::readonly); if(burnfile.isopen()) { burnfilecontent = burnfile.readall(); burnfilesize = burnfilecontent.size(); burnfile.close(); infoshowhandle(tr(开始烧录固件, 请稍后...)); burnfiletimer->start(rjlink_burn_time_interval); } else { infoshowhandle(tr(烧录固件打开失败,请检查文件是否存在...)); jlinkdisconnecthandle(); } }}
定时器处理函数,每次烧录从缓冲区提取1k数据进行烧录:
void rjlinkview::burnfiletimerhandle(void){ int burnpercent = 0; if(burnfiletimer) { burnfiletimer->stop(); if(burnfilecontent.isempty()) { ui->burnprogressbar->setvalue(100); jlinkdisconnecthandle(); infoshowhandle(tr(烧录完成!)); return; } else { if(burnfilecontent.size() > rjlink_burn_content_size) // 每次搬运1k数据 { jlinkwritememfunc(burnaddr, rjlink_burn_content_size, burnfilecontent.data()); burnaddr += rjlink_burn_content_size; burnfilecontent.remove(0, rjlink_burn_content_size); } else { jlinkwritememfunc(burnaddr, burnfilecontent.size(), burnfilecontent.data()); burnaddr += burnfilecontent.size(); burnfilecontent.clear(); } burnpercent = (burnfilesize - burnfilecontent.size()) * 100 / burnfilesize; ui->burnprogressbar->setvalue(burnpercent); burnfiletimer->start(rjlink_burn_time_interval); } }}
演示实例
获取cpu id演示,点击获取cpu id按钮,在显示窗体便可以看到对应的id:
擦除flash演示,点击擦除flash按钮,会调用segger应用,然后进行flash擦除:
烧录程序,点击一键烧录按钮,同样会调用segger应用,然后进行烧录:
目标检测YOLO系列算法的发展过程
后疫情时代智慧医院建设更应强调人、财、物的精细化管理
电感元件的作用_电感元件的特性是什么
三星未获得代工骁龙865芯片的机会,采用台积电的7n“N7P”工艺
电气工程与智能电网迫切需要攻克这些核心技术
QT编写一个JLINK烧录工具
航顺主流级32位MCU系列的MCU-HK32C0家族介绍
电力系统用单相逆变电源的研制
嵌入式技术的应用与就业方向
新起点,新征程,WAYON维安DCDC炼成之路
鸿利智汇Mini LED显示屏获CDIA年度最佳显示模组组件产品奖
VC供应瓶颈将成为LFP电池市场进一步增长的障碍
英特尔描绘融入AI的未来折叠屏PC
TSMC OIP创新平台-EDA认证工具
8吋晶圆产能吃紧,已确定会涨价
华为提出Sorted LLaMA:SoFT代替SFT,训练多合一大语言模型
什么是串扰?PCB走线串扰详解
重磅!成立仅一年的新公司凭什么私有化上市公司先进半导体?
高效节能台式机首次纳入“节能补贴”范围
福特将增加电动汽车投资?新投资额是此前的1倍以上