2014-09-10 1 views
2

Я разрабатываю приложение для микроконтроллера NXP LPC1788 с использованием IAR Embedded Workbench IDE, и одним из требований является получение CAN-сообщений от порта CAN1 как можно быстрее. Моя проблема заключается в том, что, по-видимому, существует задержка ~ 270 микросекунд между началом одного вызова CAN IRQ и началом следующего, хотя кажется, что для управления прерыванием требуется только микроконтроллер ~ 30 микросекунд.Как я могу улучшить скорость, с которой CAN IRQ вызывается для микроконтроллера LPC1788?

В дополнение к этому прерыванию у меня также есть регулярные прерывания таймера, запланированные каждые 50 микросекунд, которые обрабатывают принятые сообщения CAN и USB. Тем не менее, эта процедура прерывания занимает ~ 3 микросекунды, если нет сообщений CAN или USB для обработки и только около ~ 80 микросекунд, если они есть.

Из-за этого мне интересно, почему CAN IRQ не способен запускать быстрее, чем каждые 270 микросекунд, и что я могу сделать, чтобы исправить это.

USB-прерывания оказывают незначительное влияние на производительность, поскольку сообщения USB поступают гораздо реже, чем сообщения CAN, и я решил, что эта проблема возникает даже тогда, когда это прерывание никогда не запускается.

В настоящее время мое приложение, похоже, может обрабатывать сообщения CAN со средним временем между приходами ~ 300 us (проверено на 3 сообщения, сгенерированные с интервалом 1 мс, с обработкой ~ 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 нами:

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 указывает начальный O f CAN IRQ.
  • 0xBBBBBBBB указывает конец CAN IRQ.
  • 0x88888888 указывает, что сообщение CAN должно обрабатываться interpretMessage в таймере IRQ.
  • 0x99999999 указывает, что мы вернулись с interpretMessage.

Вы можете видеть, что, несмотря на то, что сообщения поступают каждые 200 лет, CAN IRQ обслуживает только каждые 270 пользователей. Это приводит к тому, что очередь приема будет нарастать до тех пор, пока сообщения не начнут падать.

Любая помощь будет оценена по достоинству.

EDIT 1

я, возможно, следует также отметить, что у меня есть мои CAN периферийные устройства запрограммированы на работу со скоростью 500 кбод. Кроме того, мое приложение включает в себя вторя CAN получили сообщения, поэтому любые сообщения, поступающие на порт 1, повторно передаются на порт 2.

EDIT 2

CAN-и TIMER_0 прерывания процедуры имеют одинаковый приоритет:

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

EDIT 3

Я могу подтвердить, что проблема остается, и не мало/без изменений ~ 270 нас interva l между прерываниями, даже когда я отключил прерывания таймера и отключил CAN IRQ, но не скопировал полученное сообщение CAN в ОЗУ и освободил буфер приема.

+1

Частота прерываний определяется только скоростью передачи в шине CAN. Единственное, что вы можете сделать неправильно, - это не быть в курсе, что явно не проблема. –

+0

@HansPassant Спасибо, есть способ рассчитать ожидаемую скорость прерывания, основанную на скорости передачи? Мой бод - 500 000, и для этого я установил часы для периферии CAN на 12 МГц. Поэтому каждый тик составляет около 83 наносекунд, поэтому между каждым прерыванием должно быть ~ 3000 тиков (судя по задержке 270 микросекунд)? – Tagc

+1

@ HansPassant Извините, я просто кровавый идиот. При 500 kBaud потребуется 271 us для CAN-сообщения со стандартными (11-разрядными) идентификационными и максимальными битами для передачи. Я понимаю, что вы имеете в виду сейчас. – Tagc

ответ

1

Я идиот. При 500 kBaud потребуется 271 us для CAN-сообщения с 11-битным стандартным идентификатором и битами максимального количества, которые должны быть переданы. В моем коде нет явных проблем, узким местом является просто то, как быстро CAN-сообщения могут передаваться на шине в этот момент.

Смежные вопросы