2026-05-12 7:47 AM - last edited on 2026-05-12 9:16 AM by mƎALLEm
Hi,
We are busy making a setup with 2 nucleo-c092rc boards. Our first test was to send from board 1 to board 2 by the integrated CAN interface on these boards. We started at 1Mbits and this worked great. Also on 4Mbits the setup worked great. But we also want to test this on 8Mbits and now the transmitter is not sending correctly. We only see that the arbitration phase is send out and a couple of bits of the data part, but then the transmitters stops sending.
We have tried with internal HSI(48Mhz) and external HSE crystal which is also 48Mhz on the nucleo boards.
We calculated the values for the SyncJumpWidth and timeseg1 and 2 and also tested with values slightly altered.
hfdcan1.Init.DataPrescaler = 1;
hfdcan1.Init.DataSyncJumpWidth = 2;
hfdcan1.Init.DataTimeSeg1 = 2;
hfdcan1.Init.DataTimeSeg2 = 3;
But we allways have the same outcome. Sometimes it's not sending anything and sometimes only a small part of the
total message of 64 bytes.
What could be the issue here?
Main.c:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : CAN-FD TX 64 bytes every second
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fdcan.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
COM_InitTypeDef BspCOMInit;
__IO uint32_t BspButtonState = BUTTON_RELEASED;
/* USER CODE BEGIN PV */
FDCAN_TxHeaderTypeDef TxHeader;
uint8_t TxData[64];
/* Debug variabelen voor CubeIDE Live Expressions / Watch */
volatile uint32_t tx_count = 0;
volatile uint32_t tx_ok_count = 0;
volatile uint32_t tx_fail_count = 0;
volatile uint32_t tx_fifo_free = 0;
volatile HAL_StatusTypeDef last_tx_status = HAL_OK;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_FDCAN1_Init();
/* USER CODE BEGIN 2 */
/*
* CAN transceiver uit standby halen.
* NUCLEO-C092RC:
* PD2 LOW = CAN transceiver normal mode
*/
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_Delay(10);
/*
* CAN-FD frame configuratie.
*
* De echte snelheid wordt bepaald in fdcan.c:
* - nominal/arbitration bijvoorbeeld 500 kbit/s
* - data phase 2 Mbit/s
*
* Hier zetten we alleen:
* - FD frame
* - BRS aan
* - 64 bytes payload
*/
TxHeader.Identifier = 0x123;
TxHeader.IdType = FDCAN_STANDARD_ID;
TxHeader.TxFrameType = FDCAN_DATA_FRAME;
TxHeader.DataLength = FDCAN_DLC_BYTES_64;
TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
TxHeader.BitRateSwitch = FDCAN_BRS_ON;
TxHeader.FDFormat = FDCAN_FD_CAN;
TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
TxHeader.MessageMarker = 0;
/*
* Payload:
* 00 01 02 03 ... 3F
*/
for (uint8_t i = 0; i < 64; i++)
{
TxData[i] = i;
}
/*
* FDCAN starten
*/
if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE END 2 */
/* Initialize leds */
BSP_LED_Init(LED_GREEN);
BSP_LED_Init(LED_BLUE);
/* Initialize USER push-button, will be used to trigger an interrupt each time it's pressed.*/
BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI);
/* Initialize COM1 port (115200, 8 bits (7-bit data + 1 stop bit), no parity */
BspCOMInit.BaudRate = 115200;
BspCOMInit.WordLength = COM_WORDLENGTH_8B;
BspCOMInit.StopBits = COM_STOPBITS_1;
BspCOMInit.Parity = COM_PARITY_NONE;
BspCOMInit.HwFlowCtl = COM_HWCONTROL_NONE;
if (BSP_COM_Init(COM1, &BspCOMInit) != BSP_ERROR_NONE)
{
Error_Handler();
}
/* USER CODE BEGIN BSP */
printf("CAN-FD TX started: 64 bytes, BRS ON, data phase should be 2 Mbit/s\r\n");
BSP_LED_On(LED_GREEN);
BSP_LED_Off(LED_BLUE);
/* USER CODE END BSP */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/*
* Payload elke seconde opnieuw vullen.
* Nu vast patroon:
* 00 01 02 03 ... 3F
*/
for (uint8_t i = 0; i < 64; i++)
{
TxData[i] = i;
}
tx_fifo_free = HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1);
if (tx_fifo_free > 0)
{
last_tx_status = HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, TxData);
tx_count++;
if (last_tx_status == HAL_OK)
{
tx_ok_count++;
BSP_LED_Toggle(LED_BLUE);
}
else
{
tx_fail_count++;
BSP_LED_Toggle(LED_GREEN);
}
}
else
{
tx_fail_count++;
BSP_LED_Toggle(LED_GREEN);
}
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_FLASH_SET_LATENCY(FLASH_LATENCY_1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
/** Enables the Clock Security System
*/
HAL_RCC_EnableCSS();
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief BSP Push Button callback
* @PAram Button Specifies the pressed button
* @retval None
*/
void BSP_PB_Callback(Button_TypeDef Button)
{
if (Button == BUTTON_USER)
{
BspButtonState = BUTTON_PRESSED;
}
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @PAram file: pointer to the source file name
* @PAram line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
FDCAN.c:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file fdcan.c
* @brief This file provides code for the configuration
* of the FDCAN instances.
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "fdcan.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
FDCAN_HandleTypeDef hfdcan1;
/* FDCAN1 init function */
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;
/*
* CAN-FD met Bit Rate Switching.
*
* Uitgangspunt:
* FDCAN clock = 48 MHz
*
* Nominal/arbitration phase:
* 500 kbit/s
*
* Data phase:
* 8 Mbit/s
*/
hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1;
hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS;
hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
/*
* Voor normale communicatie liever ENABLE.
* Dan probeert de controller opnieuw als er bijvoorbeeld geen ACK komt.
*/
hfdcan1.Init.AutoRetransmission = DISABLE;
hfdcan1.Init.TransmitPause = DISABLE;
hfdcan1.Init.ProtocolException = DISABLE;
/*
* Nominal bitrate = 500 kbit/s
*
* FDCAN clock = 48 MHz
* tq totaal = 1 + NominalTimeSeg1 + NominalTimeSeg2
* tq totaal = 1 + 79 + 16 = 96
*
* 48 MHz / 96 = 500 kbit/s
*
* Sample point:
* (1 + 79) / 96 = 83.3%
*/
hfdcan1.Init.NominalPrescaler = 1;
hfdcan1.Init.NominalSyncJumpWidth = 16;
hfdcan1.Init.NominalTimeSeg1 = 79;
hfdcan1.Init.NominalTimeSeg2 = 16;
/*
* Data bitrate = 8 Mbit/s
*
* FDCAN clock = 48 MHz
* tq totaal = 1 + DataTimeSeg1 + DataTimeSeg2
* tq totaal = 1 + 3 + 2 = 6
*
* 48 MHz / 6 = 8 Mbit/s
*
* Sample point:
* (1 + 3) / 6 = 66.7%
*/
hfdcan1.Init.DataPrescaler = 1;
hfdcan1.Init.DataSyncJumpWidth = 2;
hfdcan1.Init.DataTimeSeg1 = 2;
hfdcan1.Init.DataTimeSeg2 = 3;
/*
* Filters:
* Zet StdFiltersNbr op 1 als je in main.c HAL_FDCAN_ConfigFilter gebruikt.
* Voor TX maakt dit niet uit, voor RX wel.
*/
hfdcan1.Init.StdFiltersNbr = 1;
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 */
}
void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef* fdcanHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
if (fdcanHandle->Instance == FDCAN1)
{
/* USER CODE BEGIN FDCAN1_MspInit 0 */
/* USER CODE END FDCAN1_MspInit 0 */
/** Initializes the peripherals clocks */
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FDCAN1;
PeriphClkInit.Fdcan1ClockSelection = RCC_FDCAN1CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
/* FDCAN1 clock enable */
__HAL_RCC_FDCAN1_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/**
FDCAN1 GPIO Configuration
PD0 ------> FDCAN1_RX
PD1 ------> FDCAN1_TX
PD2 wordt gebruikt voor CAN transceiver standby/enable.
Die staat meestal in gpio.c als gewone output.
In main.c zet je die laag:
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
PD2 LOW = transceiver normal mode
*/
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_FDCAN1;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* USER CODE BEGIN FDCAN1_MspInit 1 */
/* USER CODE END FDCAN1_MspInit 1 */
}
}
void HAL_FDCAN_MspDeInit(FDCAN_HandleTypeDef* fdcanHandle)
{
if (fdcanHandle->Instance == FDCAN1)
{
/* USER CODE BEGIN FDCAN1_MspDeInit 0 */
/* USER CODE END FDCAN1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_FDCAN1_CLK_DISABLE();
/**
FDCAN1 GPIO Configuration
PD0 ------> FDCAN1_RX
PD1 ------> FDCAN1_TX
*/
HAL_GPIO_DeInit(GPIOD, GPIO_PIN_0 | GPIO_PIN_1);
/* USER CODE BEGIN FDCAN1_MspDeInit 1 */
/* USER CODE END FDCAN1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
2026-05-12 8:21 AM - edited 2026-05-12 8:46 AM
Hello,
@leddel32 wrote:
Hi,
We have tried with internal HSI(48Mhz) and external HSE crystal which is also 48Mhz on the nucleo boards.
-> you need definitely to use HSE instead of HSI for CAN communication.
I invite you to refer to this knowledge base article: STM32 FDCAN running at 8 Mb/s on NUCLEO boards
Refer to the Github hotspot: CKB-STM32-FDCAN-8Mbs
The NUCLEO-C092 board is not included as example but the same principle applies to it.
Meanwhile and at first glance, you didn't activate the transceiver delay compensation:
/**
* Configure and enable Tx Delay Compensation, required for BRS mode.
* TdcOffset default recommended value: DataTimeSeg1 * DataPrescaler
* TdcFilter default recommended value: 0
*/
if (HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan1, (hfdcan1.Init.DataPrescaler * hfdcan1.Init.DataTimeSeg1), 0U) != HAL_OK)
{
Error_Handler();
}
if (HAL_FDCAN_EnableTxDelayCompensation(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
Please refer to the AN5348 "Introduction to FDCAN peripherals for STM32 MCUs" / section 4.3.5 Transceiver delay compensation.
2026-05-12 8:27 AM - edited 2026-05-12 8:46 AM
For your intention, your ioc file doesn't reflect the code source you are sharing.
So anyone wants to regenerate the project or modify something will overwrite your configuration.
May be you've attached the wrong project (the old one).