2024-01-25 02:17 PM - edited 2024-01-25 02:21 PM
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.
Solved! Go to Solution.
2024-01-25 02:37 PM
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.
2024-01-25 02:37 PM
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.