首页 > 行业资讯 > 正文

概述

作者一直有一个想法,就是写一个功能强大的桌面小工具,里面集成各种平时开发要用的工具。例如:串口助手,网络助手,下载工具等。那么如何也带来几个问题:

问题1:那么如何呈现在桌面上也是一个非常重要的问题 — 桌面悬浮窗。

问题2:工具的名字 — RTOOL(米饭工具)

问题3:是否贡献整个工具 — 分为两个版本:开源版本和公司项目版本(已经发布了V1.0版本)。

QT编写一个JLINK烧录工具-jlink烧写bin文件教程

本篇文章介绍RTOOL的JLINK烧录小工具,那为什么要在RTOOL中集成JLINK的烧录工具呢?原因:

像MCU,我们如果使用GCC构建我们的程序后,没有IDE的支撑,就需要使用JFLASH这样的工具进行烧录,这个操作流程还是挺多步骤的。

QT编写一个JLINK烧录工具-jlink烧写bin文件教程1

方便我们对固件进行动手术,如对固件进行加密处理,对芯片ram,flash进行随心所欲的操作。

原理说明

我们在使用JFlash烧录工具时,实际JFlash是通过调用JLinkARM.dll动态库提供的接口进行操作的。那么我们可以通过Dependency walker对JLinkARM.dll进行分析。获取到dll库中所有函数符号。

QT编写一个JLINK烧录工具-jlink烧写bin文件教程2

QT提供了QLibrary类可以动态加载dll,所以结合获取的函数符号,我们可以定义一些列函数指针指向对应的符号。

QT编写一个JLINK烧录工具-jlink烧写bin文件教程3

开发流程

UI设计,实际可以很简单,目的也是简化JFlash的操作流程:

QT编写一个JLINK烧录工具-jlink烧写bin文件教程4

定义对接动态库JLinkARM.dll的一系列函数指针,头文件RJlinkARM.h:

#ifndefRJLINKARMH #defineRJLINKARMH //JLINKTIF #defineJLINKARM_TIF_JTAG0 #defineJLINKARM_TIF_SWD1 #defineJLINKARM_TIF_DBM32 #defineJLINKARM_TIF_FINE3 #defineJLINKARM_TIF_2wire_JTAG_PIC324 //RESETTYPE #defineJLINKARM_RESET_TYPE_NORMAL0 #defineJLINKARM_RESET_TYPE_CORE1 #defineJLINKARM_RESET_TYPE_PIN2 typedefbool(*rjlinkOpenFunc)(void); typedefvoid(*rjlinkCloseFunc)(void); typedefbool(*rjlinkIsOpenFunc)(void); typedefunsignedint(*rjlinkTIFSelectFunc)(int); typedefvoid(*rjlinkSetSpeedFunc)(int); typedefunsignedint(*rjlinkGetSpeedFunc)(void); typedefvoid(*rjlinkResetFunc)(void); typedefint(*rjlinkHaltFunc)(void); typedefvoid(*rjlinkGoFunc)(void); typedefint(*rjlinkReadMemFunc)(unsignedintaddr,intlen,void*buf); typedefint(*rjlinkWriteMemFunc)(unsignedintaddr,intlen,void*buf); typedefint(*rjlinkWriteU8Func)(unsignedintaddr,unsignedchardata); typedefint(*rjlinkWriteU16Func)(unsignedintaddr,unsignedshortdata); typedefint(*rjlinkWriteU32Func)(unsignedintaddr,unsignedintdata); typedefint(*rjlinkEraseChipFunc)(void); typedefint(*rjlinkDownloadFileFunc)(constchar*file,unsignedintaddr); typedefvoid(*rjlinkBeginDownloadFunc)(intindex); typedefvoid(*rjlinkEndDownloadFunc)(void); typedefbool(*rjlinkExecCommandFunc)(constchar*cmd,inta,intb); typedefunsignedint(*rjlinkReadRegFunc)(intindex); typedefint(*rjlinkWriteRegFunc)(intindex,unsignedintdata); typedefvoid(*rjlinkSetLogFileFunc)(char*file); typedefunsignedint(*rjlinkGetDLLVersionFunc)(void); typedefunsignedint(*rjlinkGetHardwareVersionFunc)(void); typedefunsignedint(*rjlinkGetFirmwareStringFunc)(char*buff,intcount); typedefunsignedint(*rjlinkGetSNFunc)(void); typedefunsignedint(*rjlinkGetIdFunc)(void); typedefbool(*rjlinkConnectFunc)(void); typedefbool(*rjlinkIsConnectedFunc)(void); #endif//RJLINKARMH

通过QT提供了QLibrary类加载dll,然后函数指针指向对应的函数符号:

通过头文件RJlinkARM.h定义的函数指针类型定义对应的变量:

private: rjlinkOpenFuncrjlinkOpenFuncPtr=NULL; rjlinkCloseFuncrjlinkCloseFuncPtr=NULL; rjlinkIsOpenFuncrjlinkIsOpenFuncPtr=NULL; rjlinkTIFSelectFuncrjlinkTIFSelectFuncPtr=NULL; rjlinkSetSpeedFuncrjlinkSetSpeedFuncPtr=NULL; rjlinkGetSpeedFuncrjlinkGetSpeedFuncPtr=NULL; rjlinkResetFuncrjlinkResetFuncPtr=NULL; rjlinkHaltFuncrjlinkHaltFuncPtr=NULL; rjlinkGoFuncrjlinkGoFuncPtr=NULL; rjlinkReadMemFuncrjlinkReadMemFuncPtr=NULL; rjlinkWriteMemFuncrjlinkWriteMemFuncPtr=NULL; rjlinkWriteU8FuncrjlinkWriteU8FuncPtr=NULL; rjlinkWriteU16FuncrjlinkWriteU16FuncPtr=NULL; rjlinkWriteU32FuncrjlinkWriteU32FuncPtr=NULL; rjlinkEraseChipFuncrjlinkEraseChipFuncPtr=NULL; rjlinkDownloadFileFuncrjlinkDownloadFileFuncPtr=NULL; rjlinkBeginDownloadFuncrjlinkBeginDownloadFuncPtr=NULL; rjlinkEndDownloadFuncrjlinkEndDownloadFuncPtr=NULL; rjlinkExecCommandFuncrjlinkExecCommandFuncPtr=NULL; rjlinkReadRegFuncrjlinkReadRegFuncPtr=NULL; rjlinkWriteRegFuncrjlinkWriteRegFuncPtr=NULL; rjlinkSetLogFileFuncrjlinkSetLogFileFuncPtr=NULL; rjlinkGetDLLVersionFuncrjlinkGetDLLVersionFuncPtr=NULL; rjlinkGetHardwareVersionFuncrjlinkGetHardwareVersionFuncPtr=NULL; rjlinkGetFirmwareStringFuncrjlinkGetFirmwareStringFuncPtr=NULL; rjlinkGetSNFuncrjlinkGetSNFuncPtr=NULL; rjlinkGetIdFuncrjlinkGetIdFuncPtr=NULL; rjlinkConnectFuncrjlinkConnectFuncPtr=NULL; rjlinkIsConnectedFuncrjlinkIsConnectedFuncPtr=NULL;

通过动态库(JLinkARM.dll)获取对应的函数指针

voidRJLinkView::jlinkLibLoadHandle(void) { jlinkLib=newQLibrary(“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”);//判断是否连接设备 } }

上述的函数指针,其实对访问操作一点也不友好,所以通过类方法重新对其封装,也避免的空指针的访问:

boolRJLinkView::jlinkOpen(void) { if(rjlinkOpenFuncPtr){ returnrjlinkOpenFuncPtr(); } returnfalse; } voidRJLinkView::jlinkClose(void) { if(rjlinkCloseFuncPtr){ rjlinkCloseFuncPtr(); } } boolRJLinkView::jlinkIsOpen(void) { if(rjlinkIsOpenFuncPtr){ returnrjlinkIsOpenFuncPtr(); } returnfalse; } unsignedintRJLinkView::jlinkTIFSelectFunc(inttype) { if(rjlinkTIFSelectFuncPtr){ returnrjlinkTIFSelectFuncPtr(type); } returnfalse; } voidRJLinkView::jlinkSetSpeedFunc(unsignedintspeed) { if(rjlinkSetSpeedFuncPtr){ rjlinkSetSpeedFuncPtr(speed); } } unsignedintRJLinkView::jlinkGetSpeedFunc(void) { if(rjlinkGetSpeedFuncPtr){ returnrjlinkGetSpeedFuncPtr(); } return0; } voidRJLinkView::jlinkResetFunc(void) { if(rjlinkResetFuncPtr){ rjlinkResetFuncPtr(); } } intRJLinkView::jlinkHaltFunc(void) { if(rjlinkHaltFuncPtr){ returnrjlinkHaltFuncPtr(); } return0; } intRJLinkView::jlinkReadMemFunc(unsignedintaddr,intlen,void*buf) { if(rjlinkReadMemFuncPtr){ returnrjlinkReadMemFuncPtr(addr,len,buf); } return0; } intRJLinkView::jlinkWriteMemFunc(unsignedintaddr,intlen,void*buf) { if(rjlinkWriteMemFuncPtr){ returnrjlinkWriteMemFuncPtr(addr,len,buf); } return0; } intRJLinkView::jlinkEraseChipFunc(void) { if(rjlinkEraseChipFuncPtr){ returnrjlinkEraseChipFuncPtr(); } return0; } boolRJLinkView::jlinkExecCommandFunc(constchar*cmd,inta,intb) { if(rjlinkExecCommandFuncPtr){ returnrjlinkExecCommandFuncPtr(cmd,a,b); } returnfalse; } unsignedintRJLinkView::jlinkGetDLLVersionFunc(void) { if(rjlinkGetDLLVersionFuncPtr){ returnrjlinkGetDLLVersionFuncPtr(); } return0; } unsignedintRJLinkView::jlinkGetSNFunc(void) { if(rjlinkGetSNFuncPtr){ returnrjlinkGetSNFuncPtr(); } return0; } unsignedintRJLinkView::jlinkGetIdFunc(void) { if(rjlinkGetIdFuncPtr){ returnrjlinkGetIdFuncPtr(); } return0; } boolRJLinkView::jlinkConnectFunc(void) { if(rjlinkConnectFuncPtr){ returnrjlinkConnectFuncPtr(); } returnfalse; } boolRJLinkView::jlinkIsConnectedFunc(void) { if(rjlinkIsConnectedFuncPtr){ returnrjlinkIsConnectedFuncPtr(); } returnfalse; }

下载信息窗体的显示格式设置,为了方便预览我们的操作步骤,所以规定一个显示格式,增加时间戳的显示:

voidRJLinkView::infoShowHandle(QStringinfo) { QStringtimestamp=QDateTime::currentDateTime().toString(“[hhss.zzz]==>”); ui->burnInfoTextBrowser->append(timestamp+info); }

连接芯片设备,以下是按照STM32F407IG为例,下载方式采用SWD,下载速度未4000kHz:

boolRJLinkView::jlinkConnectHandle(void) { if(jlinkIsOpen()) { infoShowHandle(tr(“设备连接成功”)); returntrue; } jlinkOpen(); if(jlinkIsOpen()) { jlinkExecCommandFunc(“device=STM32F407IG”,0,0); jlinkTIFSelectFunc(JLINKARM_TIF_SWD); jlinkSetSpeedFunc(4000); jlinkConnectFunc(); if(jlinkIsConnectedFunc()){ infoShowHandle(tr(“设备连接成功”)); returntrue; }else { infoShowHandle(tr(“连接设备失败!请检查设备连接…”)); } } else { infoShowHandle(tr(“连接设备失败!请检查烧录器连接…”)); } returnfalse; }

断开芯片设备:

voidRJLinkView::jlinkdisconnectHandle(void) { jlinkClose(); if(!jlinkIsOpen()) { infoShowHandle(tr(“断开设备成功!”)); } else{ infoShowHandle(tr(“断开设备失败…”)); } }

获取CPU ID,原理其实很简单,通过读取对应UUID寄存器,就可以获取过去到芯片的UUID

QStringRJLinkView::jlinkGetCpuIdHandle(void) { unsignedcharcpuId[12]={0}; charcpuIdTemp[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]); returnQString(cpuIdTemp); }

选择固件按钮实现,这里指定了文件后缀为.bin格式:

voidRJLinkView::on_fwFilePathSelectPushButton_clicked() { QStringfwFileName=QFileDialog::getOpenFileName(this,”Firmwarefile”,QCoreApplication::applicationDirPath(),”fwfile(*.bin)”); ui->fwFilePathLineEdit->setText(fwFileName); }

清除信息显示窗体按钮实现:

voidRJLinkView::on_cleanInfoPushButton_clicked() { ui->burnInfoTextBrowser->clear(); }

获取芯片ID按钮实现,需要先连接上设备,然后调用jlinkGetCpuIdHandle方案获取ID之后,断开连接:

voidRJLinkView::on_getCpuIdPushButton_clicked() { if(jlinkConnectHandle()) { infoShowHandle(tr(“获取CPUID中,请稍后…”)); infoShowHandle(tr(“获取CPUID成功:”)+jlinkGetCpuIdHandle()); jlinkdisconnectHandle(); } }

同理,擦除flash也是要先连接上设备,然后通过调用jlinkEraseChipFunc方法进行擦除之后,断开连接:

voidRJLinkView::on_clearFlashPushButton_clicked() { if(jlinkConnectHandle()) { infoShowHandle(tr(“擦除flash中,请稍后…”)); jlinkEraseChipFunc(); infoShowHandle(tr(“擦除flash成功!”)); jlinkdisconnectHandle(); } }

一键烧录按钮原理,获取固件内容存储到一个缓冲区中,然后启动一个定时器,然后将固件内容搬运到对应的Flash区域:

voidRJLinkView::on_burnPushButton_clicked() { boolburnAddrIsOk=false; QFileburnFile; 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数据进行烧录:

voidRJLinkView::burnFileTimerHandle(void) { intburnPercent=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:

QT编写一个JLINK烧录工具-jlink烧写bin文件教程5

擦除flash演示,点击”擦除flash”按钮,会调用SEGGER应用,然后进行flash擦除:

QT编写一个JLINK烧录工具-jlink烧写bin文件教程6

烧录程序,点击”一键烧录”按钮,同样会调用SEGGER应用,然后进行烧录:

QT编写一个JLINK烧录工具-jlink烧写bin文件教程7QT编写一个JLINK烧录工具-jlink烧写bin文件教程8

审核编辑hhy

猜你喜欢