Windows 串口通信简单实例

Windows 串口通信简单实例

May 12, 2014
Coding
C++, Windows

工具软件 #

为了方便串口程序的调试,使用了如下两款工具软件。

1. 串口调试助手 #

img.png

该软件可以通过串口收发数据。可以通过串口发送数据,也可以查看串口收到的数据。下载地址

2. 虚拟串口 #

img.png

可以虚拟串口,每次虚拟出一对串口,这对串口相互连通。如图中虚拟出了串口COM2和COM3,这样通过COM2发送的数据,会由COM3接收到,反之亦然。下载地址

串口通信的基本步骤 #

  • (1) 通过CreateFile(“COMx:",…) 打开串口
  • (2) 通过配置DCB结构体和SetCommState函数,设置串口的参数。
  • (3) 通过ReadFile()和WriteFile 读写串口

Windows 串口通信实例 #

封装了一个串口通信的C++类 CSerial,通过 CSerial类的 OpenSerialPort() 可以打开一个串口,串口打开后后自动新建线程读取串口数据,并通过MessageBox简单的显示出数据。通过CSerial类的SendData()方法可以向串口发送数据。

主函数中,新建了一个CSerial类对象,打开串口2,然后简单的通过一个消息框循环来控制向串口不断的发送数据。

程序的效果图如下:

img.png

使用虚拟串口工具虚拟出串口对COM2和COM3,本实例程序读写COM2,使用串口调试助手打开COM3。

  • 在“是否向串口发送数据”消息框中,点击"是(Y)” 会向串口发送一条"This is a example" 数据。如图,在串口调试助手中收到该数据包
  • 在串口调试助手中,手动发送"jarvischu",程序会读取到该数据并弹出消息框显示。

源码 #

Serial.h

#pragma once

#include <windows.h>

class CSerial
{
public:
    CSerial(void);
    ~CSerial(void);

    //打开串口
    BOOL OpenSerialPort(TCHAR* port,UINT baud_rate,BYTE date_bits,BYTE stop_bit,BYTE parity=NOPARITY);

    //发送数据
    BOOL SendData(char* data,int len);
public:
    HANDLE m_hComm;
};

Serial.cpp

#include "StdAfx.h"
#include "Serial.h"
#include <process.h>

typedef unsigned (__stdcall *PTHREAD_START) (void *);

CSerial::CSerial(void)
{
    m_hComm = INVALID_HANDLE_VALUE;
}

CSerial::~CSerial(void)
{
    if(m_hComm != INVALID_HANDLE_VALUE){
        CloseHandle(m_hComm);
    }
}

/*********************************************************************************************
 * 功能    : 读串口线程回调函数 
 * 描述    : 收到数据后,简单的显示出来
 ********************************************************************************************/
DWORD WINAPI CommProc(LPVOID lpParam){

    CSerial* pSerial = (CSerial*)lpParam;  //

    //清空串口
    PurgeComm(pSerial->m_hComm,PURGE_RXCLEAR|PURGE_TXCLEAR);

    char buf[512];
    DWORD dwRead;
    while(pSerial->m_hComm != INVALID_HANDLE_VALUE){
        BOOL bReadOK = ReadFile(pSerial->m_hComm,buf,512,&dwRead,NULL);
        if(bReadOK && (dwRead > 0)){
            buf[dwRead] = '\0';
            MessageBoxA(NULL,buf,"串口收到数据",MB_OK);
        }
    }
    return 0;
}

/*******************************************************************************************
 * 功能     : 打开串口
 * port     : 串口号, 如_T("COM1:")
 * baud_rate: 波特率
 * date_bits: 数据位(有效范围4~8)
 * stop_bit : 停止位
 * parity   : 奇偶校验。默认为无校验。NOPARITY 0; ODDPARITY 1;EVENPARITY 2;MARKPARITY 3;SPACEPARITY 4
 ********************************************************************************************/
BOOL CSerial::OpenSerialPort(TCHAR* port,UINT baud_rate,BYTE date_bits,BYTE stop_bit,BYTE parity){
    //打开串口
    m_hComm = CreateFile(port,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);//独占方式打开串口

    TCHAR err[512];

    if(m_hComm == INVALID_HANDLE_VALUE){
        _stprintf(err,_T("打开串口%s 失败,请查看该串口是否已被占用"),port);
        MessageBox(NULL,err,_T("提示"),MB_OK);
        return FALSE;
    }

    //MessageBox(NULL,_T("打开成功"),_T("提示"),MB_OK);

    //获取串口默认配置
    DCB dcb;
    if(!GetCommState(m_hComm,&dcb)){
        MessageBox(NULL,_T("获取串口当前属性参数失败"),_T("提示"),MB_OK);
    }

    //配置串口参数
    dcb.BaudRate = baud_rate; //波特率
    dcb.fBinary = TRUE; //二进制模式。必须为TRUE
    dcb.ByteSize = date_bits; //数据位。范围4-8
    dcb.StopBits = ONESTOPBIT; //停止位

    if(parity == NOPARITY){
        dcb.fParity = FALSE; //奇偶校验。无奇偶校验
        dcb.Parity = parity; //校验模式。无奇偶校验
    }else{
        dcb.fParity = TRUE; //奇偶校验。
        dcb.Parity = parity; //校验模式。无奇偶校验
    }

    dcb.fOutxCtsFlow = FALSE; //CTS线上的硬件握手
    dcb.fOutxDsrFlow = FALSE; //DST线上的硬件握手
    dcb.fDtrControl = DTR_CONTROL_ENABLE; //DTR控制
    dcb.fDsrSensitivity = FALSE;
    dcb.fTXContinueOnXoff = FALSE;//
    dcb.fOutX = FALSE; //是否使用XON/XOFF协议
    dcb.fInX = FALSE; //是否使用XON/XOFF协议
    dcb.fErrorChar = FALSE; //是否使用发送错误协议
    dcb.fNull = FALSE; //停用null stripping
    dcb.fRtsControl = RTS_CONTROL_ENABLE;//
    dcb.fAbortOnError = FALSE; //串口发送错误,并不终止串口读写

    //设置串口参数
    if (!SetCommState(m_hComm,&dcb)){
        MessageBox(NULL,_T("设置串口参数失败"),_T("提示"),MB_OK);
        return FALSE;
    }

    //设置串口事件
    SetCommMask(m_hComm,EV_RXCHAR); //在缓存中有字符时产生事件
    SetupComm(m_hComm,16384,16384);

    //设置串口读写时间
    COMMTIMEOUTS CommTimeOuts;
    GetCommTimeouts(m_hComm,&CommTimeOuts);
    CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
    CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
    CommTimeOuts.ReadTotalTimeoutConstant = 0;
    CommTimeOuts.WriteTotalTimeoutMultiplier = 10;
    CommTimeOuts.WriteTotalTimeoutConstant = 1000;

    if(!SetCommTimeouts(m_hComm,&CommTimeOuts)){
        MessageBox(NULL,_T("设置串口时间失败"),_T("提示"),MB_OK);
        return FALSE;
    }

    //创建线程,读取数据
    HANDLE hReadCommThread = (HANDLE) _beginthreadex(NULL,0,(PTHREAD_START) CommProc,(LPVOID) this,0,NULL);

    return TRUE;
}

/********************************************************************************************
 * 功能    : 通过串口发送一条数据
 ********************************************************************************************/
BOOL CSerial::SendData(char* data,int len){
    if(m_hComm == INVALID_HANDLE_VALUE){
        MessageBox(NULL,_T("串口未打开"),_T("提示"),MB_OK);
        return FALSE;
    }

    //清空串口
    PurgeComm(m_hComm,PURGE_RXCLEAR|PURGE_TXCLEAR);

    //写串口
    DWORD dwWrite = 0;
    DWORD dwRet = WriteFile(m_hComm,data,len,&dwWrite,NULL);

    //清空串口
    PurgeComm(m_hComm,PURGE_RXCLEAR|PURGE_TXCLEAR);

    if(!dwRet){
        MessageBox(NULL,_T("发送数据失败"),_T("提示"),MB_OK);
        return FALSE;
    }
    return TRUE;
}

main.cpp

#include <windows.h>
#include "Serial.h"
#include <string.h>

int main(int argc, _TCHAR* argv[])
{
    CSerial serial;
    serial.OpenSerialPort(_T("COM2:"),9600,8,1);  //打开串口后,自动接收数据

    //向串口发送数据
    char* data = "This is a example\n";
    int ret = 1;
    while(ret != IDNO ){
        serial.SendData(data,strlen(data));
        ret = MessageBox(NULL,_T(""),_T("是否向串口发送数据"),MB_YESNO); //YES继续发送一条数据,NO不发送,退出
    }
    return 0;
}