概述
作者一直有一個想法,就是寫一個功能強大的桌面小工具,里面集成各種平時開發要用的工具。例如:串口助手,網絡助手,下載工具等。那么如何也帶來幾個問題:
問題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:
#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:
擦除flash演示,點擊"擦除flash"按鈕,會調用SEGGER應用,然后進行flash擦除:
燒錄程序,點擊"一鍵燒錄"按鈕,同樣會調用SEGGER應用,然后進行燒錄:
審核編輯:劉清
-
FlaSh
+關注
關注
10文章
1623瀏覽量
147781 -
GCC
+關注
關注
0文章
105瀏覽量
24822 -
Jlink
+關注
關注
5文章
111瀏覽量
37644
原文標題:QT編寫一個JLINK燒錄工具
文章出處:【微信號:風火輪技術團隊,微信公眾號:風火輪技術團隊】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論