Nick's audio test system (AK5572 ADC 129KHz 32bit stereo balanced input)

I'll be putting in the order for the remaining components to complete phase 1 of my ADC audio test kit so I thought I'd start using this thread as my blog as I go through.

The overall concept is to have a USB connected ADC that operates stereo balanced at 192KHz 32bit. I'm using a AK5572 board from nilita that includes opamp front ends, biasing and some power supplies. However I need to add some bits:
a) a case - a hammond metal one should be a good RFI resisting starting point.
b) a clock source - for phase 1 I will use a channel of my sig gen with the board setup with 50Ohm termination, then phase 2 I'll look at options for an internal clock.
c) a STM32 based discovery board - this will operate as the PCM/i2s to USB bridge - I have to write some code as part of this blog to get this to work - naturally if I can get this to work it's available for everyone to use as they see fit.
d) a couple of power supplies - initially in phase 1 I will use my bench power supply (SMPS so noisy) but in phase two I'll switch to a small internal linear supply. The ADC board will need +15, -15 and +5V and a second +5V for the STM board.
e) AC coupling components - I'll use 10uF MKP4 caps. I'm not interested in DC for this system. The maximum of 4Vpp inline with XLR.
f) AC High voltage option - A simple 100Vpp or 400Vpp using a simple 9M+1M voltage divider ahead of the capacitors. Phase 1.5. This is for speaker or tube use and should give me 400Vpp.

The way the system will operate is that the ADC board will be configured as master with the MCLK connected to the sig gen at 24.576MHz (I may try higher if the Mac can cope). The ADC will then clock the PCM frame clock and bit clocks across into the STM discovery board where the STM will be configured as a slave.

Inside the STM, the system will be configured to detect the FIFO 1/2 full, causing an interrupt and that will then drive a DMA (no CPU used) to transfer the data from the FIFO into PSRAM ring buffer on the discovery board.

The STM will be configured to use OTG HS USB that's available on the discovery board. The USB OTG will declare itself as a USB Audio Class 2.0 (UAC2) device and the Mac will then see it as a USB Audio device like a microphone making it available to any app (Mac or Linux in a VM) such as REW etc. The important part here is the use of USB HighSpeed (480Mbit/sec) which the data rate requires and the use of UAC2 that supports the high data rate.

Internally the STM code will setup so that when a isochronous USB frame can be sent, the driver will DMA the data from the ring buffer in PSRAM directly into the USB output, in a diagram:

REW Application <-- MAC USB <------USB HighSpeed---- [STM USB] <--DMA-- Ring buffer <--DMA-- STM FIFO <- [GPIO PCM] <--- ADC

The first phase of this will be hard coded to 192KHz 32bit but as the ADC also offers control via i2c, I can also add soft control and then expose that control via the UAC2 control commands. This soft control will allow me to alter the rate and other configuration via REW etc. However a hard coded rate first will be enough.

This should all sit in a large steel case and as I progress I will keep an eye on noise etc but for now this should help keep noise down. If need be I'll address noise with additional shielding.

Testing the ADC board so far has been successful:

IMG_0697.jpg
 
Congrats on a successful test. What is the purpose of this device? Good to see your technical ability

Digital Osciloscopes = fast low resolution sampling (ie 200MHz 8 bit samples), to show precise time.
Audio Spectrum Analyser = slower high resolution sampling (ie 192KHz 32 bit samples), to show precision over audio frequency.

So you can see the two devices are for separate reasons. I'm a beginner - so there's still plenty of things that can go right or wrong 🙂 The ADC board is prebuilt although I don't see the components being that difficult to put together, the positioning and effort to make a low noise PCB is something that should not be taken lightly.

I have a complicated hybrid tube-solid state headphone amp I'm building. This will help characterise it. The ADC has been tested against an Audio Precision audio analyser and gives a decent performance - so the hope here is to be able todo THD and other measurements. The ADC far lower noise floor and higher range than the scope.

At a later date I many also make some PCBs etc and add a DAC for output (ie being able to drive a high resolution signal out) although for now the signal generator is good enough at 14bit 60Mhz. However for now this is the simplest route to get something working in a DIY way.
 
So the parts are ordered for phase 1 so I should receive them sometime this week. This includes a big 10"x7"x3.8" steel case so it will sit under the scope and have enough room for a power supply and DAC at a later date.

I've also been playing with the pin configurations on the STM32 discovery board to ensure I could have a connection out through the Arduino pin connections. This can be done (after dumping the alternative pin configurations from the STMCube IDE to cross check).
I can see why others have moved away from the board to use their own. Although a nice starting point, board presents a restriction due to the additional devices present but for now the 17 layer board will be the fastest way to get this working. I suspect that with less components, that a 4-6 layer board could be made. This guy (
) has made a small board for guitar pedal processing. He's also done some nice work in making coding videos for the STM32.
 
So I spent this morning going through the USB Audio 2.0 standard and the USB 2.0 HighSpeed standard with a pad of paper. I now have a good enough handle on the relationships between the components, and ERD and understand the transfer of data (ie isochronous pipes, how SOF works etc).

Re-approaching the USB code generated by STMCube now makes alot more sense to where I will need to cut in the new code, how that can be done so I don't end up reinventing the wheel (code wise).

The CubeIDE seems to copy the Apple Xcode in look and feel - I've used that for many years in the last so it seems very familiar. I'll code up something this afternoon however until the parts arrive next week I can't actually run it.

The main changes are within the usb_device.c which means creating a new AUDIO function pointer structure for the new UAC2 functions, the interface descriptors so that the device appears as a UAC2 devices on the USB bus. The first step will simply fill any 32 bit data with 0xdeadbeef test word.
Setting up those pieces should allow the device to appear on the USB bus as a HS UAC2 device when enumerated and return data at that rate asked - I will have a idle rate of 0 bandwidth alternative settings too but the main rate will be hard coded at 192KHz 24bit PCM which should give a transfer of 0xdeadbee of sample. As the device is stereo, it should 24 samples per micro-frame (192K/8K=24) and as each sample will be 32bits x 2 channels that's 192 bytes per micro-frame that needs transferring.

Next I need to workout the stereo aspect, ie if the system should have a bundle of two pipes - one for left and one for right. This would mean I would need to stride the transfers for DMA (I'm unsure if the STM32F723 has that) which would allow the transactions to be separated and then issued two per frame if the channels need separate transactions.
 
The STM32 DISCO board does have a ADC/DAC/HPA codec chip on board but it's limited in performance. The issue is that when you look at the Arduino settings for the pin alternate settings it's bit of a pain given the hard coded elements on the board:
Screenshot 2022-10-09 at 13.43.09.png
Screenshot 2022-10-09 at 13.44.39.png

Adding a i2s DAC on an OUT pipe would be relatively easy. The code files should be a drop in replacement for any auto generated code - same starting functions, different source files. This way the pins can be reassigned and the code regenerated without too much issue.
 
Starting with ADC is a good idea since slave input I2S is much simpler (no async feedback). DAC can be added later.

Here is my take on this:

dac-adc-usbi2s.jpg


ES9038Q2M DAC, ES9822PRO ADC and STM32F723 USB-I2S bridge. I also have other boards with same format (AK4490/4493 DACs, AK5394A ADC).
 
Precisely.

I see from the tracking that the order is probably going international about now.. (EDIT: tell a lie - it's just landed at stansted airport)

However yesterday I have put together my USB Descriptor - the fun is that it's spread across three documents and no examples! I can't test this but it seems accurate enough for now:
Code:
Device Descriptor (setup for HS only)
Device Qualifier Descriptor (required for HS)
- Configuration 1 descriptor
   - Standard Interface Association Descriptor (IAD) for HS
   - Interface 0 descriptor
      - Standard interface descriptor (for audio control)
      - Class-specific AC interface descriptor
      - Input Terminal descriptor
      - Feature Unit descriptor
      - Output Terminal descriptor
      - Clock Source descriptor
   - Interface 1 descriptor (for audio streaming)
      - Standard Audio Streaming descriptor (alternating setting 0, no bandwidth)
      - Standard Audio Streaming descriptor (full bandwidth)
      - Class-specific Header descriptor
      - Class-specific Audio streaming format descriptor (using type IV)
      - Standard Endpoint 1 IN descriptor
      - Class Specific Audio Streaming Endpoint 1 IN descriptor
   - Standard Strings descriptor

So that was a minefield and one I hope I have close enough to get working for enumeration.

I have two settings that allow for zero/disabled streaming and streaming at full rate. This means it may be a little picky about sharing the USB bus bandwidth but it's only about 10% utilisation.
 
So it seems very easy to crash MacOS with just plugging in the USB with a bad descriptor 😀 The new DISCO kit arrived with the new case and bits so bring on the kernel panics 🙂

I've commented out the UAC2 class-specific initialisation at the moment just to test the USB HS, and this is what I get:
Code:
    | |     +-o STM32 Audio Class@14330000  <class AppleUSBDevice, id 0x1000009d9, registered, matched, active, busy 0 (0 ms), retain 9>

STM32 Audio Class:
Product ID: 0x5740
Vendor ID: 0x0483 (STMicroelectronics)
Version: 2.00
Serial Number:
Speed: Up to 480 Mb/s
Manufacturer: STMicroelectronics
Location ID: 0x14330000 / 4
Current Available (mA): 500
Extra Operating Current (mA): 0

So that's definitely operating at HighSpeed.

So this is obviously something in my USC2 class, a break for dinner and I can look at what is causing the issue. I suspect it's some of the existing code I have left in so I will step through uncommenting the functionality to find the issues. Lets see how far I can get tonight.
 
Interesting the IDE is basically Eclipse under the hood - I used to use Eclipse years ago for professional programming.. so let's see if the debugging works.. what I'm hoping for is the connection will trigger a breakpoint.

Screenshot 2022-10-12 at 21.14.59.png
 
So I spent an age wrestling with the USB Audio 2.0 descriptors I found the issue - my gut feeling was there was nothing connecting the stream to the out connector. It wouldn't take anything other than saying it's USB2.0 Audio Class! Finally found that issue and I'm back to crashing the Mac, which sound bad, but that's actually a good sign. I suspect what's happening is that as the device isn't returning data at this time, the Mac is simply waiting for the packet of data. The result is that the desktop freezes but a YouTube video in the back ground still plays audio.

So I figure that fixing that return of data should then solve the freeze issue and we have the basis of the USB Audio 2.0 side of things.
 
So to stop the Mac freezing, I'll use a RPi4 to act as host then use the Mac with the STLINK to debug (I can set breakpoints on the STM and view variables etc). The next option is to cross compile the RPi kernel release (just the older 15.5.y so the networking works) and switch on the kernel USB debugging logging.
 
Unfortunately lsusb only shows the VCOM port (serial connection to the board seems to be enabled through the wizard during creating the project):
Bus 001 Device 014: ID 0483:5740 STMicroelectronics Virtual COM Port
No audio class.

So before I start playing around rebuilding kernels, we can use udevadm to monitor the USB activity from the command line, I pipe this to a file as a hint then plug in the operating STM32:
Code:
udevadm monitor -p

Code:
UDEV  [1448.307135] add      /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.2 (usb)
ACTION=add
DEVPATH=/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.2
SUBSYSTEM=usb
DEVNAME=/dev/bus/usb/001/014
DEVTYPE=usb_device
PRODUCT=483/5740/200
TYPE=239/2/1
BUSNUM=001
DEVNUM=014
SEQNUM=2013
USEC_INITIALIZED=1448306082
ID_VENDOR=STMicroelectronics
ID_VENDOR_ENC=STMicroelectronics
ID_VENDOR_ID=0483
ID_MODEL=STM32_Audio_Class
ID_MODEL_ENC=STM32\x20Audio\x20Class
ID_MODEL_ID=5740
ID_REVISION=0200
ID_SERIAL=STMicroelectronics_STM32_Audio_Class_
ID_SERIAL_SHORT=
ID_BUS=usb
ID_USB_INTERFACES=:010120:010220:
ID_VENDOR_FROM_DATABASE=STMicroelectronics
ID_MODEL_FROM_DATABASE=Virtual COM Port
ID_PATH=platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.2
ID_PATH_TAG=platform-fd500000_pcie-pci-0000_01_00_0-usb-0_1_2
DRIVER=usb
MAJOR=189
MINOR=13

So this sees the USB 2.0 descriptor as a USB Audio. Next to see if it sees the Audio 2.0.

Code:
UDEV  [1448.342134] add      /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card4/controlC4 (sound)
ACTION=add
DEVPATH=/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card4/controlC4
SUBSYSTEM=sound
DEVNAME=/dev/snd/controlC4
SEQNUM=2016
USEC_INITIALIZED=1448333181
ID_VENDOR=STMicroelectronics
ID_VENDOR_ENC=STMicroelectronics
ID_VENDOR_ID=0483
ID_MODEL=STM32_Audio_Class
ID_MODEL_ENC=STM32\x20Audio\x20Class
ID_MODEL_ID=5740
ID_REVISION=0200
ID_SERIAL=STMicroelectronics_STM32_Audio_Class_
ID_SERIAL_SHORT=
ID_TYPE=audio
ID_BUS=usb
ID_USB_INTERFACES=:010120:010220:
ID_USB_INTERFACE_NUM=00
ID_USB_DRIVER=snd-usb-audio
ID_PATH=platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.2:1.0
ID_PATH_TAG=platform-fd500000_pcie-pci-0000_01_00_0-usb-0_1_2_1_0
SYSTEMD_WANTS=sound.target
SYSTEMD_USER_WANTS=sound.target
MAJOR=116
MINOR=128
DEVLINKS=/dev/snd/by-id/usb-STMicroelectronics_STM32_Audio_Class_-00 /dev/snd/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.2:1.0
TAGS=:systemd:uaccess:
CURRENT_TAGS=:systemd:uaccess:

So the audio class seems to be seen for interface 0 but there's nothing more happening so I suspect we're not negotiating with the usb audio driver properly.

Code:
Oct 15 09:57:29 raspberrypi kernel: [ 2558.452519] usb 1-1.2: new high-speed USB device number 17 using xhci_hcd
Oct 15 09:57:29 raspberrypi kernel: [ 2558.582984] usb 1-1.2: config 1 has an invalid descriptor of length 1, skipping remainder of the config
Oct 15 09:57:29 raspberrypi kernel: [ 2558.583010] usb 1-1.2: config 1 has 2 interfaces, different from the descriptor's value: 3
Oct 15 09:57:29 raspberrypi kernel: [ 2558.583032] usb 1-1.2: config 1 has no interface number 1
Oct 15 09:57:29 raspberrypi kernel: [ 2558.583054] usb 1-1.2: config 1 interface 2 altsetting 1 has 0 endpoint descriptors, different from the interface descriptor's value: 1
Oct 15 09:57:29 raspberrypi kernel: [ 2558.583710] usb 1-1.2: New USB device found, idVendor=0483, idProduct=5740, bcdDevice= 2.00
Oct 15 09:57:29 raspberrypi kernel: [ 2558.583735] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
Oct 15 09:57:29 raspberrypi kernel: [ 2558.583755] usb 1-1.2: Product: STM32 Audio Class
Oct 15 09:57:29 raspberrypi kernel: [ 2558.583772] usb 1-1.2: Manufacturer: STMicroelectronics
Oct 15 09:57:29 raspberrypi kernel: [ 2558.583789] usb 1-1.2: SerialNumber:
Oct 15 09:57:29 raspberrypi kernel: [ 2558.585071] usb 1-1.2: 0:1 : does not exist

Kernel logs are showing some interesting points 😀 it looks like I have 3 interfaces in the descriptor (oops).. so lets fix that and see what happens.
 
Last edited:
Code:
Oct 15 10:36:39 raspberrypi kernel: [ 4908.301005] usb 1-1.2: new high-speed USB device number 21 using xhci_hcd
Oct 15 10:36:39 raspberrypi kernel: [ 4908.432072] usb 1-1.2: New USB device found, idVendor=0483, idProduct=5740, bcdDevice= 2.00
Oct 15 10:36:39 raspberrypi kernel: [ 4908.432100] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
Oct 15 10:36:39 raspberrypi kernel: [ 4908.432121] usb 1-1.2: Product: STM32 Audio Class
Oct 15 10:36:39 raspberrypi kernel: [ 4908.432139] usb 1-1.2: Manufacturer: STMicroelectronics
Oct 15 10:36:39 raspberrypi kernel: [ 4908.432155] usb 1-1.2: SerialNumber:
Oct 15 10:36:39 raspberrypi kernel: [ 4908.433744] usb 1-1.2: parse_audio_format_rates_v2v3(): unable to retrieve number of sample rates (clock 1)

Almost 😀 Previously I had a bmBitmap field that should have been 4 bytes and I had 1 byte in the descriptor. Doh.

This seems to be we're missing the Get/Set Sampling Frequency Request USB operation (which is documented in the Audio 2.0 specification)..
 
Yes, you need to implement most of Get/Set request functionality in order for the enumeration to succeed.

You need Get/Set Current for sampling frequency, clock validity (only Get), volume and mute and Get Range for sampling frequency and volume as minimum.
 
Last edited:
  • Like
Reactions: NickKUK
Good point - I've used Wireshark years ago, also for mobile network SS7 back in the day (mobile network control signalling) at work.

I'm currently writing the required control request handling. I'm using a external clock so that will be returning 192KHz for the input, the streaming output I have used the same clock but I assume a second clock (ie 8Khz) would be specified.