cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 FDCAN node sees nothing on bus when CAN-to-USB adapter removed

aqua_dev
Associate

Board & Setup:

  • STM32H563RGT6 running FDCAN2 in Normal mode (HAL_FDCAN_Init, Mode=FDCAN_MODE_NORMAL)

  • SN65HVD230 transceiver in silent mode (Rs pin connected to 3.3V) (120 Ω intergrated) wired with a single 120 Ω terminator at the far end

  • No other pull-up/down or termination changes when the USB–CAN dongle is unplugged

  • Filters configured (Reading the data from the BMS of a custom battery):

    • FIFO0 → IDs 0x1E0 (BMS_STATE), 0x1E2 (PACK_MINMAX), 0x1E3 (PACK_AVG)

    • FIFO1 → ID 0x1E1 (PACK_VALUES)

Symptom:

  • With the USB–CAN adapter or another node on the bus, everything works: the real BMS sends all four messages, our STM32 ACKs them, and they appear in Cangaroo.

  • As soon as the USB–CAN dongle is unplugged (or if our transceiver is switched to any other mode -> Slope control or High speed (no slope control)), no messages are observed on the bus—even though the BMS should still be broadcasting at 1 Hz (it constantly spits out data at a 1 Hz rate) and our STM32 is in Normal mode.

What I need:

  1. Keep the SN65HVD230 in Normal (ACK-capable) mode—tie its RS pin LOW (or drive it low in GPIO init).

  2. Still “ignore” any frames I transmit (because I’m not sending any), so that I only see the BMS frames.

How do I ensure my SN65HVD230 is always in normal, ACK-capable mode (so the BMS sees at least one ACK and keeps talking), while my STM32 itself continues to filter out any of its own (non-existent) messages so I only capture IDs 0x1E0–0x1E3?

***My STM32CubeIDE "main.c" is given in attachments.

26 REPLIES 26

I checked and all nodes had 120 ohms resistors. So I removed the one at the end of the CAN bus and the one from the USB-CAN. This leaves the one from the BMS and the CAN transceiver.

I attached the configuration of my FDCAN2 which is the one I use for the CAN communication. I am forced to use a bitrate of 250kbits/s since the BMS is forcing that speed. I should note that the CAN is classic and only standard IDs (11 bits) will be used.


@aqua_dev wrote:

I checked and all nodes had 120 ohms resistors. So I removed the one at the end of the CAN bus and the one from the USB-CAN. This leaves the one from the BMS and the CAN transceiver.


You need only two terminating resistors at both sides of the bus:

Figure 1.png

If you are using HSI please switch to a HSE/crystal to feed FDCAN clock.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

I attached the configuration of my FDCAN2 which is the one I use for the CAN communication. I am forced to use a bitrate of 250kbits/s since the BMS is forcing that speed. I should note that the CAN is classic and only standard IDs (11 bits) will be used.

Right now, the clock feeding the CAN is the PLL1Q which comes from the CSI.

It's not recommended to use CSI/HSI or any other internal RC clock source in the CAN communication.

Please use HSE with a crystal.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

Do you have any help to give on the parameter decision? I need to achieve a 250kbits/s bitrate.

Karl Yamashita
Principal

  • STM32H563RGT6 running FDCAN2 in Normal mode (HAL_FDCAN_Init, Mode=FDCAN_MODE_NORMAL)

  • SN65HVD230 transceiver in silent mode (Rs pin connected to 3.3V) (120 Ω intergrated) wired with a single 120 Ω terminator at the far end


I missed this earlier but the CAN transceiver can not be in low power mode. You need to keep the RS pin grounded in order for the STM32 CAN Controller to ACK.

void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) {
	if (RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) {
		while (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan2, FDCAN_RX_FIFO0) > 0) {
			if (HAL_FDCAN_GetRxMessage(&hfdcan2, FDCAN_RX_FIFO0, &RxH, RxD) != HAL_OK) {
				Error_Handler();
			}

			switch (RxH.Identifier) {
			  case 480:  // BMS_STATE @ 1 Hz
				parse_BMS_STATE(RxD, &st);
				break;
			  case 482:  // PACK_MINMAX_VALUES @ 1 Hz
				parse_PACK_MINMAX_VALUES(RxD, &mm);
				break;
			  case 483:  // PACK_AVG_VALUES @ 1 Hz
				parse_PACK_AVG_VALUES(RxD, &av);
				break;
			}
		}
	}
}

What is the STM32 supposed to do when the BMS is sending messages and you parse them? I see the data structure st, mm and av, but i don't see any code else where that uses the data? Just trying to understand how do you know the STM32 is actually receiving messages? 

Don't worry, I won't byte.
TimerCallback tutorial! | UART and DMA Idle tutorial!

If you find my solution useful, please click the Accept as Solution so others see the solution.

My transceiver has a 10k resistor to ground on Rs pin at the moment. Right now the data is now being used, I just translate it to the right variable types, see the lines 994 to 1085 but I don't do much more then that as of now.

Karl Yamashita
Principal

Instead of extracting bits using BE_BIT into the data structure, try using union

typedef union 
{
    struct
    {
        uint8_t data[8];
    }Byte;
    struct
    {
        // critical flags
        bool crit_current_high;			// 63
        bool crit_imbal_high;			// 62
        bool crit_pack_volt_low;		// 61
        bool crit_pack_volt_high;		// 60
        bool crit_cell_volt_low;		// 59
        bool crit_cell_volt_high;		// 58
        bool crit_temp_low;				// 57
        bool crit_temp_high;			// 56
        // warning flags
        bool warn_current_high;			// 55
        bool warn_imbal_high;			// 54
        bool warn_pack_volt_low;		// 53
        bool warn_pack_volt_high;		// 52
        bool warn_cell_volt_low;		// 51
        bool warn_cell_volt_high;		// 50
        bool warn_temp_low;				// 49
        bool warn_temp_high;			// 48
        // remaining capacity [As]
        uint16_t remaining_capacity;	// 32-47
        // state of health [%]
        uint8_t  soh;					// 24-31
        // state of charge [%]
        uint8_t  soc;					// 16-23
        // fault register
        uint8_t  main_fault_reg;		// 8-15
        // overall pack state
        uint8_t  state;					// 0-7
    }Status;
} BMS_State;

 

Then all you need to do is copy the bytes which requires less instruction cycles than extracting all the bits individually

void parse_BMS_STATE(const uint8_t data[8], BMS_State *s)
{
    int i;

    for(i = 0;i <8;i++)
    {
        s->Byte.data[i] = data[i];
    }
}

 

and to use the data

if(st.Status.crit_current_high)
{
    // do something
}

 

Don't worry, I won't byte.
TimerCallback tutorial! | UART and DMA Idle tutorial!

If you find my solution useful, please click the Accept as Solution so others see the solution.