Link to home
Start Free TrialLog in
Avatar of AlexFM
AlexFM

asked on

Serial communications

I have very old Pascal program which reads data from some device and writes it to text file. I want to write this data also to COM port. I can build this program using Turbo Pascal.
Program has the following procedure:

PROCEDURE WRITE_DATA(V2,TIME_IS,V,V1,V4,V5:REAL);
  VAR
     results: text;
  BEGIN;
    assign (results, filename);
    append(results);
    writeln(results,V,' ',TIME_IS,'  ',V2,'  ',V1,' ',V4,' ',V5);
    close(results);
  END;

This procedure is called periodically and resulting text file looks like this:

 0.0000000000E+00  1.7579170000E+04   0.0000000000E+00   1.2846000000E-01  0.0000000000E+00  0.0000000000E+00
 0.0000000000E+00  1.7584450000E+04   0.0000000000E+00   1.3625000000E-01  0.0000000000E+00  0.0000000000E+00
...

I want to write these 6 values to COM port.
If somebody has code for this, please post it. Since I don't have experience in Turbo Pascal and working with it using programmer's common sence, please give detailed answer. I also want to know what data is written to the port, because I need to interpret it in the Windows client program. What is REAL type, how many bytes it takes etc.
Thanks.
Avatar of AlexFM
AlexFM

ASKER

BTW, I found some information in EE about serial communications in Pascal, but I hope to get specific answer to my question. I don't know how to apply this information and how to send REAL numbers.
So, Pascal experts, make your points! I will ask additional questions if necessary.
{$U+}

  var
    Port,Baud,StopBits,DataBits,Par: Integer;
    Message: String[80];

  type
    String19=String[19];

{ A set of routines to enable COM1 and COM2 to be accessed from Turbo Pascal.
  The following procedures are meant to be called by your programs:

  AssignAux(PortNumber in [1,2]) assigns Aux to COM1 or COM2
  AssignUsr(PortNumber in [1,2]) assigns Usr to COM1 or COM2
  SetSerial(PortNumber in [1,2],
            BaudRate in [110,150,300,600,1200,2400,4800,9600],
            StopBits in [1,2],
            DataBits in [7,8],
            Parity in [None,Even,Odd]) sets the baud rate, stop bits, data
                               bits, and parity of one of the serial ports.

  The arrays InError and OutError may be examined to detect errors.  The bits
  are as follows:
     Bit 7 (128)        Time out (no device connected)
     Bit 3 (8)          Framing error
     Bit 2 (4)          Parity error
     Bit 1 (2)          Overrun error

  Function SerialStatus(PortNumber in [1,2]) returns a more complete status:
     Bit 15 (negative)  Time out (no device connected)
     Bit 14 (16384)     Transmission shift register empty
     Bit 13 (8192)      Transmission holding register empty
     Bit 12 (4096)      Break detect
     Bit 11 (2048)      Framing error
     Bit 10 (1024)      Parity error
     Bit 9  (512)       Overrun error
     Bit 8  (256)       Data ready
     Bit 7  (128)       Received line signal detect
     Bit 6  (64)        Ring indicator
     Bit 5  (32)        Data set ready
     Bit 4  (16)        Clear to send
     Bit 3  (8)         Delta receive line signal detect
     Bit 2  (4)         Trailing edge ring detector
     Bit 1  (2)         Delta data set ready
     Bit 0  (1)         Delta clear to send

  Identifiers starting with "__" are not meant to be used by the user program.
}

  Type
    __RegisterSet=Record case Integer of
                  1: (AX,BX,CX,DX,BP,DI,SE,DS,ES,Flags: Integer);
                  2: (AL,AH,BL,BH,CL,CH,DL,DH: Byte);
                end;
    __ParityType=(None,Even,Odd);

  var
    __Regs: __RegisterSet;
    InError,OutError: Array [1..2] of Byte;

  procedure __Int14(PortNumber,Command,Parameter: Integer);
  { do a BIOS COM driver interrupt }

    begin
      with __Regs do
       begin
        DX:=PortNumber-1;
        AH:=Command;
        AL:=Parameter;
        Flags:=0;
        Intr($14,__Regs);
       end;
    end;


  procedure SetSerial(PortNumber,BaudRate,StopBits,DataBits: Integer;
                      Parity: __ParityType);
  { Set serial parameters on a COM port }

    var
      Parameter: Integer;

    begin
      case BaudRate of
        110: BaudRate:=0;
        150: BaudRate:=1;
        300: BaudRate:=2;
        600: BaudRate:=3;
        1200: BaudRate:=4;
        2400: BaudRate:=5;
        4800: BaudRate:=6;
        else BaudRate:=7; { Default to 9600 baud }
       end;
      if StopBits=2 then StopBits:=1
      else StopBits:=0; { Default to 1 stop bit }
      if DataBits=7 then DataBits:=2
      else DataBits:=3; { Default to 8 data bits }
      Parameter:=(BaudRate Shl 5)+(StopBits Shl 2)+DataBits;
      case Parity of
        Odd: Parameter:=Parameter+8;
        Even: Parameter:=Parameter+24;
        else; { Default to no parity }
       end;
      __Int14(PortNumber,0,Parameter);
    end;


  Function SerialStatus(PortNumber: Integer): Integer;
  { Return the status of a COM port }

    begin
      __Int14(PortNumber,3,0);
      SerialStatus:=__Regs.AX;
    end;


  procedure __OutPort1(C: Byte);
  { Called by Write to Aux or Usr when assigned to COM1 }

    begin
      while (SerialStatus(1) and $30)=0 do ;
      __Int14(1,1,C);
      OutError[1]:=OutError[1] Or (__Regs.AH and $8E);
    end;




  Function __InPort1: Char;
  { Called by Read from Aux or Usr when assigned to COM1 }

    begin
      __Int14(1,2,0);
      __InPort1:=Chr(__Regs.AL);
      InError[1]:=InError[1] Or (__Regs.AH and $8E);
    end;





  procedure __AssignPort(PortNumber: Integer; var InPtr,OutPtr: Integer);
  { Assign either Aux or Usr to either COM1 or COM2 }

    begin
        OutPtr:=Ofs(__OutPort1);
        InPtr:=Ofs(__InPort1);
        InError[1]:=0;
        OutError[1]:=0;
    end;


  procedure AssignAux(PortNumber: Integer);
  { Assign Aux to either COM1 or COM2 }

    begin
      __AssignPort(PortNumber,AuxInPtr,AuxOutPtr);
    end;




  Function Binary(V: Integer): String19;

    var
      I: Integer;
      B: Array [0..3] of String[4];

    begin
      For I:=0 To 15 do
        if (V and (1 Shl (15-I)))<>0 then B[I Div 4][(I Mod 4)+1]:='1'
        else B[I Div 4][(I Mod 4)+1]:='0';
      For I:=0 To 3 do B[I][0]:=Chr(4);
      Binary:=B[0]+' '+B[1]+' '+B[2]+' '+B[3];
    end;

procedure comset;


  begin
    port :=1;

    assignAUX(Port);
    baud :=1200;

    stopbits :=1;

    databits :=8;

    par :=0;
    message :='ATZ';

    SetSerial(1,Baud,StopBits,DataBits,__ParityType(Par));
    WriteLn(AUX,Message);

  end;
var
  c : char;
begin

     comset;

     repeat

     if keypressed then begin {$I-}
     read(kbd,c);
     write(c);

     end;

     until (c = ^E);

end.

This will do exactly what your wanting it to but you will need to tweak the parameters of the port via the code.

Hope this helps.
Avatar of AlexFM

ASKER

Thanks. Can you change the last function code to send REAL number instead of characters? I need to send 5 REAL numbers from WRITE_DATA function, and I have no idea how to convert real to characters. Something like C pointers, unions, anything else?

Real was defined as this on MIT's website:

The real data type has a positive range from 3.4x10-38 to 3.4x1038. Real values can be written in either fixed-point notation or in scientific notation, with the character E separating the mantissa from the exponent. Thus, 452.13 is the same as 4.5213e2


http://www.mit.edu/~taoyue/tutorials/pascal/pas1d.html

Mugman
You do not need BIOS or hardware access to a COM port to send data through it.
All you need to do is to set filename as a 'com1' or 'com2'.

Dos is capable of sending data through COM ports with file access as well. But COM port transfer parameters have to be set before program is run, or through BIOS or hardware access.

Just set filename as 'com1'

const filename='com1';
or
filename:=com1;

Tested it, and it works.

I used following program for testing:

Program COM;

 const filename='com1';

PROCEDURE WRITE_DATA(V2,TIME_IS,V,V1,V4,V5:REAL);
  VAR
     results: text;
  BEGIN;
    assign (results, filename);
    append(results);
    writeln(results,V,' ',TIME_IS,'  ',V2,'  ',V1,' ',V4,' ',V5);
    close(results);
  END;

begin
WRITE_DATA (1,2,3,4,5,6);
end.
Avatar of AlexFM

ASKER

Using this way, can I set COM port parameters in the program before sending data?
I used default com port settings, but it should be possible to set it with procedure:
procedure SetSerial(PortNumber,BaudRate,StopBits,DataBits: Integer; Parity: __ParityType);
Avatar of AlexFM

ASKER

Testing, thanks.
Avatar of AlexFM

ASKER

Trying to compile abbdan' code:
Error 25 Type mismatch

Cursor is set to the line:

 Intr($14,__Regs);
ASKER CERTIFIED SOLUTION
Avatar of For-Soft
For-Soft
Flag of Poland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of AlexFM

ASKER

Error 160: Device write fault.

On the line:
writeln(results,V,' ',TIME_IS,'  ',V2,'  ',V1,' ',V4,' ',V5);

Computer has one COM port. It is running under DOS, I don't know how to see COM port settings. I see this port in Windows.
I get the same error, if there is no COM connection between computer and the other device.

I used a second computer (connected with link cable) with terminal program running (NC terminal emulation) as a receiver device.
Avatar of AlexFM

ASKER

I have ttwo computers connected with NULL-modem. VC++ programs listens for COM port. It has the following COM port settings:

DCB dcb;

    dcb.BaudRate = 9600;
    dcb.ByteSize = 8;
    dcb.Parity   = NOPARITY;
    dcb.StopBits = ONESTOPBIT;
    dcb.EvtChar = 0;

    dcb.fDtrControl     = 1;
    dcb.fRtsControl     = 1;
    dcb.fOutxCtsFlow    = 0;
    dcb.fOutxDsrFlow    = 0;
    dcb.fDsrSensitivity = 0;
    dcb.fOutX           = 0;
    dcb.fInX            = 0;
    dcb.fTXContinueOnXoff = 0;
    dcb.XonChar         = 0x11;
    dcb.XoffChar        = 0x13;
    dcb.XonLim          = 0;
    dcb.XoffLim         = 0;

    dcb.fParity = TRUE;

If this doesn't say you anything, plase explain how COM port is initialized in Pascal:

Initialize (com1,bd9600,pEven,sI,b8);

I guess that baud rate is 9600, what are other parameters?
I believe parity is a problem. Try this:

Initialize (com1,bd9600,pNone,sI,b8);

Parity settings:
       pNone = 0;
       pOdd = 1;
       pEven = 3;
Stop bits settings:
       sI = 0; {1 stop bit)
       sII = 1; (2 stop bits)
Byte size settings:
       b7 = 2;
       b8 = 3;
Avatar of AlexFM

ASKER

Still get error. Tries to change dcb.fParity to FALSE in VC++ client.
What is sI and b8? I can try to play with COM port parameters in VC++ program.
Avatar of AlexFM

ASKER

Didn't see your last post, trying.
Try to check the connection first.
Run the terminal program, on both computers. This will help to check, if connection works right.

Try RTS/CTS flow control
dcb.fDtrControl     = 0;
dcb.fRtsControl     = 1;
dcb.fOutxCtsFlow    = 1;
dcb.fOutxDsrFlow    = 0;

I did not tested DTR/DSR
dcb.fDtrControl     = 0;
dcb.fRtsControl     = 1;
dcb.fOutxCtsFlow    = 1;
dcb.fOutxDsrFlow    = 0;

The setting you posted:
dcb.fDtrControl     = 1;
dcb.fRtsControl     = 1;
dcb.fOutxCtsFlow    = 0;
dcb.fOutxDsrFlow    = 0;
looks very odd to me, as I never seen DTR/RTS flow control before.    
Avatar of AlexFM

ASKER

DCB parameter ByteSize may be from 4 to 8. Your byte size is 3. Maybe this is a reason?
I tried to change:

Initialize (com1,bd9600,pEven,sI,8);

without success.
Avatar of AlexFM

ASKER

   dcb.fDtrControl     = 0; //1;
    dcb.fRtsControl = RTS_CONTROL_ENABLE;  // RTS_CONTROL_ENABLE     0x01
    dcb.fOutxCtsFlow    = 1;
    dcb.fOutxDsrFlow    = 0;

The same result. What about ByteSize?
I used RTS/CTS flow control for testing.
Since the program worked right with my terminal program, it looks like it is not the program issue.

Byte length 2 and 3 setting comes from BIOS com port routine, where 2 bits are used for character length selection:
11 - 8 bit character
10 - 7 bit character

Some flow control systems will not work with some serial link cable configurations. Check if the connection between two terminal programs exists, first.
Avatar of AlexFM

ASKER

I am working with standard null-modem which allows to connect two computers and also two COM port on the same computer.
Let's say I don't have my own program in Windows, and need to receive data using HyperTerminal. Can you describe how can I do this? Pascal:
Initialize (com1,bd9600,pNone,sI,b8);

What should I do in Windows to receive this? For now, this doesn't work.

I am working in DOS on the server computer (with Pascal program). It is impossible to run it under Windows because this program talks with device by DOS way. If I run Windows on this computer, two my programs are talking successfully.
I never used Hyper Terminal to connect through COM port. I do not know how to do it, because it want's to create modem connection first. I never used COM cable conection in windows except with Direct Cable Connection built in software.
It was easier than I thought. Hyper Terminal connection works fine. I used Hardware Flow Control.

I'm not sure, if I understand you correctly. Connection works, when both computers are running windows. But it is not working when running DOS.

I saw similiar problems, when COM port was set to AUTO in BIOS setup on some intel chipset machines with phoenix BIOS. Maybe this is the source of the problem. The solution was to set COM port adress in BIOS setup to a fixed value.
Avatar of AlexFM

ASKER

Program still doesn't work, but I think nk I got enough information in this thread. For-Soft, you are invited to participate in my next question.
Avatar of AlexFM

ASKER

For-Soft, thank you very much for your help, the program is working. I got additional information in this thread:
https://www.experts-exchange.com/questions/21236068/Serial-communications-between-DOS-and-Windows-computers.html

After adding loopback connections to communication cable the program is working as expected.
We have used the DOS to send data. The cable has to match full RS232 7 wire connection. My cable certainly is. I bought it in computer shop. It's a tricky and universal design with 2 connectors at each end (1 9pin and 1 25pin). The bad thing about it is: it is reletively more difficult to carry.