mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-21 02:32:51 +02:00
* Serial unit from Mark Morgan LLoyd
git-svn-id: trunk@26856 -
This commit is contained in:
parent
2a048692a5
commit
c2a9dccc14
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -9165,6 +9165,7 @@ rtl/win32/initc.pp svneol=native#text/plain
|
||||
rtl/win32/objinc.inc svneol=native#text/plain
|
||||
rtl/win32/rtldefs.inc svneol=native#text/plain
|
||||
rtl/win32/seh32.inc svneol=native#text/plain
|
||||
rtl/win32/serial.pp svneol=native#text/plain
|
||||
rtl/win32/signals.pp svneol=native#text/plain
|
||||
rtl/win32/sysinit.inc svneol=native#text/plain
|
||||
rtl/win32/sysinitcyg.pp svneol=native#text/plain
|
||||
|
1438
rtl/win32/Makefile
1438
rtl/win32/Makefile
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,7 @@ implicitunits=sysinitpas sysinitcyg sysinitgprof \
|
||||
rtlconsts sysconst sysutils math types \
|
||||
strutils typinfo fgl classes \
|
||||
cpu mmx charset character getopts \
|
||||
printer \
|
||||
printer serial\
|
||||
fpwidestring \
|
||||
winsysut sharemem exeinfo fpintres windirs \
|
||||
cp1250 cp1251 cp1252 cp1253 cp1254 cp1255 cp1256 cp1257 cp1258 \
|
||||
|
473
rtl/win32/serial.pp
Normal file
473
rtl/win32/serial.pp
Normal file
@ -0,0 +1,473 @@
|
||||
{ Unit for handling the serial interfaces for Linux and similar Unices.
|
||||
(c) 2000 Sebastian Guenther, sg@freepascal.org; modified MarkMLl 2012.
|
||||
Windows variant written with reference to Dejan Crnila's TComPort v1.01.
|
||||
}
|
||||
|
||||
unit Serial;
|
||||
|
||||
{$MODE objfpc}
|
||||
{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses Windows;
|
||||
|
||||
type
|
||||
|
||||
TSerialHandle = THandle;
|
||||
|
||||
TParityType = (NoneParity, OddParity, EvenParity);
|
||||
|
||||
TSerialFlags = set of (RtsCtsFlowControl);
|
||||
|
||||
TSerialState = TDCB;
|
||||
|
||||
|
||||
{ Open the serial device with the given device name, for example:
|
||||
\COM1, \COM2 (strictly, \\.\COM1...) for normal serial ports.
|
||||
ISDN devices, serial port redirectors/virtualisers etc. normally
|
||||
implement names of this form, but refer to your OS documentation.
|
||||
Returns "0" if device could not be found }
|
||||
function SerOpen(const DeviceName: String): TSerialHandle;
|
||||
|
||||
{ Closes a serial device previously opened with SerOpen. }
|
||||
procedure SerClose(Handle: TSerialHandle);
|
||||
|
||||
{ Flushes the data queues of the given serial device. DO NOT USE THIS:
|
||||
use either SerSync (non-blocking) or SerDrain (blocking). }
|
||||
procedure SerFlush(Handle: TSerialHandle); deprecated;
|
||||
|
||||
{ Suggest to the kernel that buffered output data should be sent. This
|
||||
is unlikely to have a useful effect except possibly in the case of
|
||||
buggy ports that lose Tx interrupts, and is implemented as a preferred
|
||||
alternative to the deprecated SerFlush procedure. }
|
||||
procedure SerSync(Handle: TSerialHandle);
|
||||
|
||||
{ Wait until all buffered output has been transmitted. It is the caller's
|
||||
responsibility to ensure that this won't block permanently due to an
|
||||
inappropriate handshake state. }
|
||||
procedure SerDrain(Handle: TSerialHandle);
|
||||
|
||||
{ Discard all pending input. }
|
||||
procedure SerFlushInput(Handle: TSerialHandle);
|
||||
|
||||
{ Discard all unsent output. }
|
||||
procedure SerFlushOutput(Handle: TSerialHandle);
|
||||
|
||||
{ Reads a maximum of "Count" bytes of data into the specified buffer.
|
||||
Result: Number of bytes read. }
|
||||
function SerRead(Handle: TSerialHandle; var Buffer; Count: LongInt): LongInt;
|
||||
|
||||
{ Tries to write "Count" bytes from "Buffer".
|
||||
Result: Number of bytes written. }
|
||||
function SerWrite(Handle: TSerialHandle; var Buffer; Count: LongInt): LongInt;
|
||||
|
||||
procedure SerSetParams(Handle: TSerialHandle; BitsPerSec: LongInt;
|
||||
ByteSize: Integer; Parity: TParityType; StopBits: Integer;
|
||||
Flags: TSerialFlags);
|
||||
|
||||
{ Saves and restores the state of the serial device. }
|
||||
function SerSaveState(Handle: TSerialHandle): TSerialState;
|
||||
procedure SerRestoreState(Handle: TSerialHandle; State: TSerialState);
|
||||
|
||||
{ Getting and setting the line states directly. }
|
||||
procedure SerSetDTR(Handle: TSerialHandle; State: Boolean);
|
||||
procedure SerSetRTS(Handle: TSerialHandle; State: Boolean);
|
||||
function SerGetCTS(Handle: TSerialHandle): Boolean;
|
||||
function SerGetDSR(Handle: TSerialHandle): Boolean;
|
||||
function SerGetCD(Handle: TSerialHandle): Boolean;
|
||||
function SerGetRI(Handle: TSerialHandle): Boolean;
|
||||
|
||||
{ Set a line break state. If the requested time is greater than zero this is in
|
||||
mSec, in the case of unix this is likely to be rounded up to a few hundred
|
||||
mSec and to increase by a comparable increment; on unix if the time is less
|
||||
than or equal to zero its absolute value will be passed directly to the
|
||||
operating system with implementation-specific effect. If the third parameter
|
||||
is omitted or true there will be an implicit call of SerDrain() before and
|
||||
after the break.
|
||||
|
||||
NOTE THAT on Linux, the only reliable mSec parameter is zero which results in
|
||||
a break of around 250 mSec. Might be completely ineffective on Solaris.
|
||||
}
|
||||
procedure SerBreak(Handle: TSerialHandle; mSec: LongInt= 250; sync: boolean= true);
|
||||
|
||||
type TSerialIdle= procedure(h: TSerialHandle);
|
||||
|
||||
{ Set this to a shim around Application.ProcessMessages if calling SerReadTimeout(),
|
||||
SerBreak() etc. from the main thread so that it doesn't lock up a Lazarus app. }
|
||||
var SerialIdle: TSerialIdle= nil;
|
||||
|
||||
{ This is similar to SerRead() but adds a mSec timeout. Note that this variant
|
||||
returns as soon as a single byte is available, or as dictated by the timeout. }
|
||||
function SerReadTimeout(Handle: TSerialHandle; var Buffer; mSec: LongInt): LongInt;
|
||||
|
||||
{ This is similar to SerRead() but adds a mSec timeout. Note that this variant
|
||||
attempts to accumulate as many bytes as are available, but does not exceed
|
||||
the timeout. Set up a SerIdle callback if using this in a main thread in a
|
||||
Lazarus app. }
|
||||
function SerReadTimeout(Handle: TSerialHandle; var Buffer: array of byte; count, mSec: LongInt): LongInt;
|
||||
|
||||
|
||||
{ ************************************************************************** }
|
||||
|
||||
implementation
|
||||
|
||||
|
||||
function SerSetParamsPrivate(Handle: TSerialHandle; BitsPerSec: LongInt;
|
||||
ByteSize: Integer; Parity: TParityType; StopBits: Integer;
|
||||
Flags: TSerialFlags): boolean;
|
||||
|
||||
const bufSize= 2048;
|
||||
|
||||
dcb_Binary = $00000001;
|
||||
dcb_Parity = $00000002;
|
||||
dcb_OutxCtsFlow = $00000004;
|
||||
dcb_OutxDsrFlow = $00000008;
|
||||
dcb_DtrControl = $00000030;
|
||||
dcb_DsrSensivity = $00000040;
|
||||
dcb_TXContinueOnXOff = $00000080;
|
||||
dcb_OutX = $00000100;
|
||||
dcb_InX = $00000200;
|
||||
dcb_ErrorChar = $00000400;
|
||||
dcb_Null = $00000800;
|
||||
dcb_RtsControl = $00003000;
|
||||
dcb_AbortOnError = $00004000;
|
||||
|
||||
var DCB: TDCB;
|
||||
Timeouts: TCommTimeouts;
|
||||
|
||||
begin
|
||||
result := true;
|
||||
FillChar(DCB, SizeOf(DCB), 0);
|
||||
DCB.DCBlength := SizeOf(DCB);
|
||||
DCB.XonChar := #17;
|
||||
DCB.XoffChar := #19;
|
||||
DCB.XonLim := bufSize div 4;
|
||||
DCB.XoffLim := 1;
|
||||
DCB.Flags := DCB.Flags or dcb_Binary;
|
||||
DCB.BaudRate := BitsPerSec;
|
||||
DCB.ByteSize := ByteSize;
|
||||
case Parity of
|
||||
OddParity: DCB.Parity := Windows.ODDPARITY;
|
||||
EvenParity: DCB.Parity := Windows.EVENPARITY
|
||||
else
|
||||
DCB.Parity := Windows.NOPARITY
|
||||
end;
|
||||
if StopBits > 1 then
|
||||
DCB.StopBits := TWOSTOPBITS
|
||||
else
|
||||
DCB.StopBits := ONESTOPBIT;
|
||||
if RtsCtsFlowControl in Flags then
|
||||
DCB.Flags := DCB.Flags or dcb_OutxCtsFlow or
|
||||
(dcb_RtsControl and (RTS_CONTROL_HANDSHAKE shl 12));
|
||||
if not SetCommState(Handle, DCB) then
|
||||
result := false;
|
||||
if GetCommTimeouts(Handle, Timeouts) then begin
|
||||
Timeouts.ReadIntervalTimeout := MAXDWORD;
|
||||
Timeouts.ReadTotalTimeoutMultiplier := 0;
|
||||
Timeouts.ReadTotalTimeoutConstant := 0;
|
||||
Timeouts.WriteTotalTimeoutMultiplier := 0;
|
||||
Timeouts.WriteTotalTimeoutConstant := 30000;
|
||||
if not SetCommTimeouts(Handle, Timeouts) then
|
||||
result := false
|
||||
end else
|
||||
result := false;
|
||||
if not SetupComm(Handle, bufSize, bufSize) then
|
||||
result := false
|
||||
end { SerSetParamsPrivate } ;
|
||||
|
||||
|
||||
function SerOpen(const DeviceName: String): TSerialHandle;
|
||||
|
||||
var securityAttributes: TSecurityAttributes;
|
||||
|
||||
begin
|
||||
securityAttributes.nLength := SizeOf(TSecurityAttributes);
|
||||
securityAttributes.lpSecurityDescriptor := nil;
|
||||
securityAttributes.bInheritHandle := true;
|
||||
result := CreateFile(
|
||||
PChar(DeviceName),
|
||||
GENERIC_READ or GENERIC_WRITE,
|
||||
0,
|
||||
@securityAttributes,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
if result = INVALID_HANDLE_VALUE then
|
||||
result := 0
|
||||
else
|
||||
|
||||
(* Don't trust Windows's initial state. If the internal variant (returning a *)
|
||||
(* result) of SerSetParams() fails it indicates that part of the comms API is *)
|
||||
(* unavailable, assume that this is fatal because it will probably mess up *)
|
||||
(* things like read timeouts. *)
|
||||
|
||||
if not SerSetParamsPrivate(result, 9600, 8, NoneParity, 1, []) then begin
|
||||
CloseHandle(result);
|
||||
result := 0
|
||||
end
|
||||
end { SerOpen } ;
|
||||
|
||||
|
||||
procedure SerClose(Handle: TSerialHandle);
|
||||
|
||||
begin
|
||||
CloseHandle(Handle);
|
||||
end { SerClose } ;
|
||||
|
||||
|
||||
procedure SerFlush(Handle: TSerialHandle); deprecated;
|
||||
|
||||
begin
|
||||
FlushFileBuffers(Handle);
|
||||
end { SerFlush } ;
|
||||
|
||||
|
||||
procedure SerSync(Handle: TSerialHandle);
|
||||
|
||||
begin
|
||||
FlushFileBuffers(Handle)
|
||||
end { SerSync } ;
|
||||
|
||||
|
||||
procedure SerDrain(Handle: TSerialHandle);
|
||||
|
||||
var errors: DWORD;
|
||||
comStat: TComStat;
|
||||
|
||||
begin
|
||||
FlushFileBuffers(Handle);
|
||||
repeat
|
||||
if not ClearCommError(Handle, errors, @comStat) then
|
||||
break;
|
||||
if (ComStat.cbOutQue > 0) and Assigned(SerialIdle) then
|
||||
SerialIdle(Handle)
|
||||
until ComStat.cbOutQue = 0
|
||||
end { SerDrain } ;
|
||||
|
||||
|
||||
procedure SerFlushInput(Handle: TSerialHandle);
|
||||
|
||||
begin
|
||||
PurgeComm(Handle, PURGE_RXABORT + PURGE_RXCLEAR)
|
||||
end { SerFlushInput } ;
|
||||
|
||||
|
||||
procedure SerFlushOutput(Handle: TSerialHandle);
|
||||
|
||||
begin
|
||||
PurgeComm(Handle, PURGE_TXABORT + PURGE_TXCLEAR)
|
||||
end { SerFlushOutput } ;
|
||||
|
||||
|
||||
function SerRead(Handle: TSerialHandle; var Buffer; Count: LongInt): LongInt;
|
||||
|
||||
var BytesRead: DWORD;
|
||||
Timeouts: TCommTimeouts;
|
||||
|
||||
begin
|
||||
if GetCommTimeouts(Handle, Timeouts) then begin
|
||||
Timeouts.ReadIntervalTimeout := MAXDWORD;
|
||||
Timeouts.ReadTotalTimeoutConstant := 0;
|
||||
SetCommTimeouts(Handle, Timeouts)
|
||||
end;
|
||||
if not ReadFile(Handle, Buffer, Count, BytesRead, nil) then
|
||||
result := 0
|
||||
else
|
||||
result := BytesRead
|
||||
end { SerRead } ;
|
||||
|
||||
|
||||
function SerWrite(Handle: TSerialHandle; var Buffer; Count: LongInt): LongInt;
|
||||
|
||||
var BytesWritten: DWORD;
|
||||
|
||||
begin
|
||||
if not WriteFile(Handle, Buffer, Count, BytesWritten, nil) then
|
||||
result := 0
|
||||
else
|
||||
result := BytesWritten
|
||||
end { SerWrite } ;
|
||||
|
||||
|
||||
procedure SerSetParams(Handle: TSerialHandle; BitsPerSec: LongInt;
|
||||
ByteSize: Integer; Parity: TParityType; StopBits: Integer;
|
||||
Flags: TSerialFlags);
|
||||
|
||||
begin
|
||||
if SerSetParamsPrivate(Handle, BitsPerSec, ByteSize, Parity, StopBits, Flags) then begin
|
||||
end
|
||||
end { SerSetParams } ;
|
||||
|
||||
|
||||
function SerSaveState(Handle: TSerialHandle): TSerialState;
|
||||
|
||||
begin
|
||||
GetCommState(Handle, result)
|
||||
end { SerSaveState } ;
|
||||
|
||||
|
||||
procedure SerRestoreState(Handle: TSerialHandle; State: TSerialState);
|
||||
|
||||
begin
|
||||
SetCommState(Handle, State)
|
||||
end { SerRestoreState } ;
|
||||
|
||||
|
||||
(* Mask out a 2-bit field and merge in a replacement. *)
|
||||
//
|
||||
procedure merge2Bits(var flag: dword; startBit: integer; value: dword);
|
||||
|
||||
var mask0, mask1: dword;
|
||||
index: integer;
|
||||
|
||||
begin
|
||||
mask0:= $fffffffc;
|
||||
for index:= 1 TO startBit do
|
||||
mask0:= (mask0 shl 1) or $00000001;
|
||||
mask1:= $00000003 shl startBit;
|
||||
value:= value shl startBit;
|
||||
flag:= (flag and mask0) or (value and mask1)
|
||||
end { merge2Bits } ;
|
||||
|
||||
|
||||
procedure SerSetDTR(Handle: TSerialHandle; State: Boolean);
|
||||
|
||||
var dcb: TDCB;
|
||||
|
||||
begin
|
||||
if GetCommState(Handle, dcb) then begin
|
||||
if State then
|
||||
merge2Bits(dcb.Flags, 4, DTR_CONTROL_ENABLE)
|
||||
else
|
||||
merge2Bits(dcb.Flags, 4, DTR_CONTROL_DISABLE);
|
||||
SetCommState(Handle, dcb)
|
||||
end
|
||||
end { SerSetDTR } ;
|
||||
|
||||
|
||||
procedure SerSetRTS(Handle: TSerialHandle; State: Boolean);
|
||||
|
||||
var dcb: TDCB;
|
||||
|
||||
begin
|
||||
if GetCommState(Handle, dcb) then begin
|
||||
if State then
|
||||
merge2Bits(dcb.Flags, 12, RTS_CONTROL_ENABLE)
|
||||
else
|
||||
merge2Bits(dcb.Flags, 12, RTS_CONTROL_DISABLE);
|
||||
SetCommState(Handle, dcb)
|
||||
end
|
||||
end { SerSetRTS } ;
|
||||
|
||||
|
||||
function SerGetCTS(Handle: TSerialHandle): Boolean;
|
||||
|
||||
var status: dword;
|
||||
|
||||
begin
|
||||
if GetCommModemStatus(Handle, status) then
|
||||
result := status and MS_CTS_ON <> 0
|
||||
else
|
||||
result := false
|
||||
end { SerGetCTS } ;
|
||||
|
||||
|
||||
function SerGetDSR(Handle: TSerialHandle): Boolean;
|
||||
|
||||
var status: dword;
|
||||
|
||||
begin
|
||||
if GetCommModemStatus(Handle, status) then
|
||||
result := status and MS_DSR_ON <> 0
|
||||
else
|
||||
result := false
|
||||
end { SerGetDSR } ;
|
||||
|
||||
|
||||
function SerGetCD(Handle: TSerialHandle): Boolean;
|
||||
|
||||
var status: dword;
|
||||
|
||||
begin
|
||||
if GetCommModemStatus(Handle, status) then
|
||||
result := status and MS_RLSD_ON <> 0
|
||||
else
|
||||
result := false
|
||||
end { SerGetCD } ;
|
||||
|
||||
|
||||
function SerGetRI(Handle: TSerialHandle): Boolean;
|
||||
|
||||
var status: dword;
|
||||
|
||||
begin
|
||||
if GetCommModemStatus(Handle, status) then
|
||||
result := status and MS_RING_ON <> 0
|
||||
else
|
||||
result := false
|
||||
end { SerGetRI } ;
|
||||
|
||||
|
||||
procedure SerBreak(Handle: TSerialHandle; mSec: LongInt=250; sync: boolean= true);
|
||||
|
||||
const quantum= 100;
|
||||
|
||||
begin
|
||||
if sync then
|
||||
SerDrain(Handle);
|
||||
SetCommBreak(Handle);
|
||||
repeat
|
||||
if mSec < quantum then begin
|
||||
Sleep(mSec);
|
||||
mSec := 0
|
||||
end else begin
|
||||
Sleep(quantum);
|
||||
mSec -= quantum
|
||||
end;
|
||||
if (mSec > 0) and Assigned(SerialIdle) then
|
||||
SerialIdle(Handle)
|
||||
until mSec <= 0;
|
||||
ClearCommBreak(Handle);
|
||||
if sync then
|
||||
SerDrain(handle)
|
||||
end { SerBreak } ;
|
||||
|
||||
|
||||
function SerReadTimeout(Handle: TSerialHandle; var Buffer; mSec: LongInt): LongInt;
|
||||
|
||||
var BytesRead: DWORD;
|
||||
Timeouts: TCommTimeouts;
|
||||
|
||||
begin
|
||||
if GetCommTimeouts(Handle, Timeouts) then begin
|
||||
Timeouts.ReadIntervalTimeout := 0;
|
||||
Timeouts.ReadTotalTimeoutConstant := mSec;
|
||||
SetCommTimeouts(Handle, Timeouts)
|
||||
end;
|
||||
if not ReadFile(Handle, Buffer, 1, BytesRead, nil) then
|
||||
result := 0
|
||||
else
|
||||
result := BytesRead
|
||||
end { SerReadTimeout } ;
|
||||
|
||||
|
||||
function SerReadTimeout(Handle: TSerialHandle; var Buffer: array of byte; count, mSec: LongInt): LongInt;
|
||||
|
||||
var BytesRead: DWORD;
|
||||
Timeouts: TCommTimeouts;
|
||||
|
||||
begin
|
||||
if GetCommTimeouts(Handle, Timeouts) then begin
|
||||
Timeouts.ReadIntervalTimeout := 0;
|
||||
Timeouts.ReadTotalTimeoutConstant := mSec;
|
||||
SetCommTimeouts(Handle, Timeouts)
|
||||
end;
|
||||
if not ReadFile(Handle, Buffer, count, BytesRead, nil) then
|
||||
result := 0
|
||||
else
|
||||
result := BytesRead
|
||||
end { SerReadTimeout } ;
|
||||
|
||||
|
||||
end.
|
Loading…
Reference in New Issue
Block a user