2

我正在使用 IAR Embedded Workbench IDE 为 NXP LPC1788 微控制器开发一个应用程序,其中一个要求是尽快从 CAN1 端口接收 CAN 消息。我的问题是,在 CAN IRQ 的一次调用开始和下一次调用开始之间似乎存在约 270 微秒的延迟,即使微控制器似乎只需要约 30 微秒来处理中断。

除了这个中断之外,我还安排了每 50 微秒一次的定期定时器中断,该中断处理接收到 CAN 和 USB 消息。但是,如果没有要处理的 CAN 或 USB 消息,则此中断例程需要约 3 微秒,如果有则只需约 80 微秒。

正因为如此,我想知道为什么 CAN IRQ 的触发速度不能超过每 270 微秒,以及我能做些什么来解决这个问题。

USB 中断对性能的影响可以忽略不计,因为 USB 消息比 CAN 消息更不频繁地到达,而且我确定即使从未触发此中断也会出现此问题。

目前,我的应用程序似乎能够处理平均到达间隔时间约为 300 us 的 CAN 消息(测试以 1 ms 的间隔生成 3 条消息,处理了大约 500,000 条消息,丢弃率为 0%)。

此代码初始化 CAN:

void CANHandlerInit()
{   
  // Configure CAN pins.
  PINSEL_ConfigPin(0, 0, 1); // RD1.
  PINSEL_ConfigPin(0, 1, 1); // TD1.
  PINSEL_ConfigPin(0, 4, 2); // RD2.
  PINSEL_ConfigPin(0, 5, 2); // TD2.

  CAN_Init(CAN_1, CAN_BAUD_RATE);
  CAN_Init(CAN_2, CAN_BAUD_RATE);

  //printf("CAN Handler initialised\n");
}

这用于运行 CAN:

void CANHandlerRun()
{  
  // Enter reset mode.
  LPC_CAN1->MOD |= 0x1;
  LPC_CAN2->MOD |= 0x1;

#if CAN_SOURCE_PORT == CAN_PORT_1
  SFF_GPR_Table[0].controller1 = SFF_GPR_Table[0].controller2 = CAN1_CTRL;
  SFF_GPR_Table[0].disable1 = SFF_GPR_Table[0].disable2 = MSG_ENABLE;
  SFF_GPR_Table[0].lowerID = 0x0;
  SFF_GPR_Table[0].upperID = 0x7FF;
#else
  SFF_GPR_Table[0].controller1 = SFF_GPR_Table[0].controller2 = CAN2_CTRL;
  SFF_GPR_Table[0].disable1 = SFF_GPR_Table[0].disable2 = MSG_ENABLE;
  SFF_GPR_Table[0].lowerID = 0x0;
  SFF_GPR_Table[0].upperID = 0x7FF;
#endif

  AFTable.FullCAN_Sec = NULL;
  AFTable.FC_NumEntry = 0;

  AFTable.SFF_Sec = NULL;
  AFTable.SFF_NumEntry = 0;

  AFTable.SFF_GPR_Sec = &SFF_GPR_Table[0];
  AFTable.SFF_GPR_NumEntry = 1;

  AFTable.EFF_Sec = NULL;
  AFTable.EFF_NumEntry = 0;

  AFTable.EFF_GPR_Sec = NULL;
  AFTable.EFF_GPR_NumEntry = 0;

  if(CAN_SetupAFLUT(&AFTable) != CAN_OK) printf("AFLUT error\n");

  LPC_CANAF->AFMR = 0;

  // Re-enter normal operational mode.
  LPC_CAN1->MOD &= ~0x1;
  LPC_CAN2->MOD &= ~0x1;

  // Enable interrupts on transmitting and receiving messages.
#if CAN_SOURCE_PORT == CAN_PORT_1
  LPC_CAN1->IER |= 0x1; /* RIE */
  LPC_CAN1->IER |= (1 << 8); /* IDIE */
#else
  LPC_CAN2->IER |= 0x1;
  LPC_CAN2->IER |= (1 << 8);
#endif

  NVIC_EnableIRQ(CAN_IRQn);
}

这是 CAN IRQ 代码:

void CAN_IRQHandler(void)
{
  ITM_EVENT32_WITH_PC(1, 0xAAAAAAAA);

#if CAN_SOURCE_PORT == CAN_PORT_1
  if(LPC_CAN1->SR & 0x1 /* RBS */)
  {
    CAN_ReceiveMsg(CAN_1, &recMessage);
    COMMS_NotifyCANMessageReceived();
    CAN_SetCommand(CAN_1, CAN_CMR_RRB);
  }
#else
  if(LPC_CAN2->SR & 0x1)
  {
    CAN_ReceiveMsg(CAN_2, &recMessage);
    COMMS_NotifyCANMessageReceived();
    CAN_SetCommand(CAN_2, CAN_CMR_RRB);
  }
#endif

  ITM_EVENT32_WITH_PC(1, 0xBBBBBBBB);
}

COMMS_NotifyCANMessageReceived代码:

void COMMS_NotifyCANMessageReceived()
{
   COMMS_BUFFER_T commsBuffer;

#if MAX_MSG_QUEUE_LENGTH > 0
  uint32_t length;

  LIST_AcquireLock(canMessageList);
  length = LIST_GetLength(canMessageList);
  LIST_ReleaseLock(canMessageList);

  if(length >= MAX_MSG_QUEUE_LENGTH)
  {
    ITM_EVENT32_WITH_PC(2, 0x43214321);
    return;
  }
#endif 

  commsBuffer.flags = COMMS_MODE_CAN;

  COMMS_GetData(&commsBuffer);

  LIST_AcquireLock(canMessageList);
  LIST_AddItem(canMessageList, &commsBuffer);
  LIST_ReleaseLock(canMessageList);
}

这是我的定时器中断代码,每 50 微秒触发一次:

void TIMER0_IRQHandler(void)
{
  uint32_t canMessageCount, usbMessageCount;

  if(TIM_GetIntStatus(LPC_TIM0, TIM_MR0_INT) == SET)
  {
    //ITM_EVENT32_WITH_PC(4, 0);

    LIST_AcquireLock(canMessageList);
    canMessageCount = LIST_GetLength(canMessageList);
    LIST_ReleaseLock(canMessageList);

    LIST_AcquireLock(usbMessageList);
    usbMessageCount = LIST_GetLength(usbMessageList);
    LIST_ReleaseLock(usbMessageList);

    if(canMessageList != NULL && canMessageCount > 0)
    {
      LIST_AcquireLock(canMessageList);

      if(!LIST_PopAtIndex(canMessageList, 0, &outCommsBuffer))
      {
        LIST_ReleaseLock(canMessageList);
        goto TIMER_IRQ_END;
      }

      LIST_ReleaseLock(canMessageList);

      ITM_EVENT32_WITH_PC(4, 0x88888888);
      interpretMessage(outCommsBuffer);
      ITM_EVENT32_WITH_PC(4, 0x99999999);
    }
    else if(usbMessageList != NULL && usbMessageCount > 0)
    {
      LIST_AcquireLock(usbMessageList);

      if(!LIST_PopAtIndex(usbMessageList, 0, &outCommsBuffer))
      {
        LIST_ReleaseLock(usbMessageList);
        goto TIMER_IRQ_END;
      }

      LIST_ReleaseLock(usbMessageList);

      ITM_EVENT32_WITH_PC(4, 0xCCCCCCCC);
      interpretMessage(outCommsBuffer);
      ITM_EVENT32_WITH_PC(4, 0xDDDDDDDD);
    }

    //ITM_EVENT32_WITH_PC(4, 1);
  }

TIMER_IRQ_END:
  TIM_ClearIntPending(LPC_TIM0, TIM_MR0_INT);
}

以下是运行应用程序时典型事件日志的摘录,消息之间的平均到达间隔时间为 200 us:

4s 718164.69 us     0x00005E82  0xAAAAAAAA              
4s 718175.27 us     0x00005EC4  0xBBBBBBBB              
4s 718197.10 us     0x000056C4              0x88888888  
4s 718216.50 us     0x00005700              0x99999999  
4s 718438.69 us     0x00005E82  0xAAAAAAAA              
4s 718449.40 us     0x00005EC4  0xBBBBBBBB              
4s 718456.42 us     0x000056C4              0x88888888  
4s 718476.56 us     0x00005700              0x99999999  
4s 718707.04 us     0x00005E82  0xAAAAAAAA              
4s 718717.54 us     0x00005EC4  0xBBBBBBBB              
4s 718747.15 us     0x000056C4              0x88888888  
4s 718768.00 us     0x000056C4              0x99999999  

在哪里:

  • 0xAAAAAAAA表示 CAN IRQ 的开始。
  • 0xBBBBBBBB表示 CAN IRQ 的结束。
  • 0x88888888表示interpretMessage在定时器 IRQ 内将要处理一条 CAN 报文。
  • 0x99999999表示我们已经从interpretMessage.

您可以看到,尽管消息每 200 us 到达,但 CAN IRQ 仅每约 270 us 为它们提供服务。这会导致接收队列不断增加,直到最终消息开始被丢弃。

任何帮助,将不胜感激。

编辑 1

我也许还应该提到我的 CAN 外围设备被编程为以 500 kBaud 的速率运行。此外,我的应用程序涉及回显接收到的 CAN 消息,因此任何到达端口 1 的消息都会在端口 2 上重新传输。

编辑 2

CAN 和 TIMER_0 中断例程具有相同的优先级:

NVIC_SetPriority(USB_IRQn, 1);
NVIC_SetPriority(CAN_IRQn, 1);
NVIC_SetPriority(TIMER0_IRQn, 1);

编辑 3

我可以确认问题仍然存在,即使我禁用定时器中断并且让 CAN IRQ 除了将接收到的 CAN 消息复制到 RAM 并释放接收缓冲区之外什么也不做,中断之间的 ~270 us 间隔几乎没有变化。

4

1 回答 1

1

我是个白痴。在 500 kBaud 时,需要 271 us 才能传输一个带有 11 位标准标识符和最大填充位的 CAN 消息。在我的代码中没有明显的问题,瓶颈只是 CAN 消息在那时可以在总线上传输多快。

于 2014-09-10T16:39:02.457 回答