HardwareSerial::flush causing freeze inside interrupts

Status
Not open for further replies.
I am currently trying to communicate through UART inside a timer interrupt, and I'm facing a strange issue when calling the flush() function. Here is a simple code to reproduce the problem :

Code:
void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
}

void loop() {
  delay(500);
  Serial.println("Begin setup");
  Serial1.begin(115200);
  IntervalTimer timer;
  timer.priority(64);
  timer.begin(interruptFunction, 500000);
  Serial.println("End setup");

  while(true);
}

void interruptFunction()
{
  Serial.println("Begin interrupt");
  Serial1.write(0x42);
  Serial1.flush();
  Serial.println("End interrupt");
}

I am using a Teensy 3.2 and the Arduino IDE.

The output on the usb serial if the following :
Begin setup
End setup

So apparently the teensy is frozen...

If you comment the line "Serial1.write(0x42);" OR the line "Serial1.flush();" aka if you don't flush or if there is nothing to flush then the code will run fine an you get the following output :
Begin setup
End setup
Begin interrupt
End interrupt
Begin interrupt
End interrupt
Begin interrupt
End interrupt
etc...

It is probably an interrupt-related issue, but I can't find exactly what is wrong.
I hope you can help me with that

Thanks,
Sylvain
 
You have the Teensy captured in an infinite while loop inside the infinite loop loop. That's normally not the way to go. I'd move the whole interrupt setup stuff in the setup() function and let the loop() empty.

And then, just for diagnostic purposes, I'd test if the write and flush operations which are currently on Serial1 work when you direct them temporarily towards Serial (USB). This could give a differential diagnose.
 
A couple things - both fatal:
>> This line : IntervalTimer timer;
Must be file global - as posted within setup() - it doesn't persist outside of it.
>> Interrupt functions should not generally call other functions.
With timer.priority(64); { elevated } it seems the .write() calls code using interrupts with a lower priority that then cannot complete the task and thus not return from that or .flush().
Supposing those were 'debug' prints in interruptFunction() - that is asking for trouble.
-> with default priority on the timer it happens to work as written {with timer defined file global} - but it could just as easily have failed for doing bad/too much in interruptFunction()

It works as below as a safer variant.

With Teensy using an elapsedMicros variable to hit >= 500000 - would be just as effective in the case shown where nothing else gets done in loop(). Even with other activity - if non-blocking - it might stay within a couple usecs. An example of that is below - included is a blinking LED and what my setup() typically looks like - and it shows it hitting 500000 us on the nose as written. If it drifts too X usecs much then enter a prior/outer wait loop that many usecs before waiting for it to hit the target value.
Begin write flush [usecs==500000 ] End write flush


Code:
#define qBlink() (digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN) ))

volatile uint32_t IntCnt = 0;

IntervalTimer timer;

void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  delay(500);
  Serial.println("Begin setup");
  Serial1.begin(115200);
  timer.priority(64);
  timer.begin(interruptFunction, 500000);
  Serial.println("End setup");
}

void loop() {
  static uint32_t lastIntCnt = 124234;
  while (true) {
    if ( lastIntCnt != IntCnt ) {
      lastIntCnt = IntCnt;
      Serial1.write(0x42);
      Serial1.flush();
      Serial.print("Int Count = ");
      Serial.println(lastIntCnt);
    }
  }
}

void interruptFunction()
{
  IntCnt++;
  qBlink();
}

Code:
#define qBlink() (digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN) ))

elapsedMicros PFlush;
void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  Serial1.begin(115200);
  while ( !Serial && millis() < 4000 );
  Serial.println("Hello World");
  PFlush = 0;
}

void loop() {
  uint32_t PFval;
  while (true) {
    if ( PFlush >= 500000 ) {
      PFval = PFlush;
      PFlush -= 500000;
      qBlink();
      Serial.print("Begin write flush [usecs==");
      Serial1.write(0x42);
      Serial1.flush();
      Serial.print(PFval);
      Serial.println(" ] End write flush");
    }
  }
}
 
Thanks for the advice, even though they don't really apply to my real code (the code I posted is just a quick way of reproducing the issue, nothing more ^^)
Thanks also for the workaround, it does work with the default priority.

I can understand easily that flush() needs an interrupt to be called in order to terminate, but then my question is :
which interrupt is supposed to be called ? and what is the priority of this interrupt ?
I don't think calling flush() in an interrupt is a good design and I will change that. But it would be very interesting to know which interrupts are supposed to be called for the UART to work properly.
 
My first assumption would be if you are using Serial1, it uses hardware USART0 so it would probably need: uart0_status_isr

Look in Core\Teensy3\serial1.c
 
That's exactly the sourcecode I was looking for, and it's as simple as that :
Code:
void serial_flush(void)
{
	while (transmitting) yield(); // wait
}
"transmitting" can only be set to 0 in the "uart0_status_isr" interrupt
The priority of this interrupt being 64

Now it makes perfect sense, thanks a lot :)
 
Status
Not open for further replies.
Back
Top