EmbeddedRelated.com
Forums

Two-wires RS485 and the driver enable problem

Started by pozzugno October 13, 2014
On 10/13/2014 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.]
If you can't use hardware to control the driver enable signal, then perhaps you can use hardware to control your time out. Rather than worry about software reading timers and messing with your interrupt priorities, etc... use the UART as a timer to delay the first enabled character being transmitted. Before a device transmits on the bus, send a character to the UART without enabling the RS-485 driver. Wait for the transmitter shift register to be empty ("transmit complete" by your terminology). Then enable the bus driver and start sending the packet data. This will provide 1 character transmission time delay. You still need to calculate the worst case delay needed. If it turns out to be longer than 1 character transmission time then you may need to pad with more than 1 dummy character. -- Rick
Il 13/10/2014 15:28, Wouter van Ooijen ha scritto:
>> 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.
On 8-bit microcontrollers, it's difficult to have a 32 bit counter :-)
>> 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 )
Again, you are supposing you have a 32-bits hardware counter available.
> This makes the reasonable assumption that only one carry from low to > high can occur during the execution of this code fragment.
Even if I don't have 32-bits hardware counters, thank you for sharing this approach.
On 10/13/2014 7:46 AM, Wouter van Ooijen wrote:
> Don Y schreef op 13-Oct-14 4:21 PM:
>> 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.
Correct. Disabling interrupts *forces* an atomic operation -- when there often isn't the need for one. E.g., in this case, you are trying to emulate two *concurrent* reads (Hi,Lo). As long as the result that you get "makes sense" given the approach you've taken for implementation, it's (almost) as good as any other! [E.g., (X,99) and (Y,00) are different from each other, but more correct than, for example, (Y,17)]
Il 13/10/2014 16:53, rickman ha scritto:
> Before a device transmits on the bus, send a character to the UART > without enabling the RS-485 driver. Wait for the transmitter shift > register to be empty ("transmit complete" by your terminology). Then > enable the bus driver and start sending the packet data. This will > provide 1 character transmission time delay.
Good suggestion to avoid using another hardware peripheral for implementing the delay. Thank you.
On 10/13/2014 5:56 AM, pozzugno wrote:
> Il 13/10/2014 14:47, David Brown ha scritto: >> 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
No, what you are most concerned with is ensuring every ISR manages to terminate before it can be reinvoked. So, ISR1 can be interrupted by ISR3 which can be interrupted by ISR7 which can be interrupted by ISR2, etc. AS LONG AS ISR1 can't reassert itself while ANY instance of ISR1 is still "active". (Ditto for every ISR in the system) Even this "rule" can be bent -- if you know the worst case nesting of ISR's on themselves (and ensure you have adequate stack to cover that level of penetration)
On 10/13/2014 7:59 AM, pozzugno wrote:
> Il 13/10/2014 15:28, Wouter van Ooijen ha scritto: >>> 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. > > On 8-bit microcontrollers, it's difficult to have a 32 bit counter :-)
All you need is a wide enough counter to span the maximum delay you want to measure "comfortably". You need to design such that your "most sluggish" activity happens often enough to be captured in one counter rollover period (i.e., counter can't roll over more than once between observations)
On 10/13/2014 4:58 AM, pozzugno wrote:

[bus acquisition in the face of possible contention]

> 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. > > How do you solve this problem?
First, you need to *know* that it is *you* that has been granted access to the bus/resource. If this requires you to perform some analysis of the ENTIRE MESSAGE (to verify that the "address field" is, in fact, intact!), then you probably DON'T want to try to ride the coat-tails of the message, directly. As the front end of a message (including yours) is more likely to be corrupted (by a collision on the bus -- someone jabbering too long or too soon), you might consider designing a packet format that has one checksum on the address field "early" in the packet (before the payload) and another that handles the balance of the message. This allows you to capture the address information and it's closely following checksum, verify that it is *you* that are being addressed and prepare for your acquisition of the bus before the message has completely arrived. You can also arrange to access the bus in "timeslots" referenced to some easily recognizable event (e.g., a beacon sent by the master). So, you do all of your timing off of that single event (see "some special point" in the beacon, start timer, wait to transmit until "your slot"). Note this also works if you just assume the "next slot" after the master's message is the slot you should use (you just limit the length of the master's message so it fits in that fixed delay). Similarly, the master can "know" that your reply will never exceed a fixed duration so it won't issue another beacon/request until that has expired. Hopefully, this makes sense. Sorry, I'm off for a pro bono day so no time here... :-/
On 10/13/2014 8:17 AM, Don Y wrote:

> As the front end of a message (including yours) is more likely to be corrupted > (by a collision on the bus -- someone jabbering too long or too soon), you > might consider designing a packet format that has one checksum on the > address field "early" in the packet (before the payload) and another that > handles the balance of the message. > > This allows you to capture the address information and it's closely following > checksum, verify that it is *you* that are being addressed and prepare for your > acquisition of the bus before the message has completely arrived.
... so that you don't erroneously acquire the bus only to discover that it was intended for someone else (and you've just made things worse)
On 2014-10-13, pozzugno <pozzugno@gmail.com> wrote:

> 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.
Or put a some disposable preamble bytes on the front of messages. This is pretty common when using half-duplex modems: the padding bytes give the modems time to "turn-around" and get carrier established, PLLs locked, etc. -- Grant Edwards grant.b.edwards Yow! Here I am in the at POSTERIOR OLFACTORY LOBULE gmail.com but I don't see CARL SAGAN anywhere!!
pozzugno schreef op 13-Oct-14 4:59 PM:

> Il 13/10/2014 15:28, Wouter van Ooijen ha scritto: >>> 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. > > On 8-bit microcontrollers, it's difficult to have a 32 bit counter :-)
You can use the same technique, but with a 16-bit counter and some more bytes updated by the overflow ISR. The 8-bittters I know have 16-bit counters with a mechanism to do an atomic 16-bit read. The total number of bits you need depends on the resolution and the required run time of the application. But at least on the 8-bitters I know (PIC 12 and 14 bit cores) instruction timing is totally predictable, so it is easy to make a busy delay subroutine that is accurate. Wouter van Ooijen