Typical HAL FDCAN use cases for migration

This section provides information about the typical FDCAN use cases for migration from HAL1 to HAL2.

Note

The programming model has been kept consistent between HAL1 and HAL2 as follows:

First, initialization and configuration are performed, including setting up FDCAN filters and other parameters such as the clock calibration unit.

Then, the FDCAN peripheral is started. After that, messages can be added to the TX FIFO or buffer, and the system notifies when RX messages are received.

Users can choose to either:

  • Activate interrupts and handle incoming messages through corresponding callbacks.

  • Monitor TX and RX message reception using dedicated buffers and FIFOs through specific monitoring functions.

The changes in HAL2 focus on two key improvements:

  • Interrupt management:

    • The interrupt line configuration function was split into two separate functions, one to assign grouped interrupts and another to assign individual interrupts to interrupt line(s) (e.g., line 0 or line 1) based on the availability of interrupt grouping in the STM32 device.

    • The single notification activation function was divided into distinct functions to enable interrupt sources and to enable the corresponding interrupt lines.

  • Clock calibration:

    • The clock calibration configuration was modularized by splitting it into a function to enable or bypass the clock calibration unit, and another to set the calibration parameters.

    • Additionally, the calibration callback was separated into dedicated functions, and a dedicated IRQ handler was introduced to manage clock calibration interrupts independently.


Use case: Initialization, configuration and filter configuration

Sequence diagram

  @startuml
            participant "User application" as App
  participant "<font color=black><b>HAL FDCAN"  as FDCAN #19DD3A

  == FDCAN HAL Initialization Global configuration ==
  App -> FDCAN: HAL_FDCAN_Init()
  note right
  Initialize the FDCAN handle and associate an instance
  Configure the FDCAN according to the application requirements
  end note
  App <--- FDCAN:<color green>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n

   == Acceptance filters configuration  ==

  App -> FDCAN: HAL_FDCAN_ConfigFilter()
  FDCAN --> App:<color green>HAL_OK
  App -> FDCAN: HAL_FDCAN_ConfigGlobalFilter()
  FDCAN --> App:<color green>HAL_OK
  App -> FDCAN: HAL_FDCAN_Start()
  FDCAN --> App:<color green>HAL_OK
  Note over App#lightyellow: Prepare the Tx Header

  @enduml

Code snippet

HAL1

HAL2

hfdcan.Instance                = FDCAN1;
hfdcan.Init.FrameFormat        = FDCAN_FRAME_FD_BRS;
hfdcan.Init.Mode               = FDCAN_MODE_NORMAL;
hfdcan.Init.AutoRetransmission = ENABLE;
hfdcan.Init.TransmitPause      = DISABLE;
hfdcan.Init.ProtocolException  = ENABLE;


hfdcan.Init.NominalPrescaler     = 0x1;
hfdcan.Init.NominalSyncJumpWidth = 0x8;
hfdcan.Init.NominalTimeSeg1      = 0x1F;
hfdcan.Init.NominalTimeSeg2      = 0x8;


hfdcan.Init.DataPrescaler     = 0x1;
hfdcan.Init.DataSyncJumpWidth = 0x4;
hfdcan.Init.DataTimeSeg1      = 0x5;
hfdcan.Init.DataTimeSeg2      = 0x4;




hfdcan.Init.StdFiltersNbr       = 1;
hfdcan.Init.ExtFiltersNbr       = 0;
hfdcan.Init.TxFifoQueueMode     = FDCAN_TX_FIFO_OPERATION;

hfdcan.Init.ClockDivider        = FDCAN_CLOCK_DIV2;
HAL_FDCAN_Init(&hfdcan);

sFilterConfig.IdType       = FDCAN_STANDARD_ID;
sFilterConfig.FilterIndex  = 0;
sFilterConfig.FilterType   = FDCAN_FILTER_MASK;
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
sFilterConfig.FilterID1    = 0x111;
sFilterConfig.FilterID2    = 0x7FF;
HAL_FDCAN_ConfigFilter(&hfdcan, &sFilterConfig);

HAL_FDCAN_ConfigGlobalFilter(&hfdcan,
                             FDCAN_REJECT,
                             FDCAN_REJECT,
                             FDCAN_REJECT_REMOTE,
                             FDCAN_REJECT_REMOTE);


HAL_FDCAN_Start(&hfdcan);
HAL_FDCAN_Init(&hfdcan, HAL_FDCAN1);
fdcan_config.frame_format        = HAL_FDCAN_FRAME_FORMAT_FD_BRS;
fdcan_config.mode                = HAL_FDCAN_MODE_NORMAL;
fdcan_config.auto_retransmission = HAL_FDCAN_AUTO_RETRANSMISSION_ENABLE;
fdcan_config.transmit_pause      = HAL_FDCAN_TRANSMIT_PAUSE_DISABLE;
fdcan_config.protocol_exception  = HAL_FDCAN_PROTOCOL_EXCEPTION_ENABLE;

hal_fdcan_nominal_bit_timing_t fdcan_nominal_bit_timing;
fdcan_nominal_bit_timing.nominal_prescaler  = 1;
fdcan_nominal_bit_timing.nominal_jump_width = 8;
fdcan_nominal_bit_timing.nominal_time_seg1  = 31;
fdcan_nominal_bit_timing.nominal_time_seg2  = 8;

hal_fdcan_data_bit_timing_t fdcan_data_bit_timing;
fdcan_data_bit_timing.data_prescaler  = 1;
fdcan_data_bit_timing.data_jump_width = 4;
fdcan_data_bit_timing.data_time_seg1  = 5;
fdcan_data_bit_timing.data_time_seg2  = 4;

fdcan_config.nominal_bit_timing = fdcan_nominal_bit_timing;
fdcan_config.data_bit_timing    = fdcan_data_bit_timing;

fdcan_config.std_filters_nbr        = 1;
fdcan_config.ext_filters_nbr        = 0;
fdcan_config.tx_fifo_queue_mode     = HAL_FDCAN_TX_MODE_FIFO;

HAL_FDCAN_SetClockDivider(&hfdcan, HAL_FDCAN_CLOCK_DIV_2);
HAL_FDCAN_SetConfig(&hfdcan, &fdcan_config);

filter.id_type       = HAL_FDCAN_ID_STANDARD;
filter.filter_index  = 0U;
filter.filter_type   = HAL_FDCAN_FILTER_TYPE_CLASSIC;
filter.filter_config = HAL_FDCAN_FILTER_TO_RX_FIFO_0;
filter.filter_id1    = 0x111;
filter.filter_id2    = 0x7FF;
HAL_FDCAN_SetFilter(&hfdcan, &filter);

/* Configure global filter to reject non-matching and remote frames */
global_filter.acceptance_non_matching_std = HAL_FDCAN_NON_MATCHING_REJECT;
global_filter.acceptance_non_matching_ext = HAL_FDCAN_NON_MATCHING_REJECT;
global_filter.acceptance_remote_std       = HAL_FDCAN_REMOTE_REJECT;
global_filter.acceptance_remote_ext       = HAL_FDCAN_REMOTE_REJECT;
HAL_FDCAN_SetGlobalFilter(&hfdcan, &global_filter);

HAL_FDCAN_Start(&hfdcan);

Note

  • In HAL1, if the clock divider is available and set directly within the initialization structure using hfdcan.Init.ClockDivider = clock_divider.

  • In HAL2, the clock divider configuration is separated from the initialization and must be explicitly set by calling HAL_FDCAN_SetClockDivider(&hfdcan, clock_divider).

Key changes

  1. HAL2 separates global configuration from initialization using HAL_FDCAN_SetConfig().

  2. HAL2 configures filters using HAL_FDCAN_SetFilter() and HAL_FDCAN_SetGlobalFilter().

  3. When supported by the device, HAL2 configures the clock divider using HAL_FDCAN_SetClockDivider().

Use case: Communication with FIFO monitoring (polling)

The changes between HAL1 and HAL2 are limited to naming; the overall programming model remains unchanged. For transmission, add messages to the TX FIFO. For reception, first monitor the RX FIFO level, and when a message is received, read it from the FIFO.

Sequence diagram

  @startuml
            participant "User application" as App
  participant "<font color=black><b>HAL FDCAN"  as FDCAN #19DD3A

  Note over App#lightyellow: Prepare the Tx Header \n \n

  == FDCAN HAL Message Transmit  ==
  App-> FDCAN: HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan, &TxHeader, TxData[0])
  FDCAN --> App:<color green>HAL_OK
  == FDCAN HAL Message Receive  ==
  loop#darkgrey  if fifo_level != 1
     App -> FDCAN: HAL_FDCAN_GetRxFifoFillLevel()
     note right
        Retrieve the Rx FIFO fill level
     endnote

     FDCAN --> App: fifo_level
  end

  App -> FDCAN: HAL_FDCAN_GetRxMessage()
  note right
     Retrieve an FDCAN frame from the Rx FIFO
  endnote

  FDCAN --> App:<color green>HAL_OK

  @enduml

Code snippet

HAL1

HAL2

/* Prepare FDCAN transmit message header */
TxHeader.Identifier          = 0x111;
TxHeader.IdType              = FDCAN_STANDARD_ID;
TxHeader.TxFrameType         = FDCAN_DATA_FRAME;
TxHeader.DataLength          = FDCAN_DLC_BYTES_8;
TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
TxHeader.BitRateSwitch       = FDCAN_BRS_ON;
TxHeader.FDFormat            = FDCAN_FD_CAN;
TxHeader.TxEventFifoControl  = FDCAN_NO_TX_EVENTS;
TxHeader.MessageMarker       = 0;

HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan, &TxHeader, &TxData[0]);

while(HAL_FDCAN_GetRxFifoFillLevel(&hfdcan, FDCAN_RX_FIFO0) < 1);

HAL_FDCAN_GetRxMessage(&hfdcan, FDCAN_RX_FIFO0, &RxHeader, RxData);
/* Prepare FDCAN transmit message header */
tx_element_header.b.identifier            = 0x111,
tx_element_header.b.identifier_type       = HAL_FDCAN_ID_STANDARD,
tx_element_header.b.frame_type            = HAL_FDCAN_FRAME_DATA,
tx_element_header.b.data_length           = HAL_FDCAN_DATA_LEN_CAN_FDCAN_8_BYTE,
tx_element_header.b.error_state_indicator = HAL_FDCAN_ERROR_STATE_IND_ACTIVE,
tx_element_header.b.bit_rate_switch       = HAL_FDCAN_BIT_RATE_SWITCH_ON,
tx_element_header.b.frame_format          = HAL_FDCAN_HEADER_FRAME_FORMAT_FD_CAN,
tx_element_header.b.event_fifo_control    = HAL_FDCAN_FIFO_STORE_TX_EVENTS,
tx_element_header.b.message_marker        = 0U,

HAL_FDCAN_ReqTransmitMsgFromFIFOQ(pFDCAN, &tx_element_header, pTxData);

while(HAL_FDCAN_GetRxFifoFillLevel(pFDCAN, HAL_FDCAN_RX_FIFO_0, &rx_elements) < 1);

HAL_FDCAN_GetReceivedMessage(pFDCAN, HAL_FDCAN_RX_FIFO_0, &rx_element_header, RxBuffer);

Key changes

  1. In HAL2, the transmit API is HAL_FDCAN_ReqTransmitMsgFromFIFOQ() (instead of HAL_FDCAN_AddMessageToTxFifoQ()).

  2. In HAL2, the receive API is HAL_FDCAN_GetReceivedMessage() (instead of HAL_FDCAN_GetRxMessage()).

Related examples in HAL2

Example Name

Description

example_hal_fdcan_two_boards_com_polling_controller/example_hal_fdcan_two_boards_com_polling_responder

How to handle an infinite number of transmit-receive transactions between two boards based on the FDCAN-bus protocol with the HAL API, in polling mode.


Use case: Communication with buffers monitoring (polling)

The changes between HAL1 and HAL2 are limited to naming; the overall programming model remains unchanged. For transmission, add the message to a specific TX buffer and enable the transmission request for that buffer. The transmission completion status is obtained by monitoring the TX buffer state. For reception, monitor the RX buffer for incoming messages, and once a message is available, read it from the RX buffer.

Sequence diagram

  @startuml

  participant "User application" as App #AliceBlue
  participant "<b>HAL FDCAN" as FDCAN #19DD3A

  == Dedicated Tx Buffer Transmission (Monitoring) ==

  note over App
  <b>Step 1: Fill a dedicated Tx buffer</b>
  The application prepares the Tx header and data, then fills the selected Tx buffer.
  end note
  App -> FDCAN: HAL_FDCAN_AddMessageToTxBuffer()
  FDCAN --> App: HAL_OK

  note over App
  <b>Step 2: Request transmission from the buffer</b>
  The application requests transmission from the filled Tx buffer.
  end note
  App -> FDCAN: HAL_FDCAN_EnableTxBufferRequest()
  FDCAN --> App: HAL_OK

  note over App
  <b>Step 3: Poll for transmission completion</b>
  The application polls the buffer status until transmission is complete.
  end note
  loop #lightblue until Tx complete
      App -> FDCAN: HAL_FDCAN_IsTxBufferMessagePending()
      alt Tx pending
          FDCAN --> App: <color green>1
      else Tx not pending
          FDCAN --> App: <color orange>0
      end
  end

  == Dedicated Rx Buffer Monitoring ==

  note over App
  <b>Step 4: Poll for new message in a dedicated Rx buffer</b>
  The application checks if a new message has been received in the specified Rx buffer.
  end note
  App -> FDCAN: HAL_FDCAN_IsRxBufferMessageAvailable()
  alt #LightSteelBlue New message received
      FDCAN --> App: <color green>1
      note right
      The flag is cleared by the API.
      The application can now process the received message.
      end note
      App -> FDCAN: HAL_FDCAN_GetRxMessage()
      FDCAN --> App: <color green>HAL_OK
  else #LightCyan No new message
      FDCAN --> App: <color red>0
      note right
      No new message is present in the buffer.
      end note
  end


  @enduml

Code snippet

HAL1

HAL2

/* Prepare FDCAN transmit message header */
TxHeader.Identifier          = 0x111;
TxHeader.IdType              = FDCAN_STANDARD_ID;
TxHeader.TxFrameType         = FDCAN_DATA_FRAME;
TxHeader.DataLength          = FDCAN_DLC_BYTES_8;
TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
TxHeader.BitRateSwitch       = FDCAN_BRS_ON;
TxHeader.FDFormat            = FDCAN_FD_CAN;
TxHeader.TxEventFifoControl  = FDCAN_NO_TX_EVENTS;
TxHeader.MessageMarker       = 0;

HAL_FDCAN_AddMessageToTxBuffer(&hfdcan, &TxHeader, TxData, FDCAN_TX_BUFFER0);

HAL_FDCAN_EnableTxBufferRequest(&hfdcan, FDCAN_TX_BUFFER0);

while(HAL_FDCAN_IsTxBufferMessagePending(&hfdcan, FDCAN_TX_BUFFER0) < 1);

while(HAL_FDCAN_IsRxBufferMessageAvailable(&hfdcan, FDCAN_RX_BUFFER1) < 1);

HAL_FDCAN_GetRxMessage(&hfdcan, FDCAN_RX_BUFFER1, &RxHeader, RxData);
/* Prepare FDCAN transmit message header */
tx_element_header.b.identifier            = 0x111,
tx_element_header.b.identifier_type       = HAL_FDCAN_ID_STANDARD,
tx_element_header.b.frame_type            = HAL_FDCAN_FRAME_DATA,
tx_element_header.b.data_length           = HAL_FDCAN_DATA_LEN_CAN_FDCAN_8_BYTE,
tx_element_header.b.error_state_indicator = HAL_FDCAN_ERROR_STATE_IND_ACTIVE,
tx_element_header.b.bit_rate_switch       = HAL_FDCAN_BIT_RATE_SWITCH_ON,
tx_element_header.b.frame_format          = HAL_FDCAN_HEADER_FRAME_FORMAT_FD_CAN,
tx_element_header.b.event_fifo_control    = HAL_FDCAN_FIFO_STORE_TX_EVENTS,
tx_element_header.b.message_marker        = 0U,

HAL_FDCAN_FillMessageToBuffer(&hfdcan, &TxHeader, TxData, HAL_FDCAN_TX_BUFFER_0);

HAL_FDCAN_ReqTransmitMsgFromBuffer(&hfdcan, HAL_FDCAN_TX_BUFFER_0);

while(HAL_FDCAN_IsTxBufferMessagePending(&hfdcan, HAL_FDCAN_TX_BUFFER_0) != HAL_FDCAN_TX_BUFFER_NOT_PENDING);

while(HAL_FDCAN_GetRxBufferMessageStatusAndClearFlag(&hfdcan, HAL_FDCAN_RX_BUFFER_1) != HAL_FDCAN_RX_BUFFER_NEW_MSG_RECEIVED);

HAL_FDCAN_GetReceivedMessage(&hfdcan, FDCAN_RX_BUFFER1, &RxHeader, RxData);

Key changes

  1. In HAL2, the transmit request is performed using HAL_FDCAN_ReqTransmitMsgFromBuffer().

  2. In HAL2, the receive buffer monitoring uses HAL_FDCAN_GetRxBufferMessageStatusAndClearFlag().

Related examples in HAL2

No related example in HAL2.


Use case: Communication in interrupt mode

The FDCAN interrupt management has been updated to use a more modular approach.

  • Interrupt to line configuration:

    In HAL1, the configuration of FDCAN interrupt lines is performed using the HAL_FDCAN_ConfigInterruptLines() function. When interrupt grouping is supported by the given STM32 device, grouped interrupt flags such as FDCAN_IT_GROUP_SMSG and FDCAN_IT_GROUP_RX_FIFO0 are collectively assigned to the chosen interrupt line (line 0 and/or line 1). If interrupt grouping is not supported by the device, individual interrupt sources like FDCAN_IT_TX_COMPLETE and FDCAN_IT_RX_FIFO0_NEW_MESSAGE are assigned directly to the chosen interrupt line (line 0 and/or line 1).

    In HAL2, when the interrupt grouping feature is available on the STM32 device, HAL_FDCAN_SetInterruptGroupsToLine() is used to assign grouped interrupts (e.g., HAL_FDCAN_IT_GROUP_STATUS_MSG and HAL_FDCAN_IT_GROUP_RX_FIFO_0) to the chosen interrupt line (line 0 and/or line 1). When interrupt grouping is not available, HAL_FDCAN_SetInterruptsToLine() is used to assign individual interrupts (e.g., HAL_FDCAN_IT_TX_COMPLETE and HAL_FDCAN_IT_RX_FIFO_0_NEW_MSG) to the chosen interrupt line (line 0 and/or line 1).

Interrupt grouping feature

HAL1

HAL2

Available

Use HAL_FDCAN_ConfigInterruptLines() with grouped interrupts (e.g., FDCAN_IT_GROUP_SMSG)

Use HAL_FDCAN_SetInterruptGroupsToLine() to assign grouped interrupts (e.g., HAL_FDCAN_IT_GROUP_STATUS_MSG)

Not available

Use HAL_FDCAN_ConfigInterruptLines() with individual interrupts (e.g., FDCAN_IT_TX_COMPLETE)

Use HAL_FDCAN_SetInterruptsToLine() to assign individual interrupts (e.g., HAL_FDCAN_IT_TX_COMPLETE)

  • Interrupt activation

    In HAL1, the function HAL_FDCAN_ActivateNotification() is used to enable specific FDCAN interrupt notifications in a single call, meaning it enables both the requested interrupts and the interrupt line(s) (line 0 and/or line 1) on which the corresponding interrupts are set.

    In HAL2, interrupt management is designed to be more granular and modular:

    • HAL_FDCAN_EnableInterruptLines() is used to activate the interrupt line(s) (line 0 and/or line 1).

    • HAL_FDCAN_EnableInterrupts() enables interrupt sources (e.g. HAL_FDCAN_IT_RX_FIFO_0_NEW_MSG).

    • For transmit complete interrupts like FDCAN_IT_TX_COMPLETE, the dedicated function HAL_FDCAN_EnableTxBufferCompleteInterrupts() must also be called to enable the corresponding buffer interrupts.

    • Similarly, for transmit abort interrupts such as FDCAN_IT_TX_ABORT_COMPLETE, the function HAL_FDCAN_EnableTxBufferCancellationInterrupts() must also be called.

    • Additionally, HAL_FDCAN_CCU_EnableInterrupts() is provided to enable clock calibration unit interrupts.

For more details see HAL FDCAN enables interrupt notifications..

Sequence diagram

  @startuml
            participant "User application" as App
  participant "<font color=black><b>HAL FDCAN"  as FDCAN #19DD3A
  alt #LightSteelBlue Interrupt grouping available
  App -> FDCAN: HAL_FDCAN_ConfigInterruptLines(&hfdcan, FDCAN_IT_GROUP_SMSG | FDCAN_IT_GROUP_RX_FIFO0, FDCAN_INTERRUPT_LINE0)
  else #LightCyan   else
  App -> FDCAN: HAL_FDCAN_ConfigInterruptLines(&hfdcan, FDCAN_IT_TX_COMPLETE | FDCAN_IT_RX_FIFO0_NEW_MESSAGE, FDCAN_INTERRUPT_LINE0)
  end
  FDCAN --> App:<color green>HAL_OK
  App -> FDCAN: HAL_FDCAN_ActivateNotification(&hfdcan, FDCAN_IT_TX_COMPLETE | FDCAN_IT_RX_FIFO0_NEW_MESSAGE, FDCAN_TX_BUFFER0)

  FDCAN --> App:<color green>HAL_OK

  App -> FDCAN: HAL_FDCAN_Start()
  FDCAN --> App:<color green>HAL_OK
  Note over App#lightyellow: Prepare the Tx Header

  == FDCAN HAL Message Transmit  ==
  App-> FDCAN: HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan, &TxHeader, TxData[0])
  FDCAN --> App:<color green>HAL_OK
  activate App #1000DA
  hnote over App
  <font color=blue>transmission ongoing...
  end note
  NVIC <-  : FDCAN interrupt Tx Complete
  NVIC -> FDCAN: HAL_FDCAN_IRQHandler
  FDCAN -> App: HAL_FDCAN_TxBufferCompleteCallback()
  Note over App#lightyellow: TransmissionCompleteFlag = 1U
  App --> FDCAN:
  deactivate App
  FDCAN --> NVIC:
  NVIC --> :
  deactivate App
  == FDCAN HAL Message Receive  ==
  NVIC <- : FDCAN interrupt Rx FIFO New Message
  NVIC -> FDCAN: HAL_FDCAN_IRQHandler
  FDCAN -> App: HAL_FDCAN_RxFifo0Callback()
  Note over App#lightyellow: if(RxFifo0ITs  = FDCAN_IT_RX_FIFO0_NEW_MESSAGE) \n Notification_flag  = 1U
  FDCAN <-- App
  NVIC <-- FDCAN
  NVIC --> :
  alt Notification_flag  == 1U
  App -> FDCAN: HAL_FDCAN_GetRxMessage()
  note right
     Retrieve an FDCAN frame from the Rx FIFO
  endnote

  FDCAN --> App:<color green>HAL_OK
  end

  @enduml

Code snippet

HAL1

HAL2

/* if interrupt grouping available */
HAL_FDCAN_ConfigInterruptLines(&hfdcan, FDCAN_IT_GROUP_SMSG
                                      | FDCAN_IT_GROUP_RX_FIFO0,
                                        FDCAN_INTERRUPT_LINE0);
/* else */
HAL_FDCAN_ConfigInterruptLines(&hfdcan, FDCAN_IT_TX_COMPLETE
                                      | FDCAN_IT_RX_FIFO0_NEW_MESSAGE,
                                        FDCAN_INTERRUPT_LINE0);

HAL_FDCAN_ActivateNotification(&hfdcan,
                               FDCAN_IT_TX_COMPLETE |
                               FDCAN_IT_RX_FIFO0_NEW_MESSAGE,
                               FDCAN_TX_BUFFER0);


HAL_FDCAN_Start(&hfdcan);

/* Prepare FDCAN transmit message header */
TxHeader.Identifier          = 0x111;
TxHeader.IdType              = FDCAN_STANDARD_ID;
TxHeader.TxFrameType         = FDCAN_DATA_FRAME;
TxHeader.DataLength          = FDCAN_DLC_BYTES_8;
TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
TxHeader.BitRateSwitch       = FDCAN_BRS_ON;
TxHeader.FDFormat            = FDCAN_FD_CAN;
TxHeader.TxEventFifoControl  = FDCAN_NO_TX_EVENTS;
TxHeader.MessageMarker = 0;

HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan, &TxHeader, &TxData[0]);
while (TransmissionCompleteFlag == 0);

if (Notification_flag == 1)
{
HAL_FDCAN_GetRxMessage(&hfdcan, FDCAN_RX_FIFO0, &RxHeader, RxData);
Notification_flag = 0;
}

void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
  if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET)
  {
    Notification_flag = 1;
  }
}

void HAL_FDCAN_TxBufferCompleteCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t BufferIndexes)
{
  TransmissionCompleteFlag = 1U;
}
/* if interrupt grouping */
HAL_FDCAN_SetInterruptGroupsToLine(&hfdcan, HAL_FDCAN_IT_GROUP_STATUS_MSG
                                          | HAL_FDCAN_IT_GROUP_RX_FIFO_0 ,
                                            HAL_FDCAN_IT_LINE_0 );
/* else */
HAL_FDCAN_SetInterruptsToLine(&hfdcan, HAL_FDCAN_IT_TX_COMPLETE
                                    | HAL_FDCAN_IT_RX_FIFO_0_NEW_MSG ,
                                      HAL_FDCAN_IT_LINE_0 );

HAL_FDCAN_EnableInterruptLines(&hfdcan, HAL_FDCAN_IT_LINE_0);

HAL_FDCAN_EnableTxBufferCompleteInterrupts(&hfdcan, HAL_FDCAN_IT_TX_CPLT_BUFFER_0);

HAL_FDCAN_EnableInterrupts(&hfdcan, HAL_FDCAN_IT_TX_COMPLETE | HAL_FDCAN_IT_RX_FIFO_0_NEW_MSG);

HAL_FDCAN_Start(&hfdcan);

/* Prepare FDCAN transmit message header */
tx_element_header.b.identifier            = 0x111,
tx_element_header.b.identifier_type       = HAL_FDCAN_ID_STANDARD,
tx_element_header.b.frame_type            = HAL_FDCAN_FRAME_DATA,
tx_element_header.b.data_length           = HAL_FDCAN_DATA_LEN_CAN_FDCAN_8_BYTE,
tx_element_header.b.error_state_indicator = HAL_FDCAN_ERROR_STATE_IND_ACTIVE,
tx_element_header.b.bit_rate_switch       = HAL_FDCAN_BIT_RATE_SWITCH_ON,
tx_element_header.b.frame_format          = HAL_FDCAN_HEADER_FRAME_FORMAT_FD_CAN,
tx_element_header.b.event_fifo_control    = HAL_FDCAN_FIFO_STORE_TX_EVENTS,
tx_element_header.b.message_marker        = 0U,

HAL_FDCAN_ReqTransmitMsgFromFIFOQ(hfdcan, &tx_element_header, pTxData);
while (TransmissionCompleteFlag == 0);

if (Notification_flag == 1)
{
  HAL_FDCAN_GetReceivedMessage(hfdcan, HAL_FDCAN_RX_FIFO_0, &rx_element_header, RxBuffer);
  ReceptionFlag = 0;
}

static void HAL_FDCAN_RxFifo0Callback(hal_fdcan_handle_t *hfdcan, uint32_t rx_fifo0_interrupts)
{
  if((rx_fifo0_interrupts & HAL_FDCAN_IT_RX_FIFO_0_NEW_MSG) != RESET)
  {
    Notification_flag = 1;
  }
}

static void HAL_FDCAN_TxBufferCompleteCallback(hal_fdcan_handle_t *hfdcan, uint32_t buffer_indexes)
{
  TransmissionCompleteFlag = 1U;
}

Key changes

  1. HAL2 splits notification configuration into separate APIs to configure interrupts and interrupt line(s).

  2. HAL2 provides dedicated APIs for enabling Tx buffer complete interrupts.

Related examples in HAL2

Example Name

Description

example_hal_fdcan_two_boards_com_it_controller/example_hal_fdcan_two_boards_com_it_responder

How to handle an infinite number of transmit-receive transactions between two boards based on the FDCAN-bus protocol with the HAL API, in interrupt mode.


Use case: Interrupt cancellation

Sequence diagram

  @startuml
            participant "User application" as App
  participant "<font color=black><b>HAL FDCAN"  as FDCAN #19DD3A

  alt #LightSteelBlue Interrupt grouping available
  App -> FDCAN: HAL_FDCAN_ConfigInterruptLines(&hfdcan, FDCAN_IT_GROUP_SMSG, FDCAN_INTERRUPT_LINE0)
  else #LightCyan   Interrupt grouping not available
  App -> FDCAN: HAL_FDCAN_ConfigInterruptLines(&hfdcan, FDCAN_IT_TX_ABORT_COMPLETE, FDCAN_INTERRUPT_LINE0)
  end         FDCAN --> App:<color green>HAL_OK
  App -> FDCAN: HAL_FDCAN_ActivateNotification(&hfdcan, FDCAN_IT_TX_ABORT_COMPLETE, FDCAN_TX_BUFFER0)
  FDCAN --> App:<color green>HAL_OK

  App -> FDCAN: HAL_FDCAN_Start()
  FDCAN --> App:<color green>HAL_OK
  Note over App#lightyellow: Prepare the Tx Header

  == FDCAN HAL Message Transmit  ==
  App-> FDCAN: HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan, &TxHeader, TxData[0])
  FDCAN --> App:<color green>HAL_OK
  opt User want to cancel transmission
  App-> FDCAN: HAL_FDCAN_AbortTxRequest()
  note right
  In case the user wants to abort message transmission.
  end note
  FDCAN --> App:<color green>HAL_OK
  activate App #1000DA
  hnote over App
  <font color=blue>transmission ongoing...
  end note
  NVIC <-  : FDCAN interrupt Transmission cancellation finished
  NVIC -> FDCAN: HAL_FDCAN_IRQHandler
  FDCAN -> App: HAL_FDCAN_TxBufferAbortCallback()
  App --> FDCAN:
  deactivate App
  FDCAN --> NVIC:
  NVIC --> :
  deactivate App
  end
  App --> FDCAN: HAL_FDCAN_DeactivateNotification(&hfdcan, FDCAN_IT_TX_ABORT_COMPLETE)
  FDCAN --> App:<color green>HAL_OK
  @enduml

Code snippet

HAL1

HAL2

/* if interrupt grouping available */
HAL_FDCAN_ConfigInterruptLines(&hfdcan, FDCAN_IT_GROUP_SMSG,
                                        FDCAN_INTERRUPT_LINE0);
/* else */
HAL_FDCAN_ConfigInterruptLines(&hfdcan, FDCAN_IT_TX_ABORT_COMPLETE,
                                        FDCAN_INTERRUPT_LINE0);

HAL_FDCAN_ActivateNotification(&hfdcan,
                               FDCAN_IT_TX_ABORT_COMPLETE, FDCAN_TX_BUFFER0);


HAL_FDCAN_Start(&hfdcan);


TxHeader.Identifier          = 0x111;
TxHeader.IdType              = FDCAN_STANDARD_ID;
TxHeader.TxFrameType         = FDCAN_DATA_FRAME;
TxHeader.DataLength          = FDCAN_DLC_BYTES_8;
TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
TxHeader.BitRateSwitch       = FDCAN_BRS_ON;
TxHeader.FDFormat            = FDCAN_FD_CAN;
TxHeader.TxEventFifoControl  = FDCAN_NO_TX_EVENTS;
TxHeader.MessageMarker       = 0;

HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan, &TxHeader, &TxData[0]);
/* In case the user wants to abort message transmission */
HAL_FDCAN_AbortTxRequest(&hfdcan, FDCAN_TX_BUFFER0)
while (TransmissionAbortFlag == 0);

void HAL_FDCAN_TxBufferAbortCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t BufferIndexes)
{
  TransmissionAbortFlag = 1U;
}
/* if interrupt grouping available*/
HAL_FDCAN_SetInterruptGroupsToLine(&hfdcan, HAL_FDCAN_IT_GROUP_STATUS_MSG,
                                            HAL_FDCAN_IT_LINE_0 );
/* else */
HAL_FDCAN_SetInterruptsToLine(&hfdcan, HAL_FDCAN_IT_TX_ABORT_COMPLETE,
                                      HAL_FDCAN_IT_LINE_0 );

HAL_FDCAN_EnableTxBufferCancellationInterrupts(&hfdcan, HAL_FDCAN_IT_TX_ABORT_BUFFER_0);

HAL_FDCAN_EnableInterrupts(&hfdcan,  HAL_FDCAN_IT_TX_ABORT_COMPLETE );
HAL_FDCAN_EnableInterruptLines(&hfdcan, HAL_FDCAN_IT_LINE_0);

HAL_FDCAN_Start(&hfdcan);

/* Prepare FDCAN transmit message header */
tx_element_header.b.identifier            = 0x111,
tx_element_header.b.identifier_type       = HAL_FDCAN_ID_STANDARD,
tx_element_header.b.frame_type            = HAL_FDCAN_FRAME_DATA,
tx_element_header.b.data_length           = HAL_FDCAN_DATA_LEN_CAN_FDCAN_8_BYTE,
tx_element_header.b.error_state_indicator = HAL_FDCAN_ERROR_STATE_IND_ACTIVE,
tx_element_header.b.bit_rate_switch       = HAL_FDCAN_BIT_RATE_SWITCH_ON,
tx_element_header.b.frame_format          = HAL_FDCAN_HEADER_FRAME_FORMAT_FD_CAN,
tx_element_header.b.event_fifo_control    = HAL_FDCAN_FIFO_STORE_TX_EVENTS,
tx_element_header.b.message_marker        = 0U,

HAL_FDCAN_ReqTransmitMsgFromFIFOQ(&hfdcan, &tx_element_header, pTxData);
/* In case the user wants to abort message transmission */
HAL_FDCAN_ReqAbortOfTxBuffer(&hfdcan, HAL_FDCAN_IT_TX_ABORT_BUFFER_0)
while (TransmissionAbortFlag == 0);

static void HAL_FDCAN_TxBufferAbortCallback(hal_fdcan_handle_t *hfdcan, uint32_t buffer_indexes)
{
  TransmissionAbortFlag = 1U;
}

Key changes

  1. HAL2 splits interrupt enablement into separate APIs for interrupt lines, interrupt sources, and Tx buffer cancellation interrupts.

  2. HAL2 uses HAL_FDCAN_ReqAbortOfTxBuffer() to request a Tx cancellation.

Related examples in HAL2

No related example in HAL2.


Use case: Clock calibration (polling)

The configuration and status monitoring of the FDCAN clock calibration have been significantly improved from HAL1 to HAL2:

  • Clock calibration configuration

    • In HAL1, the FDCAN clock calibration is configured using the single function HAL_FDCAN_ConfigClockCalibration(), which accepts a configuration structure containing parameters such as time quanta per bit time, calibration field length, minimum oscillator clock periods, watchdog start value, clock calibration enable flag, and clock divider. When the clock calibration is disabled by setting ClockCalibration = FDCAN_CLOCK_CALIBRATION_DISABLE, the clock divider specified in the configuration is used to adjust the FDCAN clock prescaler.

    • In HAL2, the clock calibration configuration is modularized for greater flexibility and control. The function HAL_FDCAN_CCU_SetClockRouting() selects the clock routing path, allowing the user to enable the clock calibration unit (e.g., HAL_FDCAN_CCU_CLOCK_ROUTE_CALIBRATION) or bypass it (HAL_FDCAN_CCU_CLOCK_ROUTE_BYPASS). Calibration parameters are then set separately using HAL_FDCAN_CCU_SetConfigCalibrationUnit(). When the clock calibration unit is bypassed, the FDCAN clock calibration is disabled, and the clock divider can be explicitly configured using HAL_FDCAN_SetClockDivider() to set the clock prescaler.

  • Get clock calibration state

    • In HAL1, checking the FDCAN clock calibration status requires multiple steps: waiting for the calibration state change flag using __HAL_FDCAN_GET_FLAG(), then retrieving the calibration state with HAL_FDCAN_GetClockCalibrationState(), followed by manually clearing the calibration state change flag using __HAL_FDCAN_CLEAR_FLAG().

    • In HAL2, this process is streamlined into a single function, HAL_FDCAN_CCU_GetCalibrationLevelAndClearFlag(), which reads the current calibration level and clears the calibration state change flag internally.

In both HAL1 and HAL2, when calibration is complete or the user wants to exit restricted mode, the user must call the appropriate function: in HAL1, HAL_FDCAN_ExitRestrictedOperationMode(), and in HAL2, HAL_FDCAN_DisableRestrictedOperationMode().

Sequence diagram

  @startuml
  participant App as "User Application"
  participant "<font color=black><b>HAL FDCAN" as FDCAN #19DD3A

  participant "FDCAN Hardware System" as FDCAN_HW

  == FDCAN Initialization and Configuration ==
  App -> FDCAN: HAL_FDCAN_Init(&hfdcan)
  FDCAN --> App: <color green>HAL_OK

  App -> FDCAN: HAL_FDCAN_ConfigClockCalibration(&hfdcan, &sCcuConfig)
  FDCAN --> App: <color green>HAL_OK

  == Configure Reception Filters ==
  App -> FDCAN: HAL_FDCAN_ConfigFilter(&hfdcan, &sFilterConfig) [Calibration Filter]
  note right
     Configure at least one filter
     with IsCalibrationMsg = 1
  endnote
  FDCAN --> App: <color green>HAL_OK
  App-> FDCAN: HAL_FDCAN_Start()
  App <-- FDCAN:<color green>HAL_OK

  App -> FDCAN: HAL_FDCAN_IsRxBufferMessageAvailable(&hfdcan, FDCAN_RX_BUFFER0)
  FDCAN_HW <- : FDCAN **First** Rx buffer new message
  note right
   First calibration Message available
   endnote
  FDCAN --> App: 1 (available)

  loop  Wait for calibration state change flag
    App -> FDCAN: __HAL_FDCAN_GET_FLAG(FDCAN_FLAG_CALIB_STATE_CHANGED)
    FDCAN --> App: 1 (flag set)
  end
  App -> FDCAN: HAL_FDCAN_GetClockCalibrationState(&hfdcan)
  FDCAN_HW --> FDCAN_HW: Basic calibration done and state changed
  hnote over FDCAN_HW
  <font color=orange><b>BASIC_CALIBRATED</b>
  end note
  FDCAN --> App: <font color=orange>BASIC_CALIBRATED

  App -> FDCAN: __HAL_FDCAN_CLEAR_FLAG(FDCAN_FLAG_CALIB_STATE_CHANGED)
  FDCAN --> App: <color green>HAL_OK
  FDCAN_HW <- : FDCAN **second** Rx buffer new message
  note right
   Second calibration Message available
   endnote
  FDCAN --> App: 1 (available)
  loop Wait for calibration state change flag
    App -> FDCAN: __HAL_FDCAN_GET_FLAG(FDCAN_FLAG_CALIB_STATE_CHANGED)
    FDCAN --> App: 1 (flag set)
  end

  App -> FDCAN: HAL_FDCAN_GetClockCalibrationState(&hfdcan)
  FDCAN_HW --> FDCAN_HW: Precision calibration done and state changed
  hnote over FDCAN_HW
  <font color=green><b>PRECISION_CALIBRATED</b>
  end note
  FDCAN --> App: <font color=green> PRECISION_CALIBRATED
  == Exit Restricted Operation Mode ==
  App -> FDCAN: HAL_FDCAN_ExitRestrictedOperationMode(&hfdcan)
  hNote over FDCAN_HW: The FDCAN in <color green><b>Normal Operation Mode</b></color>
  FDCAN --> App: <color green>HAL_OK
  Note over App: The FDCAN is calibrated and ready to be used for communication.

  @enduml

Code snippet

HAL1

HAL2

hfdcan.Instance                = FDCAN1;
hfdcan.Init.FrameFormat        = FDCAN_FRAME_CLASSIC;
hfdcan.Init.Mode               = FDCAN_MODE_NORMAL;
hfdcan.Init.AutoRetransmission = DISABLE;
hfdcan.Init.TransmitPause      = DISABLE;
hfdcan.Init.ProtocolException  = ENABLE;


hfdcan.Init.NominalPrescaler     = 0x1;
hfdcan.Init.NominalTimeSeg1      = 0xF;
hfdcan.Init.NominalTimeSeg2      = 0x4;
hfdcan.Init.NominalSyncJumpWidth = 0x4;

hfdcan.Init.MessageRAMOffset    = 0;
hfdcan.Init.StdFiltersNbr       = 2;
hfdcan.Init.ExtFiltersNbr       = 0;
hfdcan.Init.RxFifo0ElmtsNbr     = 0;
hfdcan.Init.RxFifo1ElmtsNbr     = 0;
hfdcan.Init.RxBuffersNbr        = 2;
hfdcan.Init.RxBufferSize        = FDCAN_DATA_BYTES_8;
hfdcan.Init.TxEventsNbr         = 0;
hfdcan.Init.TxBuffersNbr        = 1;
hfdcan.Init.TxFifoQueueElmtsNbr = 0;
hfdcan.Init.TxElmtSize          = FDCAN_DATA_BYTES_8;
HAL_FDCAN_Init(&hfdcan);

sCcuConfig.ClockCalibration     = ENABLE;


sCcuConfig.MinOscClkPeriods     = 4;
sCcuConfig.CalFieldLength       = FDCAN_CALIB_FIELD_LENGTH_64;
sCcuConfig.TimeQuantaPerBitTime = 20;

HAL_FDCAN_ConfigClockCalibration(&hfdcan, &sCcuConfig);

sFilterConfig.IdType           = FDCAN_STANDARD_ID;
sFilterConfig.FilterIndex      = 0;
sFilterConfig.FilterType       = FDCAN_FILTER_DUAL;
sFilterConfig.FilterConfig     = FDCAN_FILTER_TO_RXBUFFER;
sFilterConfig.FilterID1        = 0x555;
sFilterConfig.RxBufferIndex    = 0;
sFilterConfig.IsCalibrationMsg = 1;

HAL_FDCAN_ConfigFilter(&hfdcan, &sFilterConfig);

HAL_FDCAN_Start(&hfdcan);

while(HAL_FDCAN_IsRxBufferMessageAvailable(&hfdcan, FDCAN_RX_BUFFER0) == 0);

while(__HAL_FDCAN_GET_FLAG(&hfdcan, FDCAN_FLAG_CALIB_STATE_CHANGED) == 0);
if(HAL_FDCAN_GetClockCalibrationState(&hfdcan) != FDCAN_CLOCK_BASIC_CALIBRATED)
{
  Error_Handler();
}
__HAL_FDCAN_CLEAR_FLAG(&hfdcan, FDCAN_FLAG_CALIB_STATE_CHANGED);

while(__HAL_FDCAN_GET_FLAG(&hfdcan, FDCAN_FLAG_CALIB_STATE_CHANGED) == 0);
if(HAL_FDCAN_GetClockCalibrationState(&hfdcan) != FDCAN_CLOCK_PRECISION_CALIBRATED)
{
  Error_Handler();
}

HAL_FDCAN_ExitRestrictedOperationMode(&hfdcan);
HAL_FDCAN_Init(&hfdcan, HAL_FDCAN1);
fdcan_config.frame_format        = HAL_FDCAN_FRAME_FORMAT_CLASSIC_CAN;
fdcan_config.mode                = HAL_FDCAN_MODE_NORMAL;
fdcan_config.auto_retransmission = HAL_FDCAN_AUTO_RETRANSMISSION_ENABLE;
fdcan_config.transmit_pause      = HAL_FDCAN_TRANSMIT_PAUSE_DISABLE;
fdcan_config.protocol_exception  = HAL_FDCAN_PROTOCOL_EXCEPTION_ENABLE;

hal_fdcan_nominal_bit_timing_t fdcan_nominal_bit_timing;
fdcan_nominal_bit_timing.nominal_prescaler  = 0x1;
fdcan_nominal_bit_timing.nominal_time_seg1  = 0xF;
fdcan_nominal_bit_timing.nominal_time_seg2  = 0x4;
fdcan_nominal_bit_timing.nominal_jump_width = 0x4;
fdcan_config.nominal_bit_timing             = fdcan_nominal_bit_timing;
fdcan_config.message_ram_offset             = 0;
fdcan_config.std_filters_nbr                = 2;
fdcan_config.ext_filters_nbr                = 0;
fdcan_config.rx_fifo0_elt_nbr               = 0;
fdcan_config.rx_fifo1_elt_nbr               = 0;
fdcan_config.rx_buffer_nbr                  = 2;
fdcan_config.rx_buffer_size                 = HAL_FDCAN_DATA_BYTES_8;
fdcan_config.tx_event_nbr                   = 0;
fdcan_config.tx_buffer_nbr                  = 1;
fdcan_config.tx_fifo_queue_elt_nbr          = 0;
fdcan_config.tx_fifo_queue_elt_size         = HAL_FDCAN_DATA_BYTES_8;
HAL_FDCAN_SetConfig(&hfdcan, &fdcan_config);

hal_fdcan_ccu_clock_route_t clock_route = HAL_FDCAN_CCU_CLOCK_ROUTE_CALIBRATION;
HAL_FDCAN_CCU_SetClockRouting(&hfdcan, clock_route);

ccu_config.min_osc_clk_periods          = 4;
ccu_config.calibration_field_length     = HAL_FDCAN_CCU_FIELD_LENGTH_64;
ccu_config.time_quanta_per_bit_time     = 20;

HAL_FDCAN_CCU_SetConfigCalibrationUnit(&hfdcan, &ccu_config);

filter_calibration.id_type         = HAL_FDCAN_ID_STANDARD,
filter_calibration.filter_index    = 0U,
filter_calibration.filter_type     = HAL_FDCAN_FILTER_TYPE_DUAL,
filter_calibration.filter_config   = HAL_FDCAN_FILTER_TO_RX_BUFFER,
filter_calibration.filter_id1      = 0x555,
filter_calibration.rx_buffer_index = HAL_FDCAN_RX_BUFFER0,
filter_calibration.message_type    = HAL_FDCAN_MESSAGE_CALIBRATION,

 HAL_FDCAN_SetFilter(&hfdcan, &filter_calibration);

HAL_FDCAN_Start(&hfdcan);

while(HAL_FDCAN_GetRxBufferMessageStatusAndClearFlag(&hfdcan, HAL_FDCAN_RX_BUFFER0) == 0);

if(HAL_FDCAN_CCU_GetCalibrationLevelAndClearFlag(&hfdcan, 10) != HAL_FDCAN_CCU_CALIBRATION_LEVEL_BASIC_CALIBRATED)
{
  Error_Handler();
}



if(HAL_FDCAN_CCU_GetCalibrationLevelAndClearFlag(&hfdcan, 10) != HAL_FDCAN_CCU_CALIBRATION_LEVEL_PRECISION_CALIBRATED)
{
  Error_Handler();
}


HAL_FDCAN_DisableRestrictedOperationMode(&hfdcan);

Key changes

  1. HAL2 modularizes clock calibration configuration with dedicated CCU routing and configuration APIs.

  2. HAL2 streamlines calibration status monitoring with a single API that also clears the status flag.

Related examples in HAL2

No related example in HAL2.


Use case: Clock calibration (interrupt)

The main differences between HAL1 and HAL2 in handling FDCAN clock calibration interrupts are as follows:

  • Activate notifications

    • In HAL1, calibration interrupts are enabled using HAL_FDCAN_ActivateNotification() with interrupts FDCAN_IT_CALIB_STATE_CHANGED and FDCAN_IT_CALIB_WATCHDOG_EVENT.

    • In HAL2, these interrupts are enabled using HAL_FDCAN_CCU_EnableInterrupts(). This is enough since the clock calibration has its own dedicated interrupts line.

  • Interrupt handler

    • In HAL1, all interrupts are handled by HAL_FDCAN_IRQHandler().

    • In HAL2, line 0 and line 1 interrupts are handled by HAL_FDCAN_IRQHandler(), while clock calibration interrupts are handled by HAL_FDCAN_CCU_IRQHandler().

  • Calibration callback

    • In HAL1, the HAL_FDCAN_ClockCalibrationCallback() is triggered with specific flags (FDCAN_IT_CALIB_STATE_CHANGED or FDCAN_IT_CALIB_WATCHDOG_EVENT) to indicate calibration state changes or watchdog events.

    • In HAL2, two callbacks are provided:

      HAL_FDCAN_CCU_CalibrationCallback() to indicate calibration state changes.

      HAL_FDCAN_CCU_CalibrationWatchdogCallback() to indicate that the watchdog expired before calibration completed.

Sequence diagram

  @startuml
  participant App as "User Application"
  participant "<font color=black><b>HAL FDCAN" as FDCAN #19DD3A
  participant "NVIC" as NVIC#AliceBlue

  participant "FDCAN Hardware System" as FDCAN_HW

  == FDCAN  Clock calibration Configuration ==
  App -> FDCAN: HAL_FDCAN_ConfigClockCalibration(&hfdcan, &sCcuConfig)
  FDCAN -> FDCAN_HW: Setup Clock Calibration Unit (CCU)
  FDCAN_HW --> FDCAN: CCU configured
  FDCAN --> App: <color green>HAL_OK

  == Configure Reception Filters and enable interrupt ==
  App -> FDCAN: HAL_FDCAN_ConfigFilter(&hfdcan, &sFilterConfig) [Calibration Filter]
  note right
     Configure at least one filter for both calibration frames to enter the
     BASIC_CALIBRATED and the PRECISION_CALIBRATED
     with IsCalibrationMsg = 1
  endnote
  FDCAN --> App: <color green>HAL_OK

  App -> FDCAN: HAL_FDCAN_ConfigInterruptLines(&hfdcan, FDCAN_IT_RX_BUFFER_NEW_MESSAGE \n , FDCAN_INTERRUPT_LINE0)
  FDCAN --> App: <color green>HAL_OK
  App -> FDCAN: HAL_FDCAN_ActivateNotification(&hfdcan, FDCAN_IT_RX_BUFFER_NEW_MESSAGE \n | FDCAN_IT_CALIB_STATE_CHANGED \n | FDCAN_IT_CALIB_WATCHDOG_EVENT, FDCAN_TX_BUFFER0)
  FDCAN --> App: <color green>HAL_OK
  == FDCAN  Clock calibration Process  ==
  App -> FDCAN: HAL_FDCAN_Start(&hfdcan)
  FDCAN --> App: <color green>HAL_OK
  hNote over FDCAN_HW: The FDCAN in <color red><b>Restricted Operation Mode</b></color>

  hnote over FDCAN_HW
  <font color=red><b>BASIC_CALIBRATION or NOT_CALIBRATED</b>
  end note

  FDCAN_HW <- : FDCAN Rx buffer new calibration message
  note left
  The configured filter <index> accept the calibration message
  end note
  FDCAN_HW --> NVIC: Rx Buffer New Message interrupt \n **FDCAN_IT0_IRQ**
  NVIC -> FDCAN: HAL_FDCAN_IRQHandler()
  FDCAN -> App: HAL_FDCAN_RxBufferNewMessageCallback()
  FDCAN <-- App:
  NVIC <-- FDCAN:
  FDCAN_HW <-- NVIC:
  alt watchdog counter reaches zero before state changed
  FDCAN_HW --> FDCAN_HW: Watchdog counter reaches zero \n before CCU state changed
  FDCAN_HW-->NVIC: CCU watchdog event \n **FDCAN_CCU_IRQ**
  NVIC ->FDCAN: HAL_FDCAN_IRQHandler()

  hnote over FDCAN_HW
  <font color=red><b>NOT_CALIBRATED</b>
  end note

  FDCAN -> App: HAL_FDCAN_ClockCalibrationCallback(&hfdcan, FDCAN_IT_CALIB_WATCHDOG_EVENT)
  hNote over App:<font color=red><b>failed


  else state changed before watchdog counter reaches zero
  FDCAN_HW --> FDCAN_HW: CCU state changed before\nWatchdog counter reaches zero
  FDCAN_HW-->NVIC: CCU state change event \n **FDCAN_CCU_IRQ**
  NVIC ->FDCAN: HAL_FDCAN_IRQHandler()
  FDCAN -> App: HAL_FDCAN_ClockCalibrationCallback(&hfdcan, FDCAN_IT_CALIB_STATE_CHANGED)
  App --> FDCAN:
  FDCAN --> NVIC:


  hnote over FDCAN_HW
  <font color=orange><b>BASIC_CALIBRATION</b>
  end note
  end
  'Precision Calibration Process'

  FDCAN_HW <- : FDCAN **second** Rx buffer new message\n(From quartz node) starts with at least "1010"
  note left
  The configured filter <index> accept the calibration message
  end note

  FDCAN_HW --> NVIC: Rx Buffer New Message interrupt \n **FDCAN_IT0_IRQ**
  NVIC -> FDCAN: HAL_FDCAN_IRQHandler()
  FDCAN -> App: HAL_FDCAN_RxBufferNewMessageCallback()
  FDCAN <-- App:
  NVIC <-- FDCAN:
  FDCAN_HW <-- NVIC:
  alt watchdog counter reaches zero before state changed
  FDCAN_HW --> FDCAN_HW: Watchdog counter reaches zero \n before CCU state changed
  FDCAN_HW-->NVIC: CCU watchdog event \n **FDCAN_CCU_IRQ**
  NVIC ->FDCAN: HAL_FDCAN_IRQHandler()

  hnote over FDCAN_HW
  <font color=red><b>NOT_CALIBRATED</b>
  end note

  FDCAN -> App: HAL_FDCAN_ClockCalibrationCallback(&hfdcan, FDCAN_IT_CALIB_WATCHDOG_EVENT)
  hNote over App:<font color=red><b>failed


  else state changed before watchdog counter reaches zero
  FDCAN_HW --> FDCAN_HW: CCU state changed before\nWatchdog counter reaches zero
  FDCAN_HW-->NVIC: CCU state change event \n **FDCAN_CCU_IRQ**
  NVIC ->FDCAN: HAL_FDCAN_IRQHandler()

  hnote over FDCAN_HW
  <font color=green><b>PRECISION_CALIBRATED</b>
  end note

  FDCAN -> App: HAL_FDCAN_ClockCalibrationCallback(&hfdcan, FDCAN_IT_CALIB_STATE_CHANGED)
  hNote over App:<font color=green><b>Success
           App -> FDCAN: HAL_FDCAN_ExitRestrictedOperationMode
  hNote over FDCAN_HW: The FDCAN in <color green><b>Normal Operation Mode</b></color>
  App <-- FDCAN:<color green>HAL_OK
  end

  Note over App: The FDCAN is calibrated and ready to be used for communication.

  @enduml

Code snippet

HAL1

HAL2

HAL_FDCAN_Start(&hfdcan);

CcuConfig.ClockCalibration     = ENABLE;


CcuConfig.MinOscClkPeriods     = 4;
CcuConfig.CalFieldLength       = FDCAN_CALIB_FIELD_LENGTH_64;
CcuConfig.TimeQuantaPerBitTime = 20;
CcuConfig.WatchdogStartValue   = 40;

HAL_FDCAN_ConfigClockCalibration(&hfdcan, &CcuConfig);

HAL_FDCAN_ConfigInterruptLines(&hfdcan,
                               FDCAN_IT_RX_BUFFER_NEW_MESSAGE
                               FDCAN_INTERRUPT_LINE0);

HAL_FDCAN_ActivateNotification(&hfdcan,
                               FDCAN_IT_RX_BUFFER_NEW_MESSAGE
                               | FDCAN_IT_CALIB_STATE_CHANGED
                               | FDCAN_IT_CALIB_WATCHDOG_EVENT,
                                 FDCAN_TX_BUFFER0);
HAL_FDCAN_Start(&hfdcan);

hal_fdcan_ccu_clock_route_t clock_route = HAL_FDCAN_CCU_CLOCK_ROUTE_CALIBRATION;
HAL_FDCAN_CCU_SetClockRouting(&hfdcan, clock_route);

ccu_config.min_osc_clk_periods          = 4;
ccu_config.calibration_field_length     = HAL_FDCAN_CCU_FIELD_LENGTH_64;
ccu_config.time_quanta_per_bit_time     = 20;
ccu_config.watchdog_start_value         = 40;

HAL_FDCAN_CCU_SetConfigCalibrationUnit(&hfdcan, &ccu_config);

HAL_FDCAN_SetInterruptsToLine(&hfdcan,
               HAL_FDCAN_IT_RX_BUFFER_NEW_MESSAGE
               , HAL_FDCAN_IT_LINE_0);

HAL_FDCAN_EnableInterruptLines(&hfdcan, HAL_FDCAN_IT_LINE_0);

HAL_FDCAN_EnableInterrupts(&hfdcan,
                           HAL_FDCAN_IT_RX_BUFFER_NEW_MESSAGE
                           );
HAL_FDCAN_CCU_EnableInterrupts(&hfdcan,
                               HAL_FDCAN_CCU_IT_CALIBRATION_STATE_CHANGE
                             | HAL_FDCAN_CCU_IT_CALIBRATION_WATCHDOG_EVENT
                               );

Key changes

  1. HAL2 manages clock calibration interrupts through dedicated CCU APIs.

  2. HAL2 introduces a dedicated IRQ handler for CCU interrupts.

Related examples in HAL2

Example Name

Description

example_hal_fdcan_clock_calibration_controller/example_hal_fdcan_clock_calibration_responder

This example shows how to achieve clock calibration on an FDCAN unit.