个人收集和理解所书,转载注明出处
虽然网上很多关于这个的资料了,但还是自己总结下
获取当前可用的串口
可以通过读取注册表获取相应的值。当前可用的COM口在注册表中的位置:
HKEY_LOCAL_MACHINE\\HARDWARE\\DEVICEMAP\\SERIALCOMM
通过枚举来获取所有的COM口。实现如下:
调用举例:
enum_reg_key_value(HKEY_LOCAL_MACHINE, ”HARDWARE\\DEVICEMAP\\SERIALCOMM”);
bool CValueDlg::enum_reg_key_value(HKEY hKey, LPCTSTR lpSubKey)
{
#define MAX_VALUE_NAME 100
HKEY hOpenKey;
long lResult = 0;
CHAR achClass[MAX_PATH];
DWORD cchClassNameLen;
DWORD cMaxClassLen;
DWORD cSubKeysNum;
DWORD cbMaxSubKeyLen;
DWORD cbValuesNum;
DWORD cbMaxValueNameLen;
DWORD cbMaxValueLen;
DWORD cbSecurityDescriptor;
FILETIME ftLastWriteTime;
CHAR achValue[MAX_VALUE_NAME];
DWORD cbValue = MAX_VALUE_NAME;
DWORD DataType;
BYTE Data[255];
DWORD DataLen;
CString StrData = L"";
lResult = RegOpenKeyEx(hKey, lpSubKey, 0, KEY_READ, &hOpenKey);
if(ERROR_SUCCESS != lResult)
{
return FALSE;
}
RegQueryInfoKey(hOpenKey, //已打开的键的句柄
achClass, //类型名称
&cchClassNameLen, //类型名称的长度
NULL, //保留
&cSubKeysNum, // 返回子键的数目
&cbMaxSubKeyLen, // 返回最长的子键长度
&cMaxClassLen, // 返回最长的类长度
&cbValuesNum, // 返回值的数目
&cbMaxValueNameLen, // 返回最长的值项,名称的长度
&cbMaxValueLen, // 返回最长的值的长度
&cbSecurityDescriptor, //返回安全描述
&ftLastWriteTime); // 返回键最后被写入的时间
for (DWORD j = 0, retValue = ERROR_SUCCESS; j < cbValuesNum; j++)
{
cbValue = MAX_VALUE_NAME;
memset(achValue, 0, sizeof(achValue));
memset(Data, 0, sizeof(Data));
StrData = L"";
retValue = RegEnumValue(hOpenKey,
j,
achValue,
&cbValue,
NULL,
&DataType,
Data,
&DataLen);
if (retValue == (DWORD) ERROR_SUCCESS )
{
StrData.Format("%s", Data);
m_ComboComSelect.AddString(StrData);
}
}
RegCloseKey(hOpenKey);
return true;
#undef MAX_VALUE_NAME
}
打开串口
m_CMSComm.SetCommPort(4);
m_CMSComm. SetInBufferSize(1024);
m_CMSComm. SetOutBufferSize(512);
if(!m_CMSComm.GetPortOpen())
{
try
{
m_CMSComm.SetPortOpen(TRUE); //打开串口
}
catch(COleDispatchException *e)//如果所要打开的串口已经被其他程序使用,会报异常
{
AfxMessageBox("Error:The COM has been occupied or isn't Exist");
e = NULL;
return;
}
m_CMSComm.SetInputMode(0); //设置输入方式为Text
m_CMSComm.SetSettings(m_Uart_Struct.settings);
m_CMSComm.SetRThreshold(1); //为1表示接收到一个字符便出发接收事件
m_CMSComm.SetInputLen(0);// 设置当前接收区数据长度为0,表示全部读取
}
else
{
AfxMessageBox("Error:The COM has been occupied or isn't Exist");
}
串口数据发送
关于串口数据的发送网上很多的写法都是利用以下的方法
CByteArray array;
DWORD len = 0;
char TxData [1024 + 1];
CString StrData = L"";
len = StrData.GetLength();
memset(TxData, 0, sizeof(TxData));
strcpy(TxData, StrData);
array.RemoveAll();
array.SetSize(len);
for(DWORD i=0; i<len; i++)
{
array.SetAt(i, TxData);
}
m_CMSComm.SetOutput(COleVariant(array)); …………………………①
其实所有的关键都在于①这条语句,传入的数据类型强制转换为任意数据类型COleVariant,于是将所要发送的字符串直接强制转换也是可以的。以上代码便以下面一条语句即可实现发送数据的操作。
m_CMSComm.SetOutput(COleVariant(StrData));
数据接收
事件2表示接收事件,其他事件所对应的值可以网上查下,很多有介绍了。如
http://blog.ednchina.com/bobi2005/175488/message.aspx
其实这里的关键就在于接收到的数据存在数据类型VARIANT中,关于它的使用也自己查吧。以下是接收的一种写法。
void CValueDlg::OnComm ()
{
VARIANT vResponse;
int rec_data_len;
if(m_CMSComm.GetCommEvent() == 2)
{
rec_data_len = m_CMSComm.GetInBufferCount();
if(rec_data_len > 0)
{
vResponse = m_CMSComm.GetInput();
if(VT_BSTR == vResponse.vt)
{
save_rec_data(CString(vResponse.bstrVal));
}
}
}
return;
}
以下介绍另外两种接收数据写法,注意,这两种写法需要将open函数中的模式设为二进制模式即
m_CMSComm.SetInputMode(1);
方法 一、
void CValueDlg::OnComm ()
{
VARIANT variant_inp;
int rec_data_len;
CString strtemp;
char chData[1024 + 1];
if(m_CMSComm.GetCommEvent() == 2)
{
variant_inp = m_CMSComm.GetInput();
rec_data_len = variant_inp.parray->rgsabound->cElements;
if (rec_data_len > 0)
{
memset(chData, 0, sizeof(chData));
memcpy(chData, (char *)variant_inp.parray->pvData, rec_data_len);
strtemp.Format("%s", chData);
save_rec_data(strtemp);
}
}
return;
}
方法 二、
这种方法是网上很多人转帖的写法,也可以实现接收的功能。如下
void CValueDlg::OnComm ()
{
VARIANT variant_tmp;
COleSafeArray safe_array_tmp;
long length, i;
BYTE data[1024];
CString str ="";
CString strTemp;
if(m_CMSComm.GetCommEvent() == 2)
{
variant_tmp = m_CMSComm.GetInput();
safe_array_tmp = variant_tmp;………………②
length = safe_array_tmp.GetOneDimSize();
for(i=0; i<length; i++)
{
safe_array_tmp.GetElement(&i, data+i); //将数据转换为BYTE型
}
for(i=0; i<length; i++) //将数组转换为Cstring型变量
{
BYTE bt = *(char*)(data+i);
strTemp.Format("%c",bt);
m_strReceiveMsg += strTemp;
……
}
}
}
我最开始看到的也是这种方法,但是和许多的网友一样在执行到②语句的时候,就弹出一个警告框,忽略后还是可以继续运行。还以为是这种写法有错呢,硬着头皮看了VARIANT和COleSafeArray这两个类确定没错,回头仔细检查打开串口的设置发现设置输入模式的地方错了,如果你要用以上这种方法需要将输入模式设置成二进制模式,这样②这条语句便可以正确执行了,不知道其他人是否也很我一样的这样解决的,提出来分享下。
以上三法希望你可以找到合适的。
关闭串口
if(m_CMSComm.GetPortOpen())
{
m_CMSComm.SetPortOpen(FALSE);
}
用控件虽然大大简化,但是如果有台上位机没装VC++程序,那么Release出来的程序也是不能运行的,这里下了个批处理文件,来注册控件,见附件。这样串口通信就OK了。
用户1579310 2014-7-22 09:45
用户377235 2014-5-29 20:16
转载很多的,估计都是看了龚建伟的串口助手v1.0公开的源码
用户1511430 2013-10-5 13:35
用户1280885 2013-4-16 17:07
用户416974 2013-4-16 15:47
用户925091 2011-5-2 17:39