cancel
Showing results for 
Search instead for 
Did you mean: 

Why is writing USB BTABLE being ignored or overwritten?

PWint
Associate III

I am trying to write a USB driver. I set-up EndPoint 0 when I get a RESET from the host. This writes both the EndPoint Register as well as the Buffer Descriptor Table.

I can see that the EndPoint 0 register is written correctly, but the Buffer Descriptor Table always reads back zero. It is as-if it cannot be written at this point. When I set a breakpoint, and step through the code, then it is set correctly, but this does not work, because by that time the host would have given up.

Is there anything special about the timing when setting up the buffer descriptor table or am I missing something else?

7 REPLIES 7

Tell us more. Which STM32, how do you clock the USB, how do you exactly write to the buffer and how do read it back. Make sure the buffers for In/Out endpoints don't overlap.

JW

PWint
Associate III

I use a STM32L433.

USB is clocked form HSI48, which is slaved to LSE using CRS.

My driver has a pointer to the USB SRAM:

uint8_t *bTable = (uint8_t*)USB_SRAM_ADDR;

My buffers start at offset 32 bytes in bTable. To write the registers, I cast to a 16-bit pointer (code below is slight simplification):

uint16_t *txAddr = (uint16_t*)bTable[0];
uint16_t *txCount = (uint16_t*)bTable[2];
uint16_t *rxAddr = (uint16_t*)bTable[4];
uint16_t *rxCount = (uint16_t*)bTable[6];

And then I write to these registers:

*txAddr = 32;
*txCount = 0;
*rxAddr = 96;
*rxCount = 0x8000 | (1 << 10);

To read the registers, I have a bit of test code:

volatile uint16_t tmp[4];
volatile uint16_t* ptrMem = (volatile uint16_t*)&bTable[0]; // Point to buffer table.
for (uint8_t i=0; i<4; i++)
{
  tmp[i] = ptrMem[i];
}

If I look at tmp[], after the buffer table has been written (still in the ISR handling RESET request), then it is always zero. If I set a breakpoint on the code before it writes to the buffer table, and step through it, then it works. I know I am not being interrupted, because all of this happens in the ISR.

I am only configuring EP0 at this stage. The rest of the buffer table is initialised to zero (also when I get a RESET). This should make it impossible to have overlapping buffers at the moment.

BTABLE register is set to zero.

I am tracing ISR events in to a memory buffer (for debugging), and I am getting lots of ESOF (expected), followed by (SUSPEND), and later (RESET).

It seems like part of my problem has to do with debugging and getting RESET requests on the bus.

I've changed my code to handle the SUSPEND request correctly (previously did not do anything but clear the ISR). The sequence of ISRs is now ESOF, SUSP, ESOF, WKUP, RESET, then lots of SOF, but I never trigger a CTR. After lots of SOF, the process repeats.

Reading BD and EP registers after reset, and just before reset, the register values for EP0 (starting at offset zero) are:

ADDR0_TX = 32

COUNT0_TX = 0

ADDR0_RX = 96

COUNT0_RX = 0x8400

The USB EPn registers are.

USB_EP0R = 0x3200.

USB_EP1R = 0x1

...

USB_EP7R = 7.

But, I keep on getting SOF and no CTR. Is there something wrong with my buffer tables, or am I doing something else wrong?

> If I set a breakpoint on the code before it writes to the buffer table, and step through it, then it works.

And if you set the breakpoint, but after stop you don't step but run, is there any difference?

I don't have much experience with the device-only USB in STM32, and also that only with the 'F0 (but it should be very similar if not the same).

JW

uint16_t *txAddr = (uint16_t*)bTable[0];

You mean,

uint16_t *txAddr = (uint16_t*)&bTable[0];

?

Compiler did not complain?

Post the actual code, and the respective disasm.

JW

I think I found what the problem was, but still not 100% sure. I was getting many resets, and my code had a bug which could make it look like it worked correctly the first time, but not the second time (I was writing to wrong EP registers the second time). So no, there is no difference between stepping, and setting the breakpoint after.

Yes, the '&' is there. I re-typed my code to show what it is doing conceptually.

My actual code to setup the EP (still a work in progress):

openEndpoint(uint8_t num, EPDirection direction,
                                  EPType type, uint16_t numBytes)
{
  // Configure Buffer Descriptor table:
  // ==================================
  EPInfo* ep = &epInfo[numEndpoints];
  numEndpoints++;
  
  ep->direction = direction;
  
  if (direction == EPDirection_In)
  {
    // Transmitting endpoint (IN for host).
    // ------------------------------------
    
    // Add address and count registers to buffer table:
    ep->txAddr[0] = (uint16_t*)&bTable[bTableOffset];
    ep->txCount[0] = (uint16_t*)&bTable[bTableOffset+2];
    bTableOffset += 4;
    
    // Configure address register to point in to buffer table data:
    *ep->txAddr[0] = bTableDataOffset;
    ep->txBuff[0] = &bTable[bTableDataOffset];
    bTableDataOffset += numBytes;
    *ep->txCount[0] = 0;
  }
  else
  {
    // Receiving endpoint (OUT for host).
    // ----------------------------------
    
    // Add address and count registers to buffer table:
    ep->rxAddr[0] = (uint16_t*)&bTable[bTableOffset];
    ep->rxCount[0] = (uint16_t*)&bTable[bTableOffset+2];
    bTableOffset += 4;
    
    // Configure address register to point in to buffer table data:
    *ep->rxAddr[0] = bTableDataOffset;
    ep->rxBuff[0] = &bTable[bTableDataOffset];
    bTableDataOffset += numBytes;
    /*uint8_t numBlocks = (numBytes >> 5) - 1;
    *ep->rxCount[0] = 0x8000 |               // Using 32-byte block sizes.
                      (numBlocks << 10);     // Number of 32-byte blocks available.
    */
    uint8_t numBlocks = numBytes >> 1;
    *ep->rxCount[0] = numBlocks << 10;
  }
  
  // Set the corresponding End Point Register:
  // =========================================
  uint8_t epType = 0;
  switch (type)
  {
    case EPType_Control:     epType = 0b01; break;
    case EPType_Bulk:        epType = 0b00; break;
    case EPType_Interrupt:   epType = 0b11; break;
    case EPType_Isochronous: epType = 0b10; break;
  }
 
  volatile uint16_t* USB_EPnR = &USB->EP0R + (num * 2);  // Get pointer to correct USB_EPnR where n is the endpoint num.
  uint16_t regValue =    (epType << 9) |      // Endpoint Type.
                      // USB_EP_KIND |        // Endpoint Kind.  Has specific meaning depending on EP Type (see RM0394).
                         (num << 0);          // Endpoint address/number.
  
  if (direction ==  EPDirection_In)
  {
    // Transmitting endpoint (IN for host).
    // ------------------------------------
    
    uint8_t epState;
    if (type == EPType_Isochronous)
    {
      // Isonchronous endpoints may only be disabled or valid. It starts as
      // disabled, and will change to valid if there is data to transmit.
      epState = kState_Disabled;
    }
    else
    {
      // All other endpoints are in NAK state to show they are valid, but do not
      // have data to transmit.
      epState = kState_NAK;
    }
    regValue |=    USB_EP_CTR_TX |    // Clear Correct TX bit by writing one to it.
                // USB_EP_DTOG_TX |   // DATA0 expected next.
                   (epState << 4);
  }
  else
  {
    // Receiving endpoint (OUT for host).
    // ----------------------------------
    regValue |=    USB_EP_CTR_RX |         // Clear Correct RX bit by writing one  to it.
                // USB_EP_DTOG_RX |        // DATA0 expected next.
                   (kState_Valid << 12);   // Endpoint State = Valid (ready for reception).
  }
  
  // XOR toggle bits with bits that should be set in regValue.
  // Non-toggle bits are written as-is.
  regValue = (*USB_EPnR & kToggleBits) ^ regValue;
  // Write the result back to USB_EPnR toggling the required bits so that the
  // value of USB_EPnR is that of original regValue:
  *USB_EPnR = regValue;
}