Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5e217cc
Initial draft, not yet tested
grisharevzin Sep 12, 2024
0d2bc4d
Doc updates
grisharevzin Sep 12, 2024
eac645b
Import fixes
grisharevzin Sep 15, 2024
560ab4d
Minor WIP changes
grisharevzin Sep 22, 2024
5a78e09
Compiler warnings
grisharevzin Sep 26, 2024
c3a21cb
aligned #ifdef -> #if CANARD_ENABLE_CANFD
grisharevzin Nov 8, 2024
af119d1
WIP: hardware works (messages are sent/received), cleaning code
grisharevzin Nov 10, 2024
2198a83
Debug & cleanup; filters, transmission, reception are working
grisharevzin Nov 10, 2024
a9d701a
Fixed put_index calculation in transmit()
grisharevzin Nov 11, 2024
e82eb76
silenced unused parameter warning for non-FD mode
grisharevzin Nov 11, 2024
4c1c14a
Fixed acknowledging received frames
grisharevzin Nov 11, 2024
f08f41c
tabs -> spaces
grisharevzin Nov 11, 2024
989e47e
Added canard_stm32g4fdcan_get_statistics()
grisharevzin Nov 12, 2024
79a9934
Merge branch 'master' into add-stm32g4-fdcan-driver
grisharevzin Nov 12, 2024
35c70b1
Fixed FD frame reception
grisharevzin Jan 1, 2025
2982c7b
Set TDC = 0 for BRS
grisharevzin Jan 1, 2025
71c5040
Added wipe_filters()
grisharevzin Feb 24, 2025
c3f7fd5
Fixed enable_automatic_retransmission
grisharevzin Apr 11, 2025
cebebe5
CR, minor
grisharevzin Mar 3, 2026
748b9f1
Fixed incorrect ECR values
grisharevzin Mar 3, 2026
e1aeadb
Fixed incorrect IP offset calculation
grisharevzin Mar 3, 2026
c3a502c
CR, license notes
grisharevzin Mar 4, 2026
6c86ab1
CR, minor
grisharevzin Mar 4, 2026
2d48474
extern 'C'
grisharevzin Mar 4, 2026
2e696cb
CR, minor
grisharevzin Mar 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions drivers/stm32g4_fdcan/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# STM32G4 FDCAN driver for libcanard/DroneCAN

This is a bare-bones driver for the variety of FDCAN that's found in STM32G4 series.

* TX FIFO is used in queue mode
* Incoming messages are prioritized based on filters (see below).
* Multiple instances are supported
* No dependencies, no interrupts (unless you want them)
* Specific for DroneCAN.

FDCANs are slightly different between STM32G4, STM32H7 (this has a very configurable SRAM) and STM32G0.
So this is only for G4, but can probably be extended to support everything.

## General usage

### Initialization

1. Store a `canard_stm32g4_fdcan_driver` somewhere and set the `fdcan` pointer
to one of `FDCAN1_ADDR`/`FDCAN2_ADDR`/`FDCAN3_ADDR`, then
call `canard_stm32g4fdcan_init`.

2. Then configure filters by calling `canard_stm32g4fdcan_type_id_filter` as many times as needed (8 double
rules can be created all in all).

3. Call `canard_stm32g4fdcan_start`.

4. After a Node ID has been assigned (or if you know it beforehand), call `canard_stm32g4fdcan_enable_automatic_retransmission`.

It's the user's responsibility to provide clock to the chosen FDCAN peripheral and configure I/O pins.

### Reception

The driver sets up two RX FIFOs, FIFO0 for "more important" and FIFO1 for "less important" messages.

Three things can happen to a message on reception (see `canard_stm32g4fdcan_type_id_filter`):
1. The DroneCAN data type ID is in the "accept" filter (`accept_not_reject=1`) -> goes to FIFO0
2. The DroneCAN data type ID is in the "reject" filter (`accept_not_reject=0`) -> discarded
3. The DroneCAN data type ID is in neither -> goes to FIFO1

`canard_stm32g4fdcan_receive` reads one message at a time and prefers FIFO0.

For instance, a ESC
1. would prioritize `1031.RawCommand` above anything else - so this ID should be accepted.
2. would reject spammy `1034.Status` messages from other ESCs - so this ID should be rejected.

The filter functions are used by DroneCAN data type IDs, not CAN frame IDs.

Note that all service requests will automatically go to FIFO1.

## Config

If `CANARD_ENABLE_CANFD` is defined, FDCAN will be configured in long frame mode + bitrate switch mode.
189 changes: 189 additions & 0 deletions drivers/stm32g4_fdcan/_fdcan_g4.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* Copyright (c) 2024-2026 Flytrex, by Grisha Revzin
*
* Distributed under the MIT License, available in the file LICENSE.
*
* Created on: Sep 10, 2024
*/

#ifndef INTERNAL_FDCAN_H
#define INTERNAL_FDCAN_H

#include <stdint.h>

/* ------------------- SRAM layout --------------------------------------------------------------------------------- */

typedef struct __attribute__((packed, aligned(4))) {
volatile uint32_t CREL;
volatile uint32_t ENDN;
volatile uint32_t RESERVED1;
volatile uint32_t DBTP;
volatile uint32_t TEST;
volatile uint32_t RWD;
volatile uint32_t CCCR;
volatile uint32_t NBTP;
volatile uint32_t TSCC;
volatile uint32_t TSCV;
volatile uint32_t TOCC;
volatile uint32_t TOCV;
volatile uint32_t RESERVED2[4];
volatile uint32_t ECR;
volatile uint32_t PSR;
volatile uint32_t TDCR;
volatile uint32_t RESERVED3;
volatile uint32_t IR;
volatile uint32_t IE;
volatile uint32_t ILS;
volatile uint32_t ILE;
volatile uint32_t RESERVED4[8];
volatile uint32_t RXGFC;
volatile uint32_t XIDAM;
volatile uint32_t HPMS;
volatile uint32_t RESERVED5;
volatile uint32_t RXF0S;
volatile uint32_t RXF0A;
volatile uint32_t RXF1S;
volatile uint32_t RXF1A;
volatile uint32_t RESERVED6[8];
volatile uint32_t TXBC;
volatile uint32_t TXFQS;
volatile uint32_t TXBRP;
volatile uint32_t TXBAR;
volatile uint32_t TXBCR;
volatile uint32_t TXBTO;
volatile uint32_t TXBCF;
volatile uint32_t TXBTIE;
volatile uint32_t TXBCIE;
volatile uint32_t TXEFS;
volatile uint32_t TXEFA;
} fdcan_registers;

/* Helper to iterate over RXFxS/A */
typedef struct __attribute__((packed, aligned(4))) {
volatile uint32_t RXFxS;
volatile uint32_t RXFxA;
} fdcan_rxfifo_regs;

/* ------------------- SRAM layout --------------------------------------------------------------------------------- */
/* Note: bitfields only to get a register-like view in GDB. Don't use them directly, it explodes. */

typedef struct __attribute__((packed, aligned(4))) {
union {
uint32_t t0;
struct {
uint32_t id : 29;
uint32_t rtr : 1; /* remote frame */
uint32_t xtd : 1; /* extended frame */
uint32_t esi : 1; /* Force error passive flag */
};
};

union {
uint32_t t1;
struct {
uint32_t reserved0 : 16;
uint32_t dlc : 4; /* DLC */
uint32_t brs : 1; /* Bit Rate Switching Frame */
uint32_t fdf : 1; /* CAN FD frame */
uint32_t reserved1 : 1;
uint32_t efc : 1; /* store tx events */
uint32_t mm : 8; /* message marker (tx events) */
};
};
uint32_t data[64 / sizeof(uint32_t)];
} fdcan_tx_buf_element;

typedef struct __attribute__((packed, aligned(4))) {
union {
uint32_t r0;
struct {
uint32_t id : 29;
uint32_t rtr : 1; /* remote */
uint32_t xtd : 1; /* extended */
uint32_t esi : 1; /* transmitter error passive */
};
};

union {
uint32_t r1;
struct {
uint32_t rxts : 16;/* timestamp */
uint32_t dlc : 4;
uint32_t brs : 1; /* Bit Rate Switching Frame */
uint32_t fdf : 1; /* CAN FD frame */
uint32_t reserved0 : 1;
uint32_t fidx : 7; /* filter index */
uint32_t anmf : 1; /* no filter matched */
};
};
uint32_t data[64 / sizeof(uint32_t)];
} fdcan_rx_buf_element;

typedef struct __attribute__((packed, aligned(4))) {
/* not detailed, standard IDs not used for DroneCAN */
uint32_t stub;
} fdcan_stdid_filter_element;

typedef struct __attribute__((packed, aligned(4))) {
/* not detailed, tx events are not used */
uint32_t stub[2];
} fdcan_tx_event_element;

typedef struct __attribute__((packed, aligned(4))) {
union {
uint32_t f0;
struct {
uint32_t efid1 : 29;
enum {
FILT_ELEM_EFEC_DISABLE,
FILT_ELEM_EFEC_STORE_RXFIFO0,
FILT_ELEM_EFEC_STORE_RXFIFO1,
FILT_ELEM_EFEC_REJECT,
FILT_ELEM_EFEC_SET_PRIORITY_STORE_RXFIFO0,
FILT_ELEM_EFEC_SET_PRIORITY_STORE_RXFIFO1,
FILT_ELEM_EFEC_RESERVED,
} efec : 3;
};
};
union {
uint32_t f1;
struct {
uint32_t efid2 : 29;
uint32_t reserved0 : 1;
enum {
FILT_ELEM_EFT_RANGE,
FILT_ELEM_EFT_DUAL_ID,
FILT_ELEM_EFT_ID_MASK,
FILT_ELEM_EFT_RANGE_NO_XIDAM
} eft : 2;
};
};
} fdcan_extid_filter_element;

/* Figure 669 RM0440 */
#define FDCAN_NUM_STDID_FE 28
#define FDCAN_NUM_EXTID_FE 8
#define FDCAN_NUM_RXFIFO_ELEMENTS 3
#define FDCAN_NUM_TX_EVENT_FIFOS 3
#define FDCAN_NUM_TX_BUF 3

typedef struct __attribute__((packed, aligned(4))) {
fdcan_stdid_filter_element stdid_filter_element[FDCAN_NUM_STDID_FE]; // 28 // 28
fdcan_extid_filter_element extid_filter_element[FDCAN_NUM_EXTID_FE]; // 16 // 44
fdcan_rx_buf_element rxfifo0[FDCAN_NUM_RXFIFO_ELEMENTS]; // 54 // 98
fdcan_rx_buf_element rxfifo1[FDCAN_NUM_RXFIFO_ELEMENTS]; // 54 // 152
fdcan_tx_event_element txevent_fifo[FDCAN_NUM_TX_EVENT_FIFOS]; // 6 // 158
fdcan_tx_buf_element txbuf[FDCAN_NUM_TX_BUF]; // 54 // 212 == 0x350
} fdcan_sram;

_Static_assert(sizeof(fdcan_sram) == 0x350, "fdcan_sram size must be 0x350");

#define FDCAN1 ((fdcan_registers *) FDCAN1_ADDR)
#define FDCAN2 ((fdcan_registers *) FDCAN2_ADDR)
#define FDCAN3 ((fdcan_registers *) FDCAN3_ADDR)

#define IP_OFFSET (FDCAN2_ADDR - FDCAN1_ADDR)

#define SRAMCAN_START 0x4000A400U

#endif
Loading
Loading