Main Site Documentation

Strange timing problem with SerialPort


#1

While building a CSMA communication protocol over rs485 I run into a strange timing issue which I suspect to be in the UART port driver. In order to detect collisions while writing data to the bus, I cycle out the data byte per byte and immediately read them back in (the receive enable of the rs485 driver chip is always on).

The strange thing is that where the send-readback cycle take about 20ms consistently for the first 4 bytes, it goes up to >200ms for the 5th byte and even higher for the 6th byte. The process break there as I’ve sent the read timeout to 250ms.

Code snippet and output is below. I assume the code is pretty self-explanatory (at least, that’s the intention).
Anyone has any idea what’s going on ? Coule the GC collection cycle have anything to do with this problem ?


        private bool TransmitAndVerify(byte[] buffer, int offset, int count)
        {
            int bytesRead;
            byte[] localReadBuffer = new byte[1];
            byte[] localWriteBuffer = new byte[1];
            bool failed = false;

            driverEnablePin.Write(true);
            Thread.Sleep(Configuration.ToggleTransmitPinSleepTime);
            for (int i = offset; i < offset + count; i++)
            {
                localWriteBuffer[0] = buffer[i];
                Log.Write("Rs485Socket", "TransmitAndVerify", "Writing " + localWriteBuffer[0] + ", i="+i);
                port.Write(localWriteBuffer, 0, 1);
                bytesRead = port.Read(localReadBuffer, 0, 1);
                Log.Write("Rs485Socket", "TransmitAndVerify", "Read back " + localReadBuffer[0] + ", bytesRead=" + bytesRead);
                if (bytesRead < 1) failed = true; // timeout occured during read operation - abort
                if (localReadBuffer[0] != buffer[i]) failed = true; // sent byte was not received correctly - indicates a collision on the bus
                if (failed)
                {
                    Log.Write("Rs485Socket", "TransmitAndVerify", "Transmit failed at i="+i);
                    throw new Exception("Transmit failed");
                }
            }
            driverEnablePin.Write(false);
            Thread.Sleep(Configuration.ToggleTransmitPinSleepTime);
            return true;

typical Debug Output :

26:0.361 @ Rs485Socket.TransmitAndVerify > Writing 165, i=0
26:0.375 @ Rs485Socket.TransmitAndVerify > Read back 165, bytesRead=1
26:0.383 @ Rs485Socket.TransmitAndVerify > Writing 0, i=1
26:0.397 @ Rs485Socket.TransmitAndVerify > Read back 0, bytesRead=1
26:0.405 @ Rs485Socket.TransmitAndVerify > Writing 2, i=2
26:0.419 @ Rs485Socket.TransmitAndVerify > Read back 2, bytesRead=1
26:0.428 @ Rs485Socket.TransmitAndVerify > Writing 1, i=3
GC: 2msec 26064 bytes used, 38316 bytes available
Type 0F (STRING ): 396 bytes
Type 11 (CLASS ): 2496 bytes
Type 12 (VALUETYPE ): 252 bytes
Type 13 (SZARRAY ): 1332 bytes
Type 15 (FREEBLOCK ): 38316 bytes
Type 16 (CACHEDBLOCK ): 72 bytes
Type 17 (ASSEMBLY ): 11556 bytes
Type 18 (WEAKCLASS ): 48 bytes
Type 19 (REFLECTION ): 24 bytes
Type 1B (DELEGATE_HEAD ): 252 bytes
Type 1D (OBJECT_TO_EVENT ): 192 bytes
Type 1E (BINARY_BLOB_HEAD ): 5748 bytes
Type 1F (THREAD ): 768 bytes
Type 20 (SUBTHREAD ): 96 bytes
Type 21 (STACK_FRAME ): 1428 bytes
Type 27 (FINALIZER_HEAD ): 168 bytes
Type 31 (IO_PORT ): 216 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 948 bytes
26:0.442 @ Rs485Socket.TransmitAndVerify > Read back 1, bytesRead=1
26:0.468 @ Rs485Socket.TransmitAndVerify > Writing 0, i=4
26:0.655 @ Rs485Socket.TransmitAndVerify > Read back 0, bytesRead=1
26:0.664 @ Rs485Socket.TransmitAndVerify > Writing 0, i=5
26:0.923 @ Rs485Socket.TransmitAndVerify > Read back 0, bytesRead=0
26:0.930 @ Rs485Socket.TransmitAndVerify > Transmit failed at i=5
#### Exception System.Exception - 0x00000000 (3) ####
#### Message: Transmit failed
#### WiredNetwork.Utilities.Rs485Socket::TransmitAndVerify [IP: 00fc] ####
#### WiredNetwork.Utilities.Rs485Socket::SocketLoop [IP: 0162] ####
A first chance exception of type ‘System.Exception’ occurred in WiredNetwork.dll
An unhandled exception of type ‘System.Exception’ occurred in WiredNetwork.dll

Additional information: Transmit failed


#2

After further investigation, I turns out that the receiver enable ping on the rs485 driver chip changes state all by itself ! I’m setting the status of this pin only once in my code, at the configuration start, so it should not change state in the middle of execution.

I drive this pin from FEZ_Pin.Digital.Di2 does this pin have a second function that I am overlooking ?


#3

The right way to do this would be through RLP


#4

I have posted an RLP driver for RS-485 communications on http://code.tinyclr.com/project/280/rs-485-driver-using-rlp/

But I must say: CSMA will never work reliable on RS-485. That’s one of the reasons why they developed the CAN bus :slight_smile:


#5

Wouter,

Thanks for the code. I’ll certainly investigate RLP.
Why should CSMA not work over rs485 ?
As long as you can sense the carrier close to the start of transmission and have a mechanism to resolve collisions (e.g. through retransmitting the packet that collided) I see no reason why it shouldn’t work reliably. I’m certainly not an expert on the matter, so I may be overlooking something.

Stefaan


#6

I investigated that to. RS-485 is for single master, multiple slaves. Collisions are not allowed on RS-485, it will burn your drivers. You could use the TX line to switch the driver direction and create an open-collector system (this is how they used RS-485 drivers in early CAN systems). And you should add auto retransmission with a random delay (or use the device address as retransmit delay). You could get it to work, if the masters don’t have to send a lot of data.

On the other hand, CAN uses almost the same electrical standard. But you get collision detection per bit (not per byte), recovery and error detection for free. In addition, all of this is handled in hardware not in software.

You can only detect errors at your own node, while in a CAN system, other nodes will NACK a packet that contains errors; even if the packet is not ment for that node.

This topic has already been discussed on many forums, search google for CSMA RS485.

Use the right tool for the job :slight_smile:


#7

Wouter,

Elektor Magazine is currently developing a series on a rs485 CSMA protocol for µC networking.
I have been working on the idea longer than the series in Elektor. I already have a sensor network running on AVR, but I need more complexity and my experience is in C# rather than in C.
Neither in my own experience nor in the Elektor bus discussions have I ever seen or hear of an rs485 driver blowing up because two masters are transmitting at the same time.

This being said, I guess I should do some reading up on CAN.

Stefaan


#8

Well the datasheets says they are short circuit protected.

I have worked for more than 8yrs with RS-485 (in domotics) and we had many installers that needed to replace ALL drivers on the network because of one that was failing and was blocked in transmit state.

We used drivers of Maxim (MAX1487), also tried drivers from TI.

Also, when you don’t use open collector, you can’t send broadcast messages. When 2 masters at different location but on same bus are broadcasting at the same moment, they will both read back their data just fine, but nodes in the middle of the bus will just receive garbage.