cancel
Showing results for 
Search instead for 
Did you mean: 

USB ZLP send on STM32F429 does not work!

dmitrygr
Associate II

Has *ANYONE* *EVER* gotten USB to work on STM32 without linking in the thousands of lines that Cube framework is?

Calling the documentation inadequate is an injustice to that word! The quality of the documentation approaches negative inf! And the cube code doesn't seem to work in DMA mode...

For example, RXFLVL is *NEVER* set in DMA mode. Ever, but thenwho pops status via GRXSTSP? your guess is as good as mine. And when to do that?! HAHA! now knowing that is simply asking too much

And how do you accomplish this in DMA mode?

3. The application must read the 2 words of the SETUP packet from the receive FIFO.
4. The application must read and discard the Setup stage done word from the receive
FIFO.

No idea! None!

Here, i'll quote you the ENTIRETY of what the doc says on the DMA mode

35.13.4 DMA mode
The OTG host uses the AHB master interface to fetch the transmit packet data (AHB to
USB) and receive the data update (USB to AHB). The AHB master uses the programmed
DMA address (HCDMAx register in host mode and DIEPDMAx/DOEPDMAx register in
peripheral mode) to access the data buffers.

And what the hell is this in cube code?

uint32_t gSNPSiD = *(__IO uint32_t *)(&USBx->CID + 0x1U);   // WTF, ST, document your ****!!!
[...]
    if ((gSNPSiD > USB_OTG_CORE_ID_300A) &&
[...]

All I want is to send a ZLP, but...nothing happens! i even scoped the bus. The chip keeps responsing to polls from host with a NAK on EP0... forever.

Now, this right now may be a hobby project, and STM is not likely to make $$$ on it, BUT every real place i've worked (Lab126, Kno, Google) I got to be part of parts selection. And given this experience, I'll be screaming "NOOO STM32!!!! in every part selection meeting from now on.

Code:

static bool usbEpSendInternal(uint8_t epNumIn, const void *data, uint32_t len)
{
	USB_OTG_INEndpointTypeDef *epIn = usbPrvGetInEpPtr(epNumIn);
	uint32_t epSz = usbPrvGetEpSz(true, epNumIn, epIn->DIEPCTL);
	uint32_t numPackets = (len + (epSz - 1)) / epSz;
	struct UsbGlobals *g = globalsGet();
	
	
	if (!len)
		numPackets = 1;	//ZLP
	
	if (!epNumIn && numPackets > 1) {
		logw("multi-packet tx impossible for EP0.in\n");
		return false;
	}
	
	if (numPackets > (USB_OTG_DIEPTSIZ_PKTCNT_Msk >> USB_OTG_DIEPTSIZ_PKTCNT_Pos) || len > (USB_OTG_DIEPTSIZ_XFRSIZ_Msk >> USB_OTG_DIEPTSIZ_XFRSIZ_Pos)) {
		logw("TX too big: %u/%u > %u/%u\n", len, numPackets, (USB_OTG_DIEPTSIZ_XFRSIZ_Msk >> USB_OTG_DIEPTSIZ_XFRSIZ_Pos), (USB_OTG_DIEPTSIZ_PKTCNT_Msk >> USB_OTG_DIEPTSIZ_PKTCNT_Pos));
		return false;
	}
	
	epIn->DIEPINT |= 0x39ff;
	epIn->DIEPDMA = (uintptr_t)data;
	epIn->DIEPTSIZ = (epIn->DIEPTSIZ &~ (USB_OTG_DIEPTSIZ_PKTCNT_Msk | USB_OTG_DIEPTSIZ_XFRSIZ_Msk)) | (numPackets << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | (len << USB_OTG_DIEPTSIZ_XFRSIZ_Pos);
	
	OTG_HS_DEV->DAINTMSK |= 1 << epNumIn;
	epIn->DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_USBAEP;
	
	
	
	
	
	logi("sent\n");
 
	return true;
}

code looks about like that. sending the descriptor works, but i cannot get a ZLP to send. What gives?!

endpoint stays enabled DIEPCTL top bit (EPENA) keeps set, while core is sending NAKs on the bus)!! The INEPNE bit is set, clearing it using CNAK has no effect!

1 REPLY 1

I've told in https://community.st.com/s/question/0D50X0000B6PwcBSQS/stm32f-429-usb-ep0-in-with-dma-does-not-work-always-sends-zero-bytes what GSNPSID is. I don't say it's OK it's undocumented, nor that the UNDOCUMENTED different actions based on IP version are OK. And I've been ranting here on the state of documentation of Synopsys OTG for like 2 or 3 years.

However, as I also told, I never used DMA in it. I don't see anything wrong with your code as far as I can judge. My only recommendation is to gulp the frog, look at the Cube code and follow it to the last letter. If there are several consecutive writes to some register, don't shortcut it.

JW

PS. A stylistic one: I know the *effect* of if (!epNumIn) is the same as that of if (epNumIn == 0); however, ! is to be used on booleans. (C is at least as cripple of a programming language as the synopsys IP is a cripple of an USB hardware; abusing all the shortcuts resulting from flawed language design usually makes things worse. And it's not the 70s anymore, we can afford to spend the extra bytes of source code and it's not 80 characters fitting on the teleprinter's line anymore). For future maintainability, I'd recommend something like #define USB_EP0 0 and then if (epNumIn == USB_EP0) - much easier to search for all instances where EP0 matters.