2025-11-11 12:22 PM
I've set up my STM32U5A9J-DK to use ThreadX and connect to my PC via USBX and Audio Class.
I'm correctly see the Audio Device on my PC and I can easily stream audio to it. The only thing is that, for some reason, the callback for new frame done is not fired
audio_stream_parameter[0].ux_device_class_audio_stream_parameter_callbacks.ux_device_class_audio_stream_change
= USBD_AUDIO_PlaybackStreamChange;
audio_stream_parameter[0].ux_device_class_audio_stream_parameter_callbacks.ux_device_class_audio_stream_frame_done
= USBD_AUDIO_PlaybackStreamFrameDone;The strage thing is that USBD_AUDIO_PlaybackStreamChange is correctly called
VOID USBD_AUDIO_PlaybackStreamChange(UX_DEVICE_CLASS_AUDIO_STREAM *audio_play_stream,
ULONG alternate_setting)
{
/* USER CODE BEGIN USBD_AUDIO_PlaybackStreamChange */
/* Do nothing if alternate setting is 0 (stream closed). */
if (alternate_setting == 0)
{
return;
}
BufferCtl.state = PLAY_BUFFER_OFFSET_UNKNOWN;
/* Start reception (stream opened). */
ux_device_class_audio_reception_start(audio_play_stream);
/* USER CODE END USBD_AUDIO_PlaybackStreamChange */
return;
}Instead, USBD_AUDIO_PlaybackStreamFrameDone is never called
VOID USBD_AUDIO_PlaybackStreamFrameDone(UX_DEVICE_CLASS_AUDIO_STREAM *audio_play_stream,
ULONG length)
{
/* USER CODE BEGIN USBD_AUDIO_PlaybackStreamFrameDone */
UCHAR *frame_buffer;
ULONG frame_length;
/* Get access to first audio input frame. */
ux_device_class_audio_read_frame_get(audio_play_stream, &frame_buffer, &frame_length);
...
}
Solved! Go to Solution.
2025-11-22 1:36 AM
As expected, the issue was due to the USBX API handling of the single frequency in case the host asks to set the frequency. As explained below, no matter if the frequency asked to be set by the host is the exact same as the one the device supports. It will be automatically discharged and set into a STALL mode. Same if multiple frequency is supported but just a single frequency is specified in the array (no matter if it's the correct one).
The workaround (because this issue will need a fix at the USBX API level) is to set up the device to support multifrequency selection (even if you want to work with one frequency).
In the USBD_AUDIO_SetControlValues you need to set the current frequency as the single frequency you want as well as the array with the range of frequencies you want (which is still one)
audio_control[0].ux_device_class_audio20_control_sampling_frequency_cur = USBD_AUDIO_FREQ_48_K;
audio_control[0].ux_device_class_audio20_control_sampling_frequency_range = sampling_freq_range;where
static UCHAR sampling_freq_range[] = {
0x01, 0x00, // wNumSubRanges = 1
0x80, 0xBB, 0x00, 0x00, // dMIN = 48000
0x80, 0xBB, 0x00, 0x00, // dMAX = 48000
0x01, 0x00, 0x00, 0x00 // dRES = 1
};So, in summary:
If ux_device_class_audio20_control_sampling_frequency is set to a non-zero value (like 48000), the code treats it as a fixed frequency and rejects SET_CUR.
Solution: When initializing your audio control structure, make sure:
control->ux_device_class_audio20_control_sampling_frequency = 0; // Must be 0 for variable rate
control->ux_device_class_audio20_control_sampling_frequency_cur = 48000; // Current/default rate
You need to set up ux_device_class_audio20_control_sampling_frequency_range properly. This should point to a buffer containing:
// Example for 48kHz only support
static UCHAR sampling_freq_range[] = {
0x01, 0x00, // wNumSubRanges = 1
0x80, 0xBB, 0x00, 0x00, // dMIN = 48000 (0x0000BB80)
0x80, 0xBB, 0x00, 0x00, // dMAX = 48000
0x01, 0x00, 0x00, 0x00 // dRES = 1 (means variable within range)
};and then
control->ux_device_class_audio20_control_sampling_frequency_range = sampling_freq_range;If you only have 1 subrange and dRES = 0, the code treats it as a fixed frequency. To allow SET_CUR with a single frequency, you need to set dRes = 1 as above.
At this point audio_stream_parameter[audio_stream_index].ux_device_class_audio_stream_parameter_callbacks.ux_device_class_audio_stream_frame_done = USBD_AUDIO_PlaybackStreamFrameDone; will be correctly called
2025-11-12 1:40 AM
Hello @nico23
To help you fix the issue where the ux_device_class_audio_stream_frame_done callback is not triggered, I recommend referring to the STM32WBA USBX Audio example available here:
STM32WBA BLE USBX Audio Example .
2025-11-12 1:41 AM
Hi @nico23
Would you attach minimum firmware to reproduce the behavior?
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2025-11-12 1:47 AM - edited 2025-11-12 1:58 AM
Hi @FBL
Here you are https://github.com/NicoCaldo/NovaSonus_ThreadX
I'm seeing that my memory allocation is one-third the one in the example
#define USBX_DEVICE_MEMORY_STACK_SIZE 20 * 1024vs
#define USBX_APP_MEM_POOL_SIZE 60 * 1024
#define USBX_MEMORY_STACK_SIZE 53 * 1024Could it be the issue?
2025-11-12 9:49 AM
Hi @nico23
I suggest isolate the USB audio functionality by disabling other middleware like TouchGFX to rule out conflicts and narrow down the issue. Would you please provide a minimal project based on this STM32U5A9J-DK board, without any other peripherals configured? This will help us assist you more efficiently.
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2025-11-12 1:52 PM
I mean, I can try but it's basically the starting project the CubeMX generates when you select TouchGFX with the demo board and add USBX with the audio class. There's no custom code and the code of the initialization of the USBX Audio class is literally the one shown by @T_Hamdi on the example so,
it sounds strange to me,you need just the USBX Audio class implementation to see it works...
Have you had the chance to check the project, specifically the https://github.com/NicoCaldo/NovaSonus_ThreadX/blob/main/USBX/App/app_usbx_device.c and https://github.com/NicoCaldo/NovaSonus_ThreadX/blob/main/AZURE_RTOS/App/app_azure_rtos.c ?
There's nothing fancy on the TouchGFX, just a white text on black screen...
2025-11-15 10:32 AM
So I have investigated a bit, and when I start to stream to the board, I'm getting a correct
Audio Stream Change: alt=0
Audio Stream Change: endpoint_dir=OUT
Audio Stream Change: alt=1
ux_device_class_audio_reception_start with status 0(I'm logging what's going on)
Audio starts streaming, but the callback is not called. If I pause the execution of the program, I'm seeing the ux_class_audio_stream_thread is always SUSPENDED in waiting for a semaphore (?)
(I've disabled ThouchGFX thread)
2025-11-16 9:10 AM - edited 2025-11-16 9:55 AM
Checking into the actual source code, I've found the _ux_utility_error_callback_register. I've implemented it, and it turned out that on startup, when the USB is connected to my PC, I'm seeing the error
USB Error: INTERRUPT level, HCD context, TIMEOUT (0x2/0x8/0x4)Meanwhile, when I'm starting the audio stream from the PC, I'm getting
USB Error: INTERRUPT level, CLASS context, TRANSFER (0x2/0x7/0x23)Now, I'm seeing the second error is a transfer error, probably due to the fact that at the beginning, something went into a timeout. My question is, why am I seeing an HCD context error if I'm using the USBX in Device Only mode?
After tracing the issue, I've understood that the error is called inside
_ux_utility_mutex_on
_ux_system_mutex_on(&_ux_system -> ux_system_mutex);
memory = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS) * UX_MAX_SLAVE_CLASS_DRIVER);The tx_mutex_get returns 0x4 which is triggered by
if (TX_THREAD_GET_SYSTEM_STATE() != ((ULONG) 0))
{
/* A non-thread is trying to suspend, return appropriate error code. */
status = TX_WAIT_ERROR;
}From what I'm understanding, the error occurs because ux_device_stack_initialize is executed before ThreadX is running. Am I right, or am I missing something?
2025-11-17 9:13 AM
Despite the TIMEOUT at the initialization (I don't think it's the main issue of my frame_done not being called), I'm seeing that when I try to start an audio flux from my PC to the USB Audio driver, I'm seeing the error handler being called with the following stack:
status = _ux_device_stack_transfer_request(transfer, max_packet_size, max_packet_size); triggers the error as the status is 38 (what does it mean?). Inside the function, I've found
status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_TRANSFER_REQUEST, transfer_request);which is the one that returns 38.
In ux_api.h, the only value I was able to find that matches the return value was
#define UX_TRANSFER_BUS_RESET 0x26From here, I'm stuck as the ux_slave_dcd_function is defined as
typedef struct UX_SLAVE_DCD_STRUCT
{
UINT ux_slave_dcd_status;
UINT ux_slave_dcd_controller_type;
UINT ux_slave_dcd_otg_capabilities;
UINT ux_slave_dcd_irq;
ULONG ux_slave_dcd_io;
ULONG ux_slave_dcd_device_address;
UINT (*ux_slave_dcd_function) (struct UX_SLAVE_DCD_STRUCT *,UINT, VOID *);
void *ux_slave_dcd_controller_hardware;
} UX_SLAVE_DCD;
2025-11-18 4:10 AM
Hi @nico23
It seems the issue is related to mutex acquisition failing because USBX initialization is called before ThreadX kernel is fully running. This is a known limitation using USBX.
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.