4
votes

I have a Coin Counter Machine(SC350/360) which connects to a computer via RS232C Interface. I've the technical documentation which describes the communication protocols and a working pascal program is also included for manipulating the machine. I copied the pascal code and tested it on Turbo Pascal, using DosBox, for windows 7 64 bit and the code compiles successfully. What I wanted to achieve now was to convert those pascal codes to C#.Net but I was having a hard time when converting few lines to C# as I don't have much experience on serial port programming.

This was the code in Pascal to Initialize communication with the machine. (Set baudrate to 9600, 8 bits, no parity, 1 stop bit)

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;

    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 }

The corresponding C# for the above code I came up was; (Please correct me if I get it wrong)

SerialPort port=new SerialPort("COM1",9600,Parity.None,8,StopBits.One);

But I couldn't understand how to convert the rest of the pascal procedures. Some of these procedures i had difficulty with are;

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 }

Could you guys please help me how to convert those pascal codes to the equivalent C#? I know I can write to a port using the 'port.Write' Method but that couldn't exactly fit the turbo pascal code with the port array.(e.g port[RXTX + 3] := $80;) I don't know what the port array index 'RXTX+3' is referring relating to C#.

I would really appreciate it if you could give me a hand on this and I hope I'll learn to convert the rest of the pascal codes myself. :)

I have written the following equivalent C# code for the pascal program using the help I got from the good people here. Please correct me if i have made a mistake in my code.

            public void Tx(int data)
            {
                if (!port.IsOpen)
                    port.Open();
                port.Write(new byte[] { (byte)(data & 0xFF) }, 0, 1);
                port.Close();
            }
            /// <summary>
            /// Wait for a character from serial channel
            /// </summary>
            /// <returns></returns>
            public int RxWait()
            {
                if (!port.IsOpen)
                    port.Open();
                int readByte = port.ReadByte();
                port.Close();
                return readByte;
            }
            /// <summary>
            /// Transmit a char on serial channel + Calculate check sum
            /// </summary>
            /// <param name="data"></param>
            public void Tx2(int data)
            {
                Tx(data);
                checkSum = (checkSum + data) & 0xFF;
            }

By the way here is the protocol described in the device documentation.

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)

And moreover i've here provided the rest of the code with an example of a command send to count the number of coins.

 /// <summary>
    /// Transmit command (no data) on serial channel
    /// </summary>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <param name="sendCheckSum"></param>
        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();
            }
        }
        /// <summary>
        /// Read n bytes from serial channel
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        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;
        }

        /// <summary>
        /// Read the number of Coins counted
        /// </summary>
        /// <returns>Number of Coins</returns>
        public double ReadCountReg()
        {
            TxCommand('R', 'C', false);
            double coinsCounted = ReadNumber(4);
            dummy = RxWait();
            return coinsCounted;
        }

To send a command to count coins;

double coinsCounted = ReadCountReg();
 Console.WriteLine(Math.Round(coinsCounted, 0) + " coins counted");

The protocol for reading count register is;

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

2 Answers

6
votes

Ugh. 8250 UART registers are documented here. Directly accessing the UART registers in Windows is not supported. Your SerialPort initialization is correct. Beware that it is fairly unlikely your machine has a COM1 port unless you have real hardware. USB emulators tend to pick higher port numbers. Use SerialPort.GetPortNames() to have a look-see.

Tx() waits for transmitter empty status bit. Simply replace with SerialPort.Write() to write one byte, it already blocks if the transmit buffer is full. It won't be.

RxWait() waits for the receiver ready status bit. Simply replace with SerialPort.ReadByte().

Tx2() is just a helper procedure to keep a simple checksum updated, just add the byte you send to a checksum variable.

4
votes

What's being done here is direct, PC hardware port (specifically COM1) access. For those curious what's going on, base port+5 is the line status register of a 8250 UART and its pincompatible successors like the 16450 and 16550. See here for more details concerning the inner workings of classic PC style serial ports.

Not sure if you'll even ever get this to work properly on Windows (and one thing is certain, it will never ever work with eg serial port dongles that are attached via USB), it's DOS code that partially relies on being quite close to the hardware, have perfect control over timing (DOS was single tasking) and perfectly knowing what hardware to expect. In most cases it should be possible to rely on the facilities that Windows (and in your case, the .Net framework) offer - The stuff you show above is for sending bytes (you can use the Write method for that). The checksum part should be quite trivial to reproduce.

.Net offers an API SerialPort, it should be possible to use that API and do away with this remnant of "good" old DOS days.