Skip to main content
Visitor II
February 20, 2023
Solved

STM32F303ZE -- Not receiving SET_ADDRESS after responding to GET_DESCRIPTOR.

  • February 20, 2023
  • 3 replies
  • 1840 views

Problem

I'm having an issue with my USB peripheral in the STM32F303ZE. Right now I'm capable of receiving the first request from the host after reset, which is a GET_DESCRIPTOR request. I populate the TX buffer and set the EP0R register to VALID for TX. I then get interrupted for an IN request, meaning the host ACKed my TX data (which I assume means the data was transmitted successfully) and then I receive an OUT status to finish the control transfer. And then I receive a reset and do this all over again two more times, resulting in the device failing to enumerate.

What confuses me is that, from experience programming USB on a different MCU, I know I should be receiving a reset partway through transferring my device descriptor, yet the entire request goes through and then I receive the reset afterwards. While it may be possible that the host is letting my transfer complete, I still don't receive a SET_ADDRESS setup packet afterwards. As I said above, it just repeats over and over. My hope here is to get help in receiving the SET_ADDRESS next.

Clocks

I've already verified the clocks are correct. I'm bypassing an 8MHz clock as HSE and multiplying it by 6 with the PLL to reach the required 48MHz for the USB peripheral. Also, I wouldn't receive the GET_DESCRIPTOR if the peripheral wasn't being clocked. I already struggled with trying to use an external oscillator that wasn't actually on the board... For reference though, this is my configuration

 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE;
 RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
 RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
 RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;
 RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
 HAL_RCC_OscConfig(&RCC_OscInitStruct)
 
 
 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1)
 
 PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB|RCC_PERIPHCLK_USART3
 |RCC_PERIPHCLK_ADC12|RCC_PERIPHCLK_TIM34;
 PeriphClkInit.Usart3ClockSelection = RCC_USART3CLKSOURCE_HSI;
 PeriphClkInit.Adc12ClockSelection = RCC_ADC12PLLCLK_DIV1;
 PeriphClkInit.USBClockSelection = RCC_USBCLKSOURCE_PLL;
 PeriphClkInit.Tim34ClockSelection = RCC_TIM34CLK_PLLCLK;
 HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit)

USB Code:

Bear with me, the code is in a very prototypish stage right now

Declarations

USB_TypeDef * USBz;		/* USB Address Handle */
 
typedef struct __attribute__((packed))
{
	__IO uint16_t ADDR_TX;
	__IO uint16_t COUNT_TX;
	__IO uint16_t ADDR_RX;
	__IO uint16_t COUNT_RX;
} BTableLayout;
volatile BTableLayout *my_btable;
volatile uint8_t buffer[64];
 
union Device{
DeviceDescriptor descriptor;
uint8_t data[18];
} device;
 
uint8_t address = 0;

Initialization

void myusb_Initialize(void)
{
 //Initialize Descriptors
 DescriptorInitialization();
 
 //Map USBz to registers
 USBz = USB;
 my_btable = (BTableLayout*)0x40006000;
 
 //Enable clocks
 __HAL_RCC_USB_CLK_ENABLE();
 
 //Initialize interrupts + remapping
 __HAL_REMAPINTERRUPT_USB_ENABLE();
 HAL_NVIC_SetPriority(USB_HP_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(USB_HP_IRQn);
 HAL_NVIC_SetPriority(USB_LP_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(USB_LP_IRQn);
 HAL_NVIC_SetPriority(USBWakeUp_RMP_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(USBWakeUp_RMP_IRQn);
 
 //Configure USB GPIO
 __HAL_RCC_GPIOG_CLK_ENABLE();
 
 GPIO_InitTypeDef GPIO_InitStruct = {0};
 
 GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
 GPIO_InitStruct.Alternate = GPIO_AF14_USB;
 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
 GPIO_InitStruct.Pin = GPIO_PIN_6;
 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
 
 HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_RESET);
 
 //Start USB peripheral
 USBz->CNTR = 1;	//Start transceiver
 
 for (int i = 0; i < 1000; i++)	//Delay according to datasheet
 {
 	__asm volatile("nop");
 }
 
 //De-assert reset
 USBz->CNTR = 0;
 
 //Clear pending interrupts
 USBz->ISTR = 0U;
 
 //Enable these USB Interrupts
 USBz->CNTR |= USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_WKUPM | USB_CNTR_SUSPM | USB_CNTR_ESOFM;
 
 //Enable pull up to start enumeration
 HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_SET);
}
 
void DescriptorInitialization(void)
{
	//Just device descriptor until I reach further requests
	device.descriptor.bLength = 18;
	device.descriptor.bDescriptorType = 0x01;
	device.descriptor.bcdUSB = 0x0110;
	device.descriptor.bDeviceClass = 0x00;
	device.descriptor.bDeviceSubClass = 0x00;
	device.descriptor.bDeviceProtocol = 0x00;
	device.descriptor.bMaxPacketSize0 = 64;
	device.descriptor.idVendor = 0x03ED;
	device.descriptor.idProduct = 0x2FF4;
	device.descriptor.bcdDevice = 0x0100;
	device.descriptor.iManufacturer = 0x00;
	device.descriptor.iProduct = 0x00;
	device.descriptor.iSerialNumber = 0x00;
	device.descriptor.bNumConfigurations = 1;
}

Read/Write Functions

void ReadEndpoint(uint8_t endpoint, uint8_t num_bytes)
{
 //Get offset to endpoint n RX buffer
 volatile uint8_t* location = (uint8_t*)(0x40006000 + ((endpoint * 8) + 4));
 
 //Point to RX buffer
 location = ((uint8_t*)0x40006000 + (*location));
 
 for (int i = 0; i < num_bytes; i++)
 {
 	buffer[i] = ((uint8_t*)location)[i];
 }
}
 
void WriteEndpoint(uint8_t endpoint, uint8_t* data_buffer, uint8_t num_bytes)
{
 //Get offset to endpoint n TX buffer
 volatile uint8_t* location = (uint8_t*)(0x40006000 + (endpoint*8));
 
 //Point to TX buffer
 location = ((uint8_t*)0x40006000 + (*location));
 
 for (int i = 0; i < num_bytes; i++)
 {
 	location[i] = data_buffer[i];
 }
 
 //Set STAT_TX to VALID. Mask to avoid toggling the toggle bits.
 switch(endpoint)
 {
 case 0:
 	USBz->EP0R = (1 << 4) | (USBz->EP0R & 0x8F9F);
 	break;
 case 1:
 	USBz->EP1R = (1 << 4) | (USBz->EP1R & 0x8F9F);
 	break;
 }
}

CONTINUATION OF CODE IN COMMENTS

    This topic has been closed for replies.
    Best answer by ASnow.1

    Restarted my pc, rebuilt, and it worked. Not sure how but it is working now. Sorry to whoever sees this in the future with the same issue.

    3 replies

    ASnow.1Author
    Visitor II
    February 20, 2023

    CONTINUATION

    Interrupts

    I know printing isn't great in interrupts. Although even without the prints the result is the same, so they're not interfering yet.

    void USB_LP_IRQHandler(void)
    {
     if (USBz->ISTR & USB_ISTR_CTR)
     {
     	EndpointCallback();
     }
     if (USBz->ISTR & USB_ISTR_RESET)
     {
     	USBz->ISTR = ~USB_ISTR_RESET;
     
     	USBz->BTABLE = 0;
     
     	//Configure endpoint as control, TX NAK, RX VALID
     	USBz->EP0R = USB_EP_CONTROL | (2 << 4) | (3 << 12);
     
     	//Configure BTable for endpoint 0
     	my_btable[0].ADDR_TX = (uint16_t)0x20;
     	my_btable[0].COUNT_TX = (uint16_t)0;
     	my_btable[0].ADDR_RX = (uint16_t)0x60;
     	my_btable[0].COUNT_RX = (uint16_t)0x8400;
     
     	//Enable USB interrupts and peripheral
     	USBz->CNTR |= USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_WKUPM | USB_CNTR_SUSPM | USB_CNTR_ESOFM;
     	USBz->DADDR = USB_DADDR_EF;
     
     	myprint("Reset Interrupt\r\n");
     }
     if (USBz->ISTR & USB_ISTR_PMAOVR)
     {
     	USBz->ISTR = ~USB_ISTR_PMAOVR;
     	myprint("PMAOVR Interrupt\r\n");
     }
     if (USBz->ISTR & USB_ISTR_ERR)
     {
     	USBz->ISTR = ~USB_ISTR_ERR;
     	//myprint("ERR Interrupt\r\n");
     }
     if (USBz->ISTR & USB_ISTR_SUSP)
     {
     	USBz->CNTR |= USB_CNTR_FSUSP;
     	USBz->ISTR = ~USB_ISTR_SUSP;
     	USBz->CNTR |= USB_CNTR_LPMODE;
     
     	myprint("SUSP Interrupt\r\n");
     }
     if (USBz->ISTR & USB_ISTR_WKUP)
     {
     	USBz->CNTR &= (uint16_t) ~(USB_CNTR_LPMODE);
     	USBz->CNTR &= (uint16_t) ~(USB_CNTR_FSUSP);
     	USBz->ISTR = ~USB_ISTR_WKUP;
     
     	USBz->CNTR |= USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_WKUPM | USB_CNTR_SUSPM | USB_CNTR_ESOFM | USB_CNTR_PMAOVRM | USB_CNTR_ERRM | USB_CNTR_SOFM;	//Enable these USB Interrupts
     	myprint("WKUP Interrupt\r\n");
     }
     if (USBz->ISTR & USB_ISTR_SOF)
     {
     	USBz->ISTR = ~USB_ISTR_SOF;
     }
     if (USBz->ISTR & USB_ISTR_ESOF)
     {
     	USBz->ISTR = ~USB_ISTR_ESOF;
     }
    }
     
    void EndpointCallback(void)
    {
     //Loop to take care of all endpoint interrupts
     while (USBz->ISTR & USB_ISTR_CTR)
     {
     	uint16_t ep = USBz->ISTR & USB_ISTR_EP_ID;
     	uint16_t dir = USBz->ISTR & USB_ISTR_DIR;
     
     	switch(ep)
     {
     case 0:	//Control
     	 if (dir) //OUT/SETUP
     	 {
     		//See if SETUP has been received
     		if (USBz->EP0R & USB_EP_SETUP)
     		{
     		 uint8_t num_bytes = my_btable[0].COUNT_RX;
     		 ReadEndpoint(0, num_bytes);
     
     		 USBz->EP0R = ((~USB_EP_CTR_RX) & USBz->EP0R) & 0x8F8F;
     
     		 SetupCallback();
     
     		 //Set RX to Valid
     		 USBz->EP0R = (1 << 12) | (USBz->EP0R & 0x9F8F);	
     		}
     		else if (USBz->EP0R & USB_EP_CTR_RX)
     		{
     		 myprint("OUT RECEIVED\r\n");
     		 USBz->EP0R = ((~USB_EP_CTR_RX) & USBz->EP0R) & 0x8F8F;
     uint8_t num_bytes = my_btable[0].COUNT_RX;
     
     		 if ((address > 0) && (num_bytes == 0))
     		 {
     //Set address and enable
     		 USBz->DADDR = address | (1 << 7);
     		 address = 0;
     		 }
     
     		 //Set RX to Valid
     		 USBz->EP0R = (1 << 12) | (USBz->EP0R & 0x9F8F);	
     		}
     	 }
     	 else //IN
     	 {
     	 myprint("IN RECEIVED\r\n");
     	 USBz->EP0R = ((~USB_EP_CTR_TX) & USBz->EP0R) & 0x8F8F;
     	 }
     	break;
     	case 1: //Endpoint
     	break;
     	}
     }
    }
     
    void SetupCallback(void)
    {
    	myprint("\tSETUP RECEIVED\r\n");
     
    	//Determine Request
    	uint8_t bmRequestType = buffer[0];
    	uint8_t bRequest = buffer[1];
    	uint16_t wValue = ((uint16_t*)buffer)[1];
    	uint16_t wIndex = ((uint16_t*)buffer)[2];
    	uint16_t wLength = ((uint16_t*)buffer)[3];
     
    	USBRequestDirection direction 	= (bmRequestType & 0x80) ? REQUEST_D2H : REQUEST_H2D;
    	USBRequestType type 		= (bmRequestType & 0x60) >> 5;
    	USBRequestRecipient recipient 	= bmRequestType & 0x1F;
     
    	//Handle Request
    	switch(bRequest)
    	{
    	case GET_STATUS:
    		myprint("\t\tGET_STATUS\r\n");
    		break;
    	case CLEAR_FEATURE:
    		myprint("\t\tCLEAR_FEATURE\r\n");
    		break;
    	case SET_FEATURE:
    		myprint("\t\tSET_FEATURE\r\n");
    		break;
    	case SET_ADDRESS:
    		address = wValue & 0x7F;
    		myprint("\t\tSET_ADDRESS\r\n");
    		break;
    	case GET_DESCRIPTOR:
    		myprint("\t\tGET_DESCRIPTOR\r\n");
     
    		//Put number of bytes to be transferred inside count_tx
    		my_btable[0].COUNT_TX = 18;
     
    		//Write data to PMA (sets endpoint transfer as valid)
    		WriteEndpoint(0, device.data, 18);
     
    		break;
    	case SET_DESCRIPTOR:
    		myprint("\t\tSET_DESCRIPTOR\r\n");
    		break;
    	case GET_CONFIGURATION:
    		myprint("\t\tGET_CONFIGURATION\r\n");
    		break;
    	case SET_CONFIGURATION:
    		myprint("\t\tSET_CONFIGURATION\r\n");
    		break;
    	case GET_INTERFACE:
    		myprint("\t\tGET_INTERFACE\r\n");
    		break;
    	case SET_INTERFACE:
    		myprint("\t\tSET_INTERFACE\r\n");
    		break;
    	case SYNCH_FRAME:
    		myprint("\t\tSYNCH_FRAME\r\n");
    		break;
    	default:
    		myprint("\t\tWE SHOULDN'T BE HERE IN THE SETUP\r\n");
    		break;
    	}
    }

    Serial Output

    SUSP Interrupt
    Reset Interrupt
    WKUP Interrupt
    Reset Interrupt
     SETUP RECEIVED
     GET_DESCRIPTOR
    IN RECEIVED
    OUT RECEIVED
    Reset Interrupt
     SETUP RECEIVED
     GET_DESCRIPTOR
    IN RECEIVED
    OUT RECEIVED
    Reset Interrupt
     SETUP RECEIVED
     GET_DESCRIPTOR
    IN RECEIVED
    OUT RECEIVED
    SUSP Interrupt

    I'm sure I'm missing something completely obvious but I've been scratching my head for a while now trying to figure it out. Life would be much easier with a USB analyzer. Thanks for any help. Also, I would like to mention that it is my goal to do this baremetal, so no HAL. Although I have attempted to use the HAL to troubleshoot this. I couldn't even get this far with the HAL. Not sure why.

    I suspect that maybe my data isn't sending correctly, but I'm not sure how to verify that without a USB analyzer. Or maybe I'm not dealing with the OUT status stage correctly. Or maybe my reset isn't happening fast enough to see the SET_ADDRESS setup packet. Not sure.

    Super User
    February 21, 2023

    Check/post the descriptor byte array. Try some known working example (in binary form, don't be fancy with the struct at this point).

    On Win, try wireshark, it may be half-usable with USB, google (or, as is the saying these days, chatgpt). Lin may or may not be more usable out of the box, Lin is said to be user friendly but we are not friends.

    Get an USB analyzer. Besides dedicated devices, some LA/oscilloscopes have USB decoders. You can try at this stage decode manually, to, if you are desperate enough, it's not fun but not impossible for the first few packets.

    JW

    ASnow.1AuthorAnswer
    Visitor II
    February 21, 2023

    Restarted my pc, rebuilt, and it worked. Not sure how but it is working now. Sorry to whoever sees this in the future with the same issue.