如何提高LPC1788微控制器调用CAN IRQ的速率?

时间:2014-09-10 07:46:44

标签: c microcontroller can-bus lpc irq

我正在使用IAR Embedded Workbench IDE为恩智浦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表示计时器IRQ中的interpretMessage即将处理CAN消息。
  • 0x99999999表示我们已从interpretMessage返回。

你可以看到,尽管消息每200个消息到达,但CAN IRQ只为每270个消息提供服务。这会导致接收队列建立,直到最终消息开始被丢弃。

任何帮助都将不胜感激。

编辑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并释放接收,中断之间的间隔很小/没有变化缓冲液中。

1 个答案:

答案 0 :(得分:1)

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