STM32: FDCAN stuck in INIT - Page 1
Forum section: electronics / general

author: bittumbler
author_rank: Contributor
timestamp: August 03, 2021, 01:34:43 pm
content: 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:void FDCANsetup (void) {// configure PA11, PA12 for AF9 FDCAN1 - PA11 Pin21 is FDCAN1_RX, PA12 Pin22 is FDCAN1_TXRCC->AHB2ENR |= (RCC_AHB2ENR_GPIOAEN);RCC->CCIPR &= ~( RCC_CCIPR_FDCANSEL); // HSE clock 8MhzRCC->APB1ENR1 |= (RCC_APB1ENR1_FDCANEN); // turn on FDCANRCC->APB1SMENR1 |= (RCC_APB1SMENR1_FDCANSMEN); // clock enabled during sleepRCC->APB1RSTR1 |= (RCC_APB1RSTR1_FDCANRST); // reset FDCANGPIOA->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 &= ~( GPIO_AFRH_AFSEL11 | GPIO_AFRH_AFSEL12);GPIOA->AFR |= ( ( 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 InitFDCAN1->CCCR |= (FDCAN_CCCR_TEST); // enable loopback modeFDCAN1->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 handlingFDCAN1->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 clockFDCAN1->DBTP |= (FDCAN_DBTP_TDC); // transmitter delay compensationFDCAN1->DBTP |= (0x1 << FDCAN_DBTP_DBRP_Pos); // 8Mhz/2 -> 4Mhz quanta// DBTP_DTSEG1 default 0xA// DBTP_DTSEG2 default 0x3// DBTP_DSJW default 0x3FDCAN1->NBTP |= (0x1 << FDCAN_NBTP_NBRP_Pos); // 8Mhz/2 -> 4Mhz quanta// NBTP_NSJW default// NBTP_NTSEG1 default// NBTP_NTSEG2 defaultFDCAN1->TSCC |= (0x0 << FDCAN_TSCC_TCP_Pos); // Prescaler = 1 FDCAN_TSCC_TCP// FDCAN1->TSCC |= (0x10 << FDCAN_TSCC_TSS_Pos); // for FDCANFDCAN1->CCCR &= ~(FDCAN_CCCR_CCE);FDCAN1->CCCR &= ~(FDCAN_CCCR_INIT); // enable FDCANdbg_printf("FDCAN setup2 \n\r");while (((FDCAN1->CCCR & FDCAN_CCCR_INIT) == FDCAN_CCCR_INIT)) { }; // wait for Init// never reaches here CCCR stays at 0x00000001dbg_printf("FDCAN setup3 \n\r");// setup an extended acceptance filter to RxFIFO0 for ID 0x4711 0x4712FDCAN1_RAM->EFE.F0 |= ((0x1 << FDCAN_EFE0_EFEC_Pos) | (0x4711 << FDCAN_EFE0_EFID1_Pos ));FDCAN1_RAM->EFE.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 framesFDCAN1->IE |= (FDCAN_IE_RF0NE); // interrupt for new message in Rx FIFO0// FDCAN1->ILS &= ~(FDCAN_ILS_RxFIFO0);FDCAN1->ILE |= (FDCAN_ILE_EINT0); // enable interrupt 0NVIC_EnableIRQ(FDCAN1_IT0_IRQn); // enable interruptdbg_printf("FDCAN Release: 0x%8.8x \n\r",FDCAN1->CREL);}

author: psysc0rpi0n
author_rank: Frequent Contributor
timestamp: August 03, 2021, 03:19:21 pm
content: 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

author: bittumbler
author_rank: Contributor
timestamp: August 03, 2021, 04:08:27 pm
content: 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.

author: capt bullshot
author_rank: Super Contributor
timestamp: August 03, 2021, 05:55:28 pm
content: 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.

author: bittumbler
author_rank: Contributor
timestamp: August 03, 2021, 06:13:56 pm
content: 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.

author: psysc0rpi0n
author_rank: Frequent Contributor
timestamp: August 03, 2021, 06:47:53 pm
content: Quote from: bittumbler on August 03, 2021, 04:08:27 pmWell, 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!

author: bittumbler
author_rank: Contributor
timestamp: August 04, 2021, 08:41:08 am
content: 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.

author: Rudolph Riedel
author_rank: Regular Contributor
timestamp: August 08, 2021, 08:28:58 am
content: 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 InitFDCAN1->CCCR |= (FDCAN_CCCR_CCE);while ((FDCAN1->CCCR & FDCAN_CCCR_CCE) == 0); // wait for cce

author: bittumbler
author_rank: Contributor
timestamp: August 08, 2021, 09:42:17 am
content: 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 FDCANRCC->APB1RSTR1 &= ~(RCC_APB1RSTR1_FDCANRST); // reset FDCANSo 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".

author: Siwastaja
author_rank: Super Contributor
timestamp: August 08, 2021, 09:58:31 am
content: 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.

author: bittumbler
author_rank: Contributor
timestamp: August 08, 2021, 10:23:36 am
content: 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".

author: Siwastaja
author_rank: Super Contributor
timestamp: August 08, 2021, 10:28:35 am
content: 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 (afterenabling the clock to FDCAN). It isn't filled with zeroes by default and the filters will malfunction unless you clear it.

author: bittumbler
author_rank: Contributor
timestamp: August 08, 2021, 11:39:24 am
content: 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.

author: Siwastaja
author_rank: Super Contributor
timestamp: August 08, 2021, 12:28:21 pm
content: 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:#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 0x123uint32_t rtr : 1;// 1 = remote frameuint32_t xtd : 1;// 1 = use 29-bit ID. 0 = use 11-bit IDuint32_t esi : 1;// Error state indicator, no idea what this doesuint32_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 baudrateuint32_t fdf : 1;// Set to 1 to send CAN FD frameuint32_t res : 1;uint32_t efc : 1;// Set to 1 to store TX eventuint32_t mm : 8;// Marker copied to TX event fifo, if enabled};uint64_t header_u64;};union{uint8_t u8;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 0x123uint32_t rtr : 1;// 1 = remote frameuint32_t xtd : 1;// 1 = use 29-bit ID. 0 = use 11-bit IDuint32_t esi : 1;// Error state indicator, no idea what this doesuint32_t rxts : 16; // RX timestampuint32_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 switchinguint32_t fdf : 1;// Received as CAN FD frameuint32_t res : 1;uint32_t fidx : 7;// Matching filter indexuint32_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;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_MASKEDuint32_t res : 5;uint32_t id1 : 11;uint32_t dest : 2;uint32_t prio : 1; // if '1', HPM interrupt is generateduint32_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 generateduint32_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.