四、PC端上位機(jī)的開(kāi)發(fā)
用C#開(kāi)發(fā)上位機(jī)是工控領(lǐng)域比較好的選擇,因?yàn)镃#與C/C++的語(yǔ)法比較相似,且編譯出來(lái)的代碼比Python小的多,速度也快。與C++比起來(lái),C#自動(dòng)釋放資源回收垃圾,不容易產(chǎn)生內(nèi)存泄漏。
上位機(jī)要實(shí)現(xiàn)的功能在這個(gè)項(xiàng)目中比較簡(jiǎn)單,分為兩部分:
-
- out格式文件的處理
- CAN數(shù)據(jù)的收發(fā)
作者用的是Visual Studio 2022。首先是實(shí)現(xiàn)周立功CAN盒的收發(fā)功能。周立功的CAN盒是支持二次開(kāi)發(fā)的。因此,可以從周立功的官方網(wǎng)站https://manual.zlg.cn/web/#/61/5359上下載對(duì)應(yīng)的開(kāi)發(fā)庫(kù)。
首先,將周立功的CAN盒的支持庫(kù)放入C#工程文件夾中,同時(shí)還要將hex2000.exe放入,方便直接調(diào)用。
做完這些以后,開(kāi)始進(jìn)行上位機(jī)界面的設(shè)計(jì)。
然后看一下C#文件夾的結(jié)構(gòu)
先看"zlgcan.cs"文件。
該文件其實(shí)可以在周立功官方的例程中找到。定義了zlgcan.dll中的一些結(jié)構(gòu)體和方法。注意,因?yàn)閦lgcan.dll是用C++開(kāi)發(fā)的,是非托管代碼。C#中不能直接使用,所以每個(gè)結(jié)構(gòu)體前要用[StructLayout(LayoutKind.Sequential)]注明。同樣在非托管代碼中調(diào)用方法,用下面的方式
接下來(lái),也是官方例程提供的一個(gè)文件recvdatathread.cs,用于CAN的接收。主要功能就是在UI線程之外再開(kāi)辟一個(gè)輔助線程,專(zhuān)門(mén)用于CAN數(shù)據(jù)的接收,防止CAN大數(shù)據(jù)量的接收影響UI界面的操作。
下面先實(shí)現(xiàn)對(duì).out文件的轉(zhuǎn)換功能。
當(dāng)按下“選擇程序源文件”按鍵時(shí),在主窗體文件FormMain.cs中,會(huì)觸發(fā)下面的事件
private void button_ProgramDownload_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "程序源代碼|*.out";
DialogResult result = openFileDialog.ShowDialog();
var openFileName = openFileDialog.FileName;
if (result == DialogResult.OK)
{
textBox_FileName.Text = openFileDialog.FileName;
}
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.FileName = "hex2000.exe"; // 調(diào)用外部程序?qū)?out轉(zhuǎn)換可以傳輸?shù)拇a
process.StartInfo.Arguments = openFileName + " -a -boot -gpio8 -o F28035_FlashBoot.txt";
process.Start();
timer2.Enabled = true; // 要等待1s,在F28035_FlashBoot.txt文件生成后再載入該文件
button_ProgramDownload.Text = "正在刷新";
button_ProgramDownload.Enabled = false;
}
先通過(guò)對(duì)話框,選擇要寫(xiě)入的app文件(.out輸出文件),得到.out文件的路徑和文件名。
然后,通過(guò)實(shí)例化一個(gè)System.Diagnostics.Process類(lèi),就可以調(diào)用外部的hex2000.exe程序。
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.FileName = "hex2000.exe"; // 調(diào)用外部程序?qū)?out轉(zhuǎn)換可以傳輸?shù)拇a
process.StartInfo.Arguments = openFileName + " -a -boot -gpio8 -o F28035_FlashBoot.txt";
process.Start();
注意,hex2000的參數(shù)是"-a -boot -gpio8 -o 指定輸出文件名"。在這里,指定了輸出的文件名,后綴是.txt而不是默認(rèn)的.a00。其實(shí),后綴不重要,可以隨便寫(xiě),最終生成的都是文本文件。
然后,要等待一段時(shí)間,這里作者選擇等待1秒,主要是hex2000轉(zhuǎn)換文件需要一定的時(shí)間。當(dāng)用timer2定時(shí)器定時(shí)1秒到達(dá)后,定義一個(gè)方法來(lái)處理生成的文件。
先將文件讀入內(nèi)存中,再將內(nèi)存文件中的“\r\n”回車(chē)換行符去掉,然后將文件以空格為分隔符轉(zhuǎn)換成一個(gè)整型數(shù)組,同時(shí)得到數(shù)組的長(zhǎng)度。
這時(shí)輸出文件的使命就完成了,釋放文件資源,同時(shí)刪除hex2000生成的文件。
最后使能一個(gè)定時(shí)器,用做CAN收發(fā)狀態(tài)機(jī)FSM()的定時(shí)。
下面,來(lái)實(shí)現(xiàn)CAN的收發(fā)功能。
當(dāng)點(diǎn)擊“連接設(shè)備”按鍵時(shí),會(huì)觸發(fā)CAN盒連接事件。CAN設(shè)備的連接是分下面幾部分構(gòu)成:
- 根據(jù)選擇的設(shè)備類(lèi)型和設(shè)備索引號(hào),通過(guò)zlgcan.dll內(nèi)部的Method.ZCAN_OpenDevice庫(kù)函數(shù),打開(kāi)can設(shè)備,并返回一個(gè)設(shè)備句柄。
- 用Method.ZCAN_SetValue函數(shù)設(shè)置CAN通訊的波特率。
- 根據(jù)先前得到的設(shè)備句柄、波特率,再將CAN的幀類(lèi)型、CAN_ID濾波值、發(fā)送模式做為參數(shù)傳入到zlgcan.dll內(nèi)部的Method.ZCAN_InitCAN初始化函數(shù),當(dāng)成功完成初始化后,會(huì)返回一個(gè)can設(shè)備通道的句柄。
經(jīng)過(guò)上面三個(gè)步驟后,CAN盒就可以收發(fā)數(shù)據(jù)了。
當(dāng)CAN接收到大量數(shù)據(jù)后,處理數(shù)據(jù)的時(shí)間會(huì)變長(zhǎng),將會(huì)影響UI線程的響應(yīng),導(dǎo)致UI界面變的卡頓,降低用戶(hù)的體驗(yàn)。因此,要將CAN的接收函數(shù),重新開(kāi)一個(gè)輔助線程,這樣就不會(huì)影響UI主線程的響應(yīng)。
當(dāng)前面的工作完成后,通過(guò)啟動(dòng)timer1定時(shí)器,定時(shí)執(zhí)行一個(gè)數(shù)據(jù)發(fā)送狀態(tài)機(jī)FSM()。該函數(shù)的作用就是將前面.out文件轉(zhuǎn)換成的數(shù)組的數(shù)據(jù),以段為單位,通過(guò)can總線發(fā)送給MCU,同時(shí)接收MCU返回的數(shù)據(jù)。這個(gè)通訊協(xié)議,可以按照自己方式擬定,并沒(méi)有固定的格式。
最終的效果:
附件中提供上位機(jī)的源碼下載。
程序可以成功連接CAN盒;能夠正常接收發(fā)送數(shù)據(jù);可以調(diào)用hex2000將.out進(jìn)行文件轉(zhuǎn)換生成數(shù)組。但是去掉了CAN數(shù)據(jù)發(fā)送狀態(tài)機(jī)方法FSM(),并不能像上面動(dòng)畫(huà)一樣完成MCU的app刷寫(xiě)。所以讀者需要編寫(xiě)自己的通訊協(xié)議,來(lái)完成app代碼的傳輸。(完)