From 717492fdec31e4a954b40a1a3fba6cd2dc7a76b5 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 06:54:36 -0700 Subject: [PATCH 01/15] Rename `CAN_Identifier` to `CubeCAN_Identifier` Signed-off-by: Daniel Hansen --- Lib/Peripherals/CubeCAN/Inc/CubeCAN.h | 23 +---------------------- Lib/Peripherals/CubeCAN/Src/can_rx.c | 2 +- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h b/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h index 0f609862b..d845cb9ce 100644 --- a/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h +++ b/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h @@ -39,7 +39,7 @@ typedef union { GRCAN_NODE_ID tx_node_id : 8; uint32_t _ : 4; }; -} CAN_Identifier; +} CubeCAN_Identifier; /** * @brief Union for user-defined context data in CubeCAN configuration. @@ -124,25 +124,4 @@ void CubeCAN_Tick(void); */ HAL_StatusTypeDef CubeCAN_Send(CubeCAN_Handle *const handle, const GRCAN_NODE_ID rx_node, const GRCAN_MSG_ID msg_id, const void *const data, const uint8_t size); -/** - * @brief Constructs a CAN message identifier from the given transmitting node ID, receiving node ID, and message ID. - * - * @param identifier Pointer to the CAN_Identifier structure containing the node and message IDs. - * - * @return The constructed 29-bit CAN message extended identifier. - */ -uint32_t CubeCAN_Construct_Identifier(const CAN_Identifier *const identifier); - -/** - * @brief Deconstructs a 29-bit CAN message extended identifier into its constituent transmitting node ID, receiving node ID, and message ID. - * - * @param message_id The 29-bit CAN message extended identifier to be deconstructed. - * - * @return A CAN_Identifier structure containing the deconstructed node and message IDs. - * - * @warning The function does not guarantee that the returned structure will represent a valid CAN message identifier. - * @warning The function does not support custom IDs. - */ -CAN_Identifier CubeCAN_Deconstruct_Identifier(const uint32_t message_id); - #endif diff --git a/Lib/Peripherals/CubeCAN/Src/can_rx.c b/Lib/Peripherals/CubeCAN/Src/can_rx.c index 2e345b0a6..d752644c7 100644 --- a/Lib/Peripherals/CubeCAN/Src/can_rx.c +++ b/Lib/Peripherals/CubeCAN/Src/can_rx.c @@ -51,7 +51,7 @@ void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) while (HAL_FDCAN_GetRxFifoFillLevel(hfdcan, FDCAN_RX_FIFO0) > 0U) { if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &rx_header, rx_data) == HAL_OK) { - CAN_Identifier id = {.raw_id = rx_header.Identifier}; + CubeCAN_Identifier id = {.raw_id = rx_header.Identifier}; uint8_t size = CubeCAN_Private_DlcToBytes(rx_header.DataLength); rx_callback(&context, &id, rx_data, size); } else { From 47e0ea34e29b332ffc9bdd6659c0d4e42783c8c1 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 06:55:11 -0700 Subject: [PATCH 02/15] Support sending `GRCAN_CUSTOM_ID` identifiers Signed-off-by: Daniel Hansen --- Lib/Peripherals/CubeCAN/Inc/CubeCAN.h | 13 ++++++++++++- .../CubeCAN/Src/PrivateInc/internal.h | 11 +++++++++++ Lib/Peripherals/CubeCAN/Src/can_tx.c | 19 +++++++++++++++---- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h b/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h index d845cb9ce..79fccf710 100644 --- a/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h +++ b/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h @@ -64,7 +64,7 @@ typedef union { * @warning The callback function should not call any CubeCAN functions, as it may lead to undefined behavior. * @warning It is the responsibility of the callback function to verify the integrity of the received data and handle any errors or null inputs. */ -typedef void (*CubeCAN_RxCallback)(const CubeCAN_Config_Context *const context, const CAN_Identifier *const identifier, const uint8_t *const data, const uint8_t size); +typedef void (*CubeCAN_RxCallback)(const CubeCAN_Config_Context *const context, const CubeCAN_Identifier *const identifier, const uint8_t *const data, const uint8_t size); /** * @brief Configuration structure for CubeCAN CAN. @@ -112,6 +112,17 @@ HAL_StatusTypeDef CubeCAN_Exit(CubeCAN_Handle *handle); */ void CubeCAN_Tick(void); +/** + * @brief Sends a CAN message using the CubeCAN CAN handle, with the specified custom identifier, data payload, and size. + * @param handle Pointer to the CubeCAN CAN handle. + * @param custom_id The custom identifier for the message. + * @param data Pointer to the data payload of the message. + * @param size The size of the data payload in bytes. + * @return HAL_StatusTypeDef indicating the success or failure of the operation. + * @note The size of the data payload must not exceed FDCAN_MAX_DATA_BYTES (64 bytes). If the size exceeds this limit, the function will return HAL_ERROR. + */ +HAL_StatusTypeDef CubeCAN_Send_Custom(CubeCAN_Handle *const handle, const GRCAN_CUSTOM_ID custom_id, const void *const data, const uint8_t size); + /** * @brief Sends a CAN message using the CubeCAN CAN handle, with the specified receive node, message ID, data payload, and size. * @param handle Pointer to the CubeCAN CAN handle. diff --git a/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h b/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h index 2579c6185..3cfe8e636 100644 --- a/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h +++ b/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h @@ -193,4 +193,15 @@ HAL_StatusTypeDef CubeCAN_Private_Stop(CubeCAN_Handle *const handle); */ HAL_StatusTypeDef CubeCAN_Private_Release(CubeCAN_Handle *const handle); +/** + * @brief Sends a CAN message using the CubeCAN CAN handle, with the specified identifier, data payload, and size. + * @param handle Pointer to the CubeCAN CAN handle. + * @param id The identifier for the message. + * @param data Pointer to the data payload of the message. + * @param size The size of the data payload in bytes. + * @return HAL_StatusTypeDef indicating the success or failure of the operation. + * @note The size of the data payload must not exceed FDCAN_MAX_DATA_BYTES (64 bytes). If the size exceeds this limit, the function will return HAL_ERROR. + */ +HAL_StatusTypeDef CubeCAN_Private_Send(CubeCAN_Handle *const handle, const CubeCAN_Identifier id, const void *const data, const uint8_t size); + #endif diff --git a/Lib/Peripherals/CubeCAN/Src/can_tx.c b/Lib/Peripherals/CubeCAN/Src/can_tx.c index b72ea2a0d..c49be4525 100644 --- a/Lib/Peripherals/CubeCAN/Src/can_tx.c +++ b/Lib/Peripherals/CubeCAN/Src/can_tx.c @@ -8,6 +8,7 @@ #include "CubeCAN_Config.h" #include "Logomatic.h" #include "PrivateInc/internal.h" +#include "GRCAN_CUSTOM_ID.h" #include "main.h" void CubeCAN_Private_RateChecker(void) @@ -71,7 +72,7 @@ void CubeCAN_Tick(void) } } -HAL_StatusTypeDef CubeCAN_Send(CubeCAN_Handle *const handle, const GRCAN_NODE_ID rx_node, const GRCAN_MSG_ID msg_id, const void *const data, const uint8_t size) +HAL_StatusTypeDef CubeCAN_Private_Send(CubeCAN_Handle *const handle, const CubeCAN_Identifier id, const void *const data, const uint8_t size) { if (handle == NULL || data == NULL || size > FDCAN_MAX_DATA_BYTES) { return HAL_ERROR; @@ -103,13 +104,11 @@ HAL_StatusTypeDef CubeCAN_Send(CubeCAN_Handle *const handle, const GRCAN_NODE_ID return HAL_ERROR; } - const CAN_Identifier identifier_struct = {.tx_node_id = handle->config.sending_node_id, .rx_node_id = rx_node, .msg_id = msg_id}; - const FDCAN_TxHeaderTypeDef header = {.BitRateSwitch = brs, .DataLength = dlc, .ErrorStateIndicator = FDCAN_ESI_ACTIVE, .FDFormat = fdformat, - .Identifier = identifier_struct.raw_id, + .Identifier = id.raw_id, .IdType = FDCAN_EXTENDED_ID, .MessageMarker = 0U, // TODO We can do cool things with this to track transmission queue statistics .TxEventFifoControl = FDCAN_NO_TX_EVENTS, @@ -121,6 +120,18 @@ HAL_StatusTypeDef CubeCAN_Send(CubeCAN_Handle *const handle, const GRCAN_NODE_ID return CubeCAN_Private_QueueTx(handle, &message); } +HAL_StatusTypeDef CubeCAN_Send_Custom(CubeCAN_Handle *const handle, const GRCAN_CUSTOM_ID custom_id, const void *const data, const uint8_t size) +{ + CubeCAN_Identifier id = {.raw_id = custom_id}; + return CubeCAN_Send_Raw(handle, id, data, size); +} + +HAL_StatusTypeDef CubeCAN_Send(CubeCAN_Handle *const handle, const GRCAN_NODE_ID rx_node, const GRCAN_MSG_ID msg_id, const void *const data, const uint8_t size) +{ + CubeCAN_Identifier id = {.rx_node_id = rx_node, .msg_id = msg_id, .tx_node_id = handle->config.sending_node_id}; + return CubeCAN_Send_Raw(handle, id, data, size); +} + HAL_StatusTypeDef CubeCAN_Private_QueueTx(CubeCAN_Handle *handle, const GRCAN_Private_TxMessage *message) { if (handle == NULL || message == NULL) { From d7b846a2592f6115e12649937aef8f49939846a3 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 06:57:55 -0700 Subject: [PATCH 03/15] Fix name of internal send function Signed-off-by: Daniel Hansen --- Lib/Peripherals/CubeCAN/Src/can_tx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/Peripherals/CubeCAN/Src/can_tx.c b/Lib/Peripherals/CubeCAN/Src/can_tx.c index c49be4525..31b7eb0dd 100644 --- a/Lib/Peripherals/CubeCAN/Src/can_tx.c +++ b/Lib/Peripherals/CubeCAN/Src/can_tx.c @@ -123,13 +123,13 @@ HAL_StatusTypeDef CubeCAN_Private_Send(CubeCAN_Handle *const handle, const CubeC HAL_StatusTypeDef CubeCAN_Send_Custom(CubeCAN_Handle *const handle, const GRCAN_CUSTOM_ID custom_id, const void *const data, const uint8_t size) { CubeCAN_Identifier id = {.raw_id = custom_id}; - return CubeCAN_Send_Raw(handle, id, data, size); + return CubeCAN_Private_Send(handle, id, data, size); } HAL_StatusTypeDef CubeCAN_Send(CubeCAN_Handle *const handle, const GRCAN_NODE_ID rx_node, const GRCAN_MSG_ID msg_id, const void *const data, const uint8_t size) { CubeCAN_Identifier id = {.rx_node_id = rx_node, .msg_id = msg_id, .tx_node_id = handle->config.sending_node_id}; - return CubeCAN_Send_Raw(handle, id, data, size); + return CubeCAN_Private_Send(handle, id, data, size); } HAL_StatusTypeDef CubeCAN_Private_QueueTx(CubeCAN_Handle *handle, const GRCAN_Private_TxMessage *message) From 35b1f7bd9285f9e6a1678d946ff5f2d3b9b065f9 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 06:59:51 -0700 Subject: [PATCH 04/15] Correct remaining uses of `CubeCAN_Identifier` to use proper name --- Lib/Peripherals/CubeCAN/Src/can_assert.c | 2 +- Lib/Peripherals/CubeCAN/Src/can_init.c | 2 +- Testbench/CUBEMXTESTING/Application/Inc/CANdler.h | 2 +- Testbench/CUBEMXTESTING/Application/Src/CANdler.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/Peripherals/CubeCAN/Src/can_assert.c b/Lib/Peripherals/CubeCAN/Src/can_assert.c index 22090a2c2..4119e6e83 100644 --- a/Lib/Peripherals/CubeCAN/Src/can_assert.c +++ b/Lib/Peripherals/CubeCAN/Src/can_assert.c @@ -27,4 +27,4 @@ static_assert(CUBEMX_CAN_MAX_INSTANCES >= 1 && CUBEMX_CAN_MAX_INSTANCES <= 3, "C static_assert(sizeof(GRCAN_BUS_ID) <= sizeof(void *), "GRCAN_BUS_ID must fit within a pointer-sized type to support being unioned with a pointer in CubeCAN_Config"); -static_assert(sizeof(CAN_Identifier) == sizeof(uint32_t), "CAN_Identifier must be 32 bits in size"); +static_assert(sizeof(CubeCAN_Identifier) == sizeof(uint32_t), "CAN_Identifier must be 32 bits in size"); diff --git a/Lib/Peripherals/CubeCAN/Src/can_init.c b/Lib/Peripherals/CubeCAN/Src/can_init.c index d5c178136..fd7338d10 100644 --- a/Lib/Peripherals/CubeCAN/Src/can_init.c +++ b/Lib/Peripherals/CubeCAN/Src/can_init.c @@ -13,7 +13,7 @@ static bool cubecan_internal_checks_passed = false; HAL_StatusTypeDef CubeCAN_Private_InternalOneTimeChecks(void) { - CAN_Identifier test_id = {.raw_id = 0x01122233}; + CubeCAN_Identifier test_id = {.raw_id = 0x01122233}; if (test_id._ != 0 || test_id.tx_node_id != 0x11 || test_id.msg_id != 0x222 || test_id.rx_node_id != 0x33) { LOGOMATIC_ERROR("CubeCAN_Private_InternalOneTimeChecks: CAN_Identifier bitfield order is incorrect on this platform, expected TX ID 0x11, MSG ID 0x222, RX ID 0x33 for raw_id 0x01122233, got TX ID 0x%02X, MSG ID 0x%03X, RX ID 0x%02X\n", test_id.tx_node_id, test_id.msg_id, test_id.rx_node_id); return HAL_ERROR; diff --git a/Testbench/CUBEMXTESTING/Application/Inc/CANdler.h b/Testbench/CUBEMXTESTING/Application/Inc/CANdler.h index 4950e32d2..8fc54bba0 100644 --- a/Testbench/CUBEMXTESTING/Application/Inc/CANdler.h +++ b/Testbench/CUBEMXTESTING/Application/Inc/CANdler.h @@ -3,7 +3,7 @@ #ifndef CANDLER_H #define CANDLER_H -void CANdler_Callback(const CubeCAN_Config_Context *const context, const CAN_Identifier *const identifier, const uint8_t *const data, const uint8_t size); +void CANdler_Callback(const CubeCAN_Config_Context *const context, const CubeCAN_Identifier *const identifier, const uint8_t *const data, const uint8_t size); void CANdler_VCP_Callback(const uint8_t *const data, const uint16_t size); #endif diff --git a/Testbench/CUBEMXTESTING/Application/Src/CANdler.c b/Testbench/CUBEMXTESTING/Application/Src/CANdler.c index 5e573cdb7..5e297c765 100644 --- a/Testbench/CUBEMXTESTING/Application/Src/CANdler.c +++ b/Testbench/CUBEMXTESTING/Application/Src/CANdler.c @@ -8,7 +8,7 @@ int value; -void CANdler_Callback(const CubeCAN_Config_Context *const context, const CAN_Identifier *const identifier, const uint8_t *const data, const uint8_t size) +void CANdler_Callback(const CubeCAN_Config_Context *const context, const CubeCAN_Identifier *const identifier, const uint8_t *const data, const uint8_t size) { value++; From bde4ffde1362747cbe0b80051fd3e975f3b4ba1e Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 07:02:31 -0700 Subject: [PATCH 05/15] Fix remaining `CAN_Identifier` instances Signed-off-by: Daniel Hansen --- Lib/Peripherals/CubeCAN/Test/cubecan_rx_failure_test.c | 2 +- Lib/Peripherals/CubeCAN/Test/cubecan_rx_test.c | 4 ++-- Lib/Peripherals/CubeCAN/Test/cubecan_utils_test.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/Peripherals/CubeCAN/Test/cubecan_rx_failure_test.c b/Lib/Peripherals/CubeCAN/Test/cubecan_rx_failure_test.c index 99df49a48..368de41f6 100644 --- a/Lib/Peripherals/CubeCAN/Test/cubecan_rx_failure_test.c +++ b/Lib/Peripherals/CubeCAN/Test/cubecan_rx_failure_test.c @@ -7,7 +7,7 @@ static uint32_t callback_count; -static void receive_callback(const CubeCAN_Config_Context *context, const CAN_Identifier *identifier, const uint8_t *data, uint8_t size) +static void receive_callback(const CubeCAN_Config_Context *context, const CubeCAN_Identifier *identifier, const uint8_t *data, uint8_t size) { UNUSED(context); UNUSED(identifier); diff --git a/Lib/Peripherals/CubeCAN/Test/cubecan_rx_test.c b/Lib/Peripherals/CubeCAN/Test/cubecan_rx_test.c index c391a041b..313030eb5 100644 --- a/Lib/Peripherals/CubeCAN/Test/cubecan_rx_test.c +++ b/Lib/Peripherals/CubeCAN/Test/cubecan_rx_test.c @@ -6,12 +6,12 @@ #include "main.h" static uint32_t callback_count; -static CAN_Identifier callback_ids[2]; +static CubeCAN_Identifier callback_ids[2]; static uint8_t callback_data[2][64]; static uint8_t callback_sizes[2]; static uint32_t callback_context; -static void receive_callback(const CubeCAN_Config_Context *context, const CAN_Identifier *identifier, const uint8_t *data, uint8_t size) +static void receive_callback(const CubeCAN_Config_Context *context, const CubeCAN_Identifier *identifier, const uint8_t *data, uint8_t size) { const uint32_t index = callback_count++; callback_context = context->busid_user_context; diff --git a/Lib/Peripherals/CubeCAN/Test/cubecan_utils_test.c b/Lib/Peripherals/CubeCAN/Test/cubecan_utils_test.c index 149eba222..9ef0f2472 100644 --- a/Lib/Peripherals/CubeCAN/Test/cubecan_utils_test.c +++ b/Lib/Peripherals/CubeCAN/Test/cubecan_utils_test.c @@ -17,13 +17,13 @@ int main(void) assert(CubeCAN_Private_BytesToDlc(11U) == FDCAN_DLC_BYTES_0 && "Unsupported byte counts should be rejected"); assert(CubeCAN_Private_DlcToBytes(0x10U) == 0U && "Unsupported dlc values should map to 0B"); - const CAN_Identifier identifier = {.tx_node_id = GRCAN_TireTemp_FL, .rx_node_id = GRCAN_BrakeTemp_RR, .msg_id = GRCAN_TIRE_TEMP_FRAME_7}; - const CAN_Identifier decoded = {.raw_id = identifier.raw_id}; + const CubeCAN_Identifier identifier = {.tx_node_id = GRCAN_TireTemp_FL, .rx_node_id = GRCAN_BrakeTemp_RR, .msg_id = GRCAN_TIRE_TEMP_FRAME_7}; + const CubeCAN_Identifier decoded = {.raw_id = identifier.raw_id}; assert(decoded.tx_node_id == identifier.tx_node_id && "Identifier round-trip should preserve tx node"); assert(decoded.rx_node_id == identifier.rx_node_id && "Identifier round-trip should preserve rx node"); assert(decoded.msg_id == identifier.msg_id && "Identifier round-trip should preserve message id"); - const CAN_Identifier wire_identifier = {.raw_id = 0x01122233U}; + const CubeCAN_Identifier wire_identifier = {.raw_id = 0x01122233U}; assert(wire_identifier.tx_node_id == 0x11U && "CAN identifier should decode the tx node from the wire format"); assert(wire_identifier.msg_id == 0x222U && "CAN identifier should decode the message id from the wire format"); assert(wire_identifier.rx_node_id == 0x33U && "CAN identifier should decode the rx node from the wire format"); From 2f543aaa8ccb15c701a6cb9802b375666349a39c Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 07:05:46 -0700 Subject: [PATCH 06/15] Fix memory safety of `CubeCAN_Send` wrt checking handle null Signed-off-by: Daniel Hansen --- Lib/Peripherals/CubeCAN/Src/can_tx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/Peripherals/CubeCAN/Src/can_tx.c b/Lib/Peripherals/CubeCAN/Src/can_tx.c index 31b7eb0dd..127f739bc 100644 --- a/Lib/Peripherals/CubeCAN/Src/can_tx.c +++ b/Lib/Peripherals/CubeCAN/Src/can_tx.c @@ -6,9 +6,9 @@ #include "CriticalSection.h" #include "CubeCAN.h" #include "CubeCAN_Config.h" +#include "GRCAN_CUSTOM_ID.h" #include "Logomatic.h" #include "PrivateInc/internal.h" -#include "GRCAN_CUSTOM_ID.h" #include "main.h" void CubeCAN_Private_RateChecker(void) @@ -128,6 +128,10 @@ HAL_StatusTypeDef CubeCAN_Send_Custom(CubeCAN_Handle *const handle, const GRCAN_ HAL_StatusTypeDef CubeCAN_Send(CubeCAN_Handle *const handle, const GRCAN_NODE_ID rx_node, const GRCAN_MSG_ID msg_id, const void *const data, const uint8_t size) { + if (handle == NULL) { + return HAL_ERROR; + } + CubeCAN_Identifier id = {.rx_node_id = rx_node, .msg_id = msg_id, .tx_node_id = handle->config.sending_node_id}; return CubeCAN_Private_Send(handle, id, data, size); } From 8cb2d60cb0c800e4d6ca6b7d253d19031e0fed0d Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Sep 2026 14:09:55 +0000 Subject: [PATCH 07/15] Automatic Clang-Format: Standardized formatting automatically --- Lib/Peripherals/CubeCAN/Inc/CubeCAN.h | 3 ++- Lib/Peripherals/CubeCAN/Src/can_init.c | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h b/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h index 79fccf710..c13cf0dfa 100644 --- a/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h +++ b/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h @@ -24,7 +24,8 @@ typedef struct CubeCAN_Private_Handle CubeCAN_Handle; * This union allows for easy access to the individual components of a CAN identifier, including the receive node ID, message ID, and transmit node ID. * It also provides a raw 32-bit representation of the identifier which can be used to specify custom CAN IDs. * - * @warning The values of the fields are not validated and may not represent a valid CAN identifier. It is the responsibility of the user to ensure that the values are valid and conform to the CAN protocol. + * @warning The values of the fields are not validated and may not represent a valid CAN identifier. It is the responsibility of the user to ensure that the values are valid and conform to the CAN + * protocol. * @warning Do not use the _ field directly, as it may lead to undefined behavior. Use the provided fields instead. * * @note Setting any bit past the 29th bit is undefined behavior and may lead to unexpected results. diff --git a/Lib/Peripherals/CubeCAN/Src/can_init.c b/Lib/Peripherals/CubeCAN/Src/can_init.c index fd7338d10..3a5cd7a1c 100644 --- a/Lib/Peripherals/CubeCAN/Src/can_init.c +++ b/Lib/Peripherals/CubeCAN/Src/can_init.c @@ -15,13 +15,17 @@ HAL_StatusTypeDef CubeCAN_Private_InternalOneTimeChecks(void) { CubeCAN_Identifier test_id = {.raw_id = 0x01122233}; if (test_id._ != 0 || test_id.tx_node_id != 0x11 || test_id.msg_id != 0x222 || test_id.rx_node_id != 0x33) { - LOGOMATIC_ERROR("CubeCAN_Private_InternalOneTimeChecks: CAN_Identifier bitfield order is incorrect on this platform, expected TX ID 0x11, MSG ID 0x222, RX ID 0x33 for raw_id 0x01122233, got TX ID 0x%02X, MSG ID 0x%03X, RX ID 0x%02X\n", test_id.tx_node_id, test_id.msg_id, test_id.rx_node_id); + LOGOMATIC_ERROR("CubeCAN_Private_InternalOneTimeChecks: CAN_Identifier bitfield order is incorrect on this platform, expected TX ID 0x11, MSG ID 0x222, RX ID 0x33 for raw_id " + "0x01122233, got TX ID 0x%02X, MSG ID 0x%03X, RX ID 0x%02X\n", + test_id.tx_node_id, test_id.msg_id, test_id.rx_node_id); return HAL_ERROR; } test_id.raw_id = 0xA013FF; if (test_id._ != 0 || test_id.tx_node_id != 0x0A || test_id.msg_id != 0x013 || test_id.rx_node_id != 0xFF) { - LOGOMATIC_ERROR("CubeCAN_Private_InternalOneTimeChecks: CAN_Identifier bitfield order is incorrect on this platform, expected TX ID 0x0A, MSG ID 0x013, RX ID 0xFF for raw_id 0x0A013FF, got TX ID 0x%02X, MSG ID 0x%03X, RX ID 0x%02X\n", test_id.tx_node_id, test_id.msg_id, test_id.rx_node_id); + LOGOMATIC_ERROR("CubeCAN_Private_InternalOneTimeChecks: CAN_Identifier bitfield order is incorrect on this platform, expected TX ID 0x0A, MSG ID 0x013, RX ID 0xFF for raw_id " + "0x0A013FF, got TX ID 0x%02X, MSG ID 0x%03X, RX ID 0x%02X\n", + test_id.tx_node_id, test_id.msg_id, test_id.rx_node_id); return HAL_ERROR; } From 0624ccf5bd27bf65439d9ed924dc4d1b27d1e69d Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 07:10:41 -0700 Subject: [PATCH 08/15] Docs! Signed-off-by: Daniel Hansen --- Lib/Peripherals/CubeCAN/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/Peripherals/CubeCAN/README.md b/Lib/Peripherals/CubeCAN/README.md index 45c9dbecf..72f6df673 100644 --- a/Lib/Peripherals/CubeCAN/README.md +++ b/Lib/Peripherals/CubeCAN/README.md @@ -36,7 +36,7 @@ Add a file named `CubeCAN_Config.h` to your project, generally `Application/Inc/ #### Configuration Requirements -- `CUBEMX_CAN_TX_QUEUE_SIZE`: Must be a power of two. Defines the transmission queue depth; when the queue fills, the oldest message is dropped and `CubeCAN_Send()` returns `HAL_BUSY`. +- `CUBEMX_CAN_TX_QUEUE_SIZE`: Must be a power of two. Defines the transmission queue depth; when the queue fills, the oldest message is dropped and `CubeCAN_Send()` and `CubeCAN_Send_Custom()` return `HAL_BUSY`. - `CUBEMX_CAN_MAX_INSTANCES`: Must be 1-3 depending on your chip's available FDCAN peripherals. Omit this definition to automatically select the maximum supported by your MCU. From there just ensure that you have configured CAN through STM32CubeMX and setup a global variable for each `CubeCAN_Handle` you wish to use. @@ -59,7 +59,7 @@ Ensure you have setup some mechanism to call `CubeCAN_Tick()` frequently (a STM3 #### Transmission -Call `CubeCAN_Send()` to queue a message for transmission. Messages are queued in a lock-free ring buffer and processed in FIFO order. +Call `CubeCAN_Send()` or `CubeCAN_Send_Custom()` to queue a message for transmission. Messages are queued in a lock-free ring buffer and processed in FIFO order. - **Returns `HAL_OK`**: Message successfully queued - **Returns `HAL_BUSY`**: Transmission queue is full; the oldest queued message was dropped to make room for the new one @@ -91,7 +91,7 @@ flowchart TD Entrance --> Choice{"Functions"} Choice --> Exit(["CubeCAN_Exit()"]) Exit -.-> |"AVOID"| Entrance - Choice --> Send(["CubeCAN_Send()"]) + Choice --> Send(["CubeCAN_Send()\nCubeCAN_Send_Custom()"]) Choice --> |"Call Often\n(User Setup)"| Tick(["CubeCAN_Tick()"]) Send -.-> Queue("CubeCAN_Private_QueueTx()") Data -.-> Tick @@ -126,7 +126,7 @@ CubeCAN is designed to handle concurrent access from multiple execution contexts #### Overview - `tx_head` and `tx_tail` are atomic `uint32_t` variables that implement a lock-free ring buffer -- `CubeCAN_Send()` and `CubeCAN_Tick()` use critical sections (interrupt blocking) when necessary +- `CubeCAN_Send()`, `CubeCAN_Send_Custom()`, and `CubeCAN_Tick()` use critical sections (interrupt blocking) when necessary - Atomic operations use `memory_order_relaxed` within ISRs and `memory_order_release`/`memory_order_acquire` for inter-context synchronization - The queue size must be a power of two, allowing fast wraparound via bitwise AND instead of expensive modulo operations @@ -139,7 +139,7 @@ Interrupt protection is used only in: #### ISR Interaction -- Safe to call `CubeCAN_Send()` from `main()` or any ISR +- Safe to call `CubeCAN_Send()` or `CubeCAN_Send_Custom()` from `main()` or any ISR - Safe to call `CubeCAN_Tick()` from `main()` or any ISR - Do not call any CubeCAN function from `CubeCAN_RxCallback()`, use it only to write data to shared memory (with concurrency safety as needed) @@ -184,7 +184,7 @@ We currently provide compile time static assertions to validate: - `CUBEMX_CAN_TX_QUEUE_SIZE` is a power of two and non-zero (allows faster ring-buffer wrapping) - `CUBEMX_CAN_MAX_INSTANCES` requires management of 1 to 3 interfaces (optional parameter, otherwise automatically chooses the maximum possible) -A single runtime check `CubeCAN_Private_InternalOneTimeChecks()` is called once on first call to `CubeCAN_Entrance()` which validates that the compiled bit-packing for `CAN_Identifier` is correct, if it is not the CAN peripheral entrance fails and returns `null`. Sadly this cannot be a compile-time check. +A single runtime check `CubeCAN_Private_InternalOneTimeChecks()` is called once on first call to `CubeCAN_Entrance()` which validates that the compiled bit-packing for `CubeCAN_Identifier` is correct, if it is not the CAN peripheral entrance fails and returns `null`. Sadly this cannot be a compile-time check. ### Globals From 1945294cf5a71dd0a67b01a590706eef3dbbb1eb Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 07:15:21 -0700 Subject: [PATCH 09/15] Change CUBEMXTESTING log level for CANdler Signed-off-by: Daniel Hansen --- Testbench/CUBEMXTESTING/Application/Src/CANdler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Testbench/CUBEMXTESTING/Application/Src/CANdler.c b/Testbench/CUBEMXTESTING/Application/Src/CANdler.c index 5e297c765..52524ac91 100644 --- a/Testbench/CUBEMXTESTING/Application/Src/CANdler.c +++ b/Testbench/CUBEMXTESTING/Application/Src/CANdler.c @@ -23,7 +23,7 @@ void CANdler_Callback(const CubeCAN_Config_Context *const context, const CubeCAN const GRCAN_NODE_ID rx_node = identifier->rx_node_id; const GRCAN_MSG_ID msg_id = identifier->msg_id; - LOGOMATIC_VERBOSE("Received on bus %d with node %d -> %d with message %d and size %u\t%.*s\n", busid, tx_node, rx_node, msg_id, size, size, (const char *const)data); + LOGOMATIC_DEBUG("Received on bus %d with node %d -> %d with message %d and size %u\t%.*s\n", busid, tx_node, rx_node, msg_id, size, size, (const char *const)data); } void CANdler_VCP_Callback(const uint8_t *const data, const uint16_t size) From e5856f9e04d9545296ab3efb696d33f08adc1b59 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 07:24:25 -0700 Subject: [PATCH 10/15] Safely guard definition of `__weak` Why this changed I have no idea Signed-off-by: Daniel Hansen --- Lib/Utils/CubeVCP/Test/main.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/Utils/CubeVCP/Test/main.h b/Lib/Utils/CubeVCP/Test/main.h index a185eb320..d04a57e35 100644 --- a/Lib/Utils/CubeVCP/Test/main.h +++ b/Lib/Utils/CubeVCP/Test/main.h @@ -4,7 +4,9 @@ #ifndef CUBEVCP_TEST_MAIN_H #define CUBEVCP_TEST_MAIN_H +#ifndef __weak #define __weak __attribute__((weak)) +#endif typedef enum { HAL_OK = 0x00, From d5014f7adcba005e3c1d0b978d0a396c242001eb Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 07:54:41 -0700 Subject: [PATCH 11/15] Cache improvements Signed-off-by: Daniel Hansen --- .github/workflows/UnitTestsHOOTL.yml | 23 ++++++++++++++++++++++- CMakeLists.txt | 9 +++++---- Lib/cmake/HOOTL.cmake | 1 - 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/.github/workflows/UnitTestsHOOTL.yml b/.github/workflows/UnitTestsHOOTL.yml index 93a480a81..0a61a96f1 100644 --- a/.github/workflows/UnitTestsHOOTL.yml +++ b/.github/workflows/UnitTestsHOOTL.yml @@ -32,6 +32,7 @@ jobs: SCCACHE_LOG: "info" SCCACHE_ERROR_LOG: ${{ github.workspace }}/sccache-error.log SCCACHE_SERVER_PORT: ${{ matrix.prefix == 'Debug' && '4226' || (matrix.prefix == 'Release' && '4227' || (matrix.prefix == 'MinSizeRel' && '4228' || '4229')) }} + FETCHCONTENT_DIR: ${{ github.workspace }}/Lib/Vendor/FetchContent steps: - name: Checkout Repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -43,14 +44,24 @@ jobs: with: release: ${{ env.ARM_GCC_VERSION }} + - name: Setup FetchContent Cache + continue-on-error: true + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ env.FETCHCONTENT_DIR }} + key: fetchcontent-${{ runner.os }}-${{ hashFiles('Lib/cmake/cube.cmake', 'CMakeLists.txt', 'Testbench/testbench.cmake') }} + restore-keys: | + fetchcontent-${{ runner.os }}- + - name: Setup Cache id: cache-setup continue-on-error: true uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ${{ env.SCCACHE_DIR }} - key: sccache-${{ runner.os }}-${{ env.ARM_GCC_VERSION }}-${{ github.job }}-${{ matrix.prefix }}-${{ github.ref_name }} + key: sccache-${{ runner.os }}-${{ env.ARM_GCC_VERSION }}-${{ github.job }}-${{ matrix.prefix }}-${{ github.ref_name }}-${{ github.sha }} restore-keys: | + sccache-${{ runner.os }}-${{ env.ARM_GCC_VERSION }}-${{ github.job }}-${{ matrix.prefix }}-${{ github.ref_name }}-${{ github.sha }} sccache-${{ runner.os }}-${{ env.ARM_GCC_VERSION }}-${{ github.job }}-${{ matrix.prefix }}-${{ github.ref_name }} sccache-${{ runner.os }}-${{ env.ARM_GCC_VERSION }}-${{ github.job }}-${{ matrix.prefix }}-main sccache-${{ runner.os }}-${{ env.ARM_GCC_VERSION }}-${{ github.job }}-${{ matrix.prefix }}- @@ -199,12 +210,22 @@ jobs: SCCACHE_DIR: ${{ github.workspace }}/sccache SCCACHE_LOG: "info" SCCACHE_ERROR_LOG: ${{ github.workspace }}/sccache-error.log + FETCHCONTENT_DIR: ${{ github.workspace }}/Lib/Vendor/FetchContent steps: - name: Checkout Repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 1 + - name: Setup FetchContent Cache + continue-on-error: true + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ env.FETCHCONTENT_DIR }} + key: fetchcontent-${{ runner.os }}-${{ hashFiles('Lib/cmake/cube.cmake', 'CMakeLists.txt', 'Testbench/testbench.cmake', 'BLINKY/blinky.cmake') }} + restore-keys: | + fetchcontent-${{ runner.os }}- + - name: Setup Cache id: cache-setup continue-on-error: true diff --git a/CMakeLists.txt b/CMakeLists.txt index eec67ef56..975552ca8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,5 @@ cmake_minimum_required(VERSION 3.25) project(Firmware) -enable_language( - C - ASM -) set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS ON) @@ -106,6 +102,11 @@ else() message(STATUS "Not using ccache: ccache not found in PATH") endif() +enable_language( + C + ASM +) + # Globals set(LIB_PATH "Lib") set(platform_path "${LIB_PATH}/Platform") diff --git a/Lib/cmake/HOOTL.cmake b/Lib/cmake/HOOTL.cmake index bd3858b2a..e8db18211 100644 --- a/Lib/cmake/HOOTL.cmake +++ b/Lib/cmake/HOOTL.cmake @@ -20,7 +20,6 @@ add_compile_options( -fshort-enums -fdata-sections -ffunction-sections - -fstack-usage -fno-lto -Wall -Wextra From a3718c325df2bd454c223ee893c8c95b47df58a2 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 08:01:42 -0700 Subject: [PATCH 12/15] Fix `vcp_rx_index` to not be atomic and to be local Signed-off-by: Daniel Hansen --- Lib/Utils/CubeVCP/Src/PrivateInc/internal.h | 10 ---------- Lib/Utils/CubeVCP/Src/vcp_global.c | 1 - Lib/Utils/CubeVCP/Src/vcp_rx.c | 13 +++++++++++-- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Lib/Utils/CubeVCP/Src/PrivateInc/internal.h b/Lib/Utils/CubeVCP/Src/PrivateInc/internal.h index 71e88bb52..b2fcfdeae 100644 --- a/Lib/Utils/CubeVCP/Src/PrivateInc/internal.h +++ b/Lib/Utils/CubeVCP/Src/PrivateInc/internal.h @@ -106,16 +106,6 @@ extern uint8_t vcp_callback_staging[]; */ extern uint8_t vcp_rx_buffer[]; -/** - * @brief Index of the next byte to be written in the current VCP reception buffer. - * - * This atomic variable is used to keep track of the index where the next byte should be written in the current VCP reception buffer. - * When the buffer fills or a newline is received, the callback is invoked and this index is reset to 0. - * - * @warning Requires atomic operations to ensure thread safety when accessed from multiple contexts - */ -extern uint16_t vcp_rx_index; - /** * @brief Index of the current RX buffer (0 or 1) being written to by the ISR. * diff --git a/Lib/Utils/CubeVCP/Src/vcp_global.c b/Lib/Utils/CubeVCP/Src/vcp_global.c index cc1f0755e..088479a0d 100644 --- a/Lib/Utils/CubeVCP/Src/vcp_global.c +++ b/Lib/Utils/CubeVCP/Src/vcp_global.c @@ -14,7 +14,6 @@ _Atomic uint16_t vcp_ring_head; _Atomic uint16_t vcp_ring_tail; _Atomic bool vcp_tx_active; -uint16_t vcp_rx_index; uint8_t vcp_rx_byte; uint8_t vcp_rx_buffer[CUBE_VCP_RX_BUFFER_SIZE]; uint8_t vcp_callback_staging[CUBE_VCP_RX_BUFFER_SIZE]; diff --git a/Lib/Utils/CubeVCP/Src/vcp_rx.c b/Lib/Utils/CubeVCP/Src/vcp_rx.c index 9255db438..92fdadac0 100644 --- a/Lib/Utils/CubeVCP/Src/vcp_rx.c +++ b/Lib/Utils/CubeVCP/Src/vcp_rx.c @@ -8,6 +8,16 @@ #include "PrivateInc/internal.h" #include "main.h" +/** + * @brief Index of the next byte to be written in the current VCP reception buffer. + * + * This variable is used to keep track of the index where the next byte should be written in the current VCP reception buffer. + * When the buffer fills or a newline is received, the callback is invoked and this index is reset to 0. + * + * @note Does not require atomic operations since it is only accessed from the ISR context. + */ +static uint16_t vcp_rx_index = 0; + HAL_StatusTypeDef CubeVCP_EnableRx(CubeVCP_Rx_Callback callback) { static bool enabled = false; @@ -29,8 +39,7 @@ HAL_StatusTypeDef CubeVCP_EnableRx(CubeVCP_Rx_Callback callback) } vcp_rx_callback = callback; - - atomic_store_explicit(&vcp_rx_index, 0, memory_order_relaxed); + vcp_rx_index = 0; HAL_StatusTypeDef status = HAL_UART_Receive_IT(vcp_uart_handle, &vcp_rx_byte, 1); From 9b90c514ff5c803cb8bebbc6eb8329de239f7986 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 08:10:12 -0700 Subject: [PATCH 13/15] Fix `CubeCAN_Private_SendQueuedMessage` const-correctness Signed-off-by: Daniel Hansen --- Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h | 2 +- Lib/Peripherals/CubeCAN/Src/can_tx.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h b/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h index 3cfe8e636..fb9e57fd6 100644 --- a/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h +++ b/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h @@ -120,7 +120,7 @@ HAL_StatusTypeDef CubeCAN_Private_InternalOneTimeChecks(void); * @param handle Pointer to the CubeCAN CAN handle. * @return HAL_StatusTypeDef indicating the success or failure of the operation. */ -HAL_StatusTypeDef CubeCAN_Private_SendQueuedMessage(const CubeCAN_Handle *const handle); +HAL_StatusTypeDef CubeCAN_Private_SendQueuedMessage(CubeCAN_Handle *const handle); /** * @brief Attempts to recover the FDCAN peripheral associated with the given CubeCAN CAN handle. diff --git a/Lib/Peripherals/CubeCAN/Src/can_tx.c b/Lib/Peripherals/CubeCAN/Src/can_tx.c index 127f739bc..ba44adb3a 100644 --- a/Lib/Peripherals/CubeCAN/Src/can_tx.c +++ b/Lib/Peripherals/CubeCAN/Src/can_tx.c @@ -162,7 +162,7 @@ HAL_StatusTypeDef CubeCAN_Private_QueueTx(CubeCAN_Handle *handle, const GRCAN_Pr return status; } -HAL_StatusTypeDef CubeCAN_Private_SendQueuedMessage(const CubeCAN_Handle *const handle) +HAL_StatusTypeDef CubeCAN_Private_SendQueuedMessage(CubeCAN_Handle *const handle) { if (handle == NULL || handle->hfdcan == NULL) { LOGOMATIC_ERROR("CubeCAN_Private_SendQueuedMessage: invalid null parameter\n"); From 1845b53feee20edc8276d366335e92b1b58de3b9 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 08:30:14 -0700 Subject: [PATCH 14/15] Make sure Windows does not use MS bitfields Signed-off-by: Daniel Hansen --- Lib/cmake/HOOTL.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/cmake/HOOTL.cmake b/Lib/cmake/HOOTL.cmake index e8db18211..722f5c2de 100644 --- a/Lib/cmake/HOOTL.cmake +++ b/Lib/cmake/HOOTL.cmake @@ -36,6 +36,10 @@ else() add_link_options(-Wl,--fatal-warnings) endif() +if(WIN32) # Windows has different bitfield layout and semantics + add_compile_options(-mno-ms-bitfields) +endif() + if(ADDRESS_SANITIZER) message(STATUS "Address sanitizer enabled for tests") From b20ab7f835aba0918192b63f5efbeb90b430acac Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 30 Sep 2026 08:48:35 -0700 Subject: [PATCH 15/15] Why is windows the way that it is Signed-off-by: Daniel Hansen --- Lib/Utils/CubeVCP/Test/main.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/Utils/CubeVCP/Test/main.h b/Lib/Utils/CubeVCP/Test/main.h index d04a57e35..bc3e4d3d9 100644 --- a/Lib/Utils/CubeVCP/Test/main.h +++ b/Lib/Utils/CubeVCP/Test/main.h @@ -5,8 +5,12 @@ #define CUBEVCP_TEST_MAIN_H #ifndef __weak +#if defined(_WIN32) +#define __weak +#else #define __weak __attribute__((weak)) #endif +#endif typedef enum { HAL_OK = 0x00,