用Windows API 编写串口通讯程序(二)

     接下去是一步比较关键的操作,建立工作者线程,用来监听串口消息,如果发现inbuf中有接收到的字符,及时通知相应处理函数进行处理。

调用MFC全局函数AfxBeginThread建立线程。好的线程应该短小精悍,所以,我在这个线程里面其实什么事也不做,只是起到通知别的函数的作用。

m_pThread=AfxBeginThread(CommProc,

                         this,

                         THREAD_PRIORITY_NORMAL,

                         0,

                         CREATE_SUSPENDED,// 挂起线程

                         NULL);

m_pThread就是指向我新创建的线程的指针。

线程函数如下,有点长,但是已经是最简单的线程了:

//串口线程

UINT CommProc(LPVOID lParam)

{

       COMSTAT commstat;//这个结构体主要是用来获取端口信息的

       DWORD dwError;

       DWORD dwMask;

       DWORD dwLength;

       OVERLAPPED overlapped;

//OVERLAPPED结构体用来设置I/O异步,具体可以参见MSDN

 

       memset(&overlapped, 0, sizeof(OVERLAPPED));

       //初始化OVERLAPPED对象

 

       overlapped.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);

       //创建CEvent对象

 

       CUartDlg* dlg=(CUartDlg*)lParam;

       if(dlg->m_hCom==NULL)

       {

              AfxMessageBox("串口句柄为空!");

              return -1;

       }

      

       while(dlg->m_bConnected)

       {

              ClearCommError(dlg->m_hCom, &dwError, &commstat);

              if(commstat.cbInQue)

       //如果串口inbuf中有接收到的字符就执行下面的操作

              {

                     WaitForSingleObject(dlg->m_hPostMsgEvent, INFINITE);

                     //无线等待。。。

                     ResetEvent(dlg->m_hPostMsgEvent);

                     //设置CEvent对象为无信号状态

 

                     ::PostMessage(dlg->m_hWnd, WM_COMMSG, EV_RXCHAR, 0);

                     //发送特定信息,用来通知特定函数进行处理

                     continue;

              }

 

              if(!WaitCommEvent(dlg->m_hCom, &dwMask, &overlapped))

              {

                     if(GetLastError()==ERROR_IO_PENDING)

//如果操作被挂起,也就说正在读取或这在写,则进行下面的操作

                            GetOverlappedResult(dlg->m_hCom,

                                &overlapped, &dwLength, TRUE);

                            //无限等待这个I/O操作的完成

                     else

                     {

                            CloseHandle(overlapped.hEvent);

                            return (UINT)-1;

                     }

              }

       }

       CloseHandle(overlapped.hEvent);

       return 0;

}

    因为是多线程,所以,要注意的是对临界资源的访问问题,也就是说互斥问题,避免死锁现象的发生。所以,在这个线程中多次使用了事件对象CEvent,通过它来标志串口有没有被占据,和标志是否正在进行读取串口(串口无法同时进行读写操作)。具体的大家可以看我的代码注释 : )

    这是我对这个线程发出的消息进行处理的函数:

void CUartDlg::OnComMsg(WPARAM wParam, LPARAM lParam)

{

       char buf[MAXBLOCK/4];

       CString str;

       int nLength;

       int nStartChar, nEndChar;

 

       if(!m_bConnected ||      (wParam & EV_RXCHAR)!=EV_RXCHAR)

 // 是否是EV_RXCHAR事件?

       {

              SetEvent(m_hPostMsgEvent);

              // 允许发送下一个线程读取消息

              return;

       }

 

       nLength=ReadComm(buf,100);

       buf[nLength]='\0';

       if(nLength)

       {

    //IDC_EDIT_EDIT是我在一个对话框上一个CEdit控件的ID号,大家可设置成

    //自己的控件ID号

              GetDlgItem(IDC_EDIT_EDIT)->SetFocus();

              CString str(buf);

              m_strMessage+=str;

              UpdateData(FALSE);

              CEdit* pEdit=(CEdit*)GetDlgItem(IDC_EDIT_EDIT);

              pEdit->GetSel(nStartChar, nEndChar);

              pEdit->SetSel(nStartChar-2, nEndChar-2);

             

       }

 

       SetEvent(m_hPostMsgEvent); // 允许发送下一个线程读取消息

}

 

 

    第三步,已经建立好了工作者线程,那么接下去我们就可以进行串口的读写操作了。

DWORD CUartDlg::ReadComm(char *buf, DWORD dwLength)

{

       COMSTAT comstat;

       DWORD dwError;

       DWORD length;

       DWORD dwByteReaded;

      

       ClearCommError(m_hCom, &dwError, &comstat);

       length=min(comstat.cbInQue, dwLength);

       if(!ReadFile(m_hCom, buf, length, &dwByteReaded, &m_osRead))

              return 0;

       return dwByteReaded;

}

 

这是读串口函数;

DWORD CUartDlg::WriteComm(char *buf, DWORD dwLength)

{

       BOOL fState=FALSE;

       DWORD length=0;

       COMSTAT ComStat;

       DWORD dwErrorFlags;

 //ClearCommError是用来清除Comm中的错误,从而可以在下面的代码通过

 //GetLastError抓取错误

       ClearCommError(m_hCom,&dwErrorFlags,&ComStat);

 

       fState=WriteFile(m_hCom,buf,dwLength,&length,&m_osWrite);

 

       if(!fState)

       {

              if(GetLastError()==ERROR_IO_PENDING)

              {

                    SetEvent(m_osWrite.hEvent);

                     while(!GetOverlappedResult(m_hCom,&m_osWrite,&length,TRUE))// 等待

                     {

                            if(GetLastError()==ERROR_IO_INCOMPLETE)

                                   continue;

                     }

              }

              else

                     length=0;

       }

       return length;

}

    这是写串口函数。这两个函数其实本质是一样的,操作过程也近似,大家可以参考着写。

 

    第四步,好了,现在一个串口程序大致上已经完工了,呵呵,是不是挺繁琐?确实,用windows API函数进行硬件层次的编程都是比较繁琐的。还有一点,就是在结束程序的时候,千万不要忘了关闭串口的句柄,否则容易造成内存泄露的问题!

void CUartDlg::OnClose()

{

       // TODO: Add your message handler code here and/or call default

       if(m_bConnected)

       {

              m_bConnected=FALSE;

              SetEvent(m_hPostMsgEvent);

              SetCommMask(m_hCom, 0);

              WaitForSingleObject(m_pThread->m_hThread, INFINITE);

              m_pThread=NULL;

              CloseHandle(m_hCom);

       }

       CDialog::OnClose();

}

 

    总结:串口通讯应用非常广泛,特别是在硬件设计领域,更是没有串口不行。但是VC爱好者中懂串口编程的不多。我想,还是大家比较喜欢上层的东西吧。

在编写这个程序的时候,大体上已经写的差不多了,可以读取ARM机串口发送过来的字符,并显示出来,但是就是不能发送通过串口发送命令给ARM机。我在网上看了好多信息,也查阅了很多相关书籍,仍然没有找到答案。最后只能自己埋头一句代码一句代码得找,找了两天,还是没有找到,人差不多已经到了崩溃的边缘了,最后突然灵感发现,将错误锁定在dcb参数设定上。原来,我在dcb硬件握手参数上设置错了,那个fOutxCtsFlow参数应该设置成FALSE,否则串口读操作将一直处于阻塞状态。当时我差不多想亲吻每一个人,呵呵,程序员常常是带有一点病态的 : ) 希望大家以后不要犯我这样的错误。

    还有,就是因为基于对话框(Dialog-based)的应用程序是不能接收WM_CHAR这个消息的,所以我在进行写串口操作时,重载了PreTranslateMessage这个函数,然后在这个函数内部对消息进行分检处理,起到了很好的效果。大家可以试一试。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值