原创文章,欢迎转载。转载请注明:转载自 祥的博客
原文链接:http://blog.youkuaiyun.com/humanking7/article/details/48110543
最近要写一个串口程序,用MFC自带COM组件的效果很差(一次接收一个缓冲区的数据,没办法像写嵌入式程序那样单字节就能触发中断接收那样),所以进行了一些研究,发现了一个 第三方串口类 很强大,效果非常好。
资源下载
第三方串口类资源下载:http://download.youkuaiyun.com/detail/humanking7/9064717
步骤详解
编程环境:VS2008
Step1 构建界面
建立一个基于对话框的MFC应用程序,我在这里是 SerialPortTest
,画的界面如下:
)
Step2 添加第三方类
将 SerialPort.h
和 SerialPort.cpp
两个文件复制到工程所在的文件夹中,而且添加到工程里,并在MFC生成的对话框类的头文件中包含 #include "SerialPort.h"
Step3 添加串口响应函数
在我的对话框类中( class CSerialPortTestDlg : public CDialog
在 SerialPortTest.h 头文件中) 添加串口字符接收消息 WM_COMM_RXCHAR
(串口接收缓冲区内有一个字符)的响应函数声明:
public:
//******************************************
afx_msg LONG OnCommunication(WPARAM ch, LPARAM port);//串口接收处理函数
//******************************************
然后再在 SerialPortTest.cpp 中进行 WM_COMM_RXCHAR
消息映射:
BEGIN_MESSAGE_MAP(CSerialPortTestDlg, CDialog)
·········
//******************************************
ON_MESSAGE(WM_COMM_RXCHAR, OnCommunication)//串口接收处理函数
//******************************************
·········
END_MESSAGE_MAP()
最后在 SerialPortTest.cpp 对串口接收响应函数进行实现:
LONG CSerialPortTestDlg::OnCommunication(WPARAM ch, LPARAM port)
{//串口接收 处理函数
······
return 0;
}
Step4 初始化串口
其实就是打开串口和关闭串口
首先 class CSerialPortTestDlg : public CDialog
类加入一个 Public
的成员变量 m_Com
:
CSerialPort m_Com;//串口类
在对对话框的 “打开串口” 的 CButton
控件添加 单击消息处理函数 在里面实现打开和关闭串口的功能
void CSerialPortTestDlg::OnBnClickedBtnOpen()
{//打开&关闭 串口
m_PortName = m_Combox.GetCurSel()+1; //获取串口号
m_Baud = m_Baud_Group[ m_Combox_Baud.GetCurSel() ]; //获取波特率
m_Parity = m_Parity_Group[ m_Combox_Parity.GetCurSel() ];//获取校验位
m_DataBit = m_DataBit_Group[ m_Combox_Data.GetCurSel() ]; //获取数据位
m_StopBit = m_StopBit_Group[ m_Combox_Stop.GetCurSel() ]; //获取停止位
if (m_IsOpenCom)//串口已经打开
{
//关闭串口
m_Com.ClosePort();
m_IsOpenCom = FALSE;
m_Btn_Open.SetWindowText("打开串口");//说明已经关闭了串口
//修改状态
GetDlgItem(IDC_COMBO_PORT)->EnableWindow(TRUE); //允许改
GetDlgItem(IDC_COMBO_BAUD)->EnableWindow(TRUE); //允许改
GetDlgItem(IDC_COMBO_PARITY)->EnableWindow(TRUE);//允许改
GetDlgItem(IDC_COMBO_DATA)->EnableWindow(TRUE); //允许改
GetDlgItem(IDC_COMBO_STOP)->EnableWindow(TRUE); //允许改
}
else//串口已经关闭
{
//if (m_Com.InitPort(this, m_PortName, 9600 ,'N',8, 0))
if (m_Com.InitPort(this, m_PortName, m_Baud ,m_Parity,m_DataBit, m_StopBit))
{ //串口号,波特率,校验位,数据位,停止位为1(在此输入0,代表停止位为1)
//打开串口成功
m_Com.StartMonitoring();
m_IsOpenCom = TRUE;
m_Btn_Open.SetWindowText("关闭串口");//说明已经打开了串口
//修改状态
GetDlgItem(IDC_COMBO_PORT)->EnableWindow(FALSE); //不许改
GetDlgItem(IDC_COMBO_BAUD)->EnableWindow(FALSE); //不许改
GetDlgItem(IDC_COMBO_PARITY)->EnableWindow(FALSE);//不许改
GetDlgItem(IDC_COMBO_DATA)->EnableWindow(FALSE); //不许改
GetDlgItem(IDC_COMBO_STOP)->EnableWindow(FALSE); //不许改
}
else
{//串口打开失败
MessageBox("没有发现此串口或被占用","串口打开失败",MB_ICONWARNING);
}
}
}
Step4 串口发送
至于串口的发送可以调用 CSerialPort
类 中的类成员函数:
void WriteToPort(char* string);
void WriteToPort(char* string,int n); // add by mrlong 2007-12-25
void WriteToPort(LPCTSTR string); // add by mrlong 2007-12-25
void WriteToPort(BYTE* Buffer, int n);// add by mrlong
具体我是这样用的:
//m_String_Send_ASCII 是我CEdit控件绑定的CString的一个成员变量,用于获取发送数据
char* SendBuf;
int length = m_String_Send_ASCII.GetLength();
SendBuf = m_String_Send_ASCII.GetBuffer(length);
m_Com.WriteToPort( SendBuf ); //发送数据
m_String_Send_ASCII.ReleaseBuffer();
出现的错误及其解决
使用这个串口类会遇到这个问题
Run-Time Check Failure #3 - The variable 'comstat' is being used without being initialized
下面是我搜集到的解决方法,屡试不爽(基本用 方案1)
方案1: 改变项目配置属性
一种解决方案是改变基本运行时检查(changing the runtime checks in project settings):在菜单Project->Project properties-> C/C++ -> Code generation-> Basic Runtime checks –> change to ‘Default’,在中文版中是:项目-〉属性-〉配置属性-〉C/C++代码生成-〉基本运行时检查-〉设置为默认,当将基本运行时检查改为默认之后,编译自然顺利通过了,向串口调试助手发送消息正常了,再从串口调试助手向SerialPortTest发送消息时,也正常了。
方案2:改变comstat变量属性
另一种解决方案是将CSerialPort.CPP中的COMSTAT comstat;改为static COMSTAT comstat;这样改了之后,debug顺利通过,然后调试,与串口调试助手相互发消息都OK了。为什么这样就能解决呢?其实当你使用debug解决方案时,它的基本运行时检查初始设置为:两者(/RTC1,等同于 /RTCsu),这个两者是指:堆栈帧(/RTCs),未初始化的变量(/RTCu)。由于它要检查未初始化的变量,所以将SerialPort.cpp中的COMSTAT comstat;改为static COMSTAT comstat;就可以正常使用CSerialPort类了。
以上是在debug下的解决方案,当在Release编译状态时,你会发现并不需要将COMSTAT comstat;改为static COMSTAT comstat;就能编译成功并且发送接收消息正常。其实,在Release下,它的基本运行时检查初始设置已为默认。这应该也算是第一种解决方案之内。
而当你将它设置为两者(/RTC1,等同于 /RTCsu)时,不论是否将COMSTAT comstat;改为static COMSTAT comstat;,编译都不能通过,报错““cl.exe”返回的结果有误。”,这个应该是属于Release的问题了,另当别论了。
方案3:
添加代码:memset(&comstat, 0, sizeof(COMSTAT)); //VC6不用这句也可以用,2008就要加