0

I am implementing the SAS protocol where its documented that SAS will poll with data-packet which size is 11 bit, where one start bit, eight data bit , ninth wakeup bit and one stop bit. Also documented that When SAS sned the message then it set wakeup bit for first byte of message after that for other byte message wakeup bit is clear. and also we have to use parity bit as wakeup bit.

i am facing issue with receiving the data from comport. what i suppose to get the data is:

01 73 1D 00 09 03 00 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 B3 74 A4 02 0E 76

but i am getting

73 1D 00 09 03 00 00 01 02 03 04 05 06

here first byte is 01 is not coming into the received data as well as data after 06

bellow are the code for creating the serial port object.

SerialPort _serialPort = new SerialPort("COM1", 19200, Parity.None, 8, StopBits.One);

and when i am writing to Comport i set the Parity to MARK. like:

  byte[] f = Response.ToArray();
  _serialPort.Parity = Parity.Mark;
  _serialPort.Write(f, 0, f.Length);

and when i am reading from comport

private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
 {
            SASobj._serialPort.ParityReplace = 0;

            SASobj._serialPort.Parity = Parity.Space;

            byte[] data = new byte[SASobj._serialPort.BytesToRead];
            SASobj._serialPort.Read(data, 0, data.Length);

            InputData = ByteToHex(data);

            this.BeginInvoke(new SetTextCallback(SetText), new object[] { InputData });


        }

 private void SetText(string text)
        {

            this.txtMessage.Text += text;

        }
Manish Kumar
  • 595
  • 2
  • 5
  • 20
  • That code will never work as you expect, nothing guarantees that you will receive a full packet on a read operation, take a look at my answers on these questions, they will help you understand how to read the data properly: http://stackoverflow.com/questions/35175874/does-function1-and-function2-use-my-serial-port-class-in-thread-safe-way/35246817#35246817 http://stackoverflow.com/questions/35636655/msvs-c-sharp-serialport-received-data-loss/35637286#35637286 http://stackoverflow.com/questions/30499210/using-dma-to-access-high-speed-serial-port/30781505#30781505 – Gusman May 06 '16 at 14:05
  • It makes no sense to change port `Parity` inside the `DataReceived` event; decide whether is `Parity.None`/`.Space` or `.Mark` at the beginning. I would presume this is why you stop receiving data after the first chunk. Also post the `ByteToHex` method, because it may have a bug. Also, you should enqueue the data to a `Queue` (or even a `string`) synchronously (inside the handler) if you want to ensure that blocks don't get concatenated out of order. The dispatch the UI update of the entire queue/`string`, because concatenating inside `SetText` might be executed out of order. – vgru May 06 '16 at 15:38

1 Answers1

1

Since your protocol seems to be abusing the parity bit, you should start by setting SerialPort.ParityReplace to zero before you start receiving data. Actual value of SerialPort.Parity is then practically irrelevant, since you'll have parity errors half of the time anyway, but changing these properties in the middle of an incoming message will surely create problems.

However, note that parity errors get reported using a different event, which is SerialPort.ErrorReceived, with one of the arguments set to SerialError.RXParity.

Also, as I noted in comments, calling BeginInvoke is not a good idea because it queues the method call on a background (ThreadPool) thread, which means the order in which multiple calls to SetText happen doesn't have to be sequential.

As @Gusman suggested, there are better ways of getting data from the port, but the most simple way to ensure that you get ordered merges would be something like:

readonly object _lock = new object();
SerialPort _serialPort;
string _text = "";

public void Init()
{
    _serialPort = new SerialPort("COM1", 19200, Parity.Space, 8, StopBits.One);
    _serialPort.ParityReplace = 0;
    _serialPort.DataReceived += DataReceived;
    _serialPort.ErrorReceived += ErrorReceived;
    _serialPort.Open();
}

void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    DumpToHexString();
}

void ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
{
    if (e.EventType == SerialError.RXParity)
        DumpToHexString();
}

void DumpToHexString()
{
    lock (_lock)
    {
        while (_serialPort.BytesToRead > 0)
        {
            var chunk = new byte[_serialPort.BytesToRead];
            _serialPort.Read(chunk, 0, chunk.Length);
            _text += ByteToHex(chunk);
        }
    }        
    this.BeginInvoke(new Action(() => txtMessage.Text = _text));
}

Additionally, MSDN docs for SerialPort.ErrorReceived have a remark regarding rx parity errors:

If a parity error occurs on the trailing byte of a stream, an extra byte will be added to the input buffer with a value of 126.

I am presuming this means a trailing byte of value 126 inside ErrorReceived should be discarded, but I haven't actually had such a specific case, so you might want to check if this is really the case.

vgru
  • 49,838
  • 16
  • 120
  • 201
  • Thanks.. this is great help, but i have one more issue with this. actually SAS protocol always poll with 80 and 81 and when i am getting this result it is appending the 80 and 81 in starting and in end. like "80 {01 73 1D 00 09 03 00 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 B3 74 A4 02 0E 76} 81 80 81 80 " i want only curly bracket result and if i am putting logic for getting value then the result is coming in same way as i was getting.actually how could i know this is starting of packet and this bit is end of packet. – Manish Kumar May 06 '16 at 17:40
  • Should these bytes always be at the beginning/end? Does this message have a CRC/checksum? – vgru May 07 '16 at 06:17
  • ok , i have fixed it by sleeping the thread for 40ms before lock, and before writing on comport i sleep the thread for 20ms. by doing this i am getting the data, i have read in the document that SAS send me whole data in 40 ms so i have sleep the thread for 40 ms. does it correct way to do? – Manish Kumar May 07 '16 at 06:49
  • @ManishKumar: sleeping is usually not a good approach in Windows, because there are no guarantees about precision. It may work most of the time, but fail on some occasions. I am not sure why you would have to sleep. If I understood right, you are sending bytes `0x80 0x81` to the port, and these bytes get appended to the incoming message? I don't see why a delay would change this, because .NET `SerialPort` is a wrapper around system serial port, which has its own incoming buffer (which gets filled regardless of your delays). – vgru May 07 '16 at 07:39