Description
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 withets_install_putc1
. - The
cdc0_write_char
function, in turn, callsUSBCDC::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 withxPortInIsrContext()
, and usesxSemaphoreTakeFromISR()
orxSemaphoreTake()
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 callingportYIELD_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 callingportYIELD_FROM_ISR()
anywhere in the function body.
So, what possible consequences might arise from failing to invokeportYIELD_FROM_ISR()
as indicated in the documentation?
Metadata
Metadata
Assignees
Type
Projects
Status