diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 2eb6c769a..405eb8939 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -12,6 +12,7 @@ The codebase represents a real-time distributed system for an FSAE electric vehi ## Code Standards - Codestyle is defined in `docs/code_style.md` - Dynamic memory allocation is FORBIDDEN +- In-repo documentation should be updated in the same PR as code changes. ## Real-Time Constraints - Code must be deterministic diff --git a/common/can_library/README.md b/common/can_library/README.md index adc5a485e..0e2545901 100644 --- a/common/can_library/README.md +++ b/common/can_library/README.md @@ -12,16 +12,27 @@ Standardized framework for CAN communication and system-wide fault management wi - `faults_common.h / .c`: System-wide fault management. - `can_library.cmake`: CMake integration and node library generation. +## Logic +The high-level logic flow of an RX is shown here: +![CAN RX Logic](can_rx_logic.drawio.png) +> [!NOTE] +> The all RX IRQs push to the same queue rather than having separate queues per peripheral. + +The high-level logic flow of a TX is shown here: +![CAN TX Logic](can_tx_logic.drawio.png) +> [!NOTE] +> The actual implementation of the CAN TX task manages up to 3 seperate hardware peripherals at once, each with its own software queue. + ## Usage 1. Define your CAN network and global faults in `common/can_library/configs/` using the provided JSON schemas. 1. Use FDCAN peripherals on G4 and CAN peripherals on F4/F7. 2. Add to `COMMON_LIBRARIES` of your target: `can_node_`. -3. Define your RX interrupt handlers to call `CAN_handle_irq(CAN_TypeDef *bus, uint8_t fifo)` +3. Include the generated header for your node (e.g. `#include "common/can_library/generated/can_node_.h"`) in your `main.c`. +3. Initialize the CAN library in your `main.c` with `CAN_init()`. +4. Setup CAN tasks in your `main.c` using `DEFINE_CAN_TASKS()` and `START_CAN_TASKS()`. -> [!NOTE] -> Weird quirk: we run the CANpiler twice. -> Once during CMake configuration time and one during build time. -> This avoids any possibility of building with stale generated files. +The most recent rx'd data is available in the `can_data` struct, which is updated by the CAN RX task. +Sending CAN messages is done via the generated `CAN_SEND_()` functions, which enqueue messages to be sent by the CAN TX task. ## Fault System The `faults_common` module implements the **FIDR (Fault Isolation, Detection, and Recovery)** system. It manages the lifecycle of system-wide faults using a robust Finite State Machine (FSM) to prevent flickering and ensure deterministic fault handling. @@ -32,6 +43,7 @@ The `faults_common` module implements the **FIDR (Fault Isolation, Detection, an - `update_fault(fault_index, value)`: Called by the owner node to feed sensor/status data into the FSM. - `fault_library_periodic()`: Tally active faults and broadcast a `tx_fault_sync` message. - `is_latched(fault_index)`: Check if a specific fault is active. +- `is_clear(fault_index)`: Check if a specific fault is clear. > [!NOTE] > Each node is assigned a specific range of faults (`MY_FAULT_START` to `MY_FAULT_END`). diff --git a/common/can_library/can_common.c b/common/can_library/can_common.c index 6e49509ac..b6263ccaf 100644 --- a/common/can_library/can_common.c +++ b/common/can_library/can_common.c @@ -11,136 +11,142 @@ #include "common/can_library/generated/can_router.h" // common data structures -can_data_t can_data; -can_stats_t can_stats; +volatile can_data_t can_data; +volatile can_stats_t can_stats; volatile uint32_t last_can_rx_time_ms; -DEFINE_QUEUE(q_rx_can, CanMsgTypeDef_t, CAN_RX_QUEUE_LENGTH); + +extern osThreadId_t CAN_rx_update_handle; +extern osThreadId_t CAN_tx_update_handle; + +QueueHandle_t can_tx_queues[CAN_NUM_PERIPHERALS]; +DEFINE_QUEUE(can_rx_queue, CanMsgTypeDef_t, CAN_RX_QUEUE_LENGTH); // Shared rx update implementation void CAN_rx_update() { CanMsgTypeDef_t rx_msg; // Block until a message is received - if (xQueueReceive(q_rx_can, &rx_msg, portMAX_DELAY) == pdPASS) { + if (xQueueReceive(can_rx_queue, &rx_msg, portMAX_DELAY) == pdPASS) { last_can_rx_time_ms = OS_TICKS; - uint8_t periph_idx = GET_PERIPH_IDX(rx_msg.Bus); + CAN_peripheral_t peripheral = BUS_TO_PERIPHERAL(rx_msg.Bus); CAN_rx_dispatcher( rx_msg.IDE == 0 ? rx_msg.StdId : rx_msg.ExtId, rx_msg.Data, rx_msg.DLC, - periph_idx + peripheral ); } } -#if defined(STM32F407xx) +// Shared tx enqueue implementation +void CAN_enqueue_tx(CanMsgTypeDef_t *msg) { + CAN_peripheral_t peripheral = BUS_TO_PERIPHERAL(msg->Bus); + + // Immediately drop the message and bump overflow counter if the queue is full + if (xQueueSendToBack(can_tx_queues[peripheral], msg, 0) != pdPASS) { + can_stats.tx_overflow++; + return; + } -QueueHandle_t q_tx_can[NUM_CAN_PERIPHERALS][CAN_TX_MAILBOX_CNT]; -uint32_t can_mbx_last_send_time[NUM_CAN_PERIPHERALS][CAN_TX_MAILBOX_CNT]; + // Wake the TX task to attempt an immediate send + if (CAN_tx_update_handle != NULL) { + xTaskNotifyGive(CAN_tx_update_handle); + } +} + +// Shared tx isr implementation +[[gnu::always_inline]] +static inline void CAN_wake_tx_from_ISR() { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + if (CAN_tx_update_handle != NULL) { + vTaskNotifyGiveFromISR(CAN_tx_update_handle, &xHigherPriorityTaskWoken); + } + + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +#if defined(STM32F407xx) -// Statically allocate TX queues for CAN1 and CAN2 #ifdef USE_CAN1 -DEFINE_QUEUE(q_tx_can1_m0, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); -DEFINE_QUEUE(q_tx_can1_m1, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); -DEFINE_QUEUE(q_tx_can1_m2, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); +DEFINE_QUEUE(can1_tx_queue, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); #endif #ifdef USE_CAN2 -DEFINE_QUEUE(q_tx_can2_m0, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); -DEFINE_QUEUE(q_tx_can2_m1, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); -DEFINE_QUEUE(q_tx_can2_m2, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); +DEFINE_QUEUE(can2_tx_queue, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); #endif -void CAN_enqueue_tx(CanMsgTypeDef_t *msg) { - uint8_t mailbox; - uint8_t periph_idx = GET_PERIPH_IDX(msg->Bus); - - if (msg->IDE != 0) { - // Extended ID: Use HLP bits (26-28) to determine priority mailbox - switch ((msg->ExtId >> 26) & 0b111) { - case 0: - case 1: - mailbox = CAN_MAILBOX_HIGH_PRIO; - break; - case 2: - case 3: - mailbox = CAN_MAILBOX_MED_PRIO; - break; - default: - mailbox = CAN_MAILBOX_LOW_PRIO; - break; - } - } else { - // Standard ID: Default to high priority - mailbox = CAN_MAILBOX_HIGH_PRIO; - } +void CAN_tx_update() { + // Block until a notification is received + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - if (xQueueSendToBack(q_tx_can[periph_idx][mailbox], msg, pdMS_TO_TICKS(CAN_TX_BACKPRESSURE_MS)) != pdPASS) { - can_stats.can_peripheral_stats[periph_idx].tx_of++; - } -} + // Feed the peripheral TX FIFOs until all queues are empty or FIFOs are full + bool is_tx_success; + do { // Loop to handle the case where a FIFO slot opens during processing + is_tx_success = false; + CanMsgTypeDef_t tx_msg; + uint8_t free_index; + QueueHandle_t tx_queue; -void CAN_tx_update() { - CanMsgTypeDef_t tx_msg; - for (uint8_t i = 0; i < CAN_TX_MAILBOX_CNT; ++i) { #ifdef USE_CAN1 - // Handle CAN1 - if (PHAL_txMailboxFree(CAN1, i)) { - if (xQueueReceive(q_tx_can[CAN1_IDX][i], &tx_msg, 0) == pdPASS) { - PHAL_txCANMessage(&tx_msg, i); - can_mbx_last_send_time[CAN1_IDX][i] = OS_TICKS; - } - } else if (OS_TICKS - can_mbx_last_send_time[CAN1_IDX][i] > CAN_TX_TIMEOUT_MS) { - PHAL_txCANAbort(CAN1, i); - can_stats.can_peripheral_stats[CAN1_IDX].tx_fail++; + tx_queue = can_tx_queues[BUS_TO_PERIPHERAL(CAN1)]; + while (PHAL_getFreeTxMailbox(CAN1, &free_index) && xQueueReceive(tx_queue, &tx_msg, 0) == pdPASS) { + PHAL_txCANMessage(&tx_msg, free_index); + is_tx_success = true; } #endif #ifdef USE_CAN2 - // Handle CAN2 - if (PHAL_txMailboxFree(CAN2, i)) { - if (xQueueReceive(q_tx_can[CAN2_IDX][i], &tx_msg, 0) == pdPASS) { - PHAL_txCANMessage(&tx_msg, i); - can_mbx_last_send_time[CAN2_IDX][i] = OS_TICKS; - } - } else if (OS_TICKS - can_mbx_last_send_time[CAN2_IDX][i] > CAN_TX_TIMEOUT_MS) { - PHAL_txCANAbort(CAN2, i); - can_stats.can_peripheral_stats[CAN2_IDX].tx_fail++; + tx_queue = can_tx_queues[BUS_TO_PERIPHERAL(CAN2)]; + while (PHAL_getFreeTxMailbox(CAN2, &free_index) && xQueueReceive(tx_queue, &tx_msg, 0) == pdPASS) { + PHAL_txCANMessage(&tx_msg, free_index); + is_tx_success = true; } #endif - } + } while (is_tx_success); } [[gnu::always_inline]] -inline void CAN_handle_irq(CAN_TypeDef *bus, uint8_t fifo) { +inline void CAN_rx_ISR(CAN_TypeDef *bus, uint8_t fifo) { CanMsgTypeDef_t rx_msg; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + // Drain the hardware FIFO while (PHAL_rxCANMessage(bus, fifo, &rx_msg)) { - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - if (xQueueSendFromISR(q_rx_can, &rx_msg, &xHigherPriorityTaskWoken) != pdPASS) { - can_stats.rx_of++; + if (xQueueSendFromISR(can_rx_queue, &rx_msg, &xHigherPriorityTaskWoken) != pdPASS) { + can_stats.rx_overflow++; } - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } + + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } // ! Define these in your main.c for now ! #ifdef USE_CAN1 void __attribute__((weak, used)) CAN1_RX0_IRQHandler() { - CAN_handle_irq(CAN1, 0); + CAN_rx_ISR(CAN1, 0); } void __attribute__((weak, used)) CAN1_RX1_IRQHandler() { - CAN_handle_irq(CAN1, 1); + CAN_rx_ISR(CAN1, 1); +} + +void CAN1_TX_IRQHandler() { + CAN_wake_tx_from_ISR(); } #endif #ifdef USE_CAN2 void __attribute__((weak, used)) CAN2_RX0_IRQHandler() { - CAN_handle_irq(CAN2, 0); + CAN_rx_ISR(CAN2, 0); } void __attribute__((weak, used)) CAN2_RX1_IRQHandler() { - CAN_handle_irq(CAN2, 1); + CAN_rx_ISR(CAN2, 1); +} + +void CAN2_TX_IRQHandler() { + CAN_wake_tx_from_ISR(); } #endif @@ -171,36 +177,26 @@ static inline bool CAN_exit_filter_config(CAN_TypeDef *can) { return timeout != PHAL_CAN_INIT_TIMEOUT; } -bool CAN_library_init() { +bool CAN_init() { #ifdef USE_CAN1 - INIT_QUEUE(q_tx_can1_m0, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); - INIT_QUEUE(q_tx_can1_m1, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); - INIT_QUEUE(q_tx_can1_m2, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); - - q_tx_can[CAN1_IDX][0] = q_tx_can1_m0; - q_tx_can[CAN1_IDX][1] = q_tx_can1_m1; - q_tx_can[CAN1_IDX][2] = q_tx_can1_m2; + INIT_QUEUE(can1_tx_queue, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); + can_tx_queues[BUS_TO_PERIPHERAL(CAN1)] = can1_tx_queue; - for (uint8_t i = 0; i < CAN_TX_MAILBOX_CNT; i++) { - can_mbx_last_send_time[CAN1_IDX][i] = 0; - } + NVIC_SetPriority(CAN1_RX0_IRQn, NVIC_RX_IRQ_PRIO); + NVIC_SetPriority(CAN1_RX1_IRQn, NVIC_RX_IRQ_PRIO); + NVIC_SetPriority(CAN1_TX_IRQn, NVIC_TX_IRQ_PRIO); #endif #ifdef USE_CAN2 - INIT_QUEUE(q_tx_can2_m0, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); - INIT_QUEUE(q_tx_can2_m1, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); - INIT_QUEUE(q_tx_can2_m2, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); - - q_tx_can[CAN2_IDX][0] = q_tx_can2_m0; - q_tx_can[CAN2_IDX][1] = q_tx_can2_m1; - q_tx_can[CAN2_IDX][2] = q_tx_can2_m2; + INIT_QUEUE(can2_tx_queue, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); + can_tx_queues[BUS_TO_PERIPHERAL(CAN2)] = can2_tx_queue; - for (uint8_t i = 0; i < CAN_TX_MAILBOX_CNT; i++) { - can_mbx_last_send_time[CAN2_IDX][i] = 0; - } + NVIC_SetPriority(CAN2_RX0_IRQn, NVIC_RX_IRQ_PRIO); + NVIC_SetPriority(CAN2_RX1_IRQn, NVIC_RX_IRQ_PRIO); + NVIC_SetPriority(CAN2_TX_IRQn, NVIC_TX_IRQ_PRIO); #endif - INIT_QUEUE(q_rx_can, CanMsgTypeDef_t, CAN_RX_QUEUE_LENGTH); + INIT_QUEUE(can_rx_queue, CanMsgTypeDef_t, CAN_RX_QUEUE_LENGTH); can_stats = (can_stats_t) {0}; CAN_data_init(); @@ -221,101 +217,116 @@ bool CAN_library_init() { return true; } -#elif defined(STM32G474xx) - -// G4/FDCAN implementation - uses TX FIFO (no mailboxes) -// FDCAN has 3 TX FIFO slots handled by hardware, so we use a single software queue per peripheral - -#ifndef NUM_CAN_PERIPHERALS -#if defined(USE_FDCAN3) -#define NUM_CAN_PERIPHERALS 3 -#elif defined(USE_FDCAN2) -#define NUM_CAN_PERIPHERALS 2 -#elif defined(USE_FDCAN1) -#define NUM_CAN_PERIPHERALS 1 -#else -#define NUM_CAN_PERIPHERALS 1 +bool CAN_enable_IRQs() { +#ifdef USE_CAN1 + NVIC_EnableIRQ(CAN1_RX0_IRQn); + NVIC_EnableIRQ(CAN1_RX1_IRQn); + NVIC_EnableIRQ(CAN1_TX_IRQn); #endif +#ifdef USE_CAN2 + NVIC_EnableIRQ(CAN2_RX0_IRQn); + NVIC_EnableIRQ(CAN2_RX1_IRQn); + NVIC_EnableIRQ(CAN2_TX_IRQn); #endif + return true; +} +#elif defined(STM32G474xx) #ifdef USE_FDCAN1 -DEFINE_QUEUE(q_tx_can1, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); +DEFINE_QUEUE(can1_tx_queue, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); #endif #ifdef USE_FDCAN2 -DEFINE_QUEUE(q_tx_can2, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); +DEFINE_QUEUE(can2_tx_queue, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); #endif #ifdef USE_FDCAN3 -DEFINE_QUEUE(q_tx_can3, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); +DEFINE_QUEUE(can3_tx_queue, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); #endif -QueueHandle_t q_tx_can[NUM_CAN_PERIPHERALS]; - -void CAN_enqueue_tx(CanMsgTypeDef_t *msg) { - uint8_t periph_idx = GET_PERIPH_IDX(msg->Bus); - - // Wait up to CAN_TX_BACKPRESSURE_MS if FDCAN TX FIFO is full before dropping message - // TODO: is this the desired behavior? Or should we just drop immediately? - if (xQueueSendToBack(q_tx_can[periph_idx], msg, pdMS_TO_TICKS(CAN_TX_BACKPRESSURE_MS)) != pdPASS) { - can_stats.can_peripheral_stats[periph_idx].tx_of++; - } -} - void CAN_tx_update() { - CanMsgTypeDef_t tx_msg; + // Block until a notification is received + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + // Feed the peripheral TX FIFOs until all queues are empty or FIFOs are full + bool is_tx_success; + do { // Loop to handle the case where a FIFO slot opens during processing + is_tx_success = false; + CanMsgTypeDef_t tx_msg; + QueueHandle_t tx_queue; #ifdef USE_FDCAN1 - while (PHAL_FDCAN_txFifoFree(FDCAN1) && xQueueReceive(q_tx_can[CAN1_IDX], &tx_msg, 0) == pdPASS) { - PHAL_FDCAN_send(&tx_msg); - } + tx_queue = can_tx_queues[BUS_TO_PERIPHERAL(FDCAN1)]; + while (PHAL_FDCAN_txFifoFree(FDCAN1) && xQueueReceive(tx_queue, &tx_msg, 0) == pdPASS) { + PHAL_FDCAN_send(&tx_msg); + is_tx_success = true; + } #endif #ifdef USE_FDCAN2 - while (PHAL_FDCAN_txFifoFree(FDCAN2) && xQueueReceive(q_tx_can[CAN2_IDX], &tx_msg, 0) == pdPASS) { - PHAL_FDCAN_send(&tx_msg); - } + tx_queue = can_tx_queues[BUS_TO_PERIPHERAL(FDCAN2)]; + while (PHAL_FDCAN_txFifoFree(FDCAN2) && xQueueReceive(tx_queue, &tx_msg, 0) == pdPASS) { + PHAL_FDCAN_send(&tx_msg); + is_tx_success = true; + } #endif #ifdef USE_FDCAN3 - while (PHAL_FDCAN_txFifoFree(FDCAN3) && xQueueReceive(q_tx_can[CAN3_IDX], &tx_msg, 0) == pdPASS) { - PHAL_FDCAN_send(&tx_msg); - } + tx_queue = can_tx_queues[BUS_TO_PERIPHERAL(FDCAN3)]; + while (PHAL_FDCAN_txFifoFree(FDCAN3) && xQueueReceive(tx_queue, &tx_msg, 0) == pdPASS) { + PHAL_FDCAN_send(&tx_msg); + is_tx_success = true; + } #endif + } while (is_tx_success); } // FDCAN RX callback - enqueues received messages to the RX queue -// This overrides the weak definition in fdcan.c void PHAL_FDCAN_rxCallback(CanMsgTypeDef_t *msg) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; - if (xQueueSendFromISR(q_rx_can, msg, &xHigherPriorityTaskWoken) != pdPASS) { - can_stats.rx_of++; + if (xQueueSendFromISR(can_rx_queue, msg, &xHigherPriorityTaskWoken) != pdPASS) { + can_stats.rx_overflow++; } portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } -bool CAN_library_init() { - // Initialize TX queues (one per peripheral) +// FDCAN TX callback - wakes the TX task to handle the next message +void PHAL_FDCAN_txCallback(FDCAN_GlobalTypeDef *fdcan) { + (void)fdcan; + CAN_wake_tx_from_ISR(); +} + +bool CAN_init() { + // set up TX queues and filters for each FDCAN peripheral #ifdef USE_FDCAN1 - INIT_QUEUE(q_tx_can1, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); - q_tx_can[CAN1_IDX] = q_tx_can1; + INIT_QUEUE(can1_tx_queue, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); + can_tx_queues[BUS_TO_PERIPHERAL(FDCAN1)] = can1_tx_queue; + FDCAN1_set_filters(); + NVIC_SetPriority(FDCAN1_IT0_IRQn, NVIC_RX_IRQ_PRIO); + NVIC_SetPriority(FDCAN1_IT1_IRQn, NVIC_TX_IRQ_PRIO); #endif #ifdef USE_FDCAN2 - INIT_QUEUE(q_tx_can2, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); - q_tx_can[CAN2_IDX] = q_tx_can2; + INIT_QUEUE(can2_tx_queue, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); + can_tx_queues[BUS_TO_PERIPHERAL(FDCAN2)] = can2_tx_queue; + FDCAN2_set_filters(); + NVIC_SetPriority(FDCAN2_IT0_IRQn, NVIC_RX_IRQ_PRIO); + NVIC_SetPriority(FDCAN2_IT1_IRQn, NVIC_TX_IRQ_PRIO); #endif #ifdef USE_FDCAN3 - INIT_QUEUE(q_tx_can3, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); - q_tx_can[CAN3_IDX] = q_tx_can3; + INIT_QUEUE(can3_tx_queue, CanMsgTypeDef_t, CAN_TX_QUEUE_LENGTH); + can_tx_queues[BUS_TO_PERIPHERAL(FDCAN3)] = can3_tx_queue; + FDCAN3_set_filters(); + NVIC_SetPriority(FDCAN3_IT0_IRQn, NVIC_RX_IRQ_PRIO); + NVIC_SetPriority(FDCAN3_IT1_IRQn, NVIC_TX_IRQ_PRIO); #endif // Initialize RX queue - INIT_QUEUE(q_rx_can, CanMsgTypeDef_t, CAN_RX_QUEUE_LENGTH); + INIT_QUEUE(can_rx_queue, CanMsgTypeDef_t, CAN_RX_QUEUE_LENGTH); // Clear stats can_stats = (can_stats_t) {0}; @@ -323,18 +334,22 @@ bool CAN_library_init() { // Initialize CAN data from generated code CAN_data_init(); + return true; +} + +bool CAN_enable_IRQs() { #ifdef USE_FDCAN1 - FDCAN1_set_filters(); + NVIC_EnableIRQ(FDCAN1_IT0_IRQn); + NVIC_EnableIRQ(FDCAN1_IT1_IRQn); #endif - #ifdef USE_FDCAN2 - FDCAN2_set_filters(); + NVIC_EnableIRQ(FDCAN2_IT0_IRQn); + NVIC_EnableIRQ(FDCAN2_IT1_IRQn); #endif - #ifdef USE_FDCAN3 - FDCAN3_set_filters(); + NVIC_EnableIRQ(FDCAN3_IT0_IRQn); + NVIC_EnableIRQ(FDCAN3_IT1_IRQn); #endif - return true; } diff --git a/common/can_library/can_common.h b/common/can_library/can_common.h index 9f9fa5c6e..5c71986f4 100644 --- a/common/can_library/can_common.h +++ b/common/can_library/can_common.h @@ -14,91 +14,75 @@ #include "common/freertos/freertos.h" #include "common/phal/can.h" -typedef struct { - uint32_t tx_of; // queue overflow - uint32_t tx_fail; // timed out - uint32_t rx_overrun; // fifo overrun -} can_peripheral_stats_t; - -// FreeRTOS -#define CAN_TX_BACKPRESSURE_MS (2) // Wait up to 2ms if FDCAN TX FIFO is full before dropping message -#define CAN_TX_QUEUE_LENGTH (64) // Length of software queue for each CAN peripheral -#define CAN_RX_QUEUE_LENGTH (64) // Length of software queue for received messages - -#define CAN_TX_MAILBOX_CNT (3) -#define CAN_TX_TIMEOUT_MS (15) -#define CAN_TX_BLOCK_TIMEOUT (30 * 16000) - -#define CAN_MAILBOX_HIGH_PRIO 0 -#define CAN_MAILBOX_MED_PRIO 1 -#define CAN_MAILBOX_LOW_PRIO 2 - -/* Standard CAN flag and mask definitions */ -#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */ -#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */ -#define CAN_ERR_FLAG 0x20000000U /* error message frame */ - -/* valid bits in CAN ID for frame formats */ -#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */ -#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */ -#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */ - -#ifndef CAN1_IDX -#define CAN1_IDX 0 -#endif -#ifndef CAN2_IDX -#define CAN2_IDX 1 -#endif -#ifndef CAN3_IDX -#define CAN3_IDX 2 -#endif - -#if defined(STM32G474xx) -// G4 uses FDCAN peripheral -#define GET_PERIPH_IDX(bus) ((bus == FDCAN1) ? CAN1_IDX : ((bus == FDCAN2) ? CAN2_IDX : CAN3_IDX)) -#else -// F4/F7/L4 use bxCAN peripheral -#define GET_PERIPH_IDX(bus) ((bus == CAN1) ? CAN1_IDX : CAN2_IDX) -#endif - #if defined(STM32G474xx) -#define NUM_CAN_PERIPHERALS_MAX 3 +typedef enum : uint8_t { + CAN_PERIPHERAL1 = 0, + CAN_PERIPHERAL2 = 1, + CAN_PERIPHERAL3 = 2, + CAN_NUM_PERIPHERALS = 3, + CAN_PERIPHERAL_INVALID = 0xFF +} CAN_peripheral_t; + +static inline CAN_peripheral_t BUS_TO_PERIPHERAL(FDCAN_GlobalTypeDef *bus) { + if (bus == FDCAN1) return CAN_PERIPHERAL1; + else if (bus == FDCAN2) return CAN_PERIPHERAL2; + else if (bus == FDCAN3) return CAN_PERIPHERAL3; + else return CAN_PERIPHERAL_INVALID; +} #else -#define NUM_CAN_PERIPHERALS_MAX 2 +typedef enum : uint8_t { + CAN_PERIPHERAL1 = 0, + CAN_PERIPHERAL2 = 1, + CAN_NUM_PERIPHERALS = 2, + CAN_PERIPHERAL_INVALID = 0xFF +} CAN_peripheral_t; + +static inline CAN_peripheral_t BUS_TO_PERIPHERAL(CAN_TypeDef *bus) { + if (bus == CAN1) return CAN_PERIPHERAL1; + else if (bus == CAN2) return CAN_PERIPHERAL2; + else return CAN_PERIPHERAL_INVALID; +} #endif typedef struct { - uint32_t rx_of; // queue overflow - can_peripheral_stats_t can_peripheral_stats[NUM_CAN_PERIPHERALS_MAX]; + uint32_t rx_overflow; // software queue overflow + uint32_t tx_overflow; // software queue overflow + // todo: track hardware stats } can_stats_t; -extern can_stats_t can_stats; +extern volatile can_stats_t can_stats; extern volatile uint32_t last_can_rx_time_ms; - -#if defined(STM32F407xx) -// bxCAN uses 3 mailboxes per peripheral -extern QueueHandle_t q_tx_can[][CAN_TX_MAILBOX_CNT]; -#elif defined(STM32G474xx) -// G4/FDCAN uses a single TX queue per peripheral (no mailboxes) -extern QueueHandle_t q_tx_can[]; -#else -#error "Unsupported architecture" -#endif -extern QueueHandle_t q_rx_can; +extern QueueHandle_t can_rx_queue; +extern QueueHandle_t can_tx_queues[CAN_NUM_PERIPHERALS]; void CAN_enqueue_tx(CanMsgTypeDef_t *msg); - -#include "common/can_library/generated/can_router.h" - void CAN_tx_update(); void CAN_rx_update(); -bool CAN_library_init(); - -#if defined(STM32G474xx) -// G4/FDCAN IRQ handling is done in fdcan.c via PHAL_FDCAN_rxCallback -#else -void CAN_handle_irq(CAN_TypeDef *bus, uint8_t fifo); -#endif +bool CAN_init(); +bool CAN_enable_IRQs(); + +#define DEFINE_CAN_TASKS() \ + DEFINE_TASK(CAN_rx_update, 0, osPriorityHigh, STACK_2048); \ + DEFINE_TASK(CAN_tx_update, 0, osPriorityHigh, STACK_2048); + +#define START_CAN_TASKS() \ + START_TASK(CAN_rx_update); \ + START_TASK(CAN_tx_update); \ + CAN_enable_IRQs(); + +#define CAN_TX_QUEUE_LENGTH (16) // Length of software queue for each CAN peripheral +#define CAN_RX_QUEUE_LENGTH (16) // Length of software queue for received messages + +#define NVIC_RX_IRQ_PRIO (6) +#define NVIC_TX_IRQ_PRIO (7) +static_assert( + configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY < NVIC_RX_IRQ_PRIO, + "Do not set an IRQ priority higher (numerically lower) than FreeRTOS to prevent corruption of kernel data structures" +); +static_assert( + NVIC_RX_IRQ_PRIO < NVIC_TX_IRQ_PRIO, + "RX priority should be higher (numerically lower) than TX" +); #endif // CAN_COMMON_H diff --git a/common/can_library/can_rx_logic.drawio.png b/common/can_library/can_rx_logic.drawio.png new file mode 100644 index 000000000..45ea529f0 Binary files /dev/null and b/common/can_library/can_rx_logic.drawio.png differ diff --git a/common/can_library/can_tx_logic.drawio.png b/common/can_library/can_tx_logic.drawio.png new file mode 100644 index 000000000..308c334e7 Binary files /dev/null and b/common/can_library/can_tx_logic.drawio.png differ diff --git a/common/can_library/canpiler/templates/fault_data.c.jinja b/common/can_library/canpiler/templates/fault_data.c.jinja index afa33ebdd..cb54459e6 100644 --- a/common/can_library/canpiler/templates/fault_data.c.jinja +++ b/common/can_library/canpiler/templates/fault_data.c.jinja @@ -40,21 +40,21 @@ const char* const fault_strings[TOTAL_NUM_FAULTS] = { // --- {{ module.node_name }} Callbacks --- #ifdef FAULT_LIB_ENABLED #ifndef CAN_NODE_{{ module.node_name|upper }} -void {{ module.node_name|lower }}_fault_sync_CALLBACK(can_data_t* can_data) { +void {{ module.node_name|lower }}_fault_sync_CALLBACK(volatile can_data_t* can_data) { {% for fault in module.faults %} faults[FAULT_ID_{{ fault.name|upper }}].state = can_data->{{ module.node_name|lower }}_fault_sync.{{ fault.name }} ? FAULT_STATE_LATCHED : FAULT_STATE_CLEAR; {% endfor %} } -void {{ module.node_name|lower }}_fault_event_CALLBACK(can_data_t* can_data) { +void {{ module.node_name|lower }}_fault_event_CALLBACK(volatile can_data_t* can_data) { uint16_t idx = can_data->{{ module.node_name|lower }}_fault_event.idx; if (idx < TOTAL_NUM_FAULTS) { faults[idx].state = (can_data->{{ module.node_name|lower }}_fault_event.state != 0) ? FAULT_STATE_LATCHED : FAULT_STATE_CLEAR; } } #else -void {{ module.node_name|lower }}_fault_sync_CALLBACK(can_data_t* can_data) { (void)can_data; } -void {{ module.node_name|lower }}_fault_event_CALLBACK(can_data_t* can_data) { (void)can_data; } +void {{ module.node_name|lower }}_fault_sync_CALLBACK(volatile can_data_t* can_data) { (void)can_data; } +void {{ module.node_name|lower }}_fault_event_CALLBACK(volatile can_data_t* can_data) { (void)can_data; } #endif #endif diff --git a/common/can_library/canpiler/templates/node_header.h.jinja b/common/can_library/canpiler/templates/node_header.h.jinja index 7425ec388..bd0f6b680 100644 --- a/common/can_library/canpiler/templates/node_header.h.jinja +++ b/common/can_library/canpiler/templates/node_header.h.jinja @@ -49,12 +49,12 @@ typedef struct { {% endfor %} } can_data_t; -extern can_data_t can_data; +extern volatile can_data_t can_data; // Callback Declarations {% for rx_msg, periph, bus in rx_msgs %} {% if rx_msg.callback %} -extern void {{ rx_msg.name }}_CALLBACK(can_data_t* can_data); +extern void {{ rx_msg.name }}_CALLBACK(volatile can_data_t* can_data); {% endif %} {% endfor %} @@ -100,7 +100,7 @@ static bool is_{{ msg.name }}_stale() { // Data initialization static inline void CAN_data_init() { - memset(&can_data, 0, sizeof(can_data)); + memset((void *)&can_data, 0, sizeof(can_data)); // Underflow the last_rx timers to ensure messages are stale until first rx // Setup the stale function pointers {% for rx_msg, periph, bus in rx_msgs %} diff --git a/common/can_library/configs/external_nodes/DAQ.json b/common/can_library/configs/external_nodes/DAQ.json new file mode 100644 index 000000000..d5fb84188 --- /dev/null +++ b/common/can_library/configs/external_nodes/DAQ.json @@ -0,0 +1,10 @@ +{ + "node_name": "DAQ", + "bus_name": "VCAN", + "tx": [], + "rx": [ + { + "msg_name": "gps_time" + } + ] +} \ No newline at end of file diff --git a/common/can_library/configs/nodes/DAQ.json b/common/can_library/configs/nodes/DAQ.json deleted file mode 100644 index 02843f44a..000000000 --- a/common/can_library/configs/nodes/DAQ.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "node_name": "DAQ", - "busses": { - "VCAN": { - "peripheral": "CAN2", - "accept_all_messages": true, - "tx": [ - { - "msg_name": "daq_can_stats", - "msg_desc": "CAN diagnostics", - "signals": [ - { - "sig_name": "can_tx_overflow", - "type": "uint16_t", - "sig_desc": "CAN Tx queue overflow count" - }, - { - "sig_name": "can_tx_fail", - "type": "uint16_t", - "sig_desc": "CAN Tx fail count (due to timeout in mailbox)" - }, - { - "sig_name": "can_rx_overflow", - "type": "uint16_t", - "sig_desc": "CAN Rx queue overflow count" - }, - { - "sig_name": "can_rx_overrun", - "type": "uint16_t", - "sig_desc": "CAN Rx FIFO overrun count" - } - ], - "msg_period": 1000, - "msg_priority": 3 - }, - { - "msg_name": "daq_queue_stats", - "msg_desc": "Queue diagnostics", - "signals": [ - { - "sig_name": "can1_rx_overflow", - "type": "uint16_t", - "sig_desc": "CAN RX queue overflow" - }, - { - "sig_name": "sd_rx_overflow", - "type": "uint16_t", - "sig_desc": "SD RX queue overflow" - } - ], - "msg_period": 1000, - "msg_priority": 3 - }, - { - "msg_name": "daq_version", - "msg_desc": "DAQ version", - "signals": [ - { - "sig_name": "git_hash", - "type": "uint32_t", - "sig_desc": "int representation of git short hash" - } - ], - "msg_priority": 5, - "msg_period": 5000 - } - ], - "rx": [] - }, - "MCAN": { - "peripheral": "CAN1", - "accept_all_messages": true - } - }, - "fault_library_enabled": false -} \ No newline at end of file diff --git a/common/heartbeat/heartbeat.c b/common/heartbeat/heartbeat.c index 2c797a5f3..6c8b30f2b 100644 --- a/common/heartbeat/heartbeat.c +++ b/common/heartbeat/heartbeat.c @@ -51,7 +51,7 @@ void heartbeat_task(status_leds_t *leds) { case HEARTBEAT_STATE_NORMAL: PHAL_toggleGPIO(leds->heartbeat_port, leds->heartbeat_pin); - bool is_can_ok = (getTick() - last_can_rx_time_ms < CONN_LED_TIMEOUT_MS); + bool is_can_ok = (getTick() - last_can_rx_time_ms) < CONN_LED_TIMEOUT_MS; PHAL_writeGPIO(leds->connection_port, leds->connection_pin, is_can_ok); break; } diff --git a/common/phal_F4_F7/can/can.c b/common/phal_F4_F7/can/can.c index e830d1dd9..7010b6328 100644 --- a/common/phal_F4_F7/can/can.c +++ b/common/phal_F4_F7/can/can.c @@ -132,6 +132,9 @@ bool PHAL_initCAN(CAN_TypeDef* bus, bool test_mode, uint32_t bit_rate) { bus->IER |= CAN_IER_FMPIE0; bus->IER |= CAN_IER_FMPIE1; + // Enable TX mailbox empty interrupt + bus->IER |= CAN_IER_TMEIE; + // Enter NORMAL mode bus->MCR &= ~CAN_MCR_INRQ; while ((bus->MSR & CAN_MSR_INAK) && ++timeout < PHAL_CAN_INIT_TIMEOUT) @@ -221,6 +224,25 @@ bool PHAL_txMailboxFree(CAN_TypeDef* bus, uint8_t mbx) { } } +bool PHAL_anyTxMailboxFree(CAN_TypeDef* bus) { + return bus->TSR & (CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2); +} + +bool PHAL_getFreeTxMailbox(CAN_TypeDef* bus, uint8_t* mbx) { + if (bus->TSR & CAN_TSR_TME0) { + *mbx = 0; + return true; + } else if (bus->TSR & CAN_TSR_TME1) { + *mbx = 1; + return true; + } else if (bus->TSR & CAN_TSR_TME2) { + *mbx = 2; + return true; + } else { + return false; // No free mailbox + } +} + void PHAL_txCANAbort(CAN_TypeDef* bus, uint8_t mbx) { switch (mbx) { case 0: diff --git a/common/phal_F4_F7/can/can.h b/common/phal_F4_F7/can/can.h index 2c1edde17..92f5de9c0 100644 --- a/common/phal_F4_F7/can/can.h +++ b/common/phal_F4_F7/can/can.h @@ -69,5 +69,7 @@ bool PHAL_txCANMessage(CanMsgTypeDef_t* msg, uint8_t mbx); bool PHAL_txMailboxFree(CAN_TypeDef* bus, uint8_t mbx); void PHAL_txCANAbort(CAN_TypeDef* bus, uint8_t mbx); bool PHAL_rxCANMessage(CAN_TypeDef *bus, uint8_t fifo, CanMsgTypeDef_t *msg); +bool PHAL_anyTxMailboxFree(CAN_TypeDef* bus); +bool PHAL_getFreeTxMailbox(CAN_TypeDef* bus, uint8_t* mbx); #endif // _PHAL_CAN_H \ No newline at end of file diff --git a/common/phal_G4/fdcan/fdcan.c b/common/phal_G4/fdcan/fdcan.c index 66e08ab59..84a15b65b 100644 --- a/common/phal_G4/fdcan/fdcan.c +++ b/common/phal_G4/fdcan/fdcan.c @@ -182,14 +182,15 @@ bool PHAL_FDCAN_init(FDCAN_GlobalTypeDef *fdcan, bool test_mode, uint32_t bit_ra // FIFOs fdcan->TXBC &= ~FDCAN_TXBC_TFQM; // Tx FIFO mode - // Clear interrupt flags - fdcan->IR |= 0xFFFFFFFF; - // Enable RX FIFO0 interrupts - fdcan->IE |= FDCAN_IE_RF0NE // New message - | FDCAN_IE_RF0FE // FIFO full (optional) - | FDCAN_IE_RF0LE; // Message lost (optional) - fdcan->ILS = 0; // Route to interrupt line 0 - fdcan->ILE = FDCAN_ILE_EINT0; // Enable line 0 + // Route RX to line 0, TC to line 1 + fdcan->ILS = 0; // default all to line 0 + fdcan->ILS |= FDCAN_ILS_SMSG; // route the SMSG group to line 1 + + fdcan->IE |= FDCAN_IE_RF0NE; // New message + fdcan->IE |= FDCAN_IE_TCE; // Transmission completed + + // Enable both interrupt lines + fdcan->ILE = FDCAN_ILE_EINT0 | FDCAN_ILE_EINT1; // Mode fdcan->CCCR &= ~(FDCAN_CCCR_MON | FDCAN_CCCR_TEST | FDCAN_CCCR_ASM); @@ -336,39 +337,57 @@ static int PHAL_FDCAN_getRxMessage(FDCAN_GlobalTypeDef *fdcan, CanMsgTypeDef_t * return 0; } -void PHAL_FDCAN_RX_IRQHandler(FDCAN_GlobalTypeDef *fdcan) { - uint32_t ir = fdcan->IR; - if (ir & FDCAN_IR_RF0L) { - fdcan->IR = FDCAN_IR_RF0L; - // TODO message lost - } +[[gnu::always_inline]] +static inline void PHAL_FDCAN_RX_IRQHandler(FDCAN_GlobalTypeDef *fdcan) { + if (fdcan->IR & FDCAN_IR_RF0N) { + fdcan->IR = FDCAN_IR_RF0N; - if (ir & FDCAN_IR_RF0F) { - fdcan->IR = FDCAN_IR_RF0F; - // TODO FIFO full + CanMsgTypeDef_t rx; + while (PHAL_FDCAN_getRxMessage(fdcan, &rx) == 0) { + PHAL_FDCAN_rxCallback(&rx); // or enqueue + } } +} - if (ir & FDCAN_IR_RF0N) { - CanMsgTypeDef_t m; - while (PHAL_FDCAN_getRxMessage(fdcan, &m) == 0) { - PHAL_FDCAN_rxCallback(&m); // or enqueue - } - fdcan->IR = FDCAN_IR_RF0N; // clear after draining +[[gnu::always_inline]] +static inline void PHAL_FDCAN_TX_IRQHandler(FDCAN_GlobalTypeDef *fdcan) { + if (fdcan->IR & FDCAN_IR_TC) { + fdcan->IR = FDCAN_IR_TC; + PHAL_FDCAN_txCallback(fdcan); } } -void __attribute__((weak)) PHAL_FDCAN_rxCallback(CanMsgTypeDef_t *msg) { +[[gnu::weak]] +void PHAL_FDCAN_txCallback(FDCAN_GlobalTypeDef *fdcan) { + (void)fdcan; +} + +[[gnu::weak]] +void PHAL_FDCAN_rxCallback(CanMsgTypeDef_t *msg) { (void)msg; } +// Setup IT0 for RX and IT1 for TX void FDCAN1_IT0_IRQHandler(void) { PHAL_FDCAN_RX_IRQHandler(FDCAN1); } +void FDCAN1_IT1_IRQHandler(void) { + PHAL_FDCAN_TX_IRQHandler(FDCAN1); +} + void FDCAN2_IT0_IRQHandler(void) { PHAL_FDCAN_RX_IRQHandler(FDCAN2); } +void FDCAN2_IT1_IRQHandler(void) { + PHAL_FDCAN_TX_IRQHandler(FDCAN2); +} + void FDCAN3_IT0_IRQHandler(void) { PHAL_FDCAN_RX_IRQHandler(FDCAN3); } + +void FDCAN3_IT1_IRQHandler(void) { + PHAL_FDCAN_TX_IRQHandler(FDCAN3); +} \ No newline at end of file diff --git a/common/phal_G4/fdcan/fdcan.h b/common/phal_G4/fdcan/fdcan.h index 61a317e30..c54b46390 100644 --- a/common/phal_G4/fdcan/fdcan.h +++ b/common/phal_G4/fdcan/fdcan.h @@ -14,14 +14,17 @@ typedef struct { } CanMsgTypeDef_t; bool PHAL_FDCAN_init(FDCAN_GlobalTypeDef *fdcan, bool test_mode, uint32_t bit_rate); -void PHAL_FDCAN_setFilters(FDCAN_GlobalTypeDef *fdcan, - uint32_t *sid_list, - uint32_t num_sid, - uint32_t *xid_list, - uint32_t num_xid); +void PHAL_FDCAN_setFilters( + FDCAN_GlobalTypeDef *fdcan, + uint32_t *sid_list, + uint32_t num_sid, + uint32_t *xid_list, + uint32_t num_xid +); void PHAL_FDCAN_send(CanMsgTypeDef_t *msg); bool PHAL_FDCAN_txFifoFree(FDCAN_GlobalTypeDef *fdcan); extern void PHAL_FDCAN_rxCallback(CanMsgTypeDef_t *msg); +extern void PHAL_FDCAN_txCallback(FDCAN_GlobalTypeDef *fdcan); #define MAX_NUM_XID_FILTER (8) #define MAX_NUM_SID_FILTER (28) diff --git a/source/a_box/main.c b/source/a_box/main.c index f8facd3d8..36f85c4cf 100644 --- a/source/a_box/main.c +++ b/source/a_box/main.c @@ -120,8 +120,7 @@ void charging_fsm_periodic(void); // Thread Defines DEFINE_TASK(bms_task, 200, osPriorityHigh, STACK_2048); -DEFINE_TASK(CAN_rx_update, 0, osPriorityHigh, STACK_2048); -DEFINE_TASK(CAN_tx_update, 2, osPriorityNormal, STACK_2048); +DEFINE_CAN_TASKS(); DEFINE_TASK(check_faults, 10, osPriorityNormal, STACK_512); DEFINE_TASK(charging_fsm_periodic, ELCON_COMMAND_PERIOD_MS, osPriorityNormal, STACK_512); DEFINE_TASK(fault_library_periodic, A_BOX_FAULT_SYNC_PERIOD_MS, osPriorityNormal, STACK_1024); @@ -161,19 +160,14 @@ int main(void) { if (false == PHAL_FDCAN_init(FDCAN2, false, CCAN_BAUD_RATE)) { HardFault_Handler(); } - NVIC_SetPriority(FDCAN1_IT0_IRQn, 5); - NVIC_SetPriority(FDCAN2_IT0_IRQn, 5); - - NVIC_EnableIRQ(FDCAN1_IT0_IRQn); - NVIC_EnableIRQ(FDCAN2_IT0_IRQn); - CAN_library_init(); + + CAN_init(); // Kernel initalization osKernelInitialize(); START_TASK(bms_task); - START_TASK(CAN_rx_update); - START_TASK(CAN_tx_update); + START_CAN_TASKS(); START_TASK(check_faults); START_TASK(fault_library_periodic); START_TASK(report_telemetry); diff --git a/source/daq/CMakeLists.txt b/source/daq/CMakeLists.txt index 0f5455559..ada12f02a 100644 --- a/source/daq/CMakeLists.txt +++ b/source/daq/CMakeLists.txt @@ -1,7 +1,5 @@ -create_can_node_lib(DAQ "CMSIS_F407;FREERTOS_F407") - add_firmware_component( NAME daq LINKER_SCRIPT "STM32F407VGTx_FLASH" - LIBS "can_node_DAQ;CMSIS_F407;PHAL_F407;FREERTOS_F407;HEARTBEAT" + LIBS "CMSIS_F407;PHAL_F407;FREERTOS_F407;HEARTBEAT" ) diff --git a/source/daq/daq_hub/daq_eth.c b/source/daq/daq_hub/daq_eth.c index fbca7652f..d273da0b7 100644 --- a/source/daq/daq_hub/daq_eth.c +++ b/source/daq/daq_hub/daq_eth.c @@ -18,7 +18,6 @@ #include "main.h" #include "w5500/socket.h" #include "w5500/wizchip_conf.h" -#include "common/can_library/generated/DAQ.h" static int8_t eth_init(void); static int8_t eth_get_link_up(void); diff --git a/source/daq/main.c b/source/daq/main.c index ebdf10f83..4de06d249 100644 --- a/source/daq/main.c +++ b/source/daq/main.c @@ -1,6 +1,5 @@ #include "main.h" #include "spmc.h" -#include "common/can_library/generated/DAQ.h" #include "common/freertos/freertos.h" #include "common/phal/can.h" #include "common/phal/gpio.h" @@ -10,6 +9,7 @@ #include "common/phal/usart.h" #include "daq_spi.h" #include "common/heartbeat/heartbeat.h" +#include "common/can_library/generated/VCAN.h" GPIOInitConfig_t gpio_config[] = { // LEDs @@ -129,6 +129,8 @@ SPMC_t spmc; timestamped_frame_t buf; DEFINE_MUTEX(spi1_lock); +volatile uint32_t last_can_rx_time_ms; + static void configure_interrupts(void); void shutdown(void); @@ -189,6 +191,16 @@ static void configure_interrupts(void) { NVIC_EnableIRQ(EXTI15_10_IRQn); } +/* Standard CAN flag and mask definitions */ +#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */ +#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */ +#define CAN_ERR_FLAG 0x20000000U /* error message frame */ + +/* valid bits in CAN ID for frame formats */ +#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */ +#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */ +#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */ + static inline void can_rx_irq_handler(CAN_TypeDef* can_h) { portBASE_TYPE xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; @@ -202,6 +214,8 @@ static inline void can_rx_irq_handler(CAN_TypeDef* can_h) { if (can_h->RF0R & CAN_RF0R_FMP0_Msk) // Release message pending { + last_can_rx_time_ms = getTick(); + timestamped_frame_t* rx = &buf; rx->ticks_ms = getTick(); if (can_h == CAN1) { diff --git a/source/daq/spmc/SD_timing_diagram.png b/source/daq/spmc/SD_timing_diagram.png deleted file mode 100644 index 69cb2b744..000000000 Binary files a/source/daq/spmc/SD_timing_diagram.png and /dev/null differ diff --git a/source/dashboard/main.c b/source/dashboard/main.c index 2af07ff64..9024dd89d 100644 --- a/source/dashboard/main.c +++ b/source/dashboard/main.c @@ -146,8 +146,7 @@ ALLOCATE_STRBUF(lcd_tx_buf, 2048); // Thread Defines DEFINE_TASK(pedalsPeriodic, PEDALS_PERIOD_MS, osPriorityHigh, STACK_1024); -DEFINE_TASK(CAN_rx_update, 0, osPriorityHigh, STACK_2048); -DEFINE_TASK(CAN_tx_update, 2, osPriorityNormal, STACK_2048); // leave stack at 2048 +DEFINE_CAN_TASKS(); DEFINE_TASK(fault_library_periodic, DASHBOARD_FAULT_SYNC_PERIOD_MS, osPriorityNormal, STACK_1024); DEFINE_TASK(updateTelemetryPages, 100, osPriorityNormal, STACK_1024); DEFINE_TASK(service_start_button, START_BUTTON_PERIOD_MS, osPriorityLow, STACK_512); @@ -178,9 +177,8 @@ int main(void) { if (false == PHAL_FDCAN_init(FDCAN2, false, VCAN_BAUD_RATE)) { HardFault_Handler(); } - NVIC_EnableIRQ(FDCAN2_IT0_IRQn); - NVIC_SetPriority(FDCAN2_IT0_IRQn, 5); - CAN_library_init(); + + CAN_init(); config_button_irqs(); LCD_init(LCD_BAUD_RATE); @@ -189,8 +187,7 @@ int main(void) { osKernelInitialize(); START_TASK(pedalsPeriodic); - START_TASK(CAN_rx_update); - START_TASK(CAN_tx_update); + START_CAN_TASKS(); START_TASK(fault_library_periodic); START_TASK(updateTelemetryPages); START_TASK(service_start_button); diff --git a/source/driveline/main.c b/source/driveline/main.c index 603839653..366d007d9 100644 --- a/source/driveline/main.c +++ b/source/driveline/main.c @@ -151,8 +151,7 @@ extern void HardFault_Handler(); void shockpot_thread(); void oil_temps_thread(); -DEFINE_TASK(CAN_rx_update, 0, osPriorityHigh, STACK_2048); -DEFINE_TASK(CAN_tx_update, 2, osPriorityNormal, STACK_2048); // leave stack at 2048 +DEFINE_CAN_TASKS(); DEFINE_TASK(shockpot_thread, FRONT_SHOCKPOTS_PERIOD_MS, osPriorityNormal, STACK_512); DEFINE_TASK(oil_temps_thread, FRONT_OIL_TEMPS_PERIOD_MS, osPriorityNormal, STACK_512); DEFINE_HEARTBEAT_TASK(nullptr); @@ -203,16 +202,12 @@ int main(void) { PHAL_startADC(&adc3_config); PHAL_startADC(&adc4_config); - - CAN_library_init(); - NVIC_SetPriority(FDCAN2_IT0_IRQn, 6); - NVIC_EnableIRQ(FDCAN2_IT0_IRQn); + CAN_init(); // Software Initalization osKernelInitialize(); - START_TASK(CAN_rx_update); - START_TASK(CAN_tx_update); + START_CAN_TASKS(); START_TASK(shockpot_thread); START_TASK(oil_temps_thread); START_HEARTBEAT_TASK(); diff --git a/source/f4_testing/canpiler.c b/source/f4_testing/canpiler.c index 6ba5067ba..5f0a0fbb6 100644 --- a/source/f4_testing/canpiler.c +++ b/source/f4_testing/canpiler.c @@ -37,8 +37,7 @@ void send_periodic() { CAN_SEND_pdu_version(GIT_HASH); } -DEFINE_TASK(CAN_rx_update, 0, osPriorityHigh, STACK_2048); -DEFINE_TASK(CAN_tx_update, 2, osPriorityNormal, STACK_2048); +DEFINE_CAN_TASKS(); DEFINE_TASK(send_periodic, 10, osPriorityNormal, 1024); int main() { @@ -54,16 +53,11 @@ int main() { HardFault_Handler(); } - CAN_library_init(); - - // NVIC - NVIC_SetPriority(CAN1_RX0_IRQn, 6); - NVIC_EnableIRQ(CAN1_RX0_IRQn); + CAN_init(); osKernelInitialize(); - START_TASK(CAN_rx_update); - START_TASK(CAN_tx_update); + START_CAN_TASKS(); START_TASK(send_periodic); osKernelStart(); @@ -71,10 +65,6 @@ int main() { return 0; } -void CAN1_RX0_IRQHandler() { - CAN_handle_irq(CAN1, 0); -} - // just to avoid linker error void cooling_driver_request_CALLBACK(can_data_t* can_data) { (void)can_data; // unused diff --git a/source/g4_testing/canpiler.c b/source/g4_testing/canpiler.c index 7805924cf..9844f8204 100644 --- a/source/g4_testing/canpiler.c +++ b/source/g4_testing/canpiler.c @@ -47,8 +47,7 @@ void send_periodic() { CAN_SEND_abox_version(GIT_HASH); } -DEFINE_TASK(CAN_rx_update, 0, osPriorityHigh, STACK_2048); -DEFINE_TASK(CAN_tx_update, 2, osPriorityNormal, STACK_2048); +DEFINE_CAN_TASKS(); DEFINE_TASK(send_periodic, 10, osPriorityNormal, 1024); int main() { @@ -64,16 +63,11 @@ int main() { HardFault_Handler(); } - CAN_library_init(); - - // NVIC - NVIC_SetPriority(FDCAN1_IT0_IRQn, 6); - NVIC_EnableIRQ(FDCAN1_IT0_IRQn); + CAN_init(); osKernelInitialize(); - START_TASK(CAN_rx_update); - START_TASK(CAN_tx_update); + START_CAN_TASKS(); START_TASK(send_periodic); osKernelStart(); diff --git a/source/g4_testing/izze_imu_config.c b/source/g4_testing/izze_imu_config.c index 2c7b4fe02..2561202c1 100644 --- a/source/g4_testing/izze_imu_config.c +++ b/source/g4_testing/izze_imu_config.c @@ -63,8 +63,7 @@ void config_imu() { ); } -DEFINE_TASK(CAN_rx_update, 0, osPriorityHigh, STACK_2048); -DEFINE_TASK(CAN_tx_update, 2, osPriorityNormal, STACK_2048); +DEFINE_CAN_TASKS(); DEFINE_TASK(config_imu, IZZE_IMU_CONFIG_PERIOD_MS, osPriorityNormal, 1024); int main() { @@ -83,16 +82,11 @@ int main() { HardFault_Handler(); } - CAN_library_init(); - - // NVIC - NVIC_SetPriority(FDCAN2_IT0_IRQn, 6); - NVIC_EnableIRQ(FDCAN2_IT0_IRQn); + CAN_init(); osKernelInitialize(); - START_TASK(CAN_rx_update); - START_TASK(CAN_tx_update); + START_CAN_TASKS(); START_TASK(config_imu); osKernelStart(); diff --git a/source/main_module/main.c b/source/main_module/main.c index 20a80d40c..9ddfed93e 100644 --- a/source/main_module/main.c +++ b/source/main_module/main.c @@ -88,8 +88,7 @@ void AMK_task() { } // Thread Defines -DEFINE_TASK(CAN_rx_update, 0, osPriorityHigh, STACK_2048); -DEFINE_TASK(CAN_tx_update, 2, osPriorityHigh, STACK_2048); +DEFINE_CAN_TASKS(); DEFINE_TASK(fsm_periodic, 20, osPriorityNormal, STACK_2048); DEFINE_TASK(AMK_task, 15, osPriorityNormal, STACK_1024); DEFINE_TASK(fault_library_periodic, MAIN_MODULE_FAULT_SYNC_PERIOD_MS, osPriorityNormal, STACK_1024); @@ -110,20 +109,15 @@ int main(void) { if (false == PHAL_FDCAN_init(FDCAN3, false, MCAN_BAUD_RATE)) { HardFault_Handler(); } - NVIC_SetPriority(FDCAN2_IT0_IRQn, 5); - NVIC_SetPriority(FDCAN3_IT0_IRQn, 5); - - NVIC_EnableIRQ(FDCAN2_IT0_IRQn); - NVIC_EnableIRQ(FDCAN3_IT0_IRQn); - CAN_library_init(); + + CAN_init(); car_init(); // Software Initialization osKernelInitialize(); - START_TASK(CAN_rx_update); - START_TASK(CAN_tx_update); + START_CAN_TASKS(); START_TASK(fsm_periodic); START_TASK(AMK_task); START_TASK(fault_library_periodic); diff --git a/source/pdu/main.c b/source/pdu/main.c index 4c399ea04..7730aa781 100644 --- a/source/pdu/main.c +++ b/source/pdu/main.c @@ -286,8 +286,7 @@ void send_iv_readings() { } // Thread Defines -DEFINE_TASK(CAN_rx_update, 0, osPriorityHigh, STACK_2048); -DEFINE_TASK(CAN_tx_update, 5, osPriorityHigh, STACK_1024); +DEFINE_CAN_TASKS(); DEFINE_TASK(autoSwitchPeriodic, 15, osPriorityNormal, STACK_512); DEFINE_TASK(update_cooling_periodic, 100, osPriorityNormal, STACK_1024); DEFINE_TASK(LED_periodic, 500, osPriorityLow, STACK_512); @@ -319,9 +318,7 @@ int main() { HardFault_Handler(); } - CAN_library_init(); - NVIC_SetPriority(CAN1_RX0_IRQn, 6); - NVIC_EnableIRQ(CAN1_RX0_IRQn); + CAN_init(); if (!PHAL_SPI_init(&spi_config)) { HardFault_Handler(); @@ -348,8 +345,7 @@ int main() { osKernelInitialize(); - START_TASK(CAN_rx_update); - START_TASK(CAN_tx_update); + START_CAN_TASKS(); START_TASK(autoSwitchPeriodic); START_TASK(update_cooling_periodic); START_TASK(LED_periodic); @@ -365,10 +361,6 @@ int main() { return 0; } -void CAN1_RX0_IRQHandler() { - CAN_handle_irq(CAN1, 0); -} - // todo reboot on hardfault void HardFault_Handler() { __disable_irq(); diff --git a/source/torque_vector/main.c b/source/torque_vector/main.c index 969a80deb..33756e03f 100644 --- a/source/torque_vector/main.c +++ b/source/torque_vector/main.c @@ -58,8 +58,7 @@ void ledblink() { } // Thread Defines -DEFINE_TASK(CAN_rx_update, 0, osPriorityHigh, STACK_2048); -DEFINE_TASK(CAN_tx_update, 15, osPriorityNormal, STACK_2048); +DEFINE_CAN_TASKS(); DEFINE_HEARTBEAT_TASK(nullptr); int main(void) { @@ -75,15 +74,12 @@ int main(void) { HardFault_Handler(); } - CAN_library_init(); - NVIC_SetPriority(FDCAN2_IT0_IRQn, 6); - NVIC_EnableIRQ(FDCAN2_IT0_IRQn); + CAN_init(); // Software Initialization osKernelInitialize(); - START_TASK(CAN_rx_update); - START_TASK(CAN_tx_update); + START_CAN_TASKS(); START_HEARTBEAT_TASK(); // no way home