如何将USB DMA引擎用于LPC1788?

时间:2014-08-21 12:38:44

标签: c usb microcontroller lpc

我正在为恩智浦LPC1788微控制器开发代码,最近我一直在努力改进USB的工作方式。我目前的USB问题是它配置为从机模式,并且由于在正常操作中必须发送和接收的大量消息,CPU花费大部分时间处理USB,这会造成瓶颈。

我一直在尝试通过从从模式配置切换到DMA模式来解决此问题。我一直在使用示例项目来提供帮助,我认为我需要的大部分代码都已到位。

USB初始化工作正常,和以前一样。 问题是,只要我尝试将USB消息发送到我在DMA模式下配置的端点(通过启用EpDMAEn寄存器中的相应位),我得到该端点的USB系统错误中断。我能得到的唯一信息是:

  

如果在传输数据或获取数据时发生系统错误(AHB总线错误)   更新DD时,在该寄存器中设置相应的位。 SysErrIntSt是只读的   注册

在程序的这一点上,我没有触及UDCA或设置任何DMA描述符或类似过去的初始化。在我需要做任何事情之前,只要在USB总线上收到第一条消息就会发生这种错误,我相信。

我正在使用端点2 IN和OUT,它们是双缓冲批量端点,最大包大小为64字节。

如果我不使用DMA,我已经确认USB工作正常。

我已经确认,如果我完成初始化DMA引擎的过程但是在从模式而不是DMA模式下配置端点,USB工作正常。

我已确认Example Projects下的USB大容量存储示例 - > NXP - > LP17xx - >如果我使用默认配置,177x_8x CMSIS可以正常工作:

...
#define USB_DMA             1
#define USB_DMA_EP          0x00000000
...

如果我将其更改为:

,也会以同样的方式中断
...
#define USB_DMA             1
#define USB_DMA_EP          0x00000030 /* Endpoint 2 IN and OUT */
...

在USB硬件源文件的开头,我提出以下内容:

#ifdef USB_DMA

// Stores information received using DMA on OUT endpoints.
//uint8_t dataBufferOUT[DD_BUFFER_SIZE*MAX_PACKET_SIZE];
uint8_t *dataBufferOUT = (uint8_t*)DMA_BUF_ADR;

// Stores information transferred using DMA on IN endpoints.
//uint8_t dataBufferIN[DD_BUFFER_SIZE*MAX_PACKET_SIZE];
uint8_t *dataBufferIN = (uint8_t*)(DMA_BUF_ADR+DD_BUFFER_SIZE*
                                   USB_MAX_PACKET_SIZE);

// Current dataBufferOUT index;
uint16_t dataOUT;

// Current dataBufferIN index.
uint16_t dataIN;

#if defined (__CC_ARM)
#pragma arm section zidata = "USB_RAM"
uint32_t UDCA[USB_EP_NUM];                     /* UDCA in USB RAM */

uint32_t DD_NISO_Mem[4*DD_NISO_CNT];           /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT];            /* Iso DMA Descriptor Memory */
#pragma arm section zidata
#elif defined ( __ICCARM__ )
#pragma location = "USB_RAM"
uint32_t UDCA[USB_EP_NUM];                     /* UDCA in USB RAM */
#pragma location = "USB_RAM"
uint32_t DD_NISO_Mem[4*DD_NISO_CNT];           /* Non-Iso DMA Descriptor Memory */
#pragma location = "USB_RAM"
uint32_t DD_ISO_Mem [5*DD_ISO_CNT];            /* Iso DMA Descriptor Memory */

#else
uint32_t UDCA[USB_EP_NUM]__attribute__((section ("USB_RAM")));                     /* UDCA in USB RAM */

uint32_t DD_NISO_Mem[4*DD_NISO_CNT]__attribute__((section ("USB_RAM")));           /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]__attribute__((section ("USB_RAM")));            /* Iso DMA Descriptor Memory */
#endif /*__GNUC__*/
uint32_t udca[USB_EP_NUM];                     /* UDCA saved values */

uint32_t DDMemMap[2];                          /* DMA Descriptor Memory Usage */

#endif

我使用以下代码初始化USB外设:

void USBInit()
{  
  // 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 

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

  //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);

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

  // Reset the USB.
  USBReset();

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

  // TEMP.
  sendMessageFlag = 0;

#ifdef USB_DMA
  dataIN = 0;
  dataOUT = 0;
#endif
}

我的USB重置码:

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;

#ifdef USB_DMA
  LPC_USB->EpIntEn   = 0xFFFFFFFF ^ USB_DMA_EP;
#else
  LPC_USB->EpIntEn   = 0xFFFFFFFF;
#endif

  LPC_USB->DevIntClr = 0xFFFFFFFF;
  LPC_USB->DevIntEn  = DEV_STAT_INT | EP_SLOW_INT /*| EP_FAST_INT*/ ;

#ifdef USB_DMA
  uint32_t n;

  LPC_USB->UDCAH   = USB_RAM_ADR;
  LPC_USB->DMARClr = 0xFFFFFFFF;
  LPC_USB->EpDMADis  = 0xFFFFFFFF;
  LPC_USB->EpDMAEn   = USB_DMA_EP;
  LPC_USB->EoTIntClr = 0xFFFFFFFF;
  LPC_USB->NDDRIntClr = 0xFFFFFFFF;
  LPC_USB->SysErrIntClr = 0xFFFFFFFF;
  LPC_USB->DMAIntEn  = 0x00000007;
  DDMemMap[0] = 0x00000000;
  DDMemMap[1] = 0x00000000;
  for (n = 0; n < USB_EP_NUM; n++) {
    udca[n] = 0;
    UDCA[n] = 0;
  }
#endif
}

准备好后,用于运行USB:

void USBRun()
{
  USBSetConnection(TRUE);
}

最后,我的USB中断程序:

void USB_IRQHandler(void)
{
  OS_EnterInterrupt();

  uint32_t data, val, pIndex, lIndex, currEpisr;
  uint32_t interruptData = LPC_USB->DevIntSt;
#ifdef USB_DMA
  uint32_t dmaInterruptData = LPC_USB->DMAIntSt; 
#endif

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

  if (interruptData & ERR_INT)
  {
    writeSIECommand(CMD_RD_ERR_STAT);
    data = readSIECommandData(DAT_RD_ERR_STAT);
   // printf("Error data: 0x%x\n", data);
    //getchar();
  }

  // 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();
      }
    }

    OS_LeaveInterrupt();
    return;
  }

  // Handle endpoint interrupt.
  if(interruptData & EP_SLOW_INT)
  {  
    //printf("Endpoint slow\n");
    data = LPC_USB->EpIntSt;
    //printf("EP interrupt: 0x%x\n", data);

    currEpisr = 0;

    for(pIndex=0; pIndex < USB_EP_NUM; pIndex++)
    {
      lIndex = pIndex >> 1;

      if(data == currEpisr) break;
      if(data & (1 << pIndex))
      {
        currEpisr |= (1 << pIndex);

        LPC_USB->EpIntClr = 1 << pIndex;
        while((LPC_USB->DevIntSt & CDFULL_INT) == 0);
        val = LPC_USB->CmdData;

        // OUT endpoint.
        if((pIndex & 1) == 0)
        {
          // Control OUT endpoint.
          if(pIndex == 0)
          {
            // Setup Packet.
            if(val & EP_SEL_STP)
            {
              if(USB_P_EP[0])
              {
                USB_P_EP[0](USB_EVT_SETUP);
                continue;
              }
            }
          }

          if(USB_P_EP[lIndex])
          {
            USB_P_EP[lIndex](USB_EVT_OUT);
          }
        }

        // IN endpoint.
        else
        {
          if(USB_P_EP[lIndex])
          {
            if(lIndex > 0) clearSendMessageFlag(lIndex);
            USB_P_EP[lIndex](USB_EVT_IN);
          }
        }
      }
    }

    LPC_USB->DevIntClr = EP_SLOW_INT;
  }

#ifdef USB_DMA

  if (dmaInterruptData & 0x00000001) {          /* End of Transfer Interrupt */
    data = LPC_USB->EoTIntSt;
    for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) {      /* Check All Endpoints */
      if (data & (1 << pIndex)) {
        lIndex = pIndex >> 1;
        if ((pIndex & 1) == 0) {                 /* OUT Endpoint */
          if (USB_P_EP[lIndex]) {
            USB_P_EP[lIndex](USB_EVT_OUT_DMA_EOT);
          }
        } else {                            /* IN Endpoint */
          if (USB_P_EP[lIndex]) {
            USB_P_EP[lIndex](USB_EVT_IN_DMA_EOT);
          }
        }
      }
    }
    LPC_USB->EoTIntClr = data;
  }

  if (dmaInterruptData & 0x00000002) {          /* New DD Request Interrupt */
    data = LPC_USB->NDDRIntSt;
    for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) {      /* Check All Endpoints */
      if (data & (1 << pIndex)) {
        lIndex = pIndex >> 1;
        if ((pIndex & 1) == 0) {                 /* OUT Endpoint */
          if (USB_P_EP[lIndex]) {
            USB_P_EP[lIndex](USB_EVT_OUT_DMA_NDR);
          }
        } else {                            /* IN Endpoint */
          if (USB_P_EP[lIndex]) {
            USB_P_EP[lIndex](USB_EVT_IN_DMA_NDR);
          }
        }
      }
    }
    LPC_USB->NDDRIntClr = data;
  }

  if (dmaInterruptData & 0x00000004) {          /* System Error Interrupt */
    data = LPC_USB->SysErrIntSt;
    for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) {      /* Check All Endpoints */
      if (data & (1 << pIndex)) {
        lIndex = pIndex >> 1;
        if ((pIndex & 1) == 0) {                 /* OUT Endpoint */
          if (USB_P_EP[lIndex]) {
            USB_P_EP[lIndex](USB_EVT_OUT_DMA_ERR);
          }
        } else {                            /* IN Endpoint */
          if (USB_P_EP[lIndex]) {
            USB_P_EP[lIndex](USB_EVT_IN_DMA_ERR);
          }
        }
      }
    }
    LPC_USB->SysErrIntClr = data;
  }

#endif /* USB_DMA */

  OS_LeaveInterrupt();
}

如果您能够在我的任何代码或工作示例程序中发现错误,我可以找到理想的解决方案,该程序可以在LPC1788上运行,演示使用DMA引擎进行USB消息传输和接收。

我也很欣赏有关可能导致AHB总线错误的任何信息。

修改

回应Turbo J的回答:

  

检查UDCA的地址。所需的对齐非常严格,256字节IIRC,因此地址必须以0x00作为LDB结束。 GCC需要支持链接描述文件中的USB_RAM部分。

在我的USB硬件头文件中,我有:

/* USB RAM Definitions */
#define USB_RAM_ADR     LPC_PERI_RAM_BASE  /* USB RAM Start Address */
#define USB_RAM_SZ      0x00004000  /* USB RAM Size (4kB) */

LPC_PERI_RAM_BASE的值为0x20000000UL。

在我的源文件中,我有:

#if defined (__CC_ARM)
#pragma arm section zidata = "USB_RAM"
uint32_t UDCA[USB_EP_NUM];                     /* UDCA in USB RAM */

uint32_t DD_NISO_Mem[4*DD_NISO_CNT];           /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT];            /* Iso DMA Descriptor Memory */
#pragma arm section zidata
#elif defined ( __ICCARM__ )
#pragma location = "USB_RAM"
uint32_t UDCA[USB_EP_NUM];                     /* UDCA in USB RAM */
#pragma location = "USB_RAM"
uint32_t DD_NISO_Mem[4*DD_NISO_CNT];           /* Non-Iso DMA Descriptor Memory */
#pragma location = "USB_RAM"
uint32_t DD_ISO_Mem [5*DD_ISO_CNT];            /* Iso DMA Descriptor Memory */

#else
uint32_t UDCA[USB_EP_NUM]__attribute__((section ("USB_RAM")));                     /* UDCA in USB RAM */

uint32_t DD_NISO_Mem[4*DD_NISO_CNT]__attribute__((section ("USB_RAM")));           /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]__attribute__((section ("USB_RAM")));            /* Iso DMA Descriptor Memory */
#endif /*__GNUC__*/
uint32_t udca[USB_EP_NUM];                     /* UDCA saved values */

uint32_t DDMemMap[2];                          /* DMA Descriptor Memory Usage */

#endif

USB_EP_NUM为32。

因此,UDCA应该是一个128字节的数组,从RAM内存块的开头开始,我相信。

1 个答案:

答案 0 :(得分:2)

检查UDCA的地址。所需的对齐非常严格,256字节IIRC,因此地址必须以0x00作为LDB结束。 GCC需要支持链接描述文件中的USB_RAM部分。