Two-wires RS485 single master multi slaves: how to define the receiving buffer

I have a typical two-wires RS485 network with multiple nodes. Only one node (the master) can start a communication: it sends a packet/request and receives a packet/answer. Usually the slaves can't communicate without the help of the master.

The slaves could be different, so running a different firmware on different CPUs. Of course, all the slaves understand a common protocol (syntax of packets). I'm trying to organize the firmware of a slave and I have problems to define the size of the receiving buffer.

Typically the bytes received are stored in a fifo buffer inside the ISR. During main loop, the FIFO is spooled for a new complete packet to decode and process. What is the size of the buffer to avoid missing packets? I can't answer.

Suppose this sequence: - master sends packet P1 to slave 1 - slave 1 answers after 10ms with packet P2 - master sends packet P3 to slave 2 after 10ms

I'm writing the firmware of slave 2. I could miss P1 and/or P2 packets, because they aren't for slave 2, but I need to process P3. Consider that I have a subroutine running on slave 2 that lasts 100ms. So, if I'm not lucky, I can enter this subroutine just before receiving the first byte of P1. During the subroutine, all the 3 packets appears on the network and should be buffered. So I need the space for 3 packets.

It seems the size of the rx fifo buffer of a slave depends on the maximum packet size that could appear on the network (even a request for

*another* slave or a slave answer), how fast are *other* slaves to answer and how fast is the master to start a new communication.

If there is a very fast slave that manages very big packets (super-computer) and very slow slave that manages very small packets (small 8-bit microcontrollers) how to tune the rx fifo buffer? I could arrive to a very big buffer size that isn't compatible to the small CPU.

Should I limit the maximum packet size, even for the supercomputer (this could be acceptable)? Should I delay the new communication from the master to let the slow slaves to process the previous request/answer packets (this could be annoying, because the time of a poll of all the slaves on the network increases)?

Any suggestion?

Reply to
pozz
Loading thread data ...

You need to have such packet framing that it is simple to detect packet boundaries. Also, you need that the destination address is very near the start of the packet. It this way, you can stop receiving a packet as soon as you note that it is not for yourself, and start the scan for the next packet without storing anything.

A good example is PPP in HDLC-like Framing (RFC1662).

--

Tauno Voipio
Reply to
Tauno Voipio

Normally when you have a bussed architecture like this there is an easy and unambiguous way of addressing a particular slave, and the slaves do not keep any communications that are not addressed to them.

9-bit asynchronous with hardware wake-up used to be popular for this, and may still be for that matter. With the right serial hardware you just configure the serial port to not interrupt unless the 9th bit is set, and you use the 9th bit to label address packets (or bytes). When a slave is idle it turns off the UART interrupts except for the address packets. Then it wakes up for an address packet, decides if the message is for it, and if not, it goes back to sleep.

Even without 9-bit addressing, you should make your protocol such that packets can be parsed independently from their content (i.e., read the OSI model and keep the layers separate). The slave should read the header of the packet, decide if the mail is address to it, then just not save the contents if not.

Beyond that -- yes, giving packets some fixed maximum length system wide is a Good Idea. If you're going to have a mix of low- and high-capacity slaves then you should have protocol layer that lets a slave either communicate its capacity to the master (requiring the master to keep track and not exceed that capacity), or you should have a protocol layer that breaks up large packets into small ones and lets the slave perform flow control. Either of these complicated the protocol.

Keep in mind, too, that once you initiate a transfer with some humongous low-priority packet, you're stuck with it until it's done. So if some high-priority traffic suddenly needs to flow, it just stays where it is waiting for the log-jam to clear. This is why the CAN protocol only allows a maximum of eight bytes of data in a CAN packet -- this holds down the throughput, but it decreases latency for high priority messages, too.

--
My liberal friends think I'm a conservative kook. 
My conservative friends think I'm a liberal kook. 
Why am I not happy that they have found common ground? 

Tim Wescott, Communications, Control, Circuits & Software 
http://www.wescottdesign.com
Reply to
Tim Wescott

Put some more intelligence into the ISR, i.e. a state machine.

As long as the slave address is near the beginning of the address, do not put anything into the FIFO until the slave address is received and compared. If the message is not for you, enter skip-until-end-of-frame state, so all the next received characters in the ISR are ignored, until the end-of-frame character is received.

If the message header contains a byte count field, store the count and decrement for each received character. When the count drops to zero, resume listening for next frame header.

The ISR will require 1-3 bytes statically allocated memory for the state machine current state and/or remaining byte count.

Reply to
upsidedown

Thank you all for your suggestions. They are very good.

I have just one problem in this approach. The packets are usually protected against communication error with a checksum (CRC or similar) at the end. How could I inspect and process the first bytes of the packets if I have to receive the CRC yet to convalidate the packet?

Should I add a second CRC just for the header that contains address info?

Reply to
pozz

This should not be a big problem.

The only critical question what happens, if the slave address is corrupted.

If the slave address is corrupted producing an (incorrect) address match, the whole message will be processed, but any higher level routine would reject the message due to CRC error.

However, if the message was addressed to you, but slave address corruption cause the message to be skipped, the master will not get a response and after timeout resends the request.

Reply to
upsidedown

That is not necessary. The only case a receiving error would break that scheme is when the address gets changed and looks like the correct one for this device. You can than continue to "receive" the message and put it into your buffer. If it exceeds the max length you know something must be wrong. Then skip- until-end-of-frame state and proceed as if it is not for you.

--
Reinhardt Behm
Reply to
Reinhardt Behm

If the address is not for me OR the checksum is bad, I will ignore this message.

As this is a simplex system, waiting for the last byte to come in is not going to delay anything.

At the point of the last byte, you can throw away the packet and start looking for the next.

Also, the amount of time processing that last packet, turn on the RS-485 driver, sending the response, with all the other boards in the system is just waiting for the next packet, is dead time for all by the working node.

What is the "timeout" time for the correctly addressed node that does not respond ?

Reply to
hamilton

Il 29/05/2013 09:02, snipped-for-privacy@downunder.com ha scritto: > > [...] >

Do you think it's better to discard a packet with a bad CRC or to answer with a "bad CRC" message to the master? In the latter scenario, the retransmission is speeded up (the master will not wait for the timeout), but I'm in doubt if it is correct to answer to a corrupted packet (even the destination address could be wrong).

Ok, I was trying to implement this kind of protocol.

Every packet is encapsulated in a SLIP frame (RFC1055). The last byte

0xC0 marks the END of the frame. If END character appears inside the packet, it is escaped with the 2-bytes sequence ESC (0xDB) ESC_END (0xDC). Of course, even ESC character in the payload is escaped with the 2-bytes sequence ESC (0xDB) ESC_ESC (0xDD). To improve reliability, END character is transmitted as the first byte of the frame too (see RFC). In this way, the length of the packet is intrinsic in the frame transmission and there's no need to put this info inside the packet data.

The packet syntax is: DD SS dd ... dd CC where DD is the destination address, SS is the source address, dd is the variable-length field of data and CC is the checksum.

My implementation uses an array buf[] to store the packet, starting from buf[0]. The SLIP decoding is performed inside the ISR thanks to a simple state machine. If the first byte of the packet (destination address) matches the slave address, the remaining bytes of the packet are saved in the buffer, otherwise they are discarded and the buffer pointer reset.

When the END character is received during reception of a packet for us, the rx interrupt is disabled to let the CPU process it. After transmitting the answer, the interrupt is enabled again and buffer pointer reset.

I have just one problem with this implementation: there are some messages or destionation addresses (broadcast addresses) that doesn't want any answer from the slaves. The master could send a new packet just after a broadcast packet: there's no need to wait for some time if no answer will be received.

In this case my implementation doesn't work, because I stop receiving at the end of the first packet until the CPU will process it. There's a concrete risk some bytes of the second packet will be lost.

One solution is to delay the transmission of a packet after one packet that doesn't want an answer. I don't like this solution, the delay depends on the speed of the slaves (if they are supercomputers or smallish 8-bit microcontrollers).

So I'm thinking to change my implementation and move from a simple buffer/array to a more complex FIFO/array, without stopping anymore the rx interrupt at the end of a packet for us (a second packet could appear immediately after). However, this new approach brings other problems with it.

Now the packets are pushed in the FIFO one after the other, so I loose the separation between them (remember that SLIP decoding is performed in the ISR, because I have to check the destination address to discard packets that are for other slaves). In other words, I loose the packet length info. One solution is to add a new byte at the beginning of the packet that is the packet length in bytes. I don't like it, because I'm solving a problem related to the physical layer modifying the upper MAC layer. But it's just a philosophical argument.

With this FIFO/array implementation, a single packet could be stored in part at the end and in part at the beginning of the FIFO/array. So I need a new array where to copy all the bytes of the packet in order, starting from the first. The memory/RAM requirements increase respect the buffer/array implementation.

Other suggestions?

Reply to
pozz

If you have three slaves and one master.

Which of the three slaves should answer "bad CRC" ?

If you have a bad address but a good CRC who should answer ?

Any COMM error is just left on the floor, ignore it.

In the latter scenario, the

Reply to
hamilton

Unless you have a separate CRC for the header, responding to a message with overall CRC, could cause quite a lot of problems.

If the master and slave(s) are out of synch for some reason, having a timeout is a good thing, during the silent period every device will end into a known state.

Reply to
upsidedown

How about using a ring buffer to store the decoded packet data (what you are calling your FIFO) but have a separate FIFO to carry the details for each received message - i.e. at the end of your RX ISR you push the last byte of the message into the 'data' ring buffer and then push {pointer, length} onto the 'control' FIFO.

Of course, you will have to protect the ring buffer from overflow.

--------------------------------------- Posted through

formatting link

Reply to
srl100

Instead of inserting a length character in the buffer, you can build a FIFO of receive buffers, each with it's length and other info. If the processing is guaranteed to be shorter than the transmission time of the next message, a simple 'flip buffer' will work. Just have two receive buffers with an RxCount and 'ready' status. On reception of a complete and OK message, set the lenght, mark 'ready' and start filling the next buffer while your application processes the just received message.

--
Stef    (remove caps, dashes and .invalid from e-mail address to reply by mail) 

To generalize is to be an idiot. 
		-- William Blake
Reply to
Stef

What do you intend to use those broadcasts ?

Of course it is nice for clock synchronization, but what else in practice ?

For clock setting/synchronization, the master should cease communication for a while, in order to any buffered traffic has died out, then generate the time message from master clock. This message will then go immediately for transmission and can be handled immediately by the slaves at the frame (no messages in the FIFO), thus, there is a well defined time difference between the master and slave clocks and hence easily correctable.

Sending an unspecified broadcast messages back to back is a bad idea, since even a device with large buffers but slow responses might overflow. For this reason, silent gaps should be added around broadcast frames.

if you insist to use broadcasts with both fast and slow devices, instead of one broadcast address, Why not use several multicast addresses for slaves with small buffers, but fast processing time, one for small buffers with long processing time and one for huge buffers slow processing time etc.

Anyway, I do not see the point of using the same protocol for short messages with short response times and big computer with large transfer requirements on the same RS-485 bus. It might be a better idea to let the big slaves communicate directly over ethernet and install some eth/RS485 converters for the smaller devices.

Reply to
upsidedown

If the CRC is bad, you don't know the address is valid. If you don't know the address is valid, how do you know which unit should respond?

--
Grant Edwards               grant.b.edwards        Yow! Thousands of days of 
                                  at               civilians ... have produced 
                              gmail.com            a ... feeling for the 
                                                   aesthetic modules --
Reply to
Grant Edwards

I wrote code for a project (Traffic led signs) that used rs485, half duplex at

9600 bauds. The protocol had been designed years ago, used stx / etx frame delimiters and had a crc included to detect errors. It too was master slave and comms was only ever initiated by the master. It was fully interrupt driven with fifo buffers for both tx and rx. Each slave has a unique address, but there was also a broadcast facility, with no reply, for things like clear display, usually followed by round robin status enquiry to check all that all the slaves had received the broadcast.

The low level code was classic lower half interrupt handler, with upper half called from the application code and fifo buffers in between. For portability, the lower layers were designed to be transparent to data. That is, everything received went into the fifo and the protocol decode handled by the layers above. There is a module with a set of functions to control the uarts and another module to do protocol decode etc, which is just a simple state machine. The top level calls functions in the fifo module that search for frame delimiters and if found, the frame is handed over to the protocol handler.

There was no real time exec on this project, and the mainloop is a state machine that scans the two rs485 interfaces for traffic, then calls a command processor when a valid frame is received. While there is quite a bit of traffic on the interfaces, the frames are short and quite small buffers (512 bytes rx, less for tx) were found to be more than enough. There are up to 12 slave nodes in a system, so 11 out of 12 frames may be discarded by any slave, but the system had more than enough performance, even when output of display data was via software. Modern micros are very fast :-)...

Regards,

Chris

Reply to
chris

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.