Question

I'm working with the NXP LPC1788 microcontroller at the moment, and I'm trying to configure it to communicate with a Windows 7 PC using USB. I have limited experience with USB (I started learning the protocol at the start of this week), but I've worked with the LPC1788 for quite some time and have experience with other communication protocols (CAN, I2C, SSP).

I want to configure my microcontroller to act as a device and for the PC to act as the host. I'm suspecting that I will need to configure the microcontroller to communicate using full-speed interrupt transfers. Additionally, I will likely need to create my own vendor-specific USB driver for the PC later - I have not done this yet, and my descriptors are not properly configured.

My specific problem is that when I run my program and initialise/enable the microcontroller's USB device, I receive only two USB interrupts. The device interrupt status (DEVINTST) values in each case are:

0x19 - (FRAME, DEVSTAT, and CCEMPTY interrupts)
0x1  - (FRAME interrupt)

In the case of receiving the DEVSTAT interrupt, I read the following value from the serial interface engine using a GET DEVICE STATUS command:

0x19 - (CON (connected), SUS_CH (suspend state change), and RST (bus reset))

Using USBlyzer, I obtain only the following four packets: http://i.imgur.com/WRk7RBv.png.

I'm expecting that even without properly configured descriptors or a matching driver on the PC end, I should still be receiving more than this. I would have expected to be receiving a Get Descriptor request in Endpoint 0.

My main function only initialises my USB device and loops indefinitely.

The USB initialisation code is given below:

void USBInit()
{
  // Turn on power and clock
  CLKPWR_ConfigPPWR(CLKPWR_PCONP_PCUSB, ENABLE);

  // PLL0 clock is 96 MHz, usbclk should be 48 MHz.
  LPC_SC->USBCLKSEL = 0x102;

  // Configure USB pins.
  PINSEL_ConfigPin(0, 29, 1); // USB_D+1
  PINSEL_ConfigPin(0, 30, 1); // USB_D-1
  PINSEL_ConfigPin(1, 18, 1); // USB_UP_LED1
  PINSEL_ConfigPin(2,  9, 1); // USB_CONNECT1
  PINSEL_ConfigPin(1, 30, 2); // USB_VBUS 
  //PINSEL_ConfigPin(1, 19, 2); // USB_PPWR1

  PINSEL_SetPinMode(1, 30, PINSEL_BASICMODE_PLAINOUT);

  // Set DEV_CLK_EN and AHB_CLK_EN.
  LPC_USB->USBClkCtrl |= 0x12;

  // Wait until change is reflected in clock status register.
  while((LPC_USB->USBClkSt & 0x12) != 0x12);

  // Select USB port 1.
  LPC_USB->USBClkCtrl |= 0x8;
  while((LPC_USB->USBClkSt & 0x8) == 0);
  LPC_USB->StCtrl &= ~0x3;
  LPC_USB->USBClkCtrl &= ~0x8;

  // Reset the USB.
  USBReset();

  // Configure interrupt mode.
  writeSIECommandData(CMD_SET_MODE, 0);

  // Enable NVIC USB interrupts.
  NVIC_EnableIRQ(USB_IRQn);

  // Set device address to 0x0 and enable device & connection.
  USBSetAddress(0);
  USBSetConnection(TRUE);

  //printf("USB initialised\n");
  //printf("EpIntEn: 0x%x\n", LPC_USB->EpIntEn);

  // No errors here (SIE Error code: 0x0).
  USBPrintErrCode();

  // Packet sequence violation here (SIE Error code: 0x11).
  USBPrintErrCode();
}

The USB reset function:

void USBReset()
{
  LPC_USB->EpInd = 0;
  LPC_USB->MaxPSize = USB_MAX_PACKET_SIZE;
  LPC_USB->EpInd = 1;
  LPC_USB->MaxPSize = USB_MAX_PACKET_SIZE;
  while ((LPC_USB->DevIntSt & EP_RLZED_INT) == 0);

  LPC_USB->EpIntClr  = 0xFFFFFFFF;
  LPC_USB->EpIntEn   = 0xFFFFFFFF;
  LPC_USB->DevIntClr = 0xFFFFFFFF;
  LPC_USB->DevIntEn  = DEV_STAT_INT | EP_SLOW_INT | EP_FAST_INT;
}

The USB set address function:

void USBSetAddress(uint32_t addr)
{
  writeSIECommandData(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | addr));
  writeSIECommandData(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | addr));
}

The USB set connection function:

void USBSetConnection(uint32_t connect)
{
  writeSIECommandData(CMD_SET_DEV_STAT, DAT_WR_BYTE(connect ? DEV_CON : 0));
}

The USB interrupt service routine:

void USB_IRQHandler(void)
{
  uint32_t data;
  uint32_t interruptData = LPC_USB->DevIntSt;

  printf("InterruptData: 0x%x\n", interruptData);

  // Handle device status interrupt (reset, connection change, suspend/resume).
  if(interruptData & DEV_STAT_INT)
  {
    LPC_USB->DevIntClr = DEV_STAT_INT;
    writeSIECommand(CMD_GET_DEV_STAT);
    data = readSIECommandData(DAT_GET_DEV_STAT);
    printf("Data: 0x%x\n", data);

    // Device reset.
    if(data & DEV_RST)
    {
      USBReset();
      USBResetCore();
      printf("USB Reset\n");
    }

    // Connection change.
    if(data & DEV_CON_CH)
    {
      printf("Connection change\n");
      /* Pass */
    }

    // Suspend/resume.
    if(data & DEV_SUS_CH)
    {
      if(data & DEV_SUS)
      {
        printf("USB Suspend\n");
        USBSuspend();
      }
      else
      {
        printf("USB Resume\n");
        USBResume();
      }
    }

    return;
  }

  // Handle endpoint interrupt.
  if(interruptData & EP_SLOW_INT)
  {
    printf("Endpoint interrupt\n");
  }

  if(interruptData & EP_FAST_INT)
  {
    printf("Endpoint interrupt\n");
  }
}

The terminal output:

InterruptData: 0x19
Data: 0x19
USB Reset
USB Resume
InterruptData: 0x1

Edit: Using the SIE's GET ERROR CODE command, I found that I end up with an "unexpected packet" error by the end of the USB initialisation function. However, if I read the test register, I get back 0xA50F as expected, meaning that my communication with the SIE is working and my USB/AHB clocks are presumably configured correctly and running.

Was it helpful?

Solution

I managed to get an example USB project working in IAR Embedded Workbench. Based on that, I made two changes that mostly fixed my program:

  • I changed the configuration of my project from "semihosted" to "SWO" which made my print outputs a lot faster.

  • I removed the following line. It apparently misconfigured the USB clock rate.

    LPC_SC->USBCLKSEL = 0x102;
    
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top