Go Down

Topic: Serial.flush() hangs on 2Mbps Baud Rates (Read 5008 times) previous topic - next topic

wlong007

So I got a chance to try v1.0.6 and flush() still hangs. Without flush(), there does not seem to be any dropped bytes. However, whenever I run my project code (I did not list it here because it is beyond the scope of my initial post) I get results in line with when I use a 250Kbps speed. Just to explain a little bit of my project, from the Arduino I am sending out micros() over the Serial line (along with other data). Then with Node.js I calculate the time difference between two packets (packets being the micros() value and my other data sent from the Arduino). With my project, I can incrementally increase the baud rate and I see the time difference decrease to a certain point. After which, the time difference flat lines no matter how high the baud rate is.  I suspect an issue similar to the answer provide here is occurring when using the v1.0.6. I actually do not know if that is the case seeing as I have not taken an oscilloscope to it. I will keep digging.

wlong007

The serial converter (ATmega8U2) may be responsible for the dropped data bug.  If the Serial code is not able to sustain 2 Mbps then the serial converter code may not be able to either.


Again, according to the ATmega8U2 datasheet (see page 175) it should be able to support this speed. Now I do understand the difference between theoretical limits and what actually happens in practice. However, I just feel that if it is in the docs then we should be able to do. I suppose that might be naive of me. Call me an optimist if you want.

Delta_G

Again, according to the ATmega8U2 datasheet (see page 175) it should be able to support this speed.
The chip being able to support that speed and the code on the chip being able to support that speed are two completely separate questions.  You've shown that the first should be affirmative.  But the datasheet doesn't say anything about the code running on the chip. 
If at first you don't succeed, up - home - sudo - enter.

wlong007

The chip being able to support that speed and the code on the chip being able to support that speed are two completely separate questions.  
Fair enough. It would also appear that the latter is not possible from our tests so far.

Coding Badly

#19
Oct 11, 2017, 09:53 pm Last Edit: Oct 11, 2017, 10:31 pm by Coding Badly

Yup.  The serial converter is a bottleneck.  It is (partially? / fully?) responsible for dropped data.  If you want 2 Mbps from a stock Uno you are going to have to modify the ATmega8U2 program.  (I will post the test program later today.)

(It is still possible that PuTTY is (partially? / fully?) responsible for dropped data.  I get the same results with a Python application.  PuTTY is not the problem.)


Coding Badly

#20
Oct 11, 2017, 10:36 pm Last Edit: Oct 11, 2017, 10:37 pm by Coding Badly

Even at 1 Mbps there is dropped data.

Arduino code...
Code: [Select]

void setup( void )
{
  UCSR0A = (1 << RXC0) | (1 << TXC0) | (0 << UDRE0) | (0 << FE0) | (0 << DOR0) | (0 << UPE0) | (1 << U2X0) | (0 << MPCM0);
  UBRR0 = 0;  // 2 Mpbs
//UBRR0 = 1;  // 1 Mbps
  UCSR0B = (0 << RXCIE0) | (0 << TXCIE0) | (0 << UDRIE0) | (1 << RXEN0) | (1 << TXEN0) | (0 << UCSZ02) | (0 << RXB80) | (0 << TXB80);
//UCSR0B = (1 << RXCIE0) | (0 << TXCIE0) | (0 << UDRIE0) | (1 << RXEN0) | (1 << TXEN0) | (0 << UCSZ02) | (0 << RXB80) | (0 << TXB80);
  UCSR0C = (0 << UMSEL01) | (0 << UMSEL00) | (0 << UPM01) | (0 << UPM00) | (0 << USBS0) | (1 << UCSZ01) | (1 << UCSZ00) | (0 << UCPOL0);
}

void loop( void )
{
  while ( true )
  {
    while ( (UCSR0A & (1 << UDRE0)) == 0);
    UDR0 = 'A';
    while ( (UCSR0A & (1 << UDRE0)) == 0);
    UDR0 = '\r';
    while ( (UCSR0A & (1 << UDRE0)) == 0);
    UDR0 = '\n';
  }
}


Python...
Code: [Select]

import serial
import time
board = serial.Serial('COM6', 2000000, timeout=0, parity=serial.PARITY_NONE, rtscts=0)
time.sleep(2)
board.read(1000000)



wlong007

So I believe the ATMega8U2 is to blame. I ran Coding Badly code on my Sparkfun Redboard and there was no dropped data. It is important to note that the Sparkfun Redboard does not have the ATMega8U2 but a FTDI FT231X (which has specs up to 3Mbps). I did find a possible solution when using an Uno. I plan on flashing Hoodloader2 to the ATMega8U2. In their wiki overview page, they list full 2Mbps capabilities. This is not my first choice but it is a viable solution. Results to come.

wlong007

Hoodloader2 did the trick. I flashed my Uno R3 with the Hoodloader2 bootloader and I now have full 2Mbps baud rate with no dropped data.

Coding Badly


wlong007

Yes flush() still hangs at 2Mbps on both the UNO R3 with Hoodloader2 and the Sparkfun Redboard.


wlong007

These two lines are in the wrong order...
https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/HardwareSerial.cpp#L221-L222


That did not seem to fix it for me or I am not doing something else properly. I just need to switch those two lines located in \arduino-1.8.5\hardware\arduino\avr\cores\arduino\HardwareSerial.cpp, right? That fixed the flush() hangup for you?

Coding Badly

#27
Oct 13, 2017, 07:38 pm Last Edit: Oct 13, 2017, 07:39 pm by Coding Badly
That did not seem to fix it for me or I am not doing something else properly.  I just need to switch those two lines located in \arduino-1.8.5\hardware\arduino\avr\cores\arduino\HardwareSerial.cpp, right?
Make certain you modify the correct file.  It is possible there is more than HardwareSerial.cpp on your computer.  Mine is located in Program Files (x86).

Quote
That fixed the flush() hangup for you?
Yes.

This is my version of write...

Code: [Select]

size_t HardwareSerial::write(uint8_t c)
{
  _written = true;
  // If the buffer and the data register is empty, just write the byte
  // to the data register and be done. This shortcut helps
  // significantly improve the effective datarate at high (>
  // 500kbit/s) bitrates, where interrupt overhead becomes a slowdown.
  if (_tx_buffer_head == _tx_buffer_tail && bit_is_set(*_ucsra, UDRE0)) {
/* 20171013 - Coding Badly
    *_udr = c;
    sbi(*_ucsra, TXC0);
*/
    sbi(*_ucsra, TXC0);
    *_udr = c;
    return 1;
  }
  tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE;

  // If the output buffer is full, there's nothing for it other than to
  // wait for the interrupt handler to empty it a bit
  while (i == _tx_buffer_tail) {
    if (bit_is_clear(SREG, SREG_I)) {
      // Interrupts are disabled, so we'll have to poll the data
      // register empty flag ourselves. If it is set, pretend an
      // interrupt has happened and call the handler to free up
      // space for us.
      if(bit_is_set(*_ucsra, UDRE0))
_tx_udr_empty_irq();
    } else {
      // nop, the interrupt handler will free up space for us
    }
  }

  _tx_buffer[_tx_buffer_head] = c;
  _tx_buffer_head = i;

  sbi(*_ucsrb, UDRIE0);
 
  return 1;
}

wlong007

I got it now. I do not know what I was doing earlier but flush() is now working for me with Coding Badly's modification. What is the next step to get the fix into future releases? Also, what is the next step for the ATMega8U2 code issue?

Coding Badly


https://github.com/arduino/Arduino/blob/master/CONTRIBUTING.md

A pull request is ideal.


Bear in mind that...

The problem with flush is a bug that affects everyone using flush.  The fix is simple, easy to understand, and has no possible side effects.  I suspect the Arduino folks will make that change.

The problem with the ATmega8U2 firmware affects very very few people.  The fix is complicated, difficult to understand all the possible implications, and could have very dramatic side effects.  I suspect the Arduino folks will not make that change.


Go Up