概述
作者一直有一個想法,就是寫一個功能強(qiáng)大的桌面小工具,里面集成各種平時開發(fā)要用的工具。例如:串口助手,網(wǎng)絡(luò)助手,下載工具等。那么如何也帶來幾個問題:
問題1:那么如何呈現(xiàn)在桌面上也是一個非常重要的問題 -- 桌面懸浮窗。
問題2:工具的名字 -- RTOOL(米飯工具)
問題3:是否貢獻(xiàn)整個工具 -- 分為兩個版本:開源版本和公司項目版本(已經(jīng)發(fā)布了V1.0版本)。

本篇文章介紹RTOOL的JLINK燒錄小工具,那為什么要在RTOOL中集成JLINK的燒錄工具呢?原因:
像MCU,我們?nèi)绻褂肎CC構(gòu)建我們的程序后,沒有IDE的支撐,就需要使用JFLASH這樣的工具進(jìn)行燒錄,這個操作流程還是挺多步驟的。

方便我們對固件進(jìn)行動手術(shù),如對固件進(jìn)行加密處理,對芯片ram,flash進(jìn)行隨心所欲的操作。
原理說明
我們在使用JFlash燒錄工具時,實際JFlash是通過調(diào)用JLinkARM.dll動態(tài)庫提供的接口進(jìn)行操作的。那么我們可以通過Dependency walker對JLinkARM.dll進(jìn)行分析。獲取到dll庫中所有函數(shù)符號。

QT提供了QLibrary類可以動態(tài)加載dll,所以結(jié)合獲取的函數(shù)符號,我們可以定義一些列函數(shù)指針指向?qū)?yīng)的符號。

開發(fā)流程
UI設(shè)計,實際可以很簡單,目的也是簡化JFlash的操作流程:

定義對接動態(tài)庫JLinkARM.dll的一系列函數(shù)指針,頭文件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,然后函數(shù)指針指向?qū)?yīng)的函數(shù)符號:
通過頭文件RJlinkARM.h定義的函數(shù)指針類型定義對應(yīng)的變量:
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;
通過動態(tài)庫(JLinkARM.dll)獲取對應(yīng)的函數(shù)指針
voidRJLinkView::jlinkLibLoadHandle(void)
{
jlinkLib=newQLibrary("JLinkARM.dll");
if(jlinkLib->load())
{
rjlinkOpenFuncPtr=(rjlinkOpenFunc)jlinkLib->resolve("JLINKARM_Open");//打開設(shè)備
rjlinkCloseFuncPtr=(rjlinkCloseFunc)jlinkLib->resolve("JLINKARM_Close");//關(guān)閉設(shè)備
rjlinkIsOpenFuncPtr=(rjlinkIsOpenFunc)jlinkLib->resolve("JLINKARM_IsOpen");//判斷設(shè)備是否打開
rjlinkTIFSelectFuncPtr=(rjlinkTIFSelectFunc)jlinkLib->resolve("JLINKARM_TIF_Select");//選擇設(shè)備
rjlinkSetSpeedFuncPtr=(rjlinkSetSpeedFunc)jlinkLib->resolve("JLINKARM_SetSpeed");//設(shè)置燒錄速度
rjlinkGetSpeedFuncPtr=(rjlinkGetSpeedFunc)jlinkLib->resolve("JLINKARM_GetSpeed");//獲取燒錄速度
rjlinkResetFuncPtr=(rjlinkResetFunc)jlinkLib->resolve("JLINKARM_Reset");//復(fù)位設(shè)備
rjlinkHaltFuncPtr=(rjlinkHaltFunc)jlinkLib->resolve("JLINKARM_Halt");//中斷程序執(zhí)行
rjlinkReadMemFuncPtr=(rjlinkReadMemFunc)jlinkLib->resolve("JLINKARM_ReadMem");//讀取內(nèi)存
rjlinkWriteMemFuncPtr=(rjlinkWriteMemFunc)jlinkLib->resolve("JLINKARM_WriteMem");//寫入內(nèi)存
rjlinkEraseChipFuncPtr=(rjlinkEraseChipFunc)jlinkLib->resolve("JLINK_EraseChip");//擦除芯片
rjlinkExecCommandFuncPtr=(rjlinkExecCommandFunc)jlinkLib->resolve("JLINKARM_ExecCommand");//執(zhí)行命令
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");//連接設(shè)備
rjlinkIsConnectedFuncPtr=(rjlinkIsConnectedFunc)jlinkLib->resolve("JLINKARM_IsConnected");//判斷是否連接設(shè)備
}
}
上述的函數(shù)指針,其實對訪問操作一點(diǎn)也不友好,所以通過類方法重新對其封裝,也避免的空指針的訪問:
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;
}
下載信息窗體的顯示格式設(shè)置,為了方便預(yù)覽我們的操作步驟,所以規(guī)定一個顯示格式,增加時間戳的顯示:
voidRJLinkView::infoShowHandle(QStringinfo)
{
QStringtimestamp=QDateTime::currentDateTime().toString("[hhss.zzz]==>");
ui->burnInfoTextBrowser->append(timestamp+info);
}
連接芯片設(shè)備,以下是按照STM32F407IG為例,下載方式采用SWD,下載速度未4000kHz:
boolRJLinkView::jlinkConnectHandle(void)
{
if(jlinkIsOpen())
{
infoShowHandle(tr("設(shè)備連接成功"));
returntrue;
}
jlinkOpen();
if(jlinkIsOpen())
{
jlinkExecCommandFunc("device=STM32F407IG",0,0);
jlinkTIFSelectFunc(JLINKARM_TIF_SWD);
jlinkSetSpeedFunc(4000);
jlinkConnectFunc();
if(jlinkIsConnectedFunc()){
infoShowHandle(tr("設(shè)備連接成功"));
returntrue;
}else
{
infoShowHandle(tr("連接設(shè)備失敗!請檢查設(shè)備連接..."));
}
}
else
{
infoShowHandle(tr("連接設(shè)備失敗!請檢查燒錄器連接..."));
}
returnfalse;
}
斷開芯片設(shè)備:
voidRJLinkView::jlinkdisconnectHandle(void)
{
jlinkClose();
if(!jlinkIsOpen())
{
infoShowHandle(tr("斷開設(shè)備成功!"));
}
else{
infoShowHandle(tr("斷開設(shè)備失敗..."));
}
}
獲取CPU ID,原理其實很簡單,通過讀取對應(yīng)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);
}
選擇固件按鈕實現(xiàn),這里指定了文件后綴為.bin格式:
voidRJLinkView::on_fwFilePathSelectPushButton_clicked()
{
QStringfwFileName=QFileDialog::getOpenFileName(this,"Firmwarefile",QCoreApplication::applicationDirPath(),"fwfile(*.bin)");
ui->fwFilePathLineEdit->setText(fwFileName);
}
清除信息顯示窗體按鈕實現(xiàn):
voidRJLinkView::on_cleanInfoPushButton_clicked()
{
ui->burnInfoTextBrowser->clear();
}
獲取芯片ID按鈕實現(xiàn),需要先連接上設(shè)備,然后調(diào)用jlinkGetCpuIdHandle方案獲取ID之后,斷開連接:
voidRJLinkView::on_getCpuIdPushButton_clicked()
{
if(jlinkConnectHandle())
{
infoShowHandle(tr("獲取CPUID中,請稍后..."));
infoShowHandle(tr("獲取CPUID成功:")+jlinkGetCpuIdHandle());
jlinkdisconnectHandle();
}
}
同理,擦除flash也是要先連接上設(shè)備,然后通過調(diào)用jlinkEraseChipFunc方法進(jìn)行擦除之后,斷開連接:
voidRJLinkView::on_clearFlashPushButton_clicked()
{
if(jlinkConnectHandle())
{
infoShowHandle(tr("擦除flash中,請稍后..."));
jlinkEraseChipFunc();
infoShowHandle(tr("擦除flash成功!"));
jlinkdisconnectHandle();
}
}
一鍵燒錄按鈕原理,獲取固件內(nèi)容存儲到一個緩沖區(qū)中,然后啟動一個定時器,然后將固件內(nèi)容搬運(yùn)到對應(yīng)的Flash區(qū)域:
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();
}
}
}
定時器處理函數(shù),每次燒錄從緩沖區(qū)提取1k數(shù)據(jù)進(jìn)行燒錄:
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)//每次搬運(yùn)1K數(shù)據(jù)
{
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演示,點(diǎn)擊"獲取CPU ID"按鈕,在顯示窗體便可以看到對應(yīng)的ID:

擦除flash演示,點(diǎn)擊"擦除flash"按鈕,會調(diào)用SEGGER應(yīng)用,然后進(jìn)行flash擦除:

燒錄程序,點(diǎn)擊"一鍵燒錄"按鈕,同樣會調(diào)用SEGGER應(yīng)用,然后進(jìn)行燒錄:


審核編輯hhy
-
芯片
+關(guān)注
關(guān)注
462文章
53538瀏覽量
459157 -
Qt
+關(guān)注
關(guān)注
2文章
318瀏覽量
40301 -
燒錄
+關(guān)注
關(guān)注
8文章
305瀏覽量
36945
發(fā)布評論請先 登錄
編寫第一個QT程序
MCU_JLINK的相關(guān)資料推薦
JLINK-V8固件及燒錄指導(dǎo)教程
Qt圖形編程基礎(chǔ)之使用Qt編寫“Hello,World”程序?qū)嶒?/a>
stm32怎么燒錄程序
使用Qt編寫的串口調(diào)試工具的應(yīng)用程序和源代碼資料免費(fèi)下載
QT 編寫 STC系列MCU燒錄軟件
jlink燒錄軟件_使用 MCU BootUtility 工具來燒錄I.MXRT
單片機(jī)程序生產(chǎn)燒錄工具
使用MCUXpresso直接燒錄hex文件
手把手教你編寫一個上位機(jī)

QT編寫一個JLINK燒錄工具
評論