Skip to main content
Graduate II
January 25, 2024
Solved

STM32G431: No FDCAN interrupts

  • January 25, 2024
  • 1 reply
  • 1950 views

I have two STM32 micros connected via CAN transceivers.  The sending unit is transmitting without issue, as evidenced by measuring both the CANH/CANL bus wires, as well as the CAN_RX/CAN_TX logical wires on the transmitter.  I did have to slow the bus down significantly due to high capacitance (troubleshooting at 20 kbps; hope to increase speed once I get the situation figured out).  I am using classic 11-bit identifiers, subdivided into a 7-bit message type and a 4-bit node ID.

I can see the ACK bit on the CAN wire, so I know the receiver in the chip is correctly identifying the frame.  However, the receiver's two FDCAN interrupts never fire.  I have both set the non-matching 11-bit identifier action to FDCAN_ACCEPT_IN_RX_FIFO0 as well as set a id/mask filter with a mask of 0, as well as a filter set up the way I would intend for my application.  I will now let the code speak for itself:

 

First, the MX-generated initializer is called:

 

static void MX_FDCAN1_Init(void)
{

 /* USER CODE BEGIN FDCAN1_Init 0 */

 /* USER CODE END FDCAN1_Init 0 */

 /* USER CODE BEGIN FDCAN1_Init 1 */

 /* USER CODE END FDCAN1_Init 1 */
 hfdcan1.Instance = FDCAN1;
 hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV10;
 hfdcan1.Init.FrameFormat = FDCAN_FRAME_CLASSIC;
 hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
 hfdcan1.Init.AutoRetransmission = DISABLE;
 hfdcan1.Init.TransmitPause = DISABLE;
 hfdcan1.Init.ProtocolException = DISABLE;
 hfdcan1.Init.NominalPrescaler = 16;
 hfdcan1.Init.NominalSyncJumpWidth = 1;
 hfdcan1.Init.NominalTimeSeg1 = 2;
 hfdcan1.Init.NominalTimeSeg2 = 2;
 hfdcan1.Init.DataPrescaler = 1;
 hfdcan1.Init.DataSyncJumpWidth = 1;
 hfdcan1.Init.DataTimeSeg1 = 1;
 hfdcan1.Init.DataTimeSeg2 = 1;
 hfdcan1.Init.StdFiltersNbr = 2;
 hfdcan1.Init.ExtFiltersNbr = 0;
 hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
 if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN FDCAN1_Init 2 */

 /* USER CODE END FDCAN1_Init 2 */

}

 

 

Following that, my application CAN setup happens.  First generic settings:

 

HAL_StatusTypeDef CAN_Init(void) {
	// Clear any existing filters and whatnot, set up callbacks, etc.
	// NB: Callbacks are implemented via weak functions, not registration.
	// See notes in stm32g4xx_hal_fdcan.c regarding macro definitions

	HAL_StatusTypeDef rc = HAL_OK;

	// Make sure filter slot 0 is configured so we receive broadcast
	// messages.
	{
		FDCAN_FilterTypeDef broadcastFilter = {
				.IdType = FDCAN_STANDARD_ID,
				.FilterIndex = 0,
				.FilterType = FDCAN_FILTER_MASK,
				.FilterConfig = FDCAN_FILTER_TO_RXFIFO0_HP, /* Broadcast messages are treated as high-priority */
				.FilterID1 = 0, /* ID bits */
				.FilterID2 = 0x00f /* ID mask */
		};
		// 0/0x00f => xxxxxxx0000 filter
		rc = HAL_FDCAN_ConfigFilter(&hfdcan1, &broadcastFilter);
		if(rc != HAL_OK) return rc;
	}

	// Set global filter options
	rc = HAL_FDCAN_ConfigGlobalFilter(&hfdcan1,
			FDCAN_ACCEPT_IN_RX_FIFO0 /* Non-matching 11-bit */,
			FDCAN_REJECT /* Non-matching 28-bit */,
			FDCAN_REJECT /* Classic RTR frames (we won't be using them) */,
			FDCAN_REJECT /* Extended RTR frames (we're in classic mode anyway) */);
	if(rc != HAL_OK) return rc;

	// We don't care about timestamps
	rc = HAL_FDCAN_DisableTimestampCounter(&hfdcan1);
	if(rc != HAL_OK) return rc;

	// At least for now, we aren't going to use the timeout counter either
	rc = HAL_FDCAN_DisableTimeoutCounter(&hfdcan1);
	if(rc != HAL_OK) return rc;

	// Other configuration options will be left to POR defaults.
	// Start the peripheral, allowing it to sync to the bus.
	return HAL_FDCAN_Start(&hfdcan1);

}

 

 

...followed by setting a filter for the local intended CAN ID:

 

HAL_StatusTypeDef CAN_SetLocalNodeID(uint8_t node_id) {
	// NB: We only use the low four bits of node_id.
	// Filters can only be configured while the peripheral is stopped.
	HAL_StatusTypeDef rc = HAL_FDCAN_Stop(&hfdcan1);
	if(rc != HAL_OK) return rc;
	// \todo Create the proper filter for xxxxxxx<node_id> (that is, 7 don't cares plus the four node-id bits)
	{
		FDCAN_FilterTypeDef broadcastFilter = {
				.IdType = FDCAN_STANDARD_ID,
				.FilterIndex = 1,
				.FilterType = FDCAN_FILTER_MASK,
				.FilterConfig = FDCAN_FILTER_TO_RXFIFO0,
				.FilterID1 = (node_id & 0x0f), /* ID bits */
				.FilterID2 = 0x00f /* ID mask */
		};
		// 0/0x00f => xxxxxxx0000 filter
		rc = HAL_FDCAN_ConfigFilter(&hfdcan1, &broadcastFilter);
		if (rc != HAL_OK) return rc;

		FDCAN_FilterTypeDef promiscuousFilter = {
				.IdType = FDCAN_STANDARD_ID,
				.FilterIndex = 2,
				.FilterType = FDCAN_FILTER_MASK,
				.FilterConfig = FDCAN_FILTER_TO_RXFIFO1, /* Packets that aren't meant for us go into FIFO1 */
				.FilterID1 = 0, /* ID bits */
				.FilterID2 = 0 /* ID mask - no bits matter*/
		};
		// 0/0x00f => xxxxxxx0000 filter
		rc = HAL_FDCAN_ConfigFilter(&hfdcan1, &promiscuousFilter);
		if (rc != HAL_OK) return rc;

	}
	local_node_id = node_id;
	return HAL_FDCAN_Start(&hfdcan1);
}

 

No errors are returned in any of these functions.

Once all this is done, the main loop sits around waiting for CAN messages to be received.  I have enabled both interrupts in the NVIC section of the MX configuration UI (and can see the relevant NVIC calls in the generated HAL_FDCAN_MspInit function).  I have breakpoints set in both interrupt handlers, FDCAN1_IT0_IRQHandler and FDCAN1_IT1_IRQHandler.  However, neither of the breakpoints are hit. 

I've also adjusted the code to check the FIFO fill level, and that does indicate that the packet went into FIFO0. So I don't understand why the interrupt didn't fire.  I don't want to waste processor time polling the FIFO fill level.

I'm not sure where to look next.  Any guidance will be greatly appreciated.

    This topic has been closed for replies.
    Best answer by Brian H

    Solved it myself.  It was a very simple oversight; I was missing the following call to activate the HAL FDCAN notification:

    rc = HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);

    Once I added that line to my startup code, the interrupt began firing as expected.

    1 reply

    Brian HAuthorAnswer
    Graduate II
    January 25, 2024

    Solved it myself.  It was a very simple oversight; I was missing the following call to activate the HAL FDCAN notification:

    rc = HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);

    Once I added that line to my startup code, the interrupt began firing as expected.