I'm trying to write a program that uses serial communications with a PC. As I understand it, Arduino 1.0 has a serial input buffer, but no output buffer, except for the single byte provided in the AVR hardware. This needn't be a problem as long as I can detect the state of the single-byte output buffer, which I should be able to do with the following:
if (UCSRA & (1 << UDRE))
{
// Output buffer byte is empty
}
However, this gives me a 'UCSRA was not declared in this scope' error, as do *_ucsra, *_UCSRA, ucsra, &ucsra and every other iteration I find when googling the problem.
I don't want to change the way serial handling works, I just want a way to check the output buffer is empty before I output a byte, so my code doesn't pause while waiting for the previous byte to send.
How I think it currently works:
while ([Output buffer is full])
{
//Do nothing
}
//Put new byte in output buffer
What I want to do:
if ([Output buffer is empty])
{
//Put new byte in output buffer
}
else
{
//Do nothing; try again next iteration
}
I just want a way to check the output buffer is empty before I output a byte, so my code doesn't pause while waiting for the previous byte to send.
There is a 64 byte buffer. Say that there are 22 bytes in the buffer. Why would you not go ahead and add your byte to the buffer? Why wait until the buffer is empty?
If I understand correctly, you can afford to wait for a byte to be shifted out of the buffer, but you can afford to just throw away the data that would be sent if the buffer was empty. Do I have that right?
I just want a way to check the output buffer is empty before I output a byte, so my code doesn't pause while waiting for the previous byte to send.
There is a 64 byte buffer. Say that there are 22 bytes in the buffer. Why would you not go ahead and add your byte to the buffer? Why wait until the buffer is empty?
If I understand correctly, you can afford to wait for a byte to be shifted out of the buffer, but you can afford to just throw away the data that would be sent if the buffer was empty. Do I have that right?
Even if the code suggest that, I think you are wrong. In the else case another task can be processed (it's just doing nothing in the example). Filling the buffer with more data is not always the right way to go. Sometimes you want to continue after the sending queue is empty. Or you just do not want to calculate if your data to be send fits the half full buffer knowing that it would go into an empty one. And if you hit the buffer limit the Serial.print() becomes a blocking call - worst case.
The question is really very interesting because for instance this week I played around with RS485 hence I had to use XON/XOFF flow control in my sketch. I added code to the HardwareSerial to check if the sending queue is empty before switching to listening mode again (Arduino as slave). What happened was a missing last character at the PC side of the communication. To avoid this I added a delay(1) but what I really would like is to check if the last character got transmitted before.
This brings us back to the question of this thread: How can we check for the finished transmission?
So there is both an output and input buffer? I wasn't aware of that. The documentation only seems to mention the input buffer, when explaining the available() command.
If that's the case, there's no problem. I won't be operating near the buffer's capacity, so my serial writes won't lock up the program.
That said, an equivalent to available() for the output buffer would be handy.
Keep in mind that the sending queue is 64 chars long until you have less than 1000 byte SRAM, then it is only 16 chars. The buffered send is new in Arduino 1.0 and flush() now is a blocking call to wait for an empty sending queue.
For checking the sending queue I modified HardwareSerial.cpp (and .h) by adding
OK, because of my RS485 test I modified HardwareSerial to make a complete check for the finished transmission. In the appended code I replaced the deprecated macros sbi() and cbi() (see http://www.nongnu.org/avr-libc/user-manual/group__avr__sfr.html) but I also merged the toolchain 3.2.3 into my Arduino 1.0. I have no idea if this one works with the originally included toolchain.
Changes in HardwareSerial.cpp
void HardwareSerial::end()
{
// wait for transmission of outgoing data
while (issending())
;
*_ucsrb &= ~(_BV(_rxen)); // cbi(*_ucsrb, _rxen);
*_ucsrb &= ~(_BV(_txen)); // cbi(*_ucsrb, _txen);
*_ucsrb &= ~(_BV(_rxcie)); // cbi(*_ucsrb, _rxcie);
*_ucsrb &= ~(_BV(_udrie)); // cbi(*_ucsrb, _udrie);
// clear any received data
_rx_buffer->head = _rx_buffer->tail;
}
...
boolean HardwareSerial::issending()
{
return _tx_buffer->head == _tx_buffer->tail ? (*_ucsrb & _BV(_udrie)) : true;
}
...
void HardwareSerial::flush()
{
while (issending())
;
}
This code runs now on my Arduino but it uses only issending() and not flush. For this to test I have to rebuild my RS485 setup and run the new HardwareSerial with XON/XOFF logic in my sketch. When I will have time for this I do not know. So I will report back later on the result. Anyone willing to try?
If my macro replacement is not compilable on Arduino 1.0 then you have to change flush() and end() and add issending() with modification in your HardwareSerial manually.