最近需要給一個肌電模塊做一個信號接收和看波形的GUI,沒學過C#也不想用processing之類的工具,想着用mfc做一個
出來。需求是通過串口接收數據。
首先下載Mscomm.ocx和Mscomm.dep兩個文件,放入SysWOW64文件夾(32位系統放sys32不用多說)。接着需要以管理員權限進cmd,
命令regsvr32mscomm32.ocx進行註冊。可以直接找SysWOW64文件夾下的cmd.exe打開,對win7來說方便一些。
mfc的GUI還是挺好寫的。核心是在控件工具箱中右鍵->選擇項->COM組件->MicrosoftCommunications Control勾選後確定
這樣,工具箱中就出現了一個小電話
就是我們要用的串口控制控件。拖進對話框裏,看着很礙眼,但其實運行時是透明的。給他添加變量,類默認選擇CMscomm1。各種讀寫和打開關閉串口的函數都在這個類裏。
CMscomm1中的成員函數分爲兩類,一類put前綴,一類get前綴。put前綴指發送,設置等主動功能,get指讀取,查看等操作。例如put_PortOpen(int n)函數是打開串口COMn(聽說n>10會出bug),get_PortOpen()是返回打開的串口號。
重點並不在於CMscomm1的函數怎麼用,最坑的地方是讀寫數據的格式。get_Input()函數讀回來的格式是VARIANT,這種神奇的格式可以代表char,string,int,short,long以及他們的一二級指針和數組類型。這大概是爲了讓c和matlab,python這些類型不強的語言交互吧…但是mfc自己用起來相當智熄。
VARIANT get_Input()
{
VARIANTresult;
InvokeHelper(0x1a,DISPATCH_PROPERTYGET, VT_VARIANT, (void*)&result, NULL);
returnresult;
}
因爲VARIANT變量不是c標準庫中的類型,因此調試的時候會涉及一大堆奇怪的dll(還有各種連接符號服務器…),對我這種渣渣來說基本是沒法看懂他的單步過程的。所以格式轉換就非常玄學。
VARIANT這種類型,從其定義來看,能看懂他的初衷。他是一個結構體,其中有許多類型的成員變量。大概就是variant.iVal代表對應的short,variant.intVal代表對應的int,variant.cVal代表對應的char等等。
我的目的是將get_Input()的結果轉換爲CString輸出到一個Edit框中,而向PC發串口消息的下位機是以BYTE類型發送的數字(也就是16位ASCII碼的二進制電平)。在網上找了一些別人的代碼,主要有兩種。一種是用safearray進行中繼,轉換爲BYTE數組。
variant_inp= m_ctrlComm.get_Input();
safearray_inp = variant_inp;
len= safearray_inp.GetOneDimSize();
for(k = 0; k < len; k++)
{
safearray_inp.GetElement(&k,&temp);
strTemp.Format(_T("%c"),temp);
RX_Display+= strTemp;
}
但是實際運行時,走到safearray_inp=variant_inp這步之後就直接跳出了,在len=下的斷點根本斷不到。可能冷門的類型是缺一些調試文件吧。最後RX_Display的監視結果是無法讀取。
另一種轉換方式稍微靠譜一些
variant_inp= m_ctrlComm.get_Input();
char *ch = (char*)(unsigned char*)variant_inp.parray->pvdata;
但是Format轉出來,不論是用%d%c%s要不然是一些韓文,奇怪的漢字,要不然就是一串數字(其中有一種可以把發送的第七個BYTE正確傳輸。。。也就是發送0000004可以收到4)。最後各種組合嘗試了一遍,發現這樣能夠正確傳輸一個BYTE
variant_inp= m_ctrlComm.get_Input();
char*ch = (char*)variant_inp.parray;
RX_Display.Format(_T("%s"),ch);
但是這樣只能收到一串數,沒法滿足我發送兩位數三位數的需求。最後只能通過訂傳輸協議解決
variant_inp= m_ctrlComm.get_Input();
char*ch = (char*)variant_inp.parray;
RX_Display.Format(_T("%s"),ch);
RX_Display= RX_Display.Mid(RX_Display.Find('<') + 1);
RX_Display= RX_Display.Left(RX_Display.Find('>'));
讓RX_Display把<>中的數字取出來,但會造成一些數據的丟失。
**1月28日補充:今天打開程序想添加發送功能,結果發現前面改着改着又接不到數據了。折騰了一小時,總算恢復正常。需要補充一條:在使用本文最後給出的方法接收數據時,一定要在串口初始化時,設置put_InputMode(0),也就是用文本方式接收數據,否則接收不到正常的東西。put_InputMode這句也可以不寫,默認是文本方式