由于工作的需要最近写了一个串口服务器。记录一下方便自己以后学习。
开发环境:window10 企业版操作系统
vs2013编译器
MFC应用程序
首先说下开发流程。见下图
说明:
第一步:中控机发送指令到设备
第二步:设备收到指令解析后发送相应指令到单片机
第三步:单片机收到指令做相应的动作
例如:中控机通过串口1发送指令READLEDON
设备串口2收到指令后解析通过串口3发送指令READLENON到单片机
单片机串口1收到READLEDON立即点亮红色LED灯
中控机 设备机 单片机
com1------------------------------------->com2
com3---------------------------------------->com1
代码解释:
此为设备端的服务器源码
BOOL CTESTListenCMDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
SetIcon(m_hIcon, FALSE);// 设置大图标
SetIcon(m_hIcon, TRUE);// 设置小图标
/*初始化串口句柄*/
hDeviceCom = INVALID_HANDLE_VALUE;
hConnectMasConCom = INVALID_HANDLE_VALUE;
DeviceComNumber = ::GetPrivateProfileIntA("DevicePortSetting", _T("DeviceComNumber"), 4,path);CString DeviceComName;DeviceComName.Format("COM%d",DeviceComNumber);m_device_com.SetWindowText(DeviceComName);
/*从ini文件获取串口号*/
ConnectMCComNumber=::GetPrivateProfileIntA("ConnectMCComPortSetting", _T("ConnectMCComNumber"), 4,path);
CString CMCComName;
armComName.Format("COM%d",CMCComNumber);
m_arm_com.SetWindowText(CMCComName);
if (initConnectMC() != 0){
AfxMessageBox(_T("初始化串口失败"));
}
if (!(m_ConnectMCCom_Thread = AfxBeginThread(RecvCMCComThread, this))){
AfxMessageBox(_T("创建线程失败"));
}
return TRUE;
}
串口初始化源码
int CListenMasConComMsgDlg::ComInit(void)
{
DCB dcb;
CString mesg;
COMMTIMEOUTS commtimeout;
CString strCom;
if(ComNumber < 1)
return 1;
if(ComNumber < 9)
strCom.Format("COM%d", ComNumber);
else
strCom.Format("\\\\.\\COM%d" , ComNumber);
hbox = ::CreateFile(strCom,
GENERIC_READ | GENERIC_WRITE,//允许读写
0, //独享方式
NULL,
OPEN_EXISTING,//打开已经存在的串口而不创建串口
0, //同步方式OVERLAPPED
NULL);
if(hbox == INVALID_HANDLE_VALUE){
AfxMessageBox(_T("打开音频设备串口失败"));
return 1;
}
if (!(SetupComm(hbox, 4096, 4096))){
CloseHandle(hbox);
hbox = INVALID_HANDLE_VALUE;
AfxMessageBox(_T("音频设备串口缓存区设置失败"));
return 1;
}
/*commtimeout结构体参数初始化*/
commtimeout.ReadIntervalTimeout = 20;
commtimeout.ReadTotalTimeoutMultiplier = 0;
commtimeout.ReadTotalTimeoutConstant = 400;
commtimeout.WriteTotalTimeoutMultiplier = 400;
commtimeout.WriteTotalTimeoutConstant = 0;
if (!SetCommTimeouts(hbox, &commtimeout)) {
CloseHandle(hbox);
hbox = INVALID_HANDLE_VALUE;
AfxMessageBox(_T("设置音频设备串口超时失败"));
return 1;
}
/*获取串口状态数据非常重要----这里有一段啰嗦而沉痛的记忆----对方的疏忽自己的大意*/
if (!GetCommState(hbox, &dcb)) {
CloseHandle(hbox);
hbox = INVALID_HANDLE_VALUE;
AfxMessageBox(_T("获取音频设备串口设置错误"));
return 1;
}
/*dcb结构体初始化*/
dcb.BaudRate = CBR_9600;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
if (!SetCommState(hbox, &dcb)) {
CloseHandle(hbox);
hbox = INVALID_HANDLE_VALUE;
AfxMessageBox(_T("设置音频设备串口错误"));
return 1;
}
//清空收发缓冲区
if (FALSE ==PurgeComm(hbox, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT)){
CloseHandle(hbox);
hbox = INVALID_HANDLE_VALUE;
}
return 0;
}
线程接收数据处理数据
UINT CListenMasConComMsgDlg::RecvMasConComThread(LPVOID pParam)
{
CListenMasConComMsgDlg *port = (CListenMasConComMsgDlg*)pParam;
CString strRead = "";
char sn[20];
int state;
CString num;
while (1)
{
/*关闭exe线程自动退出资源自动释放*/
strRead = port->read_iron_msg();
if (!(strRead.CompareNoCase(_T("-1")) == 0)){
port->fun_write_log(_T("收到控制指令:")+strRead);
if (strRead.Find(_T("DEOPEN"))>=0){
if (port->DEOpenBox() == 0) {
port->fun_write_log(_T("打开设备抽屉成功"));
port->send_iron_msg(_T("OPEN\r"));
}
else{
port->fun_write_log(_T("打开设备抽屉失败"));
port->send_iron_msg(_T("ERR\r"));
}
}
else if (strRead.Find(_T("DECLOSE"))>=0){
if(port->DECloseBox()==0){
port->fun_write_log(_T("关闭设备抽屉成功"));
port->send_iron_msg(_T("DECLOSED\r"));
}
else{
port->fun_write_log(_T("关闭设备抽屉失败"));
port->send_iron_msg(_T("DEERR\r"));
}
}
else if (strRead.Find(_T("DEMODEL?"))>=0){
int len;
if (port->iron_get_verion(sn, &len) == 0){
sn[len] = '\0';
num.Format(_T("%s"), sn);
port->fun_write_log(_T("测试设备编号 ")+num);
port->send_iron_msg(num);
}
}
else if (strRead.Find(_T("DEDOOR?"))>=0){
state = port->iron_get_status();
switch(state){
case 0:
port->fun_write_log(_T("获取设备抽屉状态成功:OPEN"));
port->send_iron_msg(_T("OPEN\r"));
break;
case 1:
port->fun_write_log(_T("获取设备抽屉状态成功:CLOSED"));
port->send_iron_msg(_T("CLOSED\r"));
break;
case 2:
port->fun_write_log(_T("获取设备抽屉状态成功:MID"));
port->send_iron_msg(_T("MID\r"));
break;
case -1:
port->fun_write_log(_T("获取设备抽屉状态失败"));
port->send_iron_msg(_T("ERR\r"));
break;
}
}
else {
port->fun_write_log(_T("错误指令:")+strRead);
}
strRead.Empty();
}
//Sleep(100);
}
return 0;
}
总结:我第一次给别人分析源码就是在初始化DCB结构体上出现了问题。希望今后我记住这个教训。
那个朋友有这方面的需求联系我。我可以把源码给你 [email protected]