新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 串口編程實現單片機和PC的通訊

        串口編程實現單片機和PC的通訊

        作者: 時間:2016-11-24 來源:網絡 收藏
        進來要用串口編程實現單片機PC通訊,現在網上收錄了一些編程的例子作為備查。


        串口API通信函數編程

        16位串口應用程序中,使用的16位的WindowsAPI通信函數:
        ①OpenComm()打開串口資源,并指定輸入、輸出緩沖區的大小(以字節計)
          CloseComm()關閉串口;
        例:intidComDev;
        idComDev=OpenComm("COM1",1024,128);
        CloseComm(idComDev);
        ②BuildCommDCB()、setCommState()填寫設備控制塊DCB,然后對已打開的串口進行參數配置;
        例:DCBdcb;
        BuildCommDCB("COM1:2400,n,8,1",&dcb);
        SetCommState(&dcb);
        ③ReadComm、WriteComm()對串口進行讀寫操作,即數據的接收和發送.
        例:char*m_pRecieve;intcount;
        ReadComm(idComDev,m_pRecieve,count);
        Charwr[30];intcount2;
        WriteComm(idComDev,wr,count2);
        16位下的串口通信程序最大的特點就在于:串口等外部設備的操作有自己特有的API函數;而32位程序則把串口操作(以及并口等)和文件操作統一起來了,使用類似的操作。

        在MFC下的32位串口應用程序
        32位下串口通信程序可以用兩種方法實現:利用ActiveX控件;使用API通信函數。
        使用ActiveX控件,程序實現非常簡單,結構清晰,缺點是欠靈活;使用API通信函數的優缺點則基本上相反。

        使用ActiveX控件:
        VC++6.0提供的MSComm控件通過串行端口發送和接收數據,為應用程序提供串行通信功能。使用非常方便,但可惜的是,很少有介紹MSComm控件的資料。
          
        ⑴.在當前的Workspace中插入MSComm控件。
        Project菜單------>AddtoProject---->ComponentsandControls----->Registered
        ActiveXControls--->選擇Components:MicrosoftCommunicationsControl,
        version6.0插入到當前的Workspace中。
        結果添加了類CMSComm(及相應文件:mscomm.h和mscomm.cpp)。
          
        ⑵.在MainFrm.h中加入MSComm控件。
        protected:
        CMSCommm_ComPort;
        在Mainfrm.cpp::OnCreare()中:
        DWORDstyle=WS_VISIBLE|WS_CHILD;
        if(!m_ComPort.Create(NULL,style,CRect(0,0,0,0),this,ID_COMMCTRL)){
        TRACE0("FailedtocreateOLECommunicationsControl");
        return-1;  //failtocreate
        }
         
        ⑶.初始化串口
        m_ComPort.SetCommPort(1);  //選擇COM?
        m_ComPort.SetInBufferSize(1024);//設置輸入緩沖區的大小,Bytes
        m_ComPort.SetOutBufferSize(512);//設置輸入緩沖區的大小,Bytes//
        if(!m_ComPort.GetPortOpen())//打開串口
        m_ComPort.SetPortOpen(TRUE);
        m_ComPort.SetInputMode(1);//設置輸入方式為二進制方式
        m_ComPort.SetSettings("9600,n,8,1");//設置波特率等參數
        m_ComPort.SetRThreshold(1);//為1表示有一個字符引發一個事件
        m_ComPort.SetInputLen(0);

        ⑷.捕捉串口事項。MSComm控件可以采用輪詢或事件驅動的方法從端口獲取數據。我們介紹比較使用的事件驅動方法:有事件(如接收到數據)時通知程序。在程序中需要捕獲并處理這些通訊事件。
        在MainFrm.h中:
        protected:
        afx_msgvoidOnCommMscomm();
        DECLARE_EVENTSINK_MAP()
        在MainFrm.cpp中:
        BEGIN_EVENTSINK_MAP(CMainFrame,CFrameWnd)  
        ON_EVENT(CMainFrame,ID_COMMCTRL,1,OnCommMscomm,VTS_NONE)//映射ActiveX控件事件
        END_EVENTSINK_MAP()

        ⑸.串口讀寫.完成讀寫的函數的確很簡單,GetInput()和SetOutput()就可。兩個函數的原型是:
        VARIANTGetInput();及voidSetOutput(constVARIANT&newValue);都要使用VARIANT類型(所有Idispatch::Invoke的參數和返回值在內部都是作為VARIANT對象處理的)。
        無論是在PC機讀取上傳數據時還是在PC機發送下行命令時,我們都習慣于使用字符串的形式(也可以說是數組形式)。查閱VARIANT文檔知道,可以用BSTR表示字符串,但遺憾的是所有的BSTR都是包含寬字符,即使我們沒有定義_UNICODE_UNICODE也是這樣!WinNT支持寬字符,而Win95并不支持。為解決上述問題,我們在實際工作中使用CbyteArray,給出相應的部分程序如下:
        voidCMainFrame::OnCommMscomm(){
        VARIANTvResponse;  intk;
        if(m_commCtrl.GetCommEvent()==2){      
        k=m_commCtrl.GetInBufferCount();//接收到的字符數目
        if(k>0){
        vResponse=m_commCtrl.GetInput();//read
        SaveData(k,(unsignedchar*)vResponse.parray->pvData);
        }//接收到字符,MSComm控件發送事件}
          。。。。。//處理其他MSComm控件
        }
        voidCMainFrame::OnCommSend(){
        。。。。。。。。//準備需要發送的命令,放在TxData[]中
        CByteArrayarray;
        array.RemoveAll();
        array.SetSize(Count);
        for(i=0;iarray.SetAt(i,TxData[i]);
        m_ComPort.SetOutput(COleVariant(array));//發送數據}

        ㈡使用32位的API通信函數:
        ⑴.在中MainFrm.cpp定義全局變量
        HANDLE    hCom;//準備打開的串口的句柄
        HANDLE    hCommWatchThread;//輔助線程的全局函數
        ⑵.打開串口,設置串口
        hCom=CreateFile("COM2",GENERIC_READ|GENERIC_WRITE,//允許讀寫
                0,          //此項必須為0
                NULL,        //nosecurityattrs
                OPEN_EXISTING,    //設置產生方式
                FILE_FLAG_OVERLAPPED,//我們準備使用異步通信
                NULL);
        我使用了FILE_FLAG_OVERLAPPED結構。這正是使用API實現非阻塞通信的關鍵所在。
        ASSERT(hCom!=INVALID_HANDLE_VALUE);//檢測打開串口操作是否成功
        SetCommMask(hCom,EV_RXCHAR|EV_TXEMPTY);//設置事件驅動的類型
        SetupComm(hCom,1024,512);//設置輸入、輸出緩沖區的大小
        PurgeComm(hCom,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR
              |PURGE_RXCLEAR);//清干凈輸入、輸出緩沖區
        COMMTIMEOUTSCommTimeOuts;//定義超時結構,并填寫該結構
          …………
        SetCommTimeouts(hCom,&CommTimeOuts);//設置讀寫操作所允許的超時
        DCB    dcb;//定義數據控制塊結構
        GetCommState(hCom,&dcb);//讀串口原來的參數設置
        dcb.BaudRate=9600;dcb.ByteSize=8;dcb.Parity=NOPARITY;
        dcb.StopBits=ONESTOPBIT;dcb.fBinary=TRUE;dcb.fParity=FALSE;
        SetCommState(hCom,&dcb);//串口參數配置
        上述的COMMTIMEOUTS結構和DCB都很重要,實際工作中需要仔細選擇參數。

        ⑶啟動一個輔助線程,用于串口事件的處理。
        Windows提供了兩種線程,輔助線程和用戶界面線程。輔助線程沒有窗口,所以它沒有自己的消息循環。但是輔助線程很容易編程,通常也很有用。
        在次,我們使用輔助線程。主要用它來監視串口狀態,看有無數據到達、通信有無錯誤;而主線程則可專心進行數據處理、提供友好的用戶界面等重要的工作。
        hCommWatchThread=
            CreateThread((LPSECURITY_ATTRIBUTES)NULL,//安全屬性
                0,//初始化線程棧的大小,缺省為與主線程大小相同
        (LPTHREAD_START_ROUTINE)CommWatchProc,//線程的全局函數
                GetSafeHwnd(),//此處傳入了主框架的句柄
                0,&dwThreadID);
          ASSERT(hCommWatchThread!=NULL);

        ⑷為輔助線程寫一個全局函數,主要完成數據接收的工作。請注意OVERLAPPED結構的使用,以及怎樣實現了非阻塞通信。
        UINTCommWatchProc(HWNDhSendWnd){
          DWORDdwEvtMask=0;
          SetCommMask(hCom,EV_RXCHAR|EV_TXEMPTY);//有哪些串口事件需要監視?
          WaitCommEvent(hCom,&dwEvtMask,os);//等待串口通信事件的發生
          檢測返回的dwEvtMask,知道發生了什么串口事件:
          if((dwEvtMask&EV_RXCHAR)==EV_RXCHAR){//緩沖區中有數據到達
          COMSTATComStat;DWORDdwLength;
          ClearCommError(hCom,&dwErrorFlags,&ComStat);
          dwLength=ComStat.cbInQue;//輸入緩沖區有多少數據?
          if(dwLength>0){BOOLfReadStat;  
          fReadStat=ReadFile(hCom,lpBuffer,dwLength,&dwBytesRead,&READ_OS(npTTYInfo));//讀數據
        注:我們在CreareFile()時使用了FILE_FLAG_OVERLAPPED,現在ReadFile()也必須使用
          LPOVERLAPPED結構.否則,函數會不正確地報告讀操作已完成了.
          使用LPOVERLAPPED結構,ReadFile()立即返回,不必等待讀操作完成,實現非阻塞
          通信.此時,ReadFile()返回FALSE,GetLastError()返回ERROR_IO_PENDING.
        if(!fReadStat){
        if(GetLastError()==ERROR_IO_PENDING){
        while(!GetOverlappedResult(hCom,&READ_OS(npTTYInfo),&dwBytesRead,TRUE)){
              dwError=GetLastError();
          if(dwError==ERROR_IO_INCOMPLETE)continue;//緩沖區數據沒有讀完,繼續
              …………      
        ::PostMessage((HWND)hSendWnd,WM_NOTIFYPROCESS,0,0);//通知主線程,串口收到數據}
          所謂的非阻塞通信,也即異步通信。是指在進行需要花費大量時間的數據讀寫操作(不僅僅是指串行通信操作)時,一旦調用ReadFile()、WriteFile(),就能立即返回,而讓實際的讀寫操作在后臺運行;相反,如使用阻塞通信,則必須在讀或寫操作全部完成后才能返回。由于操作可能需要任意長的時間才能完成,于是問題就出現了。
        非常阻塞操作還允許讀、寫操作能同時進行(即重疊操作?),在實際工作中非常有用。
        要使用非阻塞通信,首先在CreateFile()時必須使用FILE_FLAG_OVERLAPPED;然后在ReadFile()時lpOverlapped參數一定不能為NULL,接著檢查函數調用的返回值,調用GetLastError(),看是否返回ERROR_IO_PENDING。如是,最后調用GetOverlappedResult()返回重疊操作(overlappedoperation)的結果;WriteFile()的使用類似。

        ⑸.在主線程中發送下行命令。
        BOOL  fWriteStat;charszBuffer[count];
              …………//準備好發送的數據,放在szBuffer[]中
        fWriteStat=WriteFile(hCom,szBuffer,dwBytesToWrite,
                  &dwBytesWritten,&WRITE_OS(npTTYInfo));//寫數據
        //我在CreareFile()時使用了FILE_FLAG_OVERLAPPED,現在WriteFile()也必須使用LPOVERLAPPED結構.否則,函數會不正確地報告寫操作已完成了.
        使用LPOVERLAPPED結構,WriteFile()立即返回,不必等待寫操作完成,實現非阻塞通信.此時,WriteFile()返回FALSE,GetLastError()返回ERROR_IO_PENDING.
        interr=GetLastError();
        if(!fWriteStat){
          if(GetLastError()==ERROR_IO_PENDING){
            while(!GetOverlappedResult(hCom,&WRITE_OS(npTTYInfo),
                  &dwBytesWritten,TRUE)){
              dwError=GetLastError();
              if(dwError==ERROR_IO_INCOMPLETE){//normalresultifnotfinished
                dwBytesSent+=dwBytesWritten;continue;}
            ......................
        //我使用了多線程技術,在輔助線程中監視串口,有數據到達時依靠事件驅動,讀入數據并向主線程報告(發送數據在主線程中,相對說來,下行命令的數據總是少得多);并且,WaitCommEvent()、ReadFile()、WriteFile()都使用了非阻塞通信技術,依靠重疊(overlapped)讀寫操作,讓串口讀寫操作在后臺運行。


        評論


        技術專區

        關閉
        主站蜘蛛池模板: 崇阳县| 柳州市| 衡东县| 建湖县| 赤水市| 湄潭县| 视频| 道真| 浪卡子县| 绥宁县| 克什克腾旗| 苍溪县| 鸡西市| 岳阳市| 额敏县| 苍南县| 汕尾市| 涿鹿县| 丽水市| 麻栗坡县| 泌阳县| 永川市| 麻阳| 山东| 海原县| 博客| 静海县| 霍林郭勒市| 兴安盟| 西丰县| 视频| 延川县| 花垣县| 丹阳市| 靖远县| 瑞丽市| 罗定市| 高尔夫| 乐安县| 合作市| 无锡市|