[En-Nut-Discussion] AT91 RS485

Ulrich Prinz uprinz2 at netscape.net
Tue Sep 8 00:02:46 CEST 2009


Hi!

I may bore some of the experienced guys but let me get into some detail 
about USART handling especially with handshake signals.

Coleman Brumley schrieb:
> On a similar note, has anyone ever modified the Nut/OS USART driver to work
> with DMA?
> 
> Is this a possible solution for the USART issues folks are reporting?  I.e.
> would a USART driver in DMA mode work "faster"?
> 
> - Coleman
> 
I don't think, that the problem is the speed, it's more the timing. If 
you control a signal like RS485 DE/RE, then you have to take care, that 
the signal is not switched in the middle of a transmitted byte.

So AVRs and others give you two transmission interrupts. One is rised if 
the transmission data register is empty, but that register is only a 
buffer in front of the transmission register in the background. From the 
last one the bits are sent out. So we need a second signal to show us, 
when the last bit (Stop-Bit) has been sent out of the background 
register. Therefore we have the second interrupt, the tx-complete-interrupt.

So, whenever a byte needs to be transmitted the usart-tx routine needs 
to switch from receiving to sending of the RS485 driver. Then it throws 
bytes at the tx buffer registrer on every buffer register empty 
interrupt. As long es this is done fast enough, an because of the double 
buffering, a tx-complete-interrupt is never rising until no new bytes 
are put in the buffer. If then the txc-interrupt comes, it only switches 
the RS485 driver back to listen mode.

Now there are two traps:
If the chain feeding new bytes in is on low priority, the stream may 
break and a txc is rised cause tx-empty was not answered fast enough.

You can avoid:
Instead of having txc-interrupt constantly on enable it right after the 
last character of the queue had been put into the buffer. The USART then 
will finish the actual byte and continue with the last on from the buffer.

Second trap:
Some people do driver switching if the last byte had been sent out and 
the tx-empty interrupt is rised again, but no new bytes are in the 
queue. This doesn't work, as no one can know, if the last bit of the 
byte still in the transmission shift register has been shifted out.
Polling is a waste of time.

Another half trap is:
If you leave the tx-empty interrupt on, you'll get another interrupt if 
the last byte is put from the buffer to the shift register. So you need 
to detect that interrupt and handle it even you cannot do anything with it.

I am pretty sure that I saw at least one of these traps in the NutO/S 
USART system. But I cannot remember in which code ( AVR, ARM, Debug).
I will stumble upon that as soon, as I have to get RS422 and RS485 
working on my system until end of September.

Ok, to make it easy: Do the driver like this:
(Its pseudo code but I hope you get the idea)

SendFunction:
- sets direction of (RS4xx) driver
- puts Character to Queue
- enables TX-Empty-Interrupt (should rise directly as UDR is empty)

SendInterrupt:
- fetches character from queue
- if queue is empty then switch off TX-Empty-Irq and switch on 
TX-Complete-Interrupt

TxCompleteInterrupt:
- switch direction of driver
- disable TX-Complete-Interrupt

Thats for all systems using GPIO for control of serial driver chips.

------------------------
More intelligent USARTs...
There is another thing about those chips, having hardware support for 
external drivers. Here only a modified setup of the USART part of the 
chip is needed. TxComplete-Interrupt handling is not needed in that 
case. (It could be interesting for special bus systems requiring certain 
timings though)

------------------------
HALF-DUPLEX
That's the thing what I implemented earlier for the at91. I found some 
of the things mentioned above where messed up in the half duplex option 
of the at91. I didn't check for AVR. Especially one thing was a problem 
for me, the half duplex was mixed with the hardware handshake. The 
original implementor might have thought that only someone who uses RS485 
needs half duplex. I need half duplex for a flawless one-wire interface 
so I had to split of hardware handshake from half duplex.

Normally you'd just send without any handshake signal. That's fine, but 
you get the echo as on a one-wire bus TX and RX are coupled. That is a 
fine thing, if you want to check, that your bus is not broken. But it 
needs buffers for these echos and code to decide if there is something 
echoed or if there is a message from another bus client. And a broken 
bus can be detected by a simple timeout function.
So you'd probably like to switch the echo off.

If you followed my ideas above, you see that half duplex is exactly the 
same thing like direction switching of an RS485 with out the switching. 
Instead of toggling the GPIO you simply toggle the TXE and RXE control 
bits of the USART. On sending out the first byte, you switch off the RX 
part of the USART. After sending out the last byte you enable 
TX-Complete Interrupt and that interrupt enables RX again.

-----------------------
And now there is a thing called DMA...
This is not available for ATmega but it will be for ATxmega and it is 
available for other ARM based CPUs. But you have to calculate carefully.
The setup of an DMA transfer needs some code and time. I didn't do DMA 
with ARM but with TI-DSP. As far as I remember you need to setup at 
least six registers before the stream starts flowing.
Source Address, Destination Address, Source Incrementation Type, Target 
Incrementation Type, Length and some other things.

That is a lot of code for often pushing out only one character over an 
USART.
It might make sense if uart related functions can early announce their 
final data length. I am not sure if puc can detect how much data the 
above printf will pass on. So you might have to implement a full new 
standard library function set including DMA handling.

If you have your own protocol running over an USART, then it is 
something else. But then there is mostly no way to implement that in a 
way that can be used by anybody. In that case, I would appreciate if 
someone writes an example and passes it over to the wiki or as code into 
the app/ tree.

DMA is interesting if long data fields have to be transmittet over and 
over to another place. So if you wrote a display driver with a frame 
buffer in RAM, it really makes sense to refresh the screen by using DMA 
as this will not block the system every few milliseconds.

I know Harald is working on a new code for RS485 handling as he reverted 
my addition for the no-GPIO half duplex as it is thought to be in the 
normal half-duplex code. There are some other bugs in the USART code 
mentioned in the mailing list so we see what he/we find.

Sorry for the long description, hope someone can use it :)

Best regards
Ulrich



More information about the En-Nut-Discussion mailing list