文章目錄
- 1 完整 功能展示
- 2 添加控件變量及聲明
- 2.1 添加控件及變量
- 2.2 SerialPortDlg.h: 頭文件
- 3 函數實現
- 3.1 數據發送
- 3.1.2 寫數據、字符串轉
- 3.2 發送文件
- 3.2.1 打開文件
- 3.2.2 發送文件
- 3.3 清空發送區
- 4 完整MFC項目項下載
1 完整 功能展示
串口通信助手 頁面展示,功能齊全,
還增加了串口打開/關閉狀態變色,發送按鈕狀態變色等功能。
發送/接收時,相應按鈕,功能禁用/可用等保護措施。
2 添加控件變量及聲明
串口設置等基本功能實現 教程MFC串口助手(一)—初級版(串口設置、初始化、打開/關閉、狀態顯示)
本節將接著上一節教程繼續實現 發送數據 、發送文件
2.1 添加控件及變量
- 添加控件
發送數據的編輯框、按鈕;
打開文件、發送文件 按鈕;
顯示文件路徑靜態文本框;清空發送區 按鈕;
修改ID如下
發送數據
IDC_EDIT_TXDATA
IDC_BUTTON_SEND
//發送文件
IDC_BUTTON_OPEN_FILE
IDC_BUTTON_SEND_FILE
IDC_STATIC_FILE_PATH
//清空發送區
IDC_BUTTON_CLEAN_SEND
- 添加控件變量
(注意下下面 m_sendbutten 控件,類型添加為CButton類型,就可以了。我這里是自己寫的一個可變色CCustomButton類型)
方法:選中控件,右鍵選擇添加變量
//CButton m_sendbutten;
CCustomButton m_sendbutten;
CButton m_transfile;
CButton m_emptySendArea;
CStatic m_sfilePath;
2.2 SerialPortDlg.h: 頭文件
頭文件的其他 變量機函數聲明
(下面是 串口助手 全部功能的頭文件內容)
// SerialPortDlg.h: 頭文件
//#pragma once#include "CCustomButton.h"//UINT ComProce(LPVOID pParam);// CSerialPortDlg 對話框
class CSerialPortDlg : public CDialogEx
{
// 構造
public:CSerialPortDlg(CWnd* pParent = nullptr); // 標準構造函數//變量=====================================================================================
public://自定義變量HANDLE m_hCom; //串口句柄volatile int m_bConnected; //串口連接成功指示BOOL m_COMStatu; //串口狀態指示long m_rxlen; //接收數據個數long m_txlen; //發送數據個數//列表框變量CComboBox m_Combo_Com; //列表框:串口CComboBox m_Combo_Baud; //列表框:波特率CComboBox m_Combo_Check; //列表框:校驗位CComboBox m_Combo_Data; //列表框:數據位CComboBox m_Combo_Stop; //列表框:停止位//字符變量CString m_Str_Com; //字符變量:串口CString m_Str_Baud; //字符變量:波特率CString m_Str_Check; //字符變量:校驗位CString m_Str_Data; //字符變量:數據位CString m_Str_Stop; //字符變量:停止位//函數=====================================================================================
public://串口相關函數BOOL InitComm(); //打開串口(不創建線程)BOOL OpenComm(int Num); //打開串口BOOL SetCommParameter(); //設置串口參數void DisplayStatus(); //顯示串口狀態void CloseConnection(); //關閉串口//按鈕函數afx_msg void OnBnClickedOpencomButton(); //串口狀態顯示//列表框函數afx_msg void OnCbnSelchangeComboCom(); //更改串口afx_msg void OnCbnSelchangeComboBaud(); //更改波特率afx_msg void OnCbnSelchangeComboCheck(); //更改校驗位afx_msg void OnCbnSelchangeComboData(); //更改數據位afx_msg void OnCbnSelchangeComboStop(); //更改停止位// 對話框數據
#ifdef AFX_DESIGN_TIMEenum { IDD = IDD_SERIALPORT_DIALOG };
#endifprotected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持void OnOK();afx_msg LRESULT OnRecvMsg(WPARAM dwEvent, LPARAM dwLen);
// 實現
protected:HICON m_hIcon;// 生成的消息映射函數virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();DECLARE_MESSAGE_MAP()//函數=====================================================================================
public://讀緩沖區int ReadBlock(BYTE* abIn, int MaxLength);//串口消息處理BOOL ProcessCOMMNotification(UINT wParam, long lParam); //串口消息處理//打開串口,創建工作線程(失敗)BOOL OpenComm2(int Num); //打開串口//打開串口,創建工作線程(監聽線程,當有數據到達串口時響應)BOOL OpenComm3(int Num); //打開串口//寫串口int WriteBlock(char* abOut, int MaxLength); //寫串口//字符串轉十六進制int String2Hex(CString str, char* SendOut);//轉十六進制char ConvertHexData(char ch);//顯示當前時間int Current_Time();//發送數據afx_msg void OnBnClickedButtonSend();afx_msg void OnTimer(UINT_PTR nIDEvent);//變量=====================================================================================
public://根據代碼中報錯未定義的變量,自己補充的,有些類型可能錯誤CString m_savefilename;// = _T("D:\\Projects\\MFC\\SerialPort-2\\test.txt");//十六進制顯示和保存到文件控件,添加控件變量CButton m_writetofile;CButton m_ctrlHexDisplay;//給IDC_EDIT_RXDATA編輯框,添加控件變量和值變量CString m_strRXData;CEdit m_EditRXData;CButton m_cHexSend;OVERLAPPED m_osWrite;OVERLAPPED m_osRead;//發送按鈕,添加控件變量// 發送數據、文件//CButton m_sendbutten;CCustomButton m_sendbutten;CButton m_transfile;CButton m_emptySendArea;CStatic m_sfilePath;CButton m_emptyReceiveArea;CMFCButton m_button_ComOpenStatus;afx_msg void OnBnClickedButtonCleanSend();afx_msg void OnBnClickedButtonCleanReceive();//發送文件afx_msg void OnBnClickedButtonOpenFile();afx_msg void OnBnClickedButtonSendFile();afx_msg void OnBnClickedButtonClearCount();afx_msg void OnBnClickedCheckSvaeFile();afx_msg void OnBnClickedButtonSavePath();
};
3 函數實現
#define MAXBLOCK 1024
3.1 數據發送
//數據發送函數
void CSerialPortDlg::OnBnClickedButtonSend()
{// TODO: 在此添加控件通知處理程序代碼char abOut[MAXBLOCK];int OutNum, length;if (!m_bConnected){AfxMessageBox(_T("串口未打開!"));return;}//發送觸發后,發送、清空按鈕 禁用,等待發送完畢后恢復可用m_sendbutten.EnableWindow(FALSE);m_transfile.EnableWindow(FALSE);m_emptyReceiveArea.EnableWindow(FALSE);m_emptySendArea.EnableWindow(FALSE);memset(abOut, 0, MAXBLOCK);//判斷是否文件寫,若是則打開文件并寫入緩沖區//讀文本框內容CString str;CString strtest;GetDlgItem(IDC_EDIT_TXDATA)->GetWindowText(str);char SendOut[MAXBLOCK];int len = str.GetLength();for (int i = 0; i < len; i++){abOut[i] = str.GetAt(i);//strtest.Format(_T("%c"),abOut[i]);//AfxMessageBox(strtest);}if (m_cHexSend.GetCheck()){CString StrHexData;abOut[len] = NULL;StrHexData = CString(abOut);len = String2Hex(StrHexData, SendOut);length = WriteBlock(SendOut, len);}elselength = WriteBlock(abOut, len);m_txlen += length;if(!length)AfxMessageBox(_T("無數據能寫入緩沖區!"));//發送完畢,發送、清空按鈕 恢復可用m_sendbutten.EnableWindow(TRUE);m_transfile.EnableWindow(TRUE);m_emptyReceiveArea.EnableWindow(TRUE);m_emptySendArea.EnableWindow(TRUE);DisplayStatus();return;
}
3.1.2 寫數據、字符串轉
數據發送中涉及兩個函數 :WriteBlock、WriteBlock
//寫串口
int CSerialPortDlg::WriteBlock(char* abOut, int MaxLength)
{BOOL JudgeWrite;//寫入串行端口數據操作的返回值COMSTAT ComStat;//通信狀態緩沖區的指針DWORD dwErrorFlags, dwLength, lentest;//接收錯誤代碼變量的指針,要寫的字節數,被寫入的字節數的變量地址m_osWrite.Offset = 0;ClearCommError(m_hCom, &dwErrorFlags, &ComStat);//清除串行端口錯誤或讀取串行端口現在的狀態==>//串口句柄,接收錯誤代碼變量的指針,通信狀態緩沖區的指針if (dwErrorFlags > 0) //如果接收到錯誤代碼{AfxMessageBox(_T("寫串口錯!請檢查參數設置。"));PurgeComm(m_hCom, PURGE_TXABORT | PURGE_TXCLEAR); //清空緩沖區==>return 0;}dwLength = MaxLength;//要寫的字節數lentest = 0;//實際字節數的指針置0JudgeWrite = WriteFile(m_hCom, abOut, dwLength, &lentest, &m_osWrite); //寫入串行端口數據==>//句柄,預發送的數據,寫入的字節數,被寫入的字節數的變量地址,OVERLAPPED結構體指針(不使用異步傳輸設為null)if (!JudgeWrite)//寫失敗{if (GetLastError() == ERROR_IO_PENDING) //重疊 I/O 操作在進行中。{GetOverlappedResult(m_hCom, &m_osWrite, &lentest, TRUE);//返回重疊操作結果==>//句柄;重疊結構的指針;實際字節數的指針;TRUE,那么只有當操作完成才會返回}elselentest = 0;//實際字節數的指針置0}return lentest;//返回字節數的指針
}
//字符串轉十六進制
int CSerialPortDlg::String2Hex(CString str, char* SendOut)
{int hexdata, lowhexdata;int hexdatalen = 0;int len = str.GetLength();//SendOut.SetSize(len/2);for (int i = 0; i < len;){char lstr, hstr = str[i];if (hstr == ' ' || hstr == '\r' || hstr == '\n'){i++;continue;}i++;if (i >= len)break;lstr = str[i];hexdata = ConvertHexData(hstr);lowhexdata = ConvertHexData(lstr);if ((hexdata == 16) || (lowhexdata == 16))break;elsehexdata = hexdata * 16 + lowhexdata;i++;SendOut[hexdatalen] = (char)hexdata;hexdatalen++;}//senddata.SetSize(hexdatalen);return hexdatalen;
}
3.2 發送文件
3.2.1 打開文件
//打開文件
void CSerialPortDlg::OnBnClickedButtonOpenFile()
{// TODO: 在此添加控件通知處理程序代碼// 設置過濾器TCHAR szFilter[] = _T("文本文件(*.txt)|*.txt|所有文件(*.*)|*.*||");// 構造打開文件對話框 (TRUE創建打開文件對話框)CFileDialog fileDlg(TRUE, _T("txt"), NULL, 0, szFilter, this);CString strFilePath;// 顯示打開文件對話框if (IDOK == fileDlg.DoModal()){// 如果點擊了文件對話框上的“打開”按鈕,則將選擇的文件路徑顯示到編輯框里strFilePath = fileDlg.GetPathName();SetDlgItemText(IDC_STATIC_FILE_PATH, strFilePath);}
}
3.2.2 發送文件
//發送文件
void CSerialPortDlg::OnBnClickedButtonSendFile()
{// TODO: 在此添加控件通知處理程序代碼CString filepath = _T(""); //文件路徑char abOut[MAXBLOCK];int OutNum, length;if (!m_bConnected){AfxMessageBox(_T("串口未打開!"));return;}else {// 如果串口連接成功,串口打開按鈕 顯示綠色,關閉串口m_button_ComOpenStatus.m_bTransparent = FALSE;m_button_ComOpenStatus.m_bDontUseWinXPTheme = TRUE;//消除黑圈//m_button_ComOpenStatus.m_bDrawFocus = FALSE;//消除邊框m_button_ComOpenStatus.m_nFlatStyle = CMFCButton::BUTTONSTYLE_NOBORDERS;//設置顏色m_button_ComOpenStatus.SetFaceColor(RGB(0, 255, 0), true);//串口打開狀態下,顯示綠色,關閉串口m_button_ComOpenStatus.SetWindowTextW(_T("關閉串口"));}//發送觸發后,發送、清空按鈕 禁用,等待發送完畢后恢復可用m_sendbutten.EnableWindow(FALSE);m_transfile.EnableWindow(FALSE);m_emptyReceiveArea.EnableWindow(FALSE);m_emptySendArea.EnableWindow(FALSE);m_sfilePath.GetWindowText(filepath);memset(abOut, 0, MAXBLOCK);//判斷是否文件寫,若是則打開文件并寫入緩沖區if (!filepath.IsEmpty()){CFile fsendout;CFileException e;fsendout.Open(filepath, CFile::modeRead, &e);OutNum = fsendout.Read(abOut, MAXBLOCK);while (OutNum)//執行循環后,文件已讀完。{//如果選擇了十六進制發送,則轉換為字符形式發送出去,否則直接發送字符數組if (m_cHexSend.GetCheck())//十六進制發送復選框選中時{//如果讀到的字符中最后有個單獨的數據,則將讀取長度退回一if (abOut[OutNum - 1] == ' ' || abOut[OutNum - 1] == '\r\n')OutNum--;if (!OutNum){AfxMessageBox(_T("讀十六進制文件出錯,請檢查格式!"));return;}//將十六進制字符串轉換為CString類,為十六進制轉換成字符的函數作準備CString StrHexData;abOut[OutNum] = NULL;StrHexData = CString(abOut);char SendOut[MAXBLOCK];int len = String2Hex(StrHexData, SendOut);length = WriteBlock(SendOut, len);}elselength = WriteBlock(abOut, OutNum);m_txlen += OutNum;OutNum = fsendout.Read(abOut, MAXBLOCK);//準備下一次循環//continue;}//while(OutNum)fsendout.Close();if (!length){AfxMessageBox(_T("已寫完!"));m_sendbutten.EnableWindow(TRUE);//發送完畢,發送、清空按鈕 恢復可用m_sendbutten.EnableWindow(TRUE);m_transfile.EnableWindow(TRUE);m_emptyReceiveArea.EnableWindow(TRUE);m_emptySendArea.EnableWindow(TRUE);DisplayStatus();return;}}//發送完畢,發送、清空按鈕 恢復可用m_sendbutten.EnableWindow(TRUE);m_transfile.EnableWindow(TRUE);m_emptyReceiveArea.EnableWindow(TRUE);m_emptySendArea.EnableWindow(TRUE);DisplayStatus();return;
}
3.3 清空發送區
最簡單的方法直接讓,你編輯框里的內容置為空;
void CSerialPortDlg::OnBnClickedButtonCleanSend()
{// TODO: 在此添加控件通知處理程序代碼//GetDlgItem(IDC_EDIT_TXDATA)->SetWindowText(_T("")); //獲取端口對話框的句柄并清除SetDlgItemText(IDC_EDIT_TXDATA,_T("")); //獲取端口對話框的句柄并清除
}
或者給編輯框添加一個控件變量,
m_strTXData.Empty();m_EditTXData.SetWindowText(m_strRXData);