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
Solved! Go to Solution.
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;
}
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;
}
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
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