cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F446 Nucleo board USB FS some sort of frame issue

ECLEW.1
Associate II

I'm stuck. I'm able to send a 440 Hz sawtooth wave to output over USB from Nucleo board (device) to my computer (host).

The only problem is, there's this repetitive clicking / choppy sound in the resulting audio when I record with Audacity. The click happens relatively regularly, like 1.3x per second.

Another issue, I'm not sure if it's related or not: When I sweep the frequency from 440Hz to 880 Hz using a potentiometer, I can audibly hear the sweep happening twice. It's like USB is caching or reusing some buffer or something. (Tried to link code and audio examples but it's restricted because I'm a new member)

Maybe there's some clock issue? I think I read somewhere that the Nucleo's built-in crystal oscillator (8MHz) may not be suitable for USB due to some out-of-sync issues with USB communication.

0693W00000aHkOCQA0.png 

Really wish I could share my code and audio links, but it's restricted for me.

7 REPLIES 7
ECLEW.1
Associate II

I’m wondering if the issue is that the host isn’t receiving data for some reason (maybe a clock issue) and it resorts to replaying whatever contents are in its buffer.

ECLEW.1
Associate II

By the way, I'm using a Nucleo STM32F446RE.

If I want to go down the route of "the issue is the 8Mhz clock".

Well, I've observed that according to the BOM for this board, the 8Mhz clock (MCO) that is being "borrowed" from the ST-link portion of the board, the 8Mhz clock has a rating of 12pf load capacitance and uses two 20pf capacitors.

According to resources online, the calculation for the capacitors to use for a 12pf-load-capacitance clock (load capacitance, subtract 3pf to 7pf stray capacitance, and multiply result by 2) is anywhere between 10pf-18pf. In this case, the two 20pf capacitors are outside of this range.

On the other hand, STM32 documentation explains that an external oscillator can be attached (X3) and the rating for the oscillator should be 8Mhz, 16pf load capacitance, and two 20pf capacitors. In this case, the two 20pf capacitors fall within the calculated range based on load capacitance (18pf-26pf).

So I'm wondering if the Nucleo board's choice of capacitors based on the 12pf 8Mhz clock is inaccurate or not. And to stretch even further, is that what is causing my missed USB frames.

Anywhere, I read somewhere that attaching an external clock will result in identical performance, but I'm going to try it anyway.

ECLEW.1
Associate II
__ALIGN_BEGIN static uint8_t USBD_MIDI_CfgDesc[USB_MIDI_CONFIG_DESC_SIZ] __ALIGN_END
= {
/* Configuration 1 */
0x09, /* bLength */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType */
LOBYTE(USB_MIDI_CONFIG_DESC_SIZ), /* wTotalLength */
HIBYTE(USB_MIDI_CONFIG_DESC_SIZ), 0x02, /* bNumInterfaces */
0x01, /* bConfigurationValue */
0x00, /* iConfiguration */
//#if (USBD_SELF_POWERED == 1U)
//  0xC0,                                 /* bmAttributes: Bus Powered according to user configuration */
//#else
//  0x80,                                 /* bmAttributes: Bus Powered according to user configuration */
//#endif /* USBD_SELF_POWERED */
		0xC0,
		USBD_MAX_POWER, /* MaxPower (mA) */
		/* 09 byte*/
 
		/* USB Speaker Standard interface descriptor */
		AUDIO_INTERFACE_DESC_SIZE, /* bLength */
		USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
		0x00, /* bInterfaceNumber */
		0x00, /* bAlternateSetting */
		0x00, /* bNumEndpoints */
		USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */
		AUDIO_SUBCLASS_AUDIOCONTROL, /* bInterfaceSubClass */
		AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */
		0x00, /* iInterface */
		/* 09 byte*/
 
		/* USB Speaker Class-specific AC Interface Descriptor */
		AUDIO_INTERFACE_DESC_SIZE, /* bLength */
		AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
		AUDIO_CONTROL_HEADER, /* bDescriptorSubtype */
		0x00, /* 1.00 *//* bcdADC */
		0x01, 0x1E, /* wTotalLength */
		0x00, 0x01, /* bInCollection */
		0x01, /* baInterfaceNr */
		/* 09 byte*/
 
		/* USB Speaker Input Terminal Descriptor */
		AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength */
		AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
		AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */
		0x01, /* bTerminalID */
		0x01, /* wTerminalType microphone   0x0201 */
		0x02, 0x00, /* bAssocTerminal */
		0x01, /* bNrChannels */
		0x00, /* wChannelConfig 0x0000  Mono */
		0x00, 0x00, /* iChannelNames */
		0x00, /* iTerminal */
		/* 12 byte*/
 
		/* USB Speaker Output Terminal Descriptor */
		0x09, /* bLength */
		AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
		AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */
		0x02, /* bTerminalID */
		0x01, /* wTerminalType  0x0101 , this used to be 0x0301 because it was originally
		 prorammed for powering a speaker perhaps.  consult  Universal Serial Bus Device Class Definition for Terminal Types.pdf*/
		0x01, 0x00, /* bAssocTerminal */
		0x01, /* bSourceID */
		0x00, /* iTerminal */
		/* 09 byte */
 
		/* USB Speaker Standard AS Interface Descriptor - Audio Streaming Zero Bandwidth */
		/* Interface 1, Alternate Setting 0                                              */
		AUDIO_INTERFACE_DESC_SIZE, /* bLength */
		USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
		0x01, /* bInterfaceNumber */
		0x00, /* bAlternateSetting */
		0x00, /* bNumEndpoints */
		USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */
		AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */
		AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */
		0x00, /* iInterface */
		/* 09 byte*/
 
		/* USB Speaker Standard AS Interface Descriptor - Audio Streaming Operational */
		/* Interface 1, Alternate Setting 1                                           */
		AUDIO_INTERFACE_DESC_SIZE, /* bLength */
		USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
		0x01, /* bInterfaceNumber */
		0x01, /* bAlternateSetting */
		0x01, /* bNumEndpoints */
		USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */
		AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */
		AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */
		0x00, /* iInterface */
		/* 09 byte*/
 
		/* USB Speaker Audio Streaming Interface Descriptor */
		AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength */
		AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
		AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */
		0x02, /* bTerminalLink */
		0x01, /* bDelay */
		0x01, /* wFormatTag AUDIO_FORMAT_PCM  0x0001 */
		0x00,
		/* 07 byte*/
 
		/* USB Speaker Audio Type III Format Interface Descriptor */
		0x0B, /* bLength */
		AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
		AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */
		AUDIO_FORMAT_TYPE_I, /* bFormatType */
		0x01, /* bNrChannels */
		0x02, /* bSubFrameSize :  2 Bytes per frame (16bits) */
		16, /* bBitResolution (16-bits per sample) */
		0x01, /* bSamFreqType only one frequency supported */
		AUDIO_SAMPLE_FREQ(USBD_AUDIO_FREQ), /* Audio sampling frequency coded on 3 bytes */
		/* 11 byte*/
 
		/* Endpoint 1 - Standard Descriptor */
		AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */
		USB_DESC_TYPE_ENDPOINT, /* bDescriptorType */
		AUDIO_IN_EP, /* bEndpointAddress 1 in endpoint */
//				USBD_EP_TYPE_BULK,
//				USBD_EP_TYPE_INTR,
		USBD_EP_TYPE_ISOC,
		//				0b00001101, /* bmAttributes */
//  AUDIO_PACKET_SZE(USBD_AUDIO_FREQ),    /* wMaxPacketSize in Bytes (Freq(Samples)*2(Stereo)*2(HalfWord)) */
//  (uint8_t)(((USBD_AUDIO_FREQ * 2U * 2U) / 1000U) & 0xFFU),
//  (uint8_t)((((USBD_AUDIO_FREQ * 2U * 2U) / 1000U) >> 8) & 0xFFU),
		MIC_PACKET_SZE(USBD_AUDIO_FREQ),
		AUDIO_FS_BINTERVAL, /* bInterval */
		0x01U, /* bRefresh */
		0x00, /* bSynchAddress */
		/* 09 byte*/
 
		/* Endpoint - Audio Streaming Descriptor */
		AUDIO_STREAMING_ENDPOINT_DESC_SIZE, /* bLength */
		AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */
		AUDIO_ENDPOINT_GENERAL, /* bDescriptor */
		0x00, /* bmAttributes */
		0x00, /* bLockDelayUnits */
		0x00, /* wLockDelay */
		0x00,
/* 07 byte*/
};

ECLEW.1
Associate II

After reading more about USB audio streaming (in particular, 1.) UM2195 User manual USB device audio streaming Expansion Package for STM32Cube and 2.) Universal Serial Bus

Specification)

it sounds like clock drift isn't something that can be cured simply by using the most accurate clock, because there will always be a minuscule amount of drift.

It sounds like there are three ways to alleviate clock drift: asynchronous, synchronous, and adaptive.

I've tried setting bmAttributes as synchronous, but that didn't help.

Maybe I need to use the same epType (same bmAttributes) in my call to USBD_LL_OpenEP.

Or maybe there is something else I need to be doing.

ECLEW.1
Associate II

One thing I noticed is I'm only calling USBD_LL_Transmit 333.3 times per second, as opposed to matching the SOF trigger at 1000 times per second.

My SOF handler function is being triggered 1000 times per second, but I have some transmission logic that reduces the amount of times that I actually call USBD_LL_Transmit to 333.3 times per second...

Actually, I got this throttling logic code directly from ST: X-CUBE-USB-AUDIO

ACTIVE STM32 USB Device Audio Streaming software expansion for STM32Cube. I don't understand the meaning behind a 1000/3 times per second execution schedule.

I'm wondering if the throttling logic might be the problem, so I tried bypassing the throttling logic and calling USBD_LL_Transmit 1000 times per second instead, but it resulted in strange behavior, namely, the audio would come out really condensed, followed by silence. It's as if the sample rate was not correct, because the resulting audio was a few frequencies higher than expected.

ECLEW.1
Associate II
static uint8_t USBD_MIDI_IsoINIncomplete(USBD_HandleTypeDef *pdev,
		uint8_t epnum) {
//	  UNUSED(pdev);
//	  UNUSED(epnum);
 
	USB_OTG_GlobalTypeDef *USBx = USB_OTG_FS;
	uint32_t USBx_BASE = (uint32_t) USBx;
	fnsof = (USBx_DEVICE->DSTS & USB_OTG_DSTS_FNSOF) >> 8;
 
	if (epnum == (AUDIO_IN_EP & 0x7F)) {
		if (tx_flag == 1U) {
			tx_flag = 0U;
			USBD_LL_FlushEP(pdev, AUDIO_IN_EP);
		}
	}
 
	return (uint8_t) USBD_OK;
}
 
static uint8_t USBD_MIDI_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum) {
//	UNUSED(pdev);
//	UNUSED(epnum);
 
	if (epnum == (AUDIO_IN_EP & 0x7F)) {
		tx_flag = 0U;
	}
	return (uint8_t) USBD_OK;
}
 
static uint8_t USBD_MIDI_SOF(USBD_HandleTypeDef *pdev) {
//	UNUSED(pdev);
 
	USBD_MIDI_HandleTypeDef *haudio;
	uint8_t retval = USBD_OK;
 
	haudio = (USBD_MIDI_HandleTypeDef*) pdev->pClassDataCmsit[pdev->classId];
	if (haudio != NULL) {
	} else {
		return;
	}
 
	if (tx_flag == 0U) {
		USB_OTG_GlobalTypeDef *USBx = USB_OTG_FS;
		uint32_t USBx_BASE = (uint32_t) USBx;
		uint32_t volatile fnsof_new = (USBx_DEVICE->DSTS & USB_OTG_DSTS_FNSOF)
				>> 8;
 
		if ((fnsof & 0x1) == (fnsof_new & 0x1)) {
			if (haudio != NULL && haudio->data_ready_flag == 0) {
				haudio->in_buffer_half = !haudio->in_buffer_half; // also serves as init to 1 or 0
				haudio->data_ready_flag = 1;
				uint16_t prev = (AUDIO_IN_PACKET / 2) * !haudio->in_buffer_half;
				USBD_LL_Transmit(pdev, AUDIO_IN_EP,
						(uint8_t*) (haudio->in_buffer + prev), AUDIO_IN_PACKET);
				/* Block transmission until it's finished. */
				tx_flag = 1U;
			}
		}
	}
 
	return (uint8_t) USBD_OK;
}

ECLEW.1
Associate II

So I sort of figured out what was going wrong.

Although my SOF callback was getting called every 1ms, USB was really only ready for data transmission every 3ms (the reason for this I'm still not sure, what would like to find out at some point).

So rather than Transmitting 1ms of audio data per transmission, I needed to Transmit 3ms of audio data.

So I just multiplied the size of my buffer by 3, and now it works.

I noticed there's a considerable latency between the MCU and monitoring through my DAW. I've experienced this with other audio devices (e.g. Roland boutique synthesizers has audio over USB). Perhaps they had a latency in the scale of 10s or 100s of milliseconds. I'm guessing they use High Speed USB instead of Full Speed. I'm just guessing because I just started using a USB monitoring app and saw that my FocusRite Scarlett uses High Speed USB. Perhaps High Speed USB is the way to go.

The audio is also not perfect. It's has a couple clicks here or there. Nothing like the repetitive, consistent 1.1hz clicks I was experiencing though. Maybe if I increase my buffer size and slow down the SOF interval (if that's possible), it will sound clearer.