Difference between revisions of "Serial unit"

From Free Pascal wiki
Jump to navigationJump to search
(→‎Timeout values: Last maintainer's comment on timeouts etc.)
 
(5 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 +
__TOC__
 +
 
Unit '''Serial''' in FPC supports work with serial port. It provides many Ser* functions.
 
Unit '''Serial''' in FPC supports work with serial port. It provides many Ser* functions.
  
 
==Example of usage==
 
==Example of usage==
 +
 +
Note that this uses Windows-style device names, unix systems (Linux, SunOS etc.) have their own conventions.
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
Program TestSerialPortCom;
 
 
{
 
{
   Usage options:
+
   Usage:
   TestSerialPortCom => uses default COM1
+
   TestSerialPortCom
   TestSerialPortCom 8 'Hello' => uses COM8 and output 'Hello' before waiting for an answer
+
    Uses default port COM1.
 +
   TestSerialPortCom 8 'Hello'
 +
    Uses COM8 and outputs 'Hello' before waiting for an answer.
 
    
 
    
   the program will open a serialport and output 'Hello', after that the code will wait until
+
   The program will open a serial port and output 'Hello', after that the code will wait until
 
   a CR (#13) is received, or a key is pressed.
 
   a CR (#13) is received, or a key is pressed.
 
}
 
}
 +
program TestSerialPortCom;
 
uses
 
uses
 
   serial, crt;
 
   serial, crt;
  
VAR
+
var
 
   serialhandle : LongInt;
 
   serialhandle : LongInt;
 
   ComPortName  : String;
 
   ComPortName  : String;
 
   s,tmpstr,txt : String;
 
   s,tmpstr,txt : String;
 
   ComIn        : String;
 
   ComIn        : String;
   ComPortNr    : integer;
+
   ComPortNr    : Integer;
   writecount  : integer;
+
   writecount  : Integer;
 
   status      : LongInt;
 
   status      : LongInt;
   Flags        : TSerialFlags; { TSerialFlags = set of (RtsCtsFlowControl); }
+
   Flags        : TSerialFlags; { set of (RtsCtsFlowControl); }
 
   ErrorCode    : Integer;
 
   ErrorCode    : Integer;
  
BEGIN
+
begin
 
   ComPortNr:= 1;
 
   ComPortNr:= 1;
 
   tmpstr:= '';
 
   tmpstr:= '';
 
   txt:= '';
 
   txt:= '';
  
   writeln('Parameters ', ParamCount);
+
   writeln('Parameters: ', ParamCount);
 
   if (ParamCount>0) then
 
   if (ParamCount>0) then
 
   begin
 
   begin
Line 39: Line 45:
  
 
     if (ParamCount>1) then
 
     if (ParamCount>1) then
    begin
 
 
       txt:= ParamStr(2);
 
       txt:= ParamStr(2);
      {val(tmpstr,ComPortNr,ErrorCode);}
 
    end;
 
 
   end;
 
   end;
  
   str(ComPortNr,tmpstr);
+
   str(ComPortNr, tmpstr);
 
   
 
   
 
   ComPortName:= 'COM'+tmpstr+':';
 
   ComPortName:= 'COM'+tmpstr+':';
   writeln('Using '+ComPortname);
+
   writeln('Using: '+ComPortname);
  
 
   serialhandle := SerOpen(ComPortName);
 
   serialhandle := SerOpen(ComPortName);
   Flags:= [ ]; // None
+
   Flags:= []; // none
   SerSetParams(serialhandle,9600,8,NoneParity,1,Flags);  
+
   SerSetParams(serialhandle, 9600, 8, NoneParity, 1, Flags);  
 
    
 
    
   s:=txt; // use the input text
+
   s:= txt; // use the input text
   writeln('OUT '+s);
+
   writeln('Output: '+s);
   s:=s+#13+#10; { CR + LF }
+
   s:= s+#13+#10; // CR + LF
 
   writecount:= length(s);
 
   writecount:= length(s);
  
   status:= SerWrite(serialhandle, s[1], writecount );
+
   status:= SerWrite(serialhandle, s[1], writecount);
  
   // The next line is for debugging only!
+
   // for debugging only
   writeln('status: ', status, '   writecount: ', writecount);
+
   writeln('Status: ', status, ', WriteCount: ', writecount);
  
 
   if status > 0 then
 
   if status > 0 then
 
   begin
 
   begin
 
     writeln('Waiting for answer');
 
     writeln('Waiting for answer');
    { wait for an answer }
+
     s:= '';
     s:='';
+
     ComIn:= '';
     ComIn:='';
+
     while (Length(ComIn)<10) and (status>=0) and not KeyPressed do  
     while (Length(Comin)<10) and (status>=0) and not keypressed do begin
+
    begin
 
 
 
       status:= SerRead(serialhandle, s[1], 10);
 
       status:= SerRead(serialhandle, s[1], 10);
       if (s[1]=#13) then status:=-1; { CR => end serial read }
+
       if (s[1]=#13) then  
 +
        status:= -1; // CR => end serial read
  
       if (status>0) then ComIn:=ComIn+s[1];
+
       if (status>0) then
      if (status>0) then begin
+
      begin
         writeln(status,' ',length(ComIn),' ASCII ',ord(s[1]),' INP ',ComIn);
+
        ComIn:= ComIn+s[1];
       end;
+
         writeln('Status: ', status, ', Len: ', length(ComIn), ', ASCII: ', ord(s[1]), ', Input: ', ComIn);
 +
       end;
 
     end;
 
     end;
 
   end
 
   end
 
   else
 
   else
     writeln('ERROR - Unable to send.');
+
     writeln('Error: unable to send');
  
   SerSync(serialhandle); { flush out any remaining before closure }
+
   SerSync(serialhandle); // flush out any remaining before closure
  
   SerFlushOutput(serialhandle); { discard any remaining output }
+
   SerFlushOutput(serialhandle); // discard any remaining output
  
 
   SerClose(serialhandle);
 
   SerClose(serialhandle);
END.
+
end.
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==When timeout starts==
+
==Timeout values==
  
 
Q: I'm trying to figure out when the timeout timer in SerRead/SerReadTimeout starts.
 
Q: I'm trying to figure out when the timeout timer in SerRead/SerReadTimeout starts.
  
 
A (by FPC developer Christo Crause): FPC uses the OS provided functionality to interact with the serial port. On Windows the timeout seems to start when the read request is made - [https://learn.microsoft.com/en-us/previous-versions/ff547486(v=vs.85) Link]. On POSIX (at least Linux) it depends on the specific set of flags specified , scroll down to the discussion on canonical/noncanonical mode for the details - [https://linux.die.net/man/3/termios Link].
 
A (by FPC developer Christo Crause): FPC uses the OS provided functionality to interact with the serial port. On Windows the timeout seems to start when the read request is made - [https://learn.microsoft.com/en-us/previous-versions/ff547486(v=vs.85) Link]. On POSIX (at least Linux) it depends on the specific set of flags specified , scroll down to the discussion on canonical/noncanonical mode for the details - [https://linux.die.net/man/3/termios Link].
 +
 +
A bit more information after peeking into the source code: on Win32 the timeout information can be read or set using Get/SetCommTimeouts. The [https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-commtimeouts COMMTIMEOUTS structure] contains a read interval timeout and total timeouts for read & write operations. SerReadTimeout sets the ReadTotalTimeoutConstant value, so basically the total timeout duration.
 +
 +
On Linux SerReadTimeout uses a fpSelect on the handle with the specified timeout, so in principle it is the same behaviour as on Windows, i.e. a total timeout.
 +
 +
While both OSs provide some functionality to specify inter character timeouts, FPC does not directly expose this functionality in the Serial unit.
 +
 +
Note from last maintainer: SerReadTimeout() explicitly says "https://github.com/MarkMLl/serialcomms" i.e. the intention is that timing starts when the function is called. Different lineages of unix handle fpSelect() differently, while not tested it's reasonable to assume that the Mac's OS-X behaves like SunOS/Solaris. See https://github.com/MarkMLl/serialcomms for examples, comments on known issues etc.

Latest revision as of 15:13, 25 April 2023

Unit Serial in FPC supports work with serial port. It provides many Ser* functions.

Example of usage

Note that this uses Windows-style device names, unix systems (Linux, SunOS etc.) have their own conventions.

{
  Usage:
  TestSerialPortCom
    Uses default port COM1.
  TestSerialPortCom 8 'Hello'
    Uses COM8 and outputs 'Hello' before waiting for an answer.
  
  The program will open a serial port and output 'Hello', after that the code will wait until
  a CR (#13) is received, or a key is pressed.
}
program TestSerialPortCom;
uses
  serial, crt;

var
  serialhandle : LongInt;
  ComPortName  : String;
  s,tmpstr,txt : String;
  ComIn        : String;
  ComPortNr    : Integer;
  writecount   : Integer;
  status       : LongInt;
  Flags        : TSerialFlags; { set of (RtsCtsFlowControl); }
  ErrorCode    : Integer;

begin
  ComPortNr:= 1;
  tmpstr:= '';
  txt:= '';

  writeln('Parameters: ', ParamCount);
  if (ParamCount>0) then
  begin
    tmpstr:= ParamStr(1);
    val(tmpstr, ComPortNr, ErrorCode);

    if (ParamCount>1) then
      txt:= ParamStr(2);
  end;

  str(ComPortNr, tmpstr);
 
  ComPortName:= 'COM'+tmpstr+':';
  writeln('Using: '+ComPortname);

  serialhandle := SerOpen(ComPortName);
  Flags:= []; // none
  SerSetParams(serialhandle, 9600, 8, NoneParity, 1, Flags); 
  
  s:= txt; // use the input text
  writeln('Output: '+s);
  s:= s+#13+#10; // CR + LF
  writecount:= length(s);

  status:= SerWrite(serialhandle, s[1], writecount);

  // for debugging only
  writeln('Status: ', status, ', WriteCount: ', writecount);

  if status > 0 then
  begin
    writeln('Waiting for answer');
    s:= '';
    ComIn:= '';
    while (Length(ComIn)<10) and (status>=0) and not KeyPressed do 
    begin
      status:= SerRead(serialhandle, s[1], 10);
      if (s[1]=#13) then 
        status:= -1; // CR => end serial read

      if (status>0) then
      begin 
        ComIn:= ComIn+s[1];
        writeln('Status: ', status, ', Len: ', length(ComIn), ', ASCII: ', ord(s[1]), ', Input: ', ComIn);
      end;  
    end;
  end
  else
    writeln('Error: unable to send');

  SerSync(serialhandle); // flush out any remaining before closure

  SerFlushOutput(serialhandle); // discard any remaining output

  SerClose(serialhandle);
end.

Timeout values

Q: I'm trying to figure out when the timeout timer in SerRead/SerReadTimeout starts.

A (by FPC developer Christo Crause): FPC uses the OS provided functionality to interact with the serial port. On Windows the timeout seems to start when the read request is made - Link. On POSIX (at least Linux) it depends on the specific set of flags specified , scroll down to the discussion on canonical/noncanonical mode for the details - Link.

A bit more information after peeking into the source code: on Win32 the timeout information can be read or set using Get/SetCommTimeouts. The COMMTIMEOUTS structure contains a read interval timeout and total timeouts for read & write operations. SerReadTimeout sets the ReadTotalTimeoutConstant value, so basically the total timeout duration.

On Linux SerReadTimeout uses a fpSelect on the handle with the specified timeout, so in principle it is the same behaviour as on Windows, i.e. a total timeout.

While both OSs provide some functionality to specify inter character timeouts, FPC does not directly expose this functionality in the Serial unit.

Note from last maintainer: SerReadTimeout() explicitly says "https://github.com/MarkMLl/serialcomms" i.e. the intention is that timing starts when the function is called. Different lineages of unix handle fpSelect() differently, while not tested it's reasonable to assume that the Mac's OS-X behaves like SunOS/Solaris. See https://github.com/MarkMLl/serialcomms for examples, comments on known issues etc.