cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F303ZE -- Not receiving SET_ADDRESS after responding to GET_DESCRIPTOR.

ASnow.1
Associate II

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

1 ACCEPTED SOLUTION

Accepted Solutions
ASnow.1
Associate II

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.

View solution in original post

3 REPLIES 3
ASnow.1
Associate II

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.

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.1
Associate II

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.