Tom Wellige  

About Me
Home
My Blog

Favourites
Books
Drink

Open Source
Miscellaneous
CAN M3S Driver
HPShell
Web DB Editor
Open Queue

Miscellaneous
Wiki
Bookmark

My Programming Page - SERIAL




SERIAL

Contents of this page




SERIAL.PAS
{************************************************}
{                                                }
{   UNIT SERIAL                                  }
{   Copyright (c) 1997 by Tom Wellige            }
{   Donated as FREEWARE                          }
{                                                }
{   E-Mail: wellige@geocities.com                }
{                                                }
{************************************************}

unit Serial;

interface

uses Objects;

{$I SERIAL.INC}

type
  ModeStr = String[3];

const
  {----------------------------------
   COM Object Version
   high byte = major version
   low byte  = minor version
   ----------------------------------}
  COMVersion : word = $0100;

  {----------------------------------
   different Modes for COM usage
   ----------------------------------}
  mdNormal     = $0;  { no special modes               }
  mdTXBuffered = $1;  { use TX interrupt and buffer    }
  mdModem      = $2;  { listen to modem interrupts     }
  mdSignalling = $4;  { using the signalling mechanism }
  mdRXError    = $8;  { Receive Error interrupts       }

  {----------------------------------
   Error- and Statusconstants
   ----------------------------------}
  COM_OK                = $0000;
  COM_ERR_IS_OPEN       = $0010;
  COM_ERR_NOT_SUPPORTED = $0011;
  COM_ERR_ALLOC_RX      = $0012;
  COM_ERR_ALLOC_TX      = $0013;
  COM_ERR_NOT_OPEN      = $0020;
  COM_ERR_FREE_RX       = $0021;
  COM_ERR_FREE_TX       = $0022;
  COM_OPEN              = $0030;


type
  {----------------------------------
   Receive and Transmit buffer type
   ----------------------------------}
  PBufferType = ^TBufferType;
  TBufferType = array[0..65534] of byte;


  {----------------------------------
   COM Object
   ----------------------------------}
  PComPort = ^TComPort;
  TComPort = object (TObject)
    constructor Init(NewPort: Byte;       NewBaud: Word;
                     NewParMode: ModeStr; NewSize: Word;
                     NewMode: Word;       NewBase: Word;   NewIRQ: Byte);

    function    Com_On: Boolean;
    function    Com_Off: Boolean;

    function    GetParMode(var m: ModeStr):  ModeStr;

    function    PutC(ch: char): integer;
    function    WriteBuf(const CBuf; Count: Word): integer;
    function    WriteStr(CStr: string): integer;
    function    GetC(var ch: char): integer;
    function    PeekC(var ch: char): integer;
    function    ReadBuf(var CBuf; Count: Word): integer;
    function    ReadStr(var CStr: string): integer;

    function    BufCount: integer;
    procedure   FlushRX;

    destructor Done; virtual;

  private
      PortBase:  Word;
      IRQ:       Byte;

      BaudRate:  Word;
      NBits:     Byte;
      NStopBits: Byte;
      Parity:    Byte;
      BufSize:   Word;

      ComIsOn:   Boolean;
      ComIsInit: Boolean;

      RX, TX:    PBufferType;
      intmask:   Byte;
      intnum:    Byte;
      intcom2:   Boolean;
      RX_out:    Word;
      RX_in:     Word;
      TX_out:    Word;
      TX_in:     Word;

      ComMode:   Word;

    function    SetBaud(NewBaud: Word):  Boolean;
    function    SetMode(NewMode: ModeStr):  Boolean;

    procedure   Clearstatus;
    function    IntNr: byte;
    function    Mode(m: word): boolean;

  end;


implementation

uses DOS;

type
  PChar = ^Char;

const
  Com : PComPort = nil;

  RXErrorInt = 6;
  RXInt      = 4;
  TXInt      = 2;
  ModemInt   = 0;

var
  ExitSave : pointer;
  OldComVec: Pointer;


{-------------------------------------
 Interrupt Service Routines
 -------------------------------------}
procedure HandleInterrupt(IntType: byte);
begin
  with Com^ do

    case IntType of
      RXInt:
        begin
          RX^[RX_in]:= Port[PortBase+RXR];
          inc(RX_in); if RX_in = BufSize then RX_in:=0;
        end;

      TXInt:
        begin
          if TX_out <> TX_in then
          begin
            Port[PortBase+TXR]:= TX^[TX_out];
            inc(TX_out); if TX_out = BufSize then TX_out:=0;
          end;
       end;

      ModemInt:
        begin
        end;

      RXErrorInt:
        begin
        end;

    end;
end;


procedure Com_Int; interrupt;
var IntType: byte;
begin
  asm cli end;

  with Com^ do
  begin
    if intcom2 then port[ICR2]:= EOI;
    port[ICR1]:= EOI;

    IntType:= Port[PortBase+IIR];
    while ( IntType <> 1 ) do
    begin
      HandleInterrupt(IntType);
      IntType:= Port[PortBase+IIR]
    end;

  end;

  asm sti end;

end;


{-------------------------------------
 PUBLIC TComPort Functions
 -------------------------------------}
constructor TComPort.Init(NewPort: byte;       NewBaud: Word;
                          NewParMode: ModeStr; NewSize: Word;
                          NewMode: Word;       NewBase: Word;   NewIRQ: Byte);
begin
   inherited Init;

   if ( NewBase = 0 ) then
   begin
     case NewPort of
       COM1: PortBase:= COM1BASE;
       COM2: PortBase:= COM2BASE;
       COM3: PortBase:= COM3BASE;
       COM4: PortBase:= COM4BASE;
     end;
   end else
     PortBase:= NewBase;

   if ( NewIRQ = 0 ) then
   begin
     case NewPort of
       COM1, COM3: IRQ:= 4;
       COM2, COM4: IRQ:= 3;
     end;
   end else
     IRQ:= NewIRQ;

   if (NOT SetBaud(NewBaud)) then
     exit;
   if (NOT SetMode(NewParMode)) then
     exit;

   BufSize:= NewSize;
   ComMode:= NewMode;

   intmask:= 1 shl IRQ;
   intcom2:= ( IRQ > 7 );
   intnum := IntNr;

   ComIsInit:= TRUE;
end;


function TComPort.GetParMode(var m: ModeStr): ModeStr;
begin
  m[0]:= #3;

  case Parity of
    NO_PARITY:   m[2]:= 'N';
    EVEN_PARITY: m[2]:= 'E';
    ODD_PARITY:  m[2]:= 'O';
  else
    m[2]:= ' ';
  end;

  m[1]:= Char(NBits + $30);
  m[3]:= Char(NStopBits + $30);
  GetParMode:= m;
end;


function TComPort.Com_On: Boolean;  { Tell Modem we are ready to go }
var
  Mask: Byte;
  d: Byte;
  ints: byte;
begin
  { allocate Receive Buffer }
  {$I-} GetMem (RX, BufSize); {$I+}
  if IOresult <> 0 then
  begin
    Com_On:= FALSE;
    exit;
  end;
  RX_in:= 0; RX_out:= 0;
  FillChar(RX^, BufSize, 0);

  if Mode(mdTXBuffered) then
  begin
    { allocate Transmit Buffer }
    {$I-} GetMem (TX, BufSize); {$I+}
    if IOresult <> 0 then
    begin
      {$I-} FreeMem(RX, BufSize); {$I+}
      Com_On:= FALSE;
      exit;
    end;
    TX_in:= 0; TX_out:= 0;
    FillChar(TX^, BufSize, 0);
  end;


  { disable interrupts }
  asm cli end;

  { set new interrupt service routine }
  GetIntVec(IntNr, OldComVec);
  SetIntVec(IntNr, @Com_Int);

  { setup and release interrupts }
  ints:= 1;                            { Receive Interrupts             }
  if Mode(mdTXBuffered) then
    ints:= ints or $2;                 { Receive Error Interrupts       }
  if Mode(mdRXError) then
    ints:= ints or $4;                 { Send Interrupts                }
  if Mode(mdModem) then
    ints:= ints or $8;                 { Modem Interrupts               }
  port[PortBase+IER]:= ints;

  port[PortBase+IIR]:= 1;

  { enable IRQ on PIC }
  if intcom2 then
    port[IMR2]:= port[IMR2] and (not intmask)
  else
    port[IMR1]:= port[IMR1] and (not intmask);

  { reset UART }
  ClearStatus;

  Com:= PComPort(@self);

  { enable interrupts }
  asm sti end;

  ComIsOn:= TRUE;
end;


function TComPort.Com_Off: Boolean; { Go Offline }
var
  Mask: Byte;
begin
  Com    := nil;
  ComIsOn:= FALSE;

  { dissable interrupts }
  asm cli end;

  { disable all interrupts on UART }
  port[PortBase+IER]:= 0;

  { disable IRQ on PIC }
  if intcom2 then
    port[IMR2]:=port[IMR2] or intmask
  else
    port[IMR1]:=port[IMR1] or intmask;

  {port[PortBase+IIR]:= 0;}

  { reset UART }
  ClearStatus;

  { set old interrupt service routine }
  SetIntVec(IntNr, @OldComVec);

  { enable interrupts }
  asm sti end;

  { release RX buffer }
  {$I-} FreeMem(RX, BufSize); {$I+}
end;


function TComPort.BufCount: integer;
begin
   if (RX_in >= RX_out) then
     BufCount:= RX_in - RX_out
   else
     BufCount:= (BUFSIZE - RX_out) + RX_in;
end;


procedure TComPort.FlushRX;
begin
  RX_in:= RX_out;
  TX_in:= TX_out;
end;


destructor TComPort.Done;
begin
   ComIsInit:= FALSE;
   inherited Done;
end;


{-------------------------------------
 TComPort's SEND Functions
 -------------------------------------}
function TComPort.PutC(ch: char): integer;
const
  TTimeOut: Word = 65535;
begin
  if (NOT ComIsOn) then
  begin
    PutC:= 0;
    Exit;
  end;

  {Wait for Transmitter to Clear}
  while ( (Port[PortBase+LSR] AND Byte(XMTRDY)) = 0) do
    if ( TTimeOut = 0) then
    begin
      PutC:= 0;
      write(#7);
      exit;
    end else
      Dec(TTimeOut);

  Port[PortBase+TXR]:= Byte(ch);

  PutC:= 1;
end;


{ WriteBuf sends Count bytes to the port.  CBUF can be any type
  of variable, and need not be a string (in fact it is best to use
  WriteStr to send strings.  The calling routine must know how many
  bytes to send }
function TComPort.WriteBuf(const CBuf; Count: Word): integer;
var
  OutCount: Word;
  Buf: PChar;
  TX_start: word;
begin
  if (NOT ComIsOn) then
  begin
    WriteBuf:= 0;
    Exit;
  end;

  Buf:= PChar(CBuf);
  OutCount:= 0;

  if Mode(mdTXBuffered) then
  begin
    TX_start:= TX_in;
    while ( Count > 0) do
    begin
      TX^[TX_in]:= ord(Buf^);
      inc(OutCount);
      inc(Buf);
      dec(Count);
      inc(TX_in); if TX_in = BufSize then TX_in:=0;
      { Overrun ? }
      if TX_in = TX_out then Count:= 0;
    end;
    { have to send the first byte ? }
    if TX_start = TX_out then
    begin
      inc(TX_out); if TX_out = BufSize then TX_out:=0;
      PutC(char(TX^[TX_start]));
    end;
  end else
    while (Count > 0) do
    begin
      if (PutC( Buf^ ) = 0) then
        Count:= 0
      else
      begin
        Inc(OutCount);
        Inc(Buf);
        Dec(Count);
      end;
    end;

  WriteBuf:= OutCount;
end;

{Send a PASCAL string to the COM port }
function TComPort.WriteStr(CStr: string): integer;
var
  OutCount: Word;
  Buf: PChar;
begin
  Buf:= PChar(@CStr[1]);      { point to first real ASCII char }
  WriteStr:= WriteBuf(Buf, Length(CStr));
end;


{-------------------------------------
 TComPort's RECEIVE Functions
 -------------------------------------}
function TComPort.GetC(var ch: Char): integer;
begin
  if (RX_in = RX_out) then
    GetC:= 0
  else
  begin
    ch:= Char( RX^[RX_out] AND $ff );
    inc(RX_out); if RX_out = BufSize then RX_out:= 0;
    GetC:= 1;
  end;
end;


function TComPort.PeekC(var ch: Char): integer;
begin
  if (RX_in = RX_out) then
    PeekC:= 0
  else
  begin
    ch:= Char( RX^[RX_out] AND $ff );
    PeekC:= 1;
  end;
end;

{ ReadBuf reads Count bytes from the port.  CBUF can be any type
  of variable, and need not be a string (in fact it is best to use
  ReadStr to send strings.  The calling routine must know how many
  bytes to read and no type or bounds checking is done so it is possible
  to read beyond the end of the array or variable!}
function TComPort.ReadBuf(var CBuf; Count: Word): integer;
var
  InCount: Word;
  Buf: PChar;
begin
  Buf:= PChar(@CBuf);
  InCount:= 0;
  While (Count > 0) do
  begin
    if (GetC( Buf^ ) = 0) then
      Count := 0
    else
    begin
      Inc(InCount);
      Inc(Buf);
      Dec(Count);
    end;
  end;
  ReadBuf:= InCount;
end;

{ Read a PASCAL string from the COM port }
function TComPort.ReadStr(var CStr: string): integer;
var
  n, i: integer;
begin
  n := 1;
  While ( GetC(CStr[n]) <> 0 ) do
    Inc(n);
  CStr[0]:= Chr(n-1);
end;


{-------------------------------------
 PRIVATE TComPort Functions
 -------------------------------------}
function TComPort.SetBaud(NewBaud: Word):  Boolean;
var c: Byte;
begin
  if (NewBaud <= 0) OR (PortBase = 0) then
  begin
    SetBaud:= FALSE;
    exit;
  end;

  Port[PortBase+LCR]:= $80;
  Port[PortBase+DLL]:= Lo(word(115200 div NewBaud));
  port[PortBase+DLH]:= Hi(word(115200 div NewBaud));
  port[PortBase+MCR]:= $0B;

  SetBaud:= TRUE;
end;


function TComPort.SetMode(NewMode: ModeStr):  Boolean;
var
  OK:  Boolean;
  tst: Byte;
begin
  OK:= TRUE;

  case NewMode[2] of
   'N': Parity:= NO_PARITY;
   'E': Parity:= EVEN_PARITY;
   'O': Parity:= ODD_PARITY;
  else
    OK:= FALSE;
  end;

  NBits:= Byte(NewMode[1]) - $30;
  NStopBits:= Byte(NewMode[3]) - $30;

  if (NBits > 8) OR (NBits < 5) then
    OK:= FALSE;
  if (NStopBits > 2) OR (NStopBits < 1) then
    OK:= FALSE;

  if (OK <> FALSE) AND (PortBase <> 0) then
    begin
      Port[PortBase+LCR]:= Parity OR (NBits-5) OR (4*(NStopBits-1));

      { Make sure it got there right }
      tst:= Port[PortBase+LCR];
      if    ( Parity    <>  (tst AND $18) )
         OR ( NBits     <>  (tst AND $03 + 5) )
         OR ( NStopBits <> ((tst AND $04) div 4 + 1) ) then
      OK:= FALSE;
    end;
  SetMode:= OK;
end;


procedure TComPort.Clearstatus;
begin
  if port[PortBase+RXR] <> 0 then;               { dummy-Read }
  if port[PortBase+LSR] <> 0 then;
  if port[PortBase+MSR] <> 0 then;
  if intcom2 then port[ICR2]:= EOI;
  port[ICR1]:= EOI;
end;


function TComPort.IntNr: byte;
begin
  if IRQ < 8 then IntNr:= IRQ + 8
             else IntNr:= IRQ + $68;
end;


function TCOMPort.Mode(m: word): boolean;
begin
  Mode:= ComMode and m <> 0;
end;



{-------------------------------------
 EXIT Procedure
 -------------------------------------}
{$F+}
procedure comexit;
begin
  if assigned(Com) then Com^.Com_Off;
  exitproc:= exitsave;
end;
{$F-}


begin
  exitsave:=exitproc;
  exitproc:=@comexit;
end.

page up


SERIAL.INC
{---------------------------------------------------------------------*
   FILENAME:                     SERIAL.INC

                  Some definitions used by SERIAL.PAS

 *--------------------------------------------------------------------}

const

     COM1 =           1;
     COM2  =          2;
     COM3  =          3;
     COM4  =          4;
     COM1BASE =       $3F8;   { Default Base port address for COM1 }
     COM2BASE =       $2F8;   { Default Base port address for COM2 }
     COM3BASE =       $3E8;   { Default Base port address for COM3 }
     COM4BASE =       $2E8;   { Default Base port address for COM4 }

{
    The 8250 UART has 10 registers accessible through 7 port addresses.
    Here are their addresses relative to COM1BASE and COM2BASE. Note
    that the baud rate registers, (DLL) and (DLH) are active only when
    the Divisor-Latch Access-Bit (DLAB) is on. The (DLAB) is bit 7 of
    the (LCR).

	o TXR Output data to the serial port.
	o RXR Input data from the serial port.
	o LCR Initialize the serial port.
	o IER Controls interrupt generation.
	o IIR Identifies interrupts.
	o MCR Send contorl signals to the modem.
	o LSR Monitor the status of the serial port.
	o MSR Receive status of the modem.
	o DLL Low byte of baud rate divisor.
	o DHH High byte of baud rate divisor.
}
      TXR =            0;       {  Transmit register (WRITE) }
      RXR =            0;       {  Receive register  (READ)  }
      IER =            1;       {  Interrupt Enable          }
      IIR =            2;       {  Interrupt ID              }
      LCR =            3;       {  Line control              }
      MCR =            4;       {  Modem control             }
      LSR =            5;       {  Line Status               }
      MSR =            6;       {  Modem Status              }
      DLL =            0;       {  Divisor Latch Low         }
      DLH =            1;       {  Divisor latch High        }


{-------------------------------------------------------------------*
  Bit values held in the Line Control Register (LCR).
	bit		meaning
	---		-------
	0-1		00=5 bits, 01=6 bits, 10=7 bits, 11=8 bits.
	2		Stop bits.
	3		0=parity off, 1=parity on.
	4		0=parity odd, 1=parity even.
	5		Sticky parity.
	6		Set break.
	7		Toggle port addresses.
 *-------------------------------------------------------------------}
      NO_PARITY   =    $00;
      EVEN_PARITY =    $18;
      ODD_PARITY  =    $08;


{-------------------------------------------------------------------*
  Bit values held in the Line Status Register (LSR).
	bit		meaning
	---		-------
	0		Data ready.
	1		Overrun error - Data register overwritten.
	2		Parity error - bad transmission.
	3		Framing error - No stop bit was found.
	4		Break detect - End to transmission requested.
	5		Transmitter holding register is empty.
	6		Transmitter shift register is empty.
	7               Time out - off line.
 *-------------------------------------------------------------------}
       RCVRDY   =       $01;
       OVRERR   =       $02;
       PRTYERR  =       $04;
       FRMERR   =       $08;
       BRKERR   =       $10;
       XMTRDY   =       $20;
       XMTRSR   =       $40;
       TIMEOUT	=	$80;

{-------------------------------------------------------------------*
  Bit values held in the Modem Output Control Register (MCR).
	bit     	meaning
	---		-------
	0		Data Terminal Ready. Computer ready to go.
	1		Request To Send. Computer wants to send data.
	2		auxillary output #1.
	3		auxillary output #2.(Note: This bit must be
			set to allow the communications card to send
			interrupts to the system)
	4		UART ouput looped back as input.
	5-7		not used.
 *------------------------------------------------------------------}
       DTR     =        $01;
       RTS     =        $02;
       MC_INT  =	$08;


{------------------------------------------------------------------*
  Bit values held in the Modem Input Status Register (MSR).
	bit		meaning
	---		-------
	0		delta Clear To Send.
	1		delta Data Set Ready.
	2		delta Ring Indicator.
	3		delta Data Carrier Detect.
	4		Clear To Send.
	5		Data Set Ready.
	6		Ring Indicator.
	7		Data Carrier Detect.
 *------------------------------------------------------------------}
       CTS   =          $10;
       DSR   =          $20;
       DCD   =          $80;


{------------------------------------------------------------------*
  Bit values held in the Interrupt Enable Register (IER).
	bit		meaning
	---		-------
	0		Interrupt when data received.
	1		Interrupt when transmitter holding reg. empty.
	2		Interrupt when data reception error.
	3		Interrupt when change in modem status register.
	4-7		Not used.
 *------------------------------------------------------------------}
       RX_INT   =       $01;


{------------------------------------------------------------------*
  Bit values held in the Interrupt Identification Register (IIR).
	bit		meaning
	---		-------
	0		Interrupt pending
	1-2             Interrupt ID code
			00=Change in modem status register,
			01=Transmitter holding register empty,
			10=Data received,
			11=reception error, or break encountered.
	3-7		Not used.
 *------------------------------------------------------------------}
       RX_ID   =        $04;
       RX_MASK =        $07;


{
    These are the port addresses of the 8259 Programmable Interrupt
    Controller (PIC).
}
       IMR1    =        $21;   { Interrupt Mask Register port }
       ICR1    =        $20;   { Interrupt Control Port       }


{
    These are the port addresses of the second 8259 Programmable Interrupt
    Controller (PIC2).
}
       IMR2    =        $A1;   { Interrupt Mask Register port }
       ICR2    =        $A0;   { Interrupt Control Port       }


{
    An end of interrupt needs to be sent to the Control Port of
    the 8259 when a hardware interrupt ends.
}
       EOI     =        $20;   { End Of Interrupt }


{
    The (IMR) tells the (PIC) to service an interrupt only if it
    is not masked (FALSE).
}
       IRQ3     =       $0B;    { Actual IRQ Number }
       IRQ4     =       $0C;
       IRQ5     =       $0D;
       IRQ7     =       $0F;

       IRQ3MASK     =       $F7;  { COM2, bit 3 }
       IRQ4MASK     =       $EF;  { COM1, bit 4 }
{       IRQ5MASK     =       $DF;  { LPT2 or BUS MOUSE, bit 5 }
{       IRQ7MASK     =       $7F;  { LPT1, bit 7 }

page up


COMTEST.PAS
uses serial, crt;

var
  Com: TComPort;
  cs, cr: Char;
  str: string;


begin
  clrscr;

  Com.Init(2, 19200, '8N1', $4000, mdNormal, 0, 0);
  Com.Com_On;

  cs := #0;
  cr := #0;
  str:= 'at&v'#13;

  while cs <> #27 do
  begin
    if keypressed then
    begin
      cs:= readkey;

      if cs <> #27 then
        if cs = '#' then
        begin
          Com.WriteStr('atdt 06071284420'#13);
        end else
          if Com.PutC(cs) = 1 then
          begin
            if cs = #13 then write(#10);
          end;
    end;

    if Com.GetC(cr) = 1 then write(cr);

  end;

  Com.Com_Off;
  Com.Done;

end.

page up

This page was created using Code Colorizer -    http://www.chami.com/colorizer





Bookmark this page.
 
 

Copyright © Tom Wellige, 1995-2008
All Rights Reserved