Skip to content

Issue realted ESP32-S2 CDC to the Serial.setDebugOutput(true); #6766

Closed
@SuGlider

Description

@SuGlider

I am starting to think that the current cause of the problem (on v2.0.3 stable) is some kind of race/lockup between two or more tasks, all of which want to write to the USB CDC. In my case, this happens due to concurrent use of log_X statements in more than one task. This scenario works correctly when using native serial pins.

Consider the following sketch:

#include <Arduino.h>

void setup()
{
  Serial.begin(115200);
  // setDebugOutput() call commented out for test
  //Serial.setDebugOutput(true);
  delay(2000);
}

void loop()
{
  static uint32_t counter = 1;

  Serial.printf("Using Serial.printf(), this is line %u of output, at msec %u...\r\n", counter, millis());
  //log_i("Using log_i(), this is line %u of output, at msec %u...", counter, millis());
  counter++;

  // No delay at all, blast port at full speed!
}

This is a single task using the serial port. When using USB CDC as the serial port, no combination of use or non-use of setDebugOutput and Serial.printf/log_i manages to lockup the board.

However, my sketches use WiFi, which starts a couple of tasks, each outputting via log_X. Also, AsyncTCPSock, which starts its own task. With this, I have noticed that the startup process, where all of these tasks display debugging strings, is the more likely place where the sketch hangs on output. After this, only the main loop keeps outputting strings, and it runs more or less normally.

Originally posted by @avillacis in #6221 (comment)

I have been reviewing the code of USBCDC.cpp , which handles the USB serial port in ESP32-S2. So far I have noticed the following:

  • The setDebugOutput() call installs a function, cdc0_write_char as a character output for debugging with ets_install_putc1.
  • The cdc0_write_char function, in turn, calls USBCDC::write(uint8_t), and through it, USBCDC::write(const uint8_t *, size_t).
  • The USBCDC::write(const uint8_t *, size_t) appears to acknowledge that it might be called from within an ISR context, as it checks with xPortInIsrContext(), and uses xSemaphoreTakeFromISR() or xSemaphoreTake() depending on the result.
  • According to the Espressif documentation, xSemaphoreTakeFromISR() outputs a flag, pxHigherPriorityTaskWoken, that is supposed to inform whether a higher-priority task was awoken as a result of calling the ISR-specific function. And if so, the function is supposed to request a context switch by calling portYIELD_FROM_ISR().
  • HOWEVER... the current implementation of USBCDC::write(const uint8_t *, size_t) collects this flag, only to discard it without taking any action based on its value, and certainly without calling portYIELD_FROM_ISR() anywhere in the function body.

So, what possible consequences might arise from failing to invokeportYIELD_FROM_ISR() as indicated in the documentation?

#6221 (comment)

Metadata

Metadata

Assignees

Type

No type

Projects

Status

Done

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions