求一个用API操作串口的例子.(100分)

  • 主题发起人 主题发起人 rikhong
  • 开始时间 开始时间
R

rikhong

Unregistered / Unconfirmed
GUEST, unregistred user!
求一个用API操作串口的例子.别帖一堆出来,请给一个完整的,可编译的代码!THANKS!
vbyouxiang@126.com
 
SPComm完全对API进行了封装,而且是使用多线程的方式
 
2ccc.com上多的事,自己去找
 
//线程
unit CommManager;
//(4,53)按"."直接退出:目的地不会单一,至少有两种可能
//采集的项目列表可能也需要模仿选择工序的显示方法,不过如果少则不需要
interface

uses
Windows, Classes,SysUtils,ComCtrls,PComm,
StrUtils,ScktComp,StdCtrls,PublishDM,DateUtils,PublicFiles;

type
TCommThread = class(TThread)
private
{ Private declarations }
fCom: Integer;
fBortRate: Integer;
fLoopTime: Integer;//系统轮循等待时间
fComState: Integer;//端口状态变量 0初始状态;1正常;2失败
fThreadExit : Boolean;//线程退出标识
fMemo:TMemo;
fRevString : string;
sleeptime: integer;
fHandle:Integer;

fHcom,fPost_Event:Thandle; //Comm接收参数
fLpolW,fLpolR:Poverlapped; //Comm接收参数

fCommData : PCOMMDATA; //端口设置参数
m_buf : array [0..512] of Char;
function PortSet():boolean; //端口参数设置
protected
Public
ExistExecute : Boolean;
fTermIndex : Integer; //当前终端索引号
fCurTerm : PTermInfo;
fEdit : TEdit;

procedure Over;
procedure Execute; overRIDE;
constructor Create(CreateSuspended: Boolean=FALSE);
destructor Destroy; override;

Function SetNextTerm():pTermInfo;
procedure SendData();
Procedure DoForRevData();
//Moxa卡
Function InitComm():Boolean;//初始化端口
procedure SetCommParam(BaudRate:integer=B9600; Parity:integer=P_NONE; ByteSize:integer=BIT_8; StopBits:integer=STOP_1;
ibaudrate:integer=12; iparity:integer=0; ibytesize:integer=3; istopbits:integer=0;
Hw:Boolean=False; Sw:Boolean=False; Dtr: boolean=True; Rts : boolean=True);//设置端口参数
function ReTrySendData(strInfo:string):Longint;
//发送数据到终端
function CommInitialize():Boolean;
Procedure CommDestroy;
Procedure ReadStr();
Function WriteStr(Com:integer;const Str:String):Boolean;
Published
property Com :integer read fCom write fCom default 1;
property Handle : Integer read fHandle write fHandle default 0;
property BortRate :integer read fBortRate write fBortRate default 9600;
property LoopTime:Integer read fLoopTime write fLoopTime default 10;//系统轮循等待时间
property ComState:Integer read fComState write fComState default 0;//端口状态变量
property ExitThread: Boolean read fThreadExit write fThreadExit;
property Hcom:Thandle read fHcom write fHcom ;
property Post_Event:Thandle read fPost_Event write fPost_Event ;
property Memo:TMemo read fMemo write fMemo;
property Edit:TEdit read fEdit write fEdit;
end;

implementation

procedure TCommThread.Over;
begin
ExitThread := True;
ExistExecute := False;
end;

function TCommThread.CommInitialize():Boolean;
Var Lpdcb:TDCB;
begin
Result := True;
hcom:=createFile(pChar('com'+inttostr(fCom)), //串口名,可为com1-com4
generic_read or Generic_write,//访问模式
0, //共享模式,必须为0
nil, //安全属性指针
open_existing, ///找开方式必须为open_existing
File_Flag_Overlapped,//文件属性,本文设为交迭标志
0); //临时文件句柄,必须为0
if hcom<>invalid_Handle_Value then
begin
//BaudRate:波特率,可直接设为110、300、600、1200、2400、4800、9600、19200等值。
//ByteBits:数据位长度,可高为4-8。
//Parity:奇偶校验方式,0-4分别为无、偶、奇、空
//StopBits:停止位长度,0,1,2分别为1、1.5、2位
SetupComm(hcom,4096,4096); //设置缓冲区长度
getCommState(hcom,lpdcb); //设置串口
lpdcb.baudrate:=fBortRate;
lpdcb.stopbits:=0;
lpdcb.bytesize:=8;
lpdcb.parity:=0;
setCommState(hcom,lpdcb);
SetCommMask(Hcom,ev_Rxchar); //设置串口事件屏蔽
end else begin
//无法打开串口
Result := False;
exit;
end;
SendData;
New(flpolW);
New(flpolR);
fLpolW^.Internal:=0;
fLpolW^.InternalHigh:=0;
fLpolW^.Offset:=0;
fLpolW^.OffsetHigh:=0;
fLpolW^.hEvent:=Createevent(nil,true,False,nil);
fLpolr^.Internal:=0;
fLpolr^.InternalHigh:=0;
fLpolr^.Offset:=0;
fLpolr^.OffsetHigh:=0;
fLpolr^.hEvent:=Createevent(nil,true,False,nil);
PurgeComm(Hcom,Purge_TxAbort or Purge_RxAbort or Purge_Txclear or Purge_Rxclear);
fPost_Event:=Createevent(nil,true,true,nil);
//不行就用下面被注释的语句
//EscapeCommFunction(hcom,CLRDTR);
//EscapeCommFunction(hcom,CLRRTS);

EscapeCommFunction(hcom,CLRDTR); //第四针 置低电平
EscapeCommFunction(hcom,CLRRTS); //第七针 置低电平
Sleep(50); //延时50毫秒
EscapeCommFunction(hcom,SETDTR); //第四针 置低高平
EscapeCommFunction(hcom,SETRTS); //第七针 置低高平

end;
Procedure TCommThread.CommDestroy; //释放内存
begin
if (fLpolW<>nil) then begin
CloseHandle(fLpolW^.hEvent);
dispose(flpolW);
fLpolW:=Nil;
end;
if (fLpolR<>nil) then begin
CloseHandle(fLpolR^.hEvent);
dispose(flpolR);
fLpolR:=Nil;
end;
SetEvent(fPost_Event);
CloseHandle(fPost_Event);
CloseHandle(fHcom);
end;
Procedure TCommThread.ReadStr(); //接收数据
var
clear:boolean;
coms:TComStat;
cbNum,Cbread,lpErrors:Dword;
S,S1,S2,S3,S_Temp:String;
i,iPos,i1,i2:integer;
P:PProcBarCodeInfo;
pTmp:PTermInfo;
begin
clear:=clearCommerror(hcom,lperrors,@Coms);
if clear then
begin
cbnum:=Coms.cbInQue; //获取接收缓冲区待接收字节数
setlength(S_Temp,cbnum+1); //分配内存
ReadFile(hcom,PChar(S_Temp)^,cbnum,Cbread,fLpolR);//读串口
setlength(S_Temp,cbread); //分配
//读取数据
fRevString:=fRevString+S_Temp;
S:=TransferHexToString(fRevString);
try
if S='' then begin
if Length(fRevString)>100 then begin
fRevString:='';
end;
end else begin
fRevString:='';
iPos:=-1;
//获得地址、条码、标志
S1:=Copy(S,2,1);
if Copy(S,Length(S),1)='1' then S3:='OK'
else S3:='NG';
if MonitorType=3 then begin
//获得条码和OK/NG标志
S2:=Copy(S,3,Length(S)-3);
if S2<>'' then begin
for i:=0 to fTermList.Count-1 do begin
pTmp:=PTermInfo(fTermList);
if pTmp.TermiAddr=S1 then begin
iPos:=i;
Break;
end;
//if pTmp.TermiAddr=S1 then begin
// if pTmp.StateValue<>'OK' then iPos:=i;
// Break;
//end;
end;
if iPos<>-1 then begin
if (pTmp.BarCode<>S2) or (pTmp.StateValue<>S3) then begin
pTmp.BarCode:=S2;
pTmp.StateValue:=S3;
new(P);
P.TermiAddr:=S1; //十六进制表示
P.BarCode:=S2;
P.StateValue:=S3;
SaveInfoList.Add(P);
end;
end;
end;
end else begin
//仅获得OK/NG标志
G_TempState:=S3;
if SaveInfoList<>nil then begin
if (SaveInfoList.Count>0) and (S3<>'') then begin
P:=PProcBarCodeInfo(SaveInfoList[0]);
if UpperCase(Trim(P.StateValue))<>S3 then begin
//默认OK就可以了,不需要采集NG
if UpperCase(Trim(P.StateValue))<>'OK' then begin
P.StateValue:=S3;
P.SaveState:=True;
end;
end;
end;
end;
end;
end;
finally
P:=nil; //检查一下这样做之后是否能够得到数据?????????????
pTmp:=nil;
end;
SetEvent(fPost_Event); //同步事件置位
end else begin
CommError:=False;
end;
end;
Procedure TCommThread.DoForRevData(); //处理收到终端的信息
var
cbNum,Cbread,lpErrors:Dword;
S,S1,S2,S3,S_Temp:String;
i,iPos,i1,i2:integer;
P:PProcBarCodeInfo;
pTmp:PTermInfo;
begin
fRevString:=fRevString+S_Temp;
S:=TransferHexToString(fRevString);
try
if S='' then begin
if Length(fRevString)>100 then begin
fRevString:='';
end;
end else begin
fmemo.Lines.Add(s);
fRevString:='';
iPos:=-1;
//获得地址、条码、标志
S1:=Copy(S,2,1);
if Copy(S,Length(S),1)='1' then S3:='OK'
else S3:='NG';
fMemo.Lines.Add(S);
if MonitorType=3 then begin
//获得条码和OK/NG标志
S2:=Copy(S,3,Length(S)-3);
if S2<>'' then begin
for i:=0 to fTermList.Count-1 do begin
pTmp:=PTermInfo(fTermList);
if pTmp.TermiAddr=S1 then begin
iPos:=i;
Break;
end;
//if pTmp.TermiAddr=S1 then begin
// if pTmp.StateValue<>'OK' then iPos:=i;
// Break;
//end;
end;
if iPos<>-1 then begin
if (pTmp.BarCode<>S2) or (pTmp.StateValue<>S3) then begin
pTmp.BarCode:=S2;
pTmp.StateValue:=S3;
new(P);
P.TermiAddr:=S1; //十六进制表示
P.BarCode:=S2;
P.StateValue:=S3;
SaveInfoList.Add(P);
end;
end;
end;
end else begin
//仅获得OK/NG标志
if SaveInfoList<>nil then begin
if (SaveInfoList.Count>0) and (S3<>'') then begin
P:=PProcBarCodeInfo(SaveInfoList[0]);
if UpperCase(Trim(P.StateValue))<>S3 then begin
//默认OK就可以了,不需要采集NG
if UpperCase(Trim(P.StateValue))<>'OK' then begin
P.StateValue:=S3;
P.SaveState:=True;
end;
end;
end;
end;
end;
end;
finally
P:=nil; //检查一下这样做之后是否能够得到数据?????????????
pTmp:=nil;
end;
end;
Function TCommThread.WriteStr(Com:integer;const Str:String):Boolean;
var
DwCharsWritten,DwRes:Dword;
S_DATA:String;
BRes:boolean;
Begin
BRes:=False;
S_Data:=Str;
if fHcom<>INVALID_HANDLE_VALUE then
begin
DwCharsWritten:=0;
BRes:=WriteFile(fHcom,PChar(S_Data)^,Length(S_Data),
DwCharsWritten,fLpolW); //返回True,数据立即发送完成
if not BRes then
begin
if GetLastError()=Error_IO_Pending then
begin //正在发送数据
DwRes:=WaitForSingleObject(fLpolW^.hEvent,Infinite);
if DwRes=Wait_Object_0 then // 如果不相等,出错
BRes:=GetOverLappedResult(fhcom,fLpolW^,DwCharsWritten,False) //返回False,出错
else BRes:=true; //数据发送完成
end;
end;
end;
Result:=Bres;
end;

procedure TCommThread.Execute;
var
//Comm接收参数
dwEvtmask,dwOvres,bb:Dword;
RXFinish:Bool;
Len,i : integer;
begin
fRevString:='';
SleepTime := 0;
ExistExecute := true;
if fTermList.Count<=0 then exit;
fCurTerm := fTermList[0];
fTermIndex := 0;
While ExistExecute do begin
try
if CommListonType=0 then begin
DwEvtMask:=0;
RXFinish:=WaitCommEvent(fHcom,dwevtmask,fLpolR); //等待串口事件EV_RXCHAR
if not RXFinish then //如果返回True,已立即完成,否则继续判断
if GetLastError()=ERROR_IO_PENDING then //正在接收数据
begin
bb:=WaitForSingleObject(fLpolR^.hEvent,5);//等待5ms
Case bb of
Wait_Object_0: RXFinish:=GetOverLappedResult(fHcom,fLpolR^,dwOvRes,False);
//返回False,出错
Wait_TimeOut: RXFinish:=False;//定时溢出
else RXFinish:=False; //出错
end;
end else begin
RXFinish:=False;
CommError:=False;
end;
if RXFinish then
begin
if WaitForsingleobject(fPost_Event,infinite)=Wait_Object_0 then //等待同步事件置位
begin
resetEvent(fPost_Event); //同步事件复位
//在这里可以触发串口接收事件
ReadStr;
//Synchronize(ReadStr);
end;
end;
end else if CommListonType=1 then begin
len := sio_read(fCom,@m_buf,512);
while len>0 do begin //直到本次所有接收数据完毕
m_buf[len] := Char(0);
fRevString := fRevString + m_buf;
IF LENGTH(fRevString)>100 THEN BEGIN //防止接收数据超出正常的数据包大小,将该包不要
fRevString := '';
break;
END;
sleep(3);
len := sio_read(fCom,@m_buf,512);
end;
DoForRevData;
end;
SendData;
//Synchronize(SendData);
Sleep(LateTime);
except
end;
END;
// WriteLog(TermLog,'T');
end;

constructor TCommThread.Create(CreateSuspended: Boolean);
begin
fThreadExit := false;
inherited Create(CreateSuspended);
end;

destructor TCommThread.Destroy;
begin
inherited Destroy;
CommDestroy;
if fCommData <> nil then dispose(fCommData);
end;

//获取下一可用终端
Function TCommThread.SetNextTerm():pTermInfo;
VAR I: INTEGER;
Tmp: pTermInfo;
Str1: String;
begin
try
result := nil;

FOR I :=0 TO fTermList.Count -1 do begin
Inc(fTermIndex);
IF fTermIndex >= ftermlist.Count THEN BEGIN
fTermIndex := 0;
END;
Tmp := ftermlist[fTermIndex];
if Tmp.bStop THEN begin //终端不使用
continue;
end else begin ////终端在使用
result := Tmp;
break;
end;
end;
finally
Tmp:=nil;
end;
end;

Function TCommThread.InitComm():Boolean;//初始化端口
var ret:Integer;
begin
result := false;
ret := sio_open(fCom);
if ret <> SIO_OK then Exit;
if PortSet = false then begin
sio_close(fcom);
Exit;
end;
result := true;
end;
function TCommThread.PortSet():boolean;
//端口参数设置
var
mode : integer;
ret : longint;
begin
{ com port default setting }
result := false;
if fCommData = nil then SetCommParam();
mode := fCommData.Parity or fCommData.ByteSize or fCommData.StopBits;
ret := sio_ioctl(fcom,fCommData.BaudRate,mode);
if ret<>SIO_OK then Exit;
result := True;
end;
procedure TCommThread.SetCommParam(BaudRate:integer=B9600;
Parity:integer=P_NONE;
ByteSize:integer=BIT_8;
StopBits:integer=STOP_1;
ibaudrate:integer=12;
iparity:integer=0;
ibytesize:integer=3;
istopbits:integer=0;
Hw:Boolean=False;
Sw:Boolean=False;
Dtr: boolean=True;
Rts : boolean=True);
begin
if fCommData= nil then New (fCommData);
fCommData.iBaudRate := BaudRate;
fCommData.iparity := iparity;
fCommData.ibytesize := ibytesize;
fCommData.istopbits := istopbits;
fCommData.BaudRate := BaudRate;
fCommData.Parity := Parity;
fCommData.ByteSize := ByteSize;
fCommData.StopBits := StopBits;
fCommData.Hw := Hw;
fCommData.Sw := Sw;
fCommData.Dtr := Dtr;
fCommData.Rts := Rts;
end;
function TCommThread.ReTrySendData(strInfo:string):Longint;//发送数据到终端
var len,ret:longint;
begin
len := length(strInfo);
ret := sio_write(fcom,PCHAR(strInfo),len);
result := Ret;
end;
//发送数据到终端
procedure TCommThread.SendData();
var pt:pTermInfo;
S,S1,S2:String;
D1,D2:Boolean;
wParam,lParam,Result:Integer;
begin
try
if MonitorType=2 then begin
if fFileList<>nil then begin
if fFileList.Count>0 then
ShellChangeNotifierChangeMonitor(ProcDirect,ProcDirectValue,PO,PPP,fMemo);
end;
end else if MonitorType=3 then begin
if LocalBarCodeNumber=2 then begin
D1:=PerformTextInfoSingle(1);
PostMessage(fHandle,Cardinal(WM_DEFMSG),0,3);

D2:=PerformTextInfo(2);
if D2 then begin
if fEdit.Text='' then
PostMessage(fHandle,Cardinal(WM_DEFMSG),0,3)
else
PostMessage(fHandle,Cardinal(WM_DEFMSG),1,3);
end else
PostMessage(fHandle,Cardinal(WM_DEFMSG),0,3);
end else begin
D2:=PerformTextInfo(2);
if D2 then begin
if fEdit.Text='' then
PostMessage(fHandle,Cardinal(WM_DEFMSG),0,3)
else
PostMessage(fHandle,Cardinal(WM_DEFMSG),1,3);
end else
PostMessage(fHandle,Cardinal(WM_DEFMSG),0,3);
end;
end;
fCurTerm:=SetNextTerm;
PT := fCurTerm;
If PT= nil then exit;
//因为线程是不停的发,第一次错,第二次可能就对了,所以校验码其实作用不大
if CommListonType=0 then begin
if MonitorType=2 then begin
S:=Chr(HexMod256ToAsc('A'+TermiAddr));
S:=CharToHex(TermiAddr)+CharToHex(S);
WriteStr(fCom,Chr($48)+Chr($34)+Chr($31)+S+Chr($47));
end else begin
S:=Chr(HexMod256ToAsc('B'+pt.TermiAddr));
S:=CharToHex(pt.TermiAddr)+CharToHex(S);
WriteStr(fCom,Chr($48)+Chr($34)+Chr($32)+S+Chr($47));
end;
end else if CommListonType=1 then begin
if MonitorType=2 then begin
S:=Chr(HexMod256ToAsc('A'+TermiAddr+'1'));
S:=CharToHex(TermiAddr)+CharToHex(S);
ReTrySendData(Chr($48)+Chr($34)+Chr($31)+S+Chr($47));
end else begin
S:=Chr(HexMod256ToAsc('B'+pt.TermiAddr));
S:=CharToHex(pt.TermiAddr)+CharToHex(S);
ReTrySendData(Chr($48)+Chr($34)+Chr($32)+S+Chr($47));
end;
end;
finally
Pt:=nil;
end;
end;

end.
//调用
ComThread:TCommThread;
ComThread := TCommThread.create(True);
ComThread.Com := ComPort;
ComThread.Edit:= Edit4;
ComThread.Handle:=Self.Handle;
ComThread.BortRate:=strtoint(BortRate);
if ComThread.CommInitialize=False then begin
showmessage('Comm Port Error!');
CommError:=False;
exit;
end else CommError:=True;
if fTermList.Count>0 then begin
fCurTerm := fTermList[0];
fTermIndex := 0;
//启动线程
ComThread.Resume;
end;
//退出
ComThread.ExistExecute:=False;
麻烦你自己去掉一些多余代码:)
 
dey-999:
你好..可以发一个例子给我学习下吗?
我现在想学一些多线程的东西..
如QQ一样的..访问人比较多的处理方法...
我的邮箱:wsc188@126.com
多谢..
 
http://www.delphibbs.com/delphibbs/dispq.asp?lid=171926
很久以前用过,现在忘记差不多了,你看看是不是符合你的要求,好像是线程安全的
 
建议用comport,开源的有控件,简单好用!
 
to dey-999:
缺少以下单元,
PublishDM,DateUtils,PublicFiles,PComm

我决定使用 SPCOMM ,但我必须把它做成 OCX,让它可以给VB,VC等其他语言使用,非可视化的组件能做成OCX 吗?
 
PublishDM,PublicFiles用到一些公用函数,你不需要,删掉吧,出现不能用的函数后删除掉
PComm就是SPComm的声明,去掉吧
DateUtils是系统自带的日期类,没问题的
 
谢谢 dey-999,谁能给我介绍或提供一点activeX组件的开发资料吗?我想把SPCOMM改成OCX,但我找了好久都找不到相关资料,好象说非可视化的组件不可以做成ACTIVEX,我记得VB里可以的
 
#include &quot;stdafx.h&quot;
#include &quot;cl800DEV.h&quot;
#include <stdio.h>
HANDLE hcom;
DWORD nobr;
DWORD nobw;
COMMTIMEOUTS comTimeOuts;

DCB dcb;
DWORD dwEvtMask;
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

/*
函数名:init
函数用途:初始化COM口
*/
void init(void)
{

//set COMMTIMEOUTS
comTimeOuts.ReadIntervalTimeout = MAXDWORD;
comTimeOuts.ReadTotalTimeoutConstant = 0;
comTimeOuts.ReadTotalTimeoutMultiplier = 0;
comTimeOuts.WriteTotalTimeoutConstant = 5000;
comTimeOuts.WriteTotalTimeoutMultiplier = 50;
SetCommTimeouts(hcom , &comTimeOuts);

GetCommState(hcom, &dcb);

dcb.BaudRate = CBR_38400;
dcb.fBinary = true;
dcb.fParity = false;
dcb.fOutxCtsFlow = false;
dcb.fOutxDsrFlow = false;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity = false;
dcb.fTXContinueOnXoff = false;
dcb.fOutX = false;
dcb.fInX = false;
dcb.fErrorChar = false;
dcb.fNull = false;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fAbortOnError = false;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;

SetCommState(hcom, &dcb);

}

/*
函数名:XCreateFile
函数差数:fileName COM口的名称
函数用途:打开COM口
*/
CL800DEV_API bool __stdcall XCreateFile(char* fileName)
{
hcom = CreateFile(fileName,
GENERIC_READ |GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);

if( hcom == INVALID_HANDLE_VALUE)
return false;
else
init();
return true;

}

/*
函数名:XCloseHandle
函数用途:关闭COM口
*/
CL800DEV_API int __stdcall XCloseHandle(void)
{
if(hcom)
{
CloseHandle(hcom);
return true;
}return false;
}

/*
函数名称:XWriteFile
函数差数:lpBuffer 写入COM的字符串
:nNumberOfBytesToWrite 应该写如的字符串的大小
:lpNumberOfBytesWritten 实际写入的大小
函数用途:关闭COM口
*/
CL800DEV_API int __stdcall XWriteFile(LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten)
{

return WriteFile(hcom, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten,NULL);

}

/*
函数名称:XReadFile
函数差数:lpBuffer 写入COM的字符串
:nNumberOfBytesToWrite 应该读出的字符串的大小
:lpNumberOfBytesWritten 实际读出的大小
函数用途:关闭COM口
*/
CL800DEV_API int __stdcall XReadFile(LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead)
{
return ReadFile(hcom, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead , NULL);
}
 
VB中串口通讯的实现- -
Tag: VB中串口通讯的实现 ActiveX控件 MSComm 通讯控件
一、概述

串口通讯作为一种古老而又灵活的通讯方式,被广泛地应用于PC间的通讯以及PC和单片机之间的通讯之中。 提到串口通讯的编程,人们往往立刻想到C、汇编等对系统底层操作支持较好的编程语言以及大串繁琐的代码。

实际上,只要我们借助相关ActiveX控件的帮助,即使是在底层操作一向不被人看好的VB中,一样能够实现串口通 讯,甚至其实现方法和C、汇编相比,要更加快捷方便。下面,笔者就介绍一下在VB中实现串口通讯的方法。

在Visual Basic中有一个名为Microsoft Communication Control(简称MSComm)的通讯控件。我们只要通 过对此控件的属性和事件进行相应编程操作,就可以轻松地实现串口通讯。下面,笔者就简要地介绍一下MSComm控件的使用方法。

二、MSComm控件的主要属性、事件

1、MSComm的属性

由于MSComm控件属性很多,在此笔者仅介绍与实现串口通讯密切相关的核心属性。

Commport:设置通讯所占用的串口号。如设成1(默认值),表示对Com1进行操作。

Setting:对串口通讯的相关参数。包括串口通讯的比特率,奇偶校验,数据位长度、停止位等。其默认值 是“9600,N,8,1”,表示串口比特率是9600bit/s,不作奇偶校验,8位数据位,1个停止位。

Portopen:设置串口状态,值为True时打开串口,值为False时关闭串口。

Input:从输入寄存器读取数据,返回值为从串口读取的数据内容,同时输入寄存器将被清空。

Ouput:发送数据到输出寄存器。

InBufferCount:设置输入寄存器所存储的字符数,当将其值设为0时,则输入寄存器将被清空。

InputMode:设置从输入寄存器中读取数据的形式。若值为0,则表示以文本形式读取;值为1,则表示以 二进制形式读取。

OutBufferCount:设置输出寄存器所存储的字符数,当将其值设为0时,则输出寄存器将被清空。

RThreshold:设置在MSComm控件设置CommEvent属性为comEvReceive并产生OnComm事件之前要接受的字符 数。

CommEvent属性:返回最近的通讯事件或错误。通过对它具体属性值的查询,我们就可以获得通讯事件和通 讯错误的完整信息。当其值是comEvReceive时表示接收到数据。

2、MSComm的事件

除了公共事件之外,MSComm只有一个OnComm事件。当CommEvent属性值变化时将发生OnComm事件,指示发生 一个通讯事件或错误。当我们设置Rtheshold属性值为0时,将使得捕获comEvReceive事件无效。

三、串口通讯编程实例

在完成了对MSComm控件的简要介绍之后,笔者就以实际程序为例,介绍一下串口通讯的具体实现方法。

1、PC机间的串口通讯

(1)、实现方法:

A、新建一个窗体,在上面放两个Text控件、两个CommandButton控件和两个Label控件(如图1.bmp所示)。

具体见下表:

控件类型 名称 Caption属性 作用

-------------------------------------------------------------------------------

Text Text1 ------- 输入所要发送的信息

Text Text2 ------- 显示接收到的信息

CommandButton Command1 发 送 ---------

CommandButton Command2 退 出 ---------

Label Label1 发送的数据 提示

Label Label2 接收的数据 提示

B、在控件工具箱中的空白处点击鼠标右键,在弹出的菜单中选择“部件”,在弹出的窗口中的控件列表中 找到“Microsoft Comm Control”,将其选中,在点击“应用”、“关闭”,在控件工具栏中就会出现一个电 话的小图标。

C、用串口线将两台电脑连接起来。您可以使用Com1对Com1的对应连接,也可以使用Com1和Com2的交叉连接。

本程序使用的是Com1对Com1的连接。

D、输入以下代码:

Private Sub Command1_Click()

'...发送数据

MSComm1.OutBufferCount = 0 '...清空输出寄存器

MSComm1.Output = Text1.Text '...发送数据

End Sub

Private Sub Command2_Click()

'...退出

Unload Me

End Sub

Private Sub Form_Load()

'...初始化

MSComm1.CommPort = 1 '...使用Com1口

MSComm1.Settings = &quot;9600,n,8,1&quot; '...设置通讯参数

MSComm1.PortOpen = True '...打开串口

End Sub

Private Sub Mscomm1_Oncomm()

'...通讯事件发生

Select Case MSComm1.CommEvent

Case comEvReceive '...有接受事件发生

Text2.Text = MSComm1.Input '...接受显示数据

MSComm1.InBufferCount = 0 '...清空输入寄存器

End Select

End Sub

2、PC机与单片机之间的通讯

PC机与单片机之间的通讯被广泛的用于工业、医疗测控等领域之中。在应用中,我们通常将单片机作为“感 受器”和“效应器”,负责数据采集、响应计算机发出的指令对电路进行控制,有时也进行一些简单的运算, 最后再将执行数据反馈给计算机处理。本程序将实现在PC机上输入一个0-255之间的整数,将此数据发送到单片 机,单片机接收到数据后,将数据在显示管上显示,再将此数除以2,将得数返回给PC机。(运行效果如图 3.BMP所示)其实现方法如下:

A、同PC机间通讯的实现方法A-B。

B、连接电脑和单片机。注意!由于PC机端的RS232电平与单片机端TTL的并不不匹配,故应注意电平转换。

C、在VB中输入以下代码:

Private Sub Mscomm1_Oncomm()

'...通讯事件发生

Dim indata As Variant

Dim bte(0) As Byte

Select Case MSComm1.CommEvent

Case comEvReceive '...有接受事件发生

indata = MSComm1.Input

'...注意!要通过MSComm控件发送或接收二进制数据必须用Variant类型的变量对二进

'...制Byte类型的变量进行转换!

bte(0) = AscB(indata)

Text2.Text = bte(0)

MSComm1.InBufferCount = 0 '...清空输入寄存器

End Select

End Sub

Private Sub Command1_Click()

'...发送数据

Dim Num As Integer

Dim outbte(0) As Byte

Num = Val(Text1.Text)

outbte(0) = CByte(Num)

MSComm1.OutBufferCount = 0 '...清空输出寄存器

MSComm1.Output = outbte(0) '...发送数据

End Sub

Private Sub Command2_Click()

'...退出

Unload Me

End Sub

Private Sub Form_Load()

'...初始化

MSComm1.CommPort = 1 '...使用Com1口

MSComm1.Settings = &quot;9600,n,8,1&quot; '...设置通讯参数

MSComm1.PortOpen = True '...打开串口

End Sub

D、单片机工作方式置于1,比特率设为9600bit/s。在单片机上,我们只得使用汇编语言编写,并且调用中 断实现对串口数据的收发工作。源代码如下:

PUSH PSW ;将程序状态字压入堆栈

PUSH ACC ;将累加器压入堆栈

CLR EA ;关闭系统中断

CLR RI ;清除中断标志位

MOV A,SBUF ;从接收寄存器中读取数据

MOV 70H,A ;分解数据百、十、个位并显示

MOV B,#100

DIV AB

MOV 52H,A ;分解百位,送入存储器52H

MOV A,B

MOV B,#10

DIV AB

MOV 51H,A ;分解十位,送入存储器51H

MOV 50H,B ;分解个位,送入存储器50H

MOV A,70H

MOV B,#2

DIV AB ;将接受的数据除以2

MOV SBUF,A ;将得数发送到输出寄存器

ACALL DL1 ;延时保证数据完整发送

ACALL DL1

CLR RI ;清除中断标志位

SETB EA ;打开系统中断

POP ACC ;累加器出栈

POP PSW ;程序状态字出栈

RETI ;中断程序返回

3、编程环境

以上程序在Windows 2000 Professional,Visual Basic 6.0企业版,AT89C52型单片机下调试通过。

四、总结

从以上程序可以看出,在VB中利用MSComm控件,可以快速开发出串口通讯程序,从而大大提高编程效率。

作者:福建 李铭 陈春美

来源:软件报
 
妈的,怎么老是贴这些垃圾出来
 
帮你解决问题,你还骂人! 什么素质啊!
 
我怀疑贴这些的人自己都没看过,我先道歉先,SORRY
 
前面已经说明别贴一堆东西出来
 
完全可以
 
能改出来我300分相送
SPCOMM 把它改成OCX,+QQ495963512
或者告诉我怎么改
 
问题不理它了,浪费太多时间在上面,请大家看看这个问题
http://www.delphibbs.com/delphibbs/dispq.asp?lid=3638490
 
多人接受答案了。
 
后退
顶部