This is a bit of an open-ended question so apologies for being fairly vague.
I have an STM32F767ZI chip on a Nucleo-144 board. I want to use it to:
I understand from the USB specification (file here: https://www.usb.org/document-library/usb-20-specification, relevant material is in the main "usb_20.pdf" file, page 73 or section 5.12.4.2) that one "optimal" way to achieve this is to send feedback from the STM32 chip to the PC that sends the USB signal. Quoting from the spec:
"An asynchronous sink must provide explicit feedback to the host by indicating accurately what its desired
data rate (Ff) is, relative to the USB (micro)frame frequency. This allows the host to continuously adjust the
number of samples sent to the sink so that neither underflow or overflow of the data buffer occurs."
This makes sense, but I'm finding it really really hard to reverse engineer any of the projects that I've found that do this. A couple of examples I've found:
The problem I have is that I can't run either of these projects because they're made for different boards, so reverse engineering by stepping through them with a debugger isn't possible. This makes it tough because I don't know C (a minor blocker which I'm working on!).
Another useful looking resource is this: https://www.st.com/resource/en/appl...mple-with-stm32f0-mcus-stmicroelectronics.pdf - "USB audio bridge example with STM32F0 MCUs". This looks like exactly the sort of thing I'm after, except there is seemingly no code example, and my STM32F7 has no 48MHz clock (as far as I can tell), and there's no mention of a CRS (the clock recovery system) in the datasheet.
Does anyone know of any resources that I can dig into that might give some more insight into this? I'm interested in application notes, code examples, forum threads, papers etc. regardless of whether they apply to STM32 - I think I need to try and absorb whatever I can get my hands on at the moment! Thanks in advance.
I have an STM32F767ZI chip on a Nucleo-144 board. I want to use it to:
- Receive an audio stream from my computer via USB
- Do some processing on the audio e.g. mono sum and then some high/low pass filtering per channel
- Output to a DAC (in my case the TI PCM5102A)
I understand from the USB specification (file here: https://www.usb.org/document-library/usb-20-specification, relevant material is in the main "usb_20.pdf" file, page 73 or section 5.12.4.2) that one "optimal" way to achieve this is to send feedback from the STM32 chip to the PC that sends the USB signal. Quoting from the spec:
"An asynchronous sink must provide explicit feedback to the host by indicating accurately what its desired
data rate (Ff) is, relative to the USB (micro)frame frequency. This allows the host to continuously adjust the
number of samples sent to the sink so that neither underflow or overflow of the data buffer occurs."
This makes sense, but I'm finding it really really hard to reverse engineer any of the projects that I've found that do this. A couple of examples I've found:
The problem I have is that I can't run either of these projects because they're made for different boards, so reverse engineering by stepping through them with a debugger isn't possible. This makes it tough because I don't know C (a minor blocker which I'm working on!).
Another useful looking resource is this: https://www.st.com/resource/en/appl...mple-with-stm32f0-mcus-stmicroelectronics.pdf - "USB audio bridge example with STM32F0 MCUs". This looks like exactly the sort of thing I'm after, except there is seemingly no code example, and my STM32F7 has no 48MHz clock (as far as I can tell), and there's no mention of a CRS (the clock recovery system) in the datasheet.
Does anyone know of any resources that I can dig into that might give some more insight into this? I'm interested in application notes, code examples, forum threads, papers etc. regardless of whether they apply to STM32 - I think I need to try and absorb whatever I can get my hands on at the moment! Thanks in advance.
You are correct in that asynchronous UAC requires a feedback to be sent from the device to the host. How this is normally done with STM32 MCUs is to implement a SOF (start of frame) callback handler which is invoked from the USB OTG interrupt handler (see stm32f7xx_hal_pcd.c). This SOF callback handler calculates and sends the desired data rate to host. IIRC STM32Cube can generate a stub for this handler.
Your STM32F767 Nucleo board is capable of USB FS speeds only since it does not have a USB HS PHY so you are basically limited to UAC1. But the STM32Cube generated code is also for UAC1 so that should be a good starting point. You could also use the code in this thread as starting point:
https://www.diyaudio.com/community/...e-achieved-with-stm32-microcontroller.293603/
Regarding your audible artefacts I suggest trying 48k sampling rate if your host is Win10. IME it can work quite well even without async feedback.
IME the easiest approach is to use a ring buffer for sample data. Async feedback is then based on balancing the write and read pointers of this buffer.
Your STM32F767 Nucleo board is capable of USB FS speeds only since it does not have a USB HS PHY so you are basically limited to UAC1. But the STM32Cube generated code is also for UAC1 so that should be a good starting point. You could also use the code in this thread as starting point:
https://www.diyaudio.com/community/...e-achieved-with-stm32-microcontroller.293603/
Regarding your audible artefacts I suggest trying 48k sampling rate if your host is Win10. IME it can work quite well even without async feedback.
IME the easiest approach is to use a ring buffer for sample data. Async feedback is then based on balancing the write and read pointers of this buffer.
Last edited:
Thanks for the thoughts, really appreciated! I'll try and digest jmf13's code on page 14 of that thread.
Yep I'm using 48k (on linux, but I don't believe it should make too much difference as this is all UAC1 and native / pretty well supported now as far as I know). When clock is very close to 48k the artefacts happen every ~6.9 minutes and happen for a couple of seconds (like a needle tearing across vinyl), when clock is further from 48k they are more regular but don't last as long.
I've seen reference to the ring buffer approach but I don't think I understand it, and I think I'm too new to C to try and work one out without having an example in front of me. If I've understood correctly this approach essentially says "if the read pointer has advanced more than the write, do X" and I''m guessing X would be something like "drop the excess data that's been read".
EDIT: Oh it looks like jmf13's example is using a ring buffer (https://github.com/jmf13/USB_Async_...aaaf4a3dc0ade1703547/Src/usbd_audio_if.c#L291) - is that the sort of concept you mean? Would I be right in thinking the explicit feedback should keep things synchronised the majority of the time, and that in this particular implementation the ring buffer is more a fallback mechanism?
Yep I'm using 48k (on linux, but I don't believe it should make too much difference as this is all UAC1 and native / pretty well supported now as far as I know). When clock is very close to 48k the artefacts happen every ~6.9 minutes and happen for a couple of seconds (like a needle tearing across vinyl), when clock is further from 48k they are more regular but don't last as long.
I've seen reference to the ring buffer approach but I don't think I understand it, and I think I'm too new to C to try and work one out without having an example in front of me. If I've understood correctly this approach essentially says "if the read pointer has advanced more than the write, do X" and I''m guessing X would be something like "drop the excess data that's been read".
EDIT: Oh it looks like jmf13's example is using a ring buffer (https://github.com/jmf13/USB_Async_...aaaf4a3dc0ade1703547/Src/usbd_audio_if.c#L291) - is that the sort of concept you mean? Would I be right in thinking the explicit feedback should keep things synchronised the majority of the time, and that in this particular implementation the ring buffer is more a fallback mechanism?
Last edited:
The ring buffer approach means that if write pointer advances faster than read pointer (i.e. too much data coming in) then send feedback to host to slow data rate and vice versa. E.g. if the nominal data rate is 48k to slow down the host the feedback value would be less than that (e.g. 47.9k).