Author Topic: STM32: FDCAN stuck in INIT  (Read 3034 times)

0 Members and 1 Guest are viewing this topic.

Offline bittumblerTopic starter

  • Contributor
  • Posts: 37
  • Country: de
STM32: FDCAN stuck in INIT
« on: August 03, 2021, 01:34:43 pm »
Hi,
this is on a stm32g431.
I try to use FDCAN. My problem is, it never leaves INIT.
Any idea, what i am doing wrong?
Is there some bare/simple example FDCAN code anywhere? I could not find anything without HAL/Cube..

Code: [Select]
void FDCANsetup (void) {
        // configure PA11, PA12 for AF9 FDCAN1 - PA11 Pin21 is FDCAN1_RX, PA12 Pin22 is FDCAN1_TX
        RCC->AHB2ENR    |=  (RCC_AHB2ENR_GPIOAEN);
        RCC->CCIPR      &= ~( RCC_CCIPR_FDCANSEL); // HSE clock 8Mhz
        RCC->APB1ENR1   |=  (RCC_APB1ENR1_FDCANEN); // turn on FDCAN
        RCC->APB1SMENR1 |=  (RCC_APB1SMENR1_FDCANSMEN); // clock enabled during sleep
        RCC->APB1RSTR1  |=  (RCC_APB1RSTR1_FDCANRST); // reset FDCAN

        GPIOA->MODER    &= ~( GPIO_MODER_MODE11                   | GPIO_MODER_MODE12 );
        GPIOA->MODER    |=  ( ( 0x2 << GPIO_MODER_MODE11_Pos)     | ( 0x2 << GPIO_MODER_MODE12_Pos) );
        GPIOA->OTYPER   &= ~( GPIO_OTYPER_OT11                    | GPIO_OTYPER_OT12 );
        GPIOA->OSPEEDR  &= ~( GPIO_OSPEEDR_OSPEED11               | GPIO_OSPEEDR_OSPEED12);
        GPIOA->OSPEEDR  |=  ( ( 0x3 << GPIO_OSPEEDR_OSPEED11_Pos) | (0x3 << GPIO_OSPEEDR_OSPEED12_Pos) );
        GPIOA->PUPDR    &= ~( GPIO_PUPDR_PUPD11                   | GPIO_PUPDR_PUPD12);
        // GPIOA->PUPDR    |=  ( ( 0x1 << GPIO_PUPDR_PUPD11_Pos )    | (0x1 << GPIO_PUPDR_PUPD12_Pos) );       
        GPIOA->AFR[1]   &= ~( GPIO_AFRH_AFSEL11                   | GPIO_AFRH_AFSEL12);
        GPIOA->AFR[1]   |=  ( ( 0x9 << GPIO_AFRH_AFSEL11_Pos )    | ( 0x9 << GPIO_AFRH_AFSEL12_Pos ) );

        FDCAN1->CCCR    |=  (FDCAN_CCCR_INIT);
        FDCAN1->CCCR    |=  (FDCAN_CCCR_CCE);
        while (! ((FDCAN1->CCCR & FDCAN_CCCR_INIT) == FDCAN_CCCR_INIT)) { };  // wait for Init

        FDCAN1->CCCR    |=  (FDCAN_CCCR_TEST);  // enable loopback mode
        FDCAN1->TEST    |=  (FDCAN_TEST_LBCK);  // enable loopback mode
        // FDCAN1->CCCR |=  (FDCAN_CCCR_MON);   // monitor mode (for internal loopback)

        FDCAN1->CCCR    |=  (FDCAN_CCCR_PXHD);  // disable Protocol Exception handling
        FDCAN1->CCCR    |=  (FDCAN_CCCR_FDOE);  // enable FDCAN Operation
        //FDCAN1->CCCR  &= ~(FDCAN_CCCR_BRSE);  // no bitrate switching (BRSE)
        FDCAN1->CCCR    |=  (FDCAN_CCCR_BRSE);  // bitrate switching (BRSE)

        FDCAN_CONFIG->CKDIV     |=  (0x0 << FDCAN_CKDIV_PDIV_Pos);      // Clock divider 0 -> 8Mhz/1 -> 8Mhz FDCAN kernel clock

        FDCAN1->DBTP    |=  (FDCAN_DBTP_TDC);           // transmitter delay compensation
        FDCAN1->DBTP    |=  (0x1 << FDCAN_DBTP_DBRP_Pos);       // 8Mhz/2 -> 4Mhz quanta
                                                                // DBTP_DTSEG1 default 0xA
                                                                // DBTP_DTSEG2 default 0x3
                                                                // DBTP_DSJW default 0x3
        FDCAN1->NBTP    |=  (0x1 << FDCAN_NBTP_NBRP_Pos);       // 8Mhz/2 -> 4Mhz quanta
                                                                // NBTP_NSJW default
                                                                // NBTP_NTSEG1 default
                                                                // NBTP_NTSEG2 default

        FDCAN1->TSCC    |=  (0x0 << FDCAN_TSCC_TCP_Pos);        // Prescaler = 1 FDCAN_TSCC_TCP
        // FDCAN1->TSCC |=  (0x10 << FDCAN_TSCC_TSS_Pos);       // for FDCAN

        FDCAN1->CCCR    &= ~(FDCAN_CCCR_CCE);
        FDCAN1->CCCR    &= ~(FDCAN_CCCR_INIT);  // enable FDCAN
dbg_printf("FDCAN setup2 \n\r");
        while (((FDCAN1->CCCR & FDCAN_CCCR_INIT) == FDCAN_CCCR_INIT)) { };  // wait for Init
        // never reaches here CCCR stays at 0x00000001
dbg_printf("FDCAN setup3 \n\r");


        // setup an extended acceptance filter to RxFIFO0 for ID 0x4711 0x4712
        FDCAN1_RAM->EFE[0].F0   |= ((0x1 << FDCAN_EFE0_EFEC_Pos) | (0x4711 << FDCAN_EFE0_EFID1_Pos ));
        FDCAN1_RAM->EFE[0].F1   |= ((0x1 << FDCAN_EFE1_EFTI_Pos) | (0x4712 << FDCAN_EFE1_EFID2_Pos ));

        FDCAN1->RXGFC           |=  ( 0x1 << FDCAN_RXGFC_LSE_Pos ); // number of extended acceptance filters
        // FDCAN1->RXGFC        |=  ( 0x3 << FDCAN_RXGFC_ANFS_Pos );    // reject nonmatching std frames
        // FDCAN1->RXGFC        |=  ( 0x3 << FDCAN_RXGFC_ANFE_Pos );    // reject nonmatching ext frames

        FDCAN1->IE      |=  (FDCAN_IE_RF0NE);   // interrupt for new message in Rx FIFO0
        // FDCAN1->ILS  &= ~(FDCAN_ILS_RxFIFO0);
        FDCAN1->ILE     |=  (FDCAN_ILE_EINT0);  // enable interrupt 0

        NVIC_EnableIRQ(FDCAN1_IT0_IRQn);        // enable interrupt

        dbg_printf("FDCAN Release: 0x%8.8x \n\r",FDCAN1->CREL);

}

 

Offline psysc0rpi0n

  • Frequent Contributor
  • **
  • Posts: 326
  • Country: ar
Re: STM32: FDCAN stuck in INIT
« Reply #1 on: August 03, 2021, 03:19:21 pm »
I'm not sure what tools are you using, but I think if you use gdb, you might be able to run the code, step by step and check where it is hanging. There are some situations where you have to wait for some bit to be set or cleared and you might be getting stuck in one of those or something like that!

I'm trying to play myself with libopenCM3, openocd and gdb but I'm also struggling to make it work because I still don't have good understanding on how to read and interpret the reference manual. So, it is being quite a struggle.

Edited;
I just saw your while loop. Ignore what I just said! :p
« Last Edit: August 03, 2021, 06:40:31 pm by psysc0rpi0n »
 

Offline bittumblerTopic starter

  • Contributor
  • Posts: 37
  • Country: de
Re: STM32: FDCAN stuck in INIT
« Reply #2 on: August 03, 2021, 04:08:27 pm »
Hi psysc0rpi0n,

i am using the microcontrollers C API. They call this "bare metal programming" nowadays. Then gcc and st-flash.

The reason for this are many: The microcontrollers software interface is well documented (here: 2000+ pages of real docs), most other APIs are not.
Even worse are these fancy IDEs like Cube. If you come back a few months or years later to your software to fix something tiny, with the C-API its just a "make" and everything works. With these IDEs and Wrappers you are in for hours of upgrades, then hours of porting to new revisions and days of debugging. If they are not already discontinued or "clouded". Then you can start from scratch.

Usually starting with the bare C API is a few hours more work at the beginn to look up all the details in the manual and transfer it to some C-macro. But for me this usually pays of quickly.
If you can find some usable example code, the starting effort can be verly small. Unfortunatelly stm provides almost no C-API examples (without this HAL wrapper).
 
Also i am somewhat lazy. So one of the first things i usually do with a new microcontroller, is connect a serial port. So i can use printf for debugging. Most of the time this works even inside ISRs.


 

Offline capt bullshot

  • Super Contributor
  • ***
  • Posts: 3033
  • Country: de
    • Mostly useless stuff, but nice to have: wunderkis.de
Re: STM32: FDCAN stuck in INIT
« Reply #3 on: August 03, 2021, 05:55:28 pm »
Don't know if this helps. I vaguely remember the (non-FD) CAN of an STM32 hanging in init if the RxCAN pin is low while trying to initialize. Can't remember the details, nor the solution.
Safety devices hinder evolution
 

Offline bittumblerTopic starter

  • Contributor
  • Posts: 37
  • Country: de
Re: STM32: FDCAN stuck in INIT
« Reply #4 on: August 03, 2021, 06:13:56 pm »
Hi capt bullshot,

the stm32 pins are currently connected to a CAN transceiver. The actual CAN bus lines are unconnected. But all of this is in the stm32s Loopback mode.
At least activating the integrated pullup/down resistors does not make a difference. Will try with a termination resistor on the bus, but have to find one first.
 

Offline psysc0rpi0n

  • Frequent Contributor
  • **
  • Posts: 326
  • Country: ar
Re: STM32: FDCAN stuck in INIT
« Reply #5 on: August 03, 2021, 06:47:53 pm »
Hi psysc0rpi0n,

i am using the microcontrollers C API. They call this "bare metal programming" nowadays. Then gcc and st-flash.

The reason for this are many: The microcontrollers software interface is well documented (here: 2000+ pages of real docs), most other APIs are not.
Even worse are these fancy IDEs like Cube. If you come back a few months or years later to your software to fix something tiny, with the C-API its just a "make" and everything works. With these IDEs and Wrappers you are in for hours of upgrades, then hours of porting to new revisions and days of debugging. If they are not already discontinued or "clouded". Then you can start from scratch.

Usually starting with the bare C API is a few hours more work at the beginn to look up all the details in the manual and transfer it to some C-macro. But for me this usually pays of quickly.
If you can find some usable example code, the starting effort can be verly small. Unfortunatelly stm provides almost no C-API examples (without this HAL wrapper).
 
Also i am somewhat lazy. So one of the first things i usually do with a new microcontroller, is connect a serial port. So i can use printf for debugging. Most of the time this works even inside ISRs.

Well, I can see for your code that you're using pretty much the registers themselves. That would also be daunting to me. ehehe. I don't have enough experience to just go that way. And I'm using libopenCM3 because it seems to be a pretty good built API and with continuous developing in github. Also, they have doxygen documentation that, although is not enough to make things easy, they help quite a lot. I don't want to use HAL API either, but I don't have enough judgement to say exactly why. I'm following a suggestion of someone that knows better libopenCM3 and says it's a better API than HAL drivers.

Still, I did some programming when I finished my graduation in an AtMega328, but STM32 chips are one whole new championship. I just hope to have enough motivation and energy to keep going because I don't want to just go the easy way and start using those fancy IDEs, because as you say, when one generates code with them, you have right from the start, tons of source and header files that probably are no needed for very basic things and if later I want to change something, probably I would come into the problems you mentioned!

Anyway, I hope you can figure out your problem a move on for the next task, as soon as possible! To be stuck for very long time in the same problem is frustrating! I can tell it!
 

Offline bittumblerTopic starter

  • Contributor
  • Posts: 37
  • Country: de
Re: STM32: FDCAN stuck in INIT
« Reply #6 on: August 04, 2021, 08:41:08 am »
ok, i found a solution.
I started removing stuff, until it finally left INIT.
Turns out, everything works, if i omit this reset statement (near top): RCC->APB1RSTR1 |= (RCC_APB1RSTR1_FDCANRST);
Not sure why.
The micro can now successfully talk to itself via FDCAN.
 

Offline Rudolph Riedel

  • Regular Contributor
  • *
  • Posts: 67
  • Country: de
Re: STM32: FDCAN stuck in INIT
« Reply #7 on: August 08, 2021, 08:28:58 am »
You are setting the INIT and CCE bits in one step, try this in two steps.

FDCAN1->CCCR    |=  (FDCAN_CCCR_INIT);
while (! ((FDCAN1->CCCR & FDCAN_CCCR_INIT) == FDCAN_CCCR_INIT)) { };  // wait for Init

FDCAN1->CCCR |=  (FDCAN_CCCR_CCE);
while ((FDCAN1->CCCR & FDCAN_CCCR_CCE) == 0);  // wait for cce
 

Offline bittumblerTopic starter

  • Contributor
  • Posts: 37
  • Country: de
Re: STM32: FDCAN stuck in INIT
« Reply #8 on: August 08, 2021, 09:42:17 am »
Hi Rudolph,

if i do a separate wait for CCE, it is stuck at this CCE wait.

However if i explicitly set the FDCANRST bit to 0 in my code, everything seems to work:

        RCC->APB1RSTR1  |=  (RCC_APB1RSTR1_FDCANRST); // reset FDCAN
        RCC->APB1RSTR1  &= ~(RCC_APB1RSTR1_FDCANRST); // reset FDCAN

So it seems the FDCANRST bit needs explicit clearing.

The description in the Reference Manual is a bit ambiguous:
On one side it says the bit is "set and reset by software". On the other side it says setting it to 0 has "no effect".

 
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8173
  • Country: fi
Re: STM32: FDCAN stuck in INIT
« Reply #9 on: August 08, 2021, 09:58:31 am »
The RCC reset register generates a reset signal when written to 1. You need to clear it back to zero manually, otherwise the reset signal stays active.

This is true with all peripherals. It's interesting you encounter this with FDCAN, surely you have seen all other peripherals being nonfunctional as well if you leave their reset signals on?

Why do you reset FDCAN through RCC at all? I have had to use RCC reset only once, when working around silicon-broken SPI implementation requiring resets during operation.

The power-on system reset takes care of resetting all the peripherals.

If you want to "deinit" everything to a known state, for example in a bootloader just before running the app, I suggest utilizing NVIC system reset instead as described by ataradov recently in another thread here.
« Last Edit: August 08, 2021, 10:00:52 am by Siwastaja »
 

Offline bittumblerTopic starter

  • Contributor
  • Posts: 37
  • Country: de
Re: STM32: FDCAN stuck in INIT
« Reply #10 on: August 08, 2021, 10:23:36 am »
Hi Siwastaja,

i did programming for some time, but am new to STM32. So the reset is "old habits".

At some point i might need to restart the software, be it a software controlled restart, or a Watchdog. Then everything should be put into a controlled state (e.g. reset). So my thinking was "do the reset anyway".

The manual just confused me, because it said writing a 0 has "no effect".

 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8173
  • Country: fi
Re: STM32: FDCAN stuck in INIT
« Reply #11 on: August 08, 2021, 10:28:35 am »
It's poor wording. Writing 0 has no effect when it's already at 0 by default. When you have written it to 1, then writing 0 is required.

Don't worry, NVIC_SystemReset(); resets all peripheral registers to the reset values described in the manual per each register. You really only need RCC resets in special cases, I don't recommend habitually doing it, although if done correctly, there should be no harm doing it.

Remember with FDCAN that you must manually clear the message RAM (after enabling the clock to FDCAN). It isn't filled with zeroes by default and the filters will malfunction unless you clear it.
« Last Edit: August 08, 2021, 10:30:35 am by Siwastaja »
 

Offline bittumblerTopic starter

  • Contributor
  • Posts: 37
  • Country: de
Re: STM32: FDCAN stuck in INIT
« Reply #12 on: August 08, 2021, 11:39:24 am »
Hi Siwastaja,

thanks for the tip about message RAM clearing.

Do you know if there exists a nice .h file from STM for the FDCAN message RAM elements?
This seems to be missing from the MCUs .h file.
I could not find one, so i did a partial on my own. However there is always the risk of typos etc. which sometimes result in hard to find problems.
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8173
  • Country: fi
Re: STM32: FDCAN stuck in INIT
« Reply #13 on: August 08, 2021, 12:28:21 pm »
I don't know, I did my own, not for all data types at once, but on as required basis. It wasn't a daunting task. Can't post the full code but I'm sure no one gets mad at me if I just give the few structs for cross reference so you can compare to your own implementation:

Code: [Select]
#define TO_SHORT_ID(x) ((x)<<18)
#define FROM_SHORT_ID(x) ((x)>>18)

typedef struct PACK
{
union
{

struct PACK
{
uint32_t id  : 29; // CAN ID. 11-bit ID goes into [28..18], use TO_SHORT_ID(0x123) macro for ID 0x123
uint32_t rtr : 1; // 1 = remote frame
uint32_t xtd : 1; // 1 = use 29-bit ID. 0 = use 11-bit ID
uint32_t esi : 1; // Error state indicator, no idea what this does

uint32_t reserved : 16;
uint32_t dlc : 4; // Data length 0-8. For CAN FD frames over 8 bytes, use macros DLC_
uint32_t brs : 1; // Set to 1 to switch to higher CAN FD baudrate
uint32_t fdf : 1; // Set to 1 to send CAN FD frame
uint32_t res : 1;
uint32_t efc : 1; // Set to 1 to store TX event
uint32_t mm  : 8; // Marker copied to TX event fifo, if enabled
};
uint64_t header_u64;
};

union
{
uint8_t u8[TX_BUF_ELEM_BYTES];
uint16_t u16[TX_BUF_ELEM_BYTES/2];
uint32_t u32[TX_BUF_ELEM_BYTES/4];
uint64_t u64[TX_BUF_ELEM_BYTES/8];
};
} can_tx_elem_t;

typedef struct PACK
{
union
{

struct PACK
{
uint32_t id  : 29; // CAN ID. 11-bit ID goes into [28..18], use FROM_SHORT_ID(0x123) macro for ID 0x123
uint32_t rtr : 1; // 1 = remote frame
uint32_t xtd : 1; // 1 = use 29-bit ID. 0 = use 11-bit ID
uint32_t esi : 1; // Error state indicator, no idea what this does

uint32_t rxts : 16;     // RX timestamp
uint32_t dlc  : 4; // Data length 0-8. For CAN FD frames over 8 bytes, use macros DLC_
uint32_t brs  : 1; // Received with CAN FD baud rate switching
uint32_t fdf  : 1; // Received as CAN FD frame
uint32_t res  : 1;
uint32_t fidx : 7; // Matching filter index
uint32_t amf  : 1; // If '1', didn't match any filters (but accepting non-matching is globally enabled). fidx is then invalid.
};
uint64_t header_u64;
};

union
{
uint8_t u8[RX_BUF_ELEM_BYTES];
uint16_t u16[RX_BUF_ELEM_BYTES/2];
uint32_t u32[RX_BUF_ELEM_BYTES/4];
uint64_t u64[RX_BUF_ELEM_BYTES/8];
};
} can_rx_elem_t;

#define FLT_TYPE_RANGE   0b00
#define FLT_TYPE_DUALOR  0b01
#define FLT_TYPE_MASKED  0b10
#define FLT_TYPE_OFF     0b11

#define DEST_OFF     0b00
#define DEST_FIFO0   0b01
#define DEST_FIFO1   0b10
#define DEST_REJECT  0b11

// Combining DEST_REJECT and prio='1' isn't supported, this combo results in a special mode in FDCAN we don't support now.
typedef struct PACK
{
uint32_t id2        : 11; // mask when FLT_TYPE_MASKED
uint32_t res        :  5;
uint32_t id1        : 11;
uint32_t dest       :  2;
uint32_t prio    :  1;  // if '1', HPM interrupt is generated
uint32_t flt_type   :  2;
} flt11b_elem_t;


// Combining DEST_REJECT and prio='1' isn't supported, this combo results in a special mode in FDCAN we don't support now.
typedef struct PACK
{
uint32_t id1        : 29;
uint32_t dest       :  2;
uint32_t prio    :  1;  // if '1', HPM interrupt is generated

uint32_t id2        : 29;
uint32_t res        :  1;
uint32_t flt_type   :  2;
} flt29b_elem_t;


I had some weird behavior when trying read-modify-write operations on message RAM, or smaller than 32-bit accesses. I didn't look at it further, just transitioned to doing full 32- or 64-bit self-aligned read/write accesses.
 
The following users thanked this post: thm_w


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf