STM32F7 Bug in CAN clock management
- December 3, 2021
- 4 replies
- 3909 views
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?
