Hello,
We are using a board with S32K148 for a project. where we need to receive messages on two CAN FD channels (500 kbps arbitration, 2000 kbps data) and transmit the received messages on one channel onto the second and vise versa (Gateway function).
Both CAN FD recption and transmission work well when the bus load is low (less than 5%, basically transmitting 1 CAN FD message on each of the two channels). When adding more messages however, we quickly run out of empty Message Buffers we can use for the transmission and CAN communication becomes unacceptably unstable.
We are using CAN FD, 64 bit DLC, meaning we only have 7 Message Buffers available for reception and transmission. We configure the MB 0, 2, 4 and 6 for transmission, MB 1 and 3 for reception of CAN FD standard IDs and MB 5 for reception of CAN FD extended IDs. This is our init function:
void CAN_Init(STRUCT_CAN_Module_Data *can_data)
{
uint16_t i;
// Init buffers.
InitCANFrameCyrcularBuffer(&can_data->tx_buffer);
InitCANFrameCyrcularBuffer((STRUCT_CAN_frame_buffer*)&can_data->rx_buffer);
// Clock for FlexCAN.
uint8_t flexcan_pcc_index = can_data->can_module == CAN0 ? PCC_FlexCAN0_INDEX : (can_data->can_module == CAN1 ? PCC_FlexCAN1_INDEX : PCC_FlexCAN2_INDEX);
if ((PCC->PCCn[flexcan_pcc_index] & PCC_PCCn_CGC_MASK) == 0x0)
{
PCC->PCCn[flexcan_pcc_index] |= PCC_PCCn_CGC(0x1);
}
// Clear interrupts.
can_data->can_module->IMASK1 = 0x00000000;
can_data->can_module->IFLAG1 = 0xFFFFFFFF;
// Deactivate module.
can_data->can_module->MCR |= CAN_MCR_MDIS(0x1);
while ((can_data->can_module->MCR & CAN_MCR_LPMACK_MASK) == 0x0) { }
// Setup clock for PE (CAN Protocol Engine) - SOSCDIV2_CLK pro HiSpeed CAN, BUS_CLK for CAN_FD.
if (can_data->can_fd == FALSE)
{
can_data->can_module->CTRL1 &= ~CAN_CTRL1_CLKSRC_MASK;
}
else
{
can_data->can_module->CTRL1 |= CAN_CTRL1_CLKSRC(0x1);
}
// Activate module.
can_data->can_module->MCR &= ~CAN_MCR_MDIS_MASK;
while ((can_data->can_module->MCR & CAN_MCR_LPMACK_MASK) != 0x0) { }
// Wait for Freeze off.
while ((can_data->can_module->MCR & CAN_MCR_FRZACK_MASK) == 0x0) { }
// Clock CAN bus.
if (can_data->can_fd == FALSE)
{
// HiSpeed CAN.
can_data->can_module->CTRL1 |= CAN_CTRL1_PROPSEG(CTRL1_PROPSEG) | CAN_CTRL1_PSEG2(CTRL1_T2) | CAN_CTRL1_PSEG1(CTRL1_T1) | CAN_CTRL1_RJW(CTRL1_RJW);
}
else
{
// CAN FD.
can_data->can_module->CBT = CAN_CBT_BTF(1U) | CAN_CBT_EPRESDIV(CBT_DIV) | CAN_CBT_EPROPSEG(CBT_PROPSEG) | CAN_CBT_EPSEG1(CBT_T1) | CAN_CBT_EPSEG2(CBT_T2) | CAN_CBT_ERJW(CBT_RJW);
can_data->can_module->FDCBT = CAN_FDCBT_FPRESDIV(FDCBT_DIV) | CAN_FDCBT_FPROPSEG(FDCBT_PROPSEG) | CAN_FDCBT_FPSEG1(FDCBT_T1) | CAN_FDCBT_FPSEG2(FDCBT_T2) | CAN_FDCBT_FRJW(FDCBT_RJW);
}
if (can_data->can_fd == TRUE)
{
// Additional CAN FD config.
can_data->can_module->FDCTRL = CAN_FDCTRL_FDRATE(1U) | CAN_FDCTRL_MBDSR0(can_data->data_size)/* | CAN_FDCTRL_TDCEN(0x1) | CAN_FDCTRL_TDCOFF(0x5)*/;
}
// Area 0x80 - 0x27F contains 512 byte (128 words) for Message Buffers (MB).
for (i = 0; i < CAN_RAMn_COUNT; i++)
{
can_data->can_module->RAMn[i] = 0x00000000;
}
// TX Message Buffers.
for (i = 0U; i < mb_count[can_data->data_size]; i += 2U)
{
can_data->can_module->RAMn[i /* MB i */ * mb_sizes[can_data->data_size] /* Velikost MB */ + 0U /* Prvni slovo MB */] = 0x8 << 24 /* CODE = 8 - MB je neaktivni. */;
}
// RX Message Buffers.
for (i = 1U; i < mb_count[can_data->data_size]; i += 2U)
{
switch (i)
{
case 1U:
//case 3U:
case 5U:
//case 7U:
case 9U:
//case 11U:
case 13U:
//case 15U:
case 17U:
//case 19U:
case 21U:
//case 23U:
case 25U:
//case 27U:
case 29U:
//case 31U:
// Standard ID.
can_data->can_module->RAMn[i /* MB i */ * mb_sizes[can_data->data_size] /* Velikost MB [slova] */ + 0U /* Prvni slovo MB */] = 0x4 << 24 /* CODE = 4 - MB je aktivni a prazdny. */;
break;
default:
// Extended ID.
can_data->can_module->RAMn[i /* MB i */ * mb_sizes[can_data->data_size] /* Velikost MB [slova] */ + 0U /* Prvni slovo MB */] = 0x4 << 24 /* CODE = 4 - MB je aktivni a prazdny. */ | (0x1 << 21) /* MB pro prijem zprav s extended ID. */;
break;
}
can_data->can_module->IMASK1 |= 0x1 << i; // Register interrupt.
}
// Shut off Freeze.
if (can_data->can_fd == FALSE)
{
can_data->can_module->MCR = CAN_MCR_SRXDIS(0x1) | CAN_MCR_MAXMB(mb_count[can_data->data_size] - 1U) | CAN_MCR_IRMQ(1U);
}
else
{
can_data->can_module->CTRL2 |= CAN_CTRL2_ISOCANFDEN(0x1);
can_data->can_module->MCR = CAN_MCR_MAXMB(mb_count[can_data->data_size] - 1U) | CAN_MCR_SRXDIS(0x1) | CAN_MCR_FDEN(0x1) | CAN_MCR_IRMQ(1U);
}
while ((can_data->can_module->MCR & (0x1 << 24 /* FRZACK */)) != 0x0) { }
// Wait for normal mode.
while ((can_data->can_module->MCR & (0x1 << 27 /* NOTRDY */)) != 0x0) { }
}
We receive the CAN messages on both channels by polling the RX Buffers, to which the data is saved in the interrupt routine:
void CAN_ReceiveInterrupt(STRUCT_CAN_Module_Data *can)
{
uint8_t mb_index, data_word_index;
uint32_t mb_control_and_status;
uint32_t mb_id;
uint32_t mb_data;
STRUCT_CAN_frame received_frame;
uint32_t iflag = can->can_module->IFLAG1 & can->can_module->IMASK1;
for (mb_index = 1U; mb_index < mb_count[can->data_size]; mb_index += 2U)
//for (mb_index = 1U; mb_index < mb_count[can->data_size]; mb_index++)
{
if ((iflag & (0x1 << mb_index)) != 0x0)
{
// Interrput from MB mb_index.
// MB status word.
mb_control_and_status = can->can_module->RAMn[mb_index /* MB */ * mb_sizes[can->data_size] /* Velikost MB v RAM. */ + 0U /* Prvni slovo MB. */];
// CAN ID.
mb_id = can->can_module->RAMn[mb_index /* MB */ * mb_sizes[can->data_size] /* Velikost MB v RAM. */ + 1U /* Druhe slovo MB. */];
// CAN message structure.
received_frame.format = (ENUM_CAN_frame_type)((mb_control_and_status & (0x1 << 21)) >> 21);
received_frame.len = (ENUM_CAN_frame_length)((mb_control_and_status & (0xF << 16)) >> 16);
received_frame.id = received_frame.format == Standard ? ((mb_id & (0x7FF << 18)) >> 18) : (mb_id & 0x1FFFFFFF);
received_frame.fd_frame = (Bool)((mb_control_and_status & (0x1U << 31)) >> 31);
received_frame.brs = (Bool)((mb_control_and_status & (0x1U << 30)) >> 30);
// CAN data.
for (data_word_index = 0U; data_word_index <= (dlcs[received_frame.len] - 1U) / 4U; data_word_index++)
{
mb_data = can->can_module->RAMn[mb_index /* MB */ * mb_sizes[can->data_size] /* Velikost MB v RAM. */ + 2 + data_word_index /* Index slova MB. */];
received_frame.data[(data_word_index * 4U) + 0U] = (mb_data & 0xFF000000) >> 24U;
received_frame.data[(data_word_index * 4U) + 1U] = (mb_data & 0xFF0000) >> 16U;
received_frame.data[(data_word_index * 4U) + 2U] = (mb_data & 0xFF00) >> 8U;
received_frame.data[(data_word_index * 4U) + 3U] = mb_data & 0xFF;
}
// Clear interrupt.
can->can_module->IFLAG1 |= 0x1 << mb_index;
// Save to buffer.
SaveCANFrameToCyrcularBuffer((STRUCT_CAN_frame_buffer*)&can->rx_buffer, received_frame);
}
}
}
When a CAN FD message is received, we try to find the TX MB on the other CAN FD channel free for transmission:
ENUM_Cyrcular_buffer_return_value LoadCANFrameFromCyrcularBuffer(STRUCT_CAN_Module_Data *can_data, STRUCT_CAN_frame_buffer *buffer, STRUCT_CAN_frame *frame, uint8_t *tx_mb)
{
uint8_t i;
uint32_t tx_mb_code;
uint8_t tx_mb_temp = 0xFF;
if (buffer->count == 0)
{
// Buffer is empty.
return Empty;
}
if (can_data != 0x0)
{
for (i = 0U; i < mb_count[can_data->data_size]; i += 2U)
{
tx_mb_code = (can_data->can_module->RAMn[i /* Message Buffer i - odesilaci. */ * mb_sizes[can_data->data_size] /* Velikost MB v RAM. */ + 0U /* Prvni slovo MB. */] >> 24) & 0xF;
if (tx_mb_code == 8U || tx_mb_code == 9U)
{
tx_mb_temp = i;
break;
}
}
if (tx_mb_temp == 0xFF)
{
return Bussy;
}
else
{
*tx_mb = tx_mb_temp;
}
}
// Read CAN frame from buffer.
frame->id = buffer->buffer[buffer->start].id;
frame->format = buffer->buffer[buffer->start].format;
frame->len = buffer->buffer[buffer->start].len;
frame->fd_frame = buffer->buffer[buffer->start].fd_frame;
frame->brs = buffer->buffer[buffer->start].brs;
for (i = 0; i < dlcs[buffer->buffer[buffer->start].len]; i++) { frame->data[i] = buffer->buffer[buffer->start].data[i]; }
for (i = dlcs[buffer->buffer[buffer->start].len]; i < 64; i++) { frame->data[i] = 0x00; }
// Shift buffer.
buffer->start++;
if (buffer->start == CAN_FRAME_BUFFER_SIZE) { buffer->start = 0; }
buffer->count--;
return Buffer_ok;
}
When we send more CAN messages onto the bus, the CAN periphery becomes overloaded and the function LoadCANFrameFromCyrcularBuffer starts to return result Bussy - unable to find a free MB for transmission. The CAN communication becomes unstable, cycle times of the CAN messages become too high / inconsistent.
Is there any way to improve the CAN functionality?
Thanks!