Nick Gammon posted code which checks the hardware to make sure the last byte has gone:
Serial.flush ();
// wait for transmit buffer to empty
while ((UCSR0A & _BV (TXC0)) == 0)
{}
Pete
Nick Gammon posted code which checks the hardware to make sure the last byte has gone:
Serial.flush ();
// wait for transmit buffer to empty
while ((UCSR0A & _BV (TXC0)) == 0)
{}
Pete
@eried thanks but I don't need help with receiving data.
@el_supremo Thanks. What library do I need to #include to get access to that functionality?
No library. That code stands on its own.
el_supremo:
Nick Gammon posted code which checks the hardware to make sure the last byte has gone:Serial.flush ();
// wait for transmit buffer to empty
while ((UCSR0A & _BV (TXC0)) == 0)
{}
Pete
In my testing that was not good enough. It worked when only 1 character was sent but it failed
if there was still a character in the TX data register when the polling started.
It is very unfortunate that the bit isn't a "BUSY" bit. It is merely a transition bit that clears when the last bit
of a character leaves the shift register. This means that if multiple characters have been sent,
the bit also has to be set appropriately when the character is fed into the USART data register otherwise
you end up seeing the bit clear for the 2nd to last character since there are two characters in the USART.
1 in the shift register and one in the data register.
It took a patch in HardwareSerial.cpp to be able to set the bit appropriately to ensure that there
were no race conditions.
I had to modify the UDRE0 ISR routine and the flush() routine to make flush() work correctly and reliably
in my testing.
--- bill
mattallen37:
@eried thanks but I don't need help with receiving data.@el_supremo Thanks. What library do I need to #include to get access to that functionality?
The code I posted is just a simple way to wait for the "full" data (timeout 10 ms), not just receive data.
@Nick Gammon, the compiler complains that some of the variables are undeclared (as if maybe I need to #include something).
@bperrybap, shouldn't the flush at the beginning wait till there is only one byte left to send? At that point, there should only be 1 byte, so shouldn't it work fine? If not, can you please post the changes you made to the HardwareSerial library?
@eried the code you posted is a way to wait until an entire string has been received not sent.
This may sound like a ridiculous question from a noob, because it is, but - when you read a byte from the serial receive buffer, is it removed from the buffer automatically at that time?
mattallen37:
@bperrybap, shouldn't the flush at the beginning wait till there is only one byte left to send? At that point, there should only be 1 byte, so shouldn't it work fine? If not, can you please post the changes you made to the HardwareSerial library?
flush() currently waits until there are no characters in the s/w queue,
but there are still up to two characters still in the USART remaining to be transmitted.
One in the transmit shift register that is currently being shifted and transmitted
out and one in the transmit data buffer register (TXB).
I don't like the way Atmel implemented their TXCn bit.
It marks a transition and isn't a state. The only automatic clearing of this
bit is by execution of a TXCI interrupt, which not normally an interrupt
that is used or needed for interrupt driven transmission.
(UDR interrupts are used to allow for the double buffering to ensure back to back transmission)
This makes TXCn status kind of a pain to use.
s/w has to clear it for the hardware to set it
but it has do it in a way that doesn't create a race condition.
A robust flush() solution has to handle all conditions when called.
What I found is that some potential solutions didn't handle all of the
above situations or couldn't handle them more than once.
--- bill
@GCone for questions that are OT, it's best to start your own thread. However, the answer is yes (unless you just peek).
A robust flush() solution has to handle all conditions when called.
- A character in the transmit shift register and no character in TXB.
- A character in TXB and in the transmit shift register
- No characters in TXB or the shift register.
What I found is that some potential solutions didn't handle all of the
above situations or couldn't handle them more than once.
Well many of us use to complain that the serial transmitting function was a simple blocking command so I guess the lesson is to be very careful what you ask for as you may get it.
@bperrybap so there isn't a solution? You said "I had to modify the UDRE0 ISR routine and the flush() routine to make flush() work correctly and reliably
in my testing.", so how can I do the same?
@retrolefty if it's going to send serial in the background, there needs to be a way to know when it has finished. If there isn't a solution that supports the current implementation, then perhaps I will bit-bang the output.
mattallen37:
@retrolefty if it's going to send serial in the background, there needs to be a way to know when it has finished. If there isn't a solution that supports the current implementation, then perhaps I will bit-bang the output.
And perhaps that will work for you. I think what Bill was trying to convey is that one really needs to understand the AVR hardware functions at the basic register level (serial hardware in this case) and even then be careful of what is possible at this level via what one's actual real world application requirements are. Sometimes there is no 'perfect' match or solution and the best compromise possible is what one has to live with. I don't know it that is the case with this issue or not, but I've worked with serial communications sense the mid 60s and it is amazing how unsimple and error prone asynchronous communications can be, especially when dealing with mixed vendors implementation, both in software and hardware.
Lefty
Most processors do not implement this function. Therefore things like changing the transmit / receive flags on RS485 bi directional buffers is difficult. The common solution is to look for the buffer empty flag and then implement a delay such that when it times out your buffer will be empty. This of course depends on the baud rate you are using.
@Grumpy_Mike that is exactly what I'm trying to do (communicate with RS485), and also exactly how I am currently doing it (flush and then delay)
I thought as much. Delay too long and the other end can reply and you miss the first byte, delay too short and you screw the last byte. When my engineers have tackled this in the past we found it best to have a packet format that was tolerant of loosing the first and last bytes. But then we were in control of the packet format.
Grumpy_Mike:
I thought as much. Delay too long and the other end can reply and you miss the first byte, delay too short and you screw the last byte. When my engineers have tackled this in the past we found it best to have a packet format that was tolerant of loosing the first and last bytes. But then we were in control of the packet format.
Or go to two twisted pair full duplex RS485 where you don't have to deal with controlling a direction driver/converter chip?
@Grumpy_Mike I'm using 115200 baud, so 1ms delay is enough for 14 bytes. If I use a 1ms delay after I use flush, then it should be at least 7-14 times longer than necessary (which is okay, because the other device on the bus won't start transmitting until a minimum of 1ms after the last byte has been received from the Arduino). I do have complete data format control, as I am writing the software for both ends.
@retrolefty that's not an option, due to the HW setup of the other controller (Lego NXT). I also want to be able to have more than 2 devices on the bus.
mattallen37:
@Grumpy_Mike I'm using 115200 baud, so 1ms delay is enough for 14 bytes. If I use a 1ms delay after I use flush, then it should be at least 7-14 times longer than necessary (which is okay, because the other device on the bus won't start transmitting until a minimum of 1ms after the last byte has been received from the Arduino). I do have complete data format control, as I am writing the software for both ends.@retrolefty that's not an option, due to the HW setup of the other controller (Lego NXT). I also want to be able to have more than 2 devices on the bus.
There is of course delayMicroseconds() if you want to cut it closer then 7-14 bytes.
Lefty
Good point.
Thanks everyone for your help and ideas. I really appreciate it!