I try to run a small RS485 network off the serial port of a PC. The PC polls
some gadgets with microcontrollers in them.
The PC is coupled via a RS232-RS485 converter which uses DTR to
switch between transmit and receive. (I use the half-duplex approach with
only one pair of wires.)
So, to send some data, I turn DTR on, write() the bytes and turn DTR off
again after the last byte fell out of the UART.
Now waiting for end of transmission to turn off DTR is my problem.
If I use tcdrain() to wait, it takes too long, since the kernel doesn't
return quick enough to my process for it to turn DTR off. This means I
lose the response from the device which received the data, as it answers
really quick.
The alternative is a busy loop polling with TIOCSERGETLSR, waiting for
end of transmission. This works, but I don't like it, since it hogs the
CPU, uses tons of system calls and feels very ugly.
Imagine what happens if the scheduler decides to schedule the process away
right after write() but before turning DTR off. Kaboom, the bus stays locked.
This is the code snipplet for that approach:
{
int arg;
/* DTR on */
ioctl(com, TIOCMGET, &arg);
arg|=TIOCM_DTR;
ioctl(com, TIOCMSET, &arg);
/* Send data */
if (write(com, buf, bp)!=bp) return -1;
/* Wait for transmitter to finish -- UGLY! */
do {
ioctl(com, TIOCSERGETLSR, &arg);
} while((arg & TIOCSER_TEMT)==0);
/* DTR off */
ioctl(com, TIOCMGET, &arg);
arg&=~TIOCM_DTR;
ioctl(com, TIOCMSET, &arg);
}
Before I go and hack up the serial driver to include automatic DTR toggling,
I wonder what other people might think.
Any hints greatly appreciated!
Thanks,
--
Olav "Mac" Wölfelschneider wo...@cardware.de
PGP fingerprint = 06 5F 66 B3 2A AD 7D 2D B7 19 67 3C 95 A7 9D AF
Mer muß doch nur emol e bissje nochdenke. -- Mundstuhl
>So, to send some data, I turn DTR on, write() the bytes and turn DTR off
>again after the last byte fell out of the UART.
>
>Now waiting for end of transmission to turn off DTR is my problem.
More so than you have realized yet. I'm not familiar with the Linux
implementation, but most serial drivers consider the buffer flushed,
and done as soon as the last byte to be transmitted as been sent to
the uart. The problem is, it takes about 1mS (9600 baud) for the uart
to finish transmitting the character. If your using a buffered uart,
the problem gets much worse!
If you manage to get a fast DTR shut off routine working, you WILL cut
off the last few bits of your last character. Been there, done that.
The easiest solution is to not control the 485 transmitter in
software. Use a one shot circuit in the RS-232 to 485 converter to do
the dirty work for you.
B&B has just such an animal, and it works great! We use about 20-30
485TBLED units per year. DB25 for RS-232 side, Screw terminals for the
485 side.
Check out: http://www.bb-elec.com/
--
+------------------------+---------------------+--------------------+
| Matthew T. Linehan | AKA -=(UDIC)=- | All Round Computer |
| American LED-gible Inc | Quantum Void Dragon | Nerd. Hardware, |
| Engineer | Ultima Fanatic | Software, Other :-)|
+------------------------+---------------------+--------------------+
Olav Woelfelschneider wrote:
> [...] So, to send some data, I turn DTR on, write() the bytes and turn DTR off
> again after the last byte fell out of the UART.
>
> Now waiting for end of transmission to turn off DTR is my problem.
>
> If I use tcdrain() to wait, it takes too long, since the kernel doesn't
> return quick enough to my process for it to turn DTR off. This means I
> lose the response from the device which received the data, as it answers
> really quick.
>
> The alternative is a busy loop polling with TIOCSERGETLSR, waiting for
> end of transmission. This works, but I don't like it, since it hogs the
> CPU, uses tons of system calls and feels very ugly. [...]
The main problem here is hardware: none of the 16450, 16550 and
other UART give the information "char completely sent", they just
give the information "Tx serialisator empty"... a small difference: the
stop and parity bits. Linux could calculate when the char will go
out of the chip to switch a pin (quite a complex calculus) but after
some experiment you will see that each manufacturer has implemented
the "Tx serialisator empty" bit his own way and the only way to
get reliable result is to wait "enough" before returning to tcdrain().
I do not think there is a software solution to your problem...
Etienne.
> > The main problem here is hardware: none of the 16450, 16550 and
> > other UART give the information "char completely sent", they just
> > give the information "Tx serialisator empty"...
I had a very similar problem in "2-wire" RS-485
comms, where each Tx/Rx only gets switched into
Tx when required, and *must* drop back into an
Rx'er asap. 5-byte packets into an 8-byte fifo.
With a receiver hanging on the line you simply
receive your own outgoing bytes, and as soon as
the last byte ends, smack it back into Rx, and
flush buffs. You don't actually have to read your
own bytes, just count the ints from your own uart,
each int being a signal that a full byte has gone.
--
Tony Williams.
Well there is a software solution. Since you are using halduplex
protocol I persume that transmit is coupled with receive through the
rs485. When you get an receive interrupt from the last character you can
savely turn the transmitter of :-).
So no extra hardware is necessary
--
Met vriendelijke groet,
Gerard van der Sel
Mailto:gvand...@hr.nl
"De dinosaurussen hadden hun komeet, wij hebben de Windows computer" -
me
"The box said: 'install on Windows 95, NT 4.0 or better'.
So I installed it on Linux."
I've never used them, just seen them advertised. Under $100.
Ralph
In article <9msdd7...@hex.athome.de>, Olav Woelfelschneider <wo...@cardware.de> says:
>
>Keywords: RS485-Network, Linux
>
>I try to run a small RS485 network off the serial port of a PC. The PC polls
>some gadgets with microcontrollers in them.
>
>The PC is coupled via a RS232-RS485 converter which uses DTR to
>switch between transmit and receive. (I use the half-duplex approach with
>only one pair of wires.)
>
>So, to send some data, I turn DTR on, write() the bytes and turn DTR off
>again after the last byte fell out of the UART.
>
<snip>
>This is the code snipplet for that approach:
>
> {
> int arg;
>
> /* DTR on */
> ioctl(com, TIOCMGET, &arg);
> arg|=TIOCM_DTR;
> ioctl(com, TIOCMSET, &arg);
>
> /* Send data */
> if (write(com, buf, bp)!=bp) return -1;
>
> /* Wait for transmitter to finish -- UGLY! */
> do {
> ioctl(com, TIOCSERGETLSR, &arg);
> } while((arg & TIOCSER_TEMT)==0);
>
> /* DTR off */
> ioctl(com, TIOCMGET, &arg);
> arg&=~TIOCM_DTR;
> ioctl(com, TIOCMSET, &arg);
> }
Pls, try so...
{
int arg;
//
// m.b. off Tx FIFO? I do it for NT4, in Linux I'm nothing, yet.
// And do not know as work write in last.
/* DTR on */
ioctl(com, TIOCMGET, &arg);
arg|=TIOCM_DTR;
ioctl(com, TIOCMSET, &arg);
/* Send data */
*(buf+bp)=0x00; // paddle fake byte: must be 0x00
if (write(com, buf, bp+1)!=(bp+1)) return -1;
/* Wait for transmitter to finish -- UGLY! */
// do {
// ioctl(com, TIOCSERGETLSR, &arg);
// } while((arg & TIOCSER_TEMT)==0);
//
/* DTR off */
ioctl(com, TIOCMGET, &arg);
arg&=~TIOCM_DTR;
ioctl(com, TIOCMSET, &arg);
}
>Before I go and hack up the serial driver to include automatic DTR toggling,
>I wonder what other people might think.
>
>Any hints greatly appreciated!
>
>Thanks,
>--
>Olav "Mac" Wцlfelschneider wo...@cardware.de
>PGP fingerprint = 06 5F 66 B3 2A AD 7D 2D B7 19 67 3C 95 A7 9D AF
>Mer muЯ doch nur emol e bissje nochdenke. -- Mundstuhl
At7
Peter wrote in message <36fd8476....@news.netcomuk.co.uk>...
>They do give the "all sent" info but only as a status bit - you cannot
>program an interrupt from it.
>
>> The main problem here is hardware: none of the 16450, 16550 and
>> other UART give the information "char completely sent", they just
>> give the information "Tx serialisator empty"...
>
>
>--
>Peter.
>
>Return address is invalid to help stop junk mail.
>E-mail replies to zX...@digiYserve.com but remove the X and the Y.
>Please do NOT copy usenet posts to email - it is NOT necessary.
>The PC is coupled via a RS232-RS485 converter which uses DTR to
>switch between transmit and receive. (I use the half-duplex approach with
>only one pair of wires.)
I went through the same thought process and code experiments that you did,
and finally just bought a half-duplex RS232-RS485 converter that does the
transmit control automaticly in hardware.
two sources of these:
http://www.bb-elec.com/
http://www.rs485.com/
>Before I go and hack up the serial driver to include automatic DTR toggling,
>I wonder what other people might think.
I think that if you want it done right, it needs to go in the driver like
this.
--
--
Steve Tell | te...@cs.unc.edu | http://www.cs.unc.edu/~tell | KF4ZPF
Research Associate, Microelectronic Systems Laboratory
Computer Science Department, UNC@Chapel Hill. W:919-962-1845
MTL> If you manage to get a fast DTR shut off routine working, you WILL cut
MTL> off the last few bits of your last character. Been there, done that.
AOL, AOL! Umh, I meant, mee too. (-:
On some microcontrollers or UARTS you have no chance detecting when the
last byte fell out of the shift register. In that cases I had to send
a final dummy byte (0xFF) which was shut off right after the start bit.
I had to make sure the protocol doesn't get confused by this.
MTL> The easiest solution is to not control the 485 transmitter in
MTL> software. Use a one shot circuit in the RS-232 to 485 converter to do
MTL> the dirty work for you.
This is easy for constant speeds. If you want to build a universal device,
then you have either an ugly pot to adjust or a complex circuit which
analizes the data. These discussions pop up on c.a.e every now and then...
But I think I might just go that way. After all, the data rate will be
constant.
Greetings,
--
Olav "Mac" Wölfelschneider wo...@cardware.de
PGP fingerprint = 06 5F 66 B3 2A AD 7D 2D B7 19 67 3C 95 A7 9D AF
Mer muß doch nur emol e bissje nochdenke. -- Mundstuhl
> Keywords: RS485-Network, Linux
>
> I try to run a small RS485 network off the serial port of a PC. The PC polls
> some gadgets with microcontrollers in them.
>
> The PC is coupled via a RS232-RS485 converter which uses DTR to
> switch between transmit and receive. (I use the half-duplex approach with
> only one pair of wires.)
>
> So, to send some data, I turn DTR on, write() the bytes and turn DTR off
> again after the last byte fell out of the UART.
IIRC, you can't tell that. IIRC, the 16{4,5,6}50 will only tell you
when the last byte has passed from the FIFO (or holding register) into
the shift register.
Maybe I'm thinking of the Zilog SCC series or the Motorola MFP,
though. It's been a while...
--
ACTUALLY reachable as @free-lunch.demon.(whitehouse)co.uk:james+usenet
> More so than you have realized yet. I'm not familiar with the Linux
> implementation, but most serial drivers consider the buffer flushed,
> and done as soon as the last byte to be transmitted as been sent to
> the uart. The problem is, it takes about 1mS (9600 baud) for the uart
> to finish transmitting the character. If your using a buffered uart,
> the problem gets much worse!
Actually, IIRC it's a holding-register versus shift-register problem.
> If you manage to get a fast DTR shut off routine working, you WILL cut
> off the last few bits of your last character. Been there, done that.
Something that just occurred to me is that you may be able to put the
UART into loopback mode and hence know to toggle the DTR when you have
*received* the character. This will only work if the Tx line is not
tri-stated (or otherwise not actually outputting) while in loopback
mode (I can't remember). If you do this, though, you will also have
to turn off the receive FIFO or wait a couple of character-times :-(
> The easiest solution is to not control the 485 transmitter in
> software. Use a one shot circuit in the RS-232 to 485 converter to do
> the dirty work for you.
Actually the easiest solution is to use an extra 2 wires :-)
* TIOCMGET directly accesses the UART registers, and when used from a user-level
program can mess up the serial driver's internal state (since many registers are
cleared when read/written).
* scheduling delay can be up to 10ms.
If you can't change the hardware, you might consider writing a custom character
driver for the device. Get Alessandro Rubini's "Writing Linux Device Drivers"
book, and you can have a working solution in less than a week.
Tim Wall
> >Before I go and hack up the serial driver to include automatic DTR toggling,
> >I wonder what other people might think.
> >
> >Any hints greatly appreciated!
> >
> >Thanks,
> >--
> >Olav "Mac" Wцlfelschneider wo...@cardware.de
> >PGP fingerprint = 06 5F 66 B3 2A AD 7D 2D B7 19 67 3C 95 A7 9D AF
> >Mer muЯ doch nur emol e bissje nochdenke. -- Mundstuhl
>
> At7
Posting binaries is generally frowned upon.
Anyway, I did it in software by writing my own driver which bangs directly
on the uart, listens to its echo and turns the driver off as soon as
all bytes came back. Timeouts ensure that nothing hangs in the process.
As a bonus, this approach does also collision detection.
--
Olav "Mac" Wölfelschneider wo...@cardware.de
PGP fingerprint = 06 5F 66 B3 2A AD 7D 2D B7 19 67 3C 95 A7 9D AF
Mer muß doch nur emol e bissje nochdenke. -- Mundstuhl
Two (possible ignorant) questions:
1) Do you turn it off after *each* char sent, or only the last char
in a packet?
2) Has anyone written a RS485 kernel-level driver for Linux that they
would share?
Thanks,
/Tommy
Ok, I'm sorry
Of course, after the last char of the packet came back as an echo.
A timeout ensures that this does not hang if there is no echo (eg. because
someone pulled the plug).
If you want to code this yourself, be careful with the FIFO. The receiver
FIFO threshold must be set to 1, or you will still turn off too late.
With a high FIFO threshold, it can take some 4 character times before
the actual receive interrupt happens, if the FIFO is not full.
However, the uart permits to change the threshold on the fly, so I usually
leave it on 14 when idly waiting for incoming data. I set it to 4 at the
start of transmission and then set it down to 1 when I got at least 4 bytes.
Given a packet is at least 4 bytes long, this minimizes the number of
interrupt triggers. Beware, this idea is not yet thoroughly tested and it
might just shoot into your foot...
Oh, and I still recommend Rubini´s book on kernel device drivers. It´s great.