MCAN TXBTIE and TCE relationship

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

MCAN TXBTIE and TCE relationship

1,126 Views
microcris
Contributor III

Hello!

I'm using several TX buffers to send bursts of CAN messages. The messages are being sent to the bus and the TXBTO bits are being set accordingly to the used TX buffer. The problem is in the number of generated Transmit Complete interrupts. If 3 messages are sent "simultaneously" it will generate just one TC interrupt and it completely breaks the SDK mcan behaviour (one buffer will be marked as free and the other two will be forever busy, unless something looks to the TXBTO and clear the busy buffers.)

So, is it supposed to have an interrupt per sent message or shall I look to the TXBTO every time I get a TC interrupt?

And by the way, I think there is a bug in here:

void MCAN_TransferAbortSend(CAN_Type *base, mcan_handle_t *handle, uint8_t bufferIdx)
{
    /* Assertion. */
    assert(handle);
    assert(bufferIdx <= 63U);

    /* Disable Buffer Interrupt. */
    MCAN_DisableTransmitBufferInterrupts(base, bufferIdx);

    MCAN_DisableInterrupts(base, CAN_IE_TCE_MASK);

    /* Cancel send request. */
    MCAN_TransmitCancelRequest(base, bufferIdx);

    /* Un-register handle. */
    handle->bufferFrameBuf[bufferIdx] = 0x0;

    handle->bufferState[bufferIdx] = kMCAN_StateIdle;
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

When using more than one TX buffer, the first TC interrupt will disable the TC interrupts (MCAN_DisableInterrupts(base, CAN_IE_TCE_MASK)) and the remaining transmit buffers will not get their interrupts being generated.

MCU: LPC54628

SDK: 2.5.0

MCAN driver version 2.0.1

Tags (1)
2 Replies

970 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Cristiano,

Regarding your question, I think you should use the CAN transmit driver like this:

for(uint32_t i=0; i<FRAME_NUMBER; i++)

{

      //update txFrame data

            txXfer.frame = &txFrame;
            txXfer.bufferIdx = 0;
            MCAN_TransferSendNonBlocking(EXAMPLE_MCAN, &mcanHandle, &txXfer);

            while (!txComplete)
            {
            }
            txComplete = false;

}

In conclusion, after you send ONE frame, you have to wait for the event that  txComplete flag is set which is set the callback function:

static void mcan_callback(CAN_Type *base, mcan_handle_t *handle, status_t status, uint32_t result, void *userData)
{
    switch (status)
    {
        case kStatus_MCAN_RxFifo0Idle:
            {
                rxComplete = true;
            }
            break;

        case kStatus_MCAN_TxIdle:
            {
                txComplete = true;
            }
            break;

        default:
            break;
    }
}

It means that you have to sent one frame by one frame, in detail, only after you have sent ONE frame and the callback function is called can you sent next frame. It is not possible to send the frame one by one continuously although CAN hardware supports sending continuously.

Hope it can help you

BR

Xiangjun Rong

0 Kudos

971 Views
microcris
Contributor III

Hello Xiangjun Rong!

Thank you for your feedback.

That is how it is done in the can driver example

What is the point of several TX buffers if in order to use the next one we have to wait for the present one to finish?

The driver heavily implemented with just one buffer in mind.

right now, to send I'm doing like this:

void can_send(CAN_Type *base, mcan_handle_t *handle, const uint8_t *data, uint8_t data_len)
{
 mcan_tx_buffer_frame_t txFrame = { {0}, {0}, 0};
 mcan_buffer_transfer_t txXfer;
 uint8_t i, idx;
 status_t tx_rc;
 static uint8_t start_idx = 0;

 idx = 1;
 for(i = 0; i < data[0]; i ++)
 {
  txFrame.id = data[idx ++];
  txFrame.id |= data[idx ++] << 8;
  txFrame.id <<= STDID_OFFSET;
  txFrame.rtr = data[idx ++];
  if(data[idx] > 8)
  {
   PRINTF("CANS: Wrong DLC\n");
   break;
  }
  txFrame.dlc = data[idx ++];
  txFrame.data = (uint8_t *)(data + idx);
  txFrame.size = 8;
  idx += txFrame.dlc;

  txXfer.frame = &txFrame;
  txXfer.bufferIdx = start_idx;

  while(1)
  {
   tx_rc = MCAN_TransferSendNonBlocking(base, handle, &txXfer);


   txXfer.bufferIdx ++;
   if(txXfer.bufferIdx >= 16)
   {
    txXfer.bufferIdx = 0;
   }

   switch(tx_rc)
   {
    case kStatus_Success:
     start_idx = txXfer.bufferIdx;
     goto next;
    break;
    case kStatus_MCAN_TxBusy:
     if(start_idx == txXfer.bufferIdx)
     {
      PRINTF("CANS:  NO TX Buffer Available\n");
      goto next;
     }
    break;
    default:
     PRINTF("CANS: Failed to Write TX Buffer\n");
     if(start_idx == txXfer.bufferIdx)
     {
      goto next;
     }
   }
  }
next: ;
 }
}

I can send and array of messages using 16 TX CAN buffers

In order to make it work correctly, in the interrupt handler I have to do like this;

void MCAN_TransferHandleIRQ(CAN_Type *base, mcan_handle_t *handle)
{
 /* Assertion. */
 assert(handle);

 status_t status;
 uint32_t result;

 do
 {
  /* Reset return status */
  status = kStatus_MCAN_UnHandled;
  /* Store Current MCAN Module Error and Status. */
  result = base->IR;
  /* Solve Rx FIFO, Tx interrupt. */
  if (result & kMCAN_TxTransmitCompleteFlag)
  {
   status = kStatus_MCAN_TxIdle;
   //MCAN_TransferAbortSend(base, handle, handle->txbufferIdx);
   for(uint8_t i = 0; i < 32; i++)
   {
    if(MCAN_IsTransmitOccurred(base, i))
    {
     MCAN_TransferAbortSend(base, handle, i);
    }
   }
  }
  else if (result & kMCAN_RxFifo0NewFlag)
  {
   MCAN_ReadRxFifo(base, 0, handle->rxFifoFrameBuf);
   status = kStatus_MCAN_RxFifo0Idle;
   MCAN_TransferAbortReceiveFifo(base, 0, handle);
  }
  else if (result & kMCAN_RxFifo0LostFlag)
  {
   status = kStatus_MCAN_RxFifo0Lost;
  }
  else if (result & kMCAN_RxFifo1NewFlag)
  {
   MCAN_ReadRxFifo(base, 1, handle->rxFifoFrameBuf);
   status = kStatus_MCAN_RxFifo1Idle;
   MCAN_TransferAbortReceiveFifo(base, 1, handle);
  }
  else if (result & kMCAN_RxFifo1LostFlag)
  {
   status = kStatus_MCAN_RxFifo0Lost;
  }
  else
  {
   ;
  }

  /* Clear resolved Rx FIFO, Tx Buffer IRQ. */
  MCAN_ClearStatusFlag(base, result);

  /* Calling Callback Function if has one. */
  if (handle->callback != NULL)
  {
   handle->callback(base, handle, status, result, handle->userData);
  }

 } while ((0 != MCAN_GetStatusFlag(base, 0xFFFFFFFFU)) ||
   (0 != (result & (kMCAN_ErrorWarningIntFlag | kMCAN_BusOffIntFlag | kMCAN_ErrorPassiveIntFlag))));
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

For now it works and since I have a bigger problem with the ENET, later I will try to optimise it.