Function TForm1.SendCommData(s:string):boolean;
var
i:integer;
buffChar:array[0..64] of char;
len:integer;
begin
result := false; StrPCopy(buffChar,s);
len := length(s); if not (Comm1.WriteCommData(@buffChar[0],1)) then
begin
result := false;
exit;
end; for i:=1 to len-1 do
begin
Comm1.WriteCommData(@buffChar[i],1); //bool
end;
result := true;end;
procedure TForm1.GetMeTheData(s:string);
begin
SendCommData('GetMeTheData');//请求串口外部设备传数据给我
end;
procedure TForm1.Comm1ReceiveData(Sender: TObject;
Buffer: Pointer; BufferLength: Word);
var
buffChar : array[0..32] of byte;
sRev,sRes: string;
i:smallint;
begin
try
ZeroMemory(@buffChar[0],33);
except
end;
//SetLength(buffChar,BufferLength);
CopyMemory(@buffChar[0],Buffer,BufferLength);
sRev := '';
sRes := '';
sRev := Format('%x',[buffChar[0]]);
if (sRev='7B') then //是需要的数据了
begin
//取出需要的数据
sRes:=需要的数据;
end;
if sRes='' then
begin
//接收失败
end
else
begin
//接收成功,显示数据
end;
end;
流程就是每当我发SendCommData('GetMeTheData')到串口,串口设备就传数据给我,
刚开始的20次左右,是好的,一切正常,之后就接不到数据了,也不知道是没发成功还是没接成功,开始我想是缓冲区满了,于是我就在发送之前,或者是接收之后,清
空缓冲区,如下
PurgeComm( Comm1.handle, PURGE_TXABORT or PURGE_RXABORT or
PURGE_TXCLEAR or PURGE_RXCLEAR ) ;
(如果用安全清除:
FlushFileBuffers(Comm1.handle),结果是成功8次左右就死机。)
结果是根本要发的数据SendCommData('GetMeTheData')都没发出去,虽然我是在发送之前清的缓冲区。
Comm1为spcomm控件实例,属性控制是默认的。比如缓冲区大小为:
SetupComm( hNewCommFile, 4096, 4096 );
属性选项如下:
BaudRate 9600
ByteSize _8
CommName COM2
DsrSensitivity False
DtrControl DtrEnable
IgnoreNullChar False
Inx_XonXoffFlow True
Name Comm1
Outx_XtsFlow False
Outx_DsrFlow False
Outx_XonXoffFlow True
Parity None
ParityCheck False
ReadIntervalTimeout 100
ReadTotalTimeoutConstant 0
ReadTotalTimeoutMultiplier 0
ReplaceChar #0
ReplaceWhenParityError False
RtsControl RtsEnable
StopBit _1
Tag 0
TxContinueOnXoff True
WriteTotalTimeoutConstant 0
WriteTotalTimeoutMultiplier 0
XoffChar #19
XoffLimit 500
XonChar #17
XonLimit 500
var
i:integer;
buffChar:array[0..64] of char;
len:integer;
begin
result := false; StrPCopy(buffChar,s);
len := length(s); if not (Comm1.WriteCommData(@buffChar[0],1)) then
begin
result := false;
exit;
end; for i:=1 to len-1 do
begin
Comm1.WriteCommData(@buffChar[i],1); //bool
end;
result := true;end;
procedure TForm1.GetMeTheData(s:string);
begin
SendCommData('GetMeTheData');//请求串口外部设备传数据给我
end;
procedure TForm1.Comm1ReceiveData(Sender: TObject;
Buffer: Pointer; BufferLength: Word);
var
buffChar : array[0..32] of byte;
sRev,sRes: string;
i:smallint;
begin
try
ZeroMemory(@buffChar[0],33);
except
end;
//SetLength(buffChar,BufferLength);
CopyMemory(@buffChar[0],Buffer,BufferLength);
sRev := '';
sRes := '';
sRev := Format('%x',[buffChar[0]]);
if (sRev='7B') then //是需要的数据了
begin
//取出需要的数据
sRes:=需要的数据;
end;
if sRes='' then
begin
//接收失败
end
else
begin
//接收成功,显示数据
end;
end;
流程就是每当我发SendCommData('GetMeTheData')到串口,串口设备就传数据给我,
刚开始的20次左右,是好的,一切正常,之后就接不到数据了,也不知道是没发成功还是没接成功,开始我想是缓冲区满了,于是我就在发送之前,或者是接收之后,清
空缓冲区,如下
PurgeComm( Comm1.handle, PURGE_TXABORT or PURGE_RXABORT or
PURGE_TXCLEAR or PURGE_RXCLEAR ) ;
(如果用安全清除:
FlushFileBuffers(Comm1.handle),结果是成功8次左右就死机。)
结果是根本要发的数据SendCommData('GetMeTheData')都没发出去,虽然我是在发送之前清的缓冲区。
Comm1为spcomm控件实例,属性控制是默认的。比如缓冲区大小为:
SetupComm( hNewCommFile, 4096, 4096 );
属性选项如下:
BaudRate 9600
ByteSize _8
CommName COM2
DsrSensitivity False
DtrControl DtrEnable
IgnoreNullChar False
Inx_XonXoffFlow True
Name Comm1
Outx_XtsFlow False
Outx_DsrFlow False
Outx_XonXoffFlow True
Parity None
ParityCheck False
ReadIntervalTimeout 100
ReadTotalTimeoutConstant 0
ReadTotalTimeoutMultiplier 0
ReplaceChar #0
ReplaceWhenParityError False
RtsControl RtsEnable
StopBit _1
Tag 0
TxContinueOnXoff True
WriteTotalTimeoutConstant 0
WriteTotalTimeoutMultiplier 0
XoffChar #19
XoffLimit 500
XonChar #17
XonLimit 500
>>刚开始的20次左右,是好的,一切正常,之后就接不到数据了,也不知道是没发成功还是
>>没接成功,开始我想是缓冲区满了,于是我就在发送
應該不是 "缓冲区满了"的原因, 是什麼數據導致線程挂起了
procedure TWriteThread.Execute;
var
msg: TMsg;
dwHandleSignaled: DWORD;
overlappedWrite: TOverLapped;
CompleteOneWriteRequire : Boolean;
label
EndWriteThread;
begin
// Needed for overlapped I/O.
FillChar( overlappedWrite, SizeOf(overlappedWrite), 0 ); {0, 0, 0, 0, NULL} overlappedWrite.hEvent := CreateEvent( nil, True, True, nil );
if overlappedWrite.hEvent = 0 then
begin
PostHangupCall;
goto EndWriteThread
end; CompleteOneWriteRequire := True; // This is the main loop. Loop until we break out.
while True do
begin
if not PeekMessage( msg, 0, 0, 0, PM_REMOVE ) then
begin
// If there are no messages pending, wait for a message or
// the CloseEvent. pFSendDataEmpty^ := True; if CompleteOneWriteRequire then
begin
if not PostMessage( hComm32Window, PWM_SENDDATAEMPTY, 0, 0 ) then
begin
PostHangupCall;
goto EndWriteThread
end
end; CompleteOneWriteRequire := False; dwHandleSignaled := MsgWaitForMultipleObjects(1, hCloseEvent, False,
INFINITE, QS_ALLINPUT); case dwHandleSignaled of
WAIT_OBJECT_0: // CloseEvent signaled!
begin
// Time to exit.
goto EndWriteThread
end; WAIT_OBJECT_0 + 1: // New message was received.
begin
// Get the message that woke us up by looping again.
Continue
end; WAIT_FAILED: // Wait failed. Shouldn't happen.
begin
PostHangupCall;
goto EndWriteThread
end
else // This case should never occur.
begin
PostHangupCall;
goto EndWriteThread
end
end
end; // Make sure the CloseEvent isn't signaled while retrieving messages.
if WAIT_TIMEOUT <> WaitForSingleObject(hCloseEvent,0) then
goto EndWriteThread; // Process the message.
// This could happen if a dialog is created on this thread.
// This doesn't occur in this sample, but might if modified.
if msg.hwnd <> 0{NULL} then
begin
TranslateMessage(msg);
DispatchMessage(msg);
Continue
end; // Handle the message.
case msg.message of
PWM_COMMWRITE: // New string to write to Comm port.
begin
// Write the string to the comm port. HandleWriteData
// does not return until the whole string has been written,
// an error occurs or until the CloseEvent is signaled.
if not HandleWriteData( @overlappedWrite,
PChar(msg.lParam), DWORD(msg.wParam) ) then
begin
// If it failed, either we got a signal to end or there
// really was a failure. LocalFree( HLOCAL(msg.lParam) );
goto EndWriteThread
end; CompleteOneWriteRequire := True;
// Data was sent in a LocalAlloc()d buffer. Must free it.
LocalFree( HLOCAL(msg.lParam) )
end
end
end; {main loop} // Thats the end. Now clean up.
EndWriteThread: PurgeComm(hCommFile, PURGE_TXABORT + PURGE_TXCLEAR);
pFSendDataEmpty^ := True;
CloseHandle(overlappedWrite.hEvent)
end; {TWriteThread.Execute}
procedure TReadThread.Execute;
var
szInputBuffer: array[0..INPUTBUFFERSIZE-1] of Char;
nNumberOfBytesRead: DWORD; HandlesToWaitFor: array[0..2] of THandle;
dwHandleSignaled: DWORD; fdwEvtMask: DWORD; // Needed for overlapped I/O (ReadFile)
overlappedRead: TOverlapped; // Needed for overlapped Comm Event handling.
overlappedCommEvent: TOverlapped;
label
EndReadThread;
begin
FillChar( overlappedRead, Sizeof(overlappedRead), 0 );
FillChar( overlappedCommEvent, Sizeof(overlappedCommEvent), 0 ); // Lets put an event in the Read overlapped structure.
overlappedRead.hEvent := CreateEvent( nil, True, True, nil);
if overlappedRead.hEvent = 0 then
begin
PostHangupCall;
goto EndReadThread
end; // And an event for the CommEvent overlapped structure.
overlappedCommEvent.hEvent := CreateEvent( nil, True, True, nil);
if overlappedCommEvent.hEvent = 0 then
begin
PostHangupCall();
goto EndReadThread
end; // We will be waiting on these objects.
HandlesToWaitFor[0] := hCloseEvent;
HandlesToWaitFor[1] := overlappedCommEvent.hEvent;
HandlesToWaitFor[2] := overlappedRead.hEvent; // Setup CommEvent handling. // Set the comm mask so we receive error signals.
if not SetCommMask(hCommFile, EV_ERR or EV_RLSD or EV_RING ) then
begin
PostHangupCall;
goto EndReadThread
end; // Start waiting for CommEvents (Errors)
if not SetupCommEvent( @overlappedCommEvent, fdwEvtMask ) then
goto EndReadThread; // Start waiting for Read events.
if not SetupReadEvent( @overlappedRead,
szInputBuffer, INPUTBUFFERSIZE,
nNumberOfBytesRead ) then
goto EndReadThread; // Keep looping until we break out.
while True do
begin
// Wait until some event occurs (data to read; error; stopping).
dwHandleSignaled := WaitForMultipleObjects(3, @HandlesToWaitFor,
False, INFINITE); // Which event occured?
case dwHandleSignaled of
WAIT_OBJECT_0: // Signal to end the thread.
begin
// Time to exit.
goto EndReadThread
end; WAIT_OBJECT_0 + 1: // CommEvent signaled.
begin
// Handle the CommEvent.
if not HandleCommEvent( @overlappedCommEvent, fdwEvtMask, TRUE ) then
goto EndReadThread; // Start waiting for the next CommEvent.
if not SetupCommEvent( @overlappedCommEvent, fdwEvtMask ) then
goto EndReadThread
{break;??}
end; WAIT_OBJECT_0 + 2: // Read Event signaled.
begin
// Get the new data!
if not HandleReadEvent( @overlappedRead,
szInputBuffer,
INPUTBUFFERSIZE,
nNumberOfBytesRead ) then
goto EndReadThread; // Wait for more new data.
if not SetupReadEvent( @overlappedRead,
szInputBuffer, INPUTBUFFERSIZE,
nNumberOfBytesRead ) then
goto EndReadThread
{break;}
end; WAIT_FAILED: // Wait failed. Shouldn't happen.
begin
PostHangupCall;
goto EndReadThread
end
else // This case should never occur.
begin
PostHangupCall;
goto EndReadThread
end
end {case dwHandleSignaled}
end; {while True} // Time to clean up Read Thread.
EndReadThread: PurgeComm( hCommFile, PURGE_RXABORT + PURGE_RXCLEAR );
CloseHandle( overlappedRead.hEvent );
CloseHandle( overlappedCommEvent.hEvent )
end; {TReadThread.Execute}
while not Terminated do
begin
end;
可以吗??
也许是下位机工作不正常了吧。你既然用Spcomm,为何还要显式地去用那些API呢?
你还要注意发送的间隔,要考虑两方(下位机、上位机)都能处理过来。
还有你这是什么代码呀? for i:=1 to len-1 do
begin
Comm1.WriteCommData(@buffChar[i],1); //bool
end;
不能用一个语句发送出去吗?自找麻烦。
可以用串口调试助手之类的作测试,还有你发送语句为什么不一起发?
move(Buffer^,buffChar,BufferLength);
可以么??
其实收到的数据是21 Byte,33个长度因该够了吗?
还有,我看spcomm的收发线程都有goto语句,我看它如果出错,goto之后,线程就完了,要怎么样防止它完蛋呢?
move(Buffer^,buffChar,BufferLength);
过程1: 点击按键--关闭端口--设置端口参数--打开端口--发送'GetMeTheData’
过程2: Comm1ReceiveData--如果收到'7B'--取出数据注意事项:
1 Inx_XonXoffFlow Outx_XonXoffFlow 可设为 false
2. 发送数据一句话就够了 Comm1.WriteCommData('GetMeTheData')
3. 接收数据 Move(Buffer^,arrByte,25);---"其实收到的数据是21 Byte',读前面一些字节就够了,防止收到的数据长度超过[0..32]
4. 打开 onReceiveError 看看出错的时候错在那里
5. 如果前面接收的数据没有问题,可能该线程中断
我防止了长度在10~25之间了。
Inx_XonXoffFlow Outx_XonXoffFlow 可设为 false 流控制到底是做什么用的?可以告诉我么?
好,我打开onReceiveError看看。
谢谢各位的热心帮助!
如果线程中断(因该就是了),该怎么防止呢?我在spcomm原代码中加了while not terminated do 都不行。
Inx_XonXoffFlow Outx_XonXoffFlow 可设为 false
就好象70次都不出错了也,现在能坚持到300次了。不知道能不能坚持1年以上?
当onReceiveError时,我就显示错误码,然后StopComm,再StartComm,可以吗??
现在贴出源码(Delphi7.0)---部分,希望对你有所帮助。
(前些日期写的,运行的很顺利。用于从TMS320C54X DSP结构上获得数据)1. 控件Comm1
object Comm1: TComm
CommName = 'COM1'
BaudRate = 9600
ParityCheck = False
Outx_CtsFlow = False
Outx_DsrFlow = False
DtrControl = DtrEnable
DsrSensitivity = False
TxContinueOnXoff = False
Outx_XonXoffFlow = False
Inx_XonXoffFlow = False
ReplaceWhenParityError = False
IgnoreNullChar = False
RtsControl = RtsEnable
XonLimit = 500
XoffLimit = 500
ByteSize = _8
Parity = None
StopBits = _1
XonChar = #17
XoffChar = #19
ReplacedChar = #0
ReadIntervalTimeout = 1
ReadTotalTimeoutMultiplier = 0
ReadTotalTimeoutConstant = 0
WriteTotalTimeoutMultiplier = 0
WriteTotalTimeoutConstant = 0
OnReceiveData = Comm1ReceiveData
Left = 48
Top = 272
end
2. 打开端口
begin
comm1.CommName:=trim(ComboBox1.Text); //从ComboBox1获得端口名称
ComboBox1.Enabled:=false;
comm1.StartComm;
end;3.接收数据
procedure TForm2.Comm1ReceiveData(Sender: TObject; Buffer: Pointer;
BufferLength: Word);
var ReceivedData: array [1..1200] of byte;
i:integer;
begin
//初始化:赋值0
for i:=1 to 1200 do
begin
ReceivedData[i]:=0;
end;
move(buffer^,pchar(@ReceivedData)^,1100); //只取部分(有效部分)
//发回命令,告知数据已经收到。
comm1.WriteCommData('0'#13,1);
end;
Inx_XonXoffFlow Outx_XonXoffFlow 设为 false
后,现在运行3000次了,还没问题。可能以后也没问题了。谢谢。