diff --git a/.vscode/launch.json b/.vscode/launch.json index ee4205e96..a274e79e8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -439,8 +439,8 @@ }, { "cwd": "${workspaceFolder}", - "executable": "${command:cmake.buildDirectory}/G4CANTESTING.elf", - "name": "G4CANTESTING", + "executable": "${command:cmake.buildDirectory}/G4NEOTESTING.elf", + "name": "G4NEOTESTING", "request": "launch", "type": "cortex-debug", "servertype": "openocd", @@ -449,12 +449,12 @@ "target/stm32g4x.cfg" ], "searchDir": [], - "preLaunchTask": "CMake: configure and build G4CANTESTING", + "preLaunchTask": "CMake: configure and build G4NEOTESTING", "showDevDebugOutput": "raw", "svdPath": "${workspaceFolder}/Lib/Vendor/CMSIS_5/SVD/STM32G474.svd", "swoConfig": { "enabled": true, - "cpuFrequency": 160000000, + "cpuFrequency": 170000000, "swoFrequency": 2000000, "source": "probe", "decoders": [ @@ -470,8 +470,8 @@ }, { "cwd": "${workspaceFolder}", - "executable": "${command:cmake.buildDirectory}/G4NEOTESTING.elf", - "name": "G4NEOTESTING", + "executable": "${command:cmake.buildDirectory}/CAN_EXTERNAL_TEST.elf", + "name": "CAN_EXTERNAL_TEST", "request": "launch", "type": "cortex-debug", "servertype": "openocd", @@ -480,7 +480,100 @@ "target/stm32g4x.cfg" ], "searchDir": [], - "preLaunchTask": "CMake: configure and build G4NEOTESTING", + "preLaunchTask": "CMake: configure and build CAN_EXTERNAL_TEST", + "showDevDebugOutput": "raw", + "svdPath": "${workspaceFolder}/Lib/Vendor/CMSIS_5/SVD/STM32G474.svd", + "swoConfig": { + "enabled": true, + "cpuFrequency": 170000000, + "swoFrequency": 2000000, + "source": "probe", + "decoders": [ + { + "type": "console", + "label": "ITM", + "showOnStartup": true, + "port": 0, + "encoding": "ascii" + } + ] + } + }, + { + "cwd": "${workspaceFolder}", + "executable": "${command:cmake.buildDirectory}/CAN_INTERNAL_TEST.elf", + "name": "CAN_INTERNAL_TEST", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32g4x.cfg" + ], + "searchDir": [], + "preLaunchTask": "CMake: configure and build CAN_INTERNAL_TEST", + "showDevDebugOutput": "raw", + "svdPath": "${workspaceFolder}/Lib/Vendor/CMSIS_5/SVD/STM32G474.svd", + "swoConfig": { + "enabled": true, + "cpuFrequency": 170000000, + "swoFrequency": 2000000, + "source": "probe", + "decoders": [ + { + "type": "console", + "label": "ITM", + "showOnStartup": true, + "port": 0, + "encoding": "ascii" + } + ] + } + }, + { + "cwd": "${workspaceFolder}", + "executable": "${command:cmake.buildDirectory}/CAN_STRESS_TEST.elf", + "name": "CAN_STRESS_TEST", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32g4x.cfg" + ], + "searchDir": [], + "preLaunchTask": "CMake: configure and build CAN_STRESS_TEST", + "showDevDebugOutput": "raw", + "svdPath": "${workspaceFolder}/Lib/Vendor/CMSIS_5/SVD/STM32G474.svd", + "swoConfig": { + "enabled": true, + "cpuFrequency": 170000000, + "swoFrequency": 2000000, + "source": "probe", + "decoders": [ + { + "type": "console", + "label": "ITM", + "showOnStartup": true, + "port": 0, + "encoding": "ascii" + } + ] + } + }, + { + "cwd": "${workspaceFolder}", + "executable": "${command:cmake.buildDirectory}/CAN_RELEASE_TEST.elf", + "name": "CAN_RELEASE_TEST", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32g4x.cfg" + ], + "searchDir": [], + "preLaunchTask": "CMake: configure and build CAN_RELEASE_TEST", "showDevDebugOutput": "raw", "svdPath": "${workspaceFolder}/Lib/Vendor/CMSIS_5/SVD/STM32G474.svd", "swoConfig": { diff --git a/.vscode/tasks.json b/.vscode/tasks.json index bdea4d956..6894f28f5 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -90,15 +90,6 @@ ], "command": "cmake --build --preset ${command:cmake.activeBuildPresetName} --target G4ADCTESTING" }, - { - "label": "CMake: configure and build G4CANTESTING", - "type": "shell", - "dependsOrder": "sequence", - "dependsOn": [ - "CMake: configure" - ], - "command": "cmake --build --preset ${command:cmake.activeBuildPresetName} --target G4CANTESTING" - }, { "label": "CMake: configure and build G4NEOTESTING", "type": "shell", @@ -161,6 +152,42 @@ "CMake: configure" ], "command": "cmake --build --preset ${command:cmake.activeBuildPresetName} --target W3_G4SPI_Receive" + }, + { + "label": "CMake: configure and build CAN_EXTERNAL_TEST", + "type": "shell", + "dependsOrder": "sequence", + "dependsOn": [ + "CMake: configure" + ], + "command": "cmake --build --preset ${command:cmake.activeBuildPresetName} --target CAN_EXTERNAL_TEST" + }, + { + "label": "CMake: configure and build CAN_INTERNAL_TEST", + "type": "shell", + "dependsOrder": "sequence", + "dependsOn": [ + "CMake: configure" + ], + "command": "cmake --build --preset ${command:cmake.activeBuildPresetName} --target CAN_INTERNAL_TEST" + }, + { + "label": "CMake: configure and build CAN_RELEASE_TEST", + "type": "shell", + "dependsOrder": "sequence", + "dependsOn": [ + "CMake: configure" + ], + "command": "cmake --build --preset ${command:cmake.activeBuildPresetName} --target CAN_RELEASE_TEST" + }, + { + "label": "CMake: configure and build CAN_STRESS_TEST", + "type": "shell", + "dependsOrder": "sequence", + "dependsOn": [ + "CMake: configure" + ], + "command": "cmake --build --preset ${command:cmake.activeBuildPresetName} --target CAN_STRESS_TEST" } ] } diff --git a/CCU/Application/Inc/can_cfg.h b/CCU/Application/Inc/can_cfg.h new file mode 100644 index 000000000..0e987b82d --- /dev/null +++ b/CCU/Application/Inc/can_cfg.h @@ -0,0 +1,7 @@ +#ifndef CAN_CFG_H +#define CAN_CFG_H + +#define USECAN1 +#define TX_BUFFER_1_SIZE 10 + +#endif diff --git a/CCU/Application/Src/CANDler.c b/CCU/Application/Src/CANDler.c index 80f6ae415..3b359d822 100644 --- a/CCU/Application/Src/CANDler.c +++ b/CCU/Application/Src/CANDler.c @@ -85,7 +85,7 @@ void CAN_Configure(void) canCfg.rx_callback = Read_CAN; canCfg.rx_interrupt_priority = 15; // TODO: Maybe make these not hardcoded canCfg.tx_interrupt_priority = 15; - canCfg.tx_buffer_length = 5; + // canCfg.tx_buffer_length = 5; // RX shared settings canCfg.init_rx_gpio.Mode = GPIO_MODE_AF_PP; diff --git a/CMakeLists.txt b/CMakeLists.txt index 94fb034c3..49495dcc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,7 +81,12 @@ add_gr_project(STM32G474xE TireTemp) # Development add_gr_project(STM32G474xE G4ADCTESTING) add_gr_project(STM32G474xE G4PERTESTING) -add_gr_project(STM32G474xE G4CANTESTING) + +# CAN Peripheral Testing +add_gr_project(STM32G474xE G4CANTESTING CAN_EXTERNAL_TEST) +add_gr_project(STM32G474xE G4CANTESTING CAN_INTERNAL_TEST) +add_gr_project(STM32G474xE G4CANTESTING CAN_RELEASE_TEST) +add_gr_project(STM32G474xE G4CANTESTING CAN_STRESS_TEST) add_gr_project(STM32G474xE G4NEOTESTING) # BLINKY Demos diff --git a/DashPanel/Application/Inc/can_cfg.h b/DashPanel/Application/Inc/can_cfg.h new file mode 100644 index 000000000..0e987b82d --- /dev/null +++ b/DashPanel/Application/Inc/can_cfg.h @@ -0,0 +1,7 @@ +#ifndef CAN_CFG_H +#define CAN_CFG_H + +#define USECAN1 +#define TX_BUFFER_1_SIZE 10 + +#endif diff --git a/DashPanel/Application/Src/CANdler.c b/DashPanel/Application/Src/CANdler.c index 65874f3de..6d0df463d 100644 --- a/DashPanel/Application/Src/CANdler.c +++ b/DashPanel/Application/Src/CANdler.c @@ -46,7 +46,7 @@ void CANInitialize() canCfg.rx_callback = CAN_callback; canCfg.rx_interrupt_priority = 15; canCfg.tx_interrupt_priority = 15; - canCfg.tx_buffer_length = 3; + // canCfg.tx_buffer_length = 3; canCfg.fdcan_instance = FDCAN1; canCfg.rx_gpio = GPIOA; diff --git a/ECU/Application/Inc/can_cfg.h b/ECU/Application/Inc/can_cfg.h new file mode 100644 index 000000000..8a18e8882 --- /dev/null +++ b/ECU/Application/Inc/can_cfg.h @@ -0,0 +1,10 @@ +#ifndef CAN_CFG_H +#define CAN_CFG_H + +#define USECAN1 +#define TX_BUFFER_1_SIZE 10 + +#define USECAN2 +#define TX_BUFFER_2_SIZE 10 + +#endif diff --git a/ECU/Core/Src/main.c b/ECU/Core/Src/main.c index 3ea450de8..77db94d2a 100644 --- a/ECU/Core/Src/main.c +++ b/ECU/Core/Src/main.c @@ -271,17 +271,17 @@ void CAN_Configure() canCfg.hal_fdcan_init.NominalSyncJumpWidth = 16; canCfg.hal_fdcan_init.NominalTimeSeg1 = 127; // Updated for 170MHz: (1+127+42)*1 = 170 ticks -> 1 Mbps canCfg.hal_fdcan_init.NominalTimeSeg2 = 42; - canCfg.hal_fdcan_init.DataPrescaler = 8; + canCfg.hal_fdcan_init.DataPrescaler = 2; canCfg.hal_fdcan_init.DataSyncJumpWidth = 16; - canCfg.hal_fdcan_init.DataTimeSeg1 = 15; // Updated for 170MHz: (1+15+5)*8 = 168 ticks -> ~5 Mbps - canCfg.hal_fdcan_init.DataTimeSeg2 = 5; + canCfg.hal_fdcan_init.DataTimeSeg1 = 12; // Updated for 170MHz: 170 MHz/((1+12+4)*2) = 5 Mbps + canCfg.hal_fdcan_init.DataTimeSeg2 = 4; canCfg.hal_fdcan_init.StdFiltersNbr = 1; canCfg.hal_fdcan_init.ExtFiltersNbr = 0; canCfg.rx_callback = NULL; canCfg.rx_interrupt_priority = 15; // TODO: Maybe make these not hardcoded canCfg.tx_interrupt_priority = 15; - canCfg.tx_buffer_length = CAN_TX_BUFFER_LENGTH; + // canCfg.tx_buffer_length = CAN_TX_BUFFER_LENGTH; // RX shared settings canCfg.init_rx_gpio.Mode = GPIO_MODE_AF_PP; diff --git a/G4CANTESTING/CAN_EXTERNAL_TEST/CMakeLists.txt b/G4CANTESTING/CAN_EXTERNAL_TEST/CMakeLists.txt new file mode 100644 index 000000000..ccedc8c6d --- /dev/null +++ b/G4CANTESTING/CAN_EXTERNAL_TEST/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 3.25) + +# Setup compiler settings +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS ON) + +# Define the build type +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# Enable compile command to ease indexing with e.g. clangd +set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) + +# Enable CMake support for ASM and C languages +enable_language( + C + ASM +) + +# Core project settings +project(${CMAKE_PROJECT_NAME}) + +# what, does in fact not get the filename of somthing but rather the name of the project from the path +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +add_library(${PROJECT_NAME}_USER_CODE INTERFACE) + +target_sources( + ${PROJECT_NAME}_USER_CODE + INTERFACE + ../Core/Src/adc.c + ../Core/Src/crc.c + ../Core/Src/dma.c + ../Core/Src/gpio.c + ../Core/Src/i2c.c + main.c + ../Core/Src/spi.c + ../Core/Src/stm32g4xx_hal_msp.c + ../Core/Src/stm32g4xx_it.c + ../Core/Src/tim.c + ../Core/Src/usart.c +) + +set_target_properties( + ${NAME} + PROPERTIES + OUTPUT_NAME + "CAN_${NAME}" +) + +target_link_libraries(${PROJECT_NAME}_USER_CODE INTERFACE PERIPHERAL_CAN_LIB) + +target_include_directories( + ${PROJECT_NAME}_USER_CODE + INTERFACE + ../Core/Inc + . +) diff --git a/G4CANTESTING/CAN_EXTERNAL_TEST/can_cfg.h b/G4CANTESTING/CAN_EXTERNAL_TEST/can_cfg.h new file mode 100644 index 000000000..fc11e3e23 --- /dev/null +++ b/G4CANTESTING/CAN_EXTERNAL_TEST/can_cfg.h @@ -0,0 +1,11 @@ + +#ifndef CAN_CFG_H +#define CAN_CFG_H + +#define USECAN1 +#define TX_BUFFER_1_SIZE 10 + +#define USECAN2 +#define TX_BUFFER_2_SIZE 10 + +#endif diff --git a/G4CANTESTING/CAN_EXTERNAL_TEST/main.c b/G4CANTESTING/CAN_EXTERNAL_TEST/main.c new file mode 100644 index 000000000..0e46a98e6 --- /dev/null +++ b/G4CANTESTING/CAN_EXTERNAL_TEST/main.c @@ -0,0 +1,274 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : main.c + * @brief : Main program body + ****************************************************************************** + * @attention + * + * Copyright (c) 2024 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ +/* Includes ------------------------------------------------------------------*/ +#include "main.h" + +#include "adc.h" +#include "can.h" // Assume this works +#include "can_tests.h" +#include "dma.h" +#include "fdcan.h" +#include "gpio.h" +#include "i2c.h" +#include "spi.h" +#include "tim.h" +#include "usart.h" + +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ +#include "Logomatic.h" +/* USER CODE END Includes */ + +/* Private typedef -----------------------------------------------------------*/ +/* USER CODE BEGIN PTD */ + +/* USER CODE END PTD */ + +/* Private define ------------------------------------------------------------*/ +/* USER CODE BEGIN PD */ + +/* USER CODE END PD */ + +/* Private macro -------------------------------------------------------------*/ +/* USER CODE BEGIN PM */ + +/* USER CODE END PM */ + +/* Private variables ---------------------------------------------------------*/ + +/* USER CODE BEGIN PV */ +/* USER CODE END PV */ + +/* Private function prototypes -----------------------------------------------*/ +void SystemClock_Config(void); +/* USER CODE BEGIN PFP */ + +/* USER CODE END PFP */ + +/* Private user code ---------------------------------------------------------*/ +/* USER CODE BEGIN 0 */ +/* Enable ITM for SWO output */ +static void ITM_Enable(void) +{ + LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB); + LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; + GPIO_InitStruct.Pin = LL_GPIO_PIN_3; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; + GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + GPIO_InitStruct.Alternate = LL_GPIO_AF_0; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + DBGMCU->CR |= DBGMCU_CR_TRACE_IOEN; + + /* Enable TRC (Trace) */ + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + + /* Configure TPI for SWO output (set prescaler for 2MHz SWO clock) */ + TPI->SPPR = 2U; /* 2 = Manchester/async UART mode */ + TPI->ACPR = 84U; /* Prescaler: (170 MHz / (84+1) / 2) ≈ 1MHz SWO */ + + ITM->TER |= (1UL << 0); + ITM->TCR |= (ITM_TCR_ITMENA_Msk | ITM_TCR_SWOENA_Msk); +} +// static int toggleze = 0; +/* USER CODE END 0 */ + +/** + * @brief The application entry point. + * @retval int + */ +int main(void) +{ + /* USER CODE BEGIN 1 */ + + /* USER CODE END 1 */ + + /* MCU + * Configuration--------------------------------------------------------*/ + + /* Reset of all peripherals, Initializes the Flash interface and the + * Systick. */ + HAL_Init(); + /* USER CODE BEGIN Init */ + ITM_Enable(); + /* USER CODE END Init */ + + /* Configure the system clock */ + SystemClock_Config(); + + /* USER CODE BEGIN SysInit */ + + /* USER CODE END SysInit */ + + /* Initialize all configured peripherals */ + MX_GPIO_Init(); + MX_DMA_Init(); + // MX_FDCAN2_Init(); + MX_ADC1_Init(); + MX_LPUART1_UART_Init(); + MX_I2C2_Init(); + MX_USART1_UART_Init(); + MX_SPI3_Init(); + MX_TIM2_Init(); + + /* USER CODE BEGIN 2 */ + + LOGOMATIC("Booted!\n"); + + // LOGOMATIC("running can_external_test:\n"); + can_external_test(); + + /* Infinite loop */ + /* USER CODE BEGIN WHILE */ + while (1) { + /* USER CODE END WHILE */ + LOGOMATIC("Main Loop\n"); + LL_mDelay(1000); + + // Receive on GPIOs + // HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, toggleze ? GPIO_PIN_SET + // : GPIO_PIN_RESET); HAL_Delay(1000); msg.data[0] = toggleze ? + // 0x00 : 0x80; can_send(data_can, &msg); + + // RCC->CFGR |= RCC_CFGR_SW; + /* USER CODE BEGIN 3 */ + } +} + +/** + * @brief System Clock Configuration + * @retval None + */ + +// void SystemClock_Config(void) +// { +// LL_FLASH_SetLatency(LL_FLASH_LATENCY_4); +// while (LL_FLASH_GetLatency() != LL_FLASH_LATENCY_4) { +// } +// LL_PWR_EnableRange1BoostMode(); +// LL_RCC_HSE_Enable(); + +// /* Wait till HSE is ready */ +// while (LL_RCC_HSE_IsReady() != 1) { +// } + +// LL_RCC_HSE_EnableCSS(); +// LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_1, 20, +// LL_RCC_PLLR_DIV_2); +// //LL_RCC_PLL_ConfigDomain_48M(LL_RCC_PLLSOURCE_HSE, ); +// LL_RCC_PLL_EnableDomain_SYS(); +// LL_RCC_PLL_Enable(); +// /* Wait till PLL is ready */ +// while (LL_RCC_PLL_IsReady() != 1) { +// } + +// LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); +// LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2); +// /* Wait till System clock is ready */ +// while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) { +// } + +// /* Insure 1us transition state at intermediate medium speed clock*/ +// for (__IO uint32_t i = (170 >> 1); i != 0; i--) +// ; + +// /* Set AHB prescaler*/ +// LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); +// LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); +// LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); +// LL_SetSystemCoreClock(160000000); + +// /* Update the time base */ +// if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) { +// Error_Handler(); +// } +// }*/ +void SystemClock_Config(void) +{ + LL_FLASH_SetLatency(LL_FLASH_LATENCY_4); + while (LL_FLASH_GetLatency() != LL_FLASH_LATENCY_4) {} + LL_PWR_EnableRange1BoostMode(); + LL_RCC_HSI_Enable(); + /* Wait till HSI is ready */ + while (LL_RCC_HSI_IsReady() != 1) {} + + LL_RCC_HSI_SetCalibTrimming(64); + LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI, LL_RCC_PLLM_DIV_4, 85, LL_RCC_PLLR_DIV_2); + LL_RCC_PLL_EnableDomain_SYS(); + LL_RCC_PLL_Enable(); + /* Wait till PLL is ready */ + while (LL_RCC_PLL_IsReady() != 1) {} + + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); + // LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); + /* Wait till System clock is ready */ + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {} + + /* Insure 1us transition state at intermediate medium speed clock*/ + for (__IO uint32_t i = (170 >> 1); i != 0; i--) + ; + + /* Set AHB prescaler*/ + LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); + LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); + LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); + LL_SetSystemCoreClock(170000000); + + /* Update the time base */ + if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) { + Error_Handler(); + } +} + +/* USER CODE BEGIN 4 */ + +/* USER CODE END 4 */ + +/** + * @brief This function is executed in case of error occurrence. + * @retval None + */ +void Error_Handler(void) +{ + /* USER CODE BEGIN Error_Handler_Debug */ + /* User can add his own implementation to report the HAL error return + * state */ + __disable_irq(); + while (1) {} + /* USER CODE END Error_Handler_Debug */ +} +#ifdef USE_FULL_ASSERT +/** + * @brief Reports the name of the source file and the source line number + * where the assert_param error has occurred. + * @param file: pointer to the source file name + * @param line: assert_param error line source number + * @retval None + */ +void assert_failed(uint8_t *file, uint32_t line) +{ + /* USER CODE BEGIN 6 */ + /* User can add his own implementation to report the file name and line + number, ex: printf("Wrong parameters value: file %s on line %d\r\n", + file, line) */ + /* USER CODE END 6 */ +} +#endif /* USE_FULL_ASSERT */ diff --git a/G4CANTESTING/CAN_INTERNAL_TEST/CMakeLists.txt b/G4CANTESTING/CAN_INTERNAL_TEST/CMakeLists.txt new file mode 100644 index 000000000..ccedc8c6d --- /dev/null +++ b/G4CANTESTING/CAN_INTERNAL_TEST/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 3.25) + +# Setup compiler settings +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS ON) + +# Define the build type +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# Enable compile command to ease indexing with e.g. clangd +set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) + +# Enable CMake support for ASM and C languages +enable_language( + C + ASM +) + +# Core project settings +project(${CMAKE_PROJECT_NAME}) + +# what, does in fact not get the filename of somthing but rather the name of the project from the path +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +add_library(${PROJECT_NAME}_USER_CODE INTERFACE) + +target_sources( + ${PROJECT_NAME}_USER_CODE + INTERFACE + ../Core/Src/adc.c + ../Core/Src/crc.c + ../Core/Src/dma.c + ../Core/Src/gpio.c + ../Core/Src/i2c.c + main.c + ../Core/Src/spi.c + ../Core/Src/stm32g4xx_hal_msp.c + ../Core/Src/stm32g4xx_it.c + ../Core/Src/tim.c + ../Core/Src/usart.c +) + +set_target_properties( + ${NAME} + PROPERTIES + OUTPUT_NAME + "CAN_${NAME}" +) + +target_link_libraries(${PROJECT_NAME}_USER_CODE INTERFACE PERIPHERAL_CAN_LIB) + +target_include_directories( + ${PROJECT_NAME}_USER_CODE + INTERFACE + ../Core/Inc + . +) diff --git a/G4CANTESTING/CAN_INTERNAL_TEST/can_cfg.h b/G4CANTESTING/CAN_INTERNAL_TEST/can_cfg.h new file mode 100644 index 000000000..fc11e3e23 --- /dev/null +++ b/G4CANTESTING/CAN_INTERNAL_TEST/can_cfg.h @@ -0,0 +1,11 @@ + +#ifndef CAN_CFG_H +#define CAN_CFG_H + +#define USECAN1 +#define TX_BUFFER_1_SIZE 10 + +#define USECAN2 +#define TX_BUFFER_2_SIZE 10 + +#endif diff --git a/G4CANTESTING/CAN_INTERNAL_TEST/main.c b/G4CANTESTING/CAN_INTERNAL_TEST/main.c new file mode 100644 index 000000000..9c2f2b7a4 --- /dev/null +++ b/G4CANTESTING/CAN_INTERNAL_TEST/main.c @@ -0,0 +1,274 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : main.c + * @brief : Main program body + ****************************************************************************** + * @attention + * + * Copyright (c) 2024 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ +/* Includes ------------------------------------------------------------------*/ +#include "main.h" + +#include "adc.h" +#include "can.h" // Assume this works +#include "can_tests.h" +#include "dma.h" +#include "fdcan.h" +#include "gpio.h" +#include "i2c.h" +#include "spi.h" +#include "tim.h" +#include "usart.h" + +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ +#include "Logomatic.h" +/* USER CODE END Includes */ + +/* Private typedef -----------------------------------------------------------*/ +/* USER CODE BEGIN PTD */ + +/* USER CODE END PTD */ + +/* Private define ------------------------------------------------------------*/ +/* USER CODE BEGIN PD */ + +/* USER CODE END PD */ + +/* Private macro -------------------------------------------------------------*/ +/* USER CODE BEGIN PM */ + +/* USER CODE END PM */ + +/* Private variables ---------------------------------------------------------*/ + +/* USER CODE BEGIN PV */ +/* USER CODE END PV */ + +/* Private function prototypes -----------------------------------------------*/ +void SystemClock_Config(void); +/* USER CODE BEGIN PFP */ + +/* USER CODE END PFP */ + +/* Private user code ---------------------------------------------------------*/ +/* USER CODE BEGIN 0 */ +/* Enable ITM for SWO output */ +static void ITM_Enable(void) +{ + LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB); + LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; + GPIO_InitStruct.Pin = LL_GPIO_PIN_3; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; + GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + GPIO_InitStruct.Alternate = LL_GPIO_AF_0; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + DBGMCU->CR |= DBGMCU_CR_TRACE_IOEN; + + /* Enable TRC (Trace) */ + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + + /* Configure TPI for SWO output (set prescaler for 2MHz SWO clock) */ + TPI->SPPR = 2U; /* 2 = Manchester/async UART mode */ + TPI->ACPR = 84U; /* Prescaler: (170 MHz / (84+1) / 2) ≈ 1MHz SWO */ + + ITM->TER |= (1UL << 0); + ITM->TCR |= (ITM_TCR_ITMENA_Msk | ITM_TCR_SWOENA_Msk); +} +// static int toggleze = 0; +/* USER CODE END 0 */ + +/** + * @brief The application entry point. + * @retval int + */ +int main(void) +{ + /* USER CODE BEGIN 1 */ + + /* USER CODE END 1 */ + + /* MCU + * Configuration--------------------------------------------------------*/ + + /* Reset of all peripherals, Initializes the Flash interface and the + * Systick. */ + HAL_Init(); + /* USER CODE BEGIN Init */ + ITM_Enable(); + /* USER CODE END Init */ + + /* Configure the system clock */ + SystemClock_Config(); + + /* USER CODE BEGIN SysInit */ + + /* USER CODE END SysInit */ + + /* Initialize all configured peripherals */ + MX_GPIO_Init(); + MX_DMA_Init(); + // MX_FDCAN2_Init(); + MX_ADC1_Init(); + MX_LPUART1_UART_Init(); + MX_I2C2_Init(); + MX_USART1_UART_Init(); + MX_SPI3_Init(); + MX_TIM2_Init(); + + /* USER CODE BEGIN 2 */ + + LOGOMATIC("Booted!\n"); + + // LOGOMATIC("running can_internal_test:\n"); + can_internal_test(); + + /* Infinite loop */ + /* USER CODE BEGIN WHILE */ + while (1) { + /* USER CODE END WHILE */ + LOGOMATIC("Main Loop\n"); + LL_mDelay(10000); + + // Receive on GPIOs + // HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, toggleze ? GPIO_PIN_SET + // : GPIO_PIN_RESET); HAL_Delay(1000); msg.data[0] = toggleze ? + // 0x00 : 0x80; can_send(data_can, &msg); + + // RCC->CFGR |= RCC_CFGR_SW; + /* USER CODE BEGIN 3 */ + } +} + +/** + * @brief System Clock Configuration + * @retval None + */ + +// void SystemClock_Config(void) +// { +// LL_FLASH_SetLatency(LL_FLASH_LATENCY_4); +// while (LL_FLASH_GetLatency() != LL_FLASH_LATENCY_4) { +// } +// LL_PWR_EnableRange1BoostMode(); +// LL_RCC_HSE_Enable(); + +// /* Wait till HSE is ready */ +// while (LL_RCC_HSE_IsReady() != 1) { +// } + +// LL_RCC_HSE_EnableCSS(); +// LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_1, 20, +// LL_RCC_PLLR_DIV_2); +// //LL_RCC_PLL_ConfigDomain_48M(LL_RCC_PLLSOURCE_HSE, ); +// LL_RCC_PLL_EnableDomain_SYS(); +// LL_RCC_PLL_Enable(); +// /* Wait till PLL is ready */ +// while (LL_RCC_PLL_IsReady() != 1) { +// } + +// LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); +// LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2); +// /* Wait till System clock is ready */ +// while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) { +// } + +// /* Insure 1us transition state at intermediate medium speed clock*/ +// for (__IO uint32_t i = (170 >> 1); i != 0; i--) +// ; + +// /* Set AHB prescaler*/ +// LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); +// LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); +// LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); +// LL_SetSystemCoreClock(160000000); + +// /* Update the time base */ +// if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) { +// Error_Handler(); +// } +// }*/ +void SystemClock_Config(void) +{ + LL_FLASH_SetLatency(LL_FLASH_LATENCY_4); + while (LL_FLASH_GetLatency() != LL_FLASH_LATENCY_4) {} + LL_PWR_EnableRange1BoostMode(); + LL_RCC_HSI_Enable(); + /* Wait till HSI is ready */ + while (LL_RCC_HSI_IsReady() != 1) {} + + LL_RCC_HSI_SetCalibTrimming(64); + LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI, LL_RCC_PLLM_DIV_4, 85, LL_RCC_PLLR_DIV_2); + LL_RCC_PLL_EnableDomain_SYS(); + LL_RCC_PLL_Enable(); + /* Wait till PLL is ready */ + while (LL_RCC_PLL_IsReady() != 1) {} + + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); + // LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); + /* Wait till System clock is ready */ + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {} + + /* Insure 1us transition state at intermediate medium speed clock*/ + for (__IO uint32_t i = (170 >> 1); i != 0; i--) + ; + + /* Set AHB prescaler*/ + LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); + LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); + LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); + LL_SetSystemCoreClock(170000000); + + /* Update the time base */ + if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) { + Error_Handler(); + } +} + +/* USER CODE BEGIN 4 */ + +/* USER CODE END 4 */ + +/** + * @brief This function is executed in case of error occurrence. + * @retval None + */ +void Error_Handler(void) +{ + /* USER CODE BEGIN Error_Handler_Debug */ + /* User can add his own implementation to report the HAL error return + * state */ + __disable_irq(); + while (1) {} + /* USER CODE END Error_Handler_Debug */ +} +#ifdef USE_FULL_ASSERT +/** + * @brief Reports the name of the source file and the source line number + * where the assert_param error has occurred. + * @param file: pointer to the source file name + * @param line: assert_param error line source number + * @retval None + */ +void assert_failed(uint8_t *file, uint32_t line) +{ + /* USER CODE BEGIN 6 */ + /* User can add his own implementation to report the file name and line + number, ex: printf("Wrong parameters value: file %s on line %d\r\n", + file, line) */ + /* USER CODE END 6 */ +} +#endif /* USE_FULL_ASSERT */ diff --git a/G4CANTESTING/CAN_RELEASE_TEST/CMakeLists.txt b/G4CANTESTING/CAN_RELEASE_TEST/CMakeLists.txt new file mode 100644 index 000000000..ccedc8c6d --- /dev/null +++ b/G4CANTESTING/CAN_RELEASE_TEST/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 3.25) + +# Setup compiler settings +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS ON) + +# Define the build type +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# Enable compile command to ease indexing with e.g. clangd +set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) + +# Enable CMake support for ASM and C languages +enable_language( + C + ASM +) + +# Core project settings +project(${CMAKE_PROJECT_NAME}) + +# what, does in fact not get the filename of somthing but rather the name of the project from the path +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +add_library(${PROJECT_NAME}_USER_CODE INTERFACE) + +target_sources( + ${PROJECT_NAME}_USER_CODE + INTERFACE + ../Core/Src/adc.c + ../Core/Src/crc.c + ../Core/Src/dma.c + ../Core/Src/gpio.c + ../Core/Src/i2c.c + main.c + ../Core/Src/spi.c + ../Core/Src/stm32g4xx_hal_msp.c + ../Core/Src/stm32g4xx_it.c + ../Core/Src/tim.c + ../Core/Src/usart.c +) + +set_target_properties( + ${NAME} + PROPERTIES + OUTPUT_NAME + "CAN_${NAME}" +) + +target_link_libraries(${PROJECT_NAME}_USER_CODE INTERFACE PERIPHERAL_CAN_LIB) + +target_include_directories( + ${PROJECT_NAME}_USER_CODE + INTERFACE + ../Core/Inc + . +) diff --git a/G4CANTESTING/CAN_RELEASE_TEST/can_cfg.h b/G4CANTESTING/CAN_RELEASE_TEST/can_cfg.h new file mode 100644 index 000000000..fc11e3e23 --- /dev/null +++ b/G4CANTESTING/CAN_RELEASE_TEST/can_cfg.h @@ -0,0 +1,11 @@ + +#ifndef CAN_CFG_H +#define CAN_CFG_H + +#define USECAN1 +#define TX_BUFFER_1_SIZE 10 + +#define USECAN2 +#define TX_BUFFER_2_SIZE 10 + +#endif diff --git a/G4CANTESTING/CAN_RELEASE_TEST/main.c b/G4CANTESTING/CAN_RELEASE_TEST/main.c new file mode 100644 index 000000000..a3197bb29 --- /dev/null +++ b/G4CANTESTING/CAN_RELEASE_TEST/main.c @@ -0,0 +1,274 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : main.c + * @brief : Main program body + ****************************************************************************** + * @attention + * + * Copyright (c) 2024 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ +/* Includes ------------------------------------------------------------------*/ +#include "main.h" + +#include "adc.h" +#include "can.h" // Assume this works +#include "can_tests.h" +#include "dma.h" +#include "fdcan.h" +#include "gpio.h" +#include "i2c.h" +#include "spi.h" +#include "tim.h" +#include "usart.h" + +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ +#include "Logomatic.h" +/* USER CODE END Includes */ + +/* Private typedef -----------------------------------------------------------*/ +/* USER CODE BEGIN PTD */ + +/* USER CODE END PTD */ + +/* Private define ------------------------------------------------------------*/ +/* USER CODE BEGIN PD */ + +/* USER CODE END PD */ + +/* Private macro -------------------------------------------------------------*/ +/* USER CODE BEGIN PM */ + +/* USER CODE END PM */ + +/* Private variables ---------------------------------------------------------*/ + +/* USER CODE BEGIN PV */ +/* USER CODE END PV */ + +/* Private function prototypes -----------------------------------------------*/ +void SystemClock_Config(void); +/* USER CODE BEGIN PFP */ + +/* USER CODE END PFP */ + +/* Private user code ---------------------------------------------------------*/ +/* USER CODE BEGIN 0 */ +/* Enable ITM for SWO output */ +static void ITM_Enable(void) +{ + LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB); + LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; + GPIO_InitStruct.Pin = LL_GPIO_PIN_3; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; + GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + GPIO_InitStruct.Alternate = LL_GPIO_AF_0; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + DBGMCU->CR |= DBGMCU_CR_TRACE_IOEN; + + /* Enable TRC (Trace) */ + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + + /* Configure TPI for SWO output (set prescaler for 2MHz SWO clock) */ + TPI->SPPR = 2U; /* 2 = Manchester/async UART mode */ + TPI->ACPR = 84U; /* Prescaler: (170 MHz / (84+1) / 2) ≈ 1MHz SWO */ + + ITM->TER |= (1UL << 0); + ITM->TCR |= (ITM_TCR_ITMENA_Msk | ITM_TCR_SWOENA_Msk); +} +// static int toggleze = 0; +/* USER CODE END 0 */ + +/** + * @brief The application entry point. + * @retval int + */ +int main(void) +{ + /* USER CODE BEGIN 1 */ + + /* USER CODE END 1 */ + + /* MCU + * Configuration--------------------------------------------------------*/ + + /* Reset of all peripherals, Initializes the Flash interface and the + * Systick. */ + HAL_Init(); + /* USER CODE BEGIN Init */ + ITM_Enable(); + /* USER CODE END Init */ + + /* Configure the system clock */ + SystemClock_Config(); + + /* USER CODE BEGIN SysInit */ + + /* USER CODE END SysInit */ + + /* Initialize all configured peripherals */ + MX_GPIO_Init(); + MX_DMA_Init(); + // MX_FDCAN2_Init(); + MX_ADC1_Init(); + MX_LPUART1_UART_Init(); + MX_I2C2_Init(); + MX_USART1_UART_Init(); + MX_SPI3_Init(); + MX_TIM2_Init(); + + /* USER CODE BEGIN 2 */ + + LOGOMATIC("Booted!\n"); + + // LOGOMATIC("running can_release_test:\n"); + can_release_test(); + + /* Infinite loop */ + /* USER CODE BEGIN WHILE */ + while (1) { + /* USER CODE END WHILE */ + LOGOMATIC("Main Loop\n"); + LL_mDelay(1000); + + // Receive on GPIOs + // HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, toggleze ? GPIO_PIN_SET + // : GPIO_PIN_RESET); HAL_Delay(1000); msg.data[0] = toggleze ? + // 0x00 : 0x80; can_send(data_can, &msg); + + // RCC->CFGR |= RCC_CFGR_SW; + /* USER CODE BEGIN 3 */ + } +} + +/** + * @brief System Clock Configuration + * @retval None + */ + +// void SystemClock_Config(void) +// { +// LL_FLASH_SetLatency(LL_FLASH_LATENCY_4); +// while (LL_FLASH_GetLatency() != LL_FLASH_LATENCY_4) { +// } +// LL_PWR_EnableRange1BoostMode(); +// LL_RCC_HSE_Enable(); + +// /* Wait till HSE is ready */ +// while (LL_RCC_HSE_IsReady() != 1) { +// } + +// LL_RCC_HSE_EnableCSS(); +// LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_1, 20, +// LL_RCC_PLLR_DIV_2); +// //LL_RCC_PLL_ConfigDomain_48M(LL_RCC_PLLSOURCE_HSE, ); +// LL_RCC_PLL_EnableDomain_SYS(); +// LL_RCC_PLL_Enable(); +// /* Wait till PLL is ready */ +// while (LL_RCC_PLL_IsReady() != 1) { +// } + +// LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); +// LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2); +// /* Wait till System clock is ready */ +// while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) { +// } + +// /* Insure 1us transition state at intermediate medium speed clock*/ +// for (__IO uint32_t i = (170 >> 1); i != 0; i--) +// ; + +// /* Set AHB prescaler*/ +// LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); +// LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); +// LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); +// LL_SetSystemCoreClock(160000000); + +// /* Update the time base */ +// if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) { +// Error_Handler(); +// } +// }*/ +void SystemClock_Config(void) +{ + LL_FLASH_SetLatency(LL_FLASH_LATENCY_4); + while (LL_FLASH_GetLatency() != LL_FLASH_LATENCY_4) {} + LL_PWR_EnableRange1BoostMode(); + LL_RCC_HSI_Enable(); + /* Wait till HSI is ready */ + while (LL_RCC_HSI_IsReady() != 1) {} + + LL_RCC_HSI_SetCalibTrimming(64); + LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI, LL_RCC_PLLM_DIV_4, 85, LL_RCC_PLLR_DIV_2); + LL_RCC_PLL_EnableDomain_SYS(); + LL_RCC_PLL_Enable(); + /* Wait till PLL is ready */ + while (LL_RCC_PLL_IsReady() != 1) {} + + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); + // LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); + /* Wait till System clock is ready */ + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {} + + /* Insure 1us transition state at intermediate medium speed clock*/ + for (__IO uint32_t i = (170 >> 1); i != 0; i--) + ; + + /* Set AHB prescaler*/ + LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); + LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); + LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); + LL_SetSystemCoreClock(170000000); + + /* Update the time base */ + if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) { + Error_Handler(); + } +} + +/* USER CODE BEGIN 4 */ + +/* USER CODE END 4 */ + +/** + * @brief This function is executed in case of error occurrence. + * @retval None + */ +void Error_Handler(void) +{ + /* USER CODE BEGIN Error_Handler_Debug */ + /* User can add his own implementation to report the HAL error return + * state */ + __disable_irq(); + while (1) {} + /* USER CODE END Error_Handler_Debug */ +} +#ifdef USE_FULL_ASSERT +/** + * @brief Reports the name of the source file and the source line number + * where the assert_param error has occurred. + * @param file: pointer to the source file name + * @param line: assert_param error line source number + * @retval None + */ +void assert_failed(uint8_t *file, uint32_t line) +{ + /* USER CODE BEGIN 6 */ + /* User can add his own implementation to report the file name and line + number, ex: printf("Wrong parameters value: file %s on line %d\r\n", + file, line) */ + /* USER CODE END 6 */ +} +#endif /* USE_FULL_ASSERT */ diff --git a/G4CANTESTING/CMakeLists.txt b/G4CANTESTING/CAN_STRESS_TEST/CMakeLists.txt similarity index 72% rename from G4CANTESTING/CMakeLists.txt rename to G4CANTESTING/CAN_STRESS_TEST/CMakeLists.txt index e1961b557..3be5c86bd 100644 --- a/G4CANTESTING/CMakeLists.txt +++ b/G4CANTESTING/CAN_STRESS_TEST/CMakeLists.txt @@ -30,24 +30,24 @@ add_library(${PROJECT_NAME}_USER_CODE INTERFACE) target_sources( ${PROJECT_NAME}_USER_CODE INTERFACE - Core/Src/adc.c - Core/Src/crc.c - Core/Src/dma.c - Core/Src/gpio.c - Core/Src/i2c.c - Core/Src/main.c - Core/Src/spi.c - Core/Src/stm32g4xx_hal_msp.c - Core/Src/stm32g4xx_it.c - Core/Src/tim.c - Core/Src/usart.c + ../Core/Src/adc.c + ../Core/Src/crc.c + ../Core/Src/dma.c + ../Core/Src/gpio.c + ../Core/Src/i2c.c + main.c + ../Core/Src/spi.c + ../Core/Src/stm32g4xx_hal_msp.c + ../Core/Src/stm32g4xx_it.c + ../Core/Src/tim.c + ../Core/Src/usart.c ) target_link_libraries(${PROJECT_NAME}_USER_CODE INTERFACE PERIPHERAL_CAN_LIB) -target_link_libraries( + +target_include_directories( ${PROJECT_NAME}_USER_CODE INTERFACE - PERIPHERAL_CAN_TEST_LIB + ../Core/Inc + . ) - -target_include_directories(${PROJECT_NAME}_USER_CODE INTERFACE Core/Inc) diff --git a/G4CANTESTING/CAN_STRESS_TEST/can_cfg.h b/G4CANTESTING/CAN_STRESS_TEST/can_cfg.h new file mode 100644 index 000000000..fc11e3e23 --- /dev/null +++ b/G4CANTESTING/CAN_STRESS_TEST/can_cfg.h @@ -0,0 +1,11 @@ + +#ifndef CAN_CFG_H +#define CAN_CFG_H + +#define USECAN1 +#define TX_BUFFER_1_SIZE 10 + +#define USECAN2 +#define TX_BUFFER_2_SIZE 10 + +#endif diff --git a/G4CANTESTING/Core/Src/main.c b/G4CANTESTING/CAN_STRESS_TEST/main.c similarity index 99% rename from G4CANTESTING/Core/Src/main.c rename to G4CANTESTING/CAN_STRESS_TEST/main.c index c659e0390..3e6f61c53 100644 --- a/G4CANTESTING/Core/Src/main.c +++ b/G4CANTESTING/CAN_STRESS_TEST/main.c @@ -133,7 +133,8 @@ int main(void) LOGOMATIC("Booted!\n"); - can_test(); + // LOGOMATIC("running can_stress_test:\n"); + can_stress_test(); /* Infinite loop */ /* USER CODE BEGIN WHILE */ diff --git a/Lib/Peripherals/CAN/Inc/can.h b/Lib/Peripherals/CAN/Inc/can.h index c20cbedae..aa1a37363 100644 --- a/Lib/Peripherals/CAN/Inc/can.h +++ b/Lib/Peripherals/CAN/Inc/can.h @@ -8,6 +8,7 @@ #error "Unsupported STM32 Family" #endif +#include "can_cfg.h" #include "can_platform_deps.h" #include "circularBuffer.h" @@ -24,7 +25,7 @@ typedef struct { uint32_t tx_interrupt_priority; // Circular Buffer - uint32_t tx_buffer_length; + // 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 @@ -34,17 +35,37 @@ typedef struct { // additional parameters } CANConfig; +#define FDCAN_MAX_DATA_BYTES 64 +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; - CircularBuffer *tx_buffer; - uint32_t tx_buffer_length; + // 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 @@ -54,15 +75,10 @@ typedef struct { // error states } CANHandle; -#define FDCAN_MAX_DATA_BYTES 64 -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; +typedef enum { + CAN_SUCCESS, + CAN_ERROR +} CAN_STATUS; CANHandle *can_init(const CANConfig *config); // user must supply an rx callback function int can_start(CANHandle *handle); @@ -77,4 +93,9 @@ int can_add_filter(CANHandle *handle, FDCAN_FilterTypeDef *filter); // 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 +// TODO: Add thread mode vs handler mode checking (None of these functions should be called in handler mode) + +// Configuration helpers +int get_cfg(FDCAN_GlobalTypeDef *instance, CAN_RXCallback callback, CANConfig *out_cfg, uint32_t Mode); + #endif // End Header Guard diff --git a/Lib/Peripherals/CAN/README.md b/Lib/Peripherals/CAN/README.md index 0bbd70084..b33700fd0 100644 --- a/Lib/Peripherals/CAN/README.md +++ b/Lib/Peripherals/CAN/README.md @@ -1,75 +1,219 @@ -- USAGE: +# Gaucho Racing CAN API Reference -CANHandle* can_init(CANConfig *config); //user must supply an rx callback function +This document provides an overview of the Gaucho Racing CAN API, a simplified wrapper for STM32 FDCAN peripherals (specifically STM32G4). It handles hardware initialization, interrupt-driven message transmission with software buffering, and simplified receiver callbacks. -void can_set_clksource(uint32_t LL_RCC_FDCAN_CLKSOURCE); //ex LL_RCC_FDCAN_CLKSOURCE_PCLK1 for STM32G474RE +--- -int can_start(CANHandle*handle); -int can_stop(CANHandle*handle); -int can_send(CANHandle*handle, FDCANMessage* buffer, size_t send); -int can_release(CANHandle* handle); //deinit circular buffer and turn off can peripheral and gpios -int can_add_filter(CANHandle* handle, HAL_FDCAN_FilterTypeDef * filter); -int can_add_global_filter(CANHandle* handle, HAL_FDCAN_FilterTypeDef* filter); +# Build Usage -//alternatively instead use the HAL filter functions -//HAL_FDCAN_ConfigGlobalFilter(canHandle->hal_fdcanP, filterTypeDef) -//HAL_FDCAN_ConfigFilter(ca) +In your application*, add a file called `can_cfg.h`, containing at least: -If no filters are set, the default behaviour is to accept all standard and extended frames into the RXFIFO0 +```c +#ifndef CAN_CFG_H +#define CAN_CFG_H -PROBLEMS: -Verify ISR safety, no race conditions, atomic read/writes - - Interrupts keep firing while trying to can_release() - - Could try to set the NVIC register to selectively disable interrupts (preferably using a bitmask) -- Need to discuss expected behaviour of API - - particularly can_start, can_stop - - can_release -- Freeing within ISRs whenever popping from CircularBuffer (yes its faster, than stack copies, but heap is getting fragmented) -- ISRS might take too long to resolve because popping and freeing circular buffer. +#define USECAN<1/2/3> +#define TX_BUFFER_<1/2/3>_SIZE -- HARDCODE Platform Usage Flag for compiler definitions -- CAN.H expects #STM32G4 to be defined, +// ... potentially other USECANM + TX_BUFFER_M_SIZE defines... -- RX Callback must perform deep copy of data supplied to it - could also malloc, but not safe to do inside ISRs +#endif +``` --Shouldn't disable GPIOs in the MSP layers when releasing, might affect other peripherals +for each CAN peripheral you want to use. -IDEAS for other features: -- abstract to different STM families besides STM32G4 -- Rx Buffering -- TX Buffering policy, do we spread them out over multiple TX buffers -- DMA support for copying from circular buffer, circular buffer could then be stack allocated -- Smaller can headers for tx and rx (right now its just use the TXHeaderTypeDef) -- TX FIFO vs Queue policy (only allow FIFOS) -- Add support for RXFifo1 +\* Make sure `can_cfg.h` is in a folder included by `target_include_directories` in the project's `CMakeLists.txt`! -TESTING- ---------------------------------------------- -USE LOGOMATIC for return status - -either returns through semihosting or debug cores -LOGOMATIC is defined platform by platform +--- -Testing framework -- Can operate on API states and behaviours, but API should work across platforms -- All API tests are defined in can_test.c -- All tests are run from the top level function in can_test.c +# 1. Quick Start Workflow -- can_test.c should initialize everything properly. -- May have to create platform specific asserts when testing state -- use LOGOMATIC to return errors or throw asserts +1. **Set Clock Source** + Call `can_set_clksource` before any other operation. -- Platform testing, such as in G4PERTESTING just needs include "can_test.h" and call top level function -in main. +2. **Define Callback** + Create a function to handle incoming messages. -Two approaches: -Platform centric -- In G4PERTesting, include "can_tests.h" and call the top level function in can_test.c -- This approach is better because we can abstract the logging and debug method +3. **Configure** + Use `get_cfg` helper or fill a `CANConfig` struct. -Library Centric Testing: -- Test the implementation in each library. +4. **Initialize** + Call `can_init`. -HAL_Rewrite: -- Alternatively, rewrite without using HAL, just use CMSIS definitions. -- PROS: Would look good on your Github. -- CONS: takes too long +5. **Start** + Call `can_start` to enable the peripheral and interrupts. + +6. **Communicate** + Use `can_send` to transmit data. + +--- + +# 2. Global Configuration + +## `can_set_clksource` + +Sets the FDCAN kernel clock, which must be common across all CAN instances. + +**Parameter** + +```c +uint32_t clksource +``` + +Example: + +```c +LL_RCC_FDCAN_CLKSOURCE_PCLK1 +``` + +**Usage** + +Must be called once before `can_init`. + +--- + +# 3. Initialization and Setup + +## `CANConfig` Structure + +This structure contains all parameters required to bridge the Gaucho API with the underlying STM32 HAL. + +| Field | Description | +|------|-------------| +| `fdcan_instance` | Base address (e.g., `FDCAN1`, `FDCAN2`). | +| `hal_fdcan_init` | Standard HAL `FDCAN_InitTypeDef` struct (baud rate, etc). | +| `rx_callback` | Your custom handler for received messages. | +| `rx_interrupt_priority` | Priority for RX interrupts (0–15). | +| `tx_interrupt_priority` | Priority for TX interrupts (0–15). | +| `rx_gpio` / `tx_gpio` | GPIO Port and Init structures for CAN pins. | + +--- + +## Configuration Helper + +Instead of filling the `CANConfig` manually, use the helper function in `can_cfg_helpers.c`: + +```c +int get_cfg( + FDCAN_GlobalTypeDef *instance, + CAN_RXCallback callback, + CANConfig *out_cfg, + uint32_t Mode +); +``` + +--- + +# 4. Example Usage + +## Define RX Callback + +```c +void on_receive(uint32_t ID, void *data, uint32_t size) +{ + uint8_t my_data[64]; + + memcpy(my_data, data, size); + + // Process my_data... +} +``` + +--- + +## Initialize CAN + +```c +can_set_clksource(LL_RCC_FDCAN_CLKSOURCE_PCLK1); + +CANConfig my_cfg; + +get_cfg(FDCAN1, on_receive, &my_cfg, FDCAN_MODE_NORMAL); + +CANHandle *h1 = can_init(&my_cfg); +``` + +--- + +## Start and Send + +```c +can_start(h1); + +FDCANTxMessage msg = { ... }; // Fill header and data + +can_send(h1, &msg); +``` + +--- + +# 5. Callback Definition + +```c +void MyCAN_RxCallback(uint32_t ID, void *data, uint32_t size); +``` + +--- + +# 6. Communication + +## `can_send` + +Sends a message using a `FDCANTxMessage` struct. + +**Behavior** + +If the hardware FIFO is full, the message is automatically queued in a software buffer. + +**Returns** + +``` +CAN_SUCCESS +CAN_ERROR +``` + +--- + +## `can_add_filter` + +Adds a hardware-level filter to the instance. + +**Constraint** + +Must be called while the peripheral is initialized but **not yet started**. + + + +# Implementation Notes and Constraints + +- `RX Callback` must perform a **deep copy** of the data supplied to it. +- Allocating memory using `malloc` inside ISRs is **not safe**. +- `can.h` expects `STM32G4` to be defined as a compiler definition. +- Platform usage flags may be **hardcoded through compiler definitions**. +- GPIOs should **not be disabled in MSP layers during release**, as they may affect other peripherals. + +--- + +# Known Issues / Current Problems + +- ISRs may take **too long to resolve** due to popping and freeing the circular buffer. + +--- + +# Ideas and Future Improvements + +- DMA support for copying **64 bytes from circular buffer** +- Abstract support for **different STM32 families** besides STM32G4 +- RX buffering support +- TX buffering policy improvements + - Possibly distribute messages across multiple TX buffers +- DMA support for copying from circular buffer + - Circular buffer could then be **stack allocated** +- Smaller CAN headers for TX/RX + - Currently uses `TXHeaderTypeDef` +- TX FIFO vs Queue policy + - Possibly only allow **FIFO** +- Add support for **RXFifo1** + +--- diff --git a/Lib/Peripherals/CAN/Src/can.c b/Lib/Peripherals/CAN/Src/can.c index e989f071b..2054b61e4 100644 --- a/Lib/Peripherals/CAN/Src/can.c +++ b/Lib/Peripherals/CAN/Src/can.c @@ -8,20 +8,36 @@ #include "Logomatic.h" // HAL handles -// #ifdef USECAN1 +#ifdef USECAN1 +#ifndef TX_BUFFER_1_SIZE +#error "Please Define TX_BUFFER_1_SIZE" +#endif static FDCAN_HandleTypeDef hal_fdcan1 = {.Instance = FDCAN1}; -static CANHandle CAN1 = {.hal_fdcanP = &hal_fdcan1}; -// #endif - -// #ifdef USECAN2 +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}; -static CANHandle CAN2 = {.hal_fdcanP = &hal_fdcan2}; -// #endif - -// #ifdef USECAN3 +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}; -static CANHandle CAN3 = {.hal_fdcanP = &hal_fdcan3}; -// #endif +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 /* @@ -31,20 +47,20 @@ static CANHandle CAN3 = {.hal_fdcanP = &hal_fdcan3}; #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) + 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"); } \ + 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) */ @@ -73,102 +89,74 @@ static CANHandle CAN3 = {.hal_fdcanP = &hal_fdcan3}; LOGOMATIC("BAD FDCAN GPIO Port"); \ } 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) -{ - uint32_t primask = __get_PRIMASK(); - __disable_irq(); - - if (fdcan_shared_clock_ref == 0) { - __HAL_RCC_FDCAN_CLK_ENABLE(); - } - fdcan_shared_clock_ref++; - - __set_PRIMASK(primask); -} - -static inline void fdcan_disable_shared_clock(void) -{ - uint32_t primask = __get_PRIMASK(); - __disable_irq(); - - if (fdcan_shared_clock_ref > 0) { - fdcan_shared_clock_ref--; - if (fdcan_shared_clock_ref == 0) { - __HAL_RCC_FDCAN_CLK_DISABLE(); - } - } - __set_PRIMASK(primask); -} - -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) +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 int can_get_irqs(FDCAN_GlobalTypeDef *instance, IRQn_Type *it0, IRQn_Type *it1); +inline void can_set_clksource(uint32_t clksource) { - 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(); - } + 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 int can_get_irqs(FDCAN_GlobalTypeDef *instance, IRQn_Type *it0, IRQn_Type *it1); +static int can_msp_init(CANHandle *canHandle, CANConfig *config); +static int can_msp_deinit(CANHandle *canHandle); +static void FDCAN_InstanceDeInit(FDCAN_HandleTypeDef *hfdcan); -static int can_msp_init(CANHandle *handle, CANConfig *config); +//================================================= API ======================================== CANHandle *can_init(const CANConfig *config) { // config validation? // assert(config != 0) // #ifdef STM32G474xx - CANHandle *canHandle = 0; - // #ifdef USECAN1 + CANHandle *canHandle = NULL; +#ifdef USECAN1 if (config->fdcan_instance == FDCAN1) { if (CAN1.init) { LOGOMATIC("CAN: CAN1 is already initialized\n"); - return 0; + return CAN_SUCCESS; } else { canHandle = &CAN1; + canHandle->tx_capacity = TX_BUFFER_1_SIZE; } } - // #endif - // #ifdef USECAN2 - else if (config->fdcan_instance == FDCAN2) { +#endif +#ifdef USECAN2 + if (config->fdcan_instance == FDCAN2) { if (CAN2.init) { LOGOMATIC("CAN: CAN2 is already initialized\n"); - return 0; + return CAN_SUCCESS; } else { canHandle = &CAN2; + canHandle->tx_capacity = TX_BUFFER_2_SIZE; } } - // #ifdef USECAN3 - else if (config->fdcan_instance == FDCAN3) { +#endif + +#ifdef USECAN3 + if (config->fdcan_instance == FDCAN3) { if (CAN3.init) { LOGOMATIC("CAN: CAN3 is already initialized\n"); - return 0; + return CAN_SUCCESS; } else { canHandle = &CAN3; + canHandle->tx_capacity = TX_BUFFER_3_SIZE; } } - // #endif +#endif // #elif defined(STM32L476xx) // #else // #error "Unsupported STM32 family" // #endif - else { + if (canHandle == NULL) { LOGOMATIC("CAN: Unrecognized FDCAN instance"); - return 0; + return NULL; } canHandle->init = false; canHandle->started = false; @@ -181,23 +169,35 @@ CANHandle *can_init(const CANConfig *config) 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; - canHandle->tx_buffer_length = config->tx_buffer_length; + + // tx buffer + // canHandle->tx_capacity = TX_BUFFER_SIZE_1; //dependent on can instance + canHandle->tx_tail = 0; + canHandle->tx_elements = 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 do nothing at all, do all the work in can_msp_init() - if (can_msp_init(canHandle, (CANConfig *)config)) { + // 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))) { LOGOMATIC("CAN_init: could not initialize MSP resources"); - can_release(canHandle); + return 0; } // 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; + return 0; // Error_Handler(); } @@ -217,156 +217,182 @@ CANHandle *can_init(const CANConfig *config) if (status & HAL_ERROR) { LOGOMATIC("CAN: Could not activate rx and tx interrupts\n"); - return NULL; + failure |= status; } // Circular Buffer - canHandle->tx_buffer = GR_CircularBuffer_Create(config->tx_buffer_length); + // 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("CAN: Could not allocate circular buffer\n"); - return 0; + 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 CAN_SUCCESS; + } + + /*if (!canHandle->tx_buffer) { + LOGOMATIC("CAN: Could not allocate circular buffer\n"); + return CAN_SUCCESS; + }*/ + canHandle->init = true; canHandle->started = false; return canHandle; } -inline void can_set_clksource(uint32_t clksource) -{ - LL_RCC_SetFDCANClockSource(clksource); -} - -// only valid for #STM32G474x, must redefine for each family -static int can_msp_init(CANHandle *canHandle, CANConfig *config) +int can_release(CANHandle *canHandle) { - // MSP Init ------- This could be inside HAL_FDCAN_MspInit() instead - // FDCAN Clock Select + if (!canHandle) { + LOGOMATIC("CAN: Tried to release a null handle"); + return CAN_ERROR; + } - fdcan_enable_shared_clock(); - // Clock speed for FDCAN determined by APB1 clock speed and FDCAN prescaler + 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; + } - // GPIOs init - GPIOx_CLK_ENABLE(config->rx_gpio); - GPIOx_CLK_ENABLE(config->tx_gpio); + // No more interrupts should be firing that modify canHandle + if (can_msp_deinit(canHandle)) { + LOGOMATIC("CAN_release: could not stop instance"); + return CAN_ERROR; + } - HAL_GPIO_Init(config->rx_gpio, &(config->init_rx_gpio)); - HAL_GPIO_Init(config->tx_gpio, &(config->init_tx_gpio)); + // 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); - IRQn_Type rxit = -1; - IRQn_Type txit = -1; - can_get_irqs(canHandle->hal_fdcanP->Instance, &rxit, &txit); + __DSB(); // Data Synchronization Barrier + __ISB(); // Instruction Synchronization Barrier - // rxfifo0 - HAL_NVIC_SetPriority(rxit, config->rx_interrupt_priority, 0); - HAL_NVIC_EnableIRQ(rxit); + // 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; - // tx - HAL_NVIC_SetPriority(txit, config->tx_interrupt_priority, 0); - HAL_NVIC_EnableIRQ(txit); - // End MSP Init -------------- + // reset can handle + FDCAN_HandleTypeDef *temp = canHandle->hal_fdcanP; + memset(canHandle, 0, sizeof(*canHandle)); + canHandle->hal_fdcanP = temp; - return 0; + return CAN_SUCCESS; } - -// valid only for STM32G4 -static int can_get_irqs(FDCAN_GlobalTypeDef *instance, IRQn_Type *it0, IRQn_Type *it1) +// TODO: Implement timer +// lock access to Circular Buffer when sending and dequeuing +static void can_tx_dequeue_helper(CANHandle *handle) { - if (instance == FDCAN1) { - *it0 = FDCAN1_IT0_IRQn; - *it1 = FDCAN1_IT1_IRQn; - return 0; - } - if (instance == FDCAN2) { - *it0 = FDCAN2_IT0_IRQn; - *it1 = FDCAN2_IT1_IRQn; - return 0; + if (!handle || !handle->tx_buffer) { + LOGOMATIC("can_tx_buffer_helper: handle is invalid"); + return; } - if (instance == FDCAN3) { - *it0 = FDCAN3_IT0_IRQn; - *it1 = FDCAN3_IT1_IRQn; - return 0; + + // 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 << 4); + return; } - return -1; // invalid instance -} + // Can Add to Fifo Q + if (HAL_FDCAN_GetTxFifoFreeLevel(handle->hal_fdcanP)) { + const FDCANTxMessage *msg = &(handle->tx_buffer[handle->tx_tail]); -// valid only for STM32G4 -// static const char *can_get_instance_name(FDCAN_GlobalTypeDef *instance) -// { -// if (instance == FDCAN1) { -// return "FDCAN1"; -// } else if (instance == FDCAN2) { -// return "FDCAN2"; -// } else if (instance == FDCAN3) { -// return "FDCAN3"; -// } -// return "UNKNOWN"; -// } + // should call Tx Buffer Callback once complete + HAL_StatusTypeDef status = HAL_FDCAN_AddMessageToTxFifoQ(handle->hal_fdcanP, &msg->tx_header, msg->data); -// valid only for STM32G4 -static CANHandle *can_get_buffer_handle(FDCAN_HandleTypeDef *hfdcan) -{ - // #ifdef STM32G474xx - if (hfdcan->Instance == FDCAN1) { - return &CAN1; - } else if (hfdcan->Instance == FDCAN2) { - return &CAN2; - } else if (hfdcan->Instance == FDCAN3) { - return &CAN3; - } else { - LOGOMATIC("CAN_get_buffer_handle: was given invalid FDCAN instance\n"); - return 0; - } + 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 << 4); + 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 << 4); } -static void can_tx_buffer_helper(CANHandle *handle) +int can_send(CANHandle *canHandle, FDCANTxMessage *message) { - while (HAL_FDCAN_GetTxFifoFreeLevel(handle->hal_fdcanP) && !GR_CircularBuffer_IsEmpty(handle->tx_buffer)) { - FDCANTxMessage *msg = GR_CircularBuffer_Pop(handle->tx_buffer); + if (!canHandle || !message) { + LOGOMATIC("CAN_send: received null pointer\n"); + return CAN_ERROR; + } - if (!msg) { - break; - } + if (!canHandle->init || !canHandle->started) { + LOGOMATIC("CAN_send: CAN not initialized or started\n"); + return CAN_ERROR; + } - HAL_StatusTypeDef status = HAL_FDCAN_AddMessageToTxFifoQ(handle->hal_fdcanP, &msg->tx_header, msg->data); + // 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 + uint32_t basepri = __get_BASEPRI(); + __set_BASEPRI((canHandle->tx_interrupt_priority) << 4); + 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); + + uint32_t val = 0; if (status != HAL_OK) { - LOGOMATIC("CAN_tx_helper: failed to add message to FIFO\n"); - free(msg); // Free the message we couldn't send - break; // Stop trying to send more + LOGOMATIC("CAN_send: failed to add to HW FIFO\n"); + val = CAN_ERROR; + } else { + val = CAN_SUCCESS; } - - free(msg); // Successfully sent, free the memory + __set_BASEPRI(basepri << 4); + return val; } -} + //} -void FDCAN1_IT0_IRQHandler(void) -{ - HAL_FDCAN_IRQHandler(&hal_fdcan1); -} -void FDCAN1_IT1_IRQHandler(void) -{ - HAL_FDCAN_IRQHandler(&hal_fdcan1); -} + // Hardware FIFO full, try software buffer + if (canHandle->tx_elements < canHandle->tx_capacity) { + // int result = GR_CircularBuffer_Push(canHandle->tx_buffer, message, sizeof(FDCANTxMessage)); -void FDCAN2_IT0_IRQHandler(void) -{ - HAL_FDCAN_IRQHandler(&hal_fdcan2); -} -void FDCAN2_IT1_IRQHandler(void) -{ - HAL_FDCAN_IRQHandler(&hal_fdcan2); -} + 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) ); -void FDCAN3_IT0_IRQHandler(void) -{ - HAL_FDCAN_IRQHandler(&hal_fdcan3); -} -void FDCAN3_IT1_IRQHandler(void) -{ - HAL_FDCAN_IRQHandler(&hal_fdcan3); + __set_BASEPRI(basepri << 4); + return CAN_SUCCESS; // added to software buffer + + /*if (result != 0) { + LOGOMATIC("CAN_send: buffer push failed\n"); + return CAN_ERROR; + } else { + return CAN_SUCCESS; + }*/ + } else { + LOGOMATIC("CAN_send: all buffers full\n"); // p + } + __set_BASEPRI(basepri << 4); + // Both buffers full + return CAN_ERROR; } void HAL_FDCAN_TxBufferCompleteCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t BufferIndexes) @@ -375,22 +401,20 @@ void HAL_FDCAN_TxBufferCompleteCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t Bu // If circular buffer has elements, send to queue // Otherwise do nothing // #ifdef USECAN1 - CANHandle *handle = can_get_buffer_handle(hfdcan); - - if (!handle || !handle->tx_buffer) { - return; - } - - if (GR_CircularBuffer_IsEmpty(handle->tx_buffer)) { - return; - } - + CANHandle *handle = can_get_handle(hfdcan); // see if you can pop any more from the buffer - can_tx_buffer_helper(handle); + can_tx_dequeue_helper(handle); } + +void HAL_FDCAN_TxFifoEmptyCallback(FDCAN_HandleTypeDef *hfdcan) +{ + CANHandle *handle = can_get_handle(hfdcan); + can_tx_dequeue_helper(handle); +} + void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) { - CANHandle *handle = can_get_buffer_handle(hfdcan); + CANHandle *handle = can_get_handle(hfdcan); if (!handle || !handle->init || !handle->rx_callback) { return; @@ -401,9 +425,9 @@ void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) return; } */ // no rx buffer at the moment - /*if (RxFifo0ITs & FDCAN_IT_RX_FIFO0_MESSAGE_LOST) { - lost_rx++; - }*/ + if (RxFifo0ITs & FDCAN_IT_RX_FIFO0_MESSAGE_LOST) { + // lost_rx++; + } if (!(RxFifo0ITs & ~FDCAN_IT_RX_FIFO0_MESSAGE_LOST)) { return; @@ -412,7 +436,7 @@ void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) // if (GR_CircularBuffer_IsFull(handle->rx_buffer)) return; FDCAN_RxHeaderTypeDef rx_header; - // TODO: Stack allocation may be unsafe + // TODO: Stack allocation may be unsafe, (but not unsafer than heap) uint8_t rx_data[64] = {0}; // TODO: maybe also use a timer for this? @@ -425,18 +449,13 @@ void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) // GR_OLD_MSG_ID messageID = (rx_header.Identifier & (0xFFF << 8)) >> 8; handle->rx_callback(rx_header.Identifier, rx_data, rx_header.DataLength); } - - /* whoopsie, don't need the rx buffer yet - while (HAL_FDCAN_GetRxFifoFillLevel(hfdcan, FDCAN_RX_FIFO0) & !GR_CircularBuffer_IsFull(handle->rx_buffer)) { - FDCAN_RxHeaderTypeDef rx_header; - uint8_t rx_data[64] = {0}; - HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &rx_header, &rx_data); - + /* if (GR_CircularBuffer_IsEmpty(handle->rx_buffer)) handle->rx_callback(rx_data, rx_header.DataLength); else { - GR_CircularBuffer_Push(handle->rx_buffer, rx_data, rx_header.DataLength); + GR_CircularBuffer_Push(handle->rx_buffer, rx_data, rx_header.DataLength); } }*/ + //__set_BASEPRI(prev_priority); } /* @@ -456,187 +475,328 @@ int can_add_filter(CANHandle *canHandle, FDCAN_FilterTypeDef *filter) { if (!canHandle) { LOGOMATIC("CAN_add_filter: handle is null"); - return -1; + return CAN_ERROR; } if (!canHandle->init || canHandle->started) { LOGOMATIC("CAN_add_filter: can instance is not initialized or already started"); - return -1; + return CAN_ERROR; } if (HAL_FDCAN_ConfigFilter(canHandle->hal_fdcanP, filter) != HAL_OK) { LOGOMATIC("CAN_add_filter: failed to configure filter"); - return -1; + return CAN_ERROR; } - return 0; + return CAN_SUCCESS; // check that # of filters isn't exceeding max value } -// Need to int can_start(CANHandle *canHandle) { if (!canHandle || !canHandle->init) { - return -1; + 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)) { + LOGOMATIC("can_start: could not obtain irq #s"); + 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; - return 0; + HAL_NVIC_EnableIRQ(rx0it); + HAL_NVIC_EnableIRQ(txit); + + return CAN_SUCCESS; } int can_stop(CANHandle *canHandle) { if (!canHandle || !canHandle->init) { - return -1; + return CAN_ERROR; } + if (!canHandle->started) { - return 0; + return CAN_SUCCESS; + } + + // stop can interrupts from activating + uint32_t prev_priority = __get_BASEPRI(); + __set_BASEPRI(MIN(canHandle->rx_interrupt_priority, canHandle->tx_interrupt_priority) << 4); + + if (HAL_FDCAN_Stop(canHandle->hal_fdcanP)) { + return ERROR; } - HAL_FDCAN_Stop(canHandle->hal_fdcanP); + IRQn_Type rx0it, txit; + rx0it = txit = -1; + if (can_get_irqs(canHandle->hal_fdcanP->Instance, &rx0it, &txit)) { + LOGOMATIC("can_stop: could not obtain irq #s"); + return CAN_ERROR; + } + HAL_NVIC_DisableIRQ(rx0it); + HAL_NVIC_DisableIRQ(txit); + HAL_NVIC_ClearPendingIRQ(rx0it); + HAL_NVIC_ClearPendingIRQ(txit); + + __set_BASEPRI(prev_priority << 4); GPIOx_CLK_DISABLE(canHandle->rx_gpio); GPIOx_CLK_DISABLE(canHandle->tx_gpio); canHandle->started = false; - return 0; + return CAN_SUCCESS; } -// Valid only for STM32G474xE -/*int can_msp_deinit(CANHandle* canHandle) { - //MSP DeInit - //turn off gpio clocks - can only turn off GPIOs if no other instances are using them +// ==================================== 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++; +} - return 0; -}*/ - -static void FDCAN_InstanceDeInit(FDCAN_HandleTypeDef *hfdcan) +static inline void fdcan_disable_shared_clock(void) { - // Enter INIT mode - hfdcan->Instance->CCCR |= FDCAN_CCCR_INIT; - while (!(hfdcan->Instance->CCCR & FDCAN_CCCR_INIT)) - ; + if (fdcan_shared_clock_ref > 0) { + fdcan_shared_clock_ref--; + if (fdcan_shared_clock_ref == 0) { + __HAL_RCC_FDCAN_CLK_DISABLE(); + } + } +} - // Clear filters - // TODO: fix magic numbers - memset((void *)hfdcan->msgRam.StandardFilterSA, 0, 0x0070); - memset((void *)hfdcan->msgRam.ExtendedFilterSA, 0, 0x0050); +// valid only for STM32G4 +static int can_get_irqs(FDCAN_GlobalTypeDef *instance, IRQn_Type *it0, IRQn_Type *it1) +{ + 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; + } - // Optionally reset FIFOs / buffers + return CAN_ERROR; // invalid instance +} - // 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); +// valid only for STM32G4 +static CANHandle *can_get_handle(FDCAN_HandleTypeDef *hfdcan) +{ +// #ifdef STM32G474xx +#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 - // Exit INIT mode - hfdcan->Instance->CCCR &= ~FDCAN_CCCR_INIT; - while (hfdcan->Instance->CCCR & FDCAN_CCCR_INIT) - ; + LOGOMATIC("CAN_get_handle: was given invalid FDCAN instance\n"); + UNUSED(hfdcan); + return CAN_SUCCESS; +} - // Update handle state - hfdcan->State = HAL_FDCAN_STATE_RESET; +/* +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(); + } } -int can_release(CANHandle *canHandle) +static inline void gpio_clk_disable(GPIO_TypeDef *gpio) { - if (!canHandle) { - LOGOMATIC("CAN: Tried to release a null handle"); - return -1; - } + 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(); + } +}*/ - if (!canHandle->init) { - LOGOMATIC("CAN_release: can instance is already deinitialized"); - return -1; - } - can_stop(canHandle); // try to prevent more interrupts from firing +// only valid for #STM32G474x, must redefine for each family +static int can_msp_init(CANHandle *canHandle, CANConfig *config) +{ + // MSP Init ------- This could be inside HAL_FDCAN_MspInit() instead + // FDCAN Clock Select - // must disable NVIC IRQs before freeing circular buffer + fdcan_enable_shared_clock(); - // turn off NVIC resources - IRQn_Type rx0it = -1; + // 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; - can_get_irqs(canHandle->hal_fdcanP->Instance, &rx0it, &txit); - HAL_NVIC_DisableIRQ(rx0it); - HAL_NVIC_DisableIRQ(txit); + can_get_irqs(canHandle->hal_fdcanP->Instance, &rxit, &txit); - // need to check if other pins are using before disabling - do this after mvp - // GPIOx_CLK_DISABLE(canHandle->rx_gpio); - // GPIOx_CLK_DISABLE(canHandle->tx_gpio); + // rxfifo0 + HAL_NVIC_SetPriority(rxit, config->rx_interrupt_priority, 0); - // 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); + // tx + HAL_NVIC_SetPriority(txit, config->tx_interrupt_priority, 0); + // End MSP Init -------------- - __DSB(); // Data Synchronization Barrier - __ISB(); // Instruction Synchronization Barrier + // Call can_start() to enable interrupts - // free circular buffer contents - GR_CircularBuffer_Free(&(canHandle->tx_buffer)); + return CAN_SUCCESS; +} - // reset can instance - FDCAN_HandleTypeDef *temp = canHandle->hal_fdcanP; - memset(canHandle, 0, sizeof(*canHandle)); - canHandle->hal_fdcanP = temp; +// Valid only for STM32G474xE +static int can_msp_deinit(CANHandle *canHandle) +{ + // MSP DeInit + // must disable NVIC IRQs before freeing circular buffer - fdcan_disable_shared_clock(); // only turns off clock if no other instances are running. + // NVIC + IRQn_Type rx0it = -1; + IRQn_Type txit = -1; + can_get_irqs(canHandle->hal_fdcanP->Instance, &rx0it, &txit); + 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 - return 0; + // RCC + // fdcan_disable_shared_clock(); + // can only disable clock after resetting all FDCAN instances + return CAN_SUCCESS; } -int can_send(CANHandle *canHandle, FDCANTxMessage *message) +static void FDCAN_InstanceDeInit(FDCAN_HandleTypeDef *hfdcan) { - if (!canHandle || !message) { - LOGOMATIC("CAN_send: received null pointer\n"); - return -1; - } + // Enter INIT mode + hfdcan->Instance->CCCR |= FDCAN_CCCR_INIT; + while (!(hfdcan->Instance->CCCR & FDCAN_CCCR_INIT)) + ; + hfdcan->Instance->CCCR |= FDCAN_CCCR_CCE; - if (!canHandle->init || !canHandle->started) { - LOGOMATIC("CAN_send: CAN not initialized or started\n"); - return -1; - } + // 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); - uint32_t primask = __get_PRIMASK(); - __disable_irq(); - // 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 - uint32_t free = HAL_FDCAN_GetTxFifoFreeLevel(canHandle->hal_fdcanP); + // + CLEAR_BIT(hfdcan->Instance->ILE, (FDCAN_INTERRUPT_LINE0 | FDCAN_INTERRUPT_LINE1)); - if (free > 0) { - HAL_StatusTypeDef status = HAL_FDCAN_AddMessageToTxFifoQ(canHandle->hal_fdcanP, &(message->tx_header), - message->data // Not &message->data if data is array - ); + // Clear filters + // TODO: fix magic numbers + memset((void *)hfdcan->msgRam.StandardFilterSA, 0, 0x0070); + memset((void *)hfdcan->msgRam.ExtendedFilterSA, 0, 0x0050); - __set_PRIMASK(primask); + // Optionally clear FIFOs / buffers - if (status != HAL_OK) { - LOGOMATIC("CAN_send: failed to add to HW FIFO\n"); - return -1; - } - return 0; - } + // Exit INIT mode + hfdcan->Instance->CCCR &= ~FDCAN_CCCR_INIT; + while (hfdcan->Instance->CCCR & FDCAN_CCCR_INIT) + ; - // Hardware FIFO full, try software buffer - if (!GR_CircularBuffer_IsFull(canHandle->tx_buffer)) { - int result = GR_CircularBuffer_Push(canHandle->tx_buffer, message, sizeof(FDCANTxMessage)); + // Update handle state + hfdcan->State = HAL_FDCAN_STATE_RESET; - __set_PRIMASK(primask); + fdcan_disable_shared_clock(); +} - if (result != 0) { - LOGOMATIC("CAN_send: buffer push failed\n"); - return -1; - } - return 0; - } +// valid only for STM32G4 +/*static const char *can_get_instance_name(FDCAN_GlobalTypeDef *instance) +{ + if (instance == FDCAN1) { + return "FDCAN1"; + } else if (instance == FDCAN2) { + return "FDCAN2"; + } else if (instance == FDCAN3) { + return "FDCAN3"; + } + return "UNKNOWN"; +}*/ - // Both buffers full - __set_PRIMASK(primask); - LOGOMATIC("CAN_send: all buffers full\n"); - return -1; +// ===================================== HAL Callbacks ================================ +// TODO: Implement Family Checks +// Probably is safe from races +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 } diff --git a/Lib/Peripherals/CAN/Src/can_cfg_helpers.c b/Lib/Peripherals/CAN/Src/can_cfg_helpers.c new file mode 100644 index 000000000..d9ae484f0 --- /dev/null +++ b/Lib/Peripherals/CAN/Src/can_cfg_helpers.c @@ -0,0 +1,106 @@ +#include + +#include "can.h" +#include "can_tests.h" + +int get_cfg(FDCAN_GlobalTypeDef *instance, CAN_RXCallback callback, CANConfig *out_cfg, uint32_t Mode) +{ +#ifdef STM32G4 + return defaultSTM32G4_CANCfg(instance, callback, out_cfg, Mode); +#elif defined(STM32L4) +#elif defined(STM32U5) +#error "STM32U5 is untested"#else +#else +#error "Untested STM32 Family" +#endif +} + +// TODO: Abstract out the system clock calculation +// Abstracts out everything but the mode and callback +int defaultSTM32G4_CANCfg(FDCAN_GlobalTypeDef *instance, CAN_RXCallback callback, CANConfig *out_cfg, uint32_t Mode) +{ + CANConfig canCfg; + // canCfg.fdcan_instance = FDCAN2; + + canCfg.hal_fdcan_init.ClockDivider = FDCAN_CLOCK_DIV1; + canCfg.hal_fdcan_init.FrameFormat = FDCAN_FRAME_FD_NO_BRS; + canCfg.hal_fdcan_init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; + canCfg.hal_fdcan_init.Mode = Mode; + canCfg.hal_fdcan_init.AutoRetransmission = ENABLE; + canCfg.hal_fdcan_init.TransmitPause = DISABLE; + canCfg.hal_fdcan_init.ProtocolException = ENABLE; + canCfg.hal_fdcan_init.NominalPrescaler = 1; + canCfg.hal_fdcan_init.NominalSyncJumpWidth = 16; + canCfg.hal_fdcan_init.NominalTimeSeg1 = 127; // Updated for 170MHz: (1+127+42)*1 = 170 ticks -> 1 Mbps + canCfg.hal_fdcan_init.NominalTimeSeg2 = 42; + canCfg.hal_fdcan_init.DataPrescaler = 8; + canCfg.hal_fdcan_init.DataSyncJumpWidth = 16; + canCfg.hal_fdcan_init.DataTimeSeg1 = 15; // Updated for 170MHz: (1+15+5)*8 = 168 ticks -> ~5 Mbps + canCfg.hal_fdcan_init.DataTimeSeg2 = 5; + canCfg.hal_fdcan_init.StdFiltersNbr = 1; + canCfg.hal_fdcan_init.ExtFiltersNbr = 0; + + canCfg.rx_callback = NULL; // PLEASE SET + canCfg.rx_interrupt_priority = 14; // PLEASE SET + canCfg.tx_interrupt_priority = 14; // PLEASE SET + + // canCfg.rx_gpio = GPIOB; + // canCfg.init_rx_gpio.Pin = GPIO_PIN_12; + canCfg.init_rx_gpio.Mode = GPIO_MODE_AF_PP; + canCfg.init_rx_gpio.Pull = GPIO_PULLUP; + canCfg.init_rx_gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + // canCfg.init_rx_gpio.Alternate = GPIO_AF9_FDCAN2; + + // canCfg.tx_gpio = GPIOB; + // canCfg.init_tx_gpio.Pin = GPIO_PIN_13; + canCfg.init_tx_gpio.Mode = GPIO_MODE_AF_PP; + canCfg.init_tx_gpio.Pull = GPIO_NOPULL; + canCfg.init_tx_gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + // canCfg.init_tx_gpio.Alternate = GPIO_AF9_FDCAN2; + + // Not testing filters at the moment + // FDCAN_FilterTypeDef filter; + + // can_add_filter(data_can, &filter); + /* USER CODE END 2 */ + + canCfg.rx_callback = callback; + +// TODO: Recheck these once you figure out how to define in application code +#ifdef USECAN1 + if (instance == FDCAN1) { + canCfg.fdcan_instance = FDCAN1; + canCfg.rx_gpio = GPIOA; + canCfg.init_rx_gpio.Pin = GPIO_PIN_11; + canCfg.init_rx_gpio.Alternate = GPIO_AF9_FDCAN1; + + canCfg.tx_gpio = GPIOA; + canCfg.init_tx_gpio.Pin = GPIO_PIN_12; + canCfg.init_tx_gpio.Alternate = GPIO_AF9_FDCAN1; + + *out_cfg = canCfg; + return SUCCESS; + } +#endif + +#ifdef USECAN2 + if (instance == FDCAN2) { + canCfg.fdcan_instance = FDCAN2; + canCfg.rx_gpio = GPIOB; + canCfg.init_rx_gpio.Pin = GPIO_PIN_12; + canCfg.init_rx_gpio.Alternate = GPIO_AF9_FDCAN2; + + canCfg.tx_gpio = GPIOB; + canCfg.init_tx_gpio.Pin = GPIO_PIN_13; + canCfg.init_tx_gpio.Alternate = GPIO_AF9_FDCAN2; + + *out_cfg = canCfg; + return SUCCESS; + } +#endif + + // #ifdef USECAN3 + // #endif + + return ERROR; +} diff --git a/Lib/Peripherals/CAN/TESTING.md b/Lib/Peripherals/CAN/TESTING.md new file mode 100644 index 000000000..8b1e82cd7 --- /dev/null +++ b/Lib/Peripherals/CAN/TESTING.md @@ -0,0 +1,207 @@ + +# Initializing a CAN Test + +This section describes how to initialize and run a CAN test using the project `CAN_STRESS_TEST` as an example. +The test entry point is implemented in `main.c`, and the project is built through the top-level `CMakeLists.txt`. + +--- + +# 1. Enabling the Test in CMake + +CAN tests are built as independent firmware projects through the helper macro: + +``` +add_gr_project() +``` + +In `CMakeLists.txt`, the stress test is enabled with: + +``` +add_gr_project(STM32G474xE G4CANTESTING CAN_STRESS_TEST) +``` + +This line instructs CMake to: + +- Create a firmware target for the **STM32G474xE platform** +- Use the **G4CANTESTING project template** +- Build the firmware located in the **CAN_STRESS_TEST directory** + +Other CAN test variants are defined in the same section: + +``` +add_gr_project(STM32G474xE G4CANTESTING CAN_EXTERNAL_TEST) +add_gr_project(STM32G474xE G4CANTESTING CAN_INTERNAL_TEST) +add_gr_project(STM32G474xE G4CANTESTING CAN_RELEASE_TEST) +add_gr_project(STM32G474xE G4CANTESTING CAN_STRESS_TEST) +``` + +These targets allow different test scenarios to be compiled independently. + +--- + +# 2. Required CAN Library Inclusion + +The CAN API and related utilities are included through CMake modules: + +``` +include("${lib_path}/Peripherals/CAN/common.cmake") +``` + +These modules provide: + +- CAN peripheral drivers +- configuration helpers +- buffer utilities +- CAN configuration code + +--- + +# 3. Entry Point (`main.c`) + +The CAN stress test is started from `main.c`. + +Key includes: + +``` +#include "can.h" +#include "can_tests.h" +#include "Logomatic.h" +``` + +These provide: + +- the CAN API +- the test framework +- logging utilities + +--- + +# 4. System Initialization + +Before running the test, the firmware initializes the MCU and all required peripherals in main.c. + +Typical initialization sequence: + +``` +HAL_Init(); + +SystemClock_Config(); + +MX_GPIO_Init(); +MX_LPUART1_UART_Init(); +``` + +This setup ensures that: + +- clocks are configured +- debugging output works +- peripherals required by the test environment are initialized + +--- + +# 5. Logging Setup + +Logging is performed using the `LOGOMATIC` utility. + +Example: + +``` +LOGOMATIC("Booted!\n"); +``` + +This typically outputs through: + +- semihosting +- SWO +- a debug UART + +--- + +# 6. Starting the CAN Stress Test + +After initialization, the test is executed directly from `main()`: + +``` +can_stress_test(); +``` + +This function is defined in the CAN test framework (``can_tests.h`) and performs the following tasks: + +- initializes CAN peripherals +- configures loopback or external modes +- transmits multiple test messages +- verifies message reception +- logs test results + +--- + +# 7. Main Loop + +After the test completes, the firmware enters the main loop: + +``` +while (1) { + LOGOMATIC("Main Loop\n"); + LL_mDelay(1000); +} +``` + +This loop keeps the firmware alive and allows continued debugging output. + +--- + +# 8. Typical CAN Test Flow + +A typical CAN test function performs the following steps: + +1. Configure CAN instances +2. Set the CAN clock source +3. Initialize the CAN handles +4. Start the CAN peripherals +5. Send test messages +6. Verify received messages +7. Release the CAN resources + +Example flow: + +``` +get_cfg(FDCAN1, rx_callback1, &cfg1, FDCAN_MODE_INTERNAL_LOOPBACK); +get_cfg(FDCAN2, rx_callback2, &cfg2, FDCAN_MODE_INTERNAL_LOOPBACK); + +can_set_clksource(LL_RCC_FDCAN_CLKSOURCE_PCLK1); + +CANHandle *can1 = can_init(&cfg1); +CANHandle *can2 = can_init(&cfg2); + +can_start(can1); +can_start(can2); + +can_send(can1, &msg); +can_send(can2, &msg); + +can_release(can1); +can_release(can2); +``` + +--- + +# 9. Summary + +To initialize and run a CAN test: + +1. Enable the test in `CMakeLists.txt` using `add_gr_project`. +2. Build the project using the desired CMake preset. +3. Flash the firmware containing `CAN_STRESS_TEST`. +4. The test is executed automatically from `main()` through: + +``` +can_stress_test(); +``` + +5. Results and diagnostics are printed through `LOGOMATIC`. + +This design allows each CAN test to run as a standalone firmware target while sharing the same CAN API implementation. + + + +--- diff --git a/Lib/Peripherals/CAN/Test/can_external_test.c b/Lib/Peripherals/CAN/Test/can_external_test.c new file mode 100644 index 000000000..682f191be --- /dev/null +++ b/Lib/Peripherals/CAN/Test/can_external_test.c @@ -0,0 +1,155 @@ +#include +#include + +#include "can.h" +#include "can_tests.h" + +// each family has a constant number of CAN peripherals + +/*int can_test_instance(FDCAN_HandleTypeDef fdcan_handle) +{ + //can_init(fdcan_handle); + UNUSED(fdcan_handle); + return 0; +}*/ + +// #define USECAN1 +// #define TX_BUFFER_1_SIZE 10 +// #define USECAN2 +// #define TX_BUFFER_2_SIZE 10 + +// TODO: could make creating these callbacks a macro, rather than defining each one separately +static volatile uint32_t rx_2_received = 0; +static void can_test_rx_callback2(uint32_t id, void *data, uint32_t size) +{ + rx_2_received++; + LOGOMATIC("CAN2 Got data! Size %ld, data[0] = 0x%x, id %" PRIu32 "\n", size, *(char *)data, id); + // Is within an ISR, so needs to exit quickly + return; +} + +static volatile uint32_t rx_1_received = 0; +static void can_test_rx_callback1(uint32_t id, void *data, uint32_t size) +{ + rx_1_received++; + LOGOMATIC("CAN1 Got data! Size %ld, data[0] = 0x%x, id %" PRIu32 "\n", size, *(char *)data, id); + + // Is within an ISR, so needs to exit quickly + return; +} + +// 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?? +int can_external_test(void) +{ + FDCAN_TxHeaderTypeDef TxHeader = { + .Identifier = 1, + + .IdType = FDCAN_STANDARD_ID, + .TxFrameType = FDCAN_DATA_FRAME, + .ErrorStateIndicator = FDCAN_ESI_ACTIVE, // honestly this might be a value you have to read from a node + // FDCAN_ESI_ACTIVE is just a state that assumes there are minimal errors + .DataLength = 1, + .BitRateSwitch = FDCAN_BRS_OFF, + .TxEventFifoControl = FDCAN_NO_TX_EVENTS, // change to FDCAN_STORE_TX_EVENTS if you need to store info regarding transmitted messages + .MessageMarker = 0 // also change this to a real address if you change fifo control + }; + + CANHandle *primary_can = 0, *data_can = 0; + primary_can = data_can = NULL; + CANConfig cfg1, cfg2; + + LOGOMATIC("Initializing primary and data CAN Bus in Normal mode.\n"); + if (get_cfg(FDCAN1, can_test_rx_callback1, &cfg1, FDCAN_MODE_NORMAL)) { + LOGOMATIC("Could not get config for FDCAN1\n"); + return ERROR; + } + if (get_cfg(FDCAN2, can_test_rx_callback2, &cfg2, FDCAN_MODE_NORMAL)) { + LOGOMATIC("Could not get config for FDCAN2\n"); + return ERROR; + } + + can_set_clksource(LL_RCC_FDCAN_CLKSOURCE_PCLK1); + + //============================================================================================= + if ((primary_can = can_init(&cfg1)) == NULL) { + LOGOMATIC("Could not initialize primary_can\n"); + return ERROR; + } + HAL_FDCAN_ConfigGlobalFilter(primary_can->hal_fdcanP, 0, 0, 0, 0); + + if ((data_can = can_init(&cfg2)) == NULL) { + LOGOMATIC("Could not initialize data_can\n"); + return ERROR; + } + HAL_FDCAN_ConfigGlobalFilter(data_can->hal_fdcanP, 0, 0, 0, 0); + + //============================================================================================= + if (can_start(primary_can)) { + LOGOMATIC("Could not start primary_can\n"); + return ERROR; + } + if (can_start(data_can)) { + LOGOMATIC("Could not start data_can\n"); + return ERROR; + } + + FDCANTxMessage msg = {0}; + msg.data[0] = 0x80; + msg.tx_header = TxHeader; + + uint32_t i = 0; + uint32_t num_messages = 5; + + LOGOMATIC("Sending %ld messages on each bus...\n", num_messages); + while (i < num_messages) { + HAL_Delay(100); + msg.data[0] = 0x2; + can_send(primary_can, &msg); + HAL_Delay(100); + msg.data[0] = 0x10; + can_send(data_can, &msg); + i += 1; + } + LOGOMATIC("Received %ld messages on bus1...\n", rx_1_received); + LOGOMATIC("Received %ld messages on bus2...\n", rx_2_received); + + uint32_t error = false; + + // TODO: Create testing functions to check state of can instance + if ((rx_1_received != num_messages)) { + error = true; + LOGOMATIC("FAIL: can_internal_test: did not receive all rx1\n"); + } else { + LOGOMATIC("SUCCESS: can_internal_test: received all rx1\n"); + } + if ((rx_2_received != num_messages)) { + error = true; + LOGOMATIC("FAIL: can_internal_test: did not receive all rx2\n"); + } else { + LOGOMATIC("SUCCESS: can_internal_test: received all rx2\n"); + } + + if (primary_can->tx_elements > 0) { + LOGOMATIC("can_internal_test: FAIL: did not send all messages from tx_buffer\n"); + } + LOGOMATIC("\n"); + + uint32_t rc; + if ((rc = can_release(primary_can))) { + LOGOMATIC("FAIL: can_external_test; could not release primary_can\n"); + } + error |= rc; + if ((rc = can_release(data_can))) { + LOGOMATIC("FAIL: can_external_test; could not release data_can\n"); + } + error |= rc; + + if (error) { + return ERROR; + } + + LOGOMATIC("can_external_test: SUCCESS\n"); + + return SUCCESS; +} diff --git a/Lib/Peripherals/CAN/Test/can_filter_test.c b/Lib/Peripherals/CAN/Test/can_filter_test.c new file mode 100644 index 000000000..ce66ebe89 --- /dev/null +++ b/Lib/Peripherals/CAN/Test/can_filter_test.c @@ -0,0 +1,3 @@ +#include "can_tests.h" + +// TODO: diff --git a/Lib/Peripherals/CAN/Test/can_init_test.c b/Lib/Peripherals/CAN/Test/can_init_test.c new file mode 100644 index 000000000..a58e253b3 --- /dev/null +++ b/Lib/Peripherals/CAN/Test/can_init_test.c @@ -0,0 +1,19 @@ +#include "can.h" +#include "can_tests.h" + +int can_init_test(CANConfig *cfg) +{ + CANHandle *handle; + if ((handle = can_init(cfg)) == NULL) { + LOGOMATIC("can_init_test: init failed\n"); + return ERROR; + } + + // inspect handle + if (!handle->init) { + LOGOMATIC("can_init_test: did not set init bool in handle\n"); + return ERROR; + } + + return SUCCESS; +} diff --git a/Lib/Peripherals/CAN/Test/can_internal_test.c b/Lib/Peripherals/CAN/Test/can_internal_test.c new file mode 100644 index 000000000..2976feecf --- /dev/null +++ b/Lib/Peripherals/CAN/Test/can_internal_test.c @@ -0,0 +1,154 @@ +#include +#include + +#include "can.h" +#include "can_tests.h" + +// each family has a constant number of CAN peripherals + +/*int can_test_instance(FDCAN_HandleTypeDef fdcan_handle) +{ + //can_init(fdcan_handle); + UNUSED(fdcan_handle); + return 0; +}*/ + +// #define USECAN1 1 +// #define TX_BUFFER_1_SIZE 10 +// #define USECAN2 1 +// #define TX_BUFFER_2_SIZE 10 + +// TODO: Make creating these callbacks a macro, rather than defining each one separately +static volatile uint32_t rx_2_received = 0; +static void can_test_rx_callback2(uint32_t id, void *data, uint32_t size) +{ + rx_2_received++; + LOGOMATIC("CAN2 Got data! Size %ld, data[0] = 0x%x, id %" PRIu32 "\n", size, *(char *)data, id); + // Is within an ISR, so needs to exit quickly + return; +} + +static volatile uint32_t rx_1_received = 0; +static void can_test_rx_callback1(uint32_t id, void *data, uint32_t size) +{ + rx_1_received++; + LOGOMATIC("CAN1 Got data! Size %ld, data[0] = 0x%x, id %" PRIu32 "\n", size, *(char *)data, id); + + // Is within an ISR, so needs to exit quickly + return; +} + +// 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?? +int can_internal_test(void) +{ + FDCAN_TxHeaderTypeDef TxHeader = { + .Identifier = 1, + + .IdType = FDCAN_STANDARD_ID, + .TxFrameType = FDCAN_DATA_FRAME, + .ErrorStateIndicator = FDCAN_ESI_ACTIVE, // honestly this might be a value you have to read from a node + // FDCAN_ESI_ACTIVE is just a state that assumes there are minimal errors + .DataLength = 1, + .BitRateSwitch = FDCAN_BRS_OFF, + .TxEventFifoControl = FDCAN_NO_TX_EVENTS, // change to FDCAN_STORE_TX_EVENTS if you need to store info regarding transmitted messages + .MessageMarker = 0 // also change this to a real address if you change fifo control + }; + + CANHandle *primary_can = 0, *data_can = 0; + primary_can = data_can = NULL; + CANConfig cfg1, cfg2; + + LOGOMATIC("Initializing primary and data CAN Bus in Normal mode.\n"); + if (get_cfg(FDCAN1, can_test_rx_callback1, &cfg1, FDCAN_MODE_INTERNAL_LOOPBACK)) { + LOGOMATIC("Could not get config for FDCAN1\n"); + return ERROR; + } + if (get_cfg(FDCAN2, can_test_rx_callback2, &cfg2, FDCAN_MODE_INTERNAL_LOOPBACK)) { + LOGOMATIC("Could not get config for FDCAN2\n"); + return ERROR; + } + + can_set_clksource(LL_RCC_FDCAN_CLKSOURCE_PCLK1); + + //============================================================================================= + if ((primary_can = can_init(&cfg1)) == NULL) { + LOGOMATIC("Could not initialize primary_can\n"); + return ERROR; + } + // HAL_FDCAN_ConfigGlobalFilter(primary_can->hal_fdcanP, 0, 0, 0, 0); + + if ((data_can = can_init(&cfg2)) == NULL) { + LOGOMATIC("Could not initialize data_can\n"); + return ERROR; + } + // HAL_FDCAN_ConfigGlobalFilter(data_can->hal_fdcanP, 0, 0, 0, 0); + + //============================================================================================= + if (can_start(primary_can)) { + LOGOMATIC("Could not start primary_can\n"); + return ERROR; + } + if (can_start(data_can)) { + LOGOMATIC("Could not start data_can\n"); + return ERROR; + } + + FDCANTxMessage msg = {0}; + msg.data[0] = 0x80; + msg.tx_header = TxHeader; + + uint32_t i = 0; + uint32_t num_messages = 5; + + LOGOMATIC("Sending %ld messages on each bus...\n", num_messages); + while (i < num_messages) { + HAL_Delay(100); + msg.data[0] = 0x2; + can_send(primary_can, &msg); + HAL_Delay(100); + msg.data[0] = 0x10; + can_send(data_can, &msg); + i += 1; + } + LOGOMATIC("Received %ld messages on bus1...\n", rx_1_received); + LOGOMATIC("Received %ld messages on bus2...\n", rx_2_received); + + uint32_t error = false; + + if ((rx_1_received != num_messages)) { + error = true; + LOGOMATIC("FAIL: can_internal_test: did not receive all rx1\n"); + } else { + LOGOMATIC("SUCCESS: can_internal_test: received all rx1\n"); + } + if ((rx_2_received != num_messages)) { + error = true; + LOGOMATIC("FAIL: can_internal_test: did not receive all rx2\n"); + } else { + LOGOMATIC("SUCCESS: can_internal_test: received all rx2\n"); + } + + if (primary_can->tx_elements > 0) { + LOGOMATIC("can_internal_test: FAIL: did not send all messages from tx_buffer\n"); + } + LOGOMATIC("\n"); + + uint32_t rc; + if ((rc = can_release(primary_can))) { + LOGOMATIC("FAIL: can_internal_test; could not release primary_can\n"); + } + error |= rc; + if ((rc = can_release(data_can))) { + LOGOMATIC("FAIL: can_internal_test; could not release data_can\n"); + } + error |= rc; + + if (error) { + return ERROR; + } + + LOGOMATIC("can_internal_test: SUCCESS\n"); + + return SUCCESS; +} diff --git a/Lib/Peripherals/CAN/Test/can_release_test.c b/Lib/Peripherals/CAN/Test/can_release_test.c new file mode 100644 index 000000000..0dcd710a0 --- /dev/null +++ b/Lib/Peripherals/CAN/Test/can_release_test.c @@ -0,0 +1,39 @@ +#include "can.h" +#include "can_tests.h" + +// #define USECAN1 +// #define TX_BUFFER_1_SIZE 10 + +// TODO: +int can_release_test() +{ + LOGOMATIC("running can_release_test\n"); + + CANConfig cfg; + if (get_cfg(FDCAN1, NULL, &cfg, FDCAN_MODE_NORMAL)) { + LOGOMATIC("Could not get config for FDCAN1\n"); + return ERROR; + } + + CANHandle *can = NULL; + + //============================================================================================= + if ((can = can_init(&cfg)) == NULL) { + LOGOMATIC("can_init: Could not initialize primary_can\n"); + return ERROR; + } + + FDCAN_HandleTypeDef *temp = can->hal_fdcanP; + if (can_release(can)) { + LOGOMATIC("can_release: Could not release can\n"); + return ERROR; + } + + // test state of canHandle after release + if (temp != can->hal_fdcanP) { + LOGOMATIC("can_release: cleared handle incorrectly\n"); + return ERROR; + } + + return SUCCESS; +} diff --git a/Lib/Peripherals/CAN/Test/can_stress_test.c b/Lib/Peripherals/CAN/Test/can_stress_test.c new file mode 100644 index 000000000..cf3715f5c --- /dev/null +++ b/Lib/Peripherals/CAN/Test/can_stress_test.c @@ -0,0 +1,116 @@ +#include + +#include "can.h" +#include "can_tests.h" + +// #define USECAN1 +// #define TX_BUFFER_1_SIZE 10 + +// TODO: +static volatile uint32_t can_stress_test_received = 0; +void can_stress_test_rx_callback(uint32_t id, void *data, uint32_t size) +{ + can_stress_test_received++; + UNUSED(id); + UNUSED(data); + UNUSED(size); + return; +} + +int can_stress_test(void) +{ + LOGOMATIC("running can_stress_test\n"); + uint32_t status, loop; + UNUSED(status); + + status = loop = 0; + + CANHandle *primary_can, *data_can; + primary_can = data_can = NULL; + CANConfig cfg1; + + if (get_cfg(FDCAN1, can_stress_test_rx_callback, &cfg1, FDCAN_MODE_INTERNAL_LOOPBACK)) { + LOGOMATIC("Could not get config for FDCAN1\n"); + return ERROR; + } + + can_set_clksource(LL_RCC_FDCAN_CLKSOURCE_PCLK1); + + FDCAN_TxHeaderTypeDef TxHeader = { + .Identifier = 1, + + .IdType = FDCAN_STANDARD_ID, + .TxFrameType = FDCAN_DATA_FRAME, + .ErrorStateIndicator = FDCAN_ESI_ACTIVE, // honestly this might be a value you have to read from a node + // FDCAN_ESI_ACTIVE is just a state that assumes there are minimal errors + .DataLength = 1, + .BitRateSwitch = FDCAN_BRS_OFF, + .TxEventFifoControl = FDCAN_NO_TX_EVENTS, // change to FDCAN_STORE_TX_EVENTS if you need to store info regarding transmitted messages + .MessageMarker = 0 // also change this to a real address if you change fifo control + }; + + if ((primary_can = can_init(&cfg1)) == NULL) { + LOGOMATIC("Could not initialize primary_can\n"); + return ERROR; + } + if (can_start(primary_can)) { + LOGOMATIC("Could not start primary_can\n"); + return ERROR; + } + + FDCANTxMessage msg; + memset(&(msg.data), 0, sizeof(msg.data)); + msg.data[0] = 0x80; + msg.tx_header = TxHeader; + + size_t i = 0; + size_t rounds = 5; + size_t messages = primary_can->tx_capacity * 2; + uint32_t successes = 0; + while (loop < rounds) { + loop++; + can_stress_test_received = 0; + i = 0; + while (i < messages) { + if (can_send(primary_can, &msg) != 0) { + LOGOMATIC("can_stress_test: FAIL: sending CAN msg at %u-th consecutive send.\n", (unsigned int)i + 1); + break; + } + i++; + } + LOGOMATIC("Sent %u/%u CAN msgs...\n", (unsigned int)i, (unsigned int)messages); + HAL_Delay(1000); + + LOGOMATIC("Received %u/%u CAN msgs after 1 second.\n", (unsigned int)can_stress_test_received, (unsigned int)messages); + + LOGOMATIC("finished loop %ld\n", loop); + + if (primary_can->tx_elements > 0) { + LOGOMATIC("can_stress_test: FAIL: did not send all messages from tx_buffer\n"); + continue; + } + LOGOMATIC("\n"); + + if (can_stress_test_received == messages) { + successes += 1; + } + // msg.data[0] = 0x10; + // can_send(data_can, &msg); + // HAL_Delay(1000); + } + + if (can_release(primary_can)) { + LOGOMATIC("can_stress_test: FAIL: could not release primary_can\n"); + return ERROR; + } + + // FINAL CHECK + LOGOMATIC("can_stress_test: succeeded %u/%u rounds\n", (unsigned int)successes, (unsigned int)rounds); + if (successes < rounds) { + LOGOMATIC("can_stress_test: FAIL\n"); + } else { + LOGOMATIC("can_stress_test: SUCCESS\n"); + } + + return SUCCESS; +} diff --git a/Lib/Peripherals/CAN/Test/can_tests.c b/Lib/Peripherals/CAN/Test/can_tests.c deleted file mode 100644 index 240d3300c..000000000 --- a/Lib/Peripherals/CAN/Test/can_tests.c +++ /dev/null @@ -1,171 +0,0 @@ -#include "can_tests.h" - -#include -#include - -#include "can.h" - -// each family has a constant number of CAN peripherals - -int can_test_instance(FDCAN_HandleTypeDef fdcan_handle) -{ - UNUSED(fdcan_handle); - return 0; -} - -void can_test_rx_callback2(uint32_t id, void *data, uint32_t size) -{ - LOGOMATIC("CAN2 Got data! Size %ld, data[0] = 0x%x, id %" PRIu32 "\n", size, *(char *)data, id); - // Is within an ISR, so needs to exit quickly - return; -} - -void can_test_rx_callback1(uint32_t id, void *data, uint32_t size) -{ - LOGOMATIC("CAN1 Got data! Size %ld, data[0] = 0x%x, id %" PRIu32 "\n", size, *(char *)data, id); - - // Is within an ISR, so needs to exit quickly - return; -} - -int can_test(void) -{ - - CANConfig canCfg; - // canCfg.fdcan_instance = FDCAN2; - - canCfg.hal_fdcan_init.ClockDivider = FDCAN_CLOCK_DIV1; - canCfg.hal_fdcan_init.FrameFormat = FDCAN_FRAME_FD_NO_BRS; - canCfg.hal_fdcan_init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; - canCfg.hal_fdcan_init.Mode = FDCAN_MODE_NORMAL; - canCfg.hal_fdcan_init.AutoRetransmission = ENABLE; - canCfg.hal_fdcan_init.TransmitPause = DISABLE; - canCfg.hal_fdcan_init.ProtocolException = ENABLE; - canCfg.hal_fdcan_init.NominalPrescaler = 1; - canCfg.hal_fdcan_init.NominalSyncJumpWidth = 16; - canCfg.hal_fdcan_init.NominalTimeSeg1 = 127; // Updated for 170MHz: (1+127+42)*1 = 170 ticks -> 1 Mbps - canCfg.hal_fdcan_init.NominalTimeSeg2 = 42; - canCfg.hal_fdcan_init.DataPrescaler = 8; - canCfg.hal_fdcan_init.DataSyncJumpWidth = 16; - canCfg.hal_fdcan_init.DataTimeSeg1 = 15; // Updated for 170MHz: (1+15+5)*8 = 168 ticks -> ~5 Mbps - canCfg.hal_fdcan_init.DataTimeSeg2 = 5; - canCfg.hal_fdcan_init.StdFiltersNbr = 1; - canCfg.hal_fdcan_init.ExtFiltersNbr = 0; - - canCfg.rx_callback = NULL; // PLEASE SET - canCfg.rx_interrupt_priority = 0; // PLEASE SET - canCfg.tx_interrupt_priority = 0; // PLEASE SET - canCfg.tx_buffer_length = 3; // PLEASE SET - - // canCfg.rx_gpio = GPIOB; - // canCfg.init_rx_gpio.Pin = GPIO_PIN_12; - canCfg.init_rx_gpio.Mode = GPIO_MODE_AF_PP; - canCfg.init_rx_gpio.Pull = GPIO_PULLUP; - canCfg.init_rx_gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - // canCfg.init_rx_gpio.Alternate = GPIO_AF9_FDCAN2; - - // canCfg.tx_gpio = GPIOB; - // canCfg.init_tx_gpio.Pin = GPIO_PIN_13; - canCfg.init_tx_gpio.Mode = GPIO_MODE_AF_PP; - canCfg.init_tx_gpio.Pull = GPIO_NOPULL; - canCfg.init_tx_gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - // canCfg.init_tx_gpio.Alternate = GPIO_AF9_FDCAN2; - - // Not testing filters at the moment - // FDCAN_FilterTypeDef filter; - - // can_add_filter(data_can, &filter); - /* USER CODE END 2 */ - - FDCAN_TxHeaderTypeDef TxHeader = { - .Identifier = 1, - - .IdType = FDCAN_STANDARD_ID, - .TxFrameType = FDCAN_DATA_FRAME, - .ErrorStateIndicator = FDCAN_ESI_ACTIVE, // honestly this might be a value you have to read from a node - // FDCAN_ESI_ACTIVE is just a state that assumes there are minimal errors - .DataLength = 1, - .BitRateSwitch = FDCAN_BRS_OFF, - .TxEventFifoControl = FDCAN_NO_TX_EVENTS, // change to FDCAN_STORE_TX_EVENTS if you need to store info regarding transmitted messages - .MessageMarker = 0 // also change this to a real address if you change fifo control - }; - - FDCANTxMessage msg; - msg.data[0] = 0x80; - memset(&(msg.data), 0, sizeof(msg.data)); - msg.tx_header = TxHeader; - - can_set_clksource(LL_RCC_FDCAN_CLKSOURCE_PCLK1); - -#ifdef FDCAN1 - - canCfg.fdcan_instance = FDCAN1; - canCfg.rx_gpio = GPIOA; - canCfg.init_rx_gpio.Pin = GPIO_PIN_11; - canCfg.init_rx_gpio.Alternate = GPIO_AF9_FDCAN1; - - canCfg.tx_gpio = GPIOA; - canCfg.init_tx_gpio.Pin = GPIO_PIN_12; - canCfg.init_tx_gpio.Alternate = GPIO_AF9_FDCAN1; - - canCfg.rx_callback = can_test_rx_callback1; // PLEASE SET - - CANHandle *primary_can = can_init(&canCfg); - HAL_FDCAN_ConfigGlobalFilter(primary_can->hal_fdcanP, 0, 0, 0, 0); - - can_start(primary_can); - -#endif -#ifdef FDCAN2 - - canCfg.fdcan_instance = FDCAN2; - canCfg.rx_gpio = GPIOB; - canCfg.init_rx_gpio.Pin = GPIO_PIN_12; - canCfg.init_rx_gpio.Alternate = GPIO_AF9_FDCAN2; - - canCfg.tx_gpio = GPIOB; - canCfg.init_tx_gpio.Pin = GPIO_PIN_13; - canCfg.init_tx_gpio.Alternate = GPIO_AF9_FDCAN2; - - canCfg.rx_callback = can_test_rx_callback2; - - // FDCAN_FilterTypeDef filter; - // filter.IdType = FDCAN_STANDARD_ID; - // filter.FilterIndex = 0; - // filter.FilterType = FDCAN_FILTER_RANGE, - // filter.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; - // filter.FilterID1 = 0x00; - // filter.FilterID2 = 0x02; - - CANHandle *data_can = can_init(&canCfg); - - // accept unmatched standard and extended frames into RXFIFO0 - default behaviour - HAL_FDCAN_ConfigGlobalFilter(data_can->hal_fdcanP, 0, 0, 0, 0); - - // not accepting filters - // can_add_filter(data_can, &filter); - - // API Testing - // can_init(&canCfg); - - can_start(data_can); - - can_send(data_can, &msg); - // can_release(data_can); - -#endif -#ifdef FDCAN3 - -#endif - - while (1) { - HAL_Delay(1000); - msg.data[0] = 0x2; - can_send(primary_can, &msg); - HAL_Delay(1000); - msg.data[0] = 0x10; - can_send(data_can, &msg); - } - - return 0; -} diff --git a/Lib/Peripherals/CAN/Test/can_tests.h b/Lib/Peripherals/CAN/Test/can_tests.h index 17985c045..e202c3079 100644 --- a/Lib/Peripherals/CAN/Test/can_tests.h +++ b/Lib/Peripherals/CAN/Test/can_tests.h @@ -6,14 +6,19 @@ #include "can_platform_deps.h" // Tested STM32 Families -#ifdef STM32G4 -// #elif defined(STM32L4) -// #elif defined(STM32U5) -#else -#error "Untested STM32 Family" -#endif +// #define USECAN1 +// #define TX_BUFFER_2_SIZE 10 +// #define USECAN2 +// #define TX_BUFFER_3_SIZE 10 + +// abstract families +extern int defaultSTM32G4_CANCfg(FDCAN_GlobalTypeDef *instance, CAN_RXCallback callback, CANConfig *out_cfg, uint32_t Mode); +extern int get_cfg(FDCAN_GlobalTypeDef *instance, CAN_RXCallback callback, CANConfig *out_cfg, uint32_t Mode); -// Assume the LOGOMATIC is setup correctly -int can_test(void); // top-level function, just call this and check if the return status is correct +// testing functions +extern int can_external_test(void); +extern int can_internal_test(void); +extern int can_stress_test(void); +extern int can_release_test(void); #endif diff --git a/Lib/Peripherals/CAN/common.cmake b/Lib/Peripherals/CAN/common.cmake index 120a4dcbe..12536205d 100644 --- a/Lib/Peripherals/CAN/common.cmake +++ b/Lib/Peripherals/CAN/common.cmake @@ -1,8 +1,18 @@ add_library(PERIPHERAL_CAN_LIB INTERFACE) -target_link_libraries(PERIPHERAL_CAN_LIB INTERFACE CircularBuffer_Lib) +target_link_libraries( + PERIPHERAL_CAN_LIB + INTERFACE + PERIPHERAL_CAN_TEST_LIB + CircularBuffer_Lib +) -target_sources(PERIPHERAL_CAN_LIB INTERFACE ${CMAKE_CURRENT_LIST_DIR}/Src/can.c) +target_sources( + PERIPHERAL_CAN_LIB + INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/Src/can.c + ${CMAKE_CURRENT_LIST_DIR}/Src/can_cfg_helpers.c +) # Make headers accessible as #include "Peripherals/CAN/can.h" target_include_directories( @@ -16,7 +26,12 @@ add_library(PERIPHERAL_CAN_TEST_LIB INTERFACE) target_sources( PERIPHERAL_CAN_TEST_LIB INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/Test/can_tests.c + ${CMAKE_CURRENT_LIST_DIR}/Test/can_external_test.c + ${CMAKE_CURRENT_LIST_DIR}/Test/can_internal_test.c + ${CMAKE_CURRENT_LIST_DIR}/Test/can_release_test.c + ${CMAKE_CURRENT_LIST_DIR}/Test/can_stress_test.c + #${CMAKE_CURRENT_LIST_DIR}/Test/can.c + #${CMAKE_CURRENT_LIST_DIR}/Test/can_tests.c ) target_include_directories( PERIPHERAL_CAN_TEST_LIB @@ -28,8 +43,8 @@ target_include_directories( #if(CMAKE_BUILD_TYPE STREQUAL "Test") # Initialization # add_executable( -# PERIPHERAL_CAN_LIB_init_test -# ${CMAKE_CURRENT_LIST_DIR}/Test/can_test_init.c +# PERIPHERAL_CAN_LIB_external_test +# ${CMAKE_CURRENT_LIST_DIR}/Test/can_external_test.c # ) # target_link_libraries( # PERIPHERAL_CAN_LIB_init_test