串口通信serialport

  private void button1_Click(object sender, EventArgs e) 
  { 
       serialPort1.PortName = "COM1"; 
       serialPort1.BaudRate = 9600; 
       serialPort1.Open(); 
       byte[] data = Encoding.Unicode.GetBytes(textBox1.Text); 
       string str = Convert.ToBase64String(data); 
       serialPort1.WriteLine(str); 
       MessageBox.Show("数据发送成功!","系统提示"); 
  } 
      private void button2_Click(object sender, EventArgs e) 
  { 
      byte[] data = Convert.FromBase64String(serialPort1.ReadLine()); 
      textBox2.Text = Encoding.Unicode.GetString(data); 
      serialPort1.Close(); 
      MessageBox.Show("数据接收成功!","系统提示"); 

  }

CserialPort类的功能及成员函数介绍

CserialPort类是免费提供的串口累,Codeguru是一个非常不错的源代码网站

CserialPort类支持线连接(非MODEM)的串口编程操作。

CserialPort类是基于多线程的,其工作流程如下:首先设置好串口参数,再开启串口检测工作线程,串口检测工作线程检测到串口接收到的数据、流控制事件或其他串口事件后,就以消息方式通知主程序,激发消息处理函数来进行数据处理,这是对接受数据而言的,发送数据可直接向串口发送。

CserialPort类定义的消息如表

消息名称

消息号

功能说明

WM_COMM_BREAK_DETECTED

WM_USER+1

检测到输入中断

WM_COMM_CTS_DETECTED

WM_USER+2

检测到CTS(清除发送)信号状态改变

WM_COMM_DSR_DETECTED

WM_USER+3

检测到DSR(数据设备准备就绪)信号状态改变

WM_COMM_ERR_DETECTED

WM_USER+4

发生线状态错误(包括CE_FRAMECE_OVERRUN,和CE_RXPARITY

WM_COMM_RING_DETECTED

WM_USER+5

检测到响铃指示信号

WM_COMM_RLSD_DETECTED

WM_USER+6

检测到RLSD(接收线信号)状态改变

WM_COMM_RXCHAR

WM_USER+7

接收到一个字符并已放入接受缓冲区

WM_COMM_RXFLAG_DETECTED

WM_USER+8

检测到接受到字符(该字符已放入接受缓冲区)事件

WM_COMM_TXEMPTY_DETECTED

WM_USER+9

检测到发送缓冲区最后一个字符已经被发送


介绍几个经常用到的函数:
1、串口初始化函数InitPort
BOOL CSerialPort::InitPort(CWnd *pPortOwner,    // the owner (CWnd) of the port (receives message) 
                           UINT  portnr,        // portnumber (1..4) 
                           UINT  baud,            // baudrate 
                           char  parity,        // parity 
                           UINT  databits,        // databits 
                           UINT  stopbits,        // stopbits 
                           DWORD dwCommEvents,    // EV_RXCHAR, EV_CTS etc 
                           UINT  writebuffersize)    // size to the writebuffer 

    assert(portnr > 0 && portnr < 5); 
    assert(pPortOwner != NULL); 
 
    // if the thread is alive: Kill 
    if (m_bThreadAlive) 
    { 
        do 
        { 
            SetEvent(m_hShutdownEvent); 
        } 
        while (m_bThreadAlive); 
        TRACE("Thread ended\n"); 
    } 
 
    // create events 
    if (m_ov.hEvent != NULL) 
        ResetEvent(m_ov.hEvent); 
    m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
 
    if (m_hWriteEvent != NULL) 
        ResetEvent(m_hWriteEvent); 
    m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
 
    if (m_hShutdownEvent != NULL) 
        ResetEvent(m_hShutdownEvent); 
    m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
 
    // initialize the event objects 
    m_hEventArray[0] = m_hShutdownEvent;    // highest priority 
    m_hEventArray[1] = m_ov.hEvent; 
    m_hEventArray[2] = m_hWriteEvent; 
 
    // initialize critical section 
    InitializeCriticalSection(&m_csCommunicationSync); 
 
    // set buffersize for writing and save the owner 
    m_pOwner = pPortOwner; 
 
    if (m_szWriteBuffer != NULL) 
        delete [] m_szWriteBuffer; 
    m_szWriteBuffer = new char[writebuffersize]; 
 
    m_nPortNr = portnr; 
 
    m_nWriteBufferSize = writebuffersize; 
    m_dwCommEvents = dwCommEvents; 
 
    BOOL bResult = FALSE; 
    char *szPort = new char[50]; 
    char *szBaud = new char[50]; 
 
    // now it critical! 
    EnterCriticalSection(&m_csCommunicationSync); 
 
    // if the port is already opened: close it 
    if (m_hComm != NULL) 
    { 
        CloseHandle(m_hComm); 
        m_hComm = NULL; 
    } 
 
    // prepare port strings 
    sprintf(szPort, "COM%d", portnr); 
    sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopbits); 
 
    // get a handle to the port 
    m_hComm = CreateFile(szPort,                        // communication port string (COMX) 
                         GENERIC_READ | GENERIC_WRITE,    // read/write types 
                         0,                                // comm devices must be opened with exclusive access 
                         NULL,                            // no security attributes 
                         OPEN_EXISTING,                    // comm devices must use OPEN_EXISTING 
                         FILE_FLAG_OVERLAPPED,            // Async I/O 
                         0);                            // template must be 0 for comm devices 
 
    if (m_hComm == INVALID_HANDLE_VALUE) 
    { 
        // port not found 
        delete [] szPort; 
        delete [] szBaud; 
 
        return FALSE; 
    } 
 
    // set the timeout values 
    m_CommTimeouts.ReadIntervalTimeout = 1000
    m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000
    m_CommTimeouts.ReadTotalTimeoutConstant = 1000
    m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000
    m_CommTimeouts.WriteTotalTimeoutConstant = 1000
 
    // configure 
    if (SetCommTimeouts(m_hComm, &m_CommTimeouts)) 
    { 
        if (SetCommMask(m_hComm, dwCommEvents)) 
        { 
            if (GetCommState(m_hComm, &m_dcb)) 
            { 
                m_dcb.fRtsControl = RTS_CONTROL_ENABLE;        // set RTS bit high! 
                if (BuildCommDCB(szBaud, &m_dcb)) 
                { 
                    if (SetCommState(m_hComm, &m_dcb)) 
                        ; // normal operation... continue 
                    else 
                        ProcessErrorMessage("SetCommState()"); 
                } 
                else 
                    ProcessErrorMessage("BuildCommDCB()"); 
            } 
            else 
                ProcessErrorMessage("GetCommState()"); 
        } 
        else 
            ProcessErrorMessage("SetCommMask()"); 
    } 
    else 
        ProcessErrorMessage("SetCommTimeouts()"); 
 
    delete [] szPort; 
    delete [] szBaud; 
 
    // flush the port 
    PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); 
 
    // release critical section 
    LeaveCriticalSection(&m_csCommunicationSync); 
 
    TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr); 
 
    return TRUE; 

 
 这个函数是用来初始化串口的,即设置串口的通信参数:需要打开的串口号、波特率、奇偶校验方式、数据位、停止位,这里还可 以用来进行事件的设定

如果串口初始化成功,就返回TRUE,若串口被其他设备占用、不存在或存在其他股占,就返回FALSE,编程者可以在这儿提示串口操作是否成功

如果在当前主串口调用这个函数,那么pPortOwner可用this指针表示,串口号在函数中做了限制,只能用1234四个串口号,而事实上在编程时可能用到更多串口号,可以通过通过注释掉本函数中的“assert(portur>0&&portnr<5)”语句取消对串口号的限制

2、启动串口通信监测线程函数StartMonitoring()

串口初始化成功后,就可以调用BOOL StartMonitoring()来启动串口检测线程,线程启动成功,发挥TRUE

BOOL CSerialPort::StartMonitoring() 

    if (!(m_Thread = AfxBeginThread(CommThread, this))) 
        return FALSE; 
    TRACE("Thread started\n"); 
    return TRUE; 

3、暂停或停止监测线程函数StopMonitoring()

该函数暂停或停止串口检测,要注意的是,调用该函数后,串口资源仍然被占用// 
// Suspend the comm thread 
// 
BOOL CSerialPort::StopMonitoring() 

     TRACE("Thread suspended\n"); 
     m_Thread->SuspendThread(); 
     return TRUE; 

4、关闭串口函数ClosePort()

该函数功能是关闭串口,释放串口资源,调用该函数后,如果要继续使用串口,还需要调用InitPort()函数

5、通过串口发送字符/写串口函数WriteToPort()

该函数完成写串口功能,即向串口发送字符。

// 
// Write a string to the port 
// 
void CSerialPort::WriteToPort(char *string) 

     assert(m_hComm != 0); 
 
     memset(m_szWriteBuffer, 0sizeof(m_szWriteBuffer)); 
     strcpy(m_szWriteBuffer, string); 
 
     // set event for write 
     SetEvent(m_hWriteEvent); 

以上是常用的函数介绍,熟悉该类的使用后,可以仔细看看其他函数,对上面介绍的函数,在对串口资源的使用上要记住一下三点:

打开串口用调用InitPort()和StartMonitoring();关闭串口用StopMonitoring()和ClosePort()而且以上函数的调用顺序不能乱

通过串口发送字符调用函数WriteToPort()

接受串口收到的字符需要自己编写WM_COMM_RXCHAR消息处理函数,需要手工添加,

操作:

首先,需要操作一个串口,所以只需要定义1个类对象就可以了,如要操作多个串口,则要为每个串口均定一个类对象,这可以通过数据方式来实现,这里定义的类对象为m_SerialPort,再定义一个布尔变量m_bSerialPortOpened用来标志串口是否打开。

CserialPort类中有多个串口事件可以响应,在一般串口编程中,只需要处理WM_COMM_RXCHAR消息就可以了,该类所有的消息均需要人工添加消息处理函数,将处理函数名定义为OnComm(),首先在SerialPortTestDlg.h(头文件)中添加串口字符接受消息WM_COMM_RXCHAR(串口接受缓冲区内有一个字符)的响应函数声明:

//Generated message map funnctions 
//{{AFX_MSG(CSCportTestView) 
afx_msg long OnComm(WPARAM ch, LPARAM port); 
//}}AFX_MSG 
 

然后在,SerilPortTestDlg.cpp文件中进行WM_COMM_RXCHAR消息映射

BEGIN_MESSAE_MAP(CSerialPortTestDlg, CDialog)s 
//{{AFX_MSG_MAP(CSerialPortTestDlg) 
ON_MESSAGE(WM_COMM_RXCHAR, OnComm) 
//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 接着,在SerialPortTestDlg.cpp文件中加入函数OnComm()的实现,并在其中完成对节诶受到的字符的处理,将接收到的字符显示在接受编辑框中:

long CSerialPortTestDlg::OnComm(WPARAM ch, LPARAM port) 

    m_strEditReceiveMsg += ch; 
    UpdateData(FLASE);//将接收到的字符显示在接受编辑框中 
    returne 0

说明:WPARAMLPARAM类型是多态数据类型,在WIN32中为32位,支持多种数据类型,根据需要自动适应,这样,程序有很强的适应性,再次,我们可以分贝理解为charinteger类型数据,每当串口接受缓冲区内有一个字符时,就会产生一个WM_COMM_RXCHAR消息,除法OnComm()函数,这时就可以在函数中进行数据处理,所以,这个消息就是整个程序的源头。

CSerialPort类的改进

虽然CSerialPort类是一个非常好的类,但毕竟只是集中了作者一个人的智慧和经验,他也有许多缺陷,

原类只能发送字符(ASCII文本)不能处理二进制发送(也就是不能发送0X00

该类不能很好的释放串口

存在内存泄漏

所以,可以进行如下改进

改进一、ASCII文本和二进制数据发送方式兼容

CSerialPort类中只有一个发送函数WriteToPort()

// 
// Write a string to the port 
// 
void CSerialPort::WriteToPort(char *string) 

    assert(m_hComm != 0); 
 
    memset(m_szWriteBuffer, 0sizeof(m_szWriteBuffer)); 
    strcpy(m_szWriteBuffer, string); 
 
    // set event for write 
    SetEvent(m_hWriteEvent); 

调用上面的函数就只能用字符串方式,而c语言中,空字符(NULL,其中ASCII码值为0,即通常所说的十六禁止0x00字符),是串结束符,当检测到NULL字符后,就认为该字符串结束了,所以0x00字符以ASCII文本方式是不能从串口发送出去的,那么解决这一问题的方法就是用二进制发送,其实这里说的二进制,只不过是不以我们通常所说的“可见”或“能显示的字符”发送,比如,要发如下的一组值:
char chSend[5]={0x33,0x96,0x00,0x31,0xf1};

下面来对类做一些改进,解决这个问题,原理就是用字符数据来发送数据,并在发送时指定其长度,这样,数据没有发送完,发送过程就不会停止,CSerialPort类是用API函数编写的在,只要在WriteFile()函数中指定其实际要发送的长度,就可以将数据全部发送出去:

实现步骤如下:

1、SerialPort.h文件中为CSerialPort类添加一个整形publicdn成员变量,:int m_nWriteSize;用于指定发送字符数据的长度

添加三个发送函数




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CSerialPort First Version by Remon Spekreijse on 2000-02-08 http://www.codeguru.com/cpp/i-n/network/serialcommunications/article.php/c2483/A-communication-class-for-serial-port.htm Second Version by mrlong on 2007-12-25 https://code.google.com/p/mycom/ 增加 ClosePort 增加 WriteToPort 两个方法 增加 SendData 与 RecvData 方法 by liquanhai on 2011-11-04 http://blog.csdn.net/liquanhai/article/details/4955253 增加 ClosePort 中交出控制权,防止死锁问题 by liquanhai on 2011-11-06 http://blog.csdn.net/liquanhai/article/details/6941574 增加 ReceiveChar 中防止线程死锁 by viruscamp on 2013-12-04 https://github.com/viruscamp/CSerialPort 增加 IsOpen 判断是否打开 修正 InitPort 中 parity Odd Even 参数取值错误 修改 InitPortportnr 取值范围,portnr>9 时特殊处理 取消对 MFC 的依赖,使用 HWND 替代 CWnd,使用 win32 thread 函数而不是 MFC 的 增加用户消息编号自定义,方法来自 CnComm by itas109 on 2014-01-10 http://blog.csdn.net/itas109/article/details/18358297 解决COM10以上端口无法显示的问题 扩展可选择端口,最大值MaxSerialPortNum可以自定义 添加QueryKey()和Hkey2ComboBox两个方法,用于自动查询当前有效的串口号。 by liquanhai on 2014-12-18 增加一些处理措施,主要是对减少CPU占用率 by itas109 on 2016-05-07 http://blog.csdn.net/itas109 修复每次打开串口发送一次,当串口无应答时,需要关闭再打开或者接收完数据才能发送的问题。 解决办法:在m_hEventArray中调整m_hWriteEvent的优先级高于读的优先级。CommThread(LPVOID pParam)函数中读写的位置也调换。 参考:http://zhidao.baidu.com/link?url=RSrbPcfTZRULFFd2ziHZPBwnoXv1iCSu_Nmycb_yEw1mklT8gkoNZAkWpl3UDhk8L35DtRPo5VV5kEGpOx-Gea 修复停止位在头文件中定义成1导致SetCommState报错的问题,应为1对应的停止位是1.5。UINT stopsbits = ONESTOPBIT switch(stopbits)和switch(parity)增加默认情况,增强程序健壮性 by itas109 on 2016-06-22 http://blog.csdn.net/itas109 增加ReceiveStr方法,用于接收字符串(接收缓冲区有多少字符就接收多少字符)。 解决ReceiveChar只能接收单个字符的问题。 by itas109 on 2016-06-29 http://blog.csdn.net/itas109 解决RestartMonitoring方法和StopMonitoring方法命令不准确引起的歧义,根据实际作用。 将RestartMonitoring更改为ResumeMonitoring,将StopMonitoring更改为SuspendMonitoring。 增加IsThreadSuspend方法,用于判断线程是否挂起。 改进ClosePort方法,增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题。 增加IsReceiveString宏定义,用于接收时采用单字节接收还是多字节接收 by itas109 on 2016-08-02 http://blog.csdn.net/itas109 https://github.com/itas109 改进IsOpen方法,m_hComm增加INVALID_HANDLE_VALUE的情况,因为CreateFile方法失败返回的是INVALID_HANDLE_VALUE,不是NULL 改进ClosePort方法:增加串口句柄无效的判断(防止关闭死锁);m_hWriteEvent不使用CloseHandle关闭 改进CommThread、ReceiveChar、ReceiveStr和WriteChar方法中异常处理的判断,增加三种判断:串口打开失败(error code:ERROR_INVALID_HANDLE)、连接过程中非法断开(error code:ERROR_BAD_COMMAND)和拒绝访问(error code:ERROR_ACCESS_DENIED) 采用安全函数sprintf_s和strcpy_s函数替换掉sprintf和strcpy 改进QueryKey方法,用于查询注册表的可用串口值,可以搜索到任意的可用串口 改进InitPort方法,串口打开失败,增加提示信息:串口不存在(error code:ERROR_FILE_NOT_FOUND)和串口拒绝访问(error code:ERROR_ACCESS_DENIED) 加入viruscamp 取消对 MFC 的依赖 改进InitPort方法,如果上次串口是打开,再次调用InitPort方法,关闭串口需要做一定的延时,否则有几率导致ERROR_ACCESS_DENIED拒绝访问,也就是串口占用问题 初始化默认波特率修改为9600 修复一些释放的BUG 规范了一些错误信息,参考winerror.h -- error code definitions for the Win32 API functions 删除SendData和RecvData方法 by itas109 on 2016-08-10 http://blog.csdn.net/itas109 https://github.com/itas109 改进ReceiveStr方法,comstat.cbInQue = 0xcccccccc的情况(如串口异常断开),会导致RXBuff初始化失败 by itas109 on 2017-02-14 http://blog.csdn.net/itas109 https://github.com/itas109 兼容ASCII和UNICODE编码 ReceiveStr函数中发送函数SendMessage的第二个参数采用结构体形式,包括portNr串口号和bytesRead读取的字节数,可以处理16进制的时候0x00截断问题 精简不必要的函数SendData和RecvData 尽量的取消对 MFC 的依赖,Hkey2ComboBox函数暂时保留 其他小问题修改 博客:blog.csdn.net/itas109 Email:itas109@qq.com
### 回答1: 串口通信是一种常见的设备间通信方式,它可以让计算机和其他设备之间进行数据传输。SerialPort类是.NET Framework中提供的一个串口控制类,可以方便地管理串口数据传输。 使用SerialPort类可以轻松打开、关闭、读取和写入数据到串口。在使用SerialPort类之前,需要先设置串口的参数,如波特率、数据位数、停止位数等,以确保数据传输的正确性。 在进行数据读取时,SerialPort类提供了多种读取方法,例如Read、ReadExisting和ReadLine,可以根据数据的类型和格式自由选择。同时,在数据传输期间,SerialPort类也会定期检查接收缓冲区,以便及时发现并处理传输中的错误或丢失数据等异常情况。 总之,SerialPort类是.NET Framework中一款非常实用的串口控制类,可以为串口通信的应用程序提供强大的支持。无论是在工控领域、自动化设备还是其他需要串口通信的场合,SerialPort类都是不可或缺的重要组成部分。 ### 回答2: 串口通信是一种常见的数据通信方式,它可以通过串口将数据发送和接收到另一个设备上。在C#中,通过SerialPort类可以实现串口通信SerialPort类提供了许多属性和方法,可以方便地设置串口的通信参数,打开和关闭串口,发送和接收数据等。例如,可以使用BaudRate属性设置波特率,使用Parity属性设置校验位,使用DataBits属性设置数据位,使用StopBits属性设置停止位等。 在发送数据时,可以使用Write方法向串口发送数据。可以发送字符串、字节数组等数据类型。在接收数据时,可以使用DataReceived事件处理程序处理接收到的数据。通过设置ReadExisting()方法来读取串口中的全部数据,或者使用Read方法设置从串口读取的字节数。 需要注意的是,使用SerialPort类进行串口通信时,需要确保串口的正确连接和设置。如果不正确设置或者连接不良,可能会导致数据无法正常发送和接收。 总之,SerialPort类是C#中常用的一个类,它可以帮助我们非常方便地进行串口通信,并在许多领域得到广泛应用,如机器人控制、工控设备控制、数据采集等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值