300分,请教串口通讯问题!(300分)

  • 主题发起人 主题发起人 chenshu_sc
  • 开始时间 开始时间
C

chenshu_sc

Unregistered / Unconfirmed
GUEST, unregistred user!
  小弟编这个东西两个星期了,仍进展不大,特来请教各位大大!我知道一个贴子问题问多个问题不好,但实在是没分了,还望各位大大帮忙啊!
问题如下:

1.若用 API   怎么对串口进行 异步 通讯(读、写),具体怎么做?

2.若用 SPCOMM  怎么实现用'AT'指令判断串口设备是否正常连接在计算机?

3.若对多串口设备进行操作(例:COM1..COM10),怎么保证程序运行过程中有设备取下时(例:COM3在程序进行时断电),可以不再对断开设备发送数据?

PS:请各位大大用“去看SPCOMM源码”这样的话回答我啊,我是看得一头雾水才跑来的啊

我的联系方式:
QQ:19420047
MAIL:chenshu_sc@163.com
 
1。一般是CreateFile, GetCommState, SetCommState 来设置端口属性
然后再用ClearCommErr来判断是否有数据,用ReadFile,WriteFile来读写数据
有很多现成的例子,可以用同步或异步方式

2。AT是Modem的指令,特殊设备应该有专门的指令,
发出此指令后,等待应答,根据应答消息,判断情况

3。串口应该是没有办法判断设备是否断开或掉电的
可以定时发送测试数据来判断设备运行情况
 
如果设备有规定好的返回格式 就可以判断是否断开或掉电
 
TO:lich & 大家
1.您说的这些我都通过网络查到过,并且现在正在这么做,我已经实现了同步的读写,但一用异步,不论是读还是写,程序都会出现无响应的问题,请问这是怎么回事呢?

2.AT好象是一般MODEM都有这个指令吧?而且我用超级终端连上之后用‘AT’指令得到结果为:AT(换行)OK。现在的问题是,如果我用SPCOMM控件的话,在ONRECEIVEDATA得到的结果并不返回到我要进行判断的地方,而是等我判断完之后才得到结果,这是为什么?

3.那样不是效率很低,而且容易出错?(例如:如果把检测周期设得很短,那么程序效率就会变得低下,反之,出错的机率就会大大增加)MODEM就没有状态改变的事件吗?如果有又该怎么用呢?

以上1.2.这两点我都有出错的程序,哪位大大觉得想看一下比较直观,可以给我来信。

谢谢!
 
SPCOMM是个线程,用着感觉反应有点迟钝。
 
//用Cport.
var
Recstr: string;//变量

//发送事件:
omPort1.WriteStr('AT' + #13#10);

//接收
procedure TMainForm.ComPort1RxChar(Sender: TObject; Count: Integer);
var
s: string;
begin
ComPort1.ReadStr(s, Count);
RecStr := RecStr + s;
if (Pos('OK'#13#10, RecStr) > 0) then
ShowMessage('连接成功!')
RecStr := '';
end;
 
try
with Comm1 do
begin
Comm1.Tag:=1;
comm1.StartComm;
Sleep(20);
tmp2:='AT'+#13;
ComSendText(Tmp2);
sleep(20);
end;
except
on exception do
Raise exception.Create('打开COM端口失败,请检查计算机端口!');
end;

function TFmSmsManage.ComSendText(sMsg: String): Boolean;
var i:Integer;
begin
for i:=1 to length(sMsg) do
begin
if not comm1.WriteCommData(@sMsg,1) then
begin
result:=false;
exit;
end;
sleep(10);
end;
end;
我写的短信开发的一些代码,希望有用!
 
使用异步读写,其CreateFile在创建时于同步是有区别的。
同步为:hcomm:=CreateFile('COM1', GENERIC_READ or GENERIC_WRITE,0, nil, OPEN_EXISTING, 0, 0);
异步为:hcomm:=CreateFile('COM1', GENERIC_READ or GENERIC_WRITE,0, nil, OPEN_EXISTING, file_flag_overlapped, 0);
发送和接收也是有区别的:
同步为:
WriteFile(hcomm,snddstr,Length(snddstr),lrc,nil);
ReadFile(hComm, inbuff, dwLength,nBytesRead, nil);
异步为:
writefile(hcomm,senddata,length(senddata),dwbyteswritten,@oswrite);
readfile(hcomm,chout,dwlength,byteread,@osread);
至于收发控制可以是消息控制也可以是别的控制。
异步比较麻烦点。希望对你有帮助。


 
TO:hnxiong
  我现在可以异步写入了,但是每次都返回FALSE,不知道是怎么回事?写入函数如下:
function ComWrite(var hComF : THandle ; pWrite : PChar ; s : string) : Boolean;
var
dwWhereToStartWriting : Dword; //从何处开始写入
dwNumberOfBytesToWrite : Dword; //共需写入字符数
dwNumberOfBytesWritten : Dword; //已经写入字符数
bWriteResult : Boolean; //读取结果
ovlWrite : Overlapped;
dwWriteError : Dword;
hWaitForWrite : THandle;
dwHandleSingaled : Dword;

label
WriteError; //写串口错误
begin
//初始化
Result := False;
dwWhereToStartWriting := 0;
dwNumberOfBytesWritten := 0;
dwNumberOfBytesToWrite := StrLen(pWrite);

FillChar( ovlWrite, Sizeof(ovlWrite), 0 );
ovlWrite.hEvent := CreateEvent(nil,True,False,nil);

hWaitForWrite := ovlWrite.hEvent;

//开始写入
repeat
bWriteResult := WriteFile(
hComF, //写入句柄
pWrite[dwWhereToStartWriting], //写入字符
dwNumberOfBytesToWrite, //写入字符总长
dwNumberOfBytesWritten, //已写入字符数
@ovlWrite //结构指针
);
if (not bWriteResult) then
bWriteResult := GetOverlappedResult(
hComF, //串口句柄
ovlWrite, //进行写操作时指定的OVERLAPPED结构
dwNumberOfBytesWritten, //写操作实际传递的字节数
True //是否等待挂起的重叠操作完成
);

Dec( dwNumberOfBytesToWrite, dwNumberOfBytesWritten );
Inc( dwWhereToStartWriting, dwNumberOfBytesWritten );
until (dwNumberOfBytesToWrite <= 0);
Result := bWriteResult;

WriteError:
Result := False;
CloseHandle(ovlWrite.hEvent);
exit;

end;

TO:forall
 我也是在做串口发短信,你用的是API还是控件呢?可以加QQ请教你吗?

TO:gyh75
 我突然想到,如果串口没有连接设备,那么不论发什么指令也不会产生EV_RXCHAR事件....:(

 
TO:大家
 SORRY,加个问题:如果要对多个串口进行写入与读取应该怎么做效率才高呢?(我是新手,请尽是说得详细些,谢谢)
 
关 注,帮顶
 
TO:liuyuhe_124
什么意思?
 
如果用 异步方式,可以考虑使用状态机,
在不同的状态处理不同的数据

实际上,同步和异步的程序是可以等价转换的
 
我知道你的问题出在哪里了,就在
WriteError:
Result := False;
CloseHandle(oswrite.hEvent);
exit;
不管前面什么结果这段程序肯定要执行,所以就会成为:result=false;
把这段代码去掉就可以了呢。
 
To:hnxiong 
 果然是这句的问题,谢谢!^_^

TO:forall
 判断设备是否正常在程序运行只是程序的初始工作,后面还会发送其它的AT命令,所以我想你的方法不是很好吧?

TO:大家
 总结一下,现在的问题还是三个: 
1.如果要对多个串口进行写入与读取应该怎么做效率才高呢?
2.若用 SPCOMM  怎么实现用'AT'指令判断串口设备是否正常连接在计算机?
3.若对多串口设备进行操作(例:COM1..COM10),怎么保证程序运行过程中有设备取下时(例:COM3在程序进行时断电),可以不再对断开设备发送数据?
 麻烦大家再帮忙看一下好吗?小弟先谢了!

 
各位大大帮忙啊![:O][:O]
 
首先我假设你已经会delphi。
简单介绍一下SPCOMM
1.属性:
●CommName:表示 COM1、 COM2等串口的名字,默认是COM2.
●BaudRate: 根据实际需要设定的波特率,在串口打开后也可更改此值,实际波特率随之更改;
●ParityCheck:表示是否需要奇偶校验;
●ByteSize:根据实际情况设定的字节长度;
●Parity: 奇偶校验位;
●StopBits:停止位,提供1位半;
●SendDataEmpty:这是一个布尔型属性,为 true时表示发送缓存为空,或者发送队列里没
有信息;为 false时表示发送缓存不为空,或者发送队列里有信息。

2.方法
●Startcomm方法用于打开串口,当打开失败时会报错。错误主要有 7种:⑴串口已经打开;
⑵打开串口错误;⑶文件句柄不是通信句柄;⑷不能够安装通信缓存;⑸不能产生事件;⑹
不能产生读进程;⑺不能产生写进程;
●StopComm方法用于关闭串口,没有返回值;
●WriteCommData(pDataToWrite: PChar;dwSizeofDataToWrite:Word )方法 是个带有布尔
型返回值的函数,用于将一个字符串发送到写进程,发送成功返回 true,发送失败返回
false。执行此函数将立即得到返回值,发送操作随后执行。该函数有两个参数,其中
pDataToWrite是要发送的字符串, dwSizeofDataToWrite是发送字符串的长度。

3.事件
●OnReceiveData :procedure (Sender: TObject;Buffer: Pointer;BufferLength: Word)
当有数据输入缓存时将触发该事件,在这里可以对从串口收到的数据进行处理。 Buffer中
是收到的数据, BufferLength是收到的数据长度,一般使用mov命令将数 据从缓存读入。
●OnReceiveError : procedure(Sender: TObject; EventMask : DWORD)
当接收数据出现错误时将触发该事件。

具体实现:
单片机端我们使用现成的,avrgcc的gcctest4是个上位机通信例子,正好可以用来发数据。

1.设定 COMM1属性:


●波特率: 9600;
●奇偶校验位:无;
●字节长度: 8;
●停止位: 1;
●串口: COM1。
Memo1中将显示发送和接收的数据。


2.编写源代码


//变量说明


var
fcomm: TFCOMM;
viewstring:string;
rbuf,sbuf:array[1..8] of byte;

//打开串口

procedure TFCOMM.FormShow(Sender: TObject);
begin
comm1.StartComm;
end;
//关闭串口
procedure TFCOMM.FormClose(Sender: TObject; var Action: TCloseAction);
begin
comm1.StopComm;
end;

//接收过程
procedure TFCOMM.Comm1ReceiveData(Sender: TObject; Buffer: Pointer;BufferLength: Word);
var
i:integer;
begin
viewstring:=‘’ ;
move(buffer^,pchar(@rbuf)^,bufferlength);
for i:=1 to bufferlength do
viewstring:=viewstring+ inttohex(rbuf,2)+‘’ ;
viewstring:=‘接收’+ viewstring;
memo1.lines.add(viewstring);
memo1.lines.add(‘’ );
end;

 
TO:crazyD
 那么如果我要向设备批量写入AT指令,那么我怎么能实时得到写入结果呢?
 
各位大大帮忙啊![:(]
 
只能使用同步通讯了,最好是用API控制.
 
后退
顶部