How to interpret RS232C interface communication written using Pascal to C#

对着背影说爱祢 提交于 2019-12-25 04:42:38

问题


I've a sample pascal program, provided in a technical handbook of coin counter S350/360 machine, that enables communication between the machine and a computer using RS232C cable. I've came up with the following equivalent C# code and for some reason only the procedures that only need to write data to the serial port are executing and working successfully. I can only write data to the port but cannot read any data from it. I checked this using a Serial Sniffer and it turns out it only sniffs data written to the port, using the C# program, but cannot read any data back. Could you guys help me with what's going on here?

First here is the original Pascal code of the sample program.

uses crt;

const
{ COM1: RS232 port address }
RXTX = $3F8; { $2F8 if COM2: is used }
ACK = 6;
NAK = 21;
ESC = 27;

var
dummy,
checkSum : integer;
key : char;
protocol : integer;

procedure InitComm;
{ Set baudrate to 9600, 8 bits, no parity, 1 stop bit }
var i : integer;
begin
i := 1843200 div 9600 div 16;
port[RXTX + 3] := $80;
port[RXTX + 1] := hi(i);
port[RXTX]:= lo(i);
port[RXTX + 3] := 3;
port[RXTX + 4] := $A;
while odd(port[RXTX + 5]) do
begin
dummy := port[RXTX];
delay(10);
end;
end; { InitComm }

procedure Tx(data : integer);
{ Transmit a character on serial channel }
begin
while port[RXTX + 5] and $20 = 0 do;
port[RXTX] := data and $FF;
end; { Tx }

function RxWait : integer;
{ Waits for a character from serial channel }
begin
while not odd(port[RXTX + 5]) do;
RxWait := port[RXTX];
end; { RxWait }

procedure Tx2(data : integer);
{ Transmit a char on serial channel + Calculate check sum }
begin
Tx(data);
checkSum := (checkSum + data) and $FF;
end; { Tx2 }

procedure TxCommand(c1, c2 : char;
sendCheckSum : boolean);
{ Transmit command (no data) on serial channel }
begin
Tx(ESC);
checkSum := 0;
Tx2(ord(c1));
Tx2(ord(c2));
if sendCheckSum then
begin
Tx2(checkSum);
dummy := RxWait;
end;
end; { TxCommand }

function ReadNumber(n : integer) : real;
{ Read n bytes from serial channel }
var
number: real;
i : integer;
begin
number := 0;
checkSum := 0;
for i := 1 to n do
number := number * 256 + RxWait;
dummy := RxWait;
ReadNumber := number;
end; { ReadNumber }

procedure Revisions;
var
tmp : integer;
sw,
prot : real;
begin
TxCommand('P', 'R', FALSE);
checkSum := 0;
tmp := RxWait;
sw := tmp + RxWait / 100.0;
protocol := RxWait;
prot := protocol + RxWait / 100.0;
dummy := RxWait;
tmp := RxWait;
writeln('Software revision: ', sw:4:2);
writeln('Protocol revision: ', prot:4:2);
end; { Revisions }

procedure ReadCountReg;
begin
TxCommand('R', 'C', FALSE);
writeln(ReadNumber(4):11:0, ' coins counted.');
dummy := RxWait;
end; { ReadCountReg }

procedure ReadAccReg;
begin
TxCommand('R', 'A', FALSE);
writeln(ReadNumber(4):11:0, ' coins in accumulator.');
dummy := RxWait;
end; { ReadAccReg }

procedure Setbatch(limit : longint);
begin
TxCommand('W', 'L', FALSE);
case protocol of
1 : begin
Tx2(limit div 256);
Tx2(limit mod 256);
end;
2 : begin
Tx2( limit div 16777216);
Tx2((limit div 65536) mod 256);
Tx2((limit div 256) mod 256);
Tx2( limit mod 256);
end;
end; { case protocol }
Tx2(checkSum);
dummy := RxWait;
end; { Setbatch }

In the protocol descriptions two logical operators are used, + for arithmetic addition and ^ for logical AND. All messages have the following principal structure:

Computer SC 350/360
–––––––> ESC (message start)
–––––––> Command
<––––––> Data (direction depends on command)
<––––––> Check sum (direction depends on command)
<––––––– Receipt:
        - ACK (if check sum is correct) or
        - NAK (if check sum is incorrect)

If the data is a binary number, the most significant byte (character) is sent first. If the data is a text string, the first character of the string is sent first. A text string always ends with a NULL character.

The check sum for received characters in a message is derived as follows: The addition of every hexadecimal character value, except those for the message start (ESC), forms the sum. The check sum is then the sum ^255. If any of the fault conditions listed below occurs, the computer should wait for at least 250 ms from the time that the last character was received before re-sending the message:

  • The computer receives an incorrect check sum.
  • The machine has sent NAK.
  • The machine sends “invalid data”.
  • The machine does not respond within 250 ms. The machine accepts all messages when counting except the message that sets a new batch quantity.

For example; To start the machine 'TxCommand('M', '1', TRUE);' is issued as written in the handbook; I've implemented the pascal code for this in C#.Net and have successfully issued this command and the machine started right away. What I couldn't get working was the procedures 'ReadCountReg', 'ReadCountReg'and 'Setbatch'.

Below is the C# code I implemented:

 public class CoinCounter : IDisposable
{

    private const int RXTX = 0x3F8;
    private const int ACK = 6;
    private const int NAK = 21;
    private const int ESC = 27;

    private int dummy, checkSum;
    private char key;
    private int protocol;

    public SerialPort port;
    private bool _disposed = false;
    public CoinCounter()
    {
        //Initialize Communication
        port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
        port.Handshake = Handshake.None;
        port.Open();
        port.DtrEnable = true;
        port.RtsEnable = true;

    }

    public void ClosePort()
    {
        if (port.IsOpen)
            port.Close();
    }


    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                DisposeManagedResources();
            }
            DisposeUnmanagedResources();
            _disposed = true;
        }
    }

    private  void DisposeManagedResources() 
    {
        port.Dispose();
    }
    private  void DisposeUnmanagedResources() { }

     ~CoinCounter() 
    {
        Dispose(false);
    }

    public void Tx(int data)
    {

        port.Write(new byte[] { (byte)(data & 0xFF) }, 0, 1);

    }

    public int RxWait()
    {

        int readByte = 0;
        CommTimer timer = new CommTimer();
        timer.Start(250);
        while ((port.BytesToRead == 0) && (timer.timedout == false))
        {
        }
        if (port.BytesToRead > 0)
            readByte = port.ReadByte();

        return readByte;

    }

    public void Tx2(int data)
    {
        Tx(data);
        checkSum = (checkSum + data) & 0xFF;
    }

    public void TxCommand(char c1, char c2, bool sendCheckSum)
    {
        Tx(ESC);
        checkSum = 0;
        Tx2((int)c1);
        Tx2((int)c2);
        if (sendCheckSum)
        {
            Tx2(checkSum);
            dummy = RxWait();
        }
    }

    public double ReadNumber(int n)
    {
        double number;
        int i;
        number = checkSum = 0;
        for (i = 0; i < n; i++)
            number = number * 256 + RxWait();
        dummy = RxWait();
        return number;
    }

    public void Revisions(out double softVersion, out double protocolVersion)
    {
        int tmp;
        TxCommand('P', 'R', false);
        checkSum = 0;
        tmp = RxWait();
        softVersion = tmp + RxWait() / 100.0;
        protocol = RxWait();
        protocolVersion = protocol + RxWait() / 100.0;
        dummy = RxWait();
        tmp = RxWait();
    }

    public double ReadCountReg()
    {
        TxCommand('R', 'C', false);
        double coinsCounted = ReadNumber(4);
        dummy = RxWait();
        return coinsCounted;
    }

    public double ReadAccReg()
    {
        TxCommand('R', 'A', false);
        double coinsInAccumulator = ReadNumber(4);
        dummy = RxWait();
        return coinsInAccumulator;
    }

    public void SetBatch(long limit)
    {
        TxCommand('W', 'L', false);
        switch (protocol)
        {
            case 1:
                Tx2((int)(limit / 256));
                break;
            case 2:
                Tx2((int)(limit / 16777216));
                Tx2((int)(limit / 65536) % 256);
                Tx2((int)(limit / 256) % 256);
                Tx2((int)(limit % 256));
                break;
        }
        Tx2(checkSum);
        dummy = RxWait();
    }

    public class CommTimer
    {
        public System.Timers.Timer tmrComm = new System.Timers.Timer();
        public bool timedout = false;
        public CommTimer()
        {
            timedout = false;
            tmrComm.AutoReset = false;
            tmrComm.Enabled = false;
            tmrComm.Interval = 1000; //default to 1 second
            tmrComm.Elapsed += new ElapsedEventHandler(OnTimedCommEvent);
        }

        public void OnTimedCommEvent(object source, ElapsedEventArgs e)
        {
            timedout = true;
            tmrComm.Stop();
        }

        public void Start(double timeoutperiod)
        {
            tmrComm.Interval = timeoutperiod; 
            tmrComm.Stop();
            timedout = false;
            tmrComm.Start();
        }


    }


}

Note that the hand book states: 1. For simplicity the handshake lines are not used in this example. 2. No check sum implemented.

While most of the commands, with simple checksum calculation, can successfully communicate with the machine I couldn't get, for example, 'ReadCountReg' procedure to work and see the results of counted coins. The protocol for this procedure is;

Computer SC 350/360
–––––––> ESC
–––––––> “R”
–––––––> “C”
<––––––– CountRegister (CR)
<––––––– (CR ^ FF00,000016 + CR ^ FF,000016 + CR ^ FF0016 +CR ^ FF16) ^ FF16
<––––––– ACK

Could you guys help me get this working as I don't have much experience with the serial port communication and could help me resolve my issue.


回答1:


SerialPort implements IDisposable. You are acquiring a serial port and you are never disposing it - this is an error. If your usage model is one wherein the client code will always make a call to you when it's done, you should Dispose the object then (this doesn't look like your model). Otherwise, you should implement IDisposable yourself (follow the code template. In your case, you will be implementing DisposeManagedResources() and disposing the serial port then) and ensure that your object is Disposed when finished.

A final option is to have the calling code allocate and dispose the serial port and pass it in to the constructor of your code.



来源:https://stackoverflow.com/questions/21535967/how-to-interpret-rs232c-interface-communication-written-using-pascal-to-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!