EmbeddedRelated.com
Forums
Memfault Beyond the Launch

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

Started by pozz May 28, 2013
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?
On 28.5.13 5:19 , pozz wrote:
> 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?
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
On Tue, 28 May 2013 16:19:04 +0200, pozz wrote:

> 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?
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
On Tue, 28 May 2013 16:19:04 +0200, pozz <pozzugno@gmail.com> wrote:

>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.
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.
Il 29/05/2013 07:50, upsidedown@downunder.com ha scritto:
> On Tue, 28 May 2013 16:19:04 +0200, pozz <pozzugno@gmail.com> wrote: > >> 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. > > 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.
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?
On Wed, 29 May 2013 08:32:07 +0200, pozz <pozzugno@gmail.com> wrote:

> >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?
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.
On Wednesday 29 May 2013 08:32 pozz wrote:

> Il 29/05/2013 07:50, upsidedown@downunder.com ha scritto: >> On Tue, 28 May 2013 16:19:04 +0200, pozz <pozzugno@gmail.com> wrote: >> >>> 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. >> >> 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. > > 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?
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
On 5/29/2013 12:32 AM, pozz wrote:
> Il 29/05/2013 07:50, upsidedown@downunder.com ha scritto: >> On Tue, 28 May 2013 16:19:04 +0200, pozz <pozzugno@gmail.com> wrote: >> >>> 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. >> >> 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. > > 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?
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 ?
> > Should I add a second CRC just for the header that contains address info? >
Il 29/05/2013 09:02, upsidedown@downunder.com ha scritto:
 >
 > [...]
 >
> 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.
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).
> 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.
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?
On 5/30/2013 3:59 PM, pozz wrote:
> Il 29/05/2013 09:02, upsidedown@downunder.com ha scritto: > > > > [...] > > >> 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. > > 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?
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
> 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). > > >> 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. > > 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?

Memfault Beyond the Launch