Skip to main content
Visitor
September 18, 2026
Question

CAN messsages not transmitting even after successful return

  • September 18, 2026
  • 8 replies
  • 36 views

Hello, hope you all are doing well.

I am unable to transmit messages over CAN using DMA on my STM32G431RB Nucleo board. My JLink didn’t show any errors in my code flow, but my logic analyzer did not register any CAN messages on the TX pin (PA12), and the output stays high. I also tried using a CAN adapter to read the messages which wasn’t fruitful. How can I debug this? I used this tutorial for reference: 

Thank you in advance!

CanManager.cpp

#include "CanManager.h"

CanManager::CanManager(FDCAN_HandleTypeDef* hfdcan)
: mHandle(hfdcan)
{
mFilterConfig.IdType = FDCAN_STANDARD_ID;
mFilterConfig.FilterIndex = 0;
mFilterConfig.FilterType = FDCAN_FILTER_RANGE;
mFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
mFilterConfig.FilterID1 = TX_ID;
mFilterConfig.FilterID2 = 0x7FFU;
}

HAL_StatusTypeDef CanManager::Init()
{
HAL_StatusTypeDef ret = HAL_FDCAN_ConfigFilter(mHandle, &mFilterConfig);
if (ret != HAL_OK) {
return ret;
}

ret = HAL_FDCAN_Start(mHandle);
if (ret != HAL_OK) {
return ret;
}

ret = HAL_FDCAN_ActivateNotification(mHandle, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);
if (ret != HAL_OK) {
return ret;
}

mTxHeader.Identifier = TX_ID;
mTxHeader.IdType = FDCAN_STANDARD_ID;
mTxHeader.TxFrameType = FDCAN_DATA_FRAME;
mTxHeader.DataLength = FDCAN_DLC_BYTES_0;
mTxHeader.ErrorStateIndicator = FDCAN_ESI_PASSIVE;
mTxHeader.BitRateSwitch = FDCAN_BRS_OFF;
mTxHeader.FDFormat = FDCAN_CLASSIC_CAN;
mTxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
mTxHeader.MessageMarker = 0;

return ret;
}

uint32_t CanManager::LengthToDlc(uint8_t length)
{
switch (length) {
case 0: return FDCAN_DLC_BYTES_0;
case 1: return FDCAN_DLC_BYTES_1;
case 2: return FDCAN_DLC_BYTES_2;
case 3: return FDCAN_DLC_BYTES_3;
case 4: return FDCAN_DLC_BYTES_4;
case 5: return FDCAN_DLC_BYTES_5;
case 6: return FDCAN_DLC_BYTES_6;
case 7: return FDCAN_DLC_BYTES_7;
default: return FDCAN_DLC_BYTES_8;
}
}

HAL_StatusTypeDef CanManager::TransmitMessage(const uint8_t* data, uint8_t length)
{
if (length > PAYLOAD_MAX_SIZE) {
return HAL_ERROR;
}

mTxHeader.DataLength = LengthToDlc(length);

return HAL_FDCAN_AddMessageToTxFifoQ(mHandle, &mTxHeader, data);
}

DoorECU.cpp 

#include "DoorECU.h"

DoorECU::DoorECU()
: mCanManager(&hfdcan1),
mButton(BUTTON_GPIO_Port, BUTTON_Pin, &htim7, &mCanManager),
mLedDriver(&htim2, TIM_CHANNEL_1),
mTerminate(false)
{
}

HAL_StatusTypeDef DoorECU::Init()
{
HAL_StatusTypeDef ret = mCanManager.Init();
return ret;
}

void DoorECU::Run()
{
while (!mTerminate) {
uint8_t data[2] = {1,1};

mCanManager.TransmitMessage(data, FDCAN_DLC_BYTES_2);
HAL_Delay(1000);
}
}

appDoorECU.cpp

#include "appDoorECU.h"
#include "DoorECU.h"

static DoorECU gDoorECU;

HAL_StatusTypeDef appDoorECU_Init()
{
return gDoorECU.Init();
}

void appDoorECU_Run()
{
gDoorECU.Run();
}

void appDoorECU_Terminate()
{
gDoorECU.Terminate();
}

void appDoorECU_StartDebounceTimer()
{
gDoorECU.StartDebounceTimer();
}

void appDoorECU_OnDebounceTimerElapsed()
{
gDoorECU.OnDebounceTimerElapsed();
}

void appDoorECU_OnPwmPulseFinished()
{
gDoorECU.OnPwmPulseFinished();
}

main.c

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_DMA_Init();
MX_FDCAN1_Init();
MX_TIM7_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
HAL_StatusTypeDef ret = appDoorECU_Init();
if (ret != HAL_OK) {
Error_Handler();
}

/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
appDoorECU_Run();
/* USER CODE END 3 */
}

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;
hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1;
hfdcan1.Init.FrameFormat = FDCAN_FRAME_CLASSIC;
hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
hfdcan1.Init.AutoRetransmission = ENABLE;
hfdcan1.Init.TransmitPause = DISABLE;
hfdcan1.Init.ProtocolException = DISABLE;
hfdcan1.Init.NominalPrescaler = 3;
hfdcan1.Init.NominalSyncJumpWidth = 1;
hfdcan1.Init.NominalTimeSeg1 = 13;
hfdcan1.Init.NominalTimeSeg2 = 2;
hfdcan1.Init.DataPrescaler = 1;
hfdcan1.Init.DataSyncJumpWidth = 1;
hfdcan1.Init.DataTimeSeg1 = 1;
hfdcan1.Init.DataTimeSeg2 = 1;
hfdcan1.Init.StdFiltersNbr = 0;
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_FDCAN;
PeriphClkInit.FdcanClockSelection = RCC_FDCANCLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}

/* FDCAN1 clock enable */
__HAL_RCC_FDCAN_CLK_ENABLE();

__HAL_RCC_GPIOA_CLK_ENABLE();
/**FDCAN1 GPIO Configuration
PA11 ------> FDCAN1_RX
PA12 ------> FDCAN1_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF9_FDCAN1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/* FDCAN1 interrupt Init */
HAL_NVIC_SetPriority(FDCAN1_IT0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn);
/* 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_FDCAN_CLK_DISABLE();

/**FDCAN1 GPIO Configuration
PA11 ------> FDCAN1_RX
PA12 ------> FDCAN1_TX
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);

/* FDCAN1 interrupt Deinit */
HAL_NVIC_DisableIRQ(FDCAN1_IT0_IRQn);
/* USER CODE BEGIN FDCAN1_MspDeInit 1 */

/* USER CODE END FDCAN1_MspDeInit 1 */
}
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

 

8 replies

Andrew Neil
Super User
September 18, 2026

My JLink didn’t show any errors in my code flow

Not sure what you mean by that?

A JLink - (like an STLink, or any other debug probe)  just provides the SWD connection - it doesn’t do any “code flow” analysis.
It neither knows now cares anything about your source code.

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
jjohnAuthor
Visitor
September 18, 2026

Sorry, to clarify, I used the JLink to debug my board step by step, and observed HAL_FDCAN_AddMessageToTxFifoQ returning HAL_OK.

mƎALLEm
ST Technical Moderator
September 18, 2026

 Hello ​@jjohn and welcome to the ST community,

I am unable to transmit messages over CAN using DMA on my STM32G431RB Nucleo board

Not sure what do you mean by CAN using DMA here. There is no connection between FDCAN peripheral and DMA and I don’t seen any usage of DMA in your code!

My JLink didn’t show any errors in my code flow, but my logic analyzer did not register any CAN messages on the TX pin (PA12), and the output stays high. I also tried using a CAN adapter to read the messages which wasn’t fruitful. How can I debug this? 

J-LINK has nothing to do here it’s just a hardware probe to connect your MCU to the debugger.

Now, as you are using FDCAN in Normal mode, could you please share your schematics focusing on CAN hardware? 

You said: “I also tried using a CAN adapter to read the messages” show us how did you connect that with a sketch. I think you are using a CAN adapter as a second node (which is a must). 

Also are you sure you are probing on PA12? not on another GPIO? please check your Nucleo board again.

But before that I recommend you to try the external loop-back mode before going ahead. So at least you can validate a part of the transmission and you can monitor FDCAN_Tx with a logic analyzer without a necessity of another node nor a CAN tranceiver.

 

To give better visibility on the answered topics, please click "Best answer" on the reply which solved your issue or answered your question.
jjohnAuthor
Visitor
September 18, 2026

Hello, mƎALLEm, thank you!

I looked at the definition of HAL_FDCAN_AddMessageToTxFifoQ and saw that it was copying the message to RAM which I thought was using DMA internally, but it looks I am mistaken about the concept, sorry about that.

Yes, I meant that I debugged the code using JLink and GDB, and saw that HAL_FDCAN_AddMessageToTxFifoQ was copying the message to RAM and returning HAL_OK.

Yes, I will try the external loop back mode as well and report back.

This is a simple schematic of my connections. Assume all the power and ground pins are connected. I am using an STM32G431RB Nucleo board with a Waveshare SN65HVD230 CAN module

 

mƎALLEm
ST Technical Moderator
September 18, 2026

You have an issue with the transceiver:

Based on the schematics, RS pin is kept floating and it’s internally pulled-up. I don’t know about that Waveshare module and how that RS is connected.

The RS pin should be connected to the ground.

According to the datasheet, when Rs is pulled up it puts the transceiver in low power mode.

But need to test the External loopback mode.

To give better visibility on the answered topics, please click "Best answer" on the reply which solved your issue or answered your question.
Karl Yamashita
Principal
September 18, 2026

With your current filters, if you want to receive CAN messages when you do loop back, you’ll at least need to use a non-zero number for hfdcan1.Init.StdFiltersNbr

If a reply has proven helpful, click on Accept as Solution so that it'll show at top of the post.CAN Jammer an open source CAN bus hacking toolCANableV3 Open Source
Associate II
September 18, 2026

Are you sure that you have enabled the clocks needed “The clock controller distributes the clocks coming from different oscillators to the core and the peripherals”  Also have the correct GPIO pins been enabled. Also has the alternate function of the selected pins been selected for FDCAN controller.

If you are not sure suggest you start from simple setup working upwards:-

  1. Test the two pins (that will be Tx Rx) as GPIO outputs and toggle them, prove you have control.
  2. Configure the FDCAN and the Tx and Rx pins and check that the Tx pin is an output (will overcome a 4K7 resistor) Check the input pin is an input (can’t drive that resistor etc). You may need the controller configured before this happens?
  3. Avoid DMA initially just configure the FDCAN (as CAN) and send a CAN message object check it is on the Tx output.
  4. Then add in DMA if its supported on CAN controller for your MCU.
  5. Remember that CAN controllers send an ACK pulse, needed to proceed as a live CAN bus.

Setting up CAN is a matter of very precise setting of config registers, usually the main control registers need setting and then an Init bit to “go live”.  Also individual Message Objects need setting up individually.  Set up and test for Tx first not Rx, Tx is easier to see working and test. You may also need to configure FIFO.