cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F7 Bug in CAN clock management

Igor Petrov
Associate II

I work on a project with STM32F767ZI that uses 3 CAN buses and require them to be turned ON and OFF at specific times to save power.

However, code generated by STM32CubeFW_F7 V1.16.1 (latest version at the time of posting) generates questionable(or even completely wrong) code that manages RCC APB clock enable bits.

Let's take a look at can.c (code is slightly redacted to save space in the post, the full file is attached) generated by CubeIDE:

static uint32_t HAL_RCC_CAN1_CLK_ENABLED=0;
static uint32_t HAL_RCC_CAN3_CLK_ENABLED=0;
static uint32_t HAL_RCC_CAN2_CLK_ENABLED=0;
 
void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{
 
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(canHandle->Instance==CAN1)
  {
	/* CAN1 clock enable */
	HAL_RCC_CAN1_CLK_ENABLED++;
	if(HAL_RCC_CAN1_CLK_ENABLED==1){
	  __HAL_RCC_CAN1_CLK_ENABLE();
	}
	/* GPIO init here */
  }
  else if(canHandle->Instance==CAN2)
  {
	/* CAN2 clock enable */
	HAL_RCC_CAN3_CLK_ENABLED++;
	if(HAL_RCC_CAN3_CLK_ENABLED==1){
	  __HAL_RCC_CAN3_CLK_ENABLE();
	}
	HAL_RCC_CAN2_CLK_ENABLED++;
	if(HAL_RCC_CAN2_CLK_ENABLED==1){
	  __HAL_RCC_CAN2_CLK_ENABLE();
	}
	HAL_RCC_CAN1_CLK_ENABLED++;
	if(HAL_RCC_CAN1_CLK_ENABLED==1){
	  __HAL_RCC_CAN1_CLK_ENABLE();
	}
	/* GPIO init here */
  }
  else if(canHandle->Instance==CAN3)
  {
	/* CAN3 clock enable */
	HAL_RCC_CAN3_CLK_ENABLED++;
	if(HAL_RCC_CAN3_CLK_ENABLED==1){
	  __HAL_RCC_CAN3_CLK_ENABLE();
	}
	HAL_RCC_CAN2_CLK_ENABLED++;
	if(HAL_RCC_CAN2_CLK_ENABLED==1){
	  __HAL_RCC_CAN2_CLK_ENABLE();
	}
	HAL_RCC_CAN1_CLK_ENABLED++;
	if(HAL_RCC_CAN1_CLK_ENABLED==1){
	  __HAL_RCC_CAN1_CLK_ENABLE();
	}
	/* GPIO init here */
  }
}
 
void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
{
 
  if(canHandle->Instance==CAN1)
  {
	/* Peripheral clock disable */
	__HAL_RCC_CAN1_CLK_DISABLE();
 
	/* GPIO deinit here */
  }
  else if(canHandle->Instance==CAN2)
  {
	/* Peripheral clock disable */
	HAL_RCC_CAN3_CLK_ENABLED--;
	if(HAL_RCC_CAN3_CLK_ENABLED==0){
	  __HAL_RCC_CAN3_CLK_DISABLE();
	}
	HAL_RCC_CAN2_CLK_ENABLED--;
	if(HAL_RCC_CAN2_CLK_ENABLED==0){
	  __HAL_RCC_CAN2_CLK_DISABLE();
	}
	HAL_RCC_CAN1_CLK_ENABLED--;
	if(HAL_RCC_CAN1_CLK_ENABLED==0){
	  __HAL_RCC_CAN1_CLK_DISABLE();
	}
 
	/* GPIO deinit here */
  }
  else if(canHandle->Instance==CAN3)
  {
	/* Peripheral clock disable */
	HAL_RCC_CAN3_CLK_ENABLED--;
	if(HAL_RCC_CAN3_CLK_ENABLED==0){
	  __HAL_RCC_CAN3_CLK_DISABLE();
	}
	HAL_RCC_CAN2_CLK_ENABLED--;
	if(HAL_RCC_CAN2_CLK_ENABLED==0){
	  __HAL_RCC_CAN2_CLK_DISABLE();
	}
	HAL_RCC_CAN1_CLK_ENABLED--;
	if(HAL_RCC_CAN1_CLK_ENABLED==0){
	  __HAL_RCC_CAN1_CLK_DISABLE();
	}		
	/* GPIO deinit here */
  }
}

This code uses HAL_RCC_CANx_CLK_ENABLED counters to decide whether clocks should be enabled or not.

Problem 1: When CAN1 is initialized, the counter gets incremented by 1, and the clock is enabled only when the value is 1. During deinitialization, the clock is disabled unconditionally, but the counter is not decremented. If I try to initialize CAN1 again, the counter value becomes 2 and clock stays disabled.

Problem 2: Why does initialization of CAN 2 enable clocks for CAN3 and vice versa, initialization of CAN3 enables clocks for CAN1 and CAN2? I understand that CAN2 is a slave peripheral that shares part of the functionality with CAN1, so when CAN2 gets initialized, it must enable clocks of CAN1. But, according to a reference manual, CAN3 is a Master and could operate independently from CAN1 and CAN2. 

Problem 3

void MX_CAN3_Init(void)
{
 
  /* USER CODE BEGIN CAN3_Init 0 */
 
  /* USER CODE END CAN3_Init 0 */
 
  /* USER CODE BEGIN CAN3_Init 1 */
 
  /* USER CODE END CAN3_Init 1 */
  hcan3.Instance = CAN3;
  hcan3.Init.Prescaler = 16;
  hcan3.Init.Mode = CAN_MODE_NORMAL;
  hcan3.Init.SyncJumpWidth = CAN_SJW_1TQ;
  hcan3.Init.TimeSeg1 = CAN_BS1_1TQ;
  hcan3.Init.TimeSeg2 = CAN_BS2_1TQ;
  hcan3.Init.TimeTriggeredMode = DISABLE;
  hcan3.Init.AutoBusOff = DISABLE;
  hcan3.Init.AutoWakeUp = DISABLE;
  hcan3.Init.AutoRetransmission = DISABLE;
  hcan3.Init.ReceiveFifoLocked = DISABLE;
  hcan3.Init.TransmitFifoPriority = DISABLE;
  if (HAL_CAN_Init(&hcan3) != HAL_OK)
  {
	Error_Handler();
  }
  /* USER CODE BEGIN CAN3_Init 2 */
 
  /* USER CODE END CAN3_Init 2 */
 
}

HAL_CAN_MspInit is called every time from MX_CANx_Init which may fail if the bus stays dominant for more than 10 ms (which may happen due to installation error if CAN low is shorted to GND). The logical reaction would be to try to reinitialize it later hoping that the abnormal situation is resolved. But if it took, let's say, 10 attempts, to successfully initialize CAN, the counter will be equal to 10. Now if I deinitialize CAN, the clock would not be disabled. How to avoid this situation?

1 ACCEPTED SOLUTION

Accepted Solutions
SofLit
ST Employee

Dear Igor,

You're right:

- To use CAN1: only CAN 1 clock should be enabled.

- To use CAN2: CAN 1 and CAN2 clocks should be enabled.

- To use CAN3: only CAN 3 clock should be enabled. CAN3 is independent from CAN1 and CAN2.

It's an issue with CubeMx code generation.

Issue raised internally to be fixed. Ticket number: 118954.

Thank you for reporting this.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

View solution in original post

8 REPLIES 8
Sara BEN HADJ YAHYA
ST Employee

Hi @Community member​ 

Thanks for posting your feedback.

I doubt that this issue is related to the STM32CubeMX tool. Please correct me if I have misunderstood.

I'm adding @Imen DAHMEN​ ​ to review from her side and provide a status on this reported issue.

Regards,

Sara.

SofLit
ST Employee

Dear Igor,

You're right:

- To use CAN1: only CAN 1 clock should be enabled.

- To use CAN2: CAN 1 and CAN2 clocks should be enabled.

- To use CAN3: only CAN 3 clock should be enabled. CAN3 is independent from CAN1 and CAN2.

It's an issue with CubeMx code generation.

Issue raised internally to be fixed. Ticket number: 118954.

Thank you for reporting this.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

The issue is related to code generated by both CubeIDE and CubeMX tools, but I'm not sure if it's an issue with the tool itself or the firmware package.

Hi @Community member​ ,

You are right, it is a CubeMX issue and as @Community member​  mentioned, this issue is raised to the de team.

We will keep you posted with the updates.

Thanks,

Sara.

Thanks for the fast reply.

From where can I check the status of this ticket? Is it publicly available?

Hello @Community member​ ,

Unfortunately the Ticket is not available for the publlic. Once the issue is fixed we will inform you in this thread.

Thanks for your contribution,

Sara.

Sara BEN HADJ YAHYA
ST Employee

Hello @Community member,

This issue is fixed in STM32CubeMX latest release.

V6.5.0 is now available under this Link.

Thanks for your contribution.

Sara.

EGold.4
Associate

I believe I am using the very latest versions, and a form of this bug still exists.

The decision of when to enable and disable CAN clock 1 is not a counter based solution. You have to assume the init and deinits will be called at any time in any order. You must use a different system to register when each CAN is needed or no longer needed, and only deinit clock 1 when none are needed.

I have worked around this bug with user code that uses a flag per channel. Once any init is called, it turns on clock 1 as well as the required one. At every deinit, i clear the relevant flag, then check if there are any other flags still set. if not, only then do I stop clock 1.