From ced8dd0ab78020fc796bb82ce481afb3210a0058 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Tue, 12 May 2026 01:06:11 -0700 Subject: [PATCH 01/20] Initial changeover, totally untested Signed-off-by: Daniel Hansen --- ECU/Application/Inc/StateData.h | 2 +- ECU/Application/Inc/StateTicks.h | 2 +- ECU/Application/Inc/ecu_can.h | 140 --- ECU/Application/Inc/ecu_can_platform_deps.h | 18 - ECU/Application/Src/CANutils.c | 2 +- ECU/Application/Src/Lights.c | 2 +- ECU/Application/Src/StateTicks.c | 2 +- ECU/Application/Src/ecu_can.c | 1075 ------------------- ECU/CMakeLists.txt | 3 +- ECU/Core/Src/main.c | 3 +- ECU/Core/Src/stm32g4xx_it.c | 12 +- 11 files changed, 9 insertions(+), 1252 deletions(-) delete mode 100644 ECU/Application/Inc/ecu_can.h delete mode 100644 ECU/Application/Inc/ecu_can_platform_deps.h delete mode 100644 ECU/Application/Src/ecu_can.c diff --git a/ECU/Application/Inc/StateData.h b/ECU/Application/Inc/StateData.h index a0debf5cf..27fc53c48 100644 --- a/ECU/Application/Inc/StateData.h +++ b/ECU/Application/Inc/StateData.h @@ -3,7 +3,7 @@ #include "GRCAN_MSG_DATA.h" #include "StateMachine.h" -#include "ecu_can.h" +#include "can.h" #ifndef _STATEDATA_H_ #define _STATEDATA_H_ diff --git a/ECU/Application/Inc/StateTicks.h b/ECU/Application/Inc/StateTicks.h index 6f136f15c..636cd77a1 100644 --- a/ECU/Application/Inc/StateTicks.h +++ b/ECU/Application/Inc/StateTicks.h @@ -1,7 +1,7 @@ #include "StateData.h" #include "StateMachine.h" #include "adc.h" -#include "ecu_can.h" +#include "can.h" #ifndef _STATE_TICKS_H_ #define _STATE_TICKS_H_ diff --git a/ECU/Application/Inc/ecu_can.h b/ECU/Application/Inc/ecu_can.h deleted file mode 100644 index 6b398748e..000000000 --- a/ECU/Application/Inc/ecu_can.h +++ /dev/null @@ -1,140 +0,0 @@ -#ifndef CAN_H -#define CAN_H - -// Supported STM32 Families -#ifdef STM32G4 -#elif defined(STM32L4) -#elif defined(STM32U5) -#error "Unsupported STM32 Family" -#endif - -#include "can_cfg.h" -#include "ecu_can_platform_deps.h" -// #include "StateData.h" - -// #include "circularBuffer.h" -#include - -// RX Callback must perform a deep copy of the data -// -typedef void (*CAN_RXCallback)(uint32_t ID, void *data, uint32_t size); -typedef struct { - // can baud rate is set by fdcan prescaler and RCC clock configurations - FDCAN_GlobalTypeDef *fdcan_instance; // Base address of FDCAN peripheral in memory (FDCAN1, FDCAN2, FDCAN3 macros) - - FDCAN_InitTypeDef hal_fdcan_init; - CAN_RXCallback rx_callback; - uint32_t rx_interrupt_priority; - uint32_t tx_interrupt_priority; - - // Circular Buffer - // uint32_t tx_buffer_capacity; - - GPIO_TypeDef *rx_gpio; // Instance name, like GPIOA, GPIOB, etc. - GPIO_InitTypeDef init_rx_gpio; // GPIO Parameters - set correct Alternate Function, no pullup/pulldown, high/very_high frequency - GPIO_TypeDef *tx_gpio; - GPIO_InitTypeDef init_tx_gpio; - - // additional parameters -} CANConfig; - -#define FDCAN_MAX_DATA_BYTES 64 -// TODO - allow user to send data without needing to construct a header for the buffer -// TODO: G4 tests are dependent on the System clock configuration?? -typedef struct { - FDCAN_TxHeaderTypeDef tx_header; - uint8_t data[FDCAN_MAX_DATA_BYTES]; -} FDCANTxMessage; -typedef struct { - FDCAN_RxHeaderTypeDef rx_header; - uint8_t data[FDCAN_MAX_DATA_BYTES]; -} FDCANRxMessage; - -// FDCAN peripheral for STM32G4 -typedef struct { - FDCAN_HandleTypeDef *hal_fdcanP; - - // TX buffer - FDCANTxMessage *const tx_buffer; - volatile uint32_t tx_capacity; - volatile uint32_t tx_tail; - volatile uint32_t tx_elements; - - // RX Callback - CAN_RXCallback rx_callback; - - uint8_t rx_interrupt_priority; // only 4 bits - uint8_t tx_interrupt_priority; - - // for release - GPIO_TypeDef *rx_gpio; - uint32_t rx_pin; - GPIO_TypeDef *tx_gpio; - uint32_t tx_pin; - // uint32_t Clock_Source; - - // state - bool init; - bool started; - - // error states - uint32_t lost_rx; -} CANHandle; - -typedef enum { - CAN_SUCCESS = 0, - CAN_ERROR -} CAN_STATUS; - -CANHandle *can_init(const CANConfig *config); // user must supply an rx callback function -CAN_STATUS can_start(CANHandle *handle); -CAN_STATUS can_stop(CANHandle *handle); -CAN_STATUS can_send(CANHandle *handle, FDCANTxMessage *buffer); -CAN_STATUS can_release(CANHandle *handle); // deinit circular buffer and turn off can peripheral and gpios -CAN_STATUS can_add_filter(CANHandle *handle, FDCAN_FilterTypeDef *filter); -CAN_STATUS can_enqueue(CANHandle *handle, FDCANTxMessage *message); // adds to software buffer, returns error if full - -// pass in a buffer to store the status string -// int can_info(char* ); - -// alternatively use -// HAL_FDCAN_ConfigGlobalFilter() //important to accept nonmatching frames into -// HAL_FDCAN_ConfigFilter() - -#define GPIOx_CLK_ENABLE(GPIOX) \ - do { \ - if (GPIOX == GPIOA) \ - __HAL_RCC_GPIOA_CLK_ENABLE(); \ - else if (GPIOX == GPIOB) \ - __HAL_RCC_GPIOB_CLK_ENABLE(); \ - else if (GPIOX == GPIOD) \ - __HAL_RCC_GPIOD_CLK_ENABLE(); \ - else \ - LOGOMATIC("BAD FDCAN GPIO Port"); \ - } while (0) - -#define GPIOx_CLK_DISABLE(GPIOX) \ - do { \ - if (GPIOX == GPIOA) \ - __HAL_RCC_GPIOA_CLK_DISABLE(); \ - else if (GPIOX == GPIOB) \ - __HAL_RCC_GPIOB_CLK_DISABLE(); \ - else if (GPIOX == GPIOD) \ - __HAL_RCC_GPIOD_CLK_DISABLE(); \ - else \ - LOGOMATIC("BAD FDCAN GPIO Port"); \ - } while (0) - -// doesn't need a handle, CAN cores share peripheral clock -void can_set_clksource(uint32_t clksource); // ex. LL_RCC_FDCAN_CLKSOURCE_PCLK1 for STM32G474RE - -// default Configuration helpers -int get_cfg(FDCAN_GlobalTypeDef *instance, CAN_RXCallback callback, CANConfig *out_cfg, uint32_t FDCAN_Mode, uint32_t numStdFilters, uint32_t numExtFilters); - -// converts CAN FD TxHeader DataLength Field -static const uint8_t CANFD_DLCtoBytes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64}; - -void CAN_Timer_Start(void); -void can_tx_dequeue_helper(CANHandle *handle); - -#endif diff --git a/ECU/Application/Inc/ecu_can_platform_deps.h b/ECU/Application/Inc/ecu_can_platform_deps.h deleted file mode 100644 index 986329ac0..000000000 --- a/ECU/Application/Inc/ecu_can_platform_deps.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef ECU_CAN_PLATFORM_DEPS_H -#define ECU_CAN_PLATFORM_DEPS_H - -#if defined(STM32G4) -#include "stm32g4xx_hal.h" -#include "stm32g4xx_hal_fdcan.h" -#include "stm32g4xx_hal_gpio.h" -#include "stm32g4xx_hal_gpio_ex.h" -#include "stm32g4xx_hal_rcc.h" -#include "stm32g4xx_ll_gpio.h" -#include "stm32g4xx_ll_rcc.h" -// #elif defined(STM32L4) -// #elif defined(STM32U5) -#else -#error "Unsupported STM32 family" -#endif - -#endif // end header guard diff --git a/ECU/Application/Src/CANutils.c b/ECU/Application/Src/CANutils.c index 5f55662e7..473376efe 100644 --- a/ECU/Application/Src/CANutils.c +++ b/ECU/Application/Src/CANutils.c @@ -10,7 +10,7 @@ #include "StateData.h" #include "StateTicks.h" #include "StateUtils.h" -#include "ecu_can.h" +#include "can.h" #include "main.h" #include "stm32g4xx_hal_fdcan.h" #include "string.h" diff --git a/ECU/Application/Src/Lights.c b/ECU/Application/Src/Lights.c index 9e2b04ff1..464186e51 100644 --- a/ECU/Application/Src/Lights.c +++ b/ECU/Application/Src/Lights.c @@ -5,7 +5,7 @@ #include "StateUtils.h" #include "adc.h" #include "bitManipulations.h" -#include "ecu_can.h" +#include "can.h" #include "main.h" #include "stm32g4xx_ll_gpio.h" diff --git a/ECU/Application/Src/StateTicks.c b/ECU/Application/Src/StateTicks.c index 3c53bc9c2..34281c706 100644 --- a/ECU/Application/Src/StateTicks.c +++ b/ECU/Application/Src/StateTicks.c @@ -13,7 +13,7 @@ #include "StateMachine.h" #include "StateUtils.h" #include "Unused.h" -#include "ecu_can.h" +#include "can.h" #include "stm32g4xx_ll_gpio.h" /** diff --git a/ECU/Application/Src/ecu_can.c b/ECU/Application/Src/ecu_can.c deleted file mode 100644 index 7788bf745..000000000 --- a/ECU/Application/Src/ecu_can.c +++ /dev/null @@ -1,1075 +0,0 @@ -#include "ecu_can.h" - -#include -#include -#include -#include - -#include "stm32g4xx_ll_bus.h" -#include "stm32g4xx_ll_dma.h" -#include "stm32g4xx_ll_tim.h" - -// TODO: make the profiler cleaner -#include "Logomatic.h" -#include "main.h" - -// TODO: define DMA usage in a better way -// #define USEDMA -#ifdef USEDMA -#include "can_dma.h" - -#endif - -// CAN CONFIGURATION HEADER -#include "can_cfg.h" -#ifndef CAN_CFG_H -#error "can.c: Please define CAN_CFG_H and define at least one USECANx and TX_BUFFER_X_SIZE" -#endif -// =============================== - -// EXAMPLE Configuration in "can_cfg.h" -// #ifndef CAN_CFG_H -// #define CAN_CFG_H - -// #define USECAN1 -// #define TX_BUFFER_1_SIZE 10 - -// #endif - -// HAL handles -#ifdef USECAN1 -#ifndef TX_BUFFER_1_SIZE -#error "Please Define TX_BUFFER_1_SIZE" -#endif -static FDCAN_HandleTypeDef hal_fdcan1 = {.Instance = FDCAN1}; -FDCANTxMessage tx_buffer_1[TX_BUFFER_1_SIZE] = {0}; -static CANHandle CAN1 = {.hal_fdcanP = &hal_fdcan1, .tx_buffer = tx_buffer_1}; -#endif - -#ifdef USECAN2 -#ifndef TX_BUFFER_2_SIZE -#error "Please Define TX_BUFFER_2_SIZE" -#endif -static FDCAN_HandleTypeDef hal_fdcan2 = {.Instance = FDCAN2}; -FDCANTxMessage tx_buffer_2[TX_BUFFER_2_SIZE] = {0}; -static CANHandle CAN2 = {.hal_fdcanP = &hal_fdcan2, .tx_buffer = tx_buffer_2}; -#endif - -#ifdef USECAN3 -#ifndef TX_BUFFER_3_SIZE -#error "Please Define TX_BUFFER_3_SIZE" -#endif -static FDCAN_HandleTypeDef hal_fdcan3 = {.Instance = FDCAN3}; -FDCANTxMessage tx_buffer_3[TX_BUFFER_3_SIZE] = {0}; -static CANHandle CAN3 = {.hal_fdcanP = &hal_fdcan3, .tx_buffer = tx_buffer_3}; -#endif - -#define MIN(A, B) ((A < B) ? A : B) - -// bool hardwareEnabled = false; - -// macro lore -/* -#define CAT(a,b) a##b -#define CAT3(a, b, c) a##b##c -#define CAT4(a, b,c,d) a##b##c##d -#define CAT5(a, b,c,d,e) a##b##c##d##e - -#define ACTIVATE_FDCAN_HELPER(FDCANX, ITY, preirq, subirq) \ - do { \ - HAL_NVIC_SetPriority( CAT4(FDCANX##,_,ITY, _IRQn ) , preirq, subirq ); \ - HAL_NVIC_EnableIRQ( CAT4(FDCANX##,_,ITY, _IRQn ) ); \ - } while(0) - -#define HAL_NVIC_ACTIVATE_FDCAN(FDCANX, ITY, preirq, subirq) \ - do { \ - if (FDCANX == ##FDCAN1 && ITY == 0) { ACTIVATE_FDCAN_HELPER(FDCAN1, IT0, preirq, subirq); } \ - else if (FDCANX == FDCAN1 && ITY == 1) { ACTIVATE_FDCAN_HELPER(FDCAN1, IT1, preirq, subirq); } \ - else if (FDCANX == FDCAN2 && ITY == 0) { ACTIVATE_FDCAN_HELPER(FDCAN2, IT0, preirq, subirq); } \ - else if (FDCANX == FDCAN2 && ITY == 1) { ACTIVATE_FDCAN_HELPER(FDCAN2, IT1, preirq, subirq); } \ - else if (FDCANX == FDCAN3 && ITY == 0) { ACTIVATE_FDCAN_HELPER(FDCAN3, IT0, preirq, subirq); } \ - else if (FDCANX == FDCAN3 && ITY == 1) { ACTIVATE_FDCAN_HELPER(FDCAN3, IT1, preirq, subirq); } \ - else { LOGOMATIC("Unrecognized FDCAN and Interrupt Line combination"); } \ - } while(0) - -*/ - -// TODO: Modify helpers to work across families -// helpers ================= -static int fdcan_shared_clock_ref = 0; -static inline void fdcan_enable_shared_clock(void); -static inline void fdcan_disable_shared_clock(void); -static CANHandle *can_get_handle(FDCAN_HandleTypeDef *hfdcan); -static CAN_STATUS can_get_irqs(FDCAN_GlobalTypeDef *instance, IRQn_Type *it0, IRQn_Type *it1); -static CAN_STATUS validate_can_handle(CANHandle *canHandle); - -inline void can_set_clksource(uint32_t clksource) -{ - LL_RCC_SetFDCANClockSource(clksource); -} -static const char *can_get_instance_name(FDCAN_GlobalTypeDef *instance); -// static inline void gpio_clk_enable(GPIO_TypeDef *gpio) -// static inline void gpio_clk_disable(GPIO_TypeDef *gpio) - -static CAN_STATUS can_msp_init(CANHandle *canHandle, CANConfig *config); -static CAN_STATUS can_msp_deinit(CANHandle *canHandle); -static void FDCAN_InstanceDeInit(FDCAN_HandleTypeDef *hfdcan); - -//================================================= API ======================================== -CANHandle *can_init(const CANConfig *config) -{ - // config validation? - // assert(config != 0) - CANHandle *canHandle = NULL; - -#ifdef STM32G474xx -#ifdef USECAN1 - if (config->fdcan_instance == FDCAN1) { - if (CAN1.init) { - LOGOMATIC("CAN: CAN1 is already initialized\n"); - return CAN_SUCCESS; - } else { - canHandle = &CAN1; - canHandle->tx_capacity = TX_BUFFER_1_SIZE; - } - } -#endif -#ifdef USECAN2 - if (config->fdcan_instance == FDCAN2) { - if (CAN2.init) { - LOGOMATIC("CAN: CAN2 is already initialized\n"); - return CAN_SUCCESS; - } else { - canHandle = &CAN2; - canHandle->tx_capacity = TX_BUFFER_2_SIZE; - LOGOMATIC("CAN: CAN2 selected with tx capacity %lu\n", canHandle->tx_capacity); - } - } -#endif -#ifdef USECAN3 - if (config->fdcan_instance == FDCAN3) { - if (CAN3.init) { - LOGOMATIC("CAN: CAN3 is already initialized\n"); - return CAN_SUCCESS; - } else { - canHandle = &CAN3; - canHandle->tx_capacity = TX_BUFFER_3_SIZE; - } - } -#endif - -// TODO: figure out a better way to extend this to other families besides ifdef soup -#elif defined(STM32G431xx) -#ifdef USECAN1 - if (config->fdcan_instance == FDCAN1) { - if (CAN1.init) { - LOGOMATIC("CAN: CAN1 is already initialized\n"); - return CAN_SUCCESS; - } else { - canHandle = &CAN1; - canHandle->tx_capacity = TX_BUFFER_1_SIZE; - } - } -#endif - -#endif - - // #elif defined(STM32L476xx) - // #else - // #error "Unsupported STM32 family" - // #endif - - if (canHandle == NULL) { - LOGOMATIC("CAN: Unrecognized FDCAN instance"); - return NULL; - } - - canHandle->init = false; - canHandle->started = false; - - // Initialize handle - assert(config->hal_fdcan_init.TxFifoQueueMode == FDCAN_TX_FIFO_OPERATION); - // assert(config->hal_fdcan_init.FrameFormat == FDCAME); - - canHandle->hal_fdcanP->Init = config->hal_fdcan_init; // copy FDCAN parameters from user - // canHandle->hal_fdcanP->Instance = config->fdcan_instance //handles initialized with correct base instance addresses - - canHandle->rx_gpio = config->rx_gpio; - canHandle->tx_gpio = config->tx_gpio; - canHandle->rx_pin = config->init_rx_gpio.Pin; - canHandle->tx_pin = config->init_tx_gpio.Pin; - - canHandle->rx_interrupt_priority = config->rx_interrupt_priority; - canHandle->tx_interrupt_priority = config->tx_interrupt_priority; - - canHandle->rx_callback = config->rx_callback; - - // tx buffer - // canHandle->tx_capacity = TX_BUFFER_SIZE_1; //dependent on can instance - canHandle->tx_tail = 0; - canHandle->tx_elements = 0; - - // error - canHandle->lost_rx = 0; - - // alternately -> have can_msp_init setup state for HAL_FDCAN_MspInit to work correctly - // have can_msp_deinit setup state for HAL_FDCAN_MspDeInit to work correctly - // Then call HAL_FDCAN_Init() and HAL_FDCAN_DeInit() - - // Current idea, redefine HAL_FDCAN_MspInit and MspDeInit to do nothing at all, do all the work in can_msp_init() - uint32_t failure = 0; - if (failure |= (can_msp_init(canHandle, (CANConfig *)config) != CAN_SUCCESS)) { - LOGOMATIC("CAN_init: could not initialize MSP resources"); - return NULL; - } - - // PROBLEM: HAL_FDCAN_Init expects HAL_FDCAN_MspInit() to be defined - if (HAL_FDCAN_Init(canHandle->hal_fdcanP) != HAL_OK) { - failure |= HAL_ERROR; - LOGOMATIC("CAN: HAL Could not initialize FDCAN peripheral"); - return NULL; - // Error_Handler(); - } - - // Active FDCAN callbacks - rxcalback uses line0, txcallback uses line1 - // uint32_t rxevents = FDCAN_IT_RX_FIFO0_NEW_MESSAGE; - uint32_t status = 0; - uint32_t rx_events = FDCAN_IT_RX_FIFO0_NEW_MESSAGE | FDCAN_IT_RX_FIFO0_FULL | FDCAN_IT_RX_FIFO0_MESSAGE_LOST; - status |= HAL_FDCAN_ActivateNotification(canHandle->hal_fdcanP, rx_events, 0); - status |= HAL_FDCAN_ConfigInterruptLines(canHandle->hal_fdcanP, rx_events, FDCAN_INTERRUPT_LINE0); - - // uint32_t txevents = FDCAN_IT_TX_COMPLETE; - uint32_t destinations = FDCAN_TX_BUFFER0; - uint32_t tx_events = FDCAN_IT_TX_COMPLETE | FDCAN_IT_TX_FIFO_EMPTY; - status |= HAL_FDCAN_ActivateNotification(canHandle->hal_fdcanP, tx_events, destinations); - status |= HAL_FDCAN_ConfigInterruptLines(canHandle->hal_fdcanP, tx_events, FDCAN_INTERRUPT_LINE1); - - uint32_t err_events = FDCAN_IT_BUS_OFF | FDCAN_IT_ERROR_PASSIVE | FDCAN_IT_ERROR_WARNING | FDCAN_IT_ARB_PROTOCOL_ERROR | FDCAN_IT_DATA_PROTOCOL_ERROR; - status |= HAL_FDCAN_ActivateNotification(canHandle->hal_fdcanP, err_events, 0); - status |= HAL_FDCAN_ConfigInterruptLines(canHandle->hal_fdcanP, err_events, FDCAN_INTERRUPT_LINE1); - // Callbacks redefined later - - if (status & HAL_ERROR) { - LOGOMATIC("CAN: Could not activate rx, tx, and error interrupts\n"); - failure |= status; - } - - // Circular Buffer - // canHandle->tx_buffer = GR_CircularBuffer_Create(config->tx_buffer_length); - // canHandle->tx_buffer = malloc(sizeof(FDCANTxMessage)*canHandle->tx_buffer_length); - if (!canHandle->tx_buffer) { - LOGOMATIC("tx_buffer isn't valid?"); - failure |= 1; - } - - if (failure) { - can_msp_deinit(canHandle); - FDCAN_InstanceDeInit(canHandle->hal_fdcanP); - - FDCAN_HandleTypeDef *temp = canHandle->hal_fdcanP; - memset(canHandle, 0, sizeof(*canHandle)); // FIXME: Make sure instance is not being overwritten (FDCANx) - canHandle->hal_fdcanP = temp; - - return NULL; - } - -#ifdef USEDMA - // for(int i = 0; i < 10; i++); - DMA_M2M_Init(canHandle->rx_interrupt_priority, 0, canHandle->rx_callback); -#endif - - canHandle->init = true; - canHandle->started = false; - - return canHandle; -} - -CAN_STATUS can_release(CANHandle *canHandle) -{ - if (validate_can_handle(canHandle) != CAN_SUCCESS) { - return CAN_ERROR; - } - - if (!canHandle->init) { - LOGOMATIC("CAN_release: can instance is already deinitialized"); - return CAN_ERROR; - } - - if (can_stop(canHandle)) { // try to prevent more interrupts from firing - LOGOMATIC("CAN_release: could not stop instance"); - return CAN_ERROR; - } - - // No more interrupts should be firing that modify canHandle - if (can_msp_deinit(canHandle) != CAN_SUCCESS) { - LOGOMATIC("CAN_release: could not stop instance"); - return CAN_ERROR; - } - - // reset FDCANx instance and message RAM and filters, clear interrupts - // HAL_FDCAN_DeInit(canHandle->hal_fdcanP); resets a little too hard - FDCAN_InstanceDeInit(canHandle->hal_fdcanP); - - __DSB(); // Data Synchronization Barrier - __ISB(); // Instruction Synchronization Barrier - - // free circular buffer contents - // GR_CircularBuffer_Free(&(canHandle->tx_buffer)); - memset((void *)canHandle->tx_buffer, 0, canHandle->tx_capacity * sizeof(canHandle->tx_buffer[0])); - canHandle->tx_elements = 0; - canHandle->tx_tail = 0; - - // reset can handle - // FDCAN_HandleTypeDef *temp = canHandle->hal_fdcanP; - // uint32_t capacity = canHandle->tx_capacity; - // FDCANTxMessage* buff = canHandle->tx_buffer; - - canHandle->init = 0; - canHandle->started = 0; - canHandle->rx_callback = 0; - canHandle->tx_elements = 0; - canHandle->rx_gpio = 0; - canHandle->rx_interrupt_priority = 0; - canHandle->tx_interrupt_priority = 0; - canHandle->tx_gpio = 0; - canHandle->rx_pin = 0; - canHandle->tx_pin = 0; - canHandle->tx_tail = 0; - canHandle->lost_rx = 0; - - // memset(canHandle, 0, sizeof(*canHandle)); - // canHandle->hal_fdcanP = temp; - // canHandle->tx_capacity = capacity; - // canHandle->tx_buffer = buff; - - return CAN_SUCCESS; -} -// TODO: Implement timer -// lock access to Circular Buffer when sending and dequeuing -void can_tx_dequeue_helper(CANHandle *handle) -{ - // TODO: validate buffer - if (!handle->tx_buffer) { - LOGOMATIC("can_tx_buffer_helper: buffer is invalid"); - return; - } - - // LOGOMATIC("CAN %s, LOAD %2.2f \n", can_get_instance_name(handle->hal_fdcanP->Instance), (float)handle->tx_elements / (float)handle->tx_capacity); - - // use interrupt masking in case any other ISRs need to lock the circular buffer - uint32_t basepri = __get_BASEPRI(); - __set_BASEPRI(handle->tx_interrupt_priority << 4); - // single consumer shouldn't affect state of circular buffer - if (handle->tx_elements == 0) { - __set_BASEPRI(basepri); - return; - } - - FDCAN_ProtocolStatusTypeDef protocol_status = {0}; - if (HAL_FDCAN_GetProtocolStatus(handle->hal_fdcanP, &protocol_status) == HAL_OK && protocol_status.BusOff) { - LOGOMATIC("CAN_send: bus off detected, attempting recovery\n"); - if (HAL_FDCAN_Stop(handle->hal_fdcanP) != HAL_OK) { - LOGOMATIC("CAN_send: failed to stop FDCAN peripheral during bus off recovery\n"); - __set_BASEPRI(basepri); - return; - } - uint32_t abort_mask = FDCAN_TX_BUFFER0 | FDCAN_TX_BUFFER1 | FDCAN_TX_BUFFER2; - HAL_FDCAN_AbortTxRequest(handle->hal_fdcanP, abort_mask); - if (HAL_FDCAN_Start(handle->hal_fdcanP) != HAL_OK) { - LOGOMATIC("CAN_send: failed to restart FDCAN peripheral during bus off recovery\n"); - __set_BASEPRI(basepri); - return; - } - } - - if (HAL_FDCAN_IsRestrictedOperationMode(handle->hal_fdcanP)) { - LOGOMATIC("CAN_send: currently in restricted operation mode\n"); - HAL_FDCAN_ExitRestrictedOperationMode(handle->hal_fdcanP); - } - - // Can Add to Fifo Q - if (HAL_FDCAN_GetTxFifoFreeLevel(handle->hal_fdcanP)) { - const FDCANTxMessage *msg = &(handle->tx_buffer[handle->tx_tail]); - - // should call Tx Buffer Callback once complete - HAL_StatusTypeDef status = HAL_FDCAN_AddMessageToTxFifoQ(handle->hal_fdcanP, &msg->tx_header, msg->data); - - if (status != HAL_OK) { - // LOGOMATIC("CAN_tx_helper: failed to add message to FIFO\n"); //FIXME: Logomatic may not work with interrupts disabled - __set_BASEPRI(basepri); - return; // Stop trying to send more - } - // free(msg); // Successfully sent, free the entry in the circular buffer (which is pointed to by tail) - handle->tx_tail = ++handle->tx_tail % handle->tx_capacity; - handle->tx_elements--; - - } else { // FIXME: call can_tx_dequeue_helper later with a timer, if this gets implemented, need to mask timer interrupts to allow atomic access - - } // alternatively, if fifo is full, tx_dequeue should get called anyways, and we don't need the else statement - - __set_BASEPRI(basepri); -} - -#ifdef PROFILE -dwt_timer_t send_timer = {0}; -#endif - -CAN_STATUS can_enqueue(CANHandle *canHandle, FDCANTxMessage *message) -{ - - if (validate_can_handle(canHandle) != CAN_SUCCESS) { - return CAN_ERROR; - } - - if (!canHandle->started) { - LOGOMATIC("CAN_send: instance is not started\n"); - return CAN_ERROR; - } - - if (message == NULL) { - LOGOMATIC("CAN_enqueue: received null pointer for message\n"); - return CAN_ERROR; - } - - if (!canHandle->init || !canHandle->started) { - LOGOMATIC("CAN_send: CAN not initialized or started\n"); - return CAN_ERROR; - } - - // enques can message - if (canHandle->tx_elements < canHandle->tx_capacity) { - // int result = GR_CircularBuffer_Push(canHandle->tx_buffer, message, sizeof(FDCANTxMessage)); - - uint32_t idx = (canHandle->tx_tail + canHandle->tx_elements) % canHandle->tx_capacity; - canHandle->tx_buffer[idx] = *message; - canHandle->tx_elements++; - - // memcpy(&canHandle->tx_buffer[idx], message , sizeof(FDCANTxMessage) ); - - __set_BASEPRI(__get_BASEPRI()); - return CAN_SUCCESS; // added to software buffer - - /*if (result != 0) { - LOGOMATIC("CAN_send: buffer push failed\n"); - return CAN_ERROR; - } else { - return CAN_SUCCESS; - }*/ - } - - LOGOMATIC("CAN_send: all buffers full\n"); // p - __set_BASEPRI(__get_BASEPRI()); - return CAN_ERROR; -} - -// ONLY CALLED FROM A TIMER -CAN_STATUS can_send(CANHandle *canHandle, FDCANTxMessage *message) -{ - // IF TX Fifos are not full, send directly to them - // If TX Fifos are full, append to circular buffer - // If circular buffer is full, return an error code - - // stop can_tx_dequeue_helper from from interleaving - // TODO: Check BASEPRI register - uint32_t basepri = __get_BASEPRI(); - __set_BASEPRI((canHandle->tx_interrupt_priority) << 4); - - FDCAN_ProtocolStatusTypeDef protocol_status = {0}; - if (HAL_FDCAN_GetProtocolStatus(canHandle->hal_fdcanP, &protocol_status) == HAL_OK && protocol_status.BusOff) { - LOGOMATIC("CAN_send: bus off detected, attempting recovery\n"); - if (HAL_FDCAN_Stop(canHandle->hal_fdcanP) != HAL_OK) { - LOGOMATIC("CAN_send: failed to stop FDCAN peripheral during bus off recovery\n"); - __set_BASEPRI(basepri); - return CAN_ERROR; - } - uint32_t abort_mask = FDCAN_TX_BUFFER0 | FDCAN_TX_BUFFER1 | FDCAN_TX_BUFFER2; - HAL_FDCAN_AbortTxRequest(canHandle->hal_fdcanP, abort_mask); - if (HAL_FDCAN_Start(canHandle->hal_fdcanP) != HAL_OK) { - LOGOMATIC("CAN_send: failed to restart FDCAN peripheral during bus off recovery\n"); - __set_BASEPRI(basepri); - return CAN_ERROR; - } - } - - if (HAL_FDCAN_IsRestrictedOperationMode(canHandle->hal_fdcanP)) { - LOGOMATIC("CAN_send: currently in restricted operation mode\n"); - HAL_FDCAN_ExitRestrictedOperationMode(canHandle->hal_fdcanP); - } - - uint32_t free = 0; - if ((free = HAL_FDCAN_GetTxFifoFreeLevel(canHandle->hal_fdcanP)) > 0) { - HAL_StatusTypeDef status = HAL_FDCAN_AddMessageToTxFifoQ(canHandle->hal_fdcanP, &(message->tx_header), message->data); - - if (status != HAL_OK) { - LOGOMATIC("CAN_send: failed to add to HW FIFO, falling back to SW queue\n"); - } else { - __set_BASEPRI(basepri); - return CAN_SUCCESS; // Successfully added to HW FIFO - } - } - //} - - LOGOMATIC("CAN_send: all buffers full\n"); // p - __set_BASEPRI(basepri); - // Both buffers full - return CAN_ERROR; -} - -void HAL_FDCAN_ErrorStatusCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t ErrorStatusITs) -{ - CANHandle *handle = can_get_handle(hfdcan); - if (!handle || !handle->init) { - return; - } - - LOGOMATIC("%s: FDCAN error status interrupt: 0x%08lX\n", can_get_instance_name(handle->hal_fdcanP->Instance), ErrorStatusITs); - - if (HAL_FDCAN_IsRestrictedOperationMode(hfdcan)) { - HAL_FDCAN_ExitRestrictedOperationMode(hfdcan); - } -} - -// #define PROFILE -// uint32_t PROFILE_AVG_RX_CYCLES = 0; -// uint32_t PROFILE_AVG_RX_CYCLES_SAMPLES = 0; - -#ifdef PROFILE -dwt_timer_t rx_timer = {0}; -#endif - -void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) -{ - // dwt_timer_start_measurement(&rx_timer); - - // dwt_timer_start_measurement(&rx_timer); - - CANHandle *handle = can_get_handle(hfdcan); - - if (!handle || !handle->init || !handle->rx_callback) { - return; - } - - /*if (!handle->rx_buffer) { - LOGOMATIC("CAN: RX Complete, but %s Buffer was released\n", can_get_instance_name(hfdcan->Instance)); - return; - } */ // no rx buffer at the moment - - if (RxFifo0ITs & FDCAN_IT_RX_FIFO0_MESSAGE_LOST) { - // TODO: Synchronize access to message counters - if (__builtin_uaddl_overflow(handle->lost_rx, 1, &handle->lost_rx)) { - handle->lost_rx = 0; - LOGOMATIC("%s: lost message counter overflowed\n", can_get_instance_name(handle->hal_fdcanP->Instance)); - } - } - - // If nothing else happened besides message loss - if (!(RxFifo0ITs & ~FDCAN_IT_RX_FIFO0_MESSAGE_LOST)) { - return; - } - - if (!(RxFifo0ITs & (FDCAN_IT_RX_FIFO0_NEW_MESSAGE | FDCAN_IT_RX_FIFO0_FULL))) { - return; - } - - // if (GR_CircularBuffer_IsFull(handle->rx_buffer)) return; - FDCAN_RxHeaderTypeDef rx_header = {0}; - - // TODO: Stack allocation should be safe - // uint8_t rx_data[64] = {0}; - uint8_t rx_data[64] __attribute__((aligned(4))) = {0}; // align to word boundary for safe DMA transfer - - // TODO: Copying takes a while, may have to spread these out over multiple ISRs - while (HAL_FDCAN_GetRxFifoFillLevel(hfdcan, FDCAN_RX_FIFO0) > 0) { -#ifdef USEDMA - - // dwt_timer_start_measurement(&rx_timer); - FDCAN_GetRxMessage_DMA(hfdcan, FDCAN_RX_FIFO0, &rx_header, rx_data); - // dwt_timer_end_measurement(&rx_timer); - -#else - - // dwt_timer_start_measurement(&rx_timer); - HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &rx_header, rx_data); - // dwt_timer_end_measurement(&rx_timer); - -#endif - - // GR_OLD_NODE_ID sendingID = (rx_header.Identifier & (0xFF << 20)) >> 20; - // GR_OLD_MSG_ID messageID = (rx_header.Identifier & (0xFFF << 8)) >> 8; - - // TODO: move callbacks to correct positions, but right now you are using polling DMA so this is fine. - handle->rx_callback(rx_header.Identifier, rx_data, CANFD_DLCtoBytes[rx_header.DataLength]); - } - - //__set_BASEPRI(prev_priority); - // dwt_timer_end_measurement(&rx_timer); -} - -/* -void can_read_rx_buffer(CANHandle* canHandle) { - //User can call this at their leisure to pop the rx_buffer - - //read rx_buffer one element at a time? - //or read until empty -}*/ - -/*void HAL_FDCAN_RxFifo1Callback(FDCAN_HandleTypeDef * hfdcan, uint32_t RxFifo0ITs) { - -}*/ - -// Just alternatively just use the HAL_FDCAN_ConfigFilter directly with the canHandle->hal_fdcan -CAN_STATUS can_add_filter(CANHandle *canHandle, FDCAN_FilterTypeDef *filter) -{ - if (validate_can_handle(canHandle) != CAN_SUCCESS) { - return CAN_ERROR; - } - - if (!canHandle->init || canHandle->started) { - LOGOMATIC("CAN_add_filter: can instance is not initialized or already started\n"); - return CAN_ERROR; - } - - if (HAL_FDCAN_ConfigFilter(canHandle->hal_fdcanP, filter) != HAL_OK) { - LOGOMATIC("CAN_add_filter: failed to configure filter\n"); - return CAN_ERROR; - } - return CAN_SUCCESS; - // check that # of filters isn't exceeding max value -} - -CAN_STATUS can_start(CANHandle *canHandle) -{ - if (validate_can_handle(canHandle) != CAN_SUCCESS) { - return CAN_ERROR; - } - - if (!canHandle->init) { - LOGOMATIC("CAN_start: can instance is not initialized\n"); - return CAN_ERROR; - } - - if (canHandle->started) { - return CAN_SUCCESS; - } - - IRQn_Type rx0it, txit; - rx0it = txit = -1; // TOOD: Check that this is a valid way to initialize an invalid value - if (can_get_irqs(canHandle->hal_fdcanP->Instance, &rx0it, &txit) != CAN_SUCCESS) { - LOGOMATIC("can_start: could not obtain irq #s\n"); - return CAN_ERROR; - } - - HAL_NVIC_ClearPendingIRQ(rx0it); // prevent a spurious interrupt - HAL_NVIC_ClearPendingIRQ(txit); - - GPIOx_CLK_ENABLE(canHandle->rx_gpio); - GPIOx_CLK_ENABLE(canHandle->tx_gpio); - - HAL_FDCAN_Start(canHandle->hal_fdcanP); - - canHandle->started = true; - - HAL_NVIC_EnableIRQ(rx0it); - HAL_NVIC_EnableIRQ(txit); - - return CAN_SUCCESS; -} - -CAN_STATUS can_stop(CANHandle *canHandle) -{ - if (validate_can_handle(canHandle) != CAN_SUCCESS) { - return CAN_ERROR; - } - - if (!canHandle->init) { - LOGOMATIC("CAN_stop: can instance is not initialized\n"); - return CAN_ERROR; - } - - if (!canHandle->started) { - return CAN_SUCCESS; - } - - // stop can interrupts from activating - uint32_t base_pri = __get_BASEPRI(); - __set_BASEPRI(MIN(canHandle->rx_interrupt_priority, canHandle->tx_interrupt_priority) << 4); - - if (HAL_FDCAN_Stop(canHandle->hal_fdcanP) != HAL_OK) { - return CAN_ERROR; - } - - IRQn_Type rx0it, txit; - rx0it = txit = -1; - if (can_get_irqs(canHandle->hal_fdcanP->Instance, &rx0it, &txit) != CAN_SUCCESS) { - return CAN_ERROR; - } - - HAL_NVIC_DisableIRQ(rx0it); - HAL_NVIC_DisableIRQ(txit); - HAL_NVIC_ClearPendingIRQ(rx0it); - HAL_NVIC_ClearPendingIRQ(txit); - - __set_BASEPRI(base_pri); - - GPIOx_CLK_DISABLE(canHandle->rx_gpio); - GPIOx_CLK_DISABLE(canHandle->tx_gpio); - - canHandle->started = false; - - // TODO: stop a DMA transfer if its in progress - - return CAN_SUCCESS; -} - -// ==================================== HELPER FUNCTIONS =============================================== -// TODO: Abstract across families - -static inline void fdcan_enable_shared_clock(void) -{ - if (fdcan_shared_clock_ref == 0) { - __HAL_RCC_FDCAN_CLK_ENABLE(); - } - fdcan_shared_clock_ref++; -} - -static inline void fdcan_disable_shared_clock(void) -{ - if (fdcan_shared_clock_ref > 0) { - fdcan_shared_clock_ref--; - if (fdcan_shared_clock_ref == 0) { - __HAL_RCC_FDCAN_CLK_DISABLE(); - } - } -} - -// valid only for STM32G4 -static CAN_STATUS can_get_irqs(FDCAN_GlobalTypeDef *instance, IRQn_Type *it0, IRQn_Type *it1) -{ -#ifdef STM32G474xx - if (instance == FDCAN1) { - *it0 = FDCAN1_IT0_IRQn; - *it1 = FDCAN1_IT1_IRQn; - return CAN_SUCCESS; - } - if (instance == FDCAN2) { - *it0 = FDCAN2_IT0_IRQn; - *it1 = FDCAN2_IT1_IRQn; - return CAN_SUCCESS; - } - if (instance == FDCAN3) { - *it0 = FDCAN3_IT0_IRQn; - *it1 = FDCAN3_IT1_IRQn; - return CAN_SUCCESS; - } - - // TODO: START of possible ifdef soup -#elif defined(STM32G431xx) - if (instance == FDCAN1) { - *it0 = FDCAN1_IT0_IRQn; - *it1 = FDCAN1_IT1_IRQn; - return CAN_SUCCESS; - } -#endif - - LOGOMATIC("can_get_irqs: could not obtain irq #s\n"); - return CAN_ERROR; // invalid instance -} - -static CANHandle *can_get_handle(FDCAN_HandleTypeDef *hfdcan) -{ -#ifdef STM32G4 -#ifdef USECAN1 - if (hfdcan->Instance == FDCAN1) { - return &CAN1; - } -#endif -#ifdef USECAN2 - if (hfdcan->Instance == FDCAN2) { - return &CAN2; - } -#endif -#ifdef USECAN3 - if (hfdcan->Instance == FDCAN3) { - return &CAN3; - } -#endif -#endif - - LOGOMATIC("CAN_get_handle: was given invalid FDCAN instance\n"); - UNUSED(hfdcan); - return NULL; -} - -static CAN_STATUS validate_can_handle(CANHandle *canHandle) -{ - - if (canHandle == NULL) { - LOGOMATIC("can.c: handle is null\n"); - return CAN_ERROR; - } - -#ifdef STM32G4 -#ifdef USECAN1 - if (canHandle == &CAN1) { - return CAN_SUCCESS; - } -#endif -#ifdef USECAN2 - if (canHandle == &CAN2) { - return CAN_SUCCESS; - } -#endif -#ifdef USECAN3 - if (canHandle == &CAN3) { - return CAN_SUCCESS; - } -#endif -#endif - - LOGOMATIC("can.c: invalid can handle\n"); - return CAN_ERROR; -} - -/* -static inline void gpio_clk_enable(GPIO_TypeDef *gpio) -{ - if (gpio == GPIOA) { - __HAL_RCC_GPIOA_CLK_ENABLE(); - } else if (gpio == GPIOB) { - __HAL_RCC_GPIOB_CLK_ENABLE(); - } else if (gpio == GPIOD) { - __HAL_RCC_GPIOD_CLK_ENABLE(); - } -} - -static inline void gpio_clk_disable(GPIO_TypeDef *gpio) -{ - if (gpio == GPIOA) { - __HAL_RCC_GPIOA_CLK_DISABLE(); - } else if (gpio == GPIOB) { - __HAL_RCC_GPIOB_CLK_DISABLE(); - } else if (gpio == GPIOD) { - __HAL_RCC_GPIOD_CLK_DISABLE(); - } -}*/ - -// only valid for #STM32G474x, must redefine for each family -static CAN_STATUS can_msp_init(CANHandle *canHandle, CANConfig *config) -{ - // MSP Init ------- This could be inside HAL_FDCAN_MspInit() instead - // FDCAN Clock Select - - fdcan_enable_shared_clock(); - - // Clock speed for FDCAN determined by APB1 clock speed and FDCAN prescaler - - // GPIOs init - GPIOx_CLK_ENABLE(config->rx_gpio); - GPIOx_CLK_ENABLE(config->tx_gpio); - - HAL_GPIO_Init(config->rx_gpio, &(config->init_rx_gpio)); - HAL_GPIO_Init(config->tx_gpio, &(config->init_tx_gpio)); - - IRQn_Type rxit = -1; - IRQn_Type txit = -1; - if (can_get_irqs(canHandle->hal_fdcanP->Instance, &rxit, &txit) != CAN_SUCCESS) { - return CAN_ERROR; - } - - // rxfifo0 - HAL_NVIC_SetPriority(rxit, config->rx_interrupt_priority, 0); - - // tx - HAL_NVIC_SetPriority(txit, config->tx_interrupt_priority, 0); - // End MSP Init -------------- - - // Call can_start() to enable interrupts - - return CAN_SUCCESS; -} - -// Valid only for STM32G474xE -static CAN_STATUS can_msp_deinit(CANHandle *canHandle) -{ - // MSP DeInit - // must disable NVIC IRQs before freeing circular buffer - - // NVIC - IRQn_Type rx0it = -1; - IRQn_Type txit = -1; - if (can_get_irqs(canHandle->hal_fdcanP->Instance, &rx0it, &txit) != CAN_SUCCESS) { - return CAN_ERROR; - } - - HAL_NVIC_DisableIRQ(rx0it); - HAL_NVIC_DisableIRQ(txit); - - // TODO: turn off gpio clocks if no other peripherals are using them??? Could implement a shared GPIO layer - // HAL_GPIO_DeInit(canHandle->rx_gpio, canHandle->rx_pin); - // HAL_GPIO_DeInit(canHandle->tx_gpio, canHandle->tx_pin); - - // MSP shared layer for GPIOs - // TODO: used to disable GPIOs clocks, but that might affect other peripherals - - // RCC - // can only disable clock after resetting all FDCAN instances - return CAN_SUCCESS; -} - -static void FDCAN_InstanceDeInit(FDCAN_HandleTypeDef *hfdcan) -{ - // Enter INIT mode - hfdcan->Instance->CCCR |= FDCAN_CCCR_INIT; - while (!(hfdcan->Instance->CCCR & FDCAN_CCCR_INIT)) - ; - hfdcan->Instance->CCCR |= FDCAN_CCCR_CCE; - - // Disable interrupts - //__HAL_FDCAN_DISABLE_IT(hfdcan, FDCAN_IT_LIST_RX_FIFO0 | FDCAN_IT_LIST_RX_FIFO1 | FDCAN_IT_LIST_SMSG | FDCAN_IT_LIST_TX_FIFO_ERROR | FDCAN_IT_LIST_MISC | //FDCAN_IT_LIST_BIT_LINE_ERROR | - // FDCAN_IT_LIST_PROTOCOL_ERROR); - - // - CLEAR_BIT(hfdcan->Instance->ILE, (FDCAN_INTERRUPT_LINE0 | FDCAN_INTERRUPT_LINE1)); - - // Clear filters - // TODO: fix magic numbers - memset((void *)hfdcan->msgRam.StandardFilterSA, 0, 0x0070); - memset((void *)hfdcan->msgRam.ExtendedFilterSA, 0, 0x0050); - - // Optionally clear FIFOs / buffers - - // Exit INIT mode - hfdcan->Instance->CCCR &= ~FDCAN_CCCR_INIT; - while (hfdcan->Instance->CCCR & FDCAN_CCCR_INIT) - ; - - // Update handle state - hfdcan->State = HAL_FDCAN_STATE_RESET; - - fdcan_disable_shared_clock(); -} - -// valid only for STM32G4 -static const char *can_get_instance_name(FDCAN_GlobalTypeDef *instance) -{ -#ifdef STM32G474xx - - if (instance == FDCAN1) { - return "FDCAN1"; - } else if (instance == FDCAN2) { - return "FDCAN2"; - } else if (instance == FDCAN3) { - return "FDCAN3"; - } -#elif defined(STM32G431xx) - if (instance == FDCAN1) { - return "FDCAN1"; - } -#endif - return "UNKNOWN"; -} - -// ===================================== HAL Callbacks ================================ -// TODO: Implement Family Checks -void FDCAN1_IT0_IRQHandler(void) -{ -#ifdef USECAN1 - HAL_FDCAN_IRQHandler(&hal_fdcan1); -#endif -} -void FDCAN1_IT1_IRQHandler(void) -{ -#ifdef USECAN1 - HAL_FDCAN_IRQHandler(&hal_fdcan1); -#endif -} - -void FDCAN2_IT0_IRQHandler(void) -{ -#ifdef USECAN2 - HAL_FDCAN_IRQHandler(&hal_fdcan2); -#endif -} -void FDCAN2_IT1_IRQHandler(void) -{ -#ifdef USECAN2 - HAL_FDCAN_IRQHandler(&hal_fdcan2); -#endif -} - -void FDCAN3_IT0_IRQHandler(void) -{ -#ifdef USECAN3 - HAL_FDCAN_IRQHandler(&hal_fdcan3); -#endif -} -void FDCAN3_IT1_IRQHandler(void) -{ -#ifdef USECAN3 - HAL_FDCAN_IRQHandler(&hal_fdcan3); -#endif -} - -// NEW THINGS - -void CAN_Timer_Start(void) -{ - static bool initialized = false; - - if (initialized) { - LOGOMATIC("CAN_Timer_Start: timer is already initialized\n"); - return; - } - - RCC_ClkInitTypeDef clkconfig = {0}; - uint32_t latency = 0; - HAL_RCC_GetClockConfig(&clkconfig, &latency); - - uint32_t apb1_div = clkconfig.APB1CLKDivider; - uint32_t tim_clock = (apb1_div == RCC_HCLK_DIV1) ? HAL_RCC_GetPCLK1Freq() : (2U * HAL_RCC_GetPCLK1Freq()); - - // 10 kHz counter clock keeps both PSC/ARR in range for a 1 second period. - const uint32_t counter_hz = 10000U; - if (tim_clock < counter_hz) { - LOGOMATIC("CAN_Timer_Start: APB1 clock is too slow to achieve desired timer frequency\n"); - return; - } - - uint32_t prescaler = (tim_clock / counter_hz) - 1U; - if (prescaler > 0xFFFFU) { - LOGOMATIC("CAN_Timer_Start: failed to initialize timer prescaler\n"); - return; - } - - uint32_t autoreload = ((CAN_TIMER_SEND_PERIOD_US * counter_hz) / 1000000U) - 1U; - if (autoreload > 0xFFFFU) { - LOGOMATIC("CAN_Timer_Start: failed to initialize timer autoreload\n"); - return; - } - - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM5); - - LL_TIM_InitTypeDef tim_init = {0}; - tim_init.Prescaler = prescaler; - tim_init.CounterMode = LL_TIM_COUNTERMODE_UP; - tim_init.Autoreload = autoreload; - tim_init.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; - - if (LL_TIM_Init(TIM5, &tim_init) != SUCCESS) { - LOGOMATIC("CAN_Timer_Start: failed to initialize timer\n"); - return; - } - - LL_TIM_SetClockSource(TIM5, LL_TIM_CLOCKSOURCE_INTERNAL); - LL_TIM_ClearFlag_UPDATE(TIM5); - LL_TIM_EnableIT_UPDATE(TIM5); - - HAL_NVIC_SetPriority(TIM5_IRQn, 15U, 0U); - HAL_NVIC_EnableIRQ(TIM5_IRQn); - - LL_TIM_EnableCounter(TIM5); - - initialized = true; - LOGOMATIC("CAN_Timer_Start: timer initialized\n"); - return; -} diff --git a/ECU/CMakeLists.txt b/ECU/CMakeLists.txt index d540a36e5..5c119b606 100644 --- a/ECU/CMakeLists.txt +++ b/ECU/CMakeLists.txt @@ -81,7 +81,6 @@ target_sources( Application/Src/CANutils.c Application/Src/Pinging.c Application/Src/Lights.c - Application/Src/ecu_can.c ) target_link_libraries( @@ -89,7 +88,7 @@ target_link_libraries( INTERFACE CANfigurator GR_ADC - #PERIPHERAL_CAN_LIB + PERIPHERAL_TIMEDCAN_LIB BitManipulations_Lib ) diff --git a/ECU/Core/Src/main.c b/ECU/Core/Src/main.c index efc335045..66c567d5e 100644 --- a/ECU/Core/Src/main.c +++ b/ECU/Core/Src/main.c @@ -40,7 +40,7 @@ #include "StateTicks.h" #include "StateUtils.h" #include "adc.h" -#include "ecu_can.h" +#include "can.h" #include "stm32g4xx_hal.h" /* USER CODE END Includes */ @@ -343,7 +343,6 @@ void CAN_Configure(void) // timer can can_start(stateLump.primary_can); can_start(stateLump.data_can); - CAN_Timer_Start(); } /** * @brief The application entry point. diff --git a/ECU/Core/Src/stm32g4xx_it.c b/ECU/Core/Src/stm32g4xx_it.c index 97519bf4d..01ab766d9 100644 --- a/ECU/Core/Src/stm32g4xx_it.c +++ b/ECU/Core/Src/stm32g4xx_it.c @@ -20,7 +20,7 @@ /* Includes ------------------------------------------------------------------*/ #include "stm32g4xx_it.h" -#include "ecu_can.h" +#include "can.h" #include "main.h" #include "stm32g4xx_ll_tim.h" /* Private includes ----------------------------------------------------------*/ @@ -190,13 +190,5 @@ void SysTick_Handler(void) } /* USER CODE BEGIN 1 */ -void TIM5_IRQHandler(void) -{ - if (LL_TIM_IsActiveFlag_UPDATE(TIM5)) { - LL_TIM_ClearFlag_UPDATE(TIM5); - can_tx_dequeue_helper(stateLump.primary_can); // TODO: make this work for multiple instances if needed - can_tx_dequeue_helper(stateLump.data_can); - HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_11); - } -} + /* USER CODE END 1 */ From 02521ccfe91b86cf400a2069ca20ea8e1a114a46 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Tue, 12 May 2026 01:11:31 -0700 Subject: [PATCH 02/20] Bring back HOOTL testing Signed-off-by: Daniel Hansen --- ECU/CMakeLists.txt | 3 +-- ECU/Test/Inc/can.h | 2 +- ECU/Test/Inc/main.h | 2 ++ ECU/Test/Src/StateTicksTest.c | 2 +- ECU/Test/Src/can.c | 8 ++++---- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/ECU/CMakeLists.txt b/ECU/CMakeLists.txt index 5c119b606..148a6806b 100644 --- a/ECU/CMakeLists.txt +++ b/ECU/CMakeLists.txt @@ -14,8 +14,7 @@ endif() get_filename_component(GR_PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) #TODO: when CAN implementation is settled, turn HOOTL tests back on -#if(CMAKE_PRESET_NAME STREQUAL "HOOTLTest") -if(FALSE) +if(CMAKE_PRESET_NAME STREQUAL "HOOTLTest") add_executable(StateTicksTest) target_include_directories( StateTicksTest diff --git a/ECU/Test/Inc/can.h b/ECU/Test/Inc/can.h index 87b34fa85..cc7810816 100644 --- a/ECU/Test/Inc/can.h +++ b/ECU/Test/Inc/can.h @@ -374,7 +374,7 @@ typedef struct { CANHandle *can_init(const CANConfig *config); // user must supply an rx callback function int can_start(CANHandle *handle); int can_stop(CANHandle *handle); -int can_send(CANHandle *handle, FDCANTxMessage *buffer); +int can_enqueue(CANHandle *canHandle, FDCANTxMessage *message); int can_release(CANHandle *handle); // deinit circular buffer and turn off can peripheral and gpios int can_add_filter(CANHandle *handle, FDCAN_FilterTypeDef *filter); // alternatively use diff --git a/ECU/Test/Inc/main.h b/ECU/Test/Inc/main.h index 4da12629e..64e24ccfa 100644 --- a/ECU/Test/Inc/main.h +++ b/ECU/Test/Inc/main.h @@ -1 +1,3 @@ #include "GRCAN_NODE_ID.h" + +#define FDCAN_CLASSIC_CAN 0 diff --git a/ECU/Test/Src/StateTicksTest.c b/ECU/Test/Src/StateTicksTest.c index ca33a402c..3f844fc29 100644 --- a/ECU/Test/Src/StateTicksTest.c +++ b/ECU/Test/Src/StateTicksTest.c @@ -5,7 +5,7 @@ #include "Logomatic.h" #include "StateData.h" #include "StateUtils.h" -#include "ecu_can.h" +#include "can.h" #include "stm32g4xx_hal.h" #include "stm32g4xx_hal_fdcan.h" diff --git a/ECU/Test/Src/can.c b/ECU/Test/Src/can.c index ba9e6581a..d8756c259 100644 --- a/ECU/Test/Src/can.c +++ b/ECU/Test/Src/can.c @@ -1,5 +1,5 @@ #include "Unused.h" -#include "ecu_can.h" +#include "can.h" int can_start(CANHandle *handle) { @@ -13,10 +13,10 @@ int can_stop(CANHandle *handle) return 0; } -int can_send(CANHandle *handle, FDCANTxMessage *buffer) +int can_enqueue(CANHandle *canHandle, FDCANTxMessage *message) { - UNUSED(handle); - UNUSED(buffer); + UNUSED(canHandle); + UNUSED(message); return 0; } From 44484e6cc9900d8c7722f5038ae05eedaf435f77 Mon Sep 17 00:00:00 2001 From: Casey Zwicker Date: Wed, 13 May 2026 00:56:45 -0700 Subject: [PATCH 03/20] Set bspd sense in test to new good value --- ECU/Test/Src/StateTicksTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ECU/Test/Src/StateTicksTest.c b/ECU/Test/Src/StateTicksTest.c index 3f844fc29..df18705da 100644 --- a/ECU/Test/Src/StateTicksTest.c +++ b/ECU/Test/Src/StateTicksTest.c @@ -70,7 +70,7 @@ int main(void) // ## Step 0.0 ## // ########################### LOGOMATIC("State Ticks test started\n"); - ECU_StateData stateLumpTest = {.ecu_state = GR_GLV_ON, .ams_sense = 1.5, .imd_sense = 1.5, .bspd_sense = 1.5}; + ECU_StateData stateLumpTest = {.ecu_state = GR_GLV_ON, .ams_sense = 1.5, .imd_sense = 1.5, .bspd_sense = 1.2}; LOGOMATIC("Check GLV ON at boot\n"); stateLumpTest.ecu_state = GR_GLV_ON; stateLumpTest.acu_software_latch = 1; From ebe95dd3db9924cda3cbf09f8f152067b608c148 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 13 May 2026 06:20:13 -0700 Subject: [PATCH 04/20] Fix analog data and inverter command frequency Signed-off-by: Daniel Hansen --- ECU/Application/Src/StateTicks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ECU/Application/Src/StateTicks.c b/ECU/Application/Src/StateTicks.c index 34281c706..9420ed5b7 100644 --- a/ECU/Application/Src/StateTicks.c +++ b/ECU/Application/Src/StateTicks.c @@ -270,8 +270,8 @@ void ECU_Drive_Active(ECU_StateData *stateData) .steering_angle_signal = stateData->steering_angle_signal, .aux_signal = stateData->aux_signal}; UNUSED(message); // FIXME Eventually figure out what to do with this message here - // ECU_CAN_Send(GRCAN_BUS_DATA, GRCAN_TCM, GRCAN_ECU_ANALOG_DATA, &message, sizeof(message)); // FIXME - last_can_inverter_request_millis = millis_since_boot; + ECU_CAN_Send(GRCAN_BUS_DATA, GRCAN_TCM, GRCAN_ECU_ANALOG_DATA, &message, 10); // FIXME + last_can_tcm_request_millis = millis_since_boot; } } From 2e3667681c98ad2649196da95ca1002c7f13fc3b Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 13 May 2026 06:21:10 -0700 Subject: [PATCH 05/20] Sanity set initial statics to 0 Signed-off-by: Daniel Hansen --- ECU/Application/Src/StateTicks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ECU/Application/Src/StateTicks.c b/ECU/Application/Src/StateTicks.c index 9420ed5b7..d610546eb 100644 --- a/ECU/Application/Src/StateTicks.c +++ b/ECU/Application/Src/StateTicks.c @@ -247,7 +247,7 @@ void ECU_Drive_Active(ECU_StateData *stateData) torque_request = 0; } - static uint32_t last_can_inverter_request_millis; + static uint32_t last_can_inverter_request_millis = 0; if (RATE_LIMIT_100_HZ(millis_since_boot, last_can_inverter_request_millis)) { GRCAN_INVERTER_COMMAND_MSG message = {.set_ac_current = torque_request * 100 + 32768, .set_dc_current = torque_request * 100 + 32768, .drive_enable = 1, .rpm_limit = 0}; ECU_CAN_Send(GRCAN_BUS_PRIMARY, GRCAN_GR_Inverter, GRCAN_INVERTER_COMMAND, &message, sizeof(message)); @@ -259,7 +259,7 @@ void ECU_Drive_Active(ECU_StateData *stateData) // placeholder for pedal data // TODO: determine send time (15, 20 ms?) - static uint32_t last_can_tcm_request_millis; + static uint32_t last_can_tcm_request_millis = 0; if (RATE_LIMIT_100_HZ(millis_since_boot, last_can_tcm_request_millis)) { GRCAN_ECU_ANALOG_DATA_MSG message = {.bspd_signal = stateData->bspd_signal, .bse_signal = stateData->bse_signal, From 619f576bc0f58a670f7ed44a01eb64e6bc06f972 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 13 May 2026 06:21:41 -0700 Subject: [PATCH 06/20] Fix can dlc size Signed-off-by: Daniel Hansen --- ECU/Application/Src/CANutils.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/ECU/Application/Src/CANutils.c b/ECU/Application/Src/CANutils.c index 473376efe..e8d8847d1 100644 --- a/ECU/Application/Src/CANutils.c +++ b/ECU/Application/Src/CANutils.c @@ -28,12 +28,34 @@ void ECU_CAN_Send(GRCAN_BUS_ID bus, GRCAN_NODE_ID destNode, GRCAN_MSG_ID message uint32_t ID = ((0xFF & GRCAN_ECU) << 20) | ((0xFFF & messageID) << 8) | (0xFF & destNode); + uint8_t dlc = 0; + if (size <= 8) { + dlc = size; + } else if (size <= 12) { + dlc = 9; + } else if (size <= 16) { + dlc = 10; + } else if (size <= 20) { + dlc = 11; + } else if (size <= 24) { + dlc = 12; + } else if (size <= 32) { + dlc = 13; + } else if (size <= 48) { + dlc = 14; + } else if (size <= 64) { + dlc = 15; + } else { + dlc = FDCAN_MAX_DATA_BYTES; // should never happen due to earlier check + LOGOMATIC("Invalid CAN data size after check: %ld\n", size); + } + FDCAN_TxHeaderTypeDef header = { .Identifier = ID, .IdType = FDCAN_EXTENDED_ID, .TxFrameType = FDCAN_DATA_FRAME, .ErrorStateIndicator = FDCAN_ESI_ACTIVE, - .DataLength = size, + .DataLength = dlc, .BitRateSwitch = FDCAN_BRS_OFF, .TxEventFifoControl = FDCAN_NO_TX_EVENTS, .MessageMarker = 0, From d8bfa01da5202e2cb0bfb1612968e4addbd69308 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 13 May 2026 06:21:58 -0700 Subject: [PATCH 07/20] Revise dti send can Signed-off-by: Daniel Hansen --- ECU/Application/Src/CANutils.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ECU/Application/Src/CANutils.c b/ECU/Application/Src/CANutils.c index e8d8847d1..04cc29398 100644 --- a/ECU/Application/Src/CANutils.c +++ b/ECU/Application/Src/CANutils.c @@ -100,17 +100,20 @@ void ECU_CAN_Send_DTI(GRCAN_CUSTOM_ID msgID, void *data, uint32_t size) TxHeader.Identifier = msgID; TxHeader.DataLength = size; - TxHeader.IdType = FDCAN_EXTENDED_ID; - TxHeader.FDFormat = FDCAN_CLASSIC_CAN; FDCANTxMessage msg = {0}; msg.tx_header = TxHeader; - for (uint32_t i = 0; i < size; i++) { - msg.data[size - i - 1] = ((uint8_t *)data)[i]; + uint8_t temp; + for (uint16_t i = 0; i < size / 2; ++i) { + temp = ((uint8_t *)data)[i]; + ((uint8_t *)data)[i] = ((uint8_t *)data)[size - i - 1]; + ((uint8_t *)data)[size - i - 1] = temp; } + memcpy(&(msg.data), data, size); + // can_send(primary_can, &msg); can_enqueue(stateLump.primary_can, &msg); } From b0180162925f46267d15502e7c004db1baf7ac11 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 13 May 2026 06:22:14 -0700 Subject: [PATCH 08/20] Scale torque request respectfully Signed-off-by: Daniel Hansen --- ECU/Application/Src/StateTicks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ECU/Application/Src/StateTicks.c b/ECU/Application/Src/StateTicks.c index d610546eb..559f23bb7 100644 --- a/ECU/Application/Src/StateTicks.c +++ b/ECU/Application/Src/StateTicks.c @@ -251,7 +251,8 @@ void ECU_Drive_Active(ECU_StateData *stateData) if (RATE_LIMIT_100_HZ(millis_since_boot, last_can_inverter_request_millis)) { GRCAN_INVERTER_COMMAND_MSG message = {.set_ac_current = torque_request * 100 + 32768, .set_dc_current = torque_request * 100 + 32768, .drive_enable = 1, .rpm_limit = 0}; ECU_CAN_Send(GRCAN_BUS_PRIMARY, GRCAN_GR_Inverter, GRCAN_INVERTER_COMMAND, &message, sizeof(message)); - ECU_CAN_Send_DTI(DTI_CONTROL_12_CAN_ID, &message.drive_enable, 2); + ECU_CAN_Send_DTI(DTI_CONTROL_12_CAN_ID, &message.drive_enable, 1); + message.set_ac_current = torque_request * 10; ECU_CAN_Send_DTI(DTI_CONTROL_1_CAN_ID, &message.set_ac_current, 2); last_can_inverter_request_millis = millis_since_boot; } From 5d63c9d7d756e37b067802b577f998abd2560516 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 13 May 2026 06:22:27 -0700 Subject: [PATCH 09/20] Random tested values for throttle Signed-off-by: Daniel Hansen --- ECU/Application/Inc/StateUtils.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ECU/Application/Inc/StateUtils.h b/ECU/Application/Inc/StateUtils.h index 215ca9ec7..ff9d427e7 100644 --- a/ECU/Application/Inc/StateUtils.h +++ b/ECU/Application/Inc/StateUtils.h @@ -15,10 +15,10 @@ uint32_t MillisecondsSinceBoot(void); #define BRAKE_F_MAX 4095 // TODO: need to be determined FIXME: Rename better #define BRAKE_R_MIN 0 // TODO: need to be determined FIXME: Rename better #define BRAKE_R_MAX 4095 // TODO: need to be determined FIXME: Rename better -#define THROTTLE_MIN_1 0 // TODO: need to be determined -#define THROTTLE_MAX_1 4095 // TODO: need to be determined -#define THROTTLE_MIN_2 0 // TODO: need to be determined -#define THROTTLE_MAX_2 4095 // TODO: need to be determined +#define THROTTLE_MIN_1 308 // TODO: need to be determined +#define THROTTLE_MAX_1 3689 // TODO: need to be determined +#define THROTTLE_MIN_2 303 // TODO: need to be determined +#define THROTTLE_MAX_2 3735 // TODO: need to be determined #define BSE_MAX 4096.0f #define BSE_DEADZONE 1.2f #define MAX_BSE_FAILURE_TIME 100 From 0b7fc64e1cde67ea46793994a5018b80e9975bc2 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 17:16:12 +0000 Subject: [PATCH 10/20] Automatic Clang-Format: Standardized formatting automatically --- ECU/Application/Inc/StateUtils.h | 4 ++-- ECU/Application/Src/StateTicks.c | 4 ++-- ECU/Test/Src/can.c | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ECU/Application/Inc/StateUtils.h b/ECU/Application/Inc/StateUtils.h index ff9d427e7..05756d270 100644 --- a/ECU/Application/Inc/StateUtils.h +++ b/ECU/Application/Inc/StateUtils.h @@ -15,9 +15,9 @@ uint32_t MillisecondsSinceBoot(void); #define BRAKE_F_MAX 4095 // TODO: need to be determined FIXME: Rename better #define BRAKE_R_MIN 0 // TODO: need to be determined FIXME: Rename better #define BRAKE_R_MAX 4095 // TODO: need to be determined FIXME: Rename better -#define THROTTLE_MIN_1 308 // TODO: need to be determined +#define THROTTLE_MIN_1 308 // TODO: need to be determined #define THROTTLE_MAX_1 3689 // TODO: need to be determined -#define THROTTLE_MIN_2 303 // TODO: need to be determined +#define THROTTLE_MIN_2 303 // TODO: need to be determined #define THROTTLE_MAX_2 3735 // TODO: need to be determined #define BSE_MAX 4096.0f #define BSE_DEADZONE 1.2f diff --git a/ECU/Application/Src/StateTicks.c b/ECU/Application/Src/StateTicks.c index 559f23bb7..f661a1d1b 100644 --- a/ECU/Application/Src/StateTicks.c +++ b/ECU/Application/Src/StateTicks.c @@ -270,8 +270,8 @@ void ECU_Drive_Active(ECU_StateData *stateData) .brakeline_r_signal = stateData->Brake_R_Signal, .steering_angle_signal = stateData->steering_angle_signal, .aux_signal = stateData->aux_signal}; - UNUSED(message); // FIXME Eventually figure out what to do with this message here - ECU_CAN_Send(GRCAN_BUS_DATA, GRCAN_TCM, GRCAN_ECU_ANALOG_DATA, &message, 10); // FIXME + UNUSED(message); // FIXME Eventually figure out what to do with this message here + ECU_CAN_Send(GRCAN_BUS_DATA, GRCAN_TCM, GRCAN_ECU_ANALOG_DATA, &message, 10); // FIXME last_can_tcm_request_millis = millis_since_boot; } } diff --git a/ECU/Test/Src/can.c b/ECU/Test/Src/can.c index d8756c259..e6154a08f 100644 --- a/ECU/Test/Src/can.c +++ b/ECU/Test/Src/can.c @@ -1,6 +1,7 @@ -#include "Unused.h" #include "can.h" +#include "Unused.h" + int can_start(CANHandle *handle) { UNUSED(handle); From de6494772ce6ff5480b32992638fccb26729b333 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 13 May 2026 10:25:54 -0700 Subject: [PATCH 11/20] Switch to custom bytes to CAN DLC function for hootl test Signed-off-by: Daniel Hansen --- ECU/Application/Src/CANutils.c | 24 +----------------------- ECU/Test/Inc/can.h | 11 ++++++----- ECU/Test/Src/can.c | 6 ++++++ Lib/Peripherals/TimedCAN/Inc/can.h | 1 + Lib/Peripherals/TimedCAN/Src/can.c | 24 ++++++++++++++++++++++++ 5 files changed, 38 insertions(+), 28 deletions(-) diff --git a/ECU/Application/Src/CANutils.c b/ECU/Application/Src/CANutils.c index 04cc29398..d5e11c199 100644 --- a/ECU/Application/Src/CANutils.c +++ b/ECU/Application/Src/CANutils.c @@ -28,34 +28,12 @@ void ECU_CAN_Send(GRCAN_BUS_ID bus, GRCAN_NODE_ID destNode, GRCAN_MSG_ID message uint32_t ID = ((0xFF & GRCAN_ECU) << 20) | ((0xFFF & messageID) << 8) | (0xFF & destNode); - uint8_t dlc = 0; - if (size <= 8) { - dlc = size; - } else if (size <= 12) { - dlc = 9; - } else if (size <= 16) { - dlc = 10; - } else if (size <= 20) { - dlc = 11; - } else if (size <= 24) { - dlc = 12; - } else if (size <= 32) { - dlc = 13; - } else if (size <= 48) { - dlc = 14; - } else if (size <= 64) { - dlc = 15; - } else { - dlc = FDCAN_MAX_DATA_BYTES; // should never happen due to earlier check - LOGOMATIC("Invalid CAN data size after check: %ld\n", size); - } - FDCAN_TxHeaderTypeDef header = { .Identifier = ID, .IdType = FDCAN_EXTENDED_ID, .TxFrameType = FDCAN_DATA_FRAME, .ErrorStateIndicator = FDCAN_ESI_ACTIVE, - .DataLength = dlc, + .DataLength = BytesToCANDLC(size), .BitRateSwitch = FDCAN_BRS_OFF, .TxEventFifoControl = FDCAN_NO_TX_EVENTS, .MessageMarker = 0, diff --git a/ECU/Test/Inc/can.h b/ECU/Test/Inc/can.h index cc7810816..c44fb112c 100644 --- a/ECU/Test/Inc/can.h +++ b/ECU/Test/Inc/can.h @@ -377,11 +377,12 @@ int can_stop(CANHandle *handle); int can_enqueue(CANHandle *canHandle, FDCANTxMessage *message); int can_release(CANHandle *handle); // deinit circular buffer and turn off can peripheral and gpios int can_add_filter(CANHandle *handle, FDCAN_FilterTypeDef *filter); -// alternatively use -// HAL_FDCAN_ConfigGlobalFilter() //important to accept nonmatching frames into -// HAL_FDCAN_ConfigFilter() +uint8_t BytesToCANDLC(uint32_t num_bytes); + // alternatively use + // HAL_FDCAN_ConfigGlobalFilter() //important to accept nonmatching frames into + // HAL_FDCAN_ConfigFilter() -// doesn't need a handle, CAN cores share peripheral clock -void can_set_clksource(uint32_t clksource); // ex. LL_RCC_FDCAN_CLKSOURCE_PCLK1 for STM32G474RE + // doesn't need a handle, CAN cores share peripheral clock + void can_set_clksource(uint32_t clksource); // ex. LL_RCC_FDCAN_CLKSOURCE_PCLK1 for STM32G474RE #endif diff --git a/ECU/Test/Src/can.c b/ECU/Test/Src/can.c index e6154a08f..712d1d776 100644 --- a/ECU/Test/Src/can.c +++ b/ECU/Test/Src/can.c @@ -33,3 +33,9 @@ int can_add_filter(CANHandle *handle, FDCAN_FilterTypeDef *filter) UNUSED(filter); return 0; } + +uint8_t BytesToCANDLC(uint32_t num_bytes) +{ + UNUSED(num_bytes); + return 0; +} diff --git a/Lib/Peripherals/TimedCAN/Inc/can.h b/Lib/Peripherals/TimedCAN/Inc/can.h index 71584c0dc..5c1bd5af7 100644 --- a/Lib/Peripherals/TimedCAN/Inc/can.h +++ b/Lib/Peripherals/TimedCAN/Inc/can.h @@ -92,6 +92,7 @@ CAN_STATUS can_stop(CANHandle *handle); CAN_STATUS can_release(CANHandle *handle); // deinit circular buffer and turn off can peripheral and gpios CAN_STATUS can_add_filter(CANHandle *handle, FDCAN_FilterTypeDef *filter); CAN_STATUS can_enqueue(CANHandle *handle, FDCANTxMessage *message); // adds to software buffer, returns error if full +uint8_t BytesToCANDLC(uint32_t num_bytes); // pass in a buffer to store the status string // int can_info(char* ); diff --git a/Lib/Peripherals/TimedCAN/Src/can.c b/Lib/Peripherals/TimedCAN/Src/can.c index a7a1bfaf1..34e31deed 100644 --- a/Lib/Peripherals/TimedCAN/Src/can.c +++ b/Lib/Peripherals/TimedCAN/Src/can.c @@ -420,6 +420,30 @@ void can_tx_dequeue_helper(CANHandle *handle) dwt_timer_t send_timer = {0}; #endif +uint8_t BytesToCANDLC(uint32_t num_bytes) +{ + if (num_bytes <= 8) { + return num_bytes; + } else if (num_bytes <= 12) { + return FDCAN_DLC_BYTES_12; + } else if (num_bytes <= 16) { + return FDCAN_DLC_BYTES_16; + } else if (num_bytes <= 20) { + return FDCAN_DLC_BYTES_20; + } else if (num_bytes <= 24) { + return FDCAN_DLC_BYTES_24; + } else if (num_bytes <= 32) { + return FDCAN_DLC_BYTES_32; + } else if (num_bytes <= 48) { + return FDCAN_DLC_BYTES_48; + } else if (num_bytes <= 64) { + return FDCAN_DLC_BYTES_64; + } else { // Should never happen + return FDCAN_DLC_BYTES_0; + LOGOMATIC("Invalid CAN data size after check: %ld\n", num_bytes); + } +} + CAN_STATUS can_enqueue(CANHandle *canHandle, FDCANTxMessage *message) { From 3a1c3904267f9ef2c195e5bc799802d922e4949a Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 13 May 2026 14:12:36 -0700 Subject: [PATCH 12/20] Allegedly fix rtd button press latch Signed-off-by: Daniel Hansen --- ECU/Application/Src/StateTicks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ECU/Application/Src/StateTicks.c b/ECU/Application/Src/StateTicks.c index f661a1d1b..74c5e9708 100644 --- a/ECU/Application/Src/StateTicks.c +++ b/ECU/Application/Src/StateTicks.c @@ -179,6 +179,9 @@ void ECU_Precharge_Complete(ECU_StateData *stateData) ECU_Transition_To_Drive_Active(stateData); stateData->rtd_button_pressed = false; return; + } else { + // Demand that we only transition to drive active if the RTD button is pressed while pressing the brake. + stateData->rtd_button_pressed = false; } } From a2a6beeeb62d5b3cfde6afc65d892a339a8a08ae Mon Sep 17 00:00:00 2001 From: kzwicker Date: Wed, 13 May 2026 15:17:42 -0700 Subject: [PATCH 13/20] Read and reset the variable set by the button read interrupt exactly once to prevent footguns and race conditions (#477) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …y once and reset in exactly one place that always executes # ## Problem and Scope ## Description ## Gotchas and Limitations ## Testing - [x] HOOTL testing - [ ] HITL testing - [ ] Human tested ### Testing Details ## Larger Impact ## Additional Context and Ticket --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- ECU/Application/Inc/StateData.h | 2 ++ ECU/Application/Src/CANdler.c | 10 ++++------ ECU/Application/Src/StateTicks.c | 23 ++++++++++++++--------- ECU/Test/Inc/can.h | 10 +++++----- ECU/Test/Src/StateTicksTest.c | 28 +++++++++++++++++++++------- Lib/Peripherals/TimedCAN/Src/can.c | 2 +- 6 files changed, 47 insertions(+), 28 deletions(-) diff --git a/ECU/Application/Inc/StateData.h b/ECU/Application/Inc/StateData.h index 27fc53c48..47b3cd03a 100644 --- a/ECU/Application/Inc/StateData.h +++ b/ECU/Application/Inc/StateData.h @@ -87,7 +87,9 @@ typedef volatile struct ECU_StateData { uint8_t glv_soc; uint8_t acu_error_warning_bits; uint8_t inverter_fault_map; + bool ts_active_button_press_interrupt; bool ts_active_button_pressed; + bool rtd_button_press_interrupt; bool rtd_button_pressed; bool ir_plus; bool ir_minus; diff --git a/ECU/Application/Src/CANdler.c b/ECU/Application/Src/CANdler.c index db0354b09..30ff94704 100644 --- a/ECU/Application/Src/CANdler.c +++ b/ECU/Application/Src/CANdler.c @@ -110,17 +110,15 @@ void ECU_CAN_MessageHandler(ECU_StateData *state_data, GRCAN_BUS_ID bus_id, GRCA // LET IT BE KNOWN: these things are LSB FIRST, TODO: I'll get it right later if (state_data->ecu_state == GR_GLV_ON) { - state_data->ts_active_button_pressed = dash_data->button_flags & 1; + state_data->ts_active_button_press_interrupt = dash_data->button_flags & 1; } else { - state_data->ts_active_button_pressed = (dash_data->button_flags >> 2) & 1; + state_data->ts_active_button_press_interrupt = (dash_data->button_flags >> 2) & 1; } if (state_data->ecu_state == GR_PRECHARGE_COMPLETE) { - state_data->rtd_button_pressed = (dash_data->button_flags >> 1) & 1; + state_data->rtd_button_press_interrupt = (dash_data->button_flags >> 1) & 1; } else if (state_data->ecu_state == GR_DRIVE_ACTIVE) { - state_data->rtd_button_pressed = (dash_data->button_flags >> 3) & 1; - } else { - state_data->rtd_button_pressed = false; + state_data->rtd_button_press_interrupt = (dash_data->button_flags >> 3) & 1; } break; diff --git a/ECU/Application/Src/StateTicks.c b/ECU/Application/Src/StateTicks.c index 74c5e9708..fa23465de 100644 --- a/ECU/Application/Src/StateTicks.c +++ b/ECU/Application/Src/StateTicks.c @@ -60,6 +60,20 @@ void ECU_State_Tick(void) // bmsFailure(&stateLump) || imdFailure(&stateLump); + if (stateLump.ts_active_button_press_interrupt) { + stateLump.ts_active_button_press_interrupt = false; + stateLump.ts_active_button_pressed = true; + } else { + stateLump.ts_active_button_pressed = false; + } + + if (stateLump.rtd_button_press_interrupt) { + stateLump.rtd_button_press_interrupt = false; + stateLump.rtd_button_pressed = true; + } else { + stateLump.rtd_button_pressed = false; + } + switch (stateLump.ecu_state) { case GR_GLV_OFF: ECU_GLV_Off(&stateLump); @@ -106,7 +120,6 @@ void ECU_GLV_On(ECU_StateData *stateData) if (stateData->ts_active_button_pressed /* && stateData->ir_plus*/) { // TODO: Talk to Owen if this is correct for precharge start confirmation LOGOMATIC("GLV ON to PRECHARGE START!\n"); ECU_Transition_To_Precharge_Engaged(stateData); - stateData->ts_active_button_pressed = false; return; } } @@ -142,7 +155,6 @@ void ECU_Precharge_Engaged(ECU_StateData *stateData) LOGOMATIC("ERROR: ts_active PRESSED! PRECHARGE ENGAGED to TS DISCHARGE START!\n"); ECU_CAN_Send(GRCAN_BUS_PRIMARY, GRCAN_Debugger, GRCAN_DEBUG_2_0, "TS-P-ITR", 8); ECU_Transition_To_Tractive_System_Discharge(stateData); - stateData->ts_active_button_pressed = false; return; } } @@ -153,7 +165,6 @@ void ECU_Precharge_Complete(ECU_StateData *stateData) if (stateData->ts_active_button_pressed) { LOGOMATIC("TS Active Toggled Off. Discharging Tractive System.\n"); ECU_Transition_To_Tractive_System_Discharge(stateData); - stateData->ts_active_button_pressed = false; return; } if (CriticalError(stateData)) { @@ -177,11 +188,7 @@ void ECU_Precharge_Complete(ECU_StateData *stateData) ECU_CAN_Send(GRCAN_BUS_DATA, GRCAN_TCM, GRCAN_ECU_ANALOG_DATA, &pedals_message, sizeof(pedals_message)); LOGOMATIC("PRECHARGE COMPLETE to DRIVE START/ACTIVE!\n"); ECU_Transition_To_Drive_Active(stateData); - stateData->rtd_button_pressed = false; return; - } else { - // Demand that we only transition to drive active if the RTD button is pressed while pressing the brake. - stateData->rtd_button_pressed = false; } } @@ -205,7 +212,6 @@ void ECU_Drive_Active(ECU_StateData *stateData) LOGOMATIC("Error: TS active button pressed in Drive Active state. Discharging Tractive System.\n"); ECU_Transition_To_Tractive_System_Discharge(stateData); ECU_CAN_Send(GRCAN_BUS_PRIMARY, GRCAN_Debugger, GRCAN_DEBUG_2_0, "DA-CritE", 8); - stateData->ts_active_button_pressed = false; return; } @@ -221,7 +227,6 @@ void ECU_Drive_Active(ECU_StateData *stateData) if (vehicle_is_moving(stateData)) { LOGOMATIC("Warning: Vehicle is moving during state transition.\n"); } - stateData->rtd_button_pressed = false; return; } diff --git a/ECU/Test/Inc/can.h b/ECU/Test/Inc/can.h index c44fb112c..0a3ebf422 100644 --- a/ECU/Test/Inc/can.h +++ b/ECU/Test/Inc/can.h @@ -378,11 +378,11 @@ int can_enqueue(CANHandle *canHandle, FDCANTxMessage *message); int can_release(CANHandle *handle); // deinit circular buffer and turn off can peripheral and gpios int can_add_filter(CANHandle *handle, FDCAN_FilterTypeDef *filter); uint8_t BytesToCANDLC(uint32_t num_bytes); - // alternatively use - // HAL_FDCAN_ConfigGlobalFilter() //important to accept nonmatching frames into - // HAL_FDCAN_ConfigFilter() +// alternatively use +// HAL_FDCAN_ConfigGlobalFilter() //important to accept nonmatching frames into +// HAL_FDCAN_ConfigFilter() - // doesn't need a handle, CAN cores share peripheral clock - void can_set_clksource(uint32_t clksource); // ex. LL_RCC_FDCAN_CLKSOURCE_PCLK1 for STM32G474RE +// doesn't need a handle, CAN cores share peripheral clock +void can_set_clksource(uint32_t clksource); // ex. LL_RCC_FDCAN_CLKSOURCE_PCLK1 for STM32G474RE #endif diff --git a/ECU/Test/Src/StateTicksTest.c b/ECU/Test/Src/StateTicksTest.c index df18705da..84126ad0d 100644 --- a/ECU/Test/Src/StateTicksTest.c +++ b/ECU/Test/Src/StateTicksTest.c @@ -36,6 +36,20 @@ static void ECU_Pseudo_State_Tick(ECU_StateData *stateLumpTest) LOGOMATIC("TSSI: TS Normal\n"); } + if (stateLumpTest->ts_active_button_press_interrupt) { + stateLumpTest->ts_active_button_press_interrupt = false; + stateLumpTest->ts_active_button_pressed = true; + } else { + stateLumpTest->ts_active_button_pressed = false; + } + + if (stateLumpTest->rtd_button_press_interrupt) { + stateLumpTest->rtd_button_press_interrupt = false; + stateLumpTest->rtd_button_pressed = true; + } else { + stateLumpTest->rtd_button_pressed = false; + } + switch (stateLumpTest->ecu_state) { case GR_GLV_OFF: ECU_GLV_Off(stateLumpTest); @@ -80,7 +94,7 @@ int main(void) // ## Step 0.1 ## // ########################## LOGOMATIC("Press and release RTD -> STAY IN GLV ON\n"); - stateLumpTest.rtd_button_pressed = true; + stateLumpTest.rtd_button_press_interrupt = true; ECU_Pseudo_State_Tick(&stateLumpTest); if (stateLumpTest.ecu_state != GR_GLV_ON) { LOGOMATIC("0.1 Failure: ecu state not in GLV ON\n"); @@ -137,7 +151,7 @@ int main(void) // ## Step 0.3 ## // ########################## LOGOMATIC("Press TS ACTIVE: Go to PRECHARGE ENGAGE\n"); - stateLumpTest.ts_active_button_pressed = true; + stateLumpTest.ts_active_button_press_interrupt = true; stateLumpTest.ir_minus = true; ECU_Pseudo_State_Tick(&stateLumpTest); if (stateLumpTest.ecu_state != GR_PRECHARGE_ENGAGED) { @@ -177,7 +191,7 @@ int main(void) // ## Step 0.6 ## // ########################## LOGOMATIC("Press RTD -> STAY IN PRECHARGE COMPLETE\n"); - stateLumpTest.rtd_button_pressed = true; + stateLumpTest.rtd_button_press_interrupt = true; ECU_Pseudo_State_Tick(&stateLumpTest); if (stateLumpTest.ecu_state != GR_PRECHARGE_COMPLETE) { LOGOMATIC("0.6 Failure: ecu state not in precharge complete\n"); @@ -188,7 +202,7 @@ int main(void) return 6; } LOGOMATIC("Release RTD -> STAY IN PRECHARGE COMPLETE\n"); - stateLumpTest.rtd_button_pressed = false; + stateLumpTest.rtd_button_press_interrupt = false; ECU_Pseudo_State_Tick(&stateLumpTest); if (stateLumpTest.ecu_state != GR_PRECHARGE_COMPLETE) { LOGOMATIC("0.6 Failure: ecu state not in precharge complete\n"); @@ -205,7 +219,7 @@ int main(void) LOGOMATIC("Press and release the RTD button WHILE pressing the brake\n"); stateLumpTest.bse_signal = BSE_MAX; LOGOMATIC("Press RTD\n"); - stateLumpTest.rtd_button_pressed = true; + stateLumpTest.rtd_button_press_interrupt = true; ECU_Pseudo_State_Tick(&stateLumpTest); LOGOMATIC("Release RTD\n"); ECU_Pseudo_State_Tick(&stateLumpTest); @@ -335,7 +349,7 @@ int main(void) // ## Step 0.15 ## // ########################## LOGOMATIC("Press RTD -> MOVE TO PRECHARGE COMPLETE\n"); - stateLumpTest.rtd_button_pressed = !stateLumpTest.rtd_button_pressed; + stateLumpTest.rtd_button_press_interrupt = true; ECU_Pseudo_State_Tick(&stateLumpTest); if (stateLumpTest.ecu_state != GR_PRECHARGE_COMPLETE) { LOGOMATIC("0.15 Failure: ecu state not in precharge complete\n"); @@ -392,7 +406,7 @@ int main(void) // ## Step 0.18 ## // ########################## LOGOMATIC("Press TS Active Button -> MOVE to TS DISCHARGE\n"); - stateLumpTest.ts_active_button_pressed = !stateLumpTest.ts_active_button_pressed; + stateLumpTest.ts_active_button_press_interrupt = true; ECU_Pseudo_State_Tick(&stateLumpTest); if (stateLumpTest.ecu_state != GR_TS_DISCHARGE) { LOGOMATIC("0.18 Failure: ecu state not in ts discharge\n"); diff --git a/Lib/Peripherals/TimedCAN/Src/can.c b/Lib/Peripherals/TimedCAN/Src/can.c index 34e31deed..486b1fcf8 100644 --- a/Lib/Peripherals/TimedCAN/Src/can.c +++ b/Lib/Peripherals/TimedCAN/Src/can.c @@ -438,7 +438,7 @@ uint8_t BytesToCANDLC(uint32_t num_bytes) return FDCAN_DLC_BYTES_48; } else if (num_bytes <= 64) { return FDCAN_DLC_BYTES_64; - } else { // Should never happen + } else { // Should never happen return FDCAN_DLC_BYTES_0; LOGOMATIC("Invalid CAN data size after check: %ld\n", num_bytes); } From 4f9ad787f6279652d01f3f0fea44491a12083de5 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 13 May 2026 15:31:34 -0700 Subject: [PATCH 14/20] Explicitly send do not torque Co-authored-by: kzwicker Signed-off-by: Daniel Hansen --- ECU/Application/Inc/StateUtils.h | 2 ++ ECU/Application/Src/StateTicks.c | 15 +++++++++++---- ECU/Application/Src/StateUtils.c | 7 +++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ECU/Application/Inc/StateUtils.h b/ECU/Application/Inc/StateUtils.h index 05756d270..abc0a64f6 100644 --- a/ECU/Application/Inc/StateUtils.h +++ b/ECU/Application/Inc/StateUtils.h @@ -54,5 +54,7 @@ float CalcAccPedalTravel(volatile const ECU_StateData *stateData); bool APPS_Plausible(volatile const ECU_StateData *stateData); bool BSE_Plausible(volatile const ECU_StateData *stateData); bool vehicle_is_moving(volatile const ECU_StateData *stateData); +/* Disable inverter for both DTI and Custom */ +void disable_inverter(void); #endif diff --git a/ECU/Application/Src/StateTicks.c b/ECU/Application/Src/StateTicks.c index fa23465de..cc38e2404 100644 --- a/ECU/Application/Src/StateTicks.c +++ b/ECU/Application/Src/StateTicks.c @@ -103,6 +103,7 @@ void ECU_State_Tick(void) void ECU_GLV_Off(ECU_StateData *stateData) { + disable_inverter(); UNUSED(stateData); LOGOMATIC("ECU_GLV_Off state reached... this should never happen!\n"); // TODO ERROR --> GLV_OFF should never be reached @@ -110,6 +111,8 @@ void ECU_GLV_Off(ECU_StateData *stateData) void ECU_GLV_On(ECU_StateData *stateData) { + disable_inverter(); + if (stateData->ts_voltage >= SAFE_VOLTAGE_LIMIT) { LOGOMATIC("Error: TS Voltage >= %d!\n", SAFE_VOLTAGE_LIMIT); ECU_Transition_To_Tractive_System_Discharge(stateData); @@ -139,6 +142,8 @@ void ECU_Transition_To_Precharge_Engaged(ECU_StateData *stateData) void ECU_Precharge_Engaged(ECU_StateData *stateData) { + disable_inverter(); + if (stateData->ir_plus) { stateData->ecu_state = GR_PRECHARGE_COMPLETE; LOGOMATIC("PRECHARGE ENGAGED to PRECHARGE COMPLETE!\n"); @@ -162,6 +167,8 @@ void ECU_Precharge_Engaged(ECU_StateData *stateData) // TODO: change for CAN button messenging void ECU_Precharge_Complete(ECU_StateData *stateData) { + disable_inverter(); + if (stateData->ts_active_button_pressed) { LOGOMATIC("TS Active Toggled Off. Discharging Tractive System.\n"); ECU_Transition_To_Tractive_System_Discharge(stateData); @@ -188,7 +195,6 @@ void ECU_Precharge_Complete(ECU_StateData *stateData) ECU_CAN_Send(GRCAN_BUS_DATA, GRCAN_TCM, GRCAN_ECU_ANALOG_DATA, &pedals_message, sizeof(pedals_message)); LOGOMATIC("PRECHARGE COMPLETE to DRIVE START/ACTIVE!\n"); ECU_Transition_To_Drive_Active(stateData); - return; } } @@ -291,11 +297,12 @@ void ECU_Transition_To_Tractive_System_Discharge(ECU_StateData *stateData) LOGOMATIC("ECU: ACU discharge Tractive System\n"); GRCAN_ACU_PRECHARGE_MSG message = {.set_ts_active = 0}; ECU_CAN_Send(GRCAN_BUS_PRIMARY, GRCAN_ACU, GRCAN_ACU_PRECHARGE, &message, sizeof(message)); - discharge_start_millis = millis_since_boot; -} + discharge_start_millis = millis_since_boot;} void ECU_Tractive_System_Discharge(ECU_StateData *stateData) { + disable_inverter(); + /* Discharge the tractive system to below 60(SAFE_VOLTAGE_LIMIT) volts */ @@ -321,4 +328,4 @@ void ECU_Tractive_System_Discharge(ECU_StateData *stateData) ECU_CAN_Send(GRCAN_BUS_PRIMARY, GRCAN_ACU, GRCAN_ACU_PRECHARGE, &message, sizeof(message)); last_discharge_request_millis = millis_since_boot; } -} // init +} diff --git a/ECU/Application/Src/StateUtils.c b/ECU/Application/Src/StateUtils.c index 53b5140a5..f4b929164 100644 --- a/ECU/Application/Src/StateUtils.c +++ b/ECU/Application/Src/StateUtils.c @@ -144,3 +144,10 @@ void SendEcuBonusInfo(const ECU_StateData *stateData) // RTT ping data // TODO Setup using data from Pinging.c per Andrey request } + +void disable_inverter(void) +{ + GRCAN_INVERTER_COMMAND_MSG inverter_msg = {.drive_enable = 0, .field_weakening = 0, .rpm_limit = 0, .set_ac_current = 0, .set_dc_current = 0}; + ECU_CAN_Send(GRCAN_BUS_PRIMARY, GRCAN_GR_Inverter, GRCAN_INVERTER_COMMAND, &inverter_msg, sizeof(inverter_msg)); + ECU_CAN_Send_DTI(DTI_CONTROL_12_CAN_ID, &inverter_msg.drive_enable, 1); +} From 55ea9e2d624791fa59f746882acf6f3b10be168b Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 13 May 2026 16:18:35 -0700 Subject: [PATCH 15/20] Driving check? Signed-off-by: Daniel Hansen --- ECU/Application/Inc/StateUtils.h | 8 ++++---- ECU/Application/Src/StateUtils.c | 7 +++++++ ECU/Core/Src/main.c | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/ECU/Application/Inc/StateUtils.h b/ECU/Application/Inc/StateUtils.h index abc0a64f6..cd1798501 100644 --- a/ECU/Application/Inc/StateUtils.h +++ b/ECU/Application/Inc/StateUtils.h @@ -15,10 +15,10 @@ uint32_t MillisecondsSinceBoot(void); #define BRAKE_F_MAX 4095 // TODO: need to be determined FIXME: Rename better #define BRAKE_R_MIN 0 // TODO: need to be determined FIXME: Rename better #define BRAKE_R_MAX 4095 // TODO: need to be determined FIXME: Rename better -#define THROTTLE_MIN_1 308 // TODO: need to be determined -#define THROTTLE_MAX_1 3689 // TODO: need to be determined -#define THROTTLE_MIN_2 303 // TODO: need to be determined -#define THROTTLE_MAX_2 3735 // TODO: need to be determined +#define THROTTLE_MIN_1 306 // TODO: need to be determined +#define THROTTLE_MAX_1 3704 // TODO: need to be determined +#define THROTTLE_MIN_2 336 // TODO: need to be determined +#define THROTTLE_MAX_2 3794 // TODO: need to be determined #define BSE_MAX 4096.0f #define BSE_DEADZONE 1.2f #define MAX_BSE_FAILURE_TIME 100 diff --git a/ECU/Application/Src/StateUtils.c b/ECU/Application/Src/StateUtils.c index f4b929164..79e7dba5d 100644 --- a/ECU/Application/Src/StateUtils.c +++ b/ECU/Application/Src/StateUtils.c @@ -78,6 +78,7 @@ bool bspdFailure(volatile const ECU_StateData *stateData) bool APPS_BSE_Violation(volatile const ECU_StateData *stateData) { + return false; // Checks 2 * APPS_1 is within 10% of APPS_2 and break + throttle at the same time return PressingBrake(stateData) && CalcAccPedalTravel(stateData) >= 0.25f; } @@ -90,12 +91,18 @@ bool PressingBrake(volatile const ECU_StateData *stateData) // bool brakeFpress = stateData->Brake_F_Signal - BRAKE_F_MIN > BSE_DEADZONE * brakeRangeF; // bool brakeRpress = stateData->Brake_R_Signal - BRAKE_R_MIN > BSE_DEADZONE * brakeRangeR; // return brakeFpress || brakeRpress; + if (stateData->ecu_state < GR_DRIVE_ACTIVE) { + return true; // don't consider brake pressed until in drive active + } else { + return false; + } return ((stateData->bse_signal) / BSE_MAX * 3.3f) > BSE_DEADZONE; // Ideally TCM receives values of 0 after this is no longer called xD. } float CalcBrakePercent(volatile const ECU_StateData *stateData) { + return 0; return stateData->bse_signal / BSE_MAX; } diff --git a/ECU/Core/Src/main.c b/ECU/Core/Src/main.c index 66c567d5e..ae33da04e 100644 --- a/ECU/Core/Src/main.c +++ b/ECU/Core/Src/main.c @@ -424,6 +424,23 @@ int main(void) } lightControl(&stateLump); } + static uint16_t min_apps_1 = 4095; + static uint16_t min_apps_2 = 4095; + if (stateLump.APPS1_Signal < min_apps_1) { + min_apps_1 = stateLump.APPS1_Signal; + } + if (stateLump.APPS2_Signal < min_apps_2) { + min_apps_2 = stateLump.APPS2_Signal; + } + static uint16_t max_apps_1 = 0; + static uint16_t max_apps_2 = 0; + if (stateLump.APPS1_Signal > max_apps_1) { + max_apps_1 = stateLump.APPS1_Signal; + } + if (stateLump.APPS2_Signal > max_apps_2) { + max_apps_2 = stateLump.APPS2_Signal; + } + LOGOMATIC("APPS 1 %d, APPS 2 %d | Min: %d, %d | Max: %d, %d\n", stateLump.APPS1_Signal, stateLump.APPS2_Signal, min_apps_1, min_apps_2, max_apps_1, max_apps_2); } /* USER CODE END 3 */ } From 42392ff42eb59054bd49351a95983b62e6a3c122 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 13 May 2026 16:33:32 -0700 Subject: [PATCH 16/20] ADC alpha is useless if not rate limited Signed-off-by: Daniel Hansen --- ECU/Core/Src/main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ECU/Core/Src/main.c b/ECU/Core/Src/main.c index ae33da04e..75165f5a8 100644 --- a/ECU/Core/Src/main.c +++ b/ECU/Core/Src/main.c @@ -404,15 +404,16 @@ int main(void) while (1) { // adcs read_digital(); - ADC_UpdateAnalogValues_EMA(ADC_buffers, NUM_SIGNALS, 0.2, ADC_outputs); - write_adc_values_to_state_data(); - // main state lopp, queues can messages within it static uint32_t delay_timer; static uint32_t ping_timer; if (MillisecondsSinceBoot() >= delay_timer) { delay_timer = MillisecondsSinceBoot() + (MAIN_LOOP_PERIOD_US / 1000); + // ADC + ADC_UpdateAnalogValues_EMA(ADC_buffers, NUM_SIGNALS, 0.2, ADC_outputs); + write_adc_values_to_state_data(); + // state tick ECU_State_Tick(); From 3e558c0aa7931d14ad0979b3810779be675c7e15 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 13 May 2026 21:00:30 -0700 Subject: [PATCH 17/20] Remove sketchy APPS debug logic Signed-off-by: Daniel Hansen --- ECU/Core/Src/main.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/ECU/Core/Src/main.c b/ECU/Core/Src/main.c index 75165f5a8..60f479a1c 100644 --- a/ECU/Core/Src/main.c +++ b/ECU/Core/Src/main.c @@ -425,23 +425,6 @@ int main(void) } lightControl(&stateLump); } - static uint16_t min_apps_1 = 4095; - static uint16_t min_apps_2 = 4095; - if (stateLump.APPS1_Signal < min_apps_1) { - min_apps_1 = stateLump.APPS1_Signal; - } - if (stateLump.APPS2_Signal < min_apps_2) { - min_apps_2 = stateLump.APPS2_Signal; - } - static uint16_t max_apps_1 = 0; - static uint16_t max_apps_2 = 0; - if (stateLump.APPS1_Signal > max_apps_1) { - max_apps_1 = stateLump.APPS1_Signal; - } - if (stateLump.APPS2_Signal > max_apps_2) { - max_apps_2 = stateLump.APPS2_Signal; - } - LOGOMATIC("APPS 1 %d, APPS 2 %d | Min: %d, %d | Max: %d, %d\n", stateLump.APPS1_Signal, stateLump.APPS2_Signal, min_apps_1, min_apps_2, max_apps_1, max_apps_2); } /* USER CODE END 3 */ } From 3e01c8540a0ec8b5f3c6de98979342084ead4ca6 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Wed, 13 May 2026 21:02:30 -0700 Subject: [PATCH 18/20] Migrate analog data message out of state ticks to be something always sent Signed-off-by: Daniel Hansen --- ECU/Application/Inc/CANutils.h | 1 + ECU/Application/Src/CANutils.c | 22 ++++++++++++++++++++-- ECU/Application/Src/StateTicks.c | 30 ++---------------------------- ECU/Core/Src/main.c | 1 + 4 files changed, 24 insertions(+), 30 deletions(-) diff --git a/ECU/Application/Inc/CANutils.h b/ECU/Application/Inc/CANutils.h index 505f65f63..b29172ef9 100644 --- a/ECU/Application/Inc/CANutils.h +++ b/ECU/Application/Inc/CANutils.h @@ -13,5 +13,6 @@ void ECU_CAN_Send(GRCAN_BUS_ID bus, GRCAN_NODE_ID destNode, GRCAN_MSG_ID messageID, void *data, uint32_t size); void ECU_CAN_Send_DTI(GRCAN_CUSTOM_ID msgID, void *data, uint32_t size); void SendECUStateDataOverCAN(ECU_StateData *stateData); +void SendECUAnalogDataOverCAN(ECU_StateData *stateData); #endif diff --git a/ECU/Application/Src/CANutils.c b/ECU/Application/Src/CANutils.c index d5e11c199..12ff59eba 100644 --- a/ECU/Application/Src/CANutils.c +++ b/ECU/Application/Src/CANutils.c @@ -119,9 +119,27 @@ void SendECUStateDataOverCAN(ECU_StateData *stateData) .RRWheelRPM = (uint16_t)(stateData->rr_wheel_rpm * 10 + 32768), .RLWheelRPM = (uint16_t)(stateData->rl_wheel_rpm * 10 + 32768)}; - // LOGOMATIC("Sending ECU State Data over CAN\n"); - ECU_CAN_Send(GRCAN_BUS_PRIMARY, GRCAN_ALL, GRCAN_ECU_STATUS_1, (void *)&messages.ECUStatusMsgOne, sizeof(messages.ECUStatusMsgOne)); ECU_CAN_Send(GRCAN_BUS_PRIMARY, GRCAN_ALL, GRCAN_ECU_STATUS_2, (void *)&messages.ECUStatusMsgTwo, sizeof(messages.ECUStatusMsgTwo)); ECU_CAN_Send(GRCAN_BUS_PRIMARY, GRCAN_ALL, GRCAN_ECU_STATUS_3, (void *)&messages.ECUStatusMsgThree, sizeof(messages.ECUStatusMsgThree)); } + +void SendECUAnalogDataOverCAN(ECU_StateData *stateData) +{ + uint32_t millis_since_boot = MillisecondsSinceBoot(); + + static uint32_t last_can_tcm_request_millis = 0; + + if (millis_since_boot - last_can_tcm_request_millis > 100) { + GRCAN_ECU_ANALOG_DATA_MSG message = {.bspd_signal = stateData->bspd_signal, + .bse_signal = stateData->bse_signal, + .apps_1_signal = stateData->APPS1_Signal, + .apps_2_signal = stateData->APPS2_Signal, + .brakeline_f_signal = stateData->Brake_F_Signal, + .brakeline_r_signal = stateData->Brake_R_Signal, + .steering_angle_signal = stateData->steering_angle_signal, + .aux_signal = stateData->aux_signal}; + ECU_CAN_Send(GRCAN_BUS_DATA, GRCAN_TCM, GRCAN_ECU_ANALOG_DATA, &message, sizeof(message)); + last_can_tcm_request_millis = millis_since_boot; + } +} diff --git a/ECU/Application/Src/StateTicks.c b/ECU/Application/Src/StateTicks.c index cc38e2404..96a14ea89 100644 --- a/ECU/Application/Src/StateTicks.c +++ b/ECU/Application/Src/StateTicks.c @@ -184,15 +184,6 @@ void ECU_Precharge_Complete(ECU_StateData *stateData) if (PressingBrake(stateData) && stateData->rtd_button_pressed) { GRCAN_INVERTER_CONFIG_MSG inverter_message = {.max_ac_current = 0xFFFF, .max_dc_current = 0xFFFF, .absolute_max_rpm_limit = 0xFFFF, .motor_direction = 0}; ECU_CAN_Send(GRCAN_BUS_PRIMARY, GRCAN_GR_Inverter, GRCAN_INVERTER_CONFIG, &inverter_message, sizeof(inverter_message)); - GRCAN_ECU_ANALOG_DATA_MSG pedals_message = {.bspd_signal = stateData->bspd_signal, - .bse_signal = stateData->bse_signal, - .apps_1_signal = stateData->APPS1_Signal, - .apps_2_signal = stateData->APPS2_Signal, - .brakeline_f_signal = stateData->Brake_F_Signal, - .brakeline_r_signal = stateData->Brake_R_Signal, - .steering_angle_signal = stateData->steering_angle_signal, - .aux_signal = stateData->aux_signal}; - ECU_CAN_Send(GRCAN_BUS_DATA, GRCAN_TCM, GRCAN_ECU_ANALOG_DATA, &pedals_message, sizeof(pedals_message)); LOGOMATIC("PRECHARGE COMPLETE to DRIVE START/ACTIVE!\n"); ECU_Transition_To_Drive_Active(stateData); } @@ -270,24 +261,6 @@ void ECU_Drive_Active(ECU_StateData *stateData) ECU_CAN_Send_DTI(DTI_CONTROL_1_CAN_ID, &message.set_ac_current, 2); last_can_inverter_request_millis = millis_since_boot; } - - // placeholder for pedal data - // TODO: determine send time (15, 20 ms?) - - static uint32_t last_can_tcm_request_millis = 0; - if (RATE_LIMIT_100_HZ(millis_since_boot, last_can_tcm_request_millis)) { - GRCAN_ECU_ANALOG_DATA_MSG message = {.bspd_signal = stateData->bspd_signal, - .bse_signal = stateData->bse_signal, - .apps_1_signal = stateData->APPS1_Signal, - .apps_2_signal = stateData->APPS2_Signal, - .brakeline_f_signal = stateData->Brake_F_Signal, - .brakeline_r_signal = stateData->Brake_R_Signal, - .steering_angle_signal = stateData->steering_angle_signal, - .aux_signal = stateData->aux_signal}; - UNUSED(message); // FIXME Eventually figure out what to do with this message here - ECU_CAN_Send(GRCAN_BUS_DATA, GRCAN_TCM, GRCAN_ECU_ANALOG_DATA, &message, 10); // FIXME - last_can_tcm_request_millis = millis_since_boot; - } } static uint32_t discharge_start_millis; @@ -297,7 +270,8 @@ void ECU_Transition_To_Tractive_System_Discharge(ECU_StateData *stateData) LOGOMATIC("ECU: ACU discharge Tractive System\n"); GRCAN_ACU_PRECHARGE_MSG message = {.set_ts_active = 0}; ECU_CAN_Send(GRCAN_BUS_PRIMARY, GRCAN_ACU, GRCAN_ACU_PRECHARGE, &message, sizeof(message)); - discharge_start_millis = millis_since_boot;} + discharge_start_millis = millis_since_boot; +} void ECU_Tractive_System_Discharge(ECU_StateData *stateData) { diff --git a/ECU/Core/Src/main.c b/ECU/Core/Src/main.c index 60f479a1c..70ae68f58 100644 --- a/ECU/Core/Src/main.c +++ b/ECU/Core/Src/main.c @@ -419,6 +419,7 @@ int main(void) // preipheral updates SendECUStateDataOverCAN(&stateLump); + SendECUAnalogDataOverCAN(&stateLump); if (MillisecondsSinceBoot() >= ping_timer) { ping_timer = MillisecondsSinceBoot() + (MAIN_LOOP_PERIOD_US / 500); // half period pingAll(); From 32f2d3083ce025c3ed2767a264c3e9f06cf1576a Mon Sep 17 00:00:00 2001 From: Thomas Xu Date: Thu, 14 May 2026 20:54:53 -0700 Subject: [PATCH 19/20] added a scary FIXME for the code causing HOOTLTest failures --- ECU/Application/Src/StateUtils.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ECU/Application/Src/StateUtils.c b/ECU/Application/Src/StateUtils.c index 79e7dba5d..2162eb661 100644 --- a/ECU/Application/Src/StateUtils.c +++ b/ECU/Application/Src/StateUtils.c @@ -91,6 +91,7 @@ bool PressingBrake(volatile const ECU_StateData *stateData) // bool brakeFpress = stateData->Brake_F_Signal - BRAKE_F_MIN > BSE_DEADZONE * brakeRangeF; // bool brakeRpress = stateData->Brake_R_Signal - BRAKE_R_MIN > BSE_DEADZONE * brakeRangeR; // return brakeFpress || brakeRpress; + // FIXME: DELETE THE FOLLOWING CONTROL BLOCK FOR BRAKE TESTING if (stateData->ecu_state < GR_DRIVE_ACTIVE) { return true; // don't consider brake pressed until in drive active } else { From 524477e45b15c2d013636360e76c5df516a4e008 Mon Sep 17 00:00:00 2001 From: Daniel Hansen Date: Fri, 15 May 2026 00:21:21 -0700 Subject: [PATCH 20/20] No more DWT Signed-off-by: Daniel Hansen --- ECU/Core/Src/main.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ECU/Core/Src/main.c b/ECU/Core/Src/main.c index 70ae68f58..6afad717a 100644 --- a/ECU/Core/Src/main.c +++ b/ECU/Core/Src/main.c @@ -383,11 +383,6 @@ int main(void) // MX_FDCAN2_Init(); /* USER CODE BEGIN 2 */ - // Initialize DWT - CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; - DWT->CYCCNT = 0; - DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; - // Initialize CAN CAN_Configure();