串口通讯理解

这篇具有很好参考价值的文章主要介绍了串口通讯理解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

机器的通信方式有两种,分别是并行通信与串行通信

并行通信:并行通信是指多比特数据同时通过并行线进行传送,这样数据传送速度大大提高,但并行传送的线路长度受到限制,因为长度增加,干扰就会增加,数据也就容易出错。

串行通信:串行通信是指使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息,特别适用于计算机与计算机、计算机与外设之间的远距离通信。

异步串行通信:异步串行通信是指通信双方以一个字符(包括特定附加位)作为数据传输单位且发送方传送字符的间隔时间不一定,具有不规则数据段传送特性的串行数据传输。

同步串行通信:同步串行通信是指在约定的通信速率下(即相同波特率),发送端和接收端的时钟信号频率和相位始终保持一致(同步),这就保证了通信双方在发送和接收数据时具有完全一致的定时关系。

比特率(Bitrate) 来表示,即每秒钟传输的二进制位数,单位为比特每秒(bit/s)。

“波特率”(Baudrate),它表示每秒钟传输了多少个码元。而码元是通讯信号调制的概念,通讯中常用时间间隔相同的符号来表示一个二进制数字,这样的信号称为码元。

波特率与比特率的关系为:比特率=波特率X单个调制状态对应的二进制位数。

因为很多常见的通讯中一个码元都是表示两种状态,人们常常直接以波特率来表示比特率

串口通讯的数据包由发送设备通过自身的TXD 接口传输到接收设备的RXD 接口。在串口通讯协议中,规定了数据包的内容,它由起始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据。

起始位占1位,为逻辑0。数据位占5 ~ 8位,可配置。校验位占1位,可配置为奇校验、偶校验、无校验,停止位的值为逻辑1。

串口通信即可以实现半双工,也可以实现全双工

单工:数据传输只支持数据在一个方向上传输

半双工:允许数据在两个方向上传输。但是,在某一时刻,只允许数据在一个方向上传输

全双工:允许数据同时在两个方向上传输

串口通讯理解

TTL 标准

理想状态下,使用5V 表示二进制逻辑“1”,使用0V 表示逻辑“0”.

UART

UART(Universal Asynchronous Receiver Transmitter:通用异步收发器),是电脑硬件的一部分,它把将要传输的资料在串行通信与并行通信之间加以转换,UART通常被集成于其他通讯接口的连接上。UART即我们通常说的“串口”。该总线有两条数据线,可以实现全双工的发送和接收,在嵌入式系统中常用于主机与辅助设备之间的通信。

UART进行串口通信使用TTL电平。5V工作电压的MCU,使用0 ~ 0.5V表示逻辑0,2.5V ~ 5V表示逻辑1;3.3V工作电压的MCU,使用0 ~ 0.5V表示逻辑0,2.5V ~ 3.3V表示逻辑1。5V的MUC不能与3.3V的MCU直接连接。

串口通讯理解

空闲位:不进行传输数据时,默认为逻辑1,为高电平;

起始位:先发出一个逻辑“0”,表示消息帧的开始;

数据位:紧接着起始位之后,可由5~8位组成,通常传输8位即一个字节。先发送数据的低位,后发送数据的高位;

奇偶校验位:紧接着数据位后面(可有可无),使得“1”的位数应为偶数(偶校验)或奇数(奇校验),校验数据传输是否正确;

停止位:它是消息传输结束的标志,它可以是1位、1.5位、2位的高电平, 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。

波特率:是衡量数据传输速率的指标,表示每秒钟传输的位数。例如设置串口的波特率为9600,则表示是1s传输9600个bit的数据,则传送每个位的时间为 1s / 9600 ≈ 104us,从而区分消息帧中每个位传输的数据;

缺点:UART一般直接使用TTL信号来表示0和1,但TTL信号抗干扰能力较差,数据在传输过程中很容易出错;且TTL信号的通信距离也很短;

RS232

串口通讯理解
串口通讯理解

在目前的其它工业控制使用的串口通信中,一般只使用RXD、TXD 以及GND 三条信号线,直接传输数据信号,而RTS、CTS、DSR、DTR 及DCD 信号都被裁剪掉了,这主要是考虑到近程通信与远程通信问题。

特性:

工作方式:单端(非平衡)

节点数:点对点通讯(1收1发)

最大传输距离:50ft ( 50 * 0.3048 = 15.24m)

最大传输速率:20kbit/s

连接方式:点对点(全双工)

电气特性:-3V ~ -15V表示逻辑1,3V ~ 15V表示逻辑0

常用芯片有max232、SP232等

缺点:通信距离短,速率低,而且只能点对点通信,无法组建多机通信系统,且容易受外界电气干扰导致信息传输错误。

RS-485

标准运行连接多个收发器,即具有多站能力,增加了多点、双向的通信能力

RS-485采用平衡发送和差分接收,因此具有抑制共模干扰的能力。

RS485有两线制和四线制两种接线,四线制只能实现点对点的通信方式,现很少采用,多采用的是两线制接线方式,这种接线方式为总线拓扑结构,在同一总线上最多可以挂接32个节点。

采用两线半双工传输,最大速率10Mb/s,电平逻辑是两线的电平差来决定的,提高抗干扰能力,传输距离长(几十米到上千米)。

特性:

工作方式:差分(平衡);

节点数:点对多通讯(1发32收);

最大传输距离:4000ft ( 4000 * 0.3048 = 1219.2m);

最大传输速率:10Mbit/s;

连接方式:多点对多点(两线制,半双工);

电气特性:2V ~ 6V表示逻辑1,-2V ~ -6V表示逻辑0;

串口通讯理解
串口通讯理解

具体编程

在32位的Windows系统中,串口和其它通信设备是作为文件处理的。串口的打开、关闭、读取和写入所用的函数与操作文件的函数完全一致。

打开串口

CreateFile()为读访问、写访问或读写访问“打开”串口。返回一个句柄、

HANDLE CreateFile
(
  LPCTSTR lpszName,
  DWORD fdwAccess,
  DWORD fdwShareMode,
  LPSECURITY_ATTRIBUTES lpsa,
  DWORD fdwCreate,
  DWORD fdwAttrsAndFlags,
  HANDLE hTemplateFile
 )

lpszName:指定要打开的串口逻辑名,用字符串表示,如“COM1”和“COM2”分别表示串口1和串口2。

·fdwAccess:用来指定串口访问的类型。与文件一样,串口也是可以被打开以供读取、写入或者两者兼有。因为大部分串口通信都是双向的,因此常常在设置中将两个标识符连接起来使用。如:

fdwAccess = GENERIC_READ | GENERIC_WRITE;

·fdwShareMode:指定该端口的共享属性。对于不能共享的串口,它必须设置为0。如果在当前的应用程序调用CreateFile()时,另一个应用程序已经打开了串口,该函数就会返回错误代码,原因是两个应用程序不能共享一个端口。

·Ipsa:引用安全性属性结构(SECURITY_ARRTIBUTES)。将该参数设置为NULL将为该端口分配缺省的安全性属性。

·fdwCreate:指定如果CreateFile()正在被已有的文件调用时应采取的动作。因为串口总是存在,fdwCreate必须设置成OPEN_EXISTING。该标志告诉Windows不用企图创建新端口,而是打开已经存在的端口

fdwAttrsAndFlags:描述了端口的各种属性。对于文件来说,有可能具有很多属性,但对于串口,异步设置为FILE_FLAG_OVERLAPPED。同步设置为0.

·hTemplateFile:指向模板文件的句柄,当端口处于打开状态时,不使用该参数,因而必须置成0。

//同步方式
        hCom = CreateFileA(portname, //串口名
            GENERIC_READ | GENERIC_WRITE, //支持读写
            0, //独占方式,串口不支持共享
            NULL,//安全属性指针,默认值为NULL
            OPEN_EXISTING, //打开现有的串口文件
            0, //0:同步方式,FILE_FLAG_OVERLAPPED:异步方式
            NULL);//用于复制文件句柄,默认值为NULL,对串口而言该参数必须置为NULL
关闭串口

只需要调用CloseHandle()函数关闭由CreateHandle()函数返回得句柄即可。

设置缓冲区

通过调用SetupComm()实现其它初始化工作。也可以不调用SetupComm()函数,Windows系统也会分配缺省的发送和接收缓冲区。

BOOL SetupComm
(
   HANDLE hFile, // 通信设备句柄
   DWORD dwInQueue, // 输入缓冲区大小
   DWORD dwOutQueue // 输出缓冲区大小
);
BOOL PurgeComm
(
   HANDLE hFile,  // 返回的句柄
   DWORD dwFlags  // 执行的动作
);

参数hFile指向由CreateFile函数返回的句柄,dwFlags表示执行的动作,这个参数可以是表表5中的任一个。参数hFile指向由CreateFile函数返回的句柄,可以调用GetLastError()函数获得进一步的错误信息。

描述

PURGE_TXABORT

即使发送操作没有完成,也终止所有的重叠发送操作,立即返回

PURGE_RXABORT

即使接收操作没有完成,也终止所有的重叠接收操作,立即返回

PURGE_TXCLEAR

清除发送缓冲区

PURGE_RXCLEAR

清除接收缓冲区

BOOL FlushFileBuffers
(
    HANDLE hFile  // 函数打开的句柄
);

如果要保证缓冲区的所有字符都被发送,应该调用FlushFileBuffer()函数。该函数只受流量控制的支配,不受超时控制的支配,它在所有的写操作完成后才返回。

获取配置

使用GetCommState()函数获取串口的当前配置。

BOOL GetCommState
(
   HANDLE hFile, // 通信设备句柄
   LPDCB lpDCB   // 指向device-control block structure的指针
);

如果GetCommState()函数调用成功,则返回值不为零。若函数调用失败,则返回值为零,如果想得到进一步的错误信息,可以调用GetLastError()函数来获取。

使用GetCommState()函数获取串口的当前配置

设置配置

调用SetCommState()函数配置修改过的DCB来配置端口。

BOOL SetCommState 
(
    HANDLE hFile, // 已打开的串口的句柄
    LPDCB lpDCB  // 指向DCB结构的指针
);

DCB结构的主要参数说明如下:

·DCBLength: 一字节为单位指定的DCB结构的大小。

·Baudrate: 用于指定串口设备通信的数据传输速率,它可以是实际的数据传输速率数值,也可以是下列数据之一:CBR_110, CBR_19200, CBR_300, CBR_38400, CBR_600, CBR_56000, CBR_1200, CBR_57600, CBR_2400, CBR_115200, CBR_4800, CBR_12800, CBR_9600, CBR_25600, CBR_14400。

·fBinary: 指定是否允许二进制。Win32API不支持非二进制传输,因此这个参数必须设置为TRUE,如果设置为FALSE则不能正常工作。

·fParity: 指定是否允许奇偶校验,如果这个参数设置为TRUE,则执行奇偶校验并报告错误信息。

·fOutxCtsFlow: 指定CTS是否用于检测发送流控制。当该成员为TRUE,而CTS为OFF时,发送将被挂起,直到CTS置ON。

·fOutxDsrFlow: 指定DSR是否用于检测发送流控制,当该成员为TRUE,而DSR为OFF时,发送将被挂起,直到DSR置ON。

·fDtrControl: 指定DTR流量控制,可以是表1中的任一值。

功能描述

DTR_CONTROL_DISABLE

禁止DTR线,并保持禁止状态

DTR_CONTROL_ENABLE

允许DTR线,并保持允许状态

DTR_CONTROL_HANDSHAKE

允许DTR握手,如果允许握手,则不允许应用程序使用EscapeCommFunction函数调整线路

·fDsrSensitivity: 指定通信驱动程序对DTR信号线是否敏感,如果该位置设为TRUE时,DSR信号为OFF,接收的任何字节将被忽略。

·fTXContinueOnXoff: 指定当接收缓冲区已满,并且驱动程序已经发送出XoffChar字符时发送是否停止。当该成员为TRUE时,在接收缓冲区内接收到了缓冲区已满的字节XoffLim,并且驱动程序已经发送出XoffChar字符终止接收字节之后,发送继续进行。该成员为FALSE时,接收缓冲区接收到代表缓冲区已空的字节XonLim,并且驱动程序已经发送出恢复发送的XonChar字符后,发送可以继续进行。

·fOutX: 该成员为TRUE时,接收到XoffChar之后停止发送,接收到XonChar之后发送将重新开始。

·fInX: 该成员为TRUE时,接收缓冲区内接收到代表缓冲区满的字节XoffLim之后,XoffChar发送出去,接收缓冲区接收到代表缓冲区已空的字节XonLim之后,XonChar发送出去。

·fErrorChar: 当该成员为TRUE,并且fParity为TRUE时,就会用ErrorChar成员指定的字符来代替奇偶校验错误的接收字符。

·fNull: 指明是否丢弃接收到的NULL( ASCII 0 )字符,该成员为TRUE时,接收时去掉空(零值)字节;反之则不丢弃。

表2 RTS 流量控制

功能描述

RTS_CONTROL_DISABLE

打开设备时禁止RTS线,并保持禁止状态

RTS_CONTROL_ENABLE

打开设备时允许RTS线,并保持允许状态

DTR_CONTROL_HANDSHAKE

允许握手。在接收缓冲区小于半满时将RTS 置为ON,在接收缓冲区超过3/4时将RTS置为OFF。如果允许握手,则不允许应用程序使用EscapeCommFunction函数调整线路

DTR_CONTROL_TOGGLE

当发送的字节有效,将RTS置为 ON,发送完缓冲区的所有字节后, RTS置为OFF

·fRtsControl: 指定 RTS 流量控制,可以取表2中的值。0值和DTR_CONTROL_HANDSHAKE等价。

·fAbortOnError: 如果发送错误,指定是否可以终止读、写操作。如果该位为TRUE,当发生错误时,驱动程序以出错状态终止所有的读写操作。只有当应用程序调用ClearCommError()函数处理后,串口才能接收随后的通信操作。

·fDummy2: 保留的位,没有使用。

·wReserved:没有使用,必须为零。

·XonLim: 指定在XOFF字符发送之前接收到缓冲区中可允许的最小字节数。

·XoffLim: 指定在XOFF字符发送之前缓冲区中可允许的最小可用字节数

·ByteSize: 指定端口当前使用的数据位数。

·Parity: 指定端口当前使用的奇偶校验方法。它的可能值如表3所示。

·StopBits: 指定串口当前使用的停止位数,可能值如表4所示。

表3 奇偶校验方法

功能描述

EVENPARITY

偶校验

MARKPARITY

标号校验

NOPARITY

无校验

ODDPARITY

奇校验

SPACEPARITY

空格效益

表4 停止位数描述

功能描述

ONESTOPBIT

1位停止位

ONE5STOPBITS

1.5位停止位

TWOSTOPBITS

2位停止位

·XonChar: 指明发送和接收的XON字符值,它表明允许继续传输。

·XoffChar: 指明发送和接收的XOFF字符值,它表示暂停数据传输。

·ErrorChar: 本字符用来代替接收到的奇偶校验发生错误的字符。

·EofChar: 用来表示数据的结束。

·EvtChar: 事件字符。当接收到此字符的时候,会产生一个事件。

·wReserved1: 保留的位,没有使用。

    // 配置参数 
    DCB p;
    memset(&p, 0, sizeof(p));
    p.DCBlength = sizeof(p);
    p.BaudRate = baudrate; // 波特率
    p.ByteSize = databit; // 数据位

    switch (parity) //校验位
    {
    case 0:
        p.Parity = NOPARITY; //无校验
        break;
    case 1:
        p.Parity = ODDPARITY; //奇校验
        break;
    case 2:
        p.Parity = EVENPARITY; //偶校验
        break;
    case 3:
        p.Parity = MARKPARITY; //标记校验
        break;
    }

    switch (stopbit) //停止位
    {
    case 1:
        p.StopBits = ONESTOPBIT; //1位停止位
        break;
    case 2:
        p.StopBits = TWOSTOPBITS; //2位停止位
        break;
    case 3:
        p.StopBits = ONE5STOPBITS; //1.5位停止位
        break;
    }

    if (!SetCommState(hCom, &p))
    {
        // 设置参数失败
        return false;
    }
超时设置

超时结构直接影响读和写的操作行为。当事先设定的超时间隔消逝时,ReadFile() 、ReadFileEx()、 WriteFile()和 WriteFileEx()操作仍未结束,那么超时设置将无条件结束读写操作,而不管是否已读出或已写入指定数量的字符。

在读或写操作期间发生的超时将不按错误处理,即读或写操作返回指定成功的值。对于同步读或写操作,实际传输的字节数由ReadFile()和Write()函数报告。对于异步操作,则有OVERLAPPED结构来获取。

如果欲获得当前超时参数,应用程序可以调用GetCommTimeouts()函数

BOOL GetCommTimeouts
(
  HANDLE hFile,
  LPCOMMTIMEOUTS lpCommTimeouts
);

如果要设置或改变原来的超时参数,应用程序可以调用SetCommTimeouts()函数

BOOL SetCommTimeouts
(
  HANDLE hFile,
  LPCOMMTIMEOUTS lpCommTimeouts
);
typedef struct_COMMTIMEOUTS
 {
    DWORD ReadIntervalTimeout;
    DWORD ReadTotalTimeoutMultiplier;
    DWORD ReadTotalTimeoutConstant;
    DWORD WriteTotalTimeoutMultiplier;
    DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

ReadIntervalTimeout:以ms为单位指定通信线路上两个字符到达之间的最大时间间隔。在ReadFile()操作期间,从接收到第一个字符时开始计时。如果任意两个字符到达之间的时间间隔超过这个最大值,则ReadFile()操作完成,并返回缓冲数据。如果被置为0,则表示不使用间隔超时。

·ReadTotalTimeoutMultiplier:以ms为单位指定一个系数,该系数用来计算读操作的总超时时间。

·ReadTotalTimeoutConstant:以ms为单位指定一个常数,该常数也用来计算读操作的总超时时间。

·WriteTotalTimeoutMultiplier:以ms为单位指定一个系数,该系数用来计算写操作的总超时时间。

·WriteTotalTimeoutConstant:以ms为单位指定一个常数,该常数也用来计算写操作的总超时时间。

Windows使用下面的式子计算总超时时间:

ReadTotalTimeout=( ReadTotalTimeoutMultiplier*bytes_to_read )+ ReadTotalTimeoutConstant;

WriteTotalTimeout=( WriteTotalTimeoutMultiplier*bytes_to_write )+ WriteTotalTimeoutConstant;


    //超时处理,单位:毫秒
    //总超时=时间系数×读或写的字符数+时间常量
    COMMTIMEOUTS TimeOuts;
    TimeOuts.ReadIntervalTimeout = 1000; //读间隔超时
    TimeOuts.ReadTotalTimeoutMultiplier = 500; //读时间系数
    TimeOuts.ReadTotalTimeoutConstant = 5000; //读时间常量
    TimeOuts.WriteTotalTimeoutMultiplier = 500; // 写时间系数
    TimeOuts.WriteTotalTimeoutConstant = 2000; //写时间常量
    SetCommTimeouts(hCom, &TimeOuts);
读串口

程序可以使用Win32API ReadFile()函数或者ReadFileEx()函数从串口中读取数据。ReadFile()函数对同步或异步操作都支持,而ReadFileEx()只支持异步操作。这两个函数都受到函数是否异步操作、超时操作等有关参数的影响和限定。

BOOL ReadFile
(
    HANDLE hFile,      // 指向标识的句柄
    LPVOID lpBuffer,    // 指向一个缓冲区
    DWORD nNumberOfBytesToRead, // 读取的字节数
    LPDWORD lpNumberOfBytesRead, // 指向调用该函数读出的字节数
    LPOVERLAPPED lpOverlapped   // 一个OVERLAPPED的结构
);

·hFile:指向标识的句柄。对串口来说,就是由CreateFile函数返回的句柄。该句柄必须拥有GENERIC_READ的权限。

·lpBuffer:指向一个缓冲区,该缓冲区主要用来存放从串口设备中读取的数据。

·nNumberOfBytesToRead:指定要从串口设备读取的字节数。

·lpNumberOfBytesRead:指向调用该函数读出的字节数。ReadFile()在读操作前,首先将其设置为0。Windows NT/2000中当lpOverlapped没有设置时,lpNumberOfBytesRead必须设置。当lpOverlapped设置时,lpNumberOfBytesRead可以不设置。这是可以调用GetOverlappedResult()函数获取实际的读取数值。Windows 9x中这个参数一定要设置。

·lpOverlapped:是一个OVERLAPPED的结构,该结构将在后面介绍。如果hFile以FILE_FLAG_OVERLAPPED方式常见,则需要此结构;否则,不需要此结构。

需要注意的是如果该函数因为超时而返回,那么返回值是TRUE。参数lpOverlapped 在操作时应该指向一个OVERLAPPED的结构,如果该参数为NULL ,那么函数将进行同步操作,而不管句柄是否是由 FILE_FLAG_OVERLAPPED 标志建立的。当ReadFile返回FALSE时,不一定就是操作失败,线程应该调用GetLastError函数分析返回的结果。例如,在重叠操作时如果操作还未完成函数返回,那么函数就返回FALSE,而且GetLastError函数返回ERROR_IO_PENDING。

写串口操作

可以使用Win32API函数WriteFile() 或者WriteFileEx()向串口中写数据。WriteFile()函数对同步或异步操作都支持,而WriteFileEx()只支持异步操作。这两个函数都受到函数是否异步操作、超时操作等有关参数的影响和限定。

BOOL WriteFile
(
   HANDLE hFile,      // 指向标识的句柄
   LPCVOID lpBuffer,    // 指向一个缓冲区
   DWORD nNumberOfBytesToWrite, // 指定要向串口设备写入的字节数
   LPDWORD lpNumberOfBytesWritten, // 指向调用该函数已写入的字节数
   LPOVERLAPPED lpOverlapped   // 一个OVERLAPPED的结构
);

·hFile:指向标识的句柄。对串口来说,就是由CreateFile函数返回的句柄。该句柄必须拥有GENERIC_WRITE的权限。

·lpBuffer:指向一个缓冲区,该缓冲区主要用来存放待写入串口设备的数据。

·nNumberOfBytesToWrite:指定要向串口设备写入的字节数。

·lpNumberOfBytesWritten:指向调用该函数已写入的字节数。WriteFile()在写操作前,首先将其设置为0。Windows NT/2000中当lpOverlapped没有设置时,lpNumberOfBytesWritten必须设置。当lpOverlapped设置时,lpNumberOfBytesWritten可以不设置。这是可以调用GetOverlappedResult()函数获取实际的读取数值。Windows 9x中这个参数一定要设置。

·lpOverlapped:是一个OVERLAPPED的结构,该结构将在后面介绍。如果hFile以FILE_FLAG_OVERLAPPED方式常见,则需要此结构;否则,不需要此结构。

如果函数调用成功,则返回值不为零;若函数调用失败,则返回值为零。调用GetLastError()函数可以获得进一步的出错信息。

通信状态和通信错误

如果在串口通信中发生错误,如发生中断,奇偶错误等,I/O操作将会终止。如果程序要进一步执行I/O操作,必须调用ClearCommError()函数。ClearCommError()函数有两个作用:第一个作用是清除错误条件;第二个作用是确定串口通信状态。ClearCommError()函数的声明如下:

BOOL ClearCommError

(

HANDLE hFile,

LPDWORD lpErrors,

LPCOMSTAT lpStat

);

其中主要参数介绍如下:

·hFile :标识通信设备,CreateFile()函数返回该句柄。

·lpErrors:指向用一个指明错误类型的掩码填充的32位变量。该参数可以是表6中各值的组合。

·lpStat:指向一个COMSTAT结构,该结构接收设备的状态信息。如果lpStat参数不设置,则没有设备状态信息被返回。

表6 通信错误列表

描述

CE_BREAK

硬件检测到一个中断条件

CE_FRAME

硬件检测到一个帧出错

CE_IOE

发生I/O错误

CE_MODE

模式出错,或者是句柄无效

CE_OVERRUN

超速错误

CE_RXOVER

接收缓冲区超限,或者是输入缓冲区中没有空间,或者实在文件结束符(EOF)接收后接收到一个字符

CE_RXPARITY

奇偶校验错误

CE_TXFULL

发送缓冲区满

CE_DNS

没有检测到并行设备

CE_OOP

并行设备缺纸

CE_PTO

并行设备发生超时错误

如果该函数调用成功,则返回值不为零;若函数调用失败,则返回值为零。调用GetLastError()函数可以获得进一步的出错信息。在同步操作时,可以调用ClearCommError()函数来确定串口的接收缓冲区处于等待状态的字节数,而后可以使用ReadFile()或者WriteFile()函数一次读写完。

COMSTAT结构存放有关通信设备的当前信息。该结构内容由ClearCommError()函数填写。COMSTAT结构声明如下:

串口通讯理解

typedef struct_COMSTAT

(

DWORD fCtsHold: 1;

DWORD fDsrHold: 1;

DWORD fRlsdHold: 1;

DWORD fXoffSent: 1;

DWORD fEof: 1;

DWORD fTxim: 1;

DWORD fReserved: 25;

DWORD cbInQue;

DWORD cbOutQue;

} COMSTAT,*LPCOMSTAT;

串口通讯理解

其中主要参数介绍如下:

·fCtsHold:指明是否等待CRS信号,如果为1,则发送等待。

·fDsrHold:指明是否等到DRS信号,如果为1,则发送等待。

·fRlsdHold:指明是否等待RLSD信号,如果为1,则发送等待。

·fXoffSent:指明收到XOFF字符后发送是否等待。如果为1,则发送等待。如果把XOFF字符发送给一系统时,该系统就把下一个字符当成XON,而不管实际字符是什么,此时发送将停止。

·fEof:EOF字符送出。

·fTxim:指明字符是否正等待被发送,如果为1,则字符正等待被发送。

·fReserved:系统保留。

·cbInQue:指明串行设备接收到的字节数。并不是指ReadFile操作要求读的字节数。

·cbOutQue:指明发送缓冲区尚未发送的字节数。如果进行不重叠写操作时值为0。

代码示例


#ifndef _WZSERIALPORT_H
#define _WZSERIALPORT_H
#include<iostream>
#include <string>
#include <vector>
using namespace std;


class WZSerialPort
{
public:
    WZSerialPort();
    ~WZSerialPort();

    // 打开串口,成功返回true,失败返回false
    // portname(串口名): 在Windows下是"COM1""COM2"等,在Linux下是"/dev/ttyS1"等
    // baudrate(波特率): 9600、19200、38400、43000、56000、57600、115200 
    // parity(校验位): 0为无校验,1为奇校验,2为偶校验,3为标记校验
    // databit(数据位): 4-8,通常为8位
    // stopbit(停止位): 1为1位停止位,2为2位停止位,3为1.5位停止位
    // synchronizable(同步、异步): 0为异步,1为同步
    //在这里已经对配置进行了初始化,你们可以自己改配置使用
    bool open(const char* portname, int baudrate = 115200, char parity = 0, char databit = 8, char stopbit = 1, char synchronizeflag = 1);

    //关闭串口,参数待定
    void close();

    //发送数据或写数据,成功返回发送数据长度,失败返回0
    int send(string dat);

    //接受数据或读数据,成功返回读取实际数据的长度,失败返回0
    string receive();


    //vector<unsigned char> revcmsg;

private:
    int pHandle[16];
    char synchronizeflag;



};

#endif


#include "client.h"

#include <stdio.h>
#include <string.h>

#include <WinSock2.h>
#include <windows.h>


WZSerialPort::WZSerialPort()
{

}

WZSerialPort::~WZSerialPort()
{

}

bool WZSerialPort::open(const char* portname,
    int baudrate,
    char parity,
    char databit,
    char stopbit,
    char synchronizeflag)
{
    this->synchronizeflag = synchronizeflag;
    HANDLE hCom = NULL;
    if (this->synchronizeflag)
    {
        //同步方式
        hCom = CreateFileA(portname, //串口名
            GENERIC_READ | GENERIC_WRITE, //支持读写
            0, //独占方式,串口不支持共享
            NULL,//安全属性指针,默认值为NULL
            OPEN_EXISTING, //打开现有的串口文件
            0, //0:同步方式,FILE_FLAG_OVERLAPPED:异步方式
            NULL);//用于复制文件句柄,默认值为NULL,对串口而言该参数必须置为NULL
    }
    else
    {
        //异步方式
        hCom = CreateFileA(portname, //串口名
            GENERIC_READ | GENERIC_WRITE, //支持读写
            0, //独占方式,串口不支持共享
            NULL,//安全属性指针,默认值为NULL
            OPEN_EXISTING, //打开现有的串口文件
            FILE_FLAG_OVERLAPPED, //0:同步方式,FILE_FLAG_OVERLAPPED:异步方式
            NULL);//用于复制文件句柄,默认值为NULL,对串口而言该参数必须置为NULL
    }

    if (hCom == (HANDLE)-1)
    {
        return false;
    }

    //配置缓冲区大小 
    if (!SetupComm(hCom, 1024, 1024))
    {
        return false;
    }

    // 配置参数 
    DCB p;
    memset(&p, 0, sizeof(p));
    p.DCBlength = sizeof(p);
    p.BaudRate = baudrate; // 波特率
    p.ByteSize = databit; // 数据位

    switch (parity) //校验位
    {
    case 0:
        p.Parity = NOPARITY; //无校验
        break;
    case 1:
        p.Parity = ODDPARITY; //奇校验
        break;
    case 2:
        p.Parity = EVENPARITY; //偶校验
        break;
    case 3:
        p.Parity = MARKPARITY; //标记校验
        break;
    }

    switch (stopbit) //停止位
    {
    case 1:
        p.StopBits = ONESTOPBIT; //1位停止位
        break;
    case 2:
        p.StopBits = TWOSTOPBITS; //2位停止位
        break;
    case 3:
        p.StopBits = ONE5STOPBITS; //1.5位停止位
        break;
    }

    if (!SetCommState(hCom, &p))
    {
        // 设置参数失败
        return false;
    }

    //超时处理,单位:毫秒
    //总超时=时间系数×读或写的字符数+时间常量
    COMMTIMEOUTS TimeOuts;
    TimeOuts.ReadIntervalTimeout = 1000; //读间隔超时
    TimeOuts.ReadTotalTimeoutMultiplier = 500; //读时间系数
    TimeOuts.ReadTotalTimeoutConstant = 5000; //读时间常量
    TimeOuts.WriteTotalTimeoutMultiplier = 500; // 写时间系数
    TimeOuts.WriteTotalTimeoutConstant = 2000; //写时间常量
    SetCommTimeouts(hCom, &TimeOuts);

    PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR);//清空串口缓冲区

    memcpy(pHandle, &hCom, sizeof(hCom));// 保存句柄

    return true;
}

void WZSerialPort::close()
{
    HANDLE hCom = *(HANDLE*)pHandle;
    CloseHandle(hCom);
}

int WZSerialPort::send(string dat)
{
    HANDLE hCom = *(HANDLE*)pHandle;

    if (this->synchronizeflag)
    {
        // 同步方式
        DWORD dwBytesWrite = dat.length(); //成功写入的数据字节数
        BOOL bWriteStat = WriteFile(hCom, //串口句柄
            (char*)dat.c_str(), //数据首地址
            dwBytesWrite, //要发送的数据字节数
            &dwBytesWrite, //DWORD*,用来接收返回成功发送的数据字节数
            NULL); //NULL为同步发送,OVERLAPPED*为异步发送
        if (!bWriteStat)
        {
            return 0;
        }
        return dwBytesWrite;
    }
    else
    {
        //异步方式
        DWORD dwBytesWrite = dat.length(); //成功写入的数据字节数
        DWORD dwErrorFlags; //错误标志
        COMSTAT comStat; //通讯状态
        OVERLAPPED m_osWrite; //异步输入输出结构体

        //创建一个用于OVERLAPPED的事件处理,不会真正用到,但系统要求这么做
        memset(&m_osWrite, 0, sizeof(m_osWrite));
        m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, L"WriteEvent");

        ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误,获得设备当前状态
        BOOL bWriteStat = WriteFile(hCom, //串口句柄
            (char*)dat.c_str(), //数据首地址
            dwBytesWrite, //要发送的数据字节数
            &dwBytesWrite, //DWORD*,用来接收返回成功发送的数据字节数
            &m_osWrite); //NULL为同步发送,OVERLAPPED*为异步发送
        if (!bWriteStat)
        {
            if (GetLastError() == ERROR_IO_PENDING) //如果串口正在写入
            {
                WaitForSingleObject(m_osWrite.hEvent, 1000); //等待写入事件1秒钟
            }
            else
            {
                ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误
                CloseHandle(m_osWrite.hEvent); //关闭并释放hEvent内存
                return 0;
            }
        }
        return dwBytesWrite;
    }
}

string WZSerialPort::receive()
{
    HANDLE hCom = *(HANDLE*)pHandle;
    string rec_str = "";
    char buf[1024];
    if (this->synchronizeflag)
    {
        //同步方式
        DWORD wCount = 1024; //成功读取的数据字节数
        BOOL bReadStat = ReadFile(hCom, //串口句柄
            buf, //数据首地址
            wCount, //要读取的数据最大字节数
            &wCount, //DWORD*,用来接收返回成功读取的数据字节数
            NULL); //NULL为同步发送,OVERLAPPED*为异步发送

        for (int i = 0; i < strlen(buf); i++)
        {
            if (buf[i] != -52)
            {
                //cout << buf[i];
                rec_str += buf[i];
                //revcmsg.push_back(buf[i]);
            }
            else
            {
                break;
            }


        }


        return rec_str;
    }
    else
    {
        //异步方式
        DWORD wCount = 1024; //成功读取的数据字节数
        DWORD dwErrorFlags; //错误标志
        COMSTAT comStat; //通讯状态
        OVERLAPPED m_osRead; //异步输入输出结构体

        //创建一个用于OVERLAPPED的事件处理,不会真正用到,但系统要求这么做
        memset(&m_osRead, 0, sizeof(m_osRead));
        m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, L"ReadEvent");

        ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误,获得设备当前状态
        if (!comStat.cbInQue)
            return ""; //如果输入缓冲区字节数为0,则返回false
            //std::cout << comStat.cbInQue << std::endl;
        BOOL bReadStat = ReadFile(hCom, //串口句柄
            buf, //数据首地址
            wCount, //要读取的数据最大字节数
            &wCount, //DWORD*,用来接收返回成功读取的数据字节数
            &m_osRead); //NULL为同步发送,OVERLAPPED*为异步发送
        if (!bReadStat)
        {
            if (GetLastError() == ERROR_IO_PENDING) //如果串口正在读取中
            {
                //GetOverlappedResult函数的最后一个参数设为TRUE
                //函数会一直等待,直到读操作完成或由于错误而返回
                GetOverlappedResult(hCom, &m_osRead, &wCount, TRUE);
            }
            else
            {
                ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误
                CloseHandle(m_osRead.hEvent); //关闭并释放hEvent的内存
                return "";
            }
        }
        for (int i = 0; i < strlen(buf); i++)
        {
            if (buf[i] != -52)
            {
                rec_str += buf[i];
                //revcmsg.push_back(buf[i]);
            }
            else
            {
                break;
            }


        }

        return rec_str;
    }
}

// test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include "client.h"

using namespace std;
int main()
{
    std::cout << "Hello World!\n";
    WZSerialPort w;
    //这里是选择端口号,其他波特率信息可在头文件修改,或者在下面重新赋值。
    if (w.open("COM3"))
    {
        cout << "打开成功" << endl;
        cout << "在这里我发送:恭喜发财" << endl;

        w.send("恭喜发财");
        //w.close();
    }
    else
    {
        cout << "打开失败" << endl;
    }

    while (true)
    {
        //w.receive();
        cout << "receive: " << w.receive() << endl;
        
        //w.revcmsg.clear();


    }
    
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件

虚拟串口工具和串口工具见:

链接:https://pan.baidu.com/s/15BfvpeWpIPRauCico3Y44Q

提取码:zd4y

另外可以借鉴:https://blog.csdn.net/qq_41480046/article/details/82220155?spm=1001.2101.3001.6650.13&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-13-82220155-blog-104156394.pc_relevant_aa2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-13-82220155-blog-104156394.pc_relevant_aa2&utm_relevant_index=21文章来源地址https://www.toymoban.com/news/detail-428567.html

到了这里,关于串口通讯理解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 采用串口中断方式实现串口通信

    中断方式 中断方式是处理器和外部设备的数据传输方式。一方通过申请中断的方式与另一方进行数据传输,收发双方可以并行工作。 中断系统 中断装置和中断处理程序统称为中断系统。 中断系统是计算机的重要组成部分。实时控制、故障自动处理、计算机与外围设备间的数

    2024年02月16日
    浏览(36)
  • TTL电平串口通信连接多个从机,二极管即可改造UART串口为一主多从总线 一对多通讯

    在重新翻阅了许多遍串口相关的书籍资料以后,(其实是看了485、232,modbus协议以后还是觉得再尝试尝试…) 改用以上连线后,通过串口助手发送数据测试… ** ** 虽然电路很简单,查阅了许多资料都建议使用RS485/232或者是改用SPI,CAN的通信,但是还是发个博记录一下吧。 关于连

    2024年02月11日
    浏览(36)
  • FPGA串口(RS422)调试笔记:理解串口通信与调试记录

    探索FPGA串口(RS422)通信的调试过程与记录,包括串口通信概念解析、调试步骤、代码示例以及数据拼接方法。记录调试中遇到的问题与解决方案,展示对串口通信功能的实现与乐趣体验。

    2023年04月08日
    浏览(42)
  • 介绍串行和并行两种通信方式

    参考文章: 串行和并行- CSDN搜索 并行传输和串行传输的区别_金陵大掌柜的博客-CSDN博客 (1)串行和并行是计算机领域中两个重要的概念。串行是指在计算机中,任务按照顺序一个一个执行,每个任务执行完后才能执行下一个任务。而并行是指多个任务同时进行,即多个任务

    2024年02月09日
    浏览(37)
  • HAL库中断方式进行串口通信

    软件: STM32CubeMX:6.6.0 KEIl:5.37.0.0 FlyMcu:0.188 硬件: STM32F103ZET6 1.1 设置SYS 1.2 设置RCC 1.3 设置USART 1.4 设置NVIC 1.5 设置时钟 1.6 创建项目 2.1 printf函数设置 在 main.c 和 usart.c 中添加头文件 #include \\\"stdio.h\\\" 之后,勾选Target中的use MicroLIB,在 mian.c 文件中,添加如下代码,进行重定义 2.2 设

    2024年02月03日
    浏览(36)
  • SpringBoot项目模块间通信的两种方式

    说明:在微服务架构开发中,一个请求是通过模块之间的互相通信来完成的,如下面这个场景: 创建两个子模块:订单模块(端口8081)、用户模块(端口8082),两个模块之间没有联系,现在需要查询订单,根据订单中的用户ID,查询该订单对应的用户信息。 (两个模块是独

    2024年02月15日
    浏览(47)
  • STM32通过DMA方式实现串口通信

    目录 一、DMA工作原理  二、创建工程项目 三、编写代码 1.在main.c写入以下函数 2.main函数中的while循环中写入以下代码

    2024年02月15日
    浏览(45)
  • # STM32中断方式实现串口通信(标准库)

    主要任务 : 1)当stm32接收到字符“s”时, 停止持续发送 “hello windows!”; 当接收到字符“t”时, 持续发送 “hello windows!”; 2)当stm32接收到字符“stop stm32!”时,停止持续发送“hello windows!”; 当接收到字符“go stm32!”时,持续发送“hello windows!” 实验工具: (1)软件 标

    2024年02月11日
    浏览(34)
  • FANUC机器人PROFINET通讯方式组态

    1  机器人机架号101为机器人做主站,机器人网口3.4做主站 2 机器人机架号102为plc做主站,机器人网口1.2做从站, 机器人基本上都是配置FANUC自身的R834板卡。R834板卡的Profinet通讯设置硬件选项:双通道Profinet板卡,有的板1,2也能做主站​ 1、 Fanuc m ini-slot Profinet carrier board ;

    2024年02月07日
    浏览(58)
  • Android Usb(OTG)串口通信,Service 后台服务实现方式

    以下是几个开源的 Android 串口通信库: 1. SerialPort:https://github.com/felHR85/SerialPort 这是一个非常流行的 Android 串口通信库,支持多种设备和多种波特率,并且提供了非常详细的文档和示例代码。 2. android-serialport-api:https://github.com/cepr/android-serialport-api 这是一个简单易用的 And

    2024年02月11日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包