S32K148 FlexCAN overloaded

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

S32K148 FlexCAN overloaded

358 Views
TomasF
Contributor I

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!

0 Kudos
4 Replies

323 Views
PetrS
NXP TechSupport
NXP TechSupport

Hi,

for a code I just have a note to do not use read-modify-write operation; can->can_module->IFLAG1 |= 0x1 << mb_index; other flags can be clear too. Just write flag you want to clear; can->can_module->IFLAG1 = 0x1 << mb_index;

Per my understanding you do not have issue with processing of incoming frames. Your code runs fast enough to read all received frames and save it into circular buffer and prepare TX MB for transmission. However when searching for empty TX MB you are finding no free one.
So a question could be why messages are not transmitted?
Is a bus so busy with incoming messages? Do you have any measurement for that to know there is no free space to send frame?
Or a FlexCAN is not able to finish internal arbitration and select winner for transmission? For that you can check TASD setting, I think.
Probably you will need to increase your circular buffer to be able to hold more frames to be resent while waiting for TX MB.

BR, Petr

0 Kudos

233 Views
TomasF
Contributor I

I have to correct myself, it seems the problem starts indeed with the processing of incoming messages. It starts after increasing the bus load, from the screenshot it is clear that the generated message (purple) is regular, while the message, that goes through the S32K onto the other CAN (azure) has outages.

I was however still not able to solve this issue.

Výstřižek3.PNG

0 Kudos

199 Views
PetrS
NXP TechSupport
NXP TechSupport

Hi,

unfortunately I did not get graphs meaning...
For TX MB preparation; is it good to use Abort in your case? That way you can eventually loss message if it is aborted. Or do you then reconfigure/prepare a same message in your SW?
Registers shows module is detecting a lot of errors and going to bus off state. Does it happen during application running or just when registers are read out?
Bus off can be due to improper CAN bit timing used.

BR, Petr

0 Kudos

289 Views
TomasF
Contributor I

Hello, Petr,

thank you for responding. I applied the code changes you suggested. I also set the TASD values for both CANs to its maximum value 0x1F. The effect of this is visible, but the CAN is still very unstable. As you have said, the reception of CAN messages isn't a problem, however transmitting CAN messages is stil getting delayed. I am attaching a screenshot of the cycle time of one of the CAN messages. Pink is the message that is beeing read by our ECU, black is the message our ECU is re-transmitting onto the other CAN. It is clear, that after a failed transmission, a double transmission follows, indicating that the received messages are indeed not being lost.

Výstřižek1.PNG

The circular buffers used for both receiving and transmitting messages have their value 'count' always == 0. So the CPU performence is also not a problem. Both CAN ECR registers are also == 0 all the time, co there is also no problem wit the bus.

I am attaching again the functios for loading a message for transmission from the circular buffer. This function also selects the TX MB. And the function for the transmission itself, which I also modified a bit so that it follows the User Manual instructions for TX process exactly:

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, it means the function is called for TX process and the TX MB must be sleected.
	if (can_data != 0x0)
	{
		for (i = 0U; i < TX_MB_COUNT; i++)
		{
			tx_mb_code = (can_data->can_module->RAMn[mb_tx[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 = mb_tx[i];
				break;
			}
		}
		if (tx_mb_temp == 0xFF)
		{
			// No empty TX MB is available. Uses the oldest TX MB and forces it to abort before transmission.
			can_data->last_tx_mb = (can_data->last_tx_mb + 1U) % TX_MB_COUNT;
			*tx_mb = mb_tx[can_data->last_tx_mb];
		}
		else
		{
			*tx_mb = tx_mb_temp;
		}
	}
	
	// Loads message from the 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; }
	
	// Shifts the buffer.
	buffer->start++;
	if (buffer->start == CAN_FRAME_BUFFER_SIZE) { buffer->start = 0; }
	buffer->count--;
		
	return Buffer_ok;
}
ENUM_CAN_transmit_return_value CAN_Transmit(STRUCT_CAN_Module_Data *can, STRUCT_CAN_frame can_frame, uint8_t tx_mb)
{
	uint8_t i;
	uint32_t tx_mb_code;
	
	if ((can_frame.fd_frame == 1U && can->can_fd == FALSE) || (can_frame.brs == TRUE && can->can_fd == FALSE))
	{
		return CAN_invalid;
	}
	
	// 1. Check whether the respective interrupt bit is set and clear it.
	can->can_module->IFLAG1 = (0x1 << tx_mb);
		
	// 2. If the MB is active (transmission pending), write the ABORT code (0b1001) to the CODE field of the Control and Status word to request an abort of the transmission.
	tx_mb_code = (can->can_module->RAMn[tx_mb /* Message Buffer i - odesilaci. */ * mb_sizes[can->data_size] /* Velikost MB v RAM. */ + 0U /* Prvni slovo MB. */] >> 24) & 0xF;
	if (tx_mb_code != 8U && tx_mb_code != 9U)
	{
		can->can_module->RAMn[tx_mb /* Message Buffer */ * mb_sizes[can->data_size] /* Velikost MB v RAM. */ + 0U /* Prvni slovo MB. */] = (can->can_module->RAMn[tx_mb * mb_sizes[can->data_size]] & ~(0xF << 24)) | (0x9 << 24);
		
		// 3. Wait for the corresponding IFLAG bit to be asserted by polling the IFLAG register, or by the interrupt request if enabled by the respective IMASK bit.
		while ((can->can_module->IFLAG1 & (0x1 << tx_mb)) == 0x0) { ; }
		
		// 4. Read back the CODE field to check if the transmission was aborted or transmitted (see Transmission abort mechanism).
		tx_mb_code = (can->can_module->RAMn[tx_mb /* Message Buffer i - odesilaci. */ * mb_sizes[can->data_size] /* Velikost MB v RAM. */ + 0U /* Prvni slovo MB. */] >> 24) & 0xF;
		
		// 5. Clear the corresponding interrupt flag.
		can->can_module->IFLAG1 = (0x1 << tx_mb);
	}
	
	// 6. Write the ID register (containing the local priority if enabled via MCR[LPRIO_EN]).
	can->can_module->RAMn[tx_mb /* Message Buffer */ * mb_sizes[can->data_size] /* Velikost MB v RAM. */ + 1U /* Druhe slovo MB. */] = can_frame.format == Standard ? (can_frame.id << 18) : can_frame.id;
	
	// 7. Write payload data bytes.
	for (i = 0U; i < mb_sizes[can->data_size] - 2U; i++)
	{
		can->can_module->RAMn[tx_mb /* Message Buffer */ * mb_sizes[can->data_size] /* Velikost MB v RAM. */ + 2U + i] = (can_frame.data[i * 4U] << 24) | (can_frame.data[(i * 4U) + 1U] << 16) | (can_frame.data[(i * 4U) + 2U] <<  | can_frame.data[(i * 4U) + 3U];
	}
	
	// 8. Configure the Control and Status word with the desired configuration.
	//	a. Set ID type via MB_CS[IDE].
	//	b. Set Remote Transmission Request (if needed) via MB_CS[RTR].
	//	c. If MCR[FDEN] is enabled, also configure the MB_CS[EDL] and MB_CS[BRS]
	//			fields. For details about the relationship between the written value and
	//			transmitted value of the MB_CS[ESI] field, see Table 55-26.1
	//	d. Set Data Length Code in bytes via MB_CS[DLC]. See Table 55-13 for detailed
	//			information.
	//	e. Activate the message buffer to transmit the CAN frame by setting
	//			MB_CS[CODE] to 0xC.
	can->can_module->RAMn[tx_mb /* Message Buffer */ * mb_sizes[can->data_size] /* Velikost MB v RAM. */ + 0U /* Prvni slovo MB. */] = ((can_frame.fd_frame << 31) & 0x80000000 /* EDL */) | ((can_frame.brs << 30U) & 0x40000000 /* BRS */) | ((0xC << 24) & 0xF000000 /* CODE = Tx DATA frame (RTR = 0) */) | (((can_frame.format == Extended ? 1U : 0U) << 22) & 0x400000 /* SRR */) | ((can_frame.format << 21) & 0x200000 /* IDE */) | ((can_frame.len << 16) & 0xF0000 /* DLC */);

	return CAN_tx_ok;
}

I am also attaching the CAN periphery configuration (the same for both channels):

Výstřižek2.PNG

0 Kudos