admin 管理员组文章数量: 1086019
2024年2月18日发(作者:不用strcat连接两个字符串)
VC实现串口通信例程
WIN95界面下的VC++串口通讯程序在WIN32下是不建议对端口进行操作的,在WIN32中所有的设备都被看成是文件,串行口也不例外也是作为文件来进行处理的。这是我的一份关于串口编程的读书笔记,对于使用VC进行编程的同行应该有一定的帮助。
1.打开串口:
在Window 95下串行口作为文件处理,使用文件操作对串行口进行处理。使用CreateFile()打开串口,CreateFile()将返回串口的句柄。
HANDLE CreateFile(
LPCTSTR lpFileName, // pointer to name of the file
DWORD dwDesiredAccess, // access (read-write) mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes
DWORD dwCreationDistribution, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to file with attributes to copy
);
lpFileName: 指明串口制备,例:COM1,COM2
dwDesiredAccess: 指明串口存取方式,例:GENERIC_READ|GENERIC_WRITE
dwShareMode: 指明串口共享方式
lpSecurityAttributes: 指明串口的安全属性结构,NULL为缺省安全属性
dwCreateionDistribution: 必须为OPEN_EXISTIN
dwFlagAndAttributes: 对串口唯一有意义的是FILE_FLAG_OVERLAPPED
hTemplateFile: 必须为NULL
2.关闭串口:
CloseHandle(hCommDev);
3.设置缓冲区长度:
BOOL SetupComm(
HANDLE hFile,
DWORD dwInQueue,
DWORD dwOutQueue
);
OP结构:
// handle of communications device
// size of input buffer
// size of output buffer
可使用GetCommProperties() 取得COMMPROP结构,COMMPROP结构中记载了系
统支持的各项设置。
typedef struct _COMMPROP {
WORD wPacketLength;
WORD wPacketVersion;
DWORD dwServiceMask;
DWORD dwReserved1;
DWORD dwMaxTxQueue;
DWORD dwMaxRxQueue;
DWORD dwMaxBaud;
DWORD dwProvSubType;
DWORD dwProvCapabilities;
DWORD dwSettableParams;
DWORD dwSettableBaud;
WORD wSettableData;
WORD wSettableStopParity;
DWORD dwCurrentTxQueue;
DWORD dwCurrentRxQueue;
DWORD dwProvSpec1;
DWORD dwProvSpec2;
WCHAR wcProvChar[1];
} COMMPROP;
// cmmp
// packet size, in bytes
// packet version
// services implemented
// reserved
// max Tx bufsize, in bytes
// max Rx bufsize, in bytes
// max baud rate, in bps
// specific provider type
// capabilities supported
// changeable parameters
// allowable baud rates
// allowable byte sizes
// stop bits/parity allowed
// Tx buffer size, in bytes
// Rx buffer size, in bytes
// provider-specific data
// provider-specific data
// provider-specific data
参数dwMaxBaud的取值有:
BAUD_075 75 bps
BAUD_110 110 bps
BAUD_134_5 134.5 bps
BAUD_150 150 bps
BAUD_300 300 bps
BAUD_600 600 bps
BAUD_1200 1200 bps
BAUD_1800 1800 bps
BAUD_2400 2400 bps
BAUD_4800 4800 bps
BAUD_7200 7200 bps
BAUD_9600 9600 bps
BAUD_14400 14400 bps
BAUD_19200 19200 bps
BAUD_38400 38400 bps
BAUD_56K 56K bps
BAUD_57600 57600 bps
BAUD_115200 115200 bps
BAUD_128K 128K bps
BAUD_USER Programmable baud rates available
参数dwProvSubType的取值有:
PST_FAX 传真设备
PST_LAT LAT协议
PST_MODEM 调制解调器设备
PST_NETWORK_BRIDGE 未指定的网桥
PST_PARALLELPORT 并口
PST_RS232 RS-232口
PST_RS422 RS-422口
PST_RS423 RS-432口
PST_RS449 RS-449口
PST_SCANNER 扫描仪设备
PST_TCPIP_TELNET TCP/IP Telnet协议
PST_UNSPECIFIED 未指定
PST_X25 X.25标准
dwProvCapabilities
PCF_16BITMODE 支持特殊的16位模式
PCF_DTRDSR 支持DTR(数据终端就绪)/DSR(数据设备就绪)
PCF_INTTIMEOUTS 支持区间超时
PCF_PARITY_CHECK 支持奇偶校验
PCF_RLSD 支持RLSD(接收线信号检测)
PCF_RTSCTS 支持RTS(请求发送)/CTS(清除发送)
PCF_SETXCHAR 支持可设置的XON/XOFF
PCF_SPECIALCHARS 支持特殊字符
PCF_TOTALTIMEOUTS 支持总(占用时间)超时
PCF_XONXOFF 支持XON/XOFF流控制
标准RS-232和WINDOW支持除PCF_16BITMODE和PCF_SPECIALCHAR外的所有功 参数dwSettableParams的取值有:
SP_BAUD 可配置波特率
SP_DATABITS 可配置数据位个数
SP_HANDSHAKING 可配置握手(流控制)
SP_PARITY 可配置奇偶校验模式
SP_PARITY_CHECK 可配置奇偶校验允许/禁止
SP_RLSD 可配置RLSD(接收信号检测)
SP_STOPBITS 可配置停止位个数
标准RS-232和WINDOW支持以上所有功能
参数wSettableData 的取值有:
DATABITS_5 5个数据位
DATABITS_6 6个数据位
DATABITS_7 7个数据位
DATABITS_8 8个数据位
DATABITS_16 16个数据位
DATABITS_16X 通过串行硬件线路的特殊宽度路径
WINDOWS 95支持16的所有设置
能
结构:
typedef struct _DCB { // dcb
DWORD DCBlength; // sizeof(DCB)
DWORD BaudRate; // current baud rate
指定当前的波特率
DWORD fBinary: 1; // binary mode, no EOF check
指定是否允许二进制模式,
WINDOWS 95中必须为TRUE
DWORD fParity: 1; // enable parity checking
指定奇偶校验是否允许
DWORD fOutxCtsFlow:1; // CTS output flow control
指定CTS是否用于检测发送控制。
当为TRUE是CTS为OFF,发送将被挂起。
DWORD fOutxDsrFlow:1; // DSR output flow control
指定CTS是否用于检测发送控制。
当为TRUE是CTS为OFF,发送将被挂起。
DWORD fDtrControl:2; // DTR flow control type
DTR_CONTROL_DISABLE值将DTR置为OFF, DTR_CONTROL_ENABLE值将DTR置为ON, DTR_CONTROL_HANDSHAKE允许DTR"握手",DWORD fDsrSensitivity:1; // DSR
sensitivity 当该值为TRUE时DSR为OFF时接收的字节被忽略
DWORD fTXContinueOnXoff:1; // XOFF continues Tx
指定当接收缓冲区已满,并且驱动程序已经发
送出XoffChar字符时发送是否停止。
TRUE时,在接收缓冲区接收到缓冲区已满的字节XoffLim且驱动程序已经发送出XoffChar字符中止接收字节之后,发送继续进行。
FALSE时,在接收缓冲区接收到代表缓冲区已空的字节XonChar且驱动程序已经发送出恢复发送的XonChar之后,发送继续进行。
DWORD fOutX: 1; // XON/XOFF out flow control
TRUE时,接收到XoffChar之后便停止发送
接收到XonChar之后将重新开始
DWORD fInX: 1; // XON/XOFF in flow control
TRUE时,接收缓冲区接收到代表缓冲区满的XoffLim之后,XoffChar发送出去
接收缓冲区接收到代表缓冲区空的XonLim之后,XonChar发送出去
DWORD fErrorChar: 1; // enable error replacement
该值为TRUE且fParity为TRUE时,用ErrorChar 成员指定的字符代替奇偶校验错误的接收字符
DWORD fNull: 1; // enable null stripping
TRUE时,接收时去掉空(0值)字节
DWORD fRtsControl:2; // RTS flow control
RTS_CONTROL_DISABLE时,RTS置为OFF
RTS_CONTROL_ENABLE时, RTS置为ON
RTS_CONTROL_HANDSHAKE时,
当接收缓冲区小于半满时RTS为ON
当接收缓冲区超过四分之三满时RTS为OFF
RTS_CONTROL_TOGGLE时,
当接收缓冲区仍有剩余字节时RTS为ON ,否则缺省为OFF
DWORD fAbortOnError:1; // abort reads/writes on error
TRUE时,有错误发生时中止读和写操作
DWORD fDummy2:17; // reserved
未使用
WORD wReserved; // not currently used
未使用,必须为0
WORD XonLim; // transmit XON threshold
指定在XON字符发送这前接收缓冲区中可允许的最小字节数
WORD XoffLim; // transmit XOFF threshold
指定在XOFF字符发送这前接收缓冲区中可允许的最小字节数
BYTE ByteSize; // number of bits/byte, 4-8
指定端口当前使用的数据位
BYTE Parity; // 0-4=no,odd,even,mark,space
指定端口当前使用的奇偶校验方法,可能为:
EVENPARITY,MARKPARITY,NOPARITY,ODDPARITY
BYTE StopBits; // 0,1,2 = 1, 1.5, 2
指定端口当前使用的停止位数,可能为:
ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS
char XonChar; // Tx and Rx XON character
指定用于发送和接收字符XON的值
char XoffChar; // Tx and Rx XOFF character
指定用于发送和接收字符XOFF值
char ErrorChar; // error replacement character
本字符用来代替接收到的奇偶校验发生错误时的值
char EofChar; // end of input character
当没有使用二进制模式时,本字符可用来指示数据的结束
char EvtChar; // received event character
当接收到此字符时,会产生一个事件
WORD wReserved1; // reserved; do not use 未使用
} DCB;
6.改变端口设置
使用如下的两个方法
BOOL GetCommState(hComm,&dcb);
BOOL SetCommState(hComm,&dcb);
7.改变普通设置
BuildCommDCB(szSettings,&DCB);
szSettings的格式:baud parity data stop
例: "baud=96 parity=n data=8 stop=1"
简写:"96,N,8,1"
szSettings 的有效值
baud: //传输频率
11 or 110 = 110 bps
15 or 150 = 150 bps
30 or 300 = 300 bps
60 or 600 = 600 bps
12 or 1200 = 1200 bps
24 or 2400 = 2400 bps
48 or 4800 = 4800 bps
96 or 9600 = 9600 bps
19 or 19200= 19200bps
parity:
n=none
e=even
o=odd
m=mark
s=space
data: //数据位
5,6,7,8
StopBit://停止位
1,1.5,2
NFIG结构:
typedef struct _COMM_CONFIG {
DWORD dwSize;
WORD wVersion;
WORD wReserved;
DCB dcb;
DWORD dwProviderSubType;
DWORD dwProviderOffset;
DWORD dwProviderSize;
WCHAR wcProviderData[1];
} COMMCONFIG, *LPCOMMCONFIG;
可方便的使用
BOOL CommConfigDialog(
LPTSTR lpszName,
HWND hWnd,
LPCOMMCONFIG lpCC
);
来设置串行口。
9.超时设置:
可通过COMMTIMEOUTS结构设置超时,
typedef struct _COMMTIMEOUTS {
DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutConstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;
区间超时:(仅对从端口中读取数据有用)它指定在读取两个字符之间要经历的时间
总超时: 当读或写特定的字节数需要的总时间超过某一阈值时,超时触发.
超时公式:
ReadTotalTimeout = (ReadTotalTimeoutMultiplier * bytes_to_read)
+ ReadToTaltimeoutConstant
WriteTotalTimeout = (WriteTotalTimeoutMuliplier * bytes_to_write)
+ WritetoTotalTimeoutConstant
NOTE:在设置超时时参数0为无限等待,既无超时
参数MAXDWORD为立即返回
超时设置:
GetCommTimeouts(hComm,&timeouts);
SetCommTimeouts(hComm,&timeouts);
10.查询方式读写数据
例程:
COMMTIMEOUTS to;
DWORD ReadThread(LPDWORD lpdwParam)
{
BYTE inbuff[100];
DWORD nBytesRead;
if(!(Capabilities&PCF_INTTIMEOUTS))
return 1L;
memset(&to,0,sizeof(to));
tervalTimeout = MAXDWORD;
SetCommTimeouts(hComm,&to);
while(bReading)
{
if(!ReadFile(hComm,inbuff,100,&nBytesRead,NULL))
locProcessCommError(GetLastError());
else if(nBytesRead)
locProcessBytes(inbuff,nBytesRead);
}
PurgeComm(hComm,PURGE_RXCLEAR);
return 0L;
}
NOTE:
PurgeComm()是一个清除函数,它可以中止任何未决的后台读或写,并且可以冲掉I/O缓冲区.
BOOL PurgeComm(HANDLE hFile,DWORD dwFlags);
dwFlages的有效值:
PURGE_TXABORT: 中止后台写操作
PRUGE_RXABORT: 中止后台读操作
PRUGE_TXCLEAR: 清除发送缓冲区
PRUGE_RXCLEAR: 清除接收缓冲区
技巧:
可通过ClearCommError()来确定接收缓区中处于等待的字节数。
BOOL ClearCommError(
HANDLE hFile, // handle to communications device
LPDWORD lpErrors, // pointer to variable to receive error codes
LPCOMSTAT lpStat // pointer to buffer for communications status
);
ClearCommError()将返回一个COMSTAT结构:
typedef struct _COMSTAT { // cst
DWORD fCtsHold : 1; // Tx waiting for CTS signal
DWORD fDsrHold : 1; // Tx waiting for DSR signal
DWORD fRlsdHold : 1; // Tx waiting for RLSD signal
DWORD fXoffHold : 1; // Tx waiting, XOFF char rec`d
DWORD fXoffSent : 1; // Tx waiting, XOFF char sent
DWORD fEof : 1; // EOF character sent
DWORD fTxim : 1; // character waiting for Tx
DWORD fReserved : 25; // reserved
DWORD cbInQue; // bytes in input buffer
DWORD cbOutQue; // bytes in output buffer
} COMSTAT, *LPCOMSTAT;
其中的cbInQue和cbOutQue中即为缓冲区字节。
11.同步I/O读写数据
COMMTIOMOUTS to;
DWORD ReadThread(LPDWORD lpdwParam)
{
BYTE inbuff[100];
DWORD nByteRead,dwErrorMask,nToRead;
COMSTAT comstat;
if(!Capabilities&PCF_TOTALTIMEOUTS)
return 1L;
memset(&to,0,sizeof(to));
talTimeoutMultiplier = 5;
talTimeoutConstant = 50;
SetCommTimeouts(hComm,&to);
while(bReading)
{
ClearCommError(hComm,&dwErrorMask,&comstat);
if(dwErrorMask)
locProcessCommError(dwErrorMask);
if(e >100)
nToRead = 100;
else
nToRead = e;
if(nToRead == 0)
continue;
if(!ReadFile(hComm,inbuff,nToRead,&nBytesRead,NULL))
locProcessCommError(GetLastError());
else
if(nBytesRead)
locProcessBytes(inbuff,nBytesRead);
}
return 0L;
}
12.异步I/O读写数据
当CreateFile()中的fdwAttrsAndFlags参数为FILE_FLAG_OVERLAPPEN时, 端口是为异步I/O打开的,此时可以在ReadFile的最后一个参数中指定一个OVERLAPPED结构,使数据的读操作在后台进行。WINDOWS 95包括了异步I/O的许多变种。
typedef struct _OVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED;
对于串行口仅hEvent成员有效,其于成员必须为0。
例程:
COMMTIMEOUTS to;
...
DWORD ReadThread((LPDWORD lpdwParam)
{
BYTE inbuff[100];
DWORD nRytesRead,endtime,lrc;
static OVERLAPPED o;
if(!Capabilities & PCF_TOTALTIMEOUTS)
return 1L;
memset(&to,0,sizeof(to));
talTimeoutMultiplier = 5;
talTimeoutConstant = 1000;
SetCommTimeouts(hComm,&to);
= CreateEvent(NULL,TRUE,FALSE,NULL);
while(bReading)
{
if(!ReadFile(hComm,inbuff,10,&nBytesRead,&o))
{
nBytesRead = 0;
if(lrc=GetLastError() == ERROR_IO_PENDING)
{
endtime = GetTickCount() + 1000;
while(!GetOverlappedResult(hComm,&o,&nBytesRead,FALSE))
if(GetTickCount() > endtime) break;
}
if(nBytesRead) locProcessBytes(inbuff,nBytesRead);
}
else
{
if(nBytesRead) locProcessBytes(inbuff,nBytesRead);
ResetEvent();
}
}
PurgeComm(hComm,PURGE_RXCLEAR);
return 0L;
}
这一例程是对一开始读缓冲区就读到所需的字节时的处理:
while(bReading)
{
if(!ReadFile(hComm,inbuff,10,&nBytesRead,&o))
{
if((lrc=GetLastError()) ==ERROR_IO_PENDING)
{
if(GetOverlappedResult(hComm,&o,&nBytesRead,TRUE))
{
if(nBytesRead)
locProcessBytesa(inbuff,nBytesRead);
}
else
locProcessCommError(GetLastError());
}
else
locProcessCommError(GetLastError));
}
else
if(nBytesRead) locProcessBytes(inbuff,nBytesRead);
ResetEvent();
}
13.事件驱I/O读写:
GetCommMask(hComm,&dwMask)
Windows 95报告给应用程序的事件由此方法返回。
SetCommMasl(hComm,&dwMask)
添加或修改Windows 95所报告的事件列表。
事件掩码如下:
EV_BREAK 检测到输入为止
EV_CTS CTS(清除发送)信号改变状态
EV_DSR DSR(数据设置就绪)信号改变状态
EV_ERR 发生了线路状态错误.
线路状态错误为:
CE_FRAME(帧错误)
CE_OVERRUN(接收缓冲区超限)
CE_RXPARITY(奇偶校验错误)
EV_RING 检测到振铃
EV_RLSD RLSD(接收线路信号检测)信号改变状态
EV_EXCHAR 接收到一个字符,并放入输入缓冲区
EV_RXFLAG 接收到事件字符(DCB成员的EvtChar成员),度放入输入缓冲区
EV_TXEMPTY 输出缓冲区中最后一个字符发送出去
在用SetCommMask指定了有用的事件后,应用程序可调用WaitCommEvent()来等待事件发生.
BOOL WaitCommEvent(
HANDLE hFile, // handle of communications device
LPDWORD lpEvtMask, // address of variable for event that occurred
LPOVERLAPPED lpOverlapped, // address of overlapped structure
);
此方法可以以同步或异步方式操作
例程:
COMMTIMEOUTS to;
...
DWORD ReadTherad(LPDWORD lpdwParam)
{
BYTE binbuff[100];
DWORD nBytesRead,dwEvent,dwError;
COMSTAT cs;
SetCommMask(hComm,EV_RXHAR);
while(bReading)
{
if(WaitCommEvent(hComm,&dwEvent,NULL))
{
ClearCommError(hComm,&dwError,&cs);
if((dwEvent&EV_RXCHAR)&&e)
{
if(!ReadFile(hComm,inbuff,e,&nBytesRead,NULL)
locProcessCommError(GetLastError());
}
else
{
if(nByteRead)
locProcessBytes(inbuff,nBytesRead);
}
else
locProcessCommError(GetLastError());
}
PurgeComm(hComm,PURGE_RXCLEAR);
return 0L;
}
NOTE: SetCommMask(hComm,0)可使WaitCommEvent()中止.
可使用GetCommmodemStatus()方法,例程:
if(Capabilities&PCF_RTSCTS)
{
SetCommMask(hComm,EV_CTS);
WaitCommEvent(hComm,&dwMask,NULL);
if(dwMask&EV_CTS)
{
GetCommModemStatus(hComm,&dwStatus)
if(dwStatus&MS_CTS_ON) /* CTS stransition OFF-ON */
else /* CTS stransition ON-OFF */
}
}
MS_CTS_ON CTS为ON
MS_DSR_ON DSR为ON
MS_RING_ON RING为ON
MS_ELSD_ON RLSD为ON
14.错误
当发生错误时应用方法ClearCommError(hComm,&dwErrorMask,&constat)得到错误掩
码。
CE_BREAK 中止条件
CE_FRAME 帧错误
CW_IOE 一般I/O错误,常伴有更为详细的错误标志
CE_MODE 不支持请求的模式
CE_OVERRUN 缓冲区超限下一个字符将丢失
CE_RXOVER 接收缓冲区超限
CE_RXPARITY 奇偶校验错误
CE_TXFULL 发送缓冲区满
CE_DNS 没有选择并行设备
CE_PTO 并行设备发生超时
CE_OOP 并行设备缺纸
15.控制命令
EscapeCommFunction()可将硬件信号置ON或OFF,模拟XON或XOFF
BOOL EscapeCommFunction(
HANDLE hFile, // handle to communications device
DWORD dwFunc // extended function to perform
);
dwFunc的有效值(可用’|’同时使用多个值)
CLRDTR DTR置OFF
CLRRTS RTS置OFF
SETDTR STR置ON
SETRTS TRS置ON
SETXOFF 模拟XOFF字符的接收
SETXON 模拟XON字符的接收
SETBREAK 在发送中产生一个中止
CLRBREAK 在发送中清除中止
串口通讯—RS-232-C详解
串行通信接口标准经过使用和发展,目前已经有几种。但都是在RS-232标准的基础上经过改进而形成的。所以,以RS-232C为主来讨论。RS-323C标准是美国EIA(电子工业联合会)与BELL等公司一起开发的1969年公布的通信协议。它适合于数据传输速率在0~20000b/s范围内的通信。这个标准对串行通信接口的有关问题,如信号线功能、电器特性都作了明确规定。由于通行设备厂商都生产与RS-232C制式兼容的通信设备,因此,它作为一种标准,目前已在微机通信接口中广泛采用。
在讨论RS-232C接口标准的内容之前,先说明两点:
首先,RS-232-C标准最初是远程通信连接数据终端设备DTE(Data Terminal Equipment)与数据通信设备DCE(Data Communication Equipment)而制定的。因此这个标准的制定,并未考虑计算机系统的应用要求。但目前它又广泛地被借来用于计算机(更准确的说,是计算机接口)与终端或外设之间的近端连接标准。显然,这个标准的有些规定及和计算机系统是不一致的,甚至是相矛盾的。有了对这种背景的了解,我们对RS-232C标准与计算机不兼容的地方就不难理解了
其次,RS-232C标准中所提到的“发送”和“接收”,都是站在DTE立场上,而不是站在DCE的立场来定义的。由于在计算机系统中,往往是CPU和I/O设备之间传送信息,两者都是DTE,因此双方都能发送和接收。
一、RS-232-C
RS-232C标准(协议)的全称是EIA-RS-232C标准,其中EIA(Electronic Industry
Association)代表美国电子工业协会,RS(ecommeded standard)代表推荐标准,232是标识号,C代表RS232的最新一次修改(1969),在这之前,有RS232B、RS232A。。它规定连接电缆和机械、电气特性、信号功能及传送过程。常用物理标准还有有EIARS-232-C、EIARS-422-A、EIARS-423A、EIARS-485。这里只介绍EIARS-232-C(简称232,RS232)。 例如,目前在IBM PC机上的COM1、COM2接口,就是RS-232C接口。
1.电气特性
EIA-RS-232C对电器特性、逻辑电平和各种信号线功能都作了规定。
在TxD和RxD上:逻辑1(MARK)=-3V~-15V
逻辑0(SPACE)=+3~+15V
在RTS、CTS、DSR、DTR和DCD等控制线上:
信号有效(接通,ON状态,正电压)=+3V~+15V
信号无效(断开,OFF状态,负电压)=-3V~-15V
串口通讯的概念及接口电路
随着计算机系统的应用和微机网络的发展,通信功能越来越显的重要。这里所说的通信是只计算机与外界的信息交换。因此,通信既包括计算机与外部设备之间,也包括计算机和计算机之间的信息交换。由于串行通信是在一根传输线上一位一位的传送信息,所用的传输线少,并且可以借助现成的电话网进行信息传送,因此,特别适合于远距离传输。对于那些与计算机相距不远的人-机交换设备和串行存储的外部设备如终端、打印机、逻辑分析仪、磁盘等,采用串行方式交换数据也很普遍。在实时控制和管理方面,采用多台微机处理机组成分级分布控制系统中,各CPU之间的通信一般都是串行方式。所以串行接口是微机应用系统常用的接口。
许多外设和计算机按串行方式进行通信,这里所说的串行方式,是指外设与接口电路之间的信息传送方式,实际上,CPU与接口之间仍按并行方式工作。
1 串行通信的概念
图1-1
所谓“串行通信”是指外设和计算机间使用一根数据信号线(另外需要地线,可能还需要控制线),数据在一根数据信号线上一位一位地进行传输,每一位数据都占据一个固定的时间长度。如图1-1所示。这种通信方式使用的数据线少,在远距离通信中可以节约通信成本,当然,其传输速度比并行传输慢。
由于CPU与接口之间按并行方式传输,接口与外设之间按串行方式传输,因此,在串行接口中,必须要有“接收移位寄存器”(串→并)和“发送移位寄存器”(并→串)。典型的串行接口的结构如1-2所示。
图1-2
在数据输入过程中,数据1位1位地从外设进入接口的“接收移位寄存器”,当“接收移位寄存器”中已接收完1个字符的各位后,数据就从“接收移位寄存器”进入“数据输入寄存器”。CPU从“数据输入寄存器”中读取接收到的字符。(并行读取,即D7~D0同时被读至累加器中)。“接收移位寄存器”的移位速度由“接收时钟”确定。
在数据输出过程中,CPU把要输出的字符(并行地)送入“数据输出寄存器”,“数据输出寄存器”的内容传输到“发送移位寄存器”,然后由“发送移位寄存器”移位,把数据1位1位地送到外设。“发送移位寄存器”的移位速度由“发送时钟”确定。
接口中的“控制寄存器”用来容纳CPU送给此接口的各种控制信息,这些控制信息决定接口的工作方式。
“状态寄存器”的各位称为“状态位”,每一个状态位都可以用来指示数据传输过程中的状态或某种错误。例如,用状态寄存器的D5位为“1”表示“数据输出寄存器”空,用D0位表示“数据输入寄存器满”,用D2位表示“奇偶检验错”等。
能够完成上述“串<- ->并”转换功能的电路,通常称为“通用异步收发器”(UART:Universal Asynchronous Receiver and Transmitter),典型的芯片有:Intel 8250/8251,16550。
串口通讯在CCD相机系统中的应用
CCD相机系统在运行过程中,有许多来自工作现场的数据需要实时采集,处理和记录。以便上级管理系统及时掌握相机的工作状态。并且上级管理系统需要实时调整相机参数,并发出相应的指令,使得相机采集到的图像像质更好。
MCS-51单片机内部含有一个可编程全双工串行通信接口,该接口电路不仅能同时进行数据的发送和接收,也可作为一个同步移位寄存器使用。MCS-51单片机串行口的结构由串行口控制寄存器、发送和接收电路等三部分组成。
串行通信是一种能把二进制数据按位传送的通信,故它所需传输线条数极少,特别适用于分级、分层和分布式控制系统以及远程通信之中[1]。根据实际使用的需要,CCD相机系统与上级管理系统之间的通讯由单片机串口来完成。本文对该系统中的串行通信系统加以介绍。
2系统串行通讯体系
按照串行数据的同步方式,串行通信可以分为同步通信和异步通信两类。本系统采用同步通信方式。数据的输入和输出接口有各自的时钟来控制,这两个时钟源彼此独立,互不同步[1]。
由于CCD相机系统的数据输入和数据输出不会在同一时刻进行,本系统的串行通讯体系结构包含以下几个部分:单片机小系统,串行数据输入模块,串行数据输出模块。其中单片机小系统结构简单,性能成熟,在这里不再赘述。现就串口输入、串口输出模块做出说明:
2.1串行数据输入模块
相机参数的注入由上级管理系统负责,CCD相机系统通过数据总线将参数直接读入相机系统中的单片机小系统。
数据输入门控信号通过单片机P1口连接。当上级管理系统欲向CCD相机系统注入调整参数指令时,先使门控信号有效,单片机系统即准备好接收数据;然后在时钟信号的配合下,一位一位地读入数据,并通过数据总线将并行数据读入单片机。
2.2串行数据输出模块
在每个间隔时间到来时,CCD相机系统向上级数据管理系统送出相机系统的参数,以备检查相机系统的状态是否正常。电路设计如图1。在方式0下,串行数据输出电路通过几
个串入并出的移位寄存器,由MCS-51单片机的RxD线串行输出数据,并从移位寄存器的最高位串行输出。
3系统的串口通讯协议
3.1系统串口通讯协议特点
由于传输距离和可靠性的要求,该通讯协议具备如下特点[2]:
4系统串口通讯软件实现
4.1通讯协议格式说明
以下对本系统的具体通讯协议格式进行说明。该协议的数据包结构大体如下所示:
(1)采用一对一的通讯方式,无握手过程。通讯中,上级管理系统为主站,CCD相(2) 为了有效地识别相机参数。针对各项指令参数设定了各自的命令代码,但帧长(3)由于校验编码是差错检测的核心,对提高数据传输的可靠性非常重要,且奇偶机系统为从站;
度保持不变。
校验方式简单可行,故采用奇偶校验方式保证数据传送的准确性;
(1)帧头
为了准确发送和接收串口数据.将帧头设定为OxAA(10101010)。
(2)命令代码
在本系统的通讯协议中,针对各项指令参数设定了各自的命令代码,这里不作具体描述。
数据代码紧跟在命令代码之后,用户可根据情况取5位、6位、7位或8位、低位在前高位在后。
(4)奇偶校验位
奇偶校验这一字节是按照通常的通讯协议标准来计算的。即:一帧数据除帧头外其它字节的累加和[3-4]。
4.2串口通讯的波特率
波特率是每秒钟传送二进制数码的位数,单位是bps(bit per second),即位/秒。波特率是串行通信的重要指标,用于表征数据传输的速度。波特率越高,数据传输速度越快。
通信的数据传输速率较高,通常可达56000bps或更高。但同步通信的缺点是要求发送时钟和接收时钟保持严格同步。
4.3串口通讯软件流程图
在充分了解用户需求的基础上,首先确定系统结构,进而确定软件开发平台和工具,采用自顶向下、逐步求精的设计方法划分软件的功能模块和功能单元,可提高软件开发的效率。
本系统使用串口的方式0,用汇编语言编程。软件流程图(只包含串口通讯子程序)如图2所示。
(3)数据代码
VC++ 的串口通讯
在VC++中有两种方法可以进行串口通讯。一种是利用Microsoft公司提供的ActiveX控件 Microsoft Communications Control。另一种是直接用VC++访问串口。下面将简述这两种方法。
一、Microsoft Communications Control
Microsoft公司在WINDOWS中提供了一个串口通讯控件,用它,我们可以很简单的利用串口进行通讯。在使用它之前,应将控件加在应用程序的对话框上。然后再用ClassWizard
生成相应的对象。现在我们可以使用它了。
该控件有很多自己的属性,你可以通过它的属性窗口来设置,也可以用程序设置。我推荐用程序设置,这样更灵活。
SetCommPort:指定使用的串口。
GetCommPort:得到当前使用的串口。
SetSettings:指定串口的参数。一般设为默认参数"9600,N,8,1"。这样方便与其他串口进行通讯。
GetSettings:取得串口参数。
SetPortOpen:打开或关闭串口,当一个程序打开串口时,另外的程序将无法使用该串口。
GetPortOpen:取得串口状态。
GetInBufferCount:输入缓冲区中接受到的字符数。
SetInPutLen:一次读取输入缓冲区的字符数。设置为0时,程序将读取缓冲区的全部字符。
GetInPut:读取输入缓冲区。
GetOutBufferCount:输出缓冲区中待发送的字符数。
SetOutPut:写入输出缓冲区。
一般而言,使用上述函数和属性就可以进行串口通讯了。以下是一个范例。
#define MESSAGELENGTH 100
class CMyDialog : public CDialog
{
protected:
VARIANT InBuffer;
VARIANT OutBuffer;
CMSComm m_Com;
public:
......
}
BOOL CMyDiaLog::OnInitDialog()
{
CDialog::OnInitDialog();
m_mPort(1);
if (!m_tOpen()) {
m_tings("57600,N,8,1");
m_tOpen(true);
m_ufferCount(0);
SetTimer(1,10,NULL);
l=new unsigned short[MESSAGELENGTH];
l=new unsigned short[MESSAGELENGTH];
=VT_BSTR;
}
return true;
}
void CMyDiaLog::OnTimer(UINT nIDEvent)
{
if (m_ufferCount()>=MESSAGELENGTH) {
InBuffer=m_ut();
// handle the InBuffer.
// Fill the OutBuffer.
m_put(OutBuffer);
}
CDialog::OnTimer(nIDEvent);
}
用该控件传输的数据是UNICODE格式。关于UNICODE和ANSI的关系和转换请参看MSDN。
关于该控件的其他详细资料请查看MSDN关于COMM CONTROL部分。
二、直接用VC++访问串口。
在VC++中,串口和磁盘文件可以统一的方式来简单读写。这两者几乎没有什么不同,只是在WINDOWS 9X下磁盘文件只能做同步访问,而串口只能做异步访问。
CreateFile:用指定的方式打开指定的串口。通常的方式为
m_hCom = CreateFile( "COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL );
m_hCom为文件句柄。GENERIC_READ | GENERIC_WRITE指定可以对串口进行读写操作。第三个参数0表示串口为独占打开。OPEN_EXISTING表示当指定串口不存在时,程序将返回失败。 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED则表示文件属性。当打开串口时,必须指定 FILE_FLAG_OVERLAPPED,它表示文件或设备不会维护访问指针,则在读写时,必须使用OVERLAPPED 结构指定访问的文件偏移量。
ReadFile:读取串口数据。
WriteFile:向串口写数据。
CloseHandle:关闭串口。
COMMTIMEOUTS:COMMTIMEOUTS主要用于串口超时参数设置。COMMTIMEOUTS结构如下:
typedef struct _COMMTIMEOUTS {
DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutConstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;
ReadIntervalTimeout:两字符之间最大的延时,当读取串口数据时,一旦两个字符传输的时间差超过该时间,读取函数将返回现有的数据。设置为0表示该参数不起作用。
ReadTotalTimeoutMultiplier:读取每字符间的超时。
ReadTotalTimeoutConstant:一次读取串口数据的固定超时。所以在一次读取串口的操作中,其超时为ReadTotalTimeoutMultiplier乘以读取的字节数再加上
ReadTotalTimeoutConstant。将ReadIntervalTimeout设置为MAXDWORD,并将ReadTotalTimeoutMultiplier 和ReadTotalTimeoutConstant设置为0,表示读取操作将立即返回存放在输入缓冲区的字符。
WriteTotalTimeoutMultiplier:写入每字符间的超时。
WriteTotalTimeoutConstant:一次写入串口数据的固定超时。所以在一次写入串口的操作中,其超时为WriteTotalTimeoutMultiplier乘以写入的字节数再加上
WriteTotalTimeoutConstant。
SetCommTimeouts函数可以设置某设备句柄的超时参数,要得到某设备句柄的超时参数可以用GetCommTimeouts函数。
DCB:DCB结构主要用于串口参数设置。该结构太庞大,这里就不一一讲述了,有兴趣者可查看MSDN关于DCB的描述。其中下面两个是比较重要的属性。
BaudRate:串口的通讯速度。一般设置为9600。
ByteSize:字节位数。一般设置为8。
DCB结构可以用SetCommState函数来设置,并可以用GetCommState来得到现有串口的属性。
SetupComm:设置串口输入、输出缓冲区。
OVERLAPPED:保存串口异步通讯的信息。具体结构如下:
typedef struct _OVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED;
Internal,InternalHigh是保留给系统使用的,用户不需要设置。
Offset,OffsetHigh是读写串口的偏移量,一般设置OffsetHigh为NULL,可以支持2GB数据。
hEvent读写事件,因为串口是异步通讯,操作可能被其他进程堵塞,程序可以通过检查该时间来得知是否读写完毕。事件将在读写完成后,自动设置为有效。
通过以上这些函数和结构,我们就可以通过串口进行通讯了,现在我们具体看下面的实例:
BOOL CSerial::Open( int nPort, int nBaud )
{
if( m_bOpened ) return( TRUE );
char szPort[15];
DCB dcb;
wsprintf( szPort, "COM%d", nPort );
m_hComDev = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL );
if( m_hComDev == NULL ) return( FALSE );
memset( &m_OverlappedRead, 0, sizeof( OVERLAPPED ) );
memset( &m_OverlappedWrite, 0, sizeof( OVERLAPPED ) );
COMMTIMEOUTS CommTimeOuts;
tervalTimeout = 0xFFFFFFFF;
talTimeoutMultiplier = 0;
talTimeoutConstant = 0;
otalTimeoutMultiplier = 0;
otalTimeoutConstant = 5000;
SetCommTimeouts( m_hComDev, &CommTimeOuts );
m_ = CreateEvent( NULL, TRUE, FALSE, NULL );
m_ = CreateEvent( NULL, TRUE, FALSE, NULL );
gth = sizeof( DCB );
GetCommState( m_hComDev, &dcb );
te = nBaud;
ze = 8;
if( !SetCommState( m_hComDev, &dcb ) ||
!SetupComm( m_hComDev, 10000, 10000 ) ||
m_ == NULL ||
m_ == NULL ){
DWORD dwError = GetLastError();
if( m_ != NULL ) CloseHandle( m_ );
if( m_ != NULL ) CloseHandle( m_ );
CloseHandle( m_hComDev );
return FALSE;
}
m_bOpened = TRUE;
return m_bOpened;
}
int CSerial::InBufferCount( void )
{
if( !m_bOpened || m_hComDev == NULL ) return( 0 );
DWORD dwErrorFlags;
COMSTAT ComStat;
ClearCommError( m_hIDComDev, &dwErrorFlags, &ComStat );
return (int)e;
}
DWORD CSerial::ReadData( void *buffer, DWORD dwBytesRead)
{
if( !m_bOpened || m_hComDev == NULL ) return 0;
BOOL bReadStatus;
DWORD dwErrorFlags;
COMSTAT ComStat;
ClearCommError( m_hComDev, &dwErrorFlags, &ComStat );
if( !e ) return 0;
dwBytesRead = min(dwBytesRead,(DWORD) e);
bReadStatus = ReadFile( m_hComDev, buffer, dwBytesRead, &dwBytesRead,
&m_OverlappedRead );
if( !bReadStatus ){
if( GetLastError() == ERROR_IO_PENDING ){
WaitForSingleObject( m_, 2000 );
return dwBytesRead;
}
return 0;
}
return dwBytesRead;
}
DWORD CSerial::SendData( const char *buffer, DWORD dwBytesWritten)
{
if( !m_bOpened || m_hComDev == NULL ) return( 0 );
BOOL bWriteStat;
bWriteStat = WriteFile( m_hComDev, buffer, dwBytesWritten, &dwBytesWritten,
&m_OverlappedWrite );
if( !bWriteStat){
if ( GetLastError() == ERROR_IO_PENDING ) {
WaitForSingleObject( m_, 1000 );
return dwBytesWritten;
}
return 0;
}
return dwBytesWritten;
}
多线程技术在VC++串口通信程式中的应用
1 概述
在现代的各种实时监视系统和通信系统中,在视窗系统 9X/NT下利用VC++对RS-232
串口编程是常用的手段。视窗系统 9X/NT是抢先式的多任务操作系统,程式对CPU的占用时间由系统决定。多任务指的是系统能同时运行多个进程,每个进程又能同时执行多个线程。进程是应用程式的运行实例,拥有自己的地址空间。每个进程拥有一个主线程,同时还能建立其他的线程。线程是操作系统分配CPU时间的基本实体,每个线程占用的CPU时间由系统分配,系统不停的在线程之间转换。进程中的线程共享进程的虚拟地址空间,能访问进程的资源,处于并行执行状态,这就是多线程的基本概念。
2 VC++对多线程的支持
使用MFC研发是较普遍的VC++编程方法。在VC++6.0下,MFC应用程式的线程由CWinThread对象表示。VC++把线程分为两种:用户界面线程和工作者线程。用户界面线程能够提供界面和用户交互,通常用于处理用户输入并相应各种事件和消息;而工作者线程主要用来处理程式的后台任务。
程式一般不必直接创建CWinThread对象,通过调用AfxBeginThread()函数就会自动创建一个CWinThread对象,从而开始一个进程。创建上述的两种线程都利用这个函数。
线程的终止取决于下列事件之一:线程函数返回;线程调用ExitThread()退出;异常情况下用线程的句柄调用TerminateThread()退出;线程所属的进程被终止。
3 多线程在串口通信中的应用
3.1 串口通信对线程同步的需求
因为同一进程的所有线程共享进程的虚拟地址空间,而在视窗系统 9X/NT系统下线程是汇编级中断,所以有可能多个线程同时访问同一个对象。这些对象可能是全局变量,MFC的对象,MFC的API等。串口通信的几个特点决定了必须采用措施来同步线程的执行。
串口通信中,对于每个串口对象,只有一个缓冲区,发送和接收都要用到,必须建立起同步机制,使得在一个时候只能进行一种操作,否则通信就会出错。
进行串口通信处理的不同线程之间需要协调运行。如果一个线程必须等待另一个线程结束才能运行,则应该挂起该线程以减少对CPU资源的占用,通过另一进程完成后发出的信号(线程间通信)来激活。
VC++提供了同步对象来协调多线程的并行,常用的有以下几种:
CSemaphore:信号灯对象,允许一定数目的线程访问某个共享资源,常用来控制访问共享资源的线程数量。
Cmutex:互斥量对象,一个时刻至多只允许一个线程访问某资源,未被占用时处于有信号状态,能实现对共享资源的互斥访问。
CEvent:事件对象,用于使一个线程通知其他线程某一事件的发生,所以也能用来封锁对某一资源的访问,直到线程释放资源使其成为有信号状态。适用于某一线程等待某事件发生才能执行的场合。
CCriticalSection:临界区对象,将一段代码置入临界区,只允许最多一个线程进入执行这段代码。一个临界区仅在创建他的进程中有效。
3.2 等待函数
Win32 API提供了能使线程阻塞其自身执行的等待函数,等待其监视的对象产生一定的信号才停止阻塞,继续线程的执行。其意义是通过暂时挂起线程减少对CPU资源的占用。在某些大型监视系统中,串口通信只是其中事务处理的一部分,所以必须考虑程式执行效率问题,当串口初始化完毕后,就使其处于等待通信事件的状态,减少消耗的CPU时间,提高程式运行效率。
常用的等待函数是WaitForSingleObject()和WaitForMultipleObjects(),前者可监测单个同步对象,后者可同时监测多个同步对象。
3.3 串口通信的重叠I/O方式
MFC对于串口作为文件设备处理,用CreateFile()打开串口,获得一个串口句柄。打开后SetCommState()进行端口设置,包括缓冲区设置,超时设置和数据格式等。成功后就能
调用函数ReadFile()和WriteFile()进行数据的读写,用WaitCommEvent()监视通信事件。CloseHandle()用于关闭串口。
在ReadFile()和WriteFile()读写串口时,能采取同步执行方式,也能采取重叠I/O方式。同步执行时,函数直到执行完毕才返回,因而同步执行的其他线程会被阻塞,效率下降;而在重叠方式下,调用的读写函数会即时返回,I/O操作在后台进行,这样线程就能处理其他事务。这样,线程能在同一串口句柄上实现读写操作,实现"重叠"。
使用重叠I/O方式时,线程要创建OVERLAPPED结构供读写函数使用,该结构最重要的成员是hEvent事件句柄。他将作为线程的同步对象使用,读写函数完成时hEvent处于有信号状态,表示可进行读写操作;读写函数未完成时,hEvent被置为无信号。
4 程式关键代码的实现
程式专门建立了一个串口通信类,下面给出关键成员函数的核心代码。
BOOL InitComm file://串口初始化,这里只给出关键步骤的代码,下同
{
HANDLE m_hComm;
COMMTIMEOUTS m_CommTimeouts;
m_hComm = CreateFile("COM1", file://在这里只使用串口1
GENERIC_READ | GENERIC_WRITE, file://打开类型为可读写
0, file://以独占模式打开串口
NULL, file://不设置安全属性
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, file://重叠I/O方式
0);
if (m_hComm == INVALID_HANDLE_VALUE) file://打开不成功
{return FALSE;}
m_tervalTimeout = 1000;
file://进行超时设置,读者应根据自己的实际需要设置
m_talTimeoutMultiplier = 500;
m_talTimeoutConstant = 5000;
m_otalTimeoutMultiplier = 500;
m_otalTimeoutConstant = 5000;
if (!SetCommTimeouts(m_hComm, &m_CommTimeouts))
{CloseHandle(m_hComm);
return FALSE;}
PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT |
PURGE_TXABORT); file://清缓冲
return TRUE;
}
以上是专门针对COM1的初始化,如果要利用同一函数对不同串口初始化,则要在初始化前先进入代码临界区,以确保在某一时刻只进行一个串口的初始化。
在串口初始化成功后,就能建立监视线程处理串口通信事件。下面是该线程的关键代码。
UINT CommThread(LPVOID pParam) file://用于监视串口的工作者线程
{
BOOL bResult = FALSE;
if (m_hComm) file://查看端口是否打开,这里m_hComm同上,作者在这里做了简化
PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT |
PURGE_TXABORT);
for (;;) file://只要线程运行,就处于监视端口行为的无限循环
{
bResult = WaitCommEvent(m_hComm, &Event, &m_ov);
file://m_ov是OVERLAPPED类型的成员变量
if (!bResult)
{ file://进行出错处理}
else
{
Event = WaitForMultipleObjects(4, m_hEvent, FALSE, INFINITE);
file://无限等待设定的事件发生,数组m_hEvent根据需要定义了须响应的接收,发送,关闭端口事件和OVERLAPPED类型的hEvent事件
switch (Event)
{ file://读写事件的响应处理过程,在此略}
}
return 0;
}
这样监视主程式就能使用AfxBeginThread()函数来产生CommThread串口监视线程。如果要实现对所有端口的同时监视,能分别对端口建立监视线程。
5 小结
作为一个机房监视系统的组成部分,本串口通信程式在VC++6.0下编译通过,在使用windows 98/NT的局域网里运行良好。
VB6.0在PLC与上位机通讯中的应用
1、引言
可编程控制器以其高可靠性,配置灵活和完善的功能,在工业控制系统中得到越来越广泛的应用。但对于操作员所需要的报表打印、趋势图形显示、工况查寻、参数在线修改等功能,PLC却不能直接方便地提供。所以通常采用计算机PC与PLC组成一个完整的监控系统。本文以台安TP02系列PLC为例,讨论用Visual Basic(VB)实现PLC与上位机的通讯。
2、VB在通讯控件中的使用
可编程控制器PLC与上位机PC之间的通信,下位机为PLC,基于其可靠性极高,主要承担控制功能,而上位PC机主要承担监察管理功能,有时兼备部分控制功能,如发出运行,
停止命令。VB语言是基于WINDOWS操作系统的功能强、易学易用、主要是面向学习对象的程序设计语言。VB带有专门管理串行通讯的MSComm控件,只需设置几个主要参数就可以实现PLC与PC串行通讯。要完成通信必须设置MSComm的相关属性值:
(1)CommPort:设置或传回通信连接端口代号
(2)Settings:设置初始化参数。以字符串的形式设置或传回连接速度、奇偶校验、数据位、停止位等4个参数
(3)PortOpen:设置或传回通信连接端口的状态
(4)Input:从输入寄存器传 回并移除字符
(5)Output:将一个字符串写入输出寄存器
(6)InputLen:指定由串行端口读入的字符串长度
(7)InBuFFerCount:传回在接收寄存器中的字符数
3、软硬件之间:
台安TP02与上位机PC通信时,为了实现两者的通信需要配备通信线。
4、通信程序的实现
4.1 通信初始化程序
首先,在窗体开始设计之前,添加MSComm控件。
4.2程序编写:
4.2.1 通信控件MSComm1属性设置:
rt = 1 设置端口号
gs = "19200,E,7,2" 设置通信参数
en = 0 设置读入字符串长度
en = True 设置通信端口状态
4.2.2 PLC运行程序:
STX$ = "::"
TransmitBuf$ = "01?5RUN"
sum$ = CheckSum(transmitBuf$)
ETX$ = Chr$(13)
SXD$ = STX$ + transmitBuf$ + sum$ + ETX$
= SXD$
Do
DoEvents
Loop Until erCount >= 12
In1$ =
4.2.3 从PLC读取资料,将寄存器内的数据资料在PC上显示以便观察监控(反应时间50ms):
STX$ = "::"
transmitBuf$ = "01?5MRVD000102"
sum$ = CheckSum(transmitBuf$)
ETX$ = Chr$(13)
SXD$ = STX$ + transmitBuf$ + sum$ + ETX$
= SXD$
Do
DoEvents
Loop Until erCount >= 20
InData$ =
n = Mid$(InData$, 10, 4)
n = Mid$(InData$, 14, 4)
4.2.4从PLC读取资料,将RelayC0001状态通过PC显示进行监控(反应时间50ms):
STX$ = "::"
transmitBuf$ = "01?5MCRC0001"
sum$ = CheckSum(transmitBuf$)
ETX$ = Chr$(13)
SXD$ = STX$ + transmitBuf$ + sum$ + ETX$
= SXD$
Do
DoEvents
Loop Until erCount >= 13
In4$ =
Coil$ = Mid$(In4$, 10, 1)
C1% = CInt(Coil$)
n = C1%
4.2.5 设定Relay状态,将Relay设定为ON(反应时间50ms):
Dim C1 As Integer
STX$ = "::"
transmitBuf$ = "01?5SCSY00011"
sum$ = CheckSum(transmitBuf$)
ETX$ = Chr$(13)
SXD$ = STX$ + transmitBuf$ + sum$ + ETX$
= SXD$
Do
DoEvents
Loop Until erCount >= 12
In2$ =
4.2.6 PLC停止运行:
STX$ = "::"
transmitBuf$ = "01?5STP"
sum$ = CheckSum(transmitBuf$)
ETX$ = Chr$(13)
SXD$ = STX$ + transmitBuf$ + sum$ + ETX$
= SXD$
Do
DoEvents
Loop Until erCount >= 12
In3$ =
4.2.7 VB6.0下CheckSum函数代码如下:
Private Function CheckSum(transmitBuf$)
L = Len(transmitBuf$)
Add = 0
Dim k, sum As Integer
For k = 1 To L
TJ$ = Mid$(transmitBuf$, k, 1)
Add = Add + Asc(TJ$)
Next k
Do While Add >= 256
Add = Add - 256
Loop
Add = 255 - Add + 1
tempBuf$ = Hex$(Add)
CheckSum = LTrim(tempBuf$)
End Function
5、结论
PLC与上位机的结合,并通过VB6.0传送数据所构成的计算机监控系统,对于近距离传输数据的现场控制来说是一种性价比很高的解决方案。充分的利用的PLC的抗干扰性能和PC强大的图形显示,浮点运算的特点,与之有效地结合,最大限度,合理的利用资源。
51单片机与VB串行通信的实现
介绍了AT89C52单片机与PC机串行通信的实现方法,串行存储器24C256的读写操作流程,并给出了具体通信接口电路、单片机串行通信程序流程以及利用VB6.0的通信控件MSComm实现PC机串行通信的程序。系统经过实际应用,效果令人满意
1. 引言:
随着计算机系统的应用和微机网络的发展,各种控制设备之间的通信功能越来越显得重
要。在设计的减振控制系统中,控制器(下位机)采用两片AT89C52单片机,分别用于对左右减振器实施控制,同时将测量的温度、电流、速度等信号按一定采样时间保存在E2PROM
ATC256中。为了能测试控制器的工作情况,包括初始安装时的状况测试和读取历史记录并做测试诊断以及能根据参数变化情况进行故障诊断,为此,同时开发了通信系统,上位机采用便携式PC机,上、下位机之间通过MAX485芯片实现串行数据通信。
2. 通信系统硬件电路设计
通信系统硬件电路设计的突出特点是,控制器的外围一改传统的并行扩展,而采用新型串行芯片进行串行总线扩展。与传统的并行扩展相比,具有体积小、性能价格比高、工作可靠性高的优点。存储器ATC256、通信芯片MAX485均是I2C总线器件。单片机AT89C52的串行数据发送端TXD和串行数据接受端RXD分别与MAX485驱动器输入端DI和驱动器输出端D0,接受器输出使能端RE接地,驱动器输出使能端DE接单片机的T1端(P3.5脚)。存储器ATC256的串行数据线SDA和串行时钟线SCL分别AT89C52的P3.7和P3.6相连,具体硬件电路框图如图1所示:
3.数据的存储—ATC256①
测得的温度、电流和速度等信号需实时保存,以便根据参数变化情况进行故障诊断和对加速度变化情况进行分析等。系统设计存储器采用美国ATMEL公司推出的串行E2PROM——24C256。24C256遵从I2C总线协议,通过数据线SDA和时钟线SLA两根线直接与单片机相连,不需要其它器件和外围电路。它具有256Kbit的位存储容量,按8位一个字节的方式可提供32K字节的存储空间。对ATC256的读写操作完全符合I2C总线的数据传送,传送的每一帧数据为一个字节,要求每传送一个字节后,对方回应一个应答位。发送时先放送数据最高位,每次传送开始有起始信号,结束时有停止信号。在系统的设计中,对ATC256的写操作采用字节写,读操作采用顺序读的方式。根据系统要求,每到一分钟就向ATC256中写入1条记录(包括三个温度、电流、速度等5个数据),上位机需要读取数据时,可根据上位机的读取指令读取任意条记录。
对ATC256的读/写程序流程如图2所示:
图2 字节写(左),连续读(右)程序流程
4.串行数据通信程序设计
4.1通信协议
本系统串行通信采用异步通信方式。协议如下:
1. 一帧数据由1位起始位、8位数据位、无奇偶校验位、1位停止位共10位组成。
2. 波特率设为2400bps。单片机串行口按方式1工作,波特率由定时器T1控制,
PC机串口波特率通过VB通讯控件的Settings属性设置,为保证数据传送的准确性,两者的波特率必须一致。
4.2下位机(单片机)串行通信及程序设计
单片机可以采用中断方式或查询RI(接受中断标志位)或TI(发送中断标志位)方式进行数据通信②。设计采用查询方式,在定时器T2中断子程序中查询RI,一旦检测到RI=1则转入接受数据子程序,在子程序中单片机读取从上位机发送的通信指令、读取记录个数等数据,经校验正确后,即从ATC256存储器中将历史记录数据上传给PC机,单片机发送数据子程序流程图如图3:
图3单片机发送数据子程序流程图
4.3上位机(PC机)串行通信及程序设计
1.编程方法
上位机利用Visual Basic 6.0编程。用VB6.0开发串行通信程序有两种法,一种是利用Windows的API函数;另一种是采用VB6.0的通信控件MSComm。利用API函数编写串行通信程序较为复杂,需要掌握大量的通信知识,其优点是可实现的功能更丰富、应用面更广泛,适合于编写较为复杂的低层次通信程序。而VB6.0的MSComm通信控件提供了标准的事件处理函数、事件、方法,并通过控件属性对串口参数进行设置,比较容易地解决了串口通信问题。
2.VB6.0的通信控件及通信方式③
MSComm是VB6.0提供的ActiveX控件,使用前需将该控件添加到VB工具栏。MSComm控件具有功能完善的串口数据发送和接受功能,有两种处理通信的方式,即事件驱动方式和查询方式,事件驱动方式是利用MSComm控件的OnComm事件捕获并处理通信错误事件,是处理串行端口交互作用的一种非常有效的方法;查询方式是通过检查CommEvent属性的值来判断事件和错误。
本系统采用事件驱动方式进行串口通信设计,图4为上位机通信界面,设计4个命令按纽,分别为发送命令、退出、保存、浏览数据;两个文本框,Text2用于输入需从下位机读取的记录个数,Text3用于显示下位机发送来的数据;进行数据通信的单片机和串行通信口的选择通过两组单选按纽完成。在发送命令按纽的Click事件中,将通信指令通过串行口发送给上位机,当选择左侧单片机时(变量LR(1)=1),通信指令为“ET”,选择右侧单片机时(变量LR(1)=2),通信指令为 “DT”,数据传送以回车符(十进制ASCII码为13)作为结束标记。
下面是MSComm控件的初始化程序、部分数据发送和接受程序:
MSComm控件的初始化(属性设置)程序:
t=1 ;设置串行端口(com1)
gs=2400,n,8,1 ;设置波特率及数据帧格式
Ode=1 ;数据接受按字节(binary)方式
erSize=4000 ;数据接受缓冲区大小为4000字节
en=0 ;INPUT读取缓冲区的所有内容
数据发送程序:
Private Sub Command1_Click()
Dim outbuf(1 To 6) As Byte
Dim lstr1 As String
Dim hstr2 As String
Dim len1 As Integer
d = False
len1 = Len(Hex(Val()))
………
If LR(1) = 1 Then
outbuf(1) = 69 ;对应字符“E”
Else
outbuf(1) = 68 ;对应字符“D”
End If
outbuf(2) = 84 ;对应字符“T”outbuf(3) = LR(1)
outbuf(4) = stoby(hstr2)
outbuf(5) = stoby(lstr1)
outbuf(6) = 13
hold = 5 * Val()
= outbuf
End Sub
数据接受程序:
Private Sub MScomm1_OnComm()
Select Case ent
Case comEvReceive
Dim inbuf() As Byte, i%, buf$
buf = ""
inbuf =
Rev_num = UBound(inbuf)
ReDim lnum(0 To Rev_num) As Integer
For i = 0 To Rev_num
lnum(i) = inbuf(i)
buf = buf + Str(inbuf(i)) + "
Next i
Case comEvSend
End Select
"
End Sub
由于MSComm控件数据接受设计为按字节方式,可接发的数值范围为0~255。当上位机读取数据个数超过255(1字节)时,为了能让下位机正确接受,解决方法是通过编制的函数stoby将文本框Text2输入的记录个数(字符型)转化成字节型,并分成两个字节送给下位机。另外,系统中有关数据保存、数据图表处理、打印等功能的实现在此不再赘述。
5.结论
I2C总线器件的采用简化了硬件电路设计,提高了可靠性。本系统已投入使用,经过实际应用表明,通信系统工作稳定、可靠。满足系统要求,每到一分钟就向ATC256中写入1条记录(5个数据),ATC256可保存12小时的记录,下位机需要读取数据时,可根据下位机的读取指令读取任意条记录。
错误!未找到引用源。错误!未找到引用源。
版权声明:本文标题:VC实现串口通信项目源码 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/p/1708248979a517716.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论