EmbeddedRelated.com
Forums
The 2024 Embedded Online Conference

Two-wires RS485 and the driver enable problem

Started by pozzugno October 13, 2014
I have a multi-drop two-wires RS485 bus.  One node is the master and all 
the others are slaves.  The master is the only node that is authorized 
to initiate a transmission, addressing one slave.  The addressed slave 
usually answers to the master.

The bus is half-duplex, so every node disables the driver.  Only THE 
node that transmits data on the bus enables the driver and disables it 
as soon as it can, just after the last byte.  An interrupt (transmit 
complete) usually triggers when the last byte is totally shifted out, so 
the driver can be disabled immediately.

Of course, other interrupts can be triggered.  What happens when 
interrupt X (whatever) triggers just before the "transmit complete" 
interrupt?  The result is the ISR X is called, postponing the execution 
of "transmit complete" ISR.  The RS485 driver will be disabled with a 
certain amount of delay.  In the worst case, the driver could be 
disabled with a delay that is the sum of the duration of all ISRs that 
could trigger.
[In this scenario, I think of ISRs that can't be interrupted by a higher 
priority interrupt.]

If a node on the bus is very fast and starts transmitting (the master) 
or answering (one slave) immediately after receving the last byte, but 
when the previous transmitting node is executing other ISRs, the final 
result is a corrupted transmission.

What is the solution? I think the only solution is to define, at the 
design time, a minimum interval between the receiving of the last byte 
from one node and the transmission of the first byte.  This interval 
could be in the range of 100 microseconds and should be calibrated on 
the sum of duration of *all* ISRs of *all* nodes on the bus.  It isn't a 
simple calculation.

Moreover, implementing a short "software" delay in the range of some 
microseconds isn't a simple task.  An empty loop on a decreasing 
volatile variable is a solution, but the final delay isn't simple to 
calculate at the design time, and it could depend on the compiler, 
compiler settings, clock frequency and so on.  Use an hw timer only for 
this pause?

How do you solve this problem?

[I know there are some microcontrollers that automatically (at the 
hw-level) toggle an output pin when the last byte is totally shifted 
out, but I'm not using one of the them and they aren't so common.]
pozzugno schreef op 13-Oct-14 1:58 PM:
> I have a multi-drop two-wires RS485 bus. One node is the master and all > the others are slaves. The master is the only node that is authorized > to initiate a transmission, addressing one slave. The addressed slave > usually answers to the master. > > The bus is half-duplex, so every node disables the driver. Only THE > node that transmits data on the bus enables the driver and disables it > as soon as it can, just after the last byte. An interrupt (transmit > complete) usually triggers when the last byte is totally shifted out, so > the driver can be disabled immediately. > > Of course, other interrupts can be triggered. What happens when > interrupt X (whatever) triggers just before the "transmit complete" > interrupt? The result is the ISR X is called, postponing the execution > of "transmit complete" ISR. The RS485 driver will be disabled with a > certain amount of delay. In the worst case, the driver could be > disabled with a delay that is the sum of the duration of all ISRs that > could trigger. > [In this scenario, I think of ISRs that can't be interrupted by a higher > priority interrupt.] > > If a node on the bus is very fast and starts transmitting (the master) > or answering (one slave) immediately after receving the last byte, but > when the previous transmitting node is executing other ISRs, the final > result is a corrupted transmission. > > What is the solution? I think the only solution is to define, at the > design time, a minimum interval between the receiving of the last byte > from one node and the transmission of the first byte. This interval > could be in the range of 100 microseconds and should be calibrated on > the sum of duration of *all* ISRs of *all* nodes on the bus. It isn't a > simple calculation. > > Moreover, implementing a short "software" delay in the range of some > microseconds isn't a simple task. An empty loop on a decreasing > volatile variable is a solution, but the final delay isn't simple to > calculate at the design time, and it could depend on the compiler, > compiler settings, clock frequency and so on. Use an hw timer only for > this pause? > > How do you solve this problem? > > [I know there are some microcontrollers that automatically (at the > hw-level) toggle an output pin when the last byte is totally shifted > out, but I'm not using one of the them and they aren't so common.]
The minimum delay that you calculated is just that: a minimum. Apart from performance, there no problem in waiting somewhat longer. IMO you must come up with two figures: the minimum respond time (determined by the maximum driver turn-off delay) and the maximum respond time (determined by the time you can afford to loose with the bus idling bewteen request and response). As for the delay: My experience is that I need delays all over the place. One approach is to have a free-running 64 bit counter (which will roll over long after you are dead) and wait for it to exceed start+delay. Wouter
Il 13/10/2014 14:29, Wouter van Ooijen ha scritto:
> The minimum delay that you calculated is just that: a minimum. Apart > from performance, there no problem in waiting somewhat longer. IMO you > must come up with two figures: the minimum respond time (determined by > the maximum driver turn-off delay) and the maximum respond time > (determined by the time you can afford to loose with the bus idling > bewteen request and response).
Ok, so you're confirming the solution is a delay.
> As for the delay: My experience is that I need delays all over the > place. One approach is to have a free-running 64 bit counter (which will > roll over long after you are dead) and wait for it to exceed start+delay.
I usually use this approach for longer delays (milliseconds or seconds), so I can increment the counter in a ISR that trigger every millisecond. I don't like to fire a trigger every 100us. 64-bit appears to me too wide. Everytime I need to read it and compare with the time, I have to disable interrupts.
On 13/10/14 13:58, pozzugno wrote:
> I have a multi-drop two-wires RS485 bus. One node is the master and all > the others are slaves. The master is the only node that is authorized > to initiate a transmission, addressing one slave. The addressed slave > usually answers to the master. > > The bus is half-duplex, so every node disables the driver. Only THE > node that transmits data on the bus enables the driver and disables it > as soon as it can, just after the last byte. An interrupt (transmit > complete) usually triggers when the last byte is totally shifted out, so > the driver can be disabled immediately. > > Of course, other interrupts can be triggered. What happens when > interrupt X (whatever) triggers just before the "transmit complete" > interrupt? The result is the ISR X is called, postponing the execution > of "transmit complete" ISR. The RS485 driver will be disabled with a > certain amount of delay. In the worst case, the driver could be > disabled with a delay that is the sum of the duration of all ISRs that > could trigger. > [In this scenario, I think of ISRs that can't be interrupted by a higher > priority interrupt.] > > If a node on the bus is very fast and starts transmitting (the master) > or answering (one slave) immediately after receving the last byte, but > when the previous transmitting node is executing other ISRs, the final > result is a corrupted transmission. > > What is the solution? I think the only solution is to define, at the > design time, a minimum interval between the receiving of the last byte > from one node and the transmission of the first byte. This interval > could be in the range of 100 microseconds and should be calibrated on > the sum of duration of *all* ISRs of *all* nodes on the bus. It isn't a > simple calculation. > > Moreover, implementing a short "software" delay in the range of some > microseconds isn't a simple task. An empty loop on a decreasing > volatile variable is a solution, but the final delay isn't simple to > calculate at the design time, and it could depend on the compiler, > compiler settings, clock frequency and so on. Use an hw timer only for > this pause? > > How do you solve this problem? > > [I know there are some microcontrollers that automatically (at the > hw-level) toggle an output pin when the last byte is totally shifted > out, but I'm not using one of the them and they aren't so common.]
You don't have to use the sum of all ISR's on all nodes as your minimum time. You have to use the largest value of delay that could occur on any one of the nodes, which is certainly not the same thing. If possible, you can make the "transmission complete" interrupt a high priority interrupt - if your microcontroller supports some sort of nested interrupt scheme, then this will greatly reduce the latency of the transmission complete interrupt and therefore the bus disable. But the key point is that interrupt functions, or other areas of code during which interrupts are disabled, should be as short, fast and deterministic as possible. Don't do any "work" during interrupt functions - or if you do, re-enable global interrupts first. Then it makes little difference if an interrupt function is running when the "transmission complete" triggers because the function will be complete in a few microseconds.
Il 13/10/2014 14:47, David Brown ha scritto:
> You don't have to use the sum of all ISR's on all nodes as your minimum > time. You have to use the largest value of delay that could occur on > any one of the nodes, which is certainly not the same thing.
Yes, I wanted to write the same thing, but my English isn't very good. I wanted to stress I have to *add all the ISRs execution time* that could trigger in a single slave and take the maximum value. One slave could use just a single interrupt, but another could use 10 interrupts (ADC, serial ports, I2C, timers, ...)
> If possible, you can make the "transmission complete" interrupt a high > priority interrupt - if your microcontroller supports some sort of > nested interrupt scheme, then this will greatly reduce the latency of > the transmission complete interrupt and therefore the bus disable.
I'm using AVR8 and SAMD20 from Atmel. In AVR8 there is a "hard wired" priority scheme that can't be changed and the TXC (transmit complete interrupt) priority is very low. Anyway an ISR can't be never interrupted, even by a higher priority interrupt event (the priority is used only if there are more than one interrupt requests at the same time).
> But the key point is that interrupt functions, or other areas of code > during which interrupts are disabled, should be as short, fast and > deterministic as possible. Don't do any "work" during interrupt > functions -
I know, I know.
> or if you do, re-enable global interrupts first. Then it > makes little difference if an interrupt function is running when the > "transmission complete" triggers because the function will be complete > in a few microseconds.
Someone considers the practice to enable interrupts inside an ISR as The Devil :-) http://betterembsw.blogspot.it/2014/01/do-not-re-enable-interrupts-in-isr.html
On 10/13/14, 7:58 AM, pozzugno wrote:
> I have a multi-drop two-wires RS485 bus. One node is the master and all > the others are slaves. The master is the only node that is authorized > to initiate a transmission, addressing one slave. The addressed slave > usually answers to the master. > > The bus is half-duplex, so every node disables the driver. Only THE > node that transmits data on the bus enables the driver and disables it > as soon as it can, just after the last byte. An interrupt (transmit > complete) usually triggers when the last byte is totally shifted out, so > the driver can be disabled immediately. > > Of course, other interrupts can be triggered. What happens when > interrupt X (whatever) triggers just before the "transmit complete" > interrupt? The result is the ISR X is called, postponing the execution > of "transmit complete" ISR. The RS485 driver will be disabled with a > certain amount of delay. In the worst case, the driver could be > disabled with a delay that is the sum of the duration of all ISRs that > could trigger. > [In this scenario, I think of ISRs that can't be interrupted by a higher > priority interrupt.] > > If a node on the bus is very fast and starts transmitting (the master) > or answering (one slave) immediately after receving the last byte, but > when the previous transmitting node is executing other ISRs, the final > result is a corrupted transmission. > > What is the solution? I think the only solution is to define, at the > design time, a minimum interval between the receiving of the last byte > from one node and the transmission of the first byte. This interval > could be in the range of 100 microseconds and should be calibrated on > the sum of duration of *all* ISRs of *all* nodes on the bus. It isn't a > simple calculation. > > Moreover, implementing a short "software" delay in the range of some > microseconds isn't a simple task. An empty loop on a decreasing > volatile variable is a solution, but the final delay isn't simple to > calculate at the design time, and it could depend on the compiler, > compiler settings, clock frequency and so on. Use an hw timer only for > this pause? > > How do you solve this problem? > > [I know there are some microcontrollers that automatically (at the > hw-level) toggle an output pin when the last byte is totally shifted > out, but I'm not using one of the them and they aren't so common.]
Yes, the best solution is to make it happen in hardware, but I understand it isn't always available. As you commented, the key is to define the maximum period after transmission is complete that a unit is allowed to drive the line, and thus the minimum time another unit must wait to respond. By defining a maximum time that a unit can drive the bus, you have now introduced a "Hard Real Time" requirement to the system. That normally means that you need to be careful what is done in interrupt routines, so no interrupt can mask off other interrupts for an extended time. A second question is how much data is being sent and how fast do responses need to be. It may be possible to define the minimum reply delay long enough that you don't really need to worry about "small" delays in the transmitter. A third question is can the protocol be made "fail-safe" so that if on an occasional unlucky combination of delays, and the beginning of the response is garbled by a slow disable, that the system can detect this and cause a retransmission (This is often good to handle other types of errors too), or otherwise tolerate an occasional missing response. In this case you just need to make sure that the responding unit takes long enough to build its response (maybe with an added delay to make sure) for the transmitter to normally get off the bus. Unless you have vastly differing speed of units, this often will just happen to be true as the receiving unit, especially if set to verify the "stop bit", won't begin to parse the message which is normally a longer task than the sending units "shut off the driver" operation.
pozzugno schreef op 13-Oct-14 2:38 PM:
> Il 13/10/2014 14:29, Wouter van Ooijen ha scritto: >> The minimum delay that you calculated is just that: a minimum. Apart >> from performance, there no problem in waiting somewhat longer. IMO you >> must come up with two figures: the minimum respond time (determined by >> the maximum driver turn-off delay) and the maximum respond time >> (determined by the time you can afford to loose with the bus idling >> bewteen request and response). > > Ok, so you're confirming the solution is a delay. > > >> As for the delay: My experience is that I need delays all over the >> place. One approach is to have a free-running 64 bit counter (which will >> roll over long after you are dead) and wait for it to exceed start+delay. > > I usually use this approach for longer delays (milliseconds or seconds), > so I can increment the counter in a ISR that trigger every millisecond. > I don't like to fire a trigger every 100us.
With a 64 bit counter that ticks at 1 ns you have a rollover after 585 years, so you can use it for all delays. If you don't have a hardware 64 bit counter you can use a 32 bit counter + rollover interrupt.
> 64-bit appears to me too wide. Everytime I need to read it and compare > with the time, I have to disable interrupts.
If you don't want to disable interrupts: h1 = read_high(); l1 = read_low(); h2 = read_high(); l2 = read_low(); if h1 == h2 return ( h1, l1 ) else return ( h2, l2 ) This makes the reasonable assumption that only one carry from low to high can occur during the execution of this code fragment. Wouter van Ooijen
On 13/10/14 14:56, pozzugno wrote:
> Il 13/10/2014 14:47, David Brown ha scritto: >> You don't have to use the sum of all ISR's on all nodes as your minimum >> time. You have to use the largest value of delay that could occur on >> any one of the nodes, which is certainly not the same thing. > > Yes, I wanted to write the same thing, but my English isn't very good. > > I wanted to stress I have to *add all the ISRs execution time* that > could trigger in a single slave and take the maximum value. > One slave could use just a single interrupt, but another could use 10 > interrupts (ADC, serial ports, I2C, timers, ...)
No, you only have to include the ISR's that could occur during that time - not all the ISR's in your system. That means you either have to know which ones might occur (for example, you may be confident that I2C interrupts could not happen at that point, and therefore discount them), or you may perhaps choose to disable some of them temporarily on the transmission of the last character, and re-enable them within the transmission complete ISR. You also have to consider things from a point of view of the chance of something going wrong, and the consequences of that error. In most communication systems, you already have a method of checking telegram correctness (CRC, checksum, etc.) and of re-trying in the case of error. Suppose - through calculation or testing - you figure out that a latency of more than 20 us occurs one telegram out of 10000. Then if your bus-turnaround delay is set to 20 us, then one out of every 10000 replies could be lost or corrupted. If that is within the level of acceptable error rates, a 20 us delay is good enough. (Note that you will not damage your drivers by having two drivers enabled at the same time - decent RS-485 drivers have protection against that.) Of course, it is highly application-dependent whether such a statistical test is good enough - it is likely to be fine for a temperature measurement system, but frowned upon for a car brake control.
> >> If possible, you can make the "transmission complete" interrupt a high >> priority interrupt - if your microcontroller supports some sort of >> nested interrupt scheme, then this will greatly reduce the latency of >> the transmission complete interrupt and therefore the bus disable. > > I'm using AVR8 and SAMD20 from Atmel. In AVR8 there is a "hard wired" > priority scheme that can't be changed and the TXC (transmit complete > interrupt) priority is very low. Anyway an ISR can't be never > interrupted, even by a higher priority interrupt event (the priority is > used only if there are more than one interrupt requests at the same time). >
The "priorities" here are of minor relevance, since they only affect which interrupt vector is used on simultaneous interrupts. The point is that you don't have a microcontroller that supports nested interrupts (other than by simply re-enabling global interrupts).
> >> But the key point is that interrupt functions, or other areas of code >> during which interrupts are disabled, should be as short, fast and >> deterministic as possible. Don't do any "work" during interrupt >> functions - > > I know, I know.
Apply that principle seriously, and you should not have a problem here. Also avoid using interrupts unnecessarily. It is often tempting to have lots of interrupts on SPI, I2C, ADC, etc., when they are actually unnecessary - and may be slower than a simple polling loop.
> > >> or if you do, re-enable global interrupts first. Then it >> makes little difference if an interrupt function is running when the >> "transmission complete" triggers because the function will be complete >> in a few microseconds. > > Someone considers the practice to enable interrupts inside an ISR as The > Devil :-) > > http://betterembsw.blogspot.it/2014/01/do-not-re-enable-interrupts-in-isr.html >
People always have opinions and their own ideas of general rules. In embedded development, general rules are often wrong - there are always exceptions. You do have to be careful about re-enabling interrupts inside an ISR, however. A typical usage is for a long-running ISR on a timer interrupt. First, acknowledge the timer interrupt. Then disable the timer interrupt to avoid recursion, then re-enable global interrupts. Reverse the process at the end of the timer ISR. But be even more careful than usual about shared data and race conditions!
On 10/13/2014 6:28 AM, Wouter van Ooijen wrote:
> If you don't want to disable interrupts: > > h1 = read_high(); > l1 = read_low(); > h2 = read_high(); > l2 = read_low(); > if h1 == h2 return ( h1, l1 ) else return ( h2, l2 ) > > This makes the reasonable assumption that only one carry from low to high can > occur during the execution of this code fragment.
You can skip the second read of the "low"; if the highs differ, you can assume the low takes on the value *at* rollover (depends on down count vs. up count). E.g., if counter increments (decimal): X 90 X 91 <--- read high (=X) X 92 X 93 X ... X 99 Y 00 Y 01 <--- read high (=Y) So, you can assume {Y,00} is a safe value for the counter regardless of whether you saw {91,92,93...00,01} as the low observation. [If the counter is built of two concatenated counters, then you need to use the modulus of the lower one as the assumed "wrap value", obviously] If both read high's yield X, then you can use the observed read low. Reading the low byte a second time doesn't improve your result: X 90 X 91 <--- read high (=X) X 92 X 93 read low first time somewhere X ... in this area X 99 Y 00 Y 01 <--- read high (=Y) Y 02 Y 03 perhaps ISR occurs here? Y ... Y 10 Y 11 <--- read low (second time) Note that an "unknown" delay (ISR) can occur between any two of these reads. So, the two high reads can be very closely spaced together, in time (which means the first low is tightly bracketed) -- then, the second low could be deferred a long time wrt the second high. As such, using *it* in the final result adds more slop to the reading. [This assumes that the "event" that you are interested in tagging has *triggered* this action -- so, you want the earliest time associated with it]
Don Y schreef op 13-Oct-14 4:21 PM:
> On 10/13/2014 6:28 AM, Wouter van Ooijen wrote: >> If you don't want to disable interrupts: >> >> h1 = read_high(); >> l1 = read_low(); >> h2 = read_high(); >> l2 = read_low(); >> if h1 == h2 return ( h1, l1 ) else return ( h2, l2 ) >> >> This makes the reasonable assumption that only one carry from low to >> high can >> occur during the execution of this code fragment. > > You can skip the second read of the "low"; if the highs differ, you can > assume > the low takes on the value *at* rollover (depends on down count vs. up > count).
Correct. But that requires a little more knowledge of the hardware. Which you will need anyway if it is about up versus down counting. But the main point was that you don't have to disable interrupts if you don't want to. An IIRC disabling interrupts doesn't help much. Wouter

The 2024 Embedded Online Conference