Conversation
| while (!(fdcan->CCCR & (1 << 0))); /* Wait until init mode sets */ | ||
| fdcan->CCCR |= (1 << 1); /* CCE=1: config change enable */ | ||
| while (!(fdcan->CCCR & (1 << 1))); /* Wait intil CCE sets */ | ||
| fdcan->CCCR &= ~(1 << 6); // DAR = 0 |
There was a problem hiding this comment.
/* comments */
There was a problem hiding this comment.
Pull request overview
Adds a new bare-metal STM32G4 FDCAN driver integration for libcanard/DroneCAN, including register/message-RAM definitions and usage documentation.
Changes:
- Introduces a new STM32G4 FDCAN driver API (
init/start/transmit/receive/filtering/statistics). - Adds an internal header describing the STM32G4 FDCAN register map and SRAM layout.
- Adds a README documenting intended usage and filter behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 15 comments.
| File | Description |
|---|---|
| drivers/stm32g4_fdcan/canard_stm32g4_fdcan.h | Public driver API/types and constants. |
| drivers/stm32g4_fdcan/canard_stm32g4_fdcan.c | Driver implementation (init, filtering, TX/RX, stats, protocol state). |
| drivers/stm32g4_fdcan/_fdcan_g4.h | Internal register/message RAM layout definitions for STM32G4 FDCAN. |
| drivers/stm32g4_fdcan/README.md | Usage and configuration documentation for the driver. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 14 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| uint32_t tx_frames; | ||
| uint32_t rx_frames; | ||
| } statistics; | ||
| uint32_t fdcan_sram_base; |
There was a problem hiding this comment.
fdcan_sram_base is declared in the public driver struct but is never initialized or used anywhere in this driver. If callers are not expected to set/use it, consider removing it to avoid confusion; otherwise initialize it in canard_stm32g4fdcan_init() and document its meaning.
| uint32_t fdcan_sram_base; |
| * Created on: Sep 10, 2024 | ||
| * Author: grishar |
There was a problem hiding this comment.
The new source file header comment lacks the standard project copyright/license boilerplate present in existing drivers (MIT license notice). Add the repository’s usual header to clarify licensing and keep driver files consistent.
| * Created on: Sep 10, 2024 | |
| * Author: grishar | |
| * Copyright (c) 2024 grishar | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| * of this software and associated documentation files (the "Software"), to deal | |
| * in the Software without restriction, including without limitation the rights | |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| * copies of the Software, and to permit persons to whom the Software is | |
| * furnished to do so, subject to the following conditions: | |
| * | |
| * The above copyright notice and this permission notice shall be included in all | |
| * copies or substantial portions of the Software. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| * SOFTWARE. | |
| * | |
| * Created on: Sep 10, 2024 | |
| * Author: grishar |
| uint32_t *tx_data_frame = (uint32_t *) frame->data; | ||
| ele->data[0] = tx_data_frame[0]; | ||
| ele->data[1] = tx_data_frame[1]; | ||
| #if CANARD_ENABLE_CANFD | ||
| for (size_t i = 2; i < frame->data_len / sizeof(uint32_t); ++i) { | ||
| ele->data[i] = tx_data_frame[i]; | ||
| } | ||
| #endif |
There was a problem hiding this comment.
frame->data is a uint8_t[] (not guaranteed 4-byte aligned). Casting it to uint32_t* can cause unaligned loads and hard faults on Cortex-M, and also violates strict-aliasing assumptions. Use byte-wise packing (like drivers/stm32/canard_stm32.c) or memcpy into the TX buffer element instead of uint32_t* casts.
| uint32_t *tx_data_frame = (uint32_t *) frame->data; | |
| ele->data[0] = tx_data_frame[0]; | |
| ele->data[1] = tx_data_frame[1]; | |
| #if CANARD_ENABLE_CANFD | |
| for (size_t i = 2; i < frame->data_len / sizeof(uint32_t); ++i) { | |
| ele->data[i] = tx_data_frame[i]; | |
| } | |
| #endif | |
| /* Clear the data area and copy the frame payload safely without | |
| * relying on potentially unaligned uint32_t accesses. */ | |
| memset(ele->data, 0, sizeof(ele->data)); | |
| memcpy(ele->data, frame->data, frame->data_len); |
| fdcan->CCCR &= ~(1 << 4); /* Exit from sleep mode */ | ||
| while (fdcan->CCCR & (1 << 3)); /* Wait until exited from sleep mode */ | ||
| fdcan->CCCR |= (1 << 0); /* Get to INIT mode */ | ||
| while (!(fdcan->CCCR & (1 << 0))); /* Wait until init mode sets */ | ||
| fdcan->CCCR |= (1 << 1); /* CCE=1: config change enable */ | ||
| while (!(fdcan->CCCR & (1 << 1))); /* Wait intil CCE sets */ | ||
|
|
There was a problem hiding this comment.
The init sequence uses multiple tight while (...) ; waits for mode transitions (sleep exit / INIT / CCE) with no timeout or error path. If the peripheral clock is not enabled or the controller is wedged, this will hang forever. Consider using bounded waits and returning a driver-specific error (similar to drivers/stm32/canard_stm32.c which times out waiting for MSR INAK).
| fdcan->CCCR &= ~(1 << 4); /* Exit from sleep mode */ | |
| while (fdcan->CCCR & (1 << 3)); /* Wait until exited from sleep mode */ | |
| fdcan->CCCR |= (1 << 0); /* Get to INIT mode */ | |
| while (!(fdcan->CCCR & (1 << 0))); /* Wait until init mode sets */ | |
| fdcan->CCCR |= (1 << 1); /* CCE=1: config change enable */ | |
| while (!(fdcan->CCCR & (1 << 1))); /* Wait intil CCE sets */ | |
| { | |
| /* Use bounded waits to avoid hanging forever if the controller or clock is misconfigured. */ | |
| const uint32_t max_wait_cycles = 1000000U; | |
| uint32_t wait_cycles; | |
| fdcan->CCCR &= ~(1 << 4); /* Exit from sleep mode */ | |
| wait_cycles = max_wait_cycles; | |
| while ((fdcan->CCCR & (1 << 3)) && (--wait_cycles != 0U)) { | |
| /* wait until exited from sleep mode or timeout */ | |
| } | |
| if (wait_cycles == 0U) { | |
| return -CANARD_ERROR_INTERNAL; | |
| } | |
| fdcan->CCCR |= (1 << 0); /* Get to INIT mode */ | |
| wait_cycles = max_wait_cycles; | |
| while (!(fdcan->CCCR & (1 << 0)) && (--wait_cycles != 0U)) { | |
| /* wait until init mode sets or timeout */ | |
| } | |
| if (wait_cycles == 0U) { | |
| return -CANARD_ERROR_INTERNAL; | |
| } | |
| fdcan->CCCR |= (1 << 1); /* CCE=1: config change enable */ | |
| wait_cycles = max_wait_cycles; | |
| while (!(fdcan->CCCR & (1 << 1)) && (--wait_cycles != 0U)) { | |
| /* wait until CCE sets or timeout */ | |
| } | |
| if (wait_cycles == 0U) { | |
| return -CANARD_ERROR_INTERNAL; | |
| } | |
| } |
| void canard_stm32g4fdcan_start(canard_stm32g4_fdcan_driver *driver) | ||
| { | ||
| fdcan_registers *fdcan = driver->fdcan; | ||
| fdcan->CCCR &= ~((1 << 0) | (1 << 1)); /* Clear INIT and CCE */ | ||
| while ((fdcan->CCCR & (1 << 0))); /* Wait until we leave init mode */ | ||
| } |
There was a problem hiding this comment.
canard_stm32g4fdcan_start() waits for INIT to clear using a tight while loop with no timeout. If the controller fails to leave init mode, this will hang the caller. Add a bounded wait and surface an error (or at least an assert in debug builds) to avoid deadlocks.
| fdcan->CCCR |= (1 << 0); /* Get to INIT mode */ | ||
| while (!(fdcan->CCCR & (1 << 0))); /* Wait until init mode sets */ | ||
| fdcan->CCCR |= (1 << 1); /* CCE=1: config change enable */ | ||
| while (!(fdcan->CCCR & (1 << 1))); /* Wait intil CCE sets */ | ||
| fdcan->CCCR &= ~(1 << 6); /* DAR = 0 */ | ||
| fdcan->CCCR &= ~((1 << 0) | (1 << 1)); /* Clear INIT and CCE */ | ||
| while ((fdcan->CCCR & (1 << 0))); /* Wait until we leave init mode */ |
There was a problem hiding this comment.
canard_stm32g4fdcan_enable_automatic_retransmission() re-enters INIT/CCE and busy-waits on CCCR bits without any timeout. A misclocked/failed peripheral will hang the system here. Prefer a bounded wait + error return (or make this function return int so failures can be reported).
| fdcan->CCCR |= (1 << 0); /* Get to INIT mode */ | |
| while (!(fdcan->CCCR & (1 << 0))); /* Wait until init mode sets */ | |
| fdcan->CCCR |= (1 << 1); /* CCE=1: config change enable */ | |
| while (!(fdcan->CCCR & (1 << 1))); /* Wait intil CCE sets */ | |
| fdcan->CCCR &= ~(1 << 6); /* DAR = 0 */ | |
| fdcan->CCCR &= ~((1 << 0) | (1 << 1)); /* Clear INIT and CCE */ | |
| while ((fdcan->CCCR & (1 << 0))); /* Wait until we leave init mode */ | |
| const uint32_t timeout = 1000000U; | |
| uint32_t counter; | |
| fdcan->CCCR |= (1 << 0); /* Get to INIT mode */ | |
| counter = 0U; | |
| while (!(fdcan->CCCR & (1 << 0)) && (counter++ < timeout)) { | |
| /* Wait until init mode sets, but with a bounded timeout */ | |
| } | |
| if (!(fdcan->CCCR & (1 << 0))) { | |
| /* Failed to enter INIT mode within timeout */ | |
| return; | |
| } | |
| fdcan->CCCR |= (1 << 1); /* CCE=1: config change enable */ | |
| counter = 0U; | |
| while (!(fdcan->CCCR & (1 << 1)) && (counter++ < timeout)) { | |
| /* Wait until CCE sets, but with a bounded timeout */ | |
| } | |
| if (!(fdcan->CCCR & (1 << 1))) { | |
| /* Failed to set CCE within timeout */ | |
| return; | |
| } | |
| fdcan->CCCR &= ~(1 << 6); /* DAR = 0 */ | |
| fdcan->CCCR &= ~((1 << 0) | (1 << 1)); /* Clear INIT and CCE */ | |
| counter = 0U; | |
| while ((fdcan->CCCR & (1 << 0)) && (counter++ < timeout)) { | |
| /* Wait until we leave init mode, but with a bounded timeout */ | |
| } | |
| /* If INIT bit is still set here, we simply return; no further action possible */ |
No description provided.