2026-05-12 7:47 AM - last edited on 2026-05-12 9:03 AM by mƎALLEm
Board: NUCLEO-G0B1RE (STM32G0B1RET6)
CAN Transceiver: CAN-FD 4 Click (NCV7344) connected via level shifter (3.3V → 5V)
FDCAN Pins: PC4 (TX), PC5 (RX) on FDCAN1
Clock: HSI 16 MHz → PLL → 64 MHz FDCAN clock
Second Node: Raspberry Pi 4 with MCP2517FD Click board (ATA6563 transceiver, 20 MHz crystal)
RPi CAN Interface: can0 configured with bitrate 500000 dbitrate 2000000 fd on
I'm building a BMS (Battery Management System) CAN-FD emulator. The STM32 reads values from a potentiometer via ADC and transmits telemetry frames (ID 0x100, 8 bytes) every 100ms. The Raspberry Pi is the receiver that should log these frames.
When both nodes are connected in FDCAN_MODE_NORMAL, the STM32 transmits frames (confirmed via serial debug prints and g_tx_count incrementing in the debugger), but nothing appears on candump can0 on the Raspberry Pi. The STM32 eventually enters BUS-OFF state.
| STM32 INTERNAL loopback mode | :white_heavy_check_mark:TX = RX, frames received correctly |
| STM32 EXTERNAL loopback mode | :white_heavy_check_mark:TX = RX, transceiver path verified |
| RPi MCP2517FD internal loopback | :white_heavy_check_mark:Frames sent with cansend are received |
| Bus termination resistance (both ends powered off) | :white_heavy_check_mark:60Ω (two 120Ω in parallel) |
| CAN_H / CAN_L idle voltages | :white_heavy_check_mark:~2.5V each |
| NCV7344 STB pin | :white_heavy_check_mark:0V (Normal Mode) |
| ATA6563 STBY pin | :white_heavy_check_mark:0V (Normal Mode) |
| Level shifter output at NCV7344 TXD | :white_heavy_check_mark:4.96V (above VIH min of 3.535V) |
| Level shifter output at NCV7344 RXD | :white_heavy_check_mark:4.96V |
| Continuity CAN_H to CAN_H, CAN_L to CAN_L | :white_heavy_check_mark:Verified |
| Common GND between both systems | :white_heavy_check_mark:Verified |
| RPi ip link show can0 shows | :white_heavy_check_mark: state ERROR-ACTIVE, mtu 72, FD,TDC-AUTO |
hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS; hfdcan1.Init.Mode = FDCAN_MODE_NORMAL; hfdcan1.Init.AutoRetransmission = DISABLE; hfdcan1.Init.NominalPrescaler = 1; hfdcan1.Init.NominalTimeSeg1 = 101; hfdcan1.Init.NominalTimeSeg2 = 26; hfdcan1.Init.DataPrescaler = 1; hfdcan1.Init.DataTimeSeg1 = 24; hfdcan1.Init.DataTimeSeg2 = 7; // TDC Auto mode enabled: hfdcan1.Instance->DBTP |= FDCAN_DBTP_TDC;
g_TxHeader.Identifier = 0x100; g_TxHeader.IdType = FDCAN_STANDARD_ID; g_TxHeader.TxFrameType = FDCAN_DATA_FRAME; g_TxHeader.DataLength = FDCAN_DLC_BYTES_8; g_TxHeader.FDFormat = FDCAN_FD_CAN; g_TxHeader.BitRateSwitch = FDCAN_BRS_ON;
When running in NORMAL mode, the STM32's PSR register shows LEC = 5 (Bit Dominant Error), and the node enters BUS-OFF after ~127 failed transmission attempts. The BUS-OFF recovery callback successfully restarts the FDCAN, but frames still don't reach the RPi.
Given that both boards pass internal and external loopback tests, all voltages are correct, termination is verified, and both transceivers are in Normal Mode — what could prevent the NCV7344 from actually driving the CAN bus lines? Is there a known issue with the NCV7344 TxD dominant timeout feature that could lock the transmitter? Any suggestions on what to check next would be greatly appreciated.
2026-05-12 8:56 AM - edited 2026-05-12 9:01 AM
Hello,
You need to provide more details.
Your complete STM32G0 project including the ioc file. to check all the parameters.
Your schematics including the transceiver+ the terminating resistors on the CAN bus.
What is the target bitrates in FDCAN mode: nominal and data phase?
As we don't support Raspberry Pi, you need to ensure you set correctly the FDCAN parameters from that side (timings, mode etc) ..
2026-05-12 9:57 AM
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @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"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "adc_handler.h"
#include "can_handler.h"
#include "cmd_handler.h"
#include "state_machine.h"
#include "bms_config.h"
#include "stm32g0xx.h"
#include "stm32g0xx_hal_fdcan.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.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 ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
FDCAN_HandleTypeDef hfdcan1;
IWDG_HandleTypeDef hiwdg;
TIM_HandleTypeDef htim7;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* Command and telemetry processing flags (written in interrupts, read in main) */
extern volatile uint8_t g_tx_flag; /* Set by TIM7 timer interrupt → trigger telemetry TX */
extern volatile uint8_t g_cmd_pending; /* Set by FDCAN RX interrupt → command received */
extern uint32_t g_fdcan_error_count;
/* Reception data (written in FDCAN interrupt, read in main) */
extern volatile FDCAN_RxHeaderTypeDef g_rx_header;
extern volatile uint8_t g_rx_data[8U];
/* Debug counters (written in ISR, read in main) */
extern volatile uint32_t g_first_fdcan_error;
extern volatile uint8_t g_fdcan_error_flag;
extern uint32_t g_fdcan_error_count;
extern volatile uint32_t g_tx_count; /* Frames transmitted */
extern volatile uint32_t g_rx_count; /* Frames received */
extern volatile uint32_t g_tim7_isr_count; /* TIM7 ISR call count */
/* DEBUG: Global checkpoint to track which init step fails */
volatile uint32_t g_init_checkpoint = 0U;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
static void MX_IWDG_Init(void);
static void MX_TIM7_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_FDCAN1_Init(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_ADC1_Init();
MX_IWDG_Init();
MX_TIM7_Init();
MX_USART2_UART_Init();
MX_FDCAN1_Init();
/* USER CODE BEGIN 2 */
GPIO_InitTypeDef txd_force;
txd_force.Pin = GPIO_PIN_5;
txd_force.Mode = GPIO_MODE_OUTPUT_PP;
txd_force.Pull = GPIO_NOPULL;
txd_force.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &txd_force);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_SET); // HIGH = recessive
HAL_Delay(10);
/* Manual UART test — this confirms serial is working before any printf */
const char msg[] = "\r\n=== UART HARDWARE TEST OK ===\r\n";
HAL_UART_Transmit(&huart2, (uint8_t*)msg, sizeof(msg)-1, 1000);
HAL_Delay(100);
/* Use HAL_UART_Transmit for critical init messages */
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n[INIT-ADC] Starting ADC...\r\n", 30, 1000);
HAL_Delay(30);
g_init_checkpoint = 1U;
ADC_Start();
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n[INIT-ADC] ADC started successfully!\r\n", 40, 1000);
HAL_Delay(30);
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n[INIT-CAN] Starting CAN...\r\n", 31, 1000);
HAL_Delay(30);
g_init_checkpoint = 2U;
CAN_Init();
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n[INIT-CAN] CAN started successfully!\r\n", 40, 1000);
HAL_Delay(30);
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n[INIT-SM] Starting state machine...\r\n", 40, 1000);
HAL_Delay(30);
g_init_checkpoint = 3U;
StateMachine_Init();
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n[INIT-SM] State machine initialized!\r\n", 40, 1000);
HAL_Delay(30);
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n[INIT-TIM] Starting TIM7...\r\n", 32, 1000);
HAL_Delay(30);
g_init_checkpoint = 4U;
if (HAL_TIM_Base_Start_IT(&htim7) != HAL_OK)
{
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n[ERROR] TIM7 start FAILED!\r\n", 31, 1000);
Error_Handler();
}
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n[INIT-TIM] TIM7 started successfully!\r\n", 41, 1000);
HAL_Delay(30);
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n[INIT-WDG] Starting watchdog...\r\n", 35, 1000);
HAL_Delay(30);
g_init_checkpoint = 5U;
MX_IWDG_Init();
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n[INIT-WDG] Watchdog initialized!\r\n", 37, 1000);
HAL_Delay(50);
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n[READY] Entering main loop...\r\n\r\n", 35, 1000);
HAL_Delay(50);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
static uint32_t status_count = 0U;
static uint32_t diag_loop_count = 0U;
while (1)
{
/* USER CODE END WHILE */
/* Diagnostic: print loop health every 1 second */
diag_loop_count++;
if (diag_loop_count >= 1000U)
{
diag_loop_count = 0U;
uint32_t tim7_delta = g_tim7_isr_count - status_count;
char diag_buf[128];
int len = sprintf(diag_buf,
"[LOOP] TX:%lu RX:%lu TIM7:%lu ERR:%lu delta:%lu\r\n",
g_tx_count, g_rx_count, g_tim7_isr_count,
g_fdcan_error_count, tim7_delta);
if (len > 0) {
HAL_UART_Transmit(&huart2, (uint8_t*)diag_buf, (uint16_t)len, 1000);
}
status_count = g_tim7_isr_count;
}
if (g_tx_flag == BMS_TRUE)
{
g_tx_flag = BMS_FALSE;
ADC_Poll();
float current_temp = ADC_GetTemperature();
BMS_SensorStatus_t sensor_status = SENSOR_OK;
StateMachine_Evaluate(current_temp, sensor_status);
CAN_SendTelemetryFrame();
int16_t temp_encoded = (int16_t)((current_temp + 40.0F) * 10.0F);
float temp_displayed = ((float)temp_encoded * 0.1F) - 40.0F;
int temp_int = (int)(temp_displayed * 10.0F);
char tx_buf[128];
int tx_len = sprintf(tx_buf, "[TX] Temp: %d.%dC | ADC: %4u\r\n",
temp_int / 10, abs(temp_int % 10), g_debug_adc_raw);
if (tx_len > 0) {
HAL_UART_Transmit(&huart2, (uint8_t*)tx_buf, (uint16_t)tx_len, HAL_MAX_DELAY);
}
}
/* Poll RX FIFO1 for loopback telemetry frames */
/*CAN_Poll_RX();*/
HAL_Delay(1);
HAL_IWDG_Refresh(&hiwdg);
/* Print FDCAN error info from main loop context (safe) */
if (g_fdcan_error_flag == 1U)
{
g_fdcan_error_flag = 0U;
char buf[128];
int len;
if (g_first_fdcan_error != 0xFFFFFFFFU)
{
len = sprintf(buf, "[FDCAN_ERR] First Error Code = 0x%08lX\r\n",
(unsigned long)g_first_fdcan_error);
HAL_UART_Transmit(&huart2, (uint8_t*)buf, len, HAL_MAX_DELAY);
g_first_fdcan_error = 0xFFFFFFFFU;
}
len = sprintf(buf, "[FDCAN] Error count: %lu | TX: %lu | RX: %lu\r\n",
g_fdcan_error_count, g_tx_count, g_rx_count);
HAL_UART_Transmit(&huart2, (uint8_t*)buf, len, HAL_MAX_DELAY);
}
}
/* 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};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
RCC_OscInitStruct.PLL.PLLN = 8;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
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_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief ADC1 Initialization Function
* @PAram None
* @retval None
*/
static void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.LowPowerAutoPowerOff = DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc1.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_160CYCLES_5;
hadc1.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_1CYCLE_5;
hadc1.Init.OversamplingMode = DISABLE;
hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_LOW;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
}
/**
* @brief FDCAN1 Initialization Function
* @PAram None
* @retval None
*/
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_DIV1;
hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS;
hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
hfdcan1.Init.AutoRetransmission = DISABLE;
hfdcan1.Init.TransmitPause = DISABLE;
hfdcan1.Init.ProtocolException = DISABLE;
hfdcan1.Init.NominalPrescaler = 1;
hfdcan1.Init.NominalSyncJumpWidth = 8;
hfdcan1.Init.NominalTimeSeg1 = 111;
hfdcan1.Init.NominalTimeSeg2 = 16;
hfdcan1.Init.DataPrescaler = 1;
hfdcan1.Init.DataSyncJumpWidth = 7;
hfdcan1.Init.DataTimeSeg1 = 21;
hfdcan1.Init.DataTimeSeg2 = 10;
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 */
/* Enable Transmitter Delay Compensation for CAN-FD.
* Auto mode is default on STM32G0 when TDC is enabled.*/
hfdcan1.Instance->DBTP |= FDCAN_DBTP_TDC;
/* USER CODE END FDCAN1_Init 2 */
}
/**
* @brief IWDG Initialization Function
* @PAram None
* @retval None
*/
static void MX_IWDG_Init(void)
{
/* USER CODE BEGIN IWDG_Init 0 */
/* USER CODE END IWDG_Init 0 */
/* USER CODE BEGIN IWDG_Init 1 */
/* USER CODE END IWDG_Init 1 */
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_64;
hiwdg.Init.Window = 4095;
hiwdg.Init.Reload = 3199 ;
if (HAL_IWDG_Init(&hiwdg) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN IWDG_Init 2 */
/* USER CODE END IWDG_Init 2 */
}
/**
* @brief TIM7 Initialization Function
* @PAram None
* @retval None
*/
static void MX_TIM7_Init(void)
{
/* USER CODE BEGIN TIM7_Init 0 */
/* USER CODE END TIM7_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM7_Init 1 */
/* USER CODE END TIM7_Init 1 */
htim7.Instance = TIM7;
htim7.Init.Prescaler = 6399;
htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
htim7.Init.Period = 999;
htim7.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim7) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM7_Init 2 */
/* USER CODE END TIM7_Init 2 */
}
/**
* @brief USART2 Initialization Function
* @PAram None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetTxFifoThreshold(&huart2, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetRxFifoThreshold(&huart2, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_DisableFifoMode(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @PAram None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|LED_GREEN_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PA0 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : LED_GREEN_Pin */
GPIO_InitStruct.Pin = LED_GREEN_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(LED_GREEN_GPIO_Port, &GPIO_InitStruct);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* Configure PA1 as analog input for potentiometer (ADC Channel 1) */
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/* Custom debug output using HAL_UART_Transmit directly (no buffering) */
/* Redirect printf to UART2 for serial output */
int __io_putchar(int ch)
{
/* Transmit with 50ms timeout per byte to ensure completion */
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 50);
/* Add small delay after newline to ensure output is visible */
if (ch == '\n')
{
HAL_Delay(2);
}
return ch;
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__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 can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
in the main i am using functions i defined in the can_handler.c
Termination: Both ends have 120Ω resistors. Measured resistance across CAN_H/CAN_L = 60Ω with power off.
Transceiver Voltages (idle):
NCV7344 TXD: 4.96V (from level shifter)
NCV7344 RXD: 4.96V
NCV7344 STB: 0V (Normal Mode)
CAN_H: 2.5V
CAN_L: 2.5V
| STM32 internal loopback (FDCAN_MODE_INTERNAL_LOOPBACK) | PASS — TX frames received correctly |
| STM32 external loopback (FDCAN_MODE_EXTERNAL_LOOPBACK) | PASS — transceiver path verified |
| RPi MCP2517FD internal loopback | PASS — frames sent/received on can0 |
| BUS-OFF in NORMAL mode | LEC = 5 (Bit Dominant Error), enters BUS-OFF after ~127 frames |
Given that both internal and external loopback modes work on the STM32, the NCV7344 appears to receive TX signals from the MCU correctly. But in NORMAL mode with the RPi connected, frames never arrive at the receiver, and the STM32 sees Bit Dominant Errors.
Is there a known behavior with the NCV7344 where the transmitter output stage gets locked out (e.g., TxD dominant timeout) during MCU reset or initialization that could prevent it from driving the CAN bus even though the STB pin is LOW?