platformIO canfd problems

Hello! I ran FDCAN in stmcubeide and it works perfectly, but when i’m trying to run it with platformIO it doesn’t work at all.

I added -D HAL_FDCAN_MODULE_ENABLED to .ini and it compiles, but sending nor getting messeges doesn’t work

I also set clocks for FDCAN

what can I check to solve my problem?

Hey, is the board definition you’re using the same one? Are you relying on the default pins, or are you configuring the pins explicitly?

i’m using PA11 and PA12 in my stm32g431

Those are pretty standard pins for g4, so should work. Getting CAN working is tricky - often it doesn’t work until it does!

It’s always better to start in loopback, that way you can rule out external hardware e.g. tranceiver or bad termination resistor etc.

I’ve written a library that supports fdcan - maybe check your code against my fdcan code and see what you are missing.

I’ve tried your library, erwin74’s code also (CAN Bus Support) still have nothing :frowning: looks like data is added to queue, but isn’t send at all (monitoring bus on osciloscope). I tried different clock setting, including FDCAN source (PLL, HSE, PCLK), various build flags, init values for FDCAN based on my stmcube and on Your libraries.

in stmcube it works good (i’m sure that hardware works), in plarformio i did working internal loopback mode, but normal mode doesn’t work :C

Could someone explain me why this works:

#include "myCAN/myCAN.h"

uint8_t TxData[64];

int main(void)
{
    myCAN_init();
  TxHeader.Identifier = 0x103; // Standard ID
  TxHeader.IdType = FDCAN_STANDARD_ID;
  TxHeader.TxFrameType = FDCAN_DATA_FRAME;
  TxHeader.DataLength = FDCAN_DLC_BYTES_8;
  TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
  TxHeader.BitRateSwitch = FDCAN_BRS_OFF;
  TxHeader.FDFormat = FDCAN_FD_CAN;
  TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
  TxHeader.MessageMarker = 0;
  TxData[0]=0b10101010;
  for(uint8_t i=1;i<64;i++){
	  TxData[i]=2*i+2;
  }

  while (1)
  {
		  HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, TxData);
  }

}

And that is not? ._____.

#include <Arduino.h>
#include "myCAN/myCAN.h"
uint8_t TxData[64];

void setup() {
    Serial1.begin(115200);
    


    myCAN_init();

  TxHeader.Identifier = 0x103; // Standard ID
  TxHeader.IdType = FDCAN_STANDARD_ID;
  TxHeader.TxFrameType = FDCAN_DATA_FRAME;
  TxHeader.DataLength = FDCAN_DLC_BYTES_8;
  TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
  TxHeader.BitRateSwitch = FDCAN_BRS_OFF;
  TxHeader.FDFormat = FDCAN_FD_CAN;
  TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
  TxHeader.MessageMarker = 0;
  TxData[0]=0b10101010;
  for(uint8_t i=1;i<64;i++){
	  TxData[i]=2*i+2;
  }
}

    
void loop(){
HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, TxData);
Serial1.println("A");
}

loop goes well

Is the first one stmcube and the second using arduino? There are plenty of reasons that boil down to the peripheral registers are unlikely to be setup identically. I’d export the peripheral registers from the known good one (stmcube) and compare to the one that doesn’t work (arduino).
Here is an example of me comparing registers from two firmwares:


Ignore the details in the picture, but where there are differences - you can look them up in the stm datasheet and see if the difference is likely to be important. I’d look at clock registers (RCC *) as well AS CAN1.
You can export Peripherals registers using stm32 programmer, note: before ‘Saving to file’ you need to reset otherwise the mcu will be halted before setup code has run

Nice trick!

Can you show what’s inside myCAN_init()?

.h

#include "stm32g4xx_hal.h"
#include "stm32g4xx_hal_fdcan.h"


extern FDCAN_TxHeaderTypeDef TxHeader;
extern FDCAN_RxHeaderTypeDef RxHeader;
extern FDCAN_HandleTypeDef hfdcan1;
extern uint8_t RxData[64];


void MX_FDCAN1_Init(void);
void myCAN_init( void );

#ifdef __cplusplus
 extern "C" {
#endif
void FDCAN1_IT0_IRQHandler(void);
void FDCAN1_IT1_IRQHandler(void);
#ifdef __cplusplus
}
#endif

.cpp

#include "myCAN.h"
#include <Arduino.h>


FDCAN_HandleTypeDef hfdcan1;
FDCAN_TxHeaderTypeDef TxHeader;
FDCAN_RxHeaderTypeDef RxHeader;

uint8_t RxData[64];

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_DIV2;
  hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS;
  hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
  hfdcan1.Init.AutoRetransmission = ENABLE;
  hfdcan1.Init.TransmitPause = DISABLE;
  hfdcan1.Init.ProtocolException = ENABLE;
  hfdcan1.Init.NominalPrescaler = 1;
  hfdcan1.Init.NominalSyncJumpWidth = 2;
  hfdcan1.Init.NominalTimeSeg1 = 53;
  hfdcan1.Init.NominalTimeSeg2 = 2;
  hfdcan1.Init.DataPrescaler = 1;
  hfdcan1.Init.DataSyncJumpWidth = 3;
  hfdcan1.Init.DataTimeSeg1 = 3;
  hfdcan1.Init.DataTimeSeg2 = 3;
  hfdcan1.Init.StdFiltersNbr = 1;
  hfdcan1.Init.ExtFiltersNbr = 0;
  hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
  if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK)
  {
  }
  /* USER CODE BEGIN FDCAN1_Init 2 */

  FDCAN_FilterTypeDef sFilterConfig;

  // Configure the filter to accept all messages with Standard ID 0x321
  sFilterConfig.IdType = FDCAN_STANDARD_ID;
  sFilterConfig.FilterIndex = 0; // Use the first filter bank
  sFilterConfig.FilterType = FDCAN_FILTER_MASK; // Specify the filter type
  sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; // Store matching messages in RX FIFO 0
  sFilterConfig.FilterID1 = 0x102; // filter
  sFilterConfig.FilterID2 = 0x102; // mask


  if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK) {
    // Handle error
  }
  HAL_FDCAN_ConfigGlobalFilter(&hfdcan1,
                               FDCAN_REJECT, // Reject non-matching Standard ID messages
                               FDCAN_REJECT, // Reject non-matching Extended ID messages
                               FDCAN_REJECT_REMOTE,      // Reject remote frames with Standard ID
                               FDCAN_REJECT_REMOTE);     // Reject remote frames with Extended ID

  /* Configure and enable Tx Delay Compensation : TdcOffset = DataTimeSeg1*DataPrescaler */
  HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan1, 3, 0);
  HAL_FDCAN_EnableTxDelayCompensation(&hfdcan1);

  /* USER CODE END FDCAN1_Init 2 */

}


void myCAN_init( void ){
    HAL_Init();
    HAL_FDCAN_MspInit(&hfdcan1);
    MX_FDCAN1_Init();
    HAL_FDCAN_Start(&hfdcan1);
    HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);
    
}


void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef* hfdcan)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  if(hfdcan->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)
    {
    }

    /* Peripheral 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_LOW;
    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);
    HAL_NVIC_SetPriority(FDCAN1_IT1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(FDCAN1_IT1_IRQn);
  /* USER CODE BEGIN FDCAN1_MspInit 1 */

  /* USER CODE END FDCAN1_MspInit 1 */
  }

}



void FDCAN1_IT0_IRQHandler(void)
{
  /* USER CODE BEGIN FDCAN1_IT0_IRQn 0 */

  /* USER CODE END FDCAN1_IT0_IRQn 0 */
  HAL_FDCAN_IRQHandler(&hfdcan1);
  /* USER CODE BEGIN FDCAN1_IT0_IRQn 1 */

  /* USER CODE END FDCAN1_IT0_IRQn 1 */
}

/**
  * @brief This function handles FDCAN1 interrupt 1.
  */
void FDCAN1_IT1_IRQHandler(void)
{
  /* USER CODE BEGIN FDCAN1_IT1_IRQn 0 */

  /* USER CODE END FDCAN1_IT1_IRQn 0 */
  HAL_FDCAN_IRQHandler(&hfdcan1);
  /* USER CODE BEGIN FDCAN1_IT1_IRQn 1 */

  /* USER CODE END FDCAN1_IT1_IRQn 1 */
}



void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs){
	HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader, RxData);
    // if(RxData[0]){
    //     digitalWrite(PC2, 1);
    // }
    // else{
    //     digitalWrite(PC2, 0);
    // }
    Serial1.println("HIHI");
    
}

Finally worked, althought it was while(1) - void loop() issue(i mean it works only with classic loop), thanks @Owen_Williams for helping

I was thinking that not working arduino’s loops could be caused by some differences between C and C++ or something like that? after all stmcube code is typical C

I compared yours with what I have working (FDCAN on G431) and they are very similar, the only difference I can find is that you are calling HAL_FDCAN_Start before activate notifications, but HAL reference says it should not matter…

Is your mcu locking up at all? Or everything is working, you just don’t see any CAN messages? If it’s locking up, it could be that some interrupt handler is not set correctly.
Are you sure you need the TxDelayCompensations?

Not to spoil the broth by adding more code to look through, but this works for me: lemon-pepper-stepper/firmware/lib/can/can.cpp at main · VIPQualityPost/lemon-pepper-stepper · GitHub

huh, CAN works well with

int main( void ){
while(1){

}
}

but doesn’t want to work with

void setup(){
}

void loop{
}

I combined simpleFOC with CAN using first option (int main) and it stared work very weird, sometimes works good, sometimes doesn’t even do simpleFOC classes and loop

when i’m trying to run simplefoc code only with int main it doesn’t work at all

CAN with void setup and void loop also doesnt work

Can you explain the first code block with main()

  1. Is this in an Arduino file/using Arduino core. Pretty sure in defining main you are bypassing all Arduino setup. It’s no longer really arduino.

  2. Or is this using stmcube and stm core?.

All my tests were in platformIO (apart from stmcubeide when everything works). For some reason CAN doesn’t work when I’m using arduino’s loops (void setup, void loop), it works with int main and while(1) but as You mentioned it is not arduino and then other functions stop working well

Sorry to say it but you may have to start doing peripheral register compare. It’s going to be something small like the wrong alternate function for a pin, or a clock not started or one of the fdcan registers.

If you go this route, you need to make sure that the peripherals registers are dumped after the code has made it to loop().

This process is painful but i learned loads doing this sort of thing because it requires you to understand what the registers do and work out how to change then to match the stmcube version using hal libraries

FDCAN1:




RCC:

I found some differences, where they come from (I mean in CAN, where i guess arduino shouldn’t config anything?) and how to deal with them? Simply force those registers with “good” values?

I discovered 1 more interesting thing - when i upload arduino sketch and then my (int main) CAN and simplefoc works until I turn off the power supply. Is there any arduino config/setup function that i can run before my code to eliminate that issue maybe?

Looking at the datasheet -

  • the ESR is the CAN error control register - the CEL=0xB means that the “good firmware” had some CAN protocol errors but bad had none
  • the PSR is the protocol protocol status reg.
    • LEC 0x0 vs 0x7 - isn’t revealing (no error vs no change in error)
    • ACT 0x1 vs 0x1 - sync vs idle
    • REDL 0x1 vs 0x0- the good firmware has reveived a can message
  • the IR is the can interrupt register and 0x1 is for a protocol error during arbitration
  • the rxfoa is the acknowledge register. not too exciting
  • the TEST is the test register. RX means that the receive pin is in recessive state. That seems plausible/normal.

Any differences in FDCAN_DBTP (bit timing and prescaler)?
Any different in ‘FDCANSEL’ bit field in the RCC_CCIPR register?

No differences in those registers

I figured out that if i upload arduino sketch and then my (int main) CAN and simplefoc works until I turn off the power supply, so would be usefull to see registers before and after turning off supply, but unfortunetely connecting stm32cubeprogrammer stops programm until i upload it again (no need to upload arduino code before :P) so I cant read registers on working can+simplefoc, or maybe can i connect stm32cubeprogrammer without reseting some registers ?

or maybe do you know what does simplefoc needs from arduino to work that is not assured by int main{ while(1){}}?