食用指南:本文主要内容为梳理CAN初始化函数主要运行逻辑及重点功能实现代码的详尽解析。函数源码在文末,建议在阅读源码之后观看。
CAN相关寄存器图:


主要逻辑分析:

下面分点梳理函数的主要逻辑(注意逻辑序号,后文依次为点进行分析):

  1. 首先,函数会检查传入的参数hcan是否为NULL,如果是NULL,则返回错误代码HAL_ERROR

  2. 接下来,函数会使用assert_param宏对传入的hcan结构体的各个参数进行检查,确保它们的取值范围符合要求。

  3. 如果宏USE_HAL_CAN_REGISTER_CALLBACKS的值为1并且hcan的状态为HAL_CAN_STATE_RESET,则表示使用了回调函数,并且需要将回调函数重置为默认的“legacy”函数。如果hcan->MspInitCallback为空,则将其设置为默认的初始化函数HAL_CAN_MspInit。然后调用hcanMspInitCallback函数,用于初始化底层硬件。

  4. 如果USE_HAL_CAN_REGISTER_CALLBACKS的值不为1或者hcan的状态不为HAL_CAN_STATE_RESET,则直接调用HAL_CAN_MspInit函数初始化底层硬件。

  5. 在初始化之前,首先需要将CAN控制器从睡眠模式唤醒。通过清除CAN_MCR_SLEEP位实现。

  1. 接着,获取当前的系统滴答计数器值tickstart,用于超时判断。

  2. 检查CAN控制器是否成功离开了睡眠模式。通过检查CAN_MSR_SLAK位,如果该位为0,则表示成功离开了睡眠模式。如果超时时间超过了预设的超时值CAN_TIMEOUT_VALUE,则更新错误代码并返回错误。

  3. 发送初始化请求,通过设置CAN_MCR_INRQ位实现。

  4. 获取当前的系统滴答计数器值tickstart,用于超时判断。

  5. 等待CAN控制器接受初始化请求。通过检查CAN_MSR_INAK位,如果该位为1,则表示CAN控制器已经接受了初始化请求。如果超时时间超过了预设的超时值CAN_TIMEOUT_VALUE,则更新错误代码并返回错误。

  6. 根据初始化参数设置CAN控制器的各种工作模式和配置,包括时间触发通信模式、自动总线断开管理、自动唤醒模式、自动重传、接收FIFO锁定模式和传输FIFO优先级等。

  1. 设置位时序寄存器BTR,将各个参数值写入寄存器中。

  2. 初始化错误代码和CAN状态。

  3. 返回函数执行状态HAL_OK

重点部分分析

逻辑 3

#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
  if (hcan->State == HAL_CAN_STATE_RESET)
  {
    /* Reset callbacks to legacy functions */
    hcan->RxFifo0MsgPendingCallback  =  HAL_CAN_RxFifo0MsgPendingCallback;  /* Legacy weak RxFifo0MsgPendingCallback  */
    hcan->RxFifo0FullCallback        =  HAL_CAN_RxFifo0FullCallback;        /* Legacy weak RxFifo0FullCallback        */
    hcan->RxFifo1MsgPendingCallback  =  HAL_CAN_RxFifo1MsgPendingCallback;  /* Legacy weak RxFifo1MsgPendingCallback  */
    hcan->RxFifo1FullCallback        =  HAL_CAN_RxFifo1FullCallback;        /* Legacy weak RxFifo1FullCallback        */
    hcan->TxMailbox0CompleteCallback =  HAL_CAN_TxMailbox0CompleteCallback; /* Legacy weak TxMailbox0CompleteCallback */
    hcan->TxMailbox1CompleteCallback =  HAL_CAN_TxMailbox1CompleteCallback; /* Legacy weak TxMailbox1CompleteCallback */
    hcan->TxMailbox2CompleteCallback =  HAL_CAN_TxMailbox2CompleteCallback; /* Legacy weak TxMailbox2CompleteCallback */
    hcan->TxMailbox0AbortCallback    =  HAL_CAN_TxMailbox0AbortCallback;    /* Legacy weak TxMailbox0AbortCallback    */
    hcan->TxMailbox1AbortCallback    =  HAL_CAN_TxMailbox1AbortCallback;    /* Legacy weak TxMailbox1AbortCallback    */
    hcan->TxMailbox2AbortCallback    =  HAL_CAN_TxMailbox2AbortCallback;    /* Legacy weak TxMailbox2AbortCallback    */
    hcan->SleepCallback              =  HAL_CAN_SleepCallback;              /* Legacy weak SleepCallback              */
    hcan->WakeUpFromRxMsgCallback    =  HAL_CAN_WakeUpFromRxMsgCallback;    /* Legacy weak WakeUpFromRxMsgCallback    */
    hcan->ErrorCallback              =  HAL_CAN_ErrorCallback;              /* Legacy weak ErrorCallback              */

    if (hcan->MspInitCallback == NULL)
    {
      hcan->MspInitCallback = HAL_CAN_MspInit; /* Legacy weak MspInit */
    }

    /* Init the low level hardware: CLOCK, NVIC */
    hcan->MspInitCallback(hcan);
  }

让我们一句一句地详细分析这段代码:

#if USE_HAL_CAN_REGISTER_CALLBACKS == 1

这是一个条件编译的预处理指令,它检查宏定义USE_HAL_CAN_REGISTER_CALLBACKS是否等于1。如果等于1,则表示要使用回调函数注册功能。

if (hcan->State == HAL_CAN_STATE_RESET)
{

这是一个条件语句,它检查CAN的状态hcan->State是否等于HAL_CAN_STATE_RESET。只有当CAN处于复位状态时,才会执行接下来的代码块。

/* Reset callbacks to legacy functions */
hcan->RxFifo0MsgPendingCallback  =  HAL_CAN_RxFifo0MsgPendingCallback;  /* Legacy weak RxFifo0MsgPendingCallback  */
……

这一系列的语句将CAN处理器结构体hcan中的回调函数成员设置为默认的回调函数。这些默认的回调函数被称为”Legacy weak”,表示它们是在历史版本中使用的弱定义回调函数。以第一句为例,作用是将HAL_CAN_RxFifo0MsgPendingCallback的回调函数的地址赋值给hcan->RxFifo0MsgPendingCallback,即将CAN模块的RxFIFO0消息待处理回调函数指针指向一个特定的函数。在CAN模块中,当RxFIFO0内有消息待处理时,可以通过注册一个回调函数来通知应用程序进行相应的处理。

if (hcan->MspInitCallback == NULL)
{
  hcan->MspInitCallback = HAL_CAN_MspInit; /* Legacy weak MspInit */
}

这个条件语句检查用户是否已经定义了自己的MspInitCallback回调函数。如果hcan->MspInitCallback为NULL,表示用户没有定义自己的回调函数,则将hcan->MspInitCallback设置为默认的回调函数HAL_CAN_MspInit。这个回调函数被命名为”Legacy weak MspInit”,表示它是在历史版本中使用的弱定义回调函数。

/* Init the low level hardware: CLOCK, NVIC */
hcan->MspInitCallback(hcan);

这行代码调用用户自定义的MspInitCallback回调函数,用于初始化CAN的底层硬件。这个回调函数在用户代码中实现,用于配置CAN控制器的时钟和中断等底层硬件设置。

逻辑4:

#else
  if (hcan->State == HAL_CAN_STATE_RESET)
  {
    /* Init the low level hardware: CLOCK, NVIC */
    HAL_CAN_MspInit(hcan);
  }

这段代码是在宏定义USE_HAL_CAN_REGISTER_CALLBACKS不等于1时执行的条件分支。它也用于初始化CAN的底层硬件,但是没有使用回调函数注册功能。

  1. 如果USE_HAL_CAN_REGISTER_CALLBACKS不等于1,表示不使用回调函数注册功能。

  2. 如果CAN的状态hcan->State等于HAL_CAN_STATE_RESET,表示CAN处于复位状态。在这种情况下,仍然需要初始化底层硬件。

  3. 调用默认的MspInit函数HAL_CAN_MspInit(hcan),用于初始化CAN的底层硬件。这个函数是在HAL库中定义的,用于配置CAN控制器的时钟和中断等底层硬件设置。

通过这段代码,可以看出当不使用回调函数注册功能时,仍然可以通过调用默认的MspInit函数来初始化CAN的底层硬件。这样,用户仍然可以在初始化之前对硬件进行必要的配置。

逻辑7

 while ((hcan->Instance->MSR & CAN_MSR_SLAK) != 0U)
  {
    if ((HAL_GetTick() - tickstart) > CAN_TIMEOUT_VALUE)
    {
      /* Update error code */
      hcan->ErrorCode |= HAL_CAN_ERROR_TIMEOUT;

      /* Change CAN state */
      hcan->State = HAL_CAN_STATE_ERROR;

      return HAL_ERROR;
    }
  }

这段代码是一个循环,用于等待CAN进入睡眠模式解锁状态。

  1. hcan->Instance->MSR & CAN_MSR_SLAK用于检查CAN状态寄存器(MSR)中的SLAK位是否被置位。SLAK位表示CAN是否处于睡眠模式。

  2. 循环条件(hcan->Instance->MSR & CAN_MSR_SLAK) != 0U表示只要CAN处于睡眠模式,就继续循环等待。

  3. 在循环内部,首先使用(HAL_GetTick() - tickstart)计算自循环开始以来经过的时间。HAL_GetTick()函数用于获取系统的滴答计数值,返回的是自系统启动以来的毫秒数。

  4. 如果经过的时间超过了CAN_TIMEOUT_VALUE,表示CAN等待超时。在这种情况下,会执行以下步骤:

    • 更新错误代码hcan->ErrorCode,将HAL_CAN_ERROR_TIMEOUT添加到错误代码中,表示超时错误。
    • 修改CAN的状态为HAL_CAN_STATE_ERROR,表示CAN进入错误状态。
    • 返回HAL_ERROR,表示操作失败。
  5. 如果CAN没有超时,继续循环等待CAN解除睡眠模式的状态。

这段代码的作用是等待CAN进入睡眠模式解锁状态。它通过查询CAN的状态寄存器中的SLAK位,并设置超时时间,确保CAN在一定时间内解锁。如果超时时间内仍然没有解锁,将返回错误。这样可以确保在使用CAN之前,CAN已经解除睡眠模式并准备好进行通信。

逻辑10

SET_BIT(hcan->Instance->MCR, CAN_MCR_INRQ);

  /* Get tick */
  tickstart = HAL_GetTick();

  /* Wait initialisation acknowledge */
  while ((hcan->Instance->MSR & CAN_MSR_INAK) == 0U)
  {
    if ((HAL_GetTick() - tickstart) > CAN_TIMEOUT_VALUE)
    {
      /* Update error code */
      hcan->ErrorCode |= HAL_CAN_ERROR_TIMEOUT;

      /* Change CAN state */
      hcan->State = HAL_CAN_STATE_ERROR;

      return HAL_ERROR;
    }
  }

这段代码的作用是发送CAN初始化请求,并等待CAN初始化的确认。它通过设置CAN模式控制寄存器中的INRQ位,请求CAN初始化。然后使用轮询方式等待CAN状态寄存器中的INAK位被置位,表示CAN已经完成初始化。如果超时时间内仍然没有完成初始化,将返回错误。这样可以确保在使用CAN之前,CAN已经完成了必要的初始化。

  1. SET_BIT(hcan->Instance->MCR, CAN_MCR_INRQ)用于设置CAN模式控制寄存器(MCR)中的INRQ位。INRQ位用于请求CAN初始化。

  2. tickstart = HAL_GetTick()用于获取当前的系统滴答计数值,即从系统启动以来的毫秒数。

  3. 循环条件(hcan->Instance->MSR & CAN_MSR_INAK) == 0U表示只要CAN处于初始化确认状态,就继续循环等待。

  4. 在循环内部,首先使用(HAL_GetTick() - tickstart)计算自循环开始以来经过的时间。

  5. 如果经过的时间超过了CAN_TIMEOUT_VALUE,表示CAN初始化超时。在这种情况下,会执行以下步骤:

    • 更新错误代码hcan->ErrorCode,将HAL_CAN_ERROR_TIMEOUT添加到错误代码中,表示超时错误。
    • 修改CAN的状态为HAL_CAN_STATE_ERROR,表示CAN进入错误状态。
    • 返回HAL_ERROR,表示操作失败。
  6. 如果CAN在超时时间内完成了初始化确认,继续循环等待CAN初始化完成。

    逻辑11

    /* Set the time triggered communication mode */
    if (hcan->Init.TimeTriggeredMode == ENABLE)
    {
     SET_BIT(hcan->Instance->MCR, CAN_MCR_TTCM);
    }
    else
    {
     CLEAR_BIT(hcan->Instance->MCR, CAN_MCR_TTCM);
    }
    
    /* Set the automatic bus-off management */
    if (hcan->Init.AutoBusOff == ENABLE)
    {
     SET_BIT(hcan->Instance->MCR, CAN_MCR_ABOM);
    }
    else
    {
     CLEAR_BIT(hcan->Instance->MCR, CAN_MCR_ABOM);
    }
    
    /* Set the automatic wake-up mode */
    if (hcan->Init.AutoWakeUp == ENABLE)
    {
     SET_BIT(hcan->Instance->MCR, CAN_MCR_AWUM);
    }
    else
    {
     CLEAR_BIT(hcan->Instance->MCR, CAN_MCR_AWUM);
    }
    
    /* Set the automatic retransmission */
    if (hcan->Init.AutoRetransmission == ENABLE)
    {
     CLEAR_BIT(hcan->Instance->MCR, CAN_MCR_NART);
    }
    else
    {
     SET_BIT(hcan->Instance->MCR, CAN_MCR_NART);
    }
    
    /* Set the receive FIFO locked mode */
    if (hcan->Init.ReceiveFifoLocked == ENABLE)
    {
     SET_BIT(hcan->Instance->MCR, CAN_MCR_RFLM);
    }
    else
    {
     CLEAR_BIT(hcan->Instance->MCR, CAN_MCR_RFLM);
    }
    
    /* Set the transmit FIFO priority */
    if (hcan->Init.TransmitFifoPriority == ENABLE)
    {
     SET_BIT(hcan->Instance->MCR, CAN_MCR_TXFP);
    }
    else
    {
     CLEAR_BIT(hcan->Instance->MCR, CAN_MCR_TXFP);
    }
    

    通过这段代码,可以根据用户在hcan->Init结构体中的配置,设置或清除CAN控制器的不同模式和功能位。这样可以根据应用需求灵活地配置CAN控制器的行为。

  7. hcan->Init.TimeTriggeredMode用于判断是否启用时间触发通信模式。如果启用,会设置CAN模式控制寄存器(MCR)中的TTCM位;否则会清除TTCM位。

  8. hcan->Init.AutoBusOff用于判断是否启用自动总线关闭管理。如果启用,会设置MCR寄存器中的ABOM位;否则会清除ABOM位。

  9. hcan->Init.AutoWakeUp用于判断是否启用自动唤醒模式。如果启用,会设置MCR寄存器中的AWUM位;否则会清除AWUM位。

  10. hcan->Init.AutoRetransmission用于判断是否启用自动重传功能。如果启用,会清除MCR寄存器中的NART位;否则会设置NART位。

  11. hcan->Init.ReceiveFifoLocked用于判断是否启用接收FIFO锁定模式。如果启用,会设置MCR寄存器中的RFLM位;否则会清除RFLM位。

  12. hcan->Init.TransmitFifoPriority用于判断是否启用发送FIFO优先级功能。如果启用,会设置MCR寄存器中的TXFP位;否则会清除TXFP位。

    逻辑12

    WRITE_REG(hcan->Instance->BTR, (uint32_t)(hcan->Init.Mode           |
                                             hcan->Init.SyncJumpWidth  |
                                             hcan->Init.TimeSeg1       |
                                             hcan->Init.TimeSeg2       |
                                             (hcan->Init.Prescaler - 1U)));
    

    通过这段代码,可以根据用户在hcan->Init结构体中的配置,计算并设置CAN的位时序和预分频器值,以配置CAN的波特率和位时序参数。这样可以根据应用需求灵活地配置CAN通信的速率和时序。

  13. (uint32_t)(hcan->Init.Mode | hcan->Init.SyncJumpWidth | hcan->Init.TimeSeg1 | hcan->Init.TimeSeg2 | (hcan->Init.Prescaler - 1U))是一个表达式,用于计算要写入BTR寄存器的值。它将CAN的工作模式(Mode)、同步跳跃宽度(SyncJumpWidth)、时间段1(TimeSeg1)、时间段2(TimeSeg2)和预分频器(Prescaler)的值合并为一个32位的值。

  14. WRITE_REG(hcan->Instance->BTR, ...)宏用于将计算得到的值写入CAN的BTR寄存器,以配置CAN的位时序和波特率。WRITE_REG 是一个宏定义,它将给定的值写入指定的寄存器。

函数源码

HAL_StatusTypeDef HAL_CAN_Init(CAN_HandleTypeDef *hcan)
{
  uint32_t tickstart;

  /* Check CAN handle */
  if (hcan == NULL)
  {
    return HAL_ERROR;
  }

  /* Check the parameters */
  assert_param(IS_CAN_ALL_INSTANCE(hcan->Instance));
  assert_param(IS_FUNCTIONAL_STATE(hcan->Init.TimeTriggeredMode));
  assert_param(IS_FUNCTIONAL_STATE(hcan->Init.AutoBusOff));
  assert_param(IS_FUNCTIONAL_STATE(hcan->Init.AutoWakeUp));
  assert_param(IS_FUNCTIONAL_STATE(hcan->Init.AutoRetransmission));
  assert_param(IS_FUNCTIONAL_STATE(hcan->Init.ReceiveFifoLocked));
  assert_param(IS_FUNCTIONAL_STATE(hcan->Init.TransmitFifoPriority));
  assert_param(IS_CAN_MODE(hcan->Init.Mode));
  assert_param(IS_CAN_SJW(hcan->Init.SyncJumpWidth));
  assert_param(IS_CAN_BS1(hcan->Init.TimeSeg1));
  assert_param(IS_CAN_BS2(hcan->Init.TimeSeg2));
  assert_param(IS_CAN_PRESCALER(hcan->Init.Prescaler));

#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
  if (hcan->State == HAL_CAN_STATE_RESET)
  {
    /* Reset callbacks to legacy functions */
    hcan->RxFifo0MsgPendingCallback  =  HAL_CAN_RxFifo0MsgPendingCallback;  /* Legacy weak RxFifo0MsgPendingCallback  */
    hcan->RxFifo0FullCallback        =  HAL_CAN_RxFifo0FullCallback;        /* Legacy weak RxFifo0FullCallback        */
    hcan->RxFifo1MsgPendingCallback  =  HAL_CAN_RxFifo1MsgPendingCallback;  /* Legacy weak RxFifo1MsgPendingCallback  */
    hcan->RxFifo1FullCallback        =  HAL_CAN_RxFifo1FullCallback;        /* Legacy weak RxFifo1FullCallback        */
    hcan->TxMailbox0CompleteCallback =  HAL_CAN_TxMailbox0CompleteCallback; /* Legacy weak TxMailbox0CompleteCallback */
    hcan->TxMailbox1CompleteCallback =  HAL_CAN_TxMailbox1CompleteCallback; /* Legacy weak TxMailbox1CompleteCallback */
    hcan->TxMailbox2CompleteCallback =  HAL_CAN_TxMailbox2CompleteCallback; /* Legacy weak TxMailbox2CompleteCallback */
    hcan->TxMailbox0AbortCallback    =  HAL_CAN_TxMailbox0AbortCallback;    /* Legacy weak TxMailbox0AbortCallback    */
    hcan->TxMailbox1AbortCallback    =  HAL_CAN_TxMailbox1AbortCallback;    /* Legacy weak TxMailbox1AbortCallback    */
    hcan->TxMailbox2AbortCallback    =  HAL_CAN_TxMailbox2AbortCallback;    /* Legacy weak TxMailbox2AbortCallback    */
    hcan->SleepCallback              =  HAL_CAN_SleepCallback;              /* Legacy weak SleepCallback              */
    hcan->WakeUpFromRxMsgCallback    =  HAL_CAN_WakeUpFromRxMsgCallback;    /* Legacy weak WakeUpFromRxMsgCallback    */
    hcan->ErrorCallback              =  HAL_CAN_ErrorCallback;              /* Legacy weak ErrorCallback              */

    if (hcan->MspInitCallback == NULL)
    {
      hcan->MspInitCallback = HAL_CAN_MspInit; /* Legacy weak MspInit */
    }

    /* Init the low level hardware: CLOCK, NVIC */
    hcan->MspInitCallback(hcan);
  }

#else
  if (hcan->State == HAL_CAN_STATE_RESET)
  {
    /* Init the low level hardware: CLOCK, NVIC */
    HAL_CAN_MspInit(hcan);
  }
#endif /* (USE_HAL_CAN_REGISTER_CALLBACKS) */

  /* Exit from sleep mode */
  CLEAR_BIT(hcan->Instance->MCR, CAN_MCR_SLEEP);

  /* Get tick */
  tickstart = HAL_GetTick();

  /* Check Sleep mode leave acknowledge */
  while ((hcan->Instance->MSR & CAN_MSR_SLAK) != 0U)
  {
    if ((HAL_GetTick() - tickstart) > CAN_TIMEOUT_VALUE)
    {
      /* Update error code */
      hcan->ErrorCode |= HAL_CAN_ERROR_TIMEOUT;

      /* Change CAN state */
      hcan->State = HAL_CAN_STATE_ERROR;

      return HAL_ERROR;
    }
  }

  /* Request initialisation */
  SET_BIT(hcan->Instance->MCR, CAN_MCR_INRQ);

  /* Get tick */
  tickstart = HAL_GetTick();

  /* Wait initialisation acknowledge */
  while ((hcan->Instance->MSR & CAN_MSR_INAK) == 0U)
  {
    if ((HAL_GetTick() - tickstart) > CAN_TIMEOUT_VALUE)
    {
      /* Update error code */
      hcan->ErrorCode |= HAL_CAN_ERROR_TIMEOUT;

      /* Change CAN state */
      hcan->State = HAL_CAN_STATE_ERROR;

      return HAL_ERROR;
    }
  }

  /* Set the time triggered communication mode */
  if (hcan->Init.TimeTriggeredMode == ENABLE)
  {
    SET_BIT(hcan->Instance->MCR, CAN_MCR_TTCM);
  }
  else
  {
    CLEAR_BIT(hcan->Instance->MCR, CAN_MCR_TTCM);
  }

  /* Set the automatic bus-off management */
  if (hcan->Init.AutoBusOff == ENABLE)
  {
    SET_BIT(hcan->Instance->MCR, CAN_MCR_ABOM);
  }
  else
  {
    CLEAR_BIT(hcan->Instance->MCR, CAN_MCR_ABOM);
  }

  /* Set the automatic wake-up mode */
  if (hcan->Init.AutoWakeUp == ENABLE)
  {
    SET_BIT(hcan->Instance->MCR, CAN_MCR_AWUM);
  }
  else
  {
    CLEAR_BIT(hcan->Instance->MCR, CAN_MCR_AWUM);
  }

  /* Set the automatic retransmission */
  if (hcan->Init.AutoRetransmission == ENABLE)
  {
    CLEAR_BIT(hcan->Instance->MCR, CAN_MCR_NART);
  }
  else
  {
    SET_BIT(hcan->Instance->MCR, CAN_MCR_NART);
  }

  /* Set the receive FIFO locked mode */
  if (hcan->Init.ReceiveFifoLocked == ENABLE)
  {
    SET_BIT(hcan->Instance->MCR, CAN_MCR_RFLM);
  }
  else
  {
    CLEAR_BIT(hcan->Instance->MCR, CAN_MCR_RFLM);
  }

  /* Set the transmit FIFO priority */
  if (hcan->Init.TransmitFifoPriority == ENABLE)
  {
    SET_BIT(hcan->Instance->MCR, CAN_MCR_TXFP);
  }
  else
  {
    CLEAR_BIT(hcan->Instance->MCR, CAN_MCR_TXFP);
  }

  /* Set the bit timing register */
  WRITE_REG(hcan->Instance->BTR, (uint32_t)(hcan->Init.Mode           |
                                            hcan->Init.SyncJumpWidth  |
                                            hcan->Init.TimeSeg1       |
                                            hcan->Init.TimeSeg2       |
                                            (hcan->Init.Prescaler - 1U)));

  /* Initialize the error code */
  hcan->ErrorCode = HAL_CAN_ERROR_NONE;

  /* Initialize the CAN state */
  hcan->State = HAL_CAN_STATE_READY;

  /* Return function status */
  return HAL_OK;
}

/**
  * @brief  Deinitializes the CAN peripheral registers to their default
  *         reset values.
  * @param  hcan pointer to a CAN_HandleTypeDef structure that contains
  *         the configuration information for the specified CAN.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_CAN_DeInit(CAN_HandleTypeDef *hcan)
{
  /* Check CAN handle */
  if (hcan == NULL)
  {
    return HAL_ERROR;
  }

  /* Check the parameters */
  assert_param(IS_CAN_ALL_INSTANCE(hcan->Instance));

  /* Stop the CAN module */
  (void)HAL_CAN_Stop(hcan);

#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
  if (hcan->MspDeInitCallback == NULL)
  {
    hcan->MspDeInitCallback = HAL_CAN_MspDeInit; /* Legacy weak MspDeInit */
  }

  /* DeInit the low level hardware: CLOCK, NVIC */
  hcan->MspDeInitCallback(hcan);

#else
  /* DeInit the low level hardware: CLOCK, NVIC */
  HAL_CAN_MspDeInit(hcan);
#endif /* (USE_HAL_CAN_REGISTER_CALLBACKS) */

  /* Reset the CAN peripheral */
  SET_BIT(hcan->Instance->MCR, CAN_MCR_RESET);

  /* Reset the CAN ErrorCode */
  hcan->ErrorCode = HAL_CAN_ERROR_NONE;

  /* Change CAN state */
  hcan->State = HAL_CAN_STATE_RESET;

  /* Return function status */
  return HAL_OK;
}