Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serial.flush() blocks forever #597

Closed
jpmeijers opened this issue Feb 3, 2021 · 9 comments · Fixed by #652
Closed

Serial.flush() blocks forever #597

jpmeijers opened this issue Feb 3, 2021 · 9 comments · Fixed by #652
Labels

Comments

@jpmeijers
Copy link

Using the latest version of the SAMD core, doing a Serial.flush() will hang forever.

Problem can be reproduced using this sketch:

void setup() {
  SerialUSB.begin(115200);
  delay(2000);
  SerialUSB.println("Start");

  Serial.begin(9600);
  Serial.flush();

  SerialUSB.println("Setup end");
}

void loop() {
  SerialUSB.println("Loop");
  delay(1000);
}

Which will only print:

15:19:30.572 -> Start

Tracing the issue down takes me to Serial.flush(), which is defined in Uart.cpp. This calls sercom->flushUART(); in SERCOM.cpp.

The previous versions of this core had the following code:

void SERCOM::flushUART()
{
  // Skip checking transmission completion if data register is empty
  if(isDataRegisterEmptyUART())
    return;

  // Wait for transmission to complete
  while(!sercom->USART.INTFLAG.bit.TXC);
}

The current version has the if(isDataRegisterEmptyUART()) commented out:

// if(isDataRegisterEmptyUART())
// return;

I don't understand why this is commented out, as clearly the while(!sercom->USART.INTFLAG.bit.TXC); will never return if flush() is called when the TX buffer is already empty.

@facchinm
Copy link
Member

facchinm commented Feb 3, 2021

See arduino-libraries/MKRWAN@0493918 . This PR #541 is the root cause, and it's not been analyzed well enough before merging. Sorry for that. Can we find a workaround to fix both the issues? @aentinger @cmaglie

@facchinm facchinm added the bug label Feb 3, 2021
@aentinger
Copy link
Contributor

Revert #541? (Sounds cheap but as I see it clearly a bug was introduced).

@matthijskooijman
Copy link
Collaborator

I haven't looked closely, but I remember that on AVR, there was the problem that TXC is only set after all pending data was sent and cleared when new data was queued. This left the problem that on startup, when no data was written yet, no data was pending, but TXC was not set either, so just waiting for TXC in flush would wait indefinitely. From the SAMD datasheet, the TXC bit seems to behave the same here. I haven't looked close enough if this explains the problems in this issue, but I suspect it might.

On AVR, IIRC we solved this by simply keeping a variable that says whether or not data has been written at all and use that to bypass TXC if needed, I think the same approach would be valid here too.

@ghost
Copy link

ghost commented Apr 15, 2021

I've made some changes that seems to have fixed this issue, but I'd appreciate if others could test. You can checkout the changes in the latest commit from my old PR #535 a744583 . It was a mistake to to push to that branch - sorry for the mess, but anyway the specific changes relating to this issue are clearly visible.

@coffeemachine
Copy link

@facchinm this bug is critical in my view. In my case it causes RS485 library to hang (sendBreak calls flush). Like @aentinger I'd suggest to revert PR #541 and make a release based on 1.8.11 with just this fixed. I'd address the solution (@acicuc) in a separate release. What do you think?

@ghost
Copy link

ghost commented Oct 17, 2021

@coffeemachine was @matthijskooijman suggestion that I implemented in a744583 helpful in your case?

facchinm pushed a commit to facchinm/ArduinoCore-samd that referenced this issue Oct 18, 2021
* The aynchronous nature of the DRE and TXC interrupt flags
  causes issues (lockups) when the TX DATA register is empty on start
  and a flush is issued. Simply looking at the DRE prior to
  waiting for TXC is insufficient because the data register
  may well be empty but the shift register could still contain
  data, in this case SERCOM::flushUART() would return before TXC
  has been raised thus before flushing is complete.
* bool added to SERCOM.h to indicate when it is ok for
  SERCOM::flushUART() to wait for the TXC flag. This flag is
  set when any data is written to the data register via
  SERCOM::writeDataUART(). It is cleared when a flush is done.
@facchinm
Copy link
Member

@acicuc I pushed your patch (rebased) on #652 . Tested reflashing an ESP32 up to 1.5Mbaud, with flush() added here and there.

facchinm added a commit that referenced this issue Oct 20, 2021
@WanaGo
Copy link

WanaGo commented Aug 8, 2022

Was the fix (#652) for this rolled out for the Arduino Due (SAM) board library, or was it specific only to the SAMD ?
As the same issue happens on the Arduino Due.

@ghost
Copy link

ghost commented Aug 13, 2022

@WanaGo, currently it doesn't look like it, but you may be able to implement a similar approach in the SAM core?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants