STM32 USB to I2S multi channel - log - ask for help

***** 03/03/2024 Update of project scope => move from UAC1 to UAC2 ******

Hello,

I'm putting on the bench someting in my head since a long time, that should not be too difficult to achieve for ones with know how... but could not be as easy for me ;-)

I want to program a "simple" USB to I2S, 8 channel device, UAC2, single sampling rate (48kHz or 44.1kHz), with Asynchronous mode.

Why UAC1: because ST proposes some UAC1 libraries, and no UAC2 ones (at least officially). UAC1 works without drivers on Windows and Linux.

SB Full Speed (FS) allows for 8x 48kHz x 1- bits (or 4x 48kHz x 24 bits). Great Monitor studios like Neumann KH150 have internal single 48kHz sampling rate. Many people consider that CD quality is "sufficient" and don't race for more bits, more Hertz. It is easy to find stm32 boards with USB FS, and more difficult with USB HS.

This would aim at providing the connectivity part to a 8ch DAC like ES9080 or AK4458. Overall target is 100% function and 99.5% performance of top products. for that niche need of multichannel. It could pair with RPi / Linux DSP offers like CamillaDSP for active speakers (or speakers / subwoofers combos...).

Plaform is a STM32F4 dicovery board to start with, then could be a STM32 black pill (seems to fit the purpose), which pave the way to dedicated board if needed. Those platforms unfortunatly have SAI, but if done well, the different I2S peripherals can be synchonized as slaves from a master, or all slaves from an external clock. A blackpill could be the USB "module" of a DAC board.

I would be happy if the the code could rely "as much as possible" on HAL libraries, code generated by STM32CubeIDE, and ST USB middleware. But, why did they made that so complex, with so much abstractions, while still needing to dig in all "layers" to fit/finalize code, and not really robust... I reallydon't like it so much, and it does not looks nice to me. But it should ease future portability (at least try to).

Intention is Open Source for the application part (not an expert about ST "mixed" licence for the USB stack but should be OK for DIY community).

Oh, if it already exists on Github or elsewhere, and I have not found it: let me know. I will be super happy ;-)

Current understanding is that, based on existing stereo code, I only need to:
  • find all locations where the number of channels impacts the code
  • change the nuber of channels from 2 to 8,
  • ensure the consistency of the buffer size,
  • on periodic basis, slit the 4x2 channels in sequence from the inputs to 4 distinct buffers that will each drive an I2S (through DMA)
Help of people knolageable about USB UAC and STM32 will accept to help... and I may have stupid questions.

I have an almost working stereo code based on ST examples. I have a first version that declares 6 channels, which is not working. I will come tomorrow with some questions...

Go :)

JMF
 
Last edited:
So... The code is basically the one of the ST libraries and the Standalone Audio example.

When I connect the board to my Windows 10 PC, my board is detected by Windows as USB_Audio device. The enumeration looks correct (audio, name...). I can select it in the mixer or in foobar. Alternate Interface is set by Host to alt_interf 0

When I start music on Foobar, the host asks to switch to alt_interf 1, which the code in usbd_audio.c does (haudio->alt_setting = (uint8_t)(req->wValue); But what is this change expected to trigger? Don't know). Attached a snapshot of the calls sequence when at breakpoint where alt_interf is set.

The Foobar HMI lives as if broacasting the audio stream (and freezes if I unplug the USB), but the DataOut is not activated, as if not receiving data on EP01 (breakpoint not activated). It is how if the audio stream was not taking place between the host and the device.

This happens to me 1/4 of the time with the stereo standard code and is systematic with the 6 channels code.

Would you have ideas where to look, what to check to get some hints ?

Does the Host waits for something from the device after the interface is set to alt_inter 1, before sending the audio stream on USB?

Note, I have a LA1010 logic analyser (but only used it yet for I2C and I2S).

Best regards,

JMF
 

Attachments

  • Capture Alt interf 1 call.JPG
    Capture Alt interf 1 call.JPG
    146.7 KB · Views: 55
Thanks Bohrock2610 for pointing in that direction.

There were only 2 cases where USBD_LL_PrepareReceive(...) was called:
  • at initialization USBD_AUDIO_Init,
  • at the end of the management of a USBD_AUDIO_DataOut call (logical).
As it was not working perfectly with the base stereo version, I feared that tehe Host would "cancel" the init USBD_LL_PrepareReceive() when asking for alt_interface ==0. So I added some code similar to what is present in https://github.com/dragonman225/stm32f469-usbaudio/tree/master to manage the USB_REQ_SET_INTERFACE modifications:
  • alt_interf ==0: USBD_LL_FlushEP(pdev, AUDIO_OUT_EP) and Deinit() in case of alt_interf ==0,
  • alt_interf ==1: Same deinit as abive+ Re-init and (void)USBD_LL_PrepareReceive(pdev, AUDIO_OUT_EP, haudio->buffer, AUDIO_OUT_PACKET).


  • But DataOut is still not called. AUDIO_OUT_PACKET looks below the max with 48 x 2 (16 bits) x 6 channels = 576. Not solved yet

  • Humm can't get rid of bullets tonight. Better go sleeping ;-)
  • Will try tomorrow to revert values to 2CH, to check if working in stereo, and then increase progrtessively the numer of channels to see if that matters.







 
Last edited:
Thanks for the indications and the help.

I didn't progressed much, but I collected some infos with my Linux machine. The details of the audio on the PC are accessible in (JMF DACbis):
http://alsa-project.org/db/?f=6f59068a1230bd1ddc6db7491bcf0c9b6e788b69

!!USB Stream information
!!----------------------
--startcollapse--

JMF Audio JMF DACbis at usb-0000:00:1d.0-2, full speed : USB Audio

Playback:
Status: Stop
Interface 1
Altset 1
Format: S16_LE
Channels: 6
Endpoint: 0x01 (1 OUT) (NONE)
Rates: 48000
Bits: 16
Channel map: FL FR FC LFE SL SR
--endcollapse--
......
[ 2141.567548] mc: Linux media interface: v0.10
[ 2141.652385] usbcore: registered new interface driver snd-usb-audio
[ 2144.742759] usb 5-2: cannot submit urb 0, error -90: internal error

The board does not apprear in the list of the audio devices in the Ubuntu parameter window. So should not be considered as working properly (but still recognized by alsa).

The investigations at least allowed me to detect that in the descriptor for EP1 the wMaxPacketSize in Bytes was too small (was not updated with the number of channels). I aligned the size to the audio packet size, but did not solved the issue. DataOut not kicked.

In windows, the board appears as a "clean" audio controller in the device manager: no yellow "!"

Tomorrow is another day ;-)

JMF
 
In windows, the board appears as a "clean" audio controller in the device manager: no yellow "!"
Windows UAC2 logs (see post #6) are very useful for troubleshooting enumeration issues although it seems enumeration is not your current problem. Wireshark+usbmon can be used for monitoring USB packets. But if DataOut interrupts are not triggered there probably is not much to look at in USB traffic.
 
Hello,

Bohrok2610: I have to look at the UAC logs. I hope that UAC1 logs are as good ac UAC1. I'm not familiar with Windows logs, so I will dig in the thread you points.

phofman: the result of the lsusb -v is captured by alsa-info. I copy paste it below. I recognize that I started from the ST library/example, there may be ports not needed for my project. Could they cause some issues?

topdevice: well noted :) this is really aligned with what I'm working on. I'll read in detail.

!!USB Descriptors !!--------------- --startcollapse-- Bus 005 Device 005: ID 0483:5730 STMicroelectronics Audio Speaker Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 0 bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x0483 STMicroelectronics idProduct 0x5730 Audio Speaker bcdDevice 2.00 iManufacturer 1 JMF Audio iProduct 2 JMF DACbis iSerial 3 396037673333 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x006d bNumInterfaces 2 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xc0 Self Powered MaxPower 100mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 0 bInterfaceClass 1 Audio bInterfaceSubClass 1 Control Device bInterfaceProtocol 0 iInterface 0 AudioControl Interface Descriptor: bLength 9 bDescriptorType 36 bDescriptorSubtype 1 (HEADER) bcdADC 1.00 wTotalLength 0x0027 bInCollection 1 baInterfaceNr(0) 1 AudioControl Interface Descriptor: bLength 12 bDescriptorType 36 bDescriptorSubtype 2 (INPUT_TERMINAL) bTerminalID 1 wTerminalType 0x0101 USB Streaming bAssocTerminal 0 bNrChannels 1 wChannelConfig 0x0000 iChannelNames 0 iTerminal 0 AudioControl Interface Descriptor: bLength 9 bDescriptorType 36 bDescriptorSubtype 6 (FEATURE_UNIT) bUnitID 2 bSourceID 1 bControlSize 1 bmaControls(0) 0x01 Mute Control bmaControls(1) 0x00 iFeature 0 AudioControl Interface Descriptor: bLength 9 bDescriptorType 36 bDescriptorSubtype 3 (OUTPUT_TERMINAL) bTerminalID 3 wTerminalType 0x0301 Speaker bAssocTerminal 0 bSourceID 2 iTerminal 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 0 bInterfaceClass 1 Audio bInterfaceSubClass 2 Streaming bInterfaceProtocol 0 iInterface 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 1 bNumEndpoints 1 bInterfaceClass 1 Audio bInterfaceSubClass 2 Streaming bInterfaceProtocol 0 iInterface 0 AudioStreaming Interface Descriptor: bLength 7 bDescriptorType 36 bDescriptorSubtype 1 (AS_GENERAL) bTerminalLink 1 bDelay 1 frames wFormatTag 0x0001 PCM AudioStreaming Interface Descriptor: bLength 11 bDescriptorType 36 bDescriptorSubtype 2 (FORMAT_TYPE) bFormatType 1 (FORMAT_TYPE_I) bNrChannels 6 bSubframeSize 2 bBitResolution 16 bSamFreqType 1 Discrete tSamFreq[ 0] 48000 Endpoint Descriptor: bLength 9 bDescriptorType 5 bEndpointAddress 0x01 EP 1 OUT bmAttributes 1 Transfer Type Isochronous Synch Type None Usage Type Data wMaxPacketSize 0x0240 1x 576 bytes bInterval 1 bRefresh 0 bSynchAddress 0 AudioStreaming Endpoint Descriptor: bLength 7 bDescriptorType 37 bDescriptorSubtype 1 (EP_GENERAL) bmAttributes 0x00 bLockDelayUnits 0 Undefined wLockDelay 0x0000 Device Status: 0x0001 Self Powered --endcollapse--
 
Short: I implemented a USBD_LL_PrepareReceive() in my USBD_AUDIO_IsoOutIncomplete() callback. Now I seem to have my stereo audio stream consistently :)

Question: How can so many available sources work without management of USBD_AUDIO_IsoOutIncomplete() ?

Long:
I reverted my code to 2 channels (2 channels in EP 01 descriptor, in wMaxPacketSize, in the USB->CODEC buffer size.

So in fact, my issue seems related to the management of USBD_AUDIO_IsoOutIncomplete() callback. Thaose arrise when switching the interface setting:
  • if it gets called, then my audio stream does not starts (DataOut is not called),
  • if it is not called, the audio stream not starts (DataOut is called).

I had some printf() for debug in the usbd_audio switches, to capture requests. I commented them all and replaced by Leds toggling. I still have the USBD_AUDIO_IsoOutIncomplete() calls.

So it seems that some management of that callback is needed (currently empty).

I see that the topic has been discussed here, and I have to read in detail.

What is strange to me, is that in most ST generated code and exemples, it is not managed the same way:

- Cube generated code => empty

- STM32CubeExpansion_USBAudioStreaming_V1.0.0 package, which could be considered as a more mature development => empty

- Audio Stand Alone for STM32469I-Discovery board => implement a USBD_LL_PrepareReceive()

- https://github.com/dragonman225/stm32f469-usbaudio = empty, as should work (many stars and forks). How can it work without management of USBD_AUDIO_IsoOutIncomplete() ?
 
Calling USBD_LL_PrepareReceive in IsoOutIncomplete callback is not needed and not what the reference manual suggests. For management of IsoOutIncomplete see the reference manual. E.g. for STM32F446 RM0390 p. 1264. Actually the whole chapter on device programming model starting from p. 1254 is useful read. HAL driver attempts to perform most of the application programming tasks described in the reference manual but the implementation is not altogether correct and varies between MCUs.
 
And this is not sufficient to enable my multi-channel configuration.

I reverted my "working" stereo proof of concept (POC) to a 6CH POC. The device enumerates. Devices Manager states that the device is working correctly. I can "play" the 6 ch .wav on Foobar. Start / stop in Foobar generate changes between interf 0 and 1. I have few IsoOutIncomplete... but my DataOut is not triggered :-(

My understanding it that the only modification on the USB side is:
  • change the descriptor to declare 6 channels instead of 2 in bNrChannels,
  • Increase wMaxPacketSize accordingly (x3) so that each audio packet can accomodate the 6 channels data

The Host will then send "longer packets" to the stm32 at the same Frequency as the stereo example. Then the "output" code has to de-interleave the different channels to their dedicated I2S. But for now, I'm stuck on the USB interface side.

Back to basic questions:
  • Does the Windows standard UAC1 driver supports multichannels?
  • Would the definition of a "cluster" in the descritors be mandatory for this use case ?
 
***** Update of project scope => move to UAC2 ******

I started my experiences with stm32 / USB audio / DSP in 2016. At that time UAC2 was already available for Linux and Mac, but not windows. I read at that time the Xmos documentation... and stayed since that time with the understanding that UAC2 on windows still needed dedicated driver... which ius not anymore the case since 5 years.

Good !

Let's start on UAC2:

Let's go.
 
OK... so one week and an half later. Different code, but same situation :):
  • I ported the OpenUAC2 code to my stm32F4 discovery board, tweaked it for USB FS instead usb HS, and succeeded to have the stereo working,
  • I the modified the Descriptor and part of the code for 5.1 (6 channels). The descriptor is a mix between the (working for stereo) OpenUAC2 descriptor and the NXP exemple in https://github.com/nxp-mcuxpresso/m...speaker/bm/cm33_core0/usb_device_descriptor.c
  • after one glitch, the board is recognized by Windows and Linux as USB UAC2 device. The alsa-info detailed report is http://alsa-project.org/db/?f=6ce206c04116f7c6e0bb8996507b213e0295cdc1
  • I can start playing a 6 channel .wav on Windows (Foobar, VLC) and Linux (player through pulseaudio, which captures my device ; can't use aplay directly)
  • the player behaves as if it was playing the music,
  • mute controls, interface management, clock seems to work (printf debug provides meaningful sequences),
  • BUT usb_audio DataOut() is never called (while same code was working in stereo).
  • (Async feedback not working yet, but want to fix after having a multichannel proof of concet)

=> So different standard (UAC1 to UAC2), Different code (ST to OpenUAC2), but same problem.

For my case USB FS, 48k, 6 channels, 16 bits, I calculate the size of the DataOut packets (audio) as: (48 +1for async packets variability) * 6 channels * 2 bytes => 588 bytes. I use that same figure for the descriptor wMaxPacketSize. Correct?

I wanted to look at Windows UAC2 drivers logs, by stepped back seeing the difficulties to find the Windows soft to read the logs, without installing a bunch of softwares I won't use.

In linux, dmsg does not report issues related to the usb sound card...

Stucked for the moment.

JMF

Bus 002 Device 008: ID 0483:5740 STMicroelectronics Virtual COM Port
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x0483 STMicroelectronics
idProduct 0x5740 Virtual COM Port
bcdDevice 2.00
iManufacturer 1 STMicroelectronics
iProduct 2 STM32 Audio Class
iSerial 3 396037673333
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x00dd
bNumInterfaces 2
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xc0
Self Powered
MaxPower 100mA
Interface Association:
bLength 8
bDescriptorType 11
bFirstInterface 0
bInterfaceCount 2
bFunctionClass 1 Audio
bFunctionSubClass 0
bFunctionProtocol 32
iFunction 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 0
bInterfaceClass 1 Audio
bInterfaceSubClass 1 Control Device
bInterfaceProtocol 32
iInterface 0
AudioControl Interface Descriptor:
bLength 9
bDescriptorType 36
bDescriptorSubtype 1 (HEADER)
bcdADC 2.00
bCategory 0
wTotalLength 0x003c
bmControls 0x00
AudioControl Interface Descriptor:
bLength 8
bDescriptorType 36
bDescriptorSubtype 10 (CLOCK_SOURCE)
bClockID 4
bmAttributes 3 Internal programmable clock
bmControls 0x03
Clock Frequency Control (read/write)
bAssocTerminal 0
iClockSource 0
AudioControl Interface Descriptor:
bLength 17
bDescriptorType 36
bDescriptorSubtype 2 (INPUT_TERMINAL)
bTerminalID 1
wTerminalType 0x0101 USB Streaming
bAssocTerminal 0
bCSourceID 4
bNrChannels 6
bmChannelConfig 0x0000003f
Front Left (FL)
Front Right (FR)
Front Center (FC)
Low Frequency Effects (LFE)
Back Left (BL)
Back Right (BR)
iChannelNames 0
bmControls 0x0000
iTerminal 0
AudioControl Interface Descriptor:
bLength 34
bDescriptorType 36
bDescriptorSubtype 6 (FEATURE_UNIT)
bUnitID 2
bSourceID 1
bmaControls(0) 0x0000000f
Mute Control (read/write)
Volume Control (read/write)
bmaControls(1) 0x0000000f
Mute Control (read/write)
Volume Control (read/write)
bmaControls(2) 0x00000000
bmaControls(3) 0x00000000
bmaControls(4) 0x00000000
bmaControls(5) 0x00000000
bmaControls(6) 0x00000000
iFeature 0
AudioControl Interface Descriptor:
bLength 12
bDescriptorType 36
bDescriptorSubtype 3 (OUTPUT_TERMINAL)
bTerminalID 3
wTerminalType 0x0301 Speaker
bAssocTerminal 0
bSourceID 2
bCSourceID 4
bmControls 0x0000
iTerminal 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 0
bInterfaceClass 1 Audio
bInterfaceSubClass 2 Streaming
bInterfaceProtocol 32
iInterface 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 1
bNumEndpoints 2
bInterfaceClass 1 Audio
bInterfaceSubClass 2 Streaming
bInterfaceProtocol 32
iInterface 0
AudioStreaming Interface Descriptor:
bLength 16
bDescriptorType 36
bDescriptorSubtype 1 (AS_GENERAL)
bTerminalLink 1
bmControls 0x00
bFormatType 1
bmFormats 0x00000001
PCM
bNrChannels 6
bmChannelConfig 0x0000003f
Front Left (FL)
Front Right (FR)
Front Center (FC)
Low Frequency Effects (LFE)
Back Left (BL)
Back Right (BR)
iChannelNames 0
AudioStreaming Interface Descriptor:
bLength 6
bDescriptorType 36
bDescriptorSubtype 2 (FORMAT_TYPE)
bFormatType 1 (FORMAT_TYPE_I)
bSubslotSize 2
bBitResolution 16
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 5
Transfer Type Isochronous
Synch Type Asynchronous
Usage Type Data
wMaxPacketSize 0x024c 1x 588 bytes
bInterval 1
AudioStreaming Endpoint Descriptor:
bLength 8
bDescriptorType 37
bDescriptorSubtype 1 (EP_GENERAL)
bmAttributes 0x00
bmControls 0x00
bLockDelayUnits 0 Undefined
wLockDelay 0x0000
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 17
Transfer Type Isochronous
Synch Type None
Usage Type Feedback
wMaxPacketSize 0x0004 1x 4 bytes
bInterval 4
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 2
bNumEndpoints 2
bInterfaceClass 1 Audio
bInterfaceSubClass 2 Streaming
bInterfaceProtocol 32
iInterface 0
AudioStreaming Interface Descriptor:
bLength 16
bDescriptorType 36
bDescriptorSubtype 1 (AS_GENERAL)
bTerminalLink 1
bmControls 0x00
bFormatType 1
bmFormats 0x00000001
PCM
bNrChannels 6
bmChannelConfig 0x0000003f
Front Left (FL)
Front Right (FR)
Front Center (FC)
Low Frequency Effects (LFE)
Back Left (BL)
Back Right (BR)
iChannelNames 0
AudioStreaming Interface Descriptor:
bLength 6
bDescriptorType 36
bDescriptorSubtype 2 (FORMAT_TYPE)
bFormatType 1 (FORMAT_TYPE_I)
bSubslotSize 4
bBitResolution 24
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 5
Transfer Type Isochronous
Synch Type Asynchronous
Usage Type Data
wMaxPacketSize 0x0080 1x 128 bytes
bInterval 1
AudioStreaming Endpoint Descriptor:
bLength 8
bDescriptorType 37
bDescriptorSubtype 1 (EP_GENERAL)
bmAttributes 0x00
bmControls 0x00
bLockDelayUnits 0 Undefined
wLockDelay 0x0000
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 17
Transfer Type Isochronous
Synch Type None
Usage Type Feedback
wMaxPacketSize 0x0004 1x 4 bytes
bInterval 4
--endcollapse--