Using multiple transmit mailboxes with the KSDK FlexCAN Driver

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

Using multiple transmit mailboxes with the KSDK FlexCAN Driver

Jump to solution
1,527 Views
zandererasmus
Contributor II

I am writing something that requires a high throughput for CAN transmission.

Looking at the FlexCAN module in the k66 datasheet, there is an elegant system where you can fill the TX Mailboxes with messages and the underlying system handles the transmission

 

Attempting to use the KSDK flexCAN drivers in this way does Not seem to work though.

 

Does the KSDK FlexCAN driver use only one TX Mailbox at a time?

Is there a simple way to make KSDK handle multiple TX mailboxes?

 

Thank you,

Zander

Labels (1)
Tags (3)
0 Kudos
1 Solution
833 Views
zandererasmus
Contributor II

To use the TX mailboxes like a TX FIFO in KSDK 1.3, you need to override the default behaviour of the drivers.

It would be better to write your own TX drivers but here's how to do it if you are not inclined to do that.

This example is written in MQX but it's probably relatively easy to do the same thing in baremetal.

The IRQ Handler:

You need to now service your own flags for the mailboxes (since the TxDone flag only works with a single active mailbox)

void MQX_CAN0_ORed_Message_buffer_IRQHandler(void)

{

    volatile uint32_t flag_reg;

    uint32_t temp;

    /* Get the interrupts that are enabled and ready */

    flag_reg = ((FLEXCAN_HAL_GetAllMsgBuffIntStatusFlag(CAN0_BASE_PTR)) & CAN_IMASK1_BUFLM_MASK) & CAN_RD_IMASK1(CAN0_BASE_PTR);

    for  (uint32_t i = MB_OFFSET_TX ; i < ( MB_OFFSET_TX + NUMBER_OF_TX_MB ) ; i++ )

    {

     temp = (1 << i);

        if (flag_reg & temp)

        {

            /* Complete transmit data */

            mailbox_flags[0] &= ~temp;

            /* Disable the transmitter data register empty interrupt */

            FLEXCAN_HAL_SetMsgBuffIntCmd(CAN0_BASE_PTR, i, false);

            /* Disable error interrupts */

            FLEXCAN_HAL_SetErrIntCmd(CAN0_BASE_PTR,kFlexCanIntErr,false);

            FLEXCAN_HAL_ClearMsgBuffIntStatusFlag(CAN0_BASE_PTR, temp & flag_reg);

        }

  }

  /* Get the interrupts that are enabled and ready */

  FLEXCAN_DRV_IRQHandler(0);

}

Transmitting a message:

Create a ring buffer of the flags and set the mailbox flag of the mailbox you are transmitting to

We are popping messages off of a message queue in a task, but the important stuff is between the _int_disable and enable

You can see that I am overriding the isTxBusy flag, this is so that the FLEXCAN_DRV_Send doesn't ignore the message

void can_send ( uint8_t chnl  )

{

    // calculate which mb to use

    uint32_t mb_to_send = (flexcan_mb_counter[chnl])+MB_OFFSET_TX;

  if ( mailbox_flags[chnl] & ( 1 << mb_to_send ) )

  {

    // _time_delay_ticks(1);

     return;

  }

    if ( _msgq_get_count( can_channels[chnl].txq ) == 0 )

    {

        return;

    }

  // get flexcan message from private queue

  can_dequeue_tx(chnl, &tx_frame[chnl]);

  // tx info has two parameters, data length and frame size (std vs ext), this sets datalen

  tx_info.data_length = (tx_frame[chnl].cs >> 16) & 0xF;

  // send the can message on the hardware

  _int_disable();

  FLEXCAN_DRV_ConfigTxMb(chnl, mb_to_send, &tx_info, tx_frame[chnl].msgId);

  can_channel_state[chnl].isTxBusy = false;

  mailbox_flags[chnl] |= ( 1 << mb_to_send );

  FLEXCAN_DRV_Send(chnl, mb_to_send, &tx_info, tx_frame[chnl].msgId, tx_frame[chnl].data);

  _int_enable();

  // update the mb counter

  flexcan_mb_counter[chnl] = (++flexcan_mb_counter[chnl])%NUMBER_OF_TX_MB;

}

View solution in original post

0 Kudos
3 Replies
834 Views
zandererasmus
Contributor II

To use the TX mailboxes like a TX FIFO in KSDK 1.3, you need to override the default behaviour of the drivers.

It would be better to write your own TX drivers but here's how to do it if you are not inclined to do that.

This example is written in MQX but it's probably relatively easy to do the same thing in baremetal.

The IRQ Handler:

You need to now service your own flags for the mailboxes (since the TxDone flag only works with a single active mailbox)

void MQX_CAN0_ORed_Message_buffer_IRQHandler(void)

{

    volatile uint32_t flag_reg;

    uint32_t temp;

    /* Get the interrupts that are enabled and ready */

    flag_reg = ((FLEXCAN_HAL_GetAllMsgBuffIntStatusFlag(CAN0_BASE_PTR)) & CAN_IMASK1_BUFLM_MASK) & CAN_RD_IMASK1(CAN0_BASE_PTR);

    for  (uint32_t i = MB_OFFSET_TX ; i < ( MB_OFFSET_TX + NUMBER_OF_TX_MB ) ; i++ )

    {

     temp = (1 << i);

        if (flag_reg & temp)

        {

            /* Complete transmit data */

            mailbox_flags[0] &= ~temp;

            /* Disable the transmitter data register empty interrupt */

            FLEXCAN_HAL_SetMsgBuffIntCmd(CAN0_BASE_PTR, i, false);

            /* Disable error interrupts */

            FLEXCAN_HAL_SetErrIntCmd(CAN0_BASE_PTR,kFlexCanIntErr,false);

            FLEXCAN_HAL_ClearMsgBuffIntStatusFlag(CAN0_BASE_PTR, temp & flag_reg);

        }

  }

  /* Get the interrupts that are enabled and ready */

  FLEXCAN_DRV_IRQHandler(0);

}

Transmitting a message:

Create a ring buffer of the flags and set the mailbox flag of the mailbox you are transmitting to

We are popping messages off of a message queue in a task, but the important stuff is between the _int_disable and enable

You can see that I am overriding the isTxBusy flag, this is so that the FLEXCAN_DRV_Send doesn't ignore the message

void can_send ( uint8_t chnl  )

{

    // calculate which mb to use

    uint32_t mb_to_send = (flexcan_mb_counter[chnl])+MB_OFFSET_TX;

  if ( mailbox_flags[chnl] & ( 1 << mb_to_send ) )

  {

    // _time_delay_ticks(1);

     return;

  }

    if ( _msgq_get_count( can_channels[chnl].txq ) == 0 )

    {

        return;

    }

  // get flexcan message from private queue

  can_dequeue_tx(chnl, &tx_frame[chnl]);

  // tx info has two parameters, data length and frame size (std vs ext), this sets datalen

  tx_info.data_length = (tx_frame[chnl].cs >> 16) & 0xF;

  // send the can message on the hardware

  _int_disable();

  FLEXCAN_DRV_ConfigTxMb(chnl, mb_to_send, &tx_info, tx_frame[chnl].msgId);

  can_channel_state[chnl].isTxBusy = false;

  mailbox_flags[chnl] |= ( 1 << mb_to_send );

  FLEXCAN_DRV_Send(chnl, mb_to_send, &tx_info, tx_frame[chnl].msgId, tx_frame[chnl].data);

  _int_enable();

  // update the mb counter

  flexcan_mb_counter[chnl] = (++flexcan_mb_counter[chnl])%NUMBER_OF_TX_MB;

}

0 Kudos
833 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Zander,

From hardware perspective, FlexCan supports multiple transmit mailbox to transfer data. in SDK tools, it also supports multiple transmit mailbox to transfer data.

This is the snippet to support multiple transmit mailbox:

  flexcan_state_t can_state;

    flexcan_user_config_t flexcan1_data;

    uint32_t TX_identifier0,TX_identifier1;

    uint32_t RX_identifier;

    uint32_t TX_mailbox_num0,TX_mailbox_num1;

    uint32_t RX_mailbox_num;

FLEXCAN_DRV_Init(flexcanInstance, &can_state, &flexcan1_data);

.......................................................................................................................

//transmit data via mailbox0

//assign the tx_info, TX_identifier for mailbox0

FLEXCAN_DRV_ConfigTxMb(flexcanInstance, TX_mailbox_num0, &tx_info, TX_identifier0);

FLEXCAN_DRV_SendBlocking(flexcanInstance, TX_mailbox_num0, &tx_info, TX_identifier0,

                                          (ch == ch1) ? ch0 : ch1, 1000);

...........................................................................................................................

...........................................................................................................................

//transmit data via mailbox1

//assign the tx_info, TX_identifier for mailbox1

FLEXCAN_DRV_ConfigTxMb(flexcanInstance, TX_mailbox_num1, &tx_info, TX_identifier1);

FLEXCAN_DRV_SendBlocking(flexcanInstance, TX_mailbox_num1, &tx_info, TX_identifier1,

                                          (ch == ch1) ? ch0 : ch1, 1000);

...................................................................................................................................

Hope it can help you

BR

Xiangjun Rong

0 Kudos
833 Views
zandererasmus
Contributor II

The goal is to improve throughput by writing data to mailboxes During transmission.

So the desired pseudo code would look like this:

while ( tx_messages in queue )

{

     if (mailbox is busy) then break

     pop tx_message

     write to mailbox

     (++mailbox) % 8

}

task_sleep

I had thought that changing the active mailbox during transmission would break the driver (since the driver IRQ handler uses the mb_idx when checking flags for TX)

The issue is the "isTxBusy" part of the state, which only changes to False when the interrupt triggers

Only "tx_mb_idx" flag is checked in the interrupt

I will try tomorrow and see what happens

0 Kudos