diff --git a/.gitignore b/.gitignore index 29c4d66d5..fbae0bc9a 100644 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,5 @@ EWARM .mxproject */Core/Src/system_stm32*xx.c */*/Core/Src/system_stm32*xx.c +*/Core/Inc/stm32_assert.h +*/*/Core/Inc/stm32_assert.h diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b5c60d558..5e67db949 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -9,6 +9,6 @@ "github.vscode-pull-request-github", "eamodio.gitlens", "davidanson.vscode-markdownlint", - "ms-vscode.vscode-serial-monitor" + "wombat.fancymon" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index fc890b664..d7ec434ff 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -32,6 +32,37 @@ ] } }, + { + "cwd": "${workspaceFolder}", + "executable": "${command:cmake.buildDirectory}/CUBEMXTESTING.elf", + "name": "CUBEMXTESTING", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32g4x.cfg" + ], + "searchDir": [], + "preLaunchTask": "CMake: configure and build CUBEMXTESTING", + "showDevDebugOutput": "raw", + "svdPath": "${workspaceFolder}/Lib/Vendor/CMSIS_5/SVD/STM32G474.svd", + "swoConfig": { + "enabled": true, + "cpuFrequency": 160000000, + "swoFrequency": 2000000, + "source": "probe", + "decoders": [ + { + "type": "console", + "label": "ITM", + "showOnStartup": true, + "port": 0, + "encoding": "ascii" + } + ] + } + }, { "cwd": "${workspaceFolder}", "executable": "${command:cmake.buildDirectory}/CCU.elf", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index bf2d6e115..dd3358969 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -38,6 +38,15 @@ ], "command": "cmake --build --preset ${command:cmake.activeBuildPresetName} --target ECU" }, + { + "label": "CMake: configure and build CUBEMXTESTING", + "type": "shell", + "dependsOrder": "sequence", + "dependsOn": [ + "CMake: configure" + ], + "command": "cmake --build --preset ${command:cmake.activeBuildPresetName} --target CUBEMXTESTING" + }, { "label": "CMake: configure and build CCU", "type": "shell", diff --git a/CMakeLists.txt b/CMakeLists.txt index dd6d899d4..46b377ddb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,6 +142,7 @@ include("Autogen/CAN/CANfigurator.cmake") include("${lib_path}/Peripherals/USART/common.cmake") include("${lib_path}/Peripherals/TimedCAN/common.cmake") include("${lib_path}/Peripherals/CAN/common.cmake") +include("${lib_path}/Peripherals/CubeCAN/cube_can.cmake") include("${lib_path}/FancyLayers-RENAME/GRCAN/grcan_fancylayer.cmake") include("${lib_path}/FancyLayers-RENAME/ADC/adc.cmake") @@ -161,6 +162,7 @@ add_gr_project(STM32G474xE G4PERTESTING) add_gr_project(STM32G474xE G4SPITESTING) add_gr_project(STM32G474xE G4ADCTESTING) add_gr_project(STM32G474xE G4NEOTESTING) +add_gr_project(STM32G474xE CUBEMXTESTING) # CAN Peripheral Testing add_gr_project(STM32G474xE G474xE_CAN_TESTING CAN_BASIC_TEST) diff --git a/CUBEMXTESTING/Application/Inc/CANdler.h b/CUBEMXTESTING/Application/Inc/CANdler.h new file mode 100644 index 000000000..5c4a98ab6 --- /dev/null +++ b/CUBEMXTESTING/Application/Inc/CANdler.h @@ -0,0 +1,8 @@ +#include "CubeCAN.h" + +#ifndef CANDLER_H +#define CANDLER_H + +void CANdler_Callback(const CubeCAN_Config_Context *const context, const CAN_Identifier *const identifier, const uint8_t *const data, const uint8_t size); + +#endif diff --git a/CUBEMXTESTING/Application/Inc/CubeCAN_Config.h b/CUBEMXTESTING/Application/Inc/CubeCAN_Config.h new file mode 100644 index 000000000..e8345d49a --- /dev/null +++ b/CUBEMXTESTING/Application/Inc/CubeCAN_Config.h @@ -0,0 +1,7 @@ +#ifndef CUBE_CAN_CONFIG_H +#define CUBE_CAN_CONFIG_H + +#define CUBEMX_CAN_TX_QUEUE_SIZE 16U +#define CUBEMX_CAN_MAX_INSTANCES 3U + +#endif diff --git a/CUBEMXTESTING/Application/Inc/Loop.h b/CUBEMXTESTING/Application/Inc/Loop.h new file mode 100644 index 000000000..4b447f829 --- /dev/null +++ b/CUBEMXTESTING/Application/Inc/Loop.h @@ -0,0 +1,6 @@ +#ifndef LOOP_H +#define LOOP_H + +void MainLoop(void); + +#endif diff --git a/CUBEMXTESTING/Application/Src/CANdler.c b/CUBEMXTESTING/Application/Src/CANdler.c new file mode 100644 index 000000000..8d16b7c36 --- /dev/null +++ b/CUBEMXTESTING/Application/Src/CANdler.c @@ -0,0 +1,27 @@ +#include "CANdler.h" + +#include "CubeCAN.h" +#include "GRCAN_BUS_ID.h" +#include "GRCAN_MSG_ID.h" +#include "GRCAN_NODE_ID.h" +#include "Logomatic.h" + +int value; + +void CANdler_Callback(const CubeCAN_Config_Context *const context, const CAN_Identifier *const identifier, const uint8_t *const data, const uint8_t size) +{ + value++; + + if (context == NULL || identifier == NULL || data == NULL || size == 0) { + LOGOMATIC("CANdler_Callback: Invalid parameters received. context: %u, identifier: %p, data: %p, size: %u\n", context->busid_user_context, (void *const)identifier, (void *const)data, + size); + return; + } + + const GRCAN_BUS_ID busid = context->busid_user_context; + const GRCAN_NODE_ID tx_node = identifier->tx_node_id; + const GRCAN_NODE_ID rx_node = identifier->rx_node_id; + const GRCAN_MSG_ID msg_id = identifier->msg_id; + + LOGOMATIC("Received on bus %d with node %d -> %d with message %d and size %u\t%.*s\n", busid, tx_node, rx_node, msg_id, size, size, (const char *const)data); +} diff --git a/CUBEMXTESTING/Application/Src/Loop.c b/CUBEMXTESTING/Application/Src/Loop.c new file mode 100644 index 000000000..f4125d3b5 --- /dev/null +++ b/CUBEMXTESTING/Application/Src/Loop.c @@ -0,0 +1,20 @@ +#include "Loop.h" + +#include + +#include "CubeCAN.h" +#include "GRCAN_MSG_ID.h" +#include "GRCAN_NODE_ID.h" +#include "Logomatic.h" +#include "main.h" + +extern int value; + +void MainLoop(void) +{ + LL_GPIO_TogglePin(USER_LED_GPIO_Port, USER_LED_Pin); + LOGOMATIC("Main Loop %d %" PRIu32 "\n", value, HAL_GetTick()); + (void)CubeCAN_Send(primaryHandle, GRCAN_Debugger, GRCAN_DEBUG_2_0, "Hello0", 6); + (void)CubeCAN_Send(dataHandle, GRCAN_Debugger, GRCAN_DEBUG_2_0, "Hello1", 6); + (void)CubeCAN_Send(chargerHandle, GRCAN_Debugger, GRCAN_DEBUG_2_0, "Hello2", 6); +} diff --git a/CUBEMXTESTING/CMakeLists.txt b/CUBEMXTESTING/CMakeLists.txt new file mode 100644 index 000000000..2f24cb5e5 --- /dev/null +++ b/CUBEMXTESTING/CMakeLists.txt @@ -0,0 +1,51 @@ +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 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 + Application/Src/CANdler.c + Application/Src/Loop.c + Core/Src/fdcan.c + Core/Src/gpio.c + Core/Src/main.c + Core/Src/stm32g4xx_it.c + Core/Src/tim.c + Core/Src/usart.c +) + +target_link_libraries( + ${PROJECT_NAME}_USER_CODE + INTERFACE + CUBE_CAN_LIB + LOGOMATIC_LIB +) + +target_include_directories( + ${PROJECT_NAME}_USER_CODE + INTERFACE + Core/Inc + Application/Inc +) diff --git a/CUBEMXTESTING/CUBEMXTESTING.ioc b/CUBEMXTESTING/CUBEMXTESTING.ioc new file mode 100644 index 000000000..fac4bcf68 --- /dev/null +++ b/CUBEMXTESTING/CUBEMXTESTING.ioc @@ -0,0 +1,224 @@ +#MicroXplorer Configuration settings - do not modify +CAD.formats=[] +CAD.pinconfig=Dual +CAD.provider= +FDCAN1.AutoRetransmission=ENABLE +FDCAN1.CalculateBaudRateNominal=1000000 +FDCAN1.CalculateTimeBitNominal=1000 +FDCAN1.CalculateTimeQuantumNominal=6.25 +FDCAN1.DataPrescaler=8 +FDCAN1.DataSyncJumpWidth=16 +FDCAN1.DataTimeSeg1=14 +FDCAN1.DataTimeSeg2=5 +FDCAN1.ExtFiltersNbr=2 +FDCAN1.IPParameters=CalculateTimeQuantumNominal,CalculateTimeBitNominal,CalculateBaudRateNominal,NominalSyncJumpWidth,AutoRetransmission,ProtocolException,DataPrescaler,DataSyncJumpWidth,DataTimeSeg1,DataTimeSeg2,ExtFiltersNbr,NominalPrescaler,NominalTimeSeg1,NominalTimeSeg2,Mode +FDCAN1.Mode=FDCAN_MODE_INTERNAL_LOOPBACK +FDCAN1.NominalPrescaler=1 +FDCAN1.NominalSyncJumpWidth=16 +FDCAN1.NominalTimeSeg1=119 +FDCAN1.NominalTimeSeg2=40 +FDCAN1.ProtocolException=ENABLE +FDCAN2.AutoRetransmission=ENABLE +FDCAN2.CalculateBaudRateNominal=1000000 +FDCAN2.CalculateTimeBitNominal=1000 +FDCAN2.CalculateTimeQuantumNominal=6.25 +FDCAN2.DataPrescaler=8 +FDCAN2.DataSyncJumpWidth=16 +FDCAN2.DataTimeSeg1=14 +FDCAN2.DataTimeSeg2=5 +FDCAN2.ExtFiltersNbr=2 +FDCAN2.IPParameters=CalculateTimeQuantumNominal,CalculateTimeBitNominal,CalculateBaudRateNominal,AutoRetransmission,ProtocolException,NominalSyncJumpWidth,DataPrescaler,DataSyncJumpWidth,DataTimeSeg1,DataTimeSeg2,ExtFiltersNbr,NominalPrescaler,NominalTimeSeg1,NominalTimeSeg2,Mode +FDCAN2.Mode=FDCAN_MODE_INTERNAL_LOOPBACK +FDCAN2.NominalPrescaler=1 +FDCAN2.NominalSyncJumpWidth=16 +FDCAN2.NominalTimeSeg1=119 +FDCAN2.NominalTimeSeg2=40 +FDCAN2.ProtocolException=ENABLE +FDCAN3.CalculateBaudRateNominal=3333333 +FDCAN3.CalculateTimeBitNominal=300 +FDCAN3.CalculateTimeQuantumNominal=100.0 +FDCAN3.IPParameters=CalculateTimeQuantumNominal,CalculateTimeBitNominal,CalculateBaudRateNominal,Mode +FDCAN3.Mode=FDCAN_MODE_INTERNAL_LOOPBACK +File.Version=6 +GPIO.groupedBy=Group By Peripherals +KeepUserPlacement=false +LPUART1.BaudRate=115200 +LPUART1.IPParameters=BaudRate +Mcu.CPN=STM32G474RET6 +Mcu.Family=STM32G4 +Mcu.IP0=FDCAN1 +Mcu.IP1=FDCAN2 +Mcu.IP2=FDCAN3 +Mcu.IP3=LPUART1 +Mcu.IP4=NVIC +Mcu.IP5=RCC +Mcu.IP6=SYS +Mcu.IP7=TIM5 +Mcu.IPNb=8 +Mcu.Name=STM32G474R(B-C-E)Tx +Mcu.Package=LQFP64 +Mcu.Pin0=PC13 +Mcu.Pin1=PC0 +Mcu.Pin10=PB3 +Mcu.Pin11=PB8-BOOT0 +Mcu.Pin12=PB9 +Mcu.Pin13=VP_SYS_VS_Systick +Mcu.Pin14=VP_SYS_VS_DBSignals +Mcu.Pin15=VP_TIM5_VS_ClockSourceINT +Mcu.Pin2=PC1 +Mcu.Pin3=PA5 +Mcu.Pin4=PB12 +Mcu.Pin5=PB13 +Mcu.Pin6=PA8 +Mcu.Pin7=PA13 +Mcu.Pin8=PA14 +Mcu.Pin9=PA15 +Mcu.PinsNb=16 +Mcu.ThirdPartyNb=0 +Mcu.UserConstants= +Mcu.UserName=STM32G474RETx +MxCube.Version=6.18.1 +MxDb.Version=DB.6.0.181 +NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.FDCAN1_IT0_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true +NVIC.FDCAN2_IT0_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true +NVIC.FDCAN3_IT0_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true +NVIC.ForceEnableDMAVector=false +NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4 +NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:true\:false\:true\:false +NVIC.TIM5_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true +NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +PA13.Mode=Trace_Asynchronous_SW +PA13.Signal=SYS_JTMS-SWDIO +PA14.Mode=Trace_Asynchronous_SW +PA14.Signal=SYS_JTCK-SWCLK +PA15.Mode=FDCAN_Activate +PA15.Signal=FDCAN3_TX +PA5.GPIOParameters=GPIO_Label +PA5.GPIO_Label=USER_LED +PA5.Locked=true +PA5.Signal=GPIO_Output +PA8.Mode=FDCAN_Activate +PA8.Signal=FDCAN3_RX +PB12.Mode=FDCAN_Activate +PB12.Signal=FDCAN2_RX +PB13.Mode=FDCAN_Activate +PB13.Signal=FDCAN2_TX +PB3.Mode=Trace_Asynchronous_SW +PB3.Signal=SYS_JTDO-SWO +PB8-BOOT0.Mode=FDCAN_Activate +PB8-BOOT0.Signal=FDCAN1_RX +PB9.Mode=FDCAN_Activate +PB9.Signal=FDCAN1_TX +PC0.Mode=Asynchronous +PC0.Signal=LPUART1_RX +PC1.Mode=Asynchronous +PC1.Signal=LPUART1_TX +PC13.GPIOParameters=GPIO_Label +PC13.GPIO_Label=USER_BUTTON +PC13.Locked=true +PC13.Signal=GPIO_Input +PinOutPanel.RotationAngle=0 +ProjectManager.AskForMigrate=true +ProjectManager.BackupPrevious=false +ProjectManager.CompilerLinker=GCC +ProjectManager.CompilerOptimize=6 +ProjectManager.ComputerToolchain=false +ProjectManager.CoupleFile=true +ProjectManager.CustomerFirmwarePackage= +ProjectManager.DefaultFWLocation=true +ProjectManager.DeletePrevious=true +ProjectManager.DeviceId=STM32G474RETx +ProjectManager.FirmwarePackage=STM32Cube FW_G4 V1.6.3 +ProjectManager.FreePins=true +ProjectManager.FreePinsContext= +ProjectManager.HalAssertFull=true +ProjectManager.HeapSize=0x200 +ProjectManager.KeepUserCode=true +ProjectManager.LastFirmware=true +ProjectManager.LibraryCopy=2 +ProjectManager.MainLocation=Core/Src +ProjectManager.NoMain=false +ProjectManager.PreviousToolchain= +ProjectManager.ProjectBuild=false +ProjectManager.ProjectFileName=CUBEMXTESTING.ioc +ProjectManager.ProjectName=CUBEMXTESTING +ProjectManager.ProjectStructure= +ProjectManager.RegisterCallBack= +ProjectManager.StackSize=0x400 +ProjectManager.TargetToolchain=EWARM V8.50 +ProjectManager.TemplateDestinationPath=C\:\\Users\\maxnm\\Firmware\\CUBEMXTESTING +ProjectManager.TemplateSourcePath=C\:\\Users\\maxnm\\Firmware\\Lib\\CubeMXTemplates +ProjectManager.TemplatesList=\\Autogen\\Inc\\can_cfg_h.ftl, +ProjectManager.ToolChainLocation= +ProjectManager.UAScriptAfterPath= +ProjectManager.UAScriptBeforePath= +ProjectManager.UnderRoot=false +ProjectManager.UseDefaultDestinationPath=true +ProjectManager.UseDefaultSourcePath=true +ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-LL-false,2-MX_GPIO_Init-GPIO-false-LL-true,3-MX_FDCAN1_Init-FDCAN1-false-HAL-true,4-MX_FDCAN2_Init-FDCAN2-false-HAL-true,5-MX_TIM5_Init-TIM5-false-HAL-true,6-MX_FDCAN3_Init-FDCAN3-false-HAL-true,7-MX_LPUART1_UART_Init-LPUART1-false-HAL-true +RCC.ADC12Freq_Value=160000000 +RCC.ADC345Freq_Value=160000000 +RCC.AHBFreq_Value=160000000 +RCC.APB1Freq_Value=160000000 +RCC.APB1TimFreq_Value=160000000 +RCC.APB2Freq_Value=160000000 +RCC.APB2TimFreq_Value=160000000 +RCC.CRSFreq_Value=48000000 +RCC.CortexFreq_Value=160000000 +RCC.EXTERNAL_CLOCK_VALUE=12288000 +RCC.FCLKCortexFreq_Value=160000000 +RCC.FDCANFreq_Value=160000000 +RCC.FamilyName=M +RCC.HCLKFreq_Value=160000000 +RCC.HRTIM1Freq_Value=160000000 +RCC.HSE_VALUE=16000000 +RCC.HSI48_VALUE=48000000 +RCC.HSI_VALUE=16000000 +RCC.I2C1Freq_Value=160000000 +RCC.I2C2Freq_Value=160000000 +RCC.I2C3Freq_Value=160000000 +RCC.I2C4Freq_Value=160000000 +RCC.I2SFreq_Value=160000000 +RCC.IPParameters=ADC12Freq_Value,ADC345Freq_Value,AHBFreq_Value,APB1Freq_Value,APB1TimFreq_Value,APB2Freq_Value,APB2TimFreq_Value,CRSFreq_Value,CortexFreq_Value,EXTERNAL_CLOCK_VALUE,FCLKCortexFreq_Value,FDCANFreq_Value,FamilyName,HCLKFreq_Value,HRTIM1Freq_Value,HSE_VALUE,HSI48_VALUE,HSI_VALUE,I2C1Freq_Value,I2C2Freq_Value,I2C3Freq_Value,I2C4Freq_Value,I2SFreq_Value,LPTIM1Freq_Value,LPUART1Freq_Value,LSCOPinFreq_Value,LSE_VALUE,LSI_VALUE,MCO1PinFreq_Value,PLLN,PLLPoutputFreq_Value,PLLQoutputFreq_Value,PLLRCLKFreq_Value,PWRFreq_Value,QSPIFreq_Value,RNGFreq_Value,SAI1Freq_Value,SYSCLKFreq_VALUE,SYSCLKSource,UART4Freq_Value,UART5Freq_Value,USART1Freq_Value,USART2Freq_Value,USART3Freq_Value,USBFreq_Value,VCOInputFreq_Value,VCOOutputFreq_Value +RCC.LPTIM1Freq_Value=160000000 +RCC.LPUART1Freq_Value=160000000 +RCC.LSCOPinFreq_Value=32000 +RCC.LSE_VALUE=32768 +RCC.LSI_VALUE=32000 +RCC.MCO1PinFreq_Value=16000000 +RCC.PLLN=20 +RCC.PLLPoutputFreq_Value=160000000 +RCC.PLLQoutputFreq_Value=160000000 +RCC.PLLRCLKFreq_Value=160000000 +RCC.PWRFreq_Value=160000000 +RCC.QSPIFreq_Value=160000000 +RCC.RNGFreq_Value=160000000 +RCC.SAI1Freq_Value=160000000 +RCC.SYSCLKFreq_VALUE=160000000 +RCC.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK +RCC.UART4Freq_Value=160000000 +RCC.UART5Freq_Value=160000000 +RCC.USART1Freq_Value=160000000 +RCC.USART2Freq_Value=160000000 +RCC.USART3Freq_Value=160000000 +RCC.USBFreq_Value=160000000 +RCC.VCOInputFreq_Value=16000000 +RCC.VCOOutputFreq_Value=320000000 +TIM5.Dithering=Disable +TIM5.IPParameters=Prescaler,PeriodNoDither,Dithering +TIM5.PeriodNoDither=49 +TIM5.Prescaler=1599 +VP_SYS_VS_DBSignals.Mode=DisableDeadBatterySignals +VP_SYS_VS_DBSignals.Signal=SYS_VS_DBSignals +VP_SYS_VS_Systick.Mode=SysTick +VP_SYS_VS_Systick.Signal=SYS_VS_Systick +VP_TIM5_VS_ClockSourceINT.Mode=Internal +VP_TIM5_VS_ClockSourceINT.Signal=TIM5_VS_ClockSourceINT +board=custom diff --git a/CUBEMXTESTING/Core/Inc/fdcan.h b/CUBEMXTESTING/Core/Inc/fdcan.h new file mode 100644 index 000000000..3597c6476 --- /dev/null +++ b/CUBEMXTESTING/Core/Inc/fdcan.h @@ -0,0 +1,57 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file fdcan.h + * @brief This file contains all the function prototypes for + * the fdcan.c file + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 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 */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __FDCAN_H__ +#define __FDCAN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "main.h" + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +extern FDCAN_HandleTypeDef hfdcan1; + +extern FDCAN_HandleTypeDef hfdcan2; + +extern FDCAN_HandleTypeDef hfdcan3; + +/* USER CODE BEGIN Private defines */ + +/* USER CODE END Private defines */ + +void MX_FDCAN1_Init(void); +void MX_FDCAN2_Init(void); +void MX_FDCAN3_Init(void); + +/* USER CODE BEGIN Prototypes */ + +/* USER CODE END Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif /* __FDCAN_H__ */ diff --git a/CUBEMXTESTING/Core/Inc/gpio.h b/CUBEMXTESTING/Core/Inc/gpio.h new file mode 100644 index 000000000..37dcc0a5c --- /dev/null +++ b/CUBEMXTESTING/Core/Inc/gpio.h @@ -0,0 +1,48 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file gpio.h + * @brief This file contains all the function prototypes for + * the gpio.c file + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 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 */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __GPIO_H__ +#define __GPIO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "main.h" + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* USER CODE BEGIN Private defines */ + +/* USER CODE END Private defines */ + +void MX_GPIO_Init(void); + +/* USER CODE BEGIN Prototypes */ + +/* USER CODE END Prototypes */ + +#ifdef __cplusplus +} +#endif +#endif /*__ GPIO_H__ */ diff --git a/CUBEMXTESTING/Core/Inc/main.h b/CUBEMXTESTING/Core/Inc/main.h new file mode 100644 index 000000000..cbeced83f --- /dev/null +++ b/CUBEMXTESTING/Core/Inc/main.h @@ -0,0 +1,88 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : main.h + * @brief : Header for main.c file. + * This file contains the common defines of the application. + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 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 */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __MAIN_H +#define __MAIN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32g4xx_hal.h" +#include "stm32g4xx_ll_bus.h" +#include "stm32g4xx_ll_cortex.h" +#include "stm32g4xx_ll_crs.h" +#include "stm32g4xx_ll_dma.h" +#include "stm32g4xx_ll_exti.h" +#include "stm32g4xx_ll_gpio.h" +#include "stm32g4xx_ll_pwr.h" +#include "stm32g4xx_ll_rcc.h" +#include "stm32g4xx_ll_system.h" +#include "stm32g4xx_ll_utils.h" + +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ +#include "CubeCAN.h" +#include "stm32g4xx_ll_lpuart.h" +#include "stm32g4xx_ll_rcc.h" +#include "stm32g4xx_ll_usart.h" +/* USER CODE END Includes */ + +/* Exported types ------------------------------------------------------------*/ +/* USER CODE BEGIN ET */ +extern CubeCAN_Handle *primaryHandle; +extern CubeCAN_Handle *dataHandle; +extern CubeCAN_Handle *chargerHandle; +/* USER CODE END ET */ + +/* Exported constants --------------------------------------------------------*/ +/* USER CODE BEGIN EC */ + +/* USER CODE END EC */ + +/* Exported macro ------------------------------------------------------------*/ +/* USER CODE BEGIN EM */ + +/* USER CODE END EM */ + +/* Exported functions prototypes ---------------------------------------------*/ +void Error_Handler(void); + +/* USER CODE BEGIN EFP */ + +/* USER CODE END EFP */ + +/* Private defines -----------------------------------------------------------*/ +#define USER_BUTTON_Pin LL_GPIO_PIN_13 +#define USER_BUTTON_GPIO_Port GPIOC +#define USER_LED_Pin LL_GPIO_PIN_5 +#define USER_LED_GPIO_Port GPIOA + +/* USER CODE BEGIN Private defines */ + +/* USER CODE END Private defines */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MAIN_H */ diff --git a/CUBEMXTESTING/Core/Inc/stm32g4xx_hal_conf.h b/CUBEMXTESTING/Core/Inc/stm32g4xx_hal_conf.h new file mode 100644 index 000000000..d12d585bd --- /dev/null +++ b/CUBEMXTESTING/Core/Inc/stm32g4xx_hal_conf.h @@ -0,0 +1,381 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file stm32g4xx_hal_conf.h + * @author MCD Application Team + * @brief HAL configuration file + ****************************************************************************** + * @attention + * + * Copyright (c) 2019 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 */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef STM32G4xx_HAL_CONF_H +#define STM32G4xx_HAL_CONF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* ########################## Module Selection ############################## */ +/** + * @brief This is the list of modules to be used in the HAL driver + */ + +#define HAL_MODULE_ENABLED + +/*#define HAL_ADC_MODULE_ENABLED */ +/*#define HAL_COMP_MODULE_ENABLED */ +/*#define HAL_CORDIC_MODULE_ENABLED */ +/*#define HAL_CRC_MODULE_ENABLED */ +/*#define HAL_CRYP_MODULE_ENABLED */ +/*#define HAL_DAC_MODULE_ENABLED */ +#define HAL_FDCAN_MODULE_ENABLED +/*#define HAL_FMAC_MODULE_ENABLED */ +/*#define HAL_HRTIM_MODULE_ENABLED */ +/*#define HAL_IRDA_MODULE_ENABLED */ +/*#define HAL_IWDG_MODULE_ENABLED */ +/*#define HAL_I2C_MODULE_ENABLED */ +/*#define HAL_I2S_MODULE_ENABLED */ +/*#define HAL_LPTIM_MODULE_ENABLED */ +/*#define HAL_NAND_MODULE_ENABLED */ +/*#define HAL_NOR_MODULE_ENABLED */ +/*#define HAL_OPAMP_MODULE_ENABLED */ +/*#define HAL_PCD_MODULE_ENABLED */ +/*#define HAL_QSPI_MODULE_ENABLED */ +/*#define HAL_RNG_MODULE_ENABLED */ +/*#define HAL_RTC_MODULE_ENABLED */ +/*#define HAL_SAI_MODULE_ENABLED */ +/*#define HAL_SMARTCARD_MODULE_ENABLED */ +/*#define HAL_SMBUS_MODULE_ENABLED */ +/*#define HAL_SPI_MODULE_ENABLED */ +/*#define HAL_SRAM_MODULE_ENABLED */ +#define HAL_TIM_MODULE_ENABLED +#define HAL_UART_MODULE_ENABLED +/*#define HAL_USART_MODULE_ENABLED */ +/*#define HAL_WWDG_MODULE_ENABLED */ +#define HAL_GPIO_MODULE_ENABLED +#define HAL_EXTI_MODULE_ENABLED +#define HAL_DMA_MODULE_ENABLED +#define HAL_RCC_MODULE_ENABLED +#define HAL_FLASH_MODULE_ENABLED +#define HAL_PWR_MODULE_ENABLED +#define HAL_CORTEX_MODULE_ENABLED + +/* ########################## Register Callbacks selection ############################## */ +/** + * @brief This is the list of modules where register callback can be used + */ +#define USE_HAL_ADC_REGISTER_CALLBACKS 0U +#define USE_HAL_COMP_REGISTER_CALLBACKS 0U +#define USE_HAL_CORDIC_REGISTER_CALLBACKS 0U +#define USE_HAL_CRYP_REGISTER_CALLBACKS 0U +#define USE_HAL_DAC_REGISTER_CALLBACKS 0U +#define USE_HAL_EXTI_REGISTER_CALLBACKS 0U +#define USE_HAL_FDCAN_REGISTER_CALLBACKS 0U +#define USE_HAL_FMAC_REGISTER_CALLBACKS 0U +#define USE_HAL_HRTIM_REGISTER_CALLBACKS 0U +#define USE_HAL_I2C_REGISTER_CALLBACKS 0U +#define USE_HAL_I2S_REGISTER_CALLBACKS 0U +#define USE_HAL_IRDA_REGISTER_CALLBACKS 0U +#define USE_HAL_LPTIM_REGISTER_CALLBACKS 0U +#define USE_HAL_NAND_REGISTER_CALLBACKS 0U +#define USE_HAL_NOR_REGISTER_CALLBACKS 0U +#define USE_HAL_OPAMP_REGISTER_CALLBACKS 0U +#define USE_HAL_PCD_REGISTER_CALLBACKS 0U +#define USE_HAL_QSPI_REGISTER_CALLBACKS 0U +#define USE_HAL_RNG_REGISTER_CALLBACKS 0U +#define USE_HAL_RTC_REGISTER_CALLBACKS 0U +#define USE_HAL_SAI_REGISTER_CALLBACKS 0U +#define USE_HAL_SMARTCARD_REGISTER_CALLBACKS 0U +#define USE_HAL_SMBUS_REGISTER_CALLBACKS 0U +#define USE_HAL_SPI_REGISTER_CALLBACKS 0U +#define USE_HAL_SRAM_REGISTER_CALLBACKS 0U +#define USE_HAL_TIM_REGISTER_CALLBACKS 0U +#define USE_HAL_UART_REGISTER_CALLBACKS 0U +#define USE_HAL_USART_REGISTER_CALLBACKS 0U +#define USE_HAL_WWDG_REGISTER_CALLBACKS 0U + +/* ########################## Oscillator Values adaptation ####################*/ +/** + * @brief Adjust the value of External High Speed oscillator (HSE) used in your application. + * This value is used by the RCC HAL module to compute the system frequency + * (when HSE is used as system clock source, directly or through the PLL). + */ +#if !defined(HSE_VALUE) +#define HSE_VALUE (16000000UL) /*!< Value of the External oscillator in Hz */ +#endif /* HSE_VALUE */ + +#if !defined(HSE_STARTUP_TIMEOUT) +#define HSE_STARTUP_TIMEOUT (100UL) /*!< Time out for HSE start up, in ms */ +#endif /* HSE_STARTUP_TIMEOUT */ + +/** + * @brief Internal High Speed oscillator (HSI) value. + * This value is used by the RCC HAL module to compute the system frequency + * (when HSI is used as system clock source, directly or through the PLL). + */ +#if !defined(HSI_VALUE) +#define HSI_VALUE (16000000UL) /*!< Value of the Internal oscillator in Hz*/ +#endif /* HSI_VALUE */ + +/** + * @brief Internal High Speed oscillator (HSI48) value for USB FS and RNG. + * This internal oscillator is mainly dedicated to provide a high precision clock to + * the USB peripheral by means of a special Clock Recovery System (CRS) circuitry. + * When the CRS is not used, the HSI48 RC oscillator runs on it default frequency + * which is subject to manufacturing process variations. + */ +#if !defined(HSI48_VALUE) +#define HSI48_VALUE \ + (48000000UL) /*!< Value of the Internal High Speed oscillator for USB FS/RNG in Hz. \ + The real value my vary depending on manufacturing process variations.*/ +#endif /* HSI48_VALUE */ + +/** + * @brief Internal Low Speed oscillator (LSI) value. + */ +#if !defined(LSI_VALUE) +/*!< Value of the Internal Low Speed oscillator in Hz +The real value may vary depending on the variations in voltage and temperature.*/ +#define LSI_VALUE (32000UL) /*!< LSI Typical Value in Hz*/ +#endif /* LSI_VALUE */ +/** + * @brief External Low Speed oscillator (LSE) value. + * This value is used by the UART, RTC HAL module to compute the system frequency + */ +#if !defined(LSE_VALUE) +#define LSE_VALUE (32768UL) /*!< Value of the External Low Speed oscillator in Hz */ +#endif /* LSE_VALUE */ + +#if !defined(LSE_STARTUP_TIMEOUT) +#define LSE_STARTUP_TIMEOUT (5000UL) /*!< Time out for LSE start up, in ms */ +#endif /* LSE_STARTUP_TIMEOUT */ + +/** + * @brief External clock source for I2S and SAI peripherals + * This value is used by the I2S and SAI HAL modules to compute the I2S and SAI clock source + * frequency, this source is inserted directly through I2S_CKIN pad. + */ +#if !defined(EXTERNAL_CLOCK_VALUE) +#define EXTERNAL_CLOCK_VALUE (12288000UL) /*!< Value of the External oscillator in Hz*/ +#endif /* EXTERNAL_CLOCK_VALUE */ + +/* Tip: To avoid modifying this file each time you need to use different HSE, + === you can define the HSE value in your toolchain compiler preprocessor. */ + +/* ########################### System Configuration ######################### */ +/** + * @brief This is the HAL system configuration section + */ + +#define VDD_VALUE (3300UL) /*!< Value of VDD in mv */ +#define TICK_INT_PRIORITY (15UL) /*!< tick interrupt priority (lowest by default) */ +#define USE_RTOS 0U +#define PREFETCH_ENABLE 0U +#define INSTRUCTION_CACHE_ENABLE 1U +#define DATA_CACHE_ENABLE 1U + +/* ########################## Assert Selection ############################## */ +/** + * @brief Uncomment the line below to expanse the "assert_param" macro in the + * HAL drivers code + */ +#define USE_FULL_ASSERT 1U + +/* ################## SPI peripheral configuration ########################## */ + +/* CRC FEATURE: Use to activate CRC feature inside HAL SPI Driver + * Activated: CRC code is present inside driver + * Deactivated: CRC code cleaned from driver + */ + +#define USE_SPI_CRC 0U + +/* Includes ------------------------------------------------------------------*/ +/** + * @brief Include module's header file + */ + +#ifdef HAL_RCC_MODULE_ENABLED +#include "stm32g4xx_hal_rcc.h" +#endif /* HAL_RCC_MODULE_ENABLED */ + +#ifdef HAL_GPIO_MODULE_ENABLED +#include "stm32g4xx_hal_gpio.h" +#endif /* HAL_GPIO_MODULE_ENABLED */ + +#ifdef HAL_DMA_MODULE_ENABLED +#include "stm32g4xx_hal_dma.h" +#endif /* HAL_DMA_MODULE_ENABLED */ + +#ifdef HAL_CORTEX_MODULE_ENABLED +#include "stm32g4xx_hal_cortex.h" +#endif /* HAL_CORTEX_MODULE_ENABLED */ + +#ifdef HAL_ADC_MODULE_ENABLED +#include "stm32g4xx_hal_adc.h" +#endif /* HAL_ADC_MODULE_ENABLED */ + +#ifdef HAL_COMP_MODULE_ENABLED +#include "stm32g4xx_hal_comp.h" +#endif /* HAL_COMP_MODULE_ENABLED */ + +#ifdef HAL_CORDIC_MODULE_ENABLED +#include "stm32g4xx_hal_cordic.h" +#endif /* HAL_CORDIC_MODULE_ENABLED */ + +#ifdef HAL_CRC_MODULE_ENABLED +#include "stm32g4xx_hal_crc.h" +#endif /* HAL_CRC_MODULE_ENABLED */ + +#ifdef HAL_CRYP_MODULE_ENABLED +#include "stm32g4xx_hal_cryp.h" +#endif /* HAL_CRYP_MODULE_ENABLED */ + +#ifdef HAL_DAC_MODULE_ENABLED +#include "stm32g4xx_hal_dac.h" +#endif /* HAL_DAC_MODULE_ENABLED */ + +#ifdef HAL_EXTI_MODULE_ENABLED +#include "stm32g4xx_hal_exti.h" +#endif /* HAL_EXTI_MODULE_ENABLED */ + +#ifdef HAL_FDCAN_MODULE_ENABLED +#include "stm32g4xx_hal_fdcan.h" +#endif /* HAL_FDCAN_MODULE_ENABLED */ + +#ifdef HAL_FLASH_MODULE_ENABLED +#include "stm32g4xx_hal_flash.h" +#endif /* HAL_FLASH_MODULE_ENABLED */ + +#ifdef HAL_FMAC_MODULE_ENABLED +#include "stm32g4xx_hal_fmac.h" +#endif /* HAL_FMAC_MODULE_ENABLED */ + +#ifdef HAL_HRTIM_MODULE_ENABLED +#include "stm32g4xx_hal_hrtim.h" +#endif /* HAL_HRTIM_MODULE_ENABLED */ + +#ifdef HAL_IRDA_MODULE_ENABLED +#include "stm32g4xx_hal_irda.h" +#endif /* HAL_IRDA_MODULE_ENABLED */ + +#ifdef HAL_IWDG_MODULE_ENABLED +#include "stm32g4xx_hal_iwdg.h" +#endif /* HAL_IWDG_MODULE_ENABLED */ + +#ifdef HAL_I2C_MODULE_ENABLED +#include "stm32g4xx_hal_i2c.h" +#endif /* HAL_I2C_MODULE_ENABLED */ + +#ifdef HAL_I2S_MODULE_ENABLED +#include "stm32g4xx_hal_i2s.h" +#endif /* HAL_I2S_MODULE_ENABLED */ + +#ifdef HAL_LPTIM_MODULE_ENABLED +#include "stm32g4xx_hal_lptim.h" +#endif /* HAL_LPTIM_MODULE_ENABLED */ + +#ifdef HAL_NAND_MODULE_ENABLED +#include "stm32g4xx_hal_nand.h" +#endif /* HAL_NAND_MODULE_ENABLED */ + +#ifdef HAL_NOR_MODULE_ENABLED +#include "stm32g4xx_hal_nor.h" +#endif /* HAL_NOR_MODULE_ENABLED */ + +#ifdef HAL_OPAMP_MODULE_ENABLED +#include "stm32g4xx_hal_opamp.h" +#endif /* HAL_OPAMP_MODULE_ENABLED */ + +#ifdef HAL_PCD_MODULE_ENABLED +#include "stm32g4xx_hal_pcd.h" +#endif /* HAL_PCD_MODULE_ENABLED */ + +#ifdef HAL_PWR_MODULE_ENABLED +#include "stm32g4xx_hal_pwr.h" +#endif /* HAL_PWR_MODULE_ENABLED */ + +#ifdef HAL_QSPI_MODULE_ENABLED +#include "stm32g4xx_hal_qspi.h" +#endif /* HAL_QSPI_MODULE_ENABLED */ + +#ifdef HAL_RNG_MODULE_ENABLED +#include "stm32g4xx_hal_rng.h" +#endif /* HAL_RNG_MODULE_ENABLED */ + +#ifdef HAL_RTC_MODULE_ENABLED +#include "stm32g4xx_hal_rtc.h" +#endif /* HAL_RTC_MODULE_ENABLED */ + +#ifdef HAL_SAI_MODULE_ENABLED +#include "stm32g4xx_hal_sai.h" +#endif /* HAL_SAI_MODULE_ENABLED */ + +#ifdef HAL_SMARTCARD_MODULE_ENABLED +#include "stm32g4xx_hal_smartcard.h" +#endif /* HAL_SMARTCARD_MODULE_ENABLED */ + +#ifdef HAL_SMBUS_MODULE_ENABLED +#include "stm32g4xx_hal_smbus.h" +#endif /* HAL_SMBUS_MODULE_ENABLED */ + +#ifdef HAL_SPI_MODULE_ENABLED +#include "stm32g4xx_hal_spi.h" +#endif /* HAL_SPI_MODULE_ENABLED */ + +#ifdef HAL_SRAM_MODULE_ENABLED +#include "stm32g4xx_hal_sram.h" +#endif /* HAL_SRAM_MODULE_ENABLED */ + +#ifdef HAL_TIM_MODULE_ENABLED +#include "stm32g4xx_hal_tim.h" +#endif /* HAL_TIM_MODULE_ENABLED */ + +#ifdef HAL_UART_MODULE_ENABLED +#include "stm32g4xx_hal_uart.h" +#endif /* HAL_UART_MODULE_ENABLED */ + +#ifdef HAL_USART_MODULE_ENABLED +#include "stm32g4xx_hal_usart.h" +#endif /* HAL_USART_MODULE_ENABLED */ + +#ifdef HAL_WWDG_MODULE_ENABLED +#include "stm32g4xx_hal_wwdg.h" +#endif /* HAL_WWDG_MODULE_ENABLED */ + +/* Exported macro ------------------------------------------------------------*/ +#ifdef USE_FULL_ASSERT +/** + * @brief The assert_param macro is used for function's parameters check. + * @param expr: If expr is false, it calls assert_failed function + * which reports the name of the source file and the source + * line number of the call that failed. + * If expr is true, it returns no value. + * @retval None + */ +#define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__)) +/* Exported functions ------------------------------------------------------- */ +void assert_failed(uint8_t *file, uint32_t line); +#else +#define assert_param(expr) ((void)0U) +#endif /* USE_FULL_ASSERT */ + +#ifdef __cplusplus +} +#endif + +#endif /* STM32G4xx_HAL_CONF_H */ diff --git a/CUBEMXTESTING/Core/Inc/stm32g4xx_it.h b/CUBEMXTESTING/Core/Inc/stm32g4xx_it.h new file mode 100644 index 000000000..f7508212b --- /dev/null +++ b/CUBEMXTESTING/Core/Inc/stm32g4xx_it.h @@ -0,0 +1,70 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file stm32g4xx_it.h + * @brief This file contains the headers of the interrupt handlers. + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 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 */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32G4xx_IT_H +#define __STM32G4xx_IT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Exported types ------------------------------------------------------------*/ +/* USER CODE BEGIN ET */ + +/* USER CODE END ET */ + +/* Exported constants --------------------------------------------------------*/ +/* USER CODE BEGIN EC */ + +/* USER CODE END EC */ + +/* Exported macro ------------------------------------------------------------*/ +/* USER CODE BEGIN EM */ + +/* USER CODE END EM */ + +/* Exported functions prototypes ---------------------------------------------*/ +void NMI_Handler(void); +void HardFault_Handler(void); +void MemManage_Handler(void); +void BusFault_Handler(void); +void UsageFault_Handler(void); +void SVC_Handler(void); +void DebugMon_Handler(void); +void PendSV_Handler(void); +void SysTick_Handler(void); +void FDCAN1_IT0_IRQHandler(void); +void TIM5_IRQHandler(void); +void FDCAN2_IT0_IRQHandler(void); +void FDCAN3_IT0_IRQHandler(void); +/* USER CODE BEGIN EFP */ + +/* USER CODE END EFP */ + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32G4xx_IT_H */ diff --git a/CUBEMXTESTING/Core/Inc/tim.h b/CUBEMXTESTING/Core/Inc/tim.h new file mode 100644 index 000000000..2d20bd560 --- /dev/null +++ b/CUBEMXTESTING/Core/Inc/tim.h @@ -0,0 +1,51 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file tim.h + * @brief This file contains all the function prototypes for + * the tim.c file + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 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 */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __TIM_H__ +#define __TIM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "main.h" + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +extern TIM_HandleTypeDef htim5; + +/* USER CODE BEGIN Private defines */ +#define CAN_TIMER_HANDLE htim5 +/* USER CODE END Private defines */ + +void MX_TIM5_Init(void); + +/* USER CODE BEGIN Prototypes */ + +/* USER CODE END Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif /* __TIM_H__ */ diff --git a/CUBEMXTESTING/Core/Inc/usart.h b/CUBEMXTESTING/Core/Inc/usart.h new file mode 100644 index 000000000..bdec0c9cb --- /dev/null +++ b/CUBEMXTESTING/Core/Inc/usart.h @@ -0,0 +1,51 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file usart.h + * @brief This file contains all the function prototypes for + * the usart.c file + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 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 */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USART_H__ +#define __USART_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "main.h" + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +extern UART_HandleTypeDef hlpuart1; + +/* USER CODE BEGIN Private defines */ + +/* USER CODE END Private defines */ + +void MX_LPUART1_UART_Init(void); + +/* USER CODE BEGIN Prototypes */ + +/* USER CODE END Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USART_H__ */ diff --git a/CUBEMXTESTING/Core/Src/fdcan.c b/CUBEMXTESTING/Core/Src/fdcan.c new file mode 100644 index 000000000..30b3c9dc1 --- /dev/null +++ b/CUBEMXTESTING/Core/Src/fdcan.c @@ -0,0 +1,312 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file fdcan.c + * @brief This file provides code for the configuration + * of the FDCAN instances. + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 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 "fdcan.h" + +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ + +FDCAN_HandleTypeDef hfdcan1; +FDCAN_HandleTypeDef hfdcan2; +FDCAN_HandleTypeDef hfdcan3; + +/* FDCAN1 init function */ +void MX_FDCAN1_Init(void) +{ + + /* USER CODE BEGIN FDCAN1_Init 0 */ + + /* USER CODE END FDCAN1_Init 0 */ + + /* USER CODE BEGIN FDCAN1_Init 1 */ + + /* USER CODE END FDCAN1_Init 1 */ + hfdcan1.Instance = FDCAN1; + hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1; + hfdcan1.Init.FrameFormat = FDCAN_FRAME_CLASSIC; + hfdcan1.Init.Mode = FDCAN_MODE_INTERNAL_LOOPBACK; + hfdcan1.Init.AutoRetransmission = ENABLE; + hfdcan1.Init.TransmitPause = DISABLE; + hfdcan1.Init.ProtocolException = ENABLE; + hfdcan1.Init.NominalPrescaler = 1; + hfdcan1.Init.NominalSyncJumpWidth = 16; + hfdcan1.Init.NominalTimeSeg1 = 119; + hfdcan1.Init.NominalTimeSeg2 = 40; + hfdcan1.Init.DataPrescaler = 8; + hfdcan1.Init.DataSyncJumpWidth = 16; + hfdcan1.Init.DataTimeSeg1 = 14; + hfdcan1.Init.DataTimeSeg2 = 5; + hfdcan1.Init.StdFiltersNbr = 0; + hfdcan1.Init.ExtFiltersNbr = 2; + hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; + if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK) { + Error_Handler(); + } + /* USER CODE BEGIN FDCAN1_Init 2 */ + + /* USER CODE END FDCAN1_Init 2 */ +} +/* FDCAN2 init function */ +void MX_FDCAN2_Init(void) +{ + + /* USER CODE BEGIN FDCAN2_Init 0 */ + + /* USER CODE END FDCAN2_Init 0 */ + + /* USER CODE BEGIN FDCAN2_Init 1 */ + + /* USER CODE END FDCAN2_Init 1 */ + hfdcan2.Instance = FDCAN2; + hfdcan2.Init.ClockDivider = FDCAN_CLOCK_DIV1; + hfdcan2.Init.FrameFormat = FDCAN_FRAME_CLASSIC; + hfdcan2.Init.Mode = FDCAN_MODE_INTERNAL_LOOPBACK; + hfdcan2.Init.AutoRetransmission = ENABLE; + hfdcan2.Init.TransmitPause = DISABLE; + hfdcan2.Init.ProtocolException = ENABLE; + hfdcan2.Init.NominalPrescaler = 1; + hfdcan2.Init.NominalSyncJumpWidth = 16; + hfdcan2.Init.NominalTimeSeg1 = 119; + hfdcan2.Init.NominalTimeSeg2 = 40; + hfdcan2.Init.DataPrescaler = 8; + hfdcan2.Init.DataSyncJumpWidth = 16; + hfdcan2.Init.DataTimeSeg1 = 14; + hfdcan2.Init.DataTimeSeg2 = 5; + hfdcan2.Init.StdFiltersNbr = 0; + hfdcan2.Init.ExtFiltersNbr = 2; + hfdcan2.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; + if (HAL_FDCAN_Init(&hfdcan2) != HAL_OK) { + Error_Handler(); + } + /* USER CODE BEGIN FDCAN2_Init 2 */ + + /* USER CODE END FDCAN2_Init 2 */ +} +/* FDCAN3 init function */ +void MX_FDCAN3_Init(void) +{ + + /* USER CODE BEGIN FDCAN3_Init 0 */ + + /* USER CODE END FDCAN3_Init 0 */ + + /* USER CODE BEGIN FDCAN3_Init 1 */ + + /* USER CODE END FDCAN3_Init 1 */ + hfdcan3.Instance = FDCAN3; + hfdcan3.Init.ClockDivider = FDCAN_CLOCK_DIV1; + hfdcan3.Init.FrameFormat = FDCAN_FRAME_CLASSIC; + hfdcan3.Init.Mode = FDCAN_MODE_INTERNAL_LOOPBACK; + hfdcan3.Init.AutoRetransmission = DISABLE; + hfdcan3.Init.TransmitPause = DISABLE; + hfdcan3.Init.ProtocolException = DISABLE; + hfdcan3.Init.NominalPrescaler = 16; + hfdcan3.Init.NominalSyncJumpWidth = 1; + hfdcan3.Init.NominalTimeSeg1 = 1; + hfdcan3.Init.NominalTimeSeg2 = 1; + hfdcan3.Init.DataPrescaler = 1; + hfdcan3.Init.DataSyncJumpWidth = 1; + hfdcan3.Init.DataTimeSeg1 = 1; + hfdcan3.Init.DataTimeSeg2 = 1; + hfdcan3.Init.StdFiltersNbr = 0; + hfdcan3.Init.ExtFiltersNbr = 0; + hfdcan3.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; + if (HAL_FDCAN_Init(&hfdcan3) != HAL_OK) { + Error_Handler(); + } + /* USER CODE BEGIN FDCAN3_Init 2 */ + + /* USER CODE END FDCAN3_Init 2 */ +} + +static uint32_t HAL_RCC_FDCAN_CLK_ENABLED = 0; + +void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef *fdcanHandle) +{ + + GPIO_InitTypeDef GPIO_InitStruct = {0}; + if (fdcanHandle->Instance == FDCAN1) { + /* USER CODE BEGIN FDCAN1_MspInit 0 */ + + /* USER CODE END FDCAN1_MspInit 0 */ + LL_RCC_SetFDCANClockSource(LL_RCC_FDCAN_CLKSOURCE_PCLK1); + + /* FDCAN1 clock enable */ + HAL_RCC_FDCAN_CLK_ENABLED++; + if (HAL_RCC_FDCAN_CLK_ENABLED == 1) { + __HAL_RCC_FDCAN_CLK_ENABLE(); + } + + __HAL_RCC_GPIOB_CLK_ENABLE(); + /**FDCAN1 GPIO Configuration + PB8-BOOT0 ------> FDCAN1_RX + PB9 ------> FDCAN1_TX + */ + GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF9_FDCAN1; + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /* FDCAN1 interrupt Init */ + HAL_NVIC_SetPriority(FDCAN1_IT0_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn); + /* USER CODE BEGIN FDCAN1_MspInit 1 */ + + /* USER CODE END FDCAN1_MspInit 1 */ + } else if (fdcanHandle->Instance == FDCAN2) { + /* USER CODE BEGIN FDCAN2_MspInit 0 */ + + /* USER CODE END FDCAN2_MspInit 0 */ + + LL_RCC_SetFDCANClockSource(LL_RCC_FDCAN_CLKSOURCE_PCLK1); + + /* FDCAN2 clock enable */ + HAL_RCC_FDCAN_CLK_ENABLED++; + if (HAL_RCC_FDCAN_CLK_ENABLED == 1) { + __HAL_RCC_FDCAN_CLK_ENABLE(); + } + + __HAL_RCC_GPIOB_CLK_ENABLE(); + /**FDCAN2 GPIO Configuration + PB12 ------> FDCAN2_RX + PB13 ------> FDCAN2_TX + */ + GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_13; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF9_FDCAN2; + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /* FDCAN2 interrupt Init */ + HAL_NVIC_SetPriority(FDCAN2_IT0_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(FDCAN2_IT0_IRQn); + /* USER CODE BEGIN FDCAN2_MspInit 1 */ + + /* USER CODE END FDCAN2_MspInit 1 */ + } else if (fdcanHandle->Instance == FDCAN3) { + /* USER CODE BEGIN FDCAN3_MspInit 0 */ + + /* USER CODE END FDCAN3_MspInit 0 */ + + LL_RCC_SetFDCANClockSource(LL_RCC_FDCAN_CLKSOURCE_PCLK1); + + /* FDCAN3 clock enable */ + HAL_RCC_FDCAN_CLK_ENABLED++; + if (HAL_RCC_FDCAN_CLK_ENABLED == 1) { + __HAL_RCC_FDCAN_CLK_ENABLE(); + } + + __HAL_RCC_GPIOA_CLK_ENABLE(); + /**FDCAN3 GPIO Configuration + PA8 ------> FDCAN3_RX + PA15 ------> FDCAN3_TX + */ + GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_15; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF11_FDCAN3; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /* FDCAN3 interrupt Init */ + HAL_NVIC_SetPriority(FDCAN3_IT0_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(FDCAN3_IT0_IRQn); + /* USER CODE BEGIN FDCAN3_MspInit 1 */ + + /* USER CODE END FDCAN3_MspInit 1 */ + } +} + +void HAL_FDCAN_MspDeInit(FDCAN_HandleTypeDef *fdcanHandle) +{ + + if (fdcanHandle->Instance == FDCAN1) { + /* USER CODE BEGIN FDCAN1_MspDeInit 0 */ + + /* USER CODE END FDCAN1_MspDeInit 0 */ + /* Peripheral clock disable */ + HAL_RCC_FDCAN_CLK_ENABLED--; + if (HAL_RCC_FDCAN_CLK_ENABLED == 0) { + __HAL_RCC_FDCAN_CLK_DISABLE(); + } + + /**FDCAN1 GPIO Configuration + PB8-BOOT0 ------> FDCAN1_RX + PB9 ------> FDCAN1_TX + */ + HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8 | GPIO_PIN_9); + + /* FDCAN1 interrupt Deinit */ + HAL_NVIC_DisableIRQ(FDCAN1_IT0_IRQn); + /* USER CODE BEGIN FDCAN1_MspDeInit 1 */ + + /* USER CODE END FDCAN1_MspDeInit 1 */ + } else if (fdcanHandle->Instance == FDCAN2) { + /* USER CODE BEGIN FDCAN2_MspDeInit 0 */ + + /* USER CODE END FDCAN2_MspDeInit 0 */ + /* Peripheral clock disable */ + HAL_RCC_FDCAN_CLK_ENABLED--; + if (HAL_RCC_FDCAN_CLK_ENABLED == 0) { + __HAL_RCC_FDCAN_CLK_DISABLE(); + } + + /**FDCAN2 GPIO Configuration + PB12 ------> FDCAN2_RX + PB13 ------> FDCAN2_TX + */ + HAL_GPIO_DeInit(GPIOB, GPIO_PIN_12 | GPIO_PIN_13); + + /* FDCAN2 interrupt Deinit */ + HAL_NVIC_DisableIRQ(FDCAN2_IT0_IRQn); + /* USER CODE BEGIN FDCAN2_MspDeInit 1 */ + + /* USER CODE END FDCAN2_MspDeInit 1 */ + } else if (fdcanHandle->Instance == FDCAN3) { + /* USER CODE BEGIN FDCAN3_MspDeInit 0 */ + + /* USER CODE END FDCAN3_MspDeInit 0 */ + /* Peripheral clock disable */ + HAL_RCC_FDCAN_CLK_ENABLED--; + if (HAL_RCC_FDCAN_CLK_ENABLED == 0) { + __HAL_RCC_FDCAN_CLK_DISABLE(); + } + + /**FDCAN3 GPIO Configuration + PA8 ------> FDCAN3_RX + PA15 ------> FDCAN3_TX + */ + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_8 | GPIO_PIN_15); + + /* FDCAN3 interrupt Deinit */ + HAL_NVIC_DisableIRQ(FDCAN3_IT0_IRQn); + /* USER CODE BEGIN FDCAN3_MspDeInit 1 */ + + /* USER CODE END FDCAN3_MspDeInit 1 */ + } +} + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ diff --git a/CUBEMXTESTING/Core/Src/gpio.c b/CUBEMXTESTING/Core/Src/gpio.c new file mode 100644 index 000000000..d0d390b5b --- /dev/null +++ b/CUBEMXTESTING/Core/Src/gpio.c @@ -0,0 +1,311 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file gpio.c + * @brief This file provides code for the configuration + * of all used GPIO pins. + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 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 "gpio.h" + +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ + +/*----------------------------------------------------------------------------*/ +/* Configure GPIO */ +/*----------------------------------------------------------------------------*/ +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ + +/** Configure pins as + * Analog + * Input + * Output + * EVENT_OUT + * EXTI + * Free pins are configured automatically as Analog (this feature is enabled through + * the Code Generation settings) + */ +void MX_GPIO_Init(void) +{ + + LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; + + /* GPIO Ports Clock Enable */ + LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC); + LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOF); + LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOG); + LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); + LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB); + LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOD); + + /**/ + LL_GPIO_ResetOutputPin(USER_LED_GPIO_Port, USER_LED_Pin); + + /**/ + GPIO_InitStruct.Pin = USER_BUTTON_Pin; + GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(USER_BUTTON_GPIO_Port, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_14; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_15; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_0; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOF, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_1; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOF, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_10; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOG, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_2; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_3; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_0; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_1; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_2; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_3; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_4; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = USER_LED_Pin; + GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT; + GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(USER_LED_GPIO_Port, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_6; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_7; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_4; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_5; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_0; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_1; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_2; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_10; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_11; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_14; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_15; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_6; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_7; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_8; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_9; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_9; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_10; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_11; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_12; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_10; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_11; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_12; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_2; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOD, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_4; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_5; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_6; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /**/ + GPIO_InitStruct.Pin = LL_GPIO_PIN_7; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(GPIOB, &GPIO_InitStruct); +} + +/* USER CODE BEGIN 2 */ + +/* USER CODE END 2 */ diff --git a/CUBEMXTESTING/Core/Src/main.c b/CUBEMXTESTING/Core/Src/main.c new file mode 100644 index 000000000..6cf7fb03a --- /dev/null +++ b/CUBEMXTESTING/Core/Src/main.c @@ -0,0 +1,248 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : main.c + * @brief : Main program body + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 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 "fdcan.h" +#include "gpio.h" +#include "tim.h" +#include "usart.h" + +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ +#include + +#include "CANdler.h" +#include "CubeCAN.h" +#include "GRCAN_BUS_ID.h" +#include "GRCAN_NODE_ID.h" +#include "Logomatic.h" +#include "Loop.h" +#include "tim.h" + +// #include "can.h" +// #include "can_cfg.h" +// #include "gr_can_init.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 */ +CubeCAN_Handle *primaryHandle = NULL; +CubeCAN_Handle *dataHandle = NULL; +CubeCAN_Handle *chargerHandle = NULL; + +LogomaticConfig logomaticConfig = {.clock_source = LOGOMATIC_PCLK1, + .bus = LOGOMATIC_BUS, + .gpio_port = LOGOMATIC_GPIOA, + .gpio_pin_rx_tx_mask = LL_GPIO_PIN_9 | LL_GPIO_PIN_10, + .baud_rate = 115200, + .data_width = LOGOMATIC_DATAWIDTH_8B, + .stop_bits = LOGOMATIC_STOPBITS_1, + .parity = LOGOMATIC_PARITY_NONE, + .transfer_direction = LOGOMATIC_DIRECTION_TX, + .hardware_flow_control = LOGOMATIC_HWCONTROL_NONE, + .prescaler = LOGOMATIC_PRESCALER_DIV1, + .tx_fifo_threshold = LOGOMATIC_FIFOTHRESHOLD_1_8, + .rx_fifo_threshold = LOGOMATIC_FIFOTHRESHOLD_1_8}; +/* 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 */ + +/* 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 */ + + /* 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_FDCAN1_Init(); + MX_FDCAN2_Init(); + MX_TIM5_Init(); + MX_FDCAN3_Init(); + MX_LPUART1_UART_Init(); + /* USER CODE BEGIN 2 */ + Setup_Logomatic(&logomaticConfig); + + CubeCAN_Config primaryCanConfig = {.rx_callback = CANdler_Callback, .context.busid_user_context = GRCAN_BUS_PRIMARY, .sending_node_id = GRCAN_Debugger}; + CubeCAN_Config dataCanConfig = {.rx_callback = CANdler_Callback, .context.busid_user_context = GRCAN_BUS_DATA, .sending_node_id = GRCAN_Debugger}; + CubeCAN_Config chargerCanConfig = {.rx_callback = CANdler_Callback, .context.busid_user_context = GRCAN_BUS_CHARGER, .sending_node_id = GRCAN_Debugger}; + + primaryHandle = CubeCAN_Entrance(&hfdcan1, &primaryCanConfig); + if (primaryHandle == NULL) { + LOGOMATIC("Failed to initialize primary CAN handle\n"); + Error_Handler(); + } + + dataHandle = CubeCAN_Entrance(&hfdcan2, &dataCanConfig); + if (dataHandle == NULL) { + LOGOMATIC("Failed to initialize data CAN handle\n"); + Error_Handler(); + } + + chargerHandle = CubeCAN_Entrance(&hfdcan3, &chargerCanConfig); + if (chargerHandle == NULL) { + LOGOMATIC("Failed to initialize charger CAN handle\n"); + Error_Handler(); + } + + LOGOMATIC("Hello World!\n"); + /* USER CODE END 2 */ + + /* Infinite loop */ + /* USER CODE BEGIN WHILE */ + while (1) { + /* USER CODE END WHILE */ + + /* USER CODE BEGIN 3 */ + MainLoop(); + HAL_Delay(100); + } + /* USER CODE END 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_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_1, 20, 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_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(); + } +} + +/* USER CODE BEGIN 4 */ + +/** + * @brief Route the CubeMX-generated CAN service timer callback to the CAN runtime. + * + * CAN_TIMER_HANDLE is generated by can_cfg.h from CAN_TIMER_INDEX. + */ +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) +{ + UNUSED(htim); + CubeCAN_Tick(); +} + +/* 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 */ + LOGOMATIC("Assert failed! File %s on line %" PRIu32 "\n", file, line); + /* USER CODE END 6 */ +} +#endif /* USE_FULL_ASSERT */ diff --git a/CUBEMXTESTING/Core/Src/stm32g4xx_hal_msp.c b/CUBEMXTESTING/Core/Src/stm32g4xx_hal_msp.c new file mode 100644 index 000000000..4668755ab --- /dev/null +++ b/CUBEMXTESTING/Core/Src/stm32g4xx_hal_msp.c @@ -0,0 +1,85 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file stm32g4xx_hal_msp.c + * @brief This file provides code for the MSP Initialization + * and de-Initialization codes. + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 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" +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Private typedef -----------------------------------------------------------*/ +/* USER CODE BEGIN TD */ + +/* USER CODE END TD */ + +/* Private define ------------------------------------------------------------*/ +/* USER CODE BEGIN Define */ + +/* USER CODE END Define */ + +/* Private macro -------------------------------------------------------------*/ +/* USER CODE BEGIN Macro */ + +/* USER CODE END Macro */ + +/* Private variables ---------------------------------------------------------*/ +/* USER CODE BEGIN PV */ + +/* USER CODE END PV */ + +/* Private function prototypes -----------------------------------------------*/ +/* USER CODE BEGIN PFP */ + +/* USER CODE END PFP */ + +/* External functions --------------------------------------------------------*/ +/* USER CODE BEGIN ExternalFunctions */ + +/* USER CODE END ExternalFunctions */ + +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ +/** + * Initializes the Global MSP. + */ +void HAL_MspInit(void) +{ + + /* USER CODE BEGIN MspInit 0 */ + + /* USER CODE END MspInit 0 */ + + __HAL_RCC_SYSCFG_CLK_ENABLE(); + __HAL_RCC_PWR_CLK_ENABLE(); + + /* System interrupt init*/ + + /** Disable the internal Pull-Up in Dead Battery pins of UCPD peripheral + */ + HAL_PWREx_DisableUCPDDeadBattery(); + + /* USER CODE BEGIN MspInit 1 */ + + /* USER CODE END MspInit 1 */ +} + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ diff --git a/CUBEMXTESTING/Core/Src/stm32g4xx_it.c b/CUBEMXTESTING/Core/Src/stm32g4xx_it.c new file mode 100644 index 000000000..fd2ad329f --- /dev/null +++ b/CUBEMXTESTING/Core/Src/stm32g4xx_it.c @@ -0,0 +1,257 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file stm32g4xx_it.c + * @brief Interrupt Service Routines. + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 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 "stm32g4xx_it.h" + +#include "main.h" +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ +/* USER CODE END Includes */ + +/* Private typedef -----------------------------------------------------------*/ +/* USER CODE BEGIN TD */ + +/* USER CODE END TD */ + +/* 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 -----------------------------------------------*/ +/* USER CODE BEGIN PFP */ + +/* USER CODE END PFP */ + +/* Private user code ---------------------------------------------------------*/ +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ + +/* External variables --------------------------------------------------------*/ +extern FDCAN_HandleTypeDef hfdcan1; +extern FDCAN_HandleTypeDef hfdcan2; +extern FDCAN_HandleTypeDef hfdcan3; +extern TIM_HandleTypeDef htim5; +/* USER CODE BEGIN EV */ + +/* USER CODE END EV */ + +/******************************************************************************/ +/* Cortex-M4 Processor Interruption and Exception Handlers */ +/******************************************************************************/ +/** + * @brief This function handles Non maskable interrupt. + */ +void NMI_Handler(void) +{ + /* USER CODE BEGIN NonMaskableInt_IRQn 0 */ + + /* USER CODE END NonMaskableInt_IRQn 0 */ + /* USER CODE BEGIN NonMaskableInt_IRQn 1 */ + while (1) {} + /* USER CODE END NonMaskableInt_IRQn 1 */ +} + +/** + * @brief This function handles Hard fault interrupt. + */ +void HardFault_Handler(void) +{ + /* USER CODE BEGIN HardFault_IRQn 0 */ + + /* USER CODE END HardFault_IRQn 0 */ + while (1) { + /* USER CODE BEGIN W1_HardFault_IRQn 0 */ + /* USER CODE END W1_HardFault_IRQn 0 */ + } +} + +/** + * @brief This function handles Memory management fault. + */ +void MemManage_Handler(void) +{ + /* USER CODE BEGIN MemoryManagement_IRQn 0 */ + + /* USER CODE END MemoryManagement_IRQn 0 */ + while (1) { + /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */ + /* USER CODE END W1_MemoryManagement_IRQn 0 */ + } +} + +/** + * @brief This function handles Prefetch fault, memory access fault. + */ +void BusFault_Handler(void) +{ + /* USER CODE BEGIN BusFault_IRQn 0 */ + + /* USER CODE END BusFault_IRQn 0 */ + while (1) { + /* USER CODE BEGIN W1_BusFault_IRQn 0 */ + /* USER CODE END W1_BusFault_IRQn 0 */ + } +} + +/** + * @brief This function handles Undefined instruction or illegal state. + */ +void UsageFault_Handler(void) +{ + /* USER CODE BEGIN UsageFault_IRQn 0 */ + + /* USER CODE END UsageFault_IRQn 0 */ + while (1) { + /* USER CODE BEGIN W1_UsageFault_IRQn 0 */ + /* USER CODE END W1_UsageFault_IRQn 0 */ + } +} + +/** + * @brief This function handles System service call via SWI instruction. + */ +void SVC_Handler(void) +{ + /* USER CODE BEGIN SVCall_IRQn 0 */ + + /* USER CODE END SVCall_IRQn 0 */ + /* USER CODE BEGIN SVCall_IRQn 1 */ + + /* USER CODE END SVCall_IRQn 1 */ +} + +/** + * @brief This function handles Debug monitor. + */ +void DebugMon_Handler(void) +{ + /* USER CODE BEGIN DebugMonitor_IRQn 0 */ + + /* USER CODE END DebugMonitor_IRQn 0 */ + /* USER CODE BEGIN DebugMonitor_IRQn 1 */ + + /* USER CODE END DebugMonitor_IRQn 1 */ +} + +/** + * @brief This function handles Pendable request for system service. + */ +void PendSV_Handler(void) +{ + /* USER CODE BEGIN PendSV_IRQn 0 */ + + /* USER CODE END PendSV_IRQn 0 */ + /* USER CODE BEGIN PendSV_IRQn 1 */ + + /* USER CODE END PendSV_IRQn 1 */ +} + +/** + * @brief This function handles System tick timer. + */ +void SysTick_Handler(void) +{ + /* USER CODE BEGIN SysTick_IRQn 0 */ + + /* USER CODE END SysTick_IRQn 0 */ + HAL_IncTick(); + /* USER CODE BEGIN SysTick_IRQn 1 */ + + /* USER CODE END SysTick_IRQn 1 */ +} + +/******************************************************************************/ +/* STM32G4xx Peripheral Interrupt Handlers */ +/* Add here the Interrupt Handlers for the used peripherals. */ +/* For the available peripheral interrupt handler names, */ +/* please refer to the startup file (startup_stm32g4xx.s). */ +/******************************************************************************/ + +/** + * @brief This function handles FDCAN1 interrupt 0. + */ +void FDCAN1_IT0_IRQHandler(void) +{ + /* USER CODE BEGIN FDCAN1_IT0_IRQn 0 */ + + /* USER CODE END FDCAN1_IT0_IRQn 0 */ + HAL_FDCAN_IRQHandler(&hfdcan1); + /* USER CODE BEGIN FDCAN1_IT0_IRQn 1 */ + + /* USER CODE END FDCAN1_IT0_IRQn 1 */ +} + +/** + * @brief This function handles TIM5 global interrupt. + */ +void TIM5_IRQHandler(void) +{ + /* USER CODE BEGIN TIM5_IRQn 0 */ + + /* USER CODE END TIM5_IRQn 0 */ + HAL_TIM_IRQHandler(&htim5); + /* USER CODE BEGIN TIM5_IRQn 1 */ + + /* USER CODE END TIM5_IRQn 1 */ +} + +/** + * @brief This function handles FDCAN2 interrupt 0. + */ +void FDCAN2_IT0_IRQHandler(void) +{ + /* USER CODE BEGIN FDCAN2_IT0_IRQn 0 */ + + /* USER CODE END FDCAN2_IT0_IRQn 0 */ + HAL_FDCAN_IRQHandler(&hfdcan2); + /* USER CODE BEGIN FDCAN2_IT0_IRQn 1 */ + + /* USER CODE END FDCAN2_IT0_IRQn 1 */ +} + +/** + * @brief This function handles FDCAN3 interrupt 0. + */ +void FDCAN3_IT0_IRQHandler(void) +{ + /* USER CODE BEGIN FDCAN3_IT0_IRQn 0 */ + + /* USER CODE END FDCAN3_IT0_IRQn 0 */ + HAL_FDCAN_IRQHandler(&hfdcan3); + /* USER CODE BEGIN FDCAN3_IT0_IRQn 1 */ + + /* USER CODE END FDCAN3_IT0_IRQn 1 */ +} + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ diff --git a/CUBEMXTESTING/Core/Src/tim.c b/CUBEMXTESTING/Core/Src/tim.c new file mode 100644 index 000000000..e77341bf2 --- /dev/null +++ b/CUBEMXTESTING/Core/Src/tim.c @@ -0,0 +1,105 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file tim.c + * @brief This file provides code for the configuration + * of the TIM instances. + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 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 "tim.h" + +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ + +TIM_HandleTypeDef htim5; + +/* TIM5 init function */ +void MX_TIM5_Init(void) +{ + + /* USER CODE BEGIN TIM5_Init 0 */ + + /* USER CODE END TIM5_Init 0 */ + + TIM_ClockConfigTypeDef sClockSourceConfig = {0}; + TIM_MasterConfigTypeDef sMasterConfig = {0}; + + /* USER CODE BEGIN TIM5_Init 1 */ + + /* USER CODE END TIM5_Init 1 */ + htim5.Instance = TIM5; + htim5.Init.Prescaler = 1599; + htim5.Init.CounterMode = TIM_COUNTERMODE_UP; + htim5.Init.Period = 49; + htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + if (HAL_TIM_Base_Init(&htim5) != HAL_OK) { + Error_Handler(); + } + sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; + if (HAL_TIM_ConfigClockSource(&htim5, &sClockSourceConfig) != HAL_OK) { + Error_Handler(); + } + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK) { + Error_Handler(); + } + /* USER CODE BEGIN TIM5_Init 2 */ + HAL_TIM_Base_Start_IT(&htim5); + /* USER CODE END TIM5_Init 2 */ +} + +void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *tim_baseHandle) +{ + + if (tim_baseHandle->Instance == TIM5) { + /* USER CODE BEGIN TIM5_MspInit 0 */ + + /* USER CODE END TIM5_MspInit 0 */ + /* TIM5 clock enable */ + __HAL_RCC_TIM5_CLK_ENABLE(); + + /* TIM5 interrupt Init */ + HAL_NVIC_SetPriority(TIM5_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(TIM5_IRQn); + /* USER CODE BEGIN TIM5_MspInit 1 */ + + /* USER CODE END TIM5_MspInit 1 */ + } +} + +void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *tim_baseHandle) +{ + + if (tim_baseHandle->Instance == TIM5) { + /* USER CODE BEGIN TIM5_MspDeInit 0 */ + + /* USER CODE END TIM5_MspDeInit 0 */ + /* Peripheral clock disable */ + __HAL_RCC_TIM5_CLK_DISABLE(); + + /* TIM5 interrupt Deinit */ + HAL_NVIC_DisableIRQ(TIM5_IRQn); + /* USER CODE BEGIN TIM5_MspDeInit 1 */ + + /* USER CODE END TIM5_MspDeInit 1 */ + } +} + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ diff --git a/CUBEMXTESTING/Core/Src/usart.c b/CUBEMXTESTING/Core/Src/usart.c new file mode 100644 index 000000000..4e3f96e17 --- /dev/null +++ b/CUBEMXTESTING/Core/Src/usart.c @@ -0,0 +1,123 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file usart.c + * @brief This file provides code for the configuration + * of the USART instances. + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 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 "usart.h" + +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ + +UART_HandleTypeDef hlpuart1; + +/* LPUART1 init function */ + +void MX_LPUART1_UART_Init(void) +{ + + /* USER CODE BEGIN LPUART1_Init 0 */ + + /* USER CODE END LPUART1_Init 0 */ + + /* USER CODE BEGIN LPUART1_Init 1 */ + + /* USER CODE END LPUART1_Init 1 */ + hlpuart1.Instance = LPUART1; + hlpuart1.Init.BaudRate = 115200; + hlpuart1.Init.WordLength = UART_WORDLENGTH_8B; + hlpuart1.Init.StopBits = UART_STOPBITS_1; + hlpuart1.Init.Parity = UART_PARITY_NONE; + hlpuart1.Init.Mode = UART_MODE_TX_RX; + hlpuart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; + hlpuart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; + hlpuart1.Init.ClockPrescaler = UART_PRESCALER_DIV1; + hlpuart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; + if (HAL_UART_Init(&hlpuart1) != HAL_OK) { + Error_Handler(); + } + if (HAL_UARTEx_SetTxFifoThreshold(&hlpuart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK) { + Error_Handler(); + } + if (HAL_UARTEx_SetRxFifoThreshold(&hlpuart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK) { + Error_Handler(); + } + if (HAL_UARTEx_DisableFifoMode(&hlpuart1) != HAL_OK) { + Error_Handler(); + } + /* USER CODE BEGIN LPUART1_Init 2 */ + + /* USER CODE END LPUART1_Init 2 */ +} + +void HAL_UART_MspInit(UART_HandleTypeDef *uartHandle) +{ + + GPIO_InitTypeDef GPIO_InitStruct = {0}; + if (uartHandle->Instance == LPUART1) { + /* USER CODE BEGIN LPUART1_MspInit 0 */ + + /* USER CODE END LPUART1_MspInit 0 */ + LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); + + /* LPUART1 clock enable */ + __HAL_RCC_LPUART1_CLK_ENABLE(); + + __HAL_RCC_GPIOC_CLK_ENABLE(); + /**LPUART1 GPIO Configuration + PC0 ------> LPUART1_RX + PC1 ------> LPUART1_TX + */ + GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF8_LPUART1; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /* USER CODE BEGIN LPUART1_MspInit 1 */ + + /* USER CODE END LPUART1_MspInit 1 */ + } +} + +void HAL_UART_MspDeInit(UART_HandleTypeDef *uartHandle) +{ + + if (uartHandle->Instance == LPUART1) { + /* USER CODE BEGIN LPUART1_MspDeInit 0 */ + + /* USER CODE END LPUART1_MspDeInit 0 */ + /* Peripheral clock disable */ + __HAL_RCC_LPUART1_CLK_DISABLE(); + + /**LPUART1 GPIO Configuration + PC0 ------> LPUART1_RX + PC1 ------> LPUART1_TX + */ + HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0 | GPIO_PIN_1); + + /* USER CODE BEGIN LPUART1_MspDeInit 1 */ + + /* USER CODE END LPUART1_MspDeInit 1 */ + } +} + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ diff --git a/CUBEMXTESTING/README_CAN_Timer_Configuration.md b/CUBEMXTESTING/README_CAN_Timer_Configuration.md new file mode 100644 index 000000000..ee8330180 --- /dev/null +++ b/CUBEMXTESTING/README_CAN_Timer_Configuration.md @@ -0,0 +1,941 @@ +# CAN Timer Configuration Guide + +This guide explains how to configure, modify, and extend the CubeMX-based CAN timer implementation. + +The current design uses STM32CubeMX for hardware configuration and a custom CAN runtime for software queue handling. + +--- + +# 1. Ownership Model + +## CubeMX controls + +CubeMX owns all hardware-level configuration: + +- enabled FDCAN peripherals +- enabled timer peripherals +- FDCAN GPIO pins +- FDCAN clock source +- FDCAN bit timing +- timer clock source +- timer prescaler +- initial timer period +- NVIC interrupt enable +- NVIC interrupt priority +- generated HAL handles +- generated interrupt handlers + +Examples: + +```c +FDCAN_HandleTypeDef hfdcan1; +FDCAN_HandleTypeDef hfdcan2; +TIM_HandleTypeDef htim5; +``` + +CubeMX also generates: + +```c +MX_FDCAN1_Init(); +MX_FDCAN2_Init(); +MX_TIM5_Init(); +``` + +Do not manually duplicate this hardware initialization inside the custom CAN library. + +## User-controlled runtime settings + +The user-controlled values are stored in the generated `can_cfg.h` file inside a preserved user-code section. + +Example: + +```c +/* USER CODE BEGIN CAN_USER_CONFIG */ + +#ifndef TX_BUFFER_1_SIZE +#define TX_BUFFER_1_SIZE 20U +#endif + +#ifndef TX_BUFFER_2_SIZE +#define TX_BUFFER_2_SIZE 20U +#endif + +#ifndef TX_BUFFER_3_SIZE +#define TX_BUFFER_3_SIZE 20U +#endif + +#ifndef CAN_TIMER_INDEX +#define CAN_TIMER_INDEX 5 +#endif + +#ifndef CAN_TIMER_TICK_US +#define CAN_TIMER_TICK_US 100U +#endif + +#ifndef CAN_DEQUEUE_PERIOD_US +#define CAN_DEQUEUE_PERIOD_US 500U +#endif + +/* USER CODE END CAN_USER_CONFIG */ +``` + +CubeMX preserves values inside this section when code is regenerated. + +--- + +# 2. Editable Values + +## `TX_BUFFER_1_SIZE` + +Controls the number of software-queued transmit messages for CAN1. + +```c +#define TX_BUFFER_1_SIZE 20U +``` + +Example: + +```c +#define TX_BUFFER_1_SIZE 50U +``` + +Use a larger value when CAN1 may temporarily produce messages faster than the hardware can transmit them. + +A larger queue consumes more RAM. + +## `TX_BUFFER_2_SIZE` + +Controls the software TX queue size for CAN2. + +```c +#define TX_BUFFER_2_SIZE 20U +``` + +## `TX_BUFFER_3_SIZE` + +Controls the software TX queue size for CAN3. + +```c +#define TX_BUFFER_3_SIZE 20U +``` + +This value is ignored when CAN3 is not enabled. + +Queue sizes must be greater than zero. + +--- + +## `CAN_TIMER_INDEX` + +Selects the CubeMX-generated timer used for CAN queue servicing. + +```c +#define CAN_TIMER_INDEX 5 +``` + +This maps to: + +```c +htim5 +``` + +Examples: + +```c +#define CAN_TIMER_INDEX 1 +``` + +selects: + +```c +htim1 +``` + +```c +#define CAN_TIMER_INDEX 2 +``` + +selects: + +```c +htim2 +``` + +```c +#define CAN_TIMER_INDEX 3 +``` + +selects: + +```c +htim3 +``` + +The selected timer must also be enabled in CubeMX. + +If the selected timer is not enabled, the generated configuration should produce a compile-time error. + +--- + +## `CAN_TIMER_TICK_US` + +Defines the duration of one timer counter tick in microseconds. + +```c +#define CAN_TIMER_TICK_US 100U +``` + +This value must match the timer prescaler configured in CubeMX. + +The equation is: + +```text +Timer counter frequency = +Timer input clock / (Prescaler + 1) +``` + +Then: + +```text +Timer tick duration = +1 / Timer counter frequency +``` + +For a 160 MHz timer clock and: + +```c +Prescaler = 15999 +``` + +the counter frequency is: + +```text +160,000,000 / 16,000 = 10,000 Hz +``` + +Therefore: + +```text +1 count = 100 us +``` + +The matching runtime value is: + +```c +#define CAN_TIMER_TICK_US 100U +``` + +### Example: 10 us timer tick + +To create a 10 us timer tick with a 160 MHz timer clock: + +```text +Prescaler = 1599 +``` + +because: + +```text +160,000,000 / 1600 = 100,000 Hz +``` + +Therefore: + +```text +1 count = 10 us +``` + +The matching configuration is: + +```c +#define CAN_TIMER_TICK_US 10U +``` + +Always change the prescaler through CubeMX. + +Do not manually edit the generated `tim.c` file. + +--- + +## `CAN_DEQUEUE_PERIOD_US` + +Controls how often the runtime attempts to dequeue and transmit a queued CAN message. + +```c +#define CAN_DEQUEUE_PERIOD_US 500U +``` + +For: + +```c +#define CAN_TIMER_TICK_US 100U +``` + +the timer uses: + +```text +500 us / 100 us = 5 counts +``` + +The auto-reload value becomes: + +```text +ARR = 5 - 1 = 4 +``` + +The generated macros calculate this automatically: + +```c +#define CAN_TIMER_PERIOD_COUNTS \ + (CAN_DEQUEUE_PERIOD_US / CAN_TIMER_TICK_US) + +#define CAN_TIMER_AUTORELOAD \ + (CAN_TIMER_PERIOD_COUNTS - 1U) +``` + +### Example periods + +With a 100 us timer tick: + +| Desired dequeue period | `CAN_DEQUEUE_PERIOD_US` | ARR | +|---:|---:|---:| +| 500 us | `500U` | 4 | +| 1 ms | `1000U` | 9 | +| 2 ms | `2000U` | 19 | +| 5 ms | `5000U` | 49 | +| 10 ms | `10000U` | 99 | +| 100 ms | `100000U` | 999 | + +The dequeue period must be evenly divisible by the timer tick. + +Valid: + +```c +#define CAN_TIMER_TICK_US 100U +#define CAN_DEQUEUE_PERIOD_US 500U +``` + +Invalid: + +```c +#define CAN_TIMER_TICK_US 100U +#define CAN_DEQUEUE_PERIOD_US 550U +``` + +The second example is invalid because 550 us cannot be represented exactly using 100 us timer counts. + +--- + +# 3. Changing the Active Timer + +To change from TIM5 to TIM2: + +## Step 1: Enable TIM2 in CubeMX + +In CubeMX: + +```text +Pinout & Configuration + Timers + TIM2 + Clock Source: Internal Clock +``` + +Configure the desired prescaler and period. + +Enable: + +```text +TIM2 global interrupt +``` + +## Step 2: Regenerate code + +CubeMX should generate: + +```c +TIM_HandleTypeDef htim2; +void MX_TIM2_Init(void); +``` + +and: + +```c +void TIM2_IRQHandler(void) +{ + HAL_TIM_IRQHandler(&htim2); +} +``` + +## Step 3: Change `can_cfg.h` + +```c +#define CAN_TIMER_INDEX 2 +``` + +## Step 4: Update the timer tick + +Set `CAN_TIMER_TICK_US` to match TIM2's prescaler. + +Example: + +```c +#define CAN_TIMER_TICK_US 100U +``` + +## Step 5: Initialize TIM2 in `main.c` + +CubeMX should generate: + +```c +MX_TIM2_Init(); +``` + +before: + +```c +CAN_Timer_Start(); +``` + +--- + +# 4. Using One Shared Timer + +The current implementation uses one selected timer for all enabled CAN buses. + +Example: + +```text +TIM5 + | + +--> CAN_Timer_Tick() + | + +--> service CAN1 queue + +--> service CAN2 queue +``` + +This is the recommended design in most cases. + +Advantages: + +- fewer hardware timers used +- simpler configuration +- simpler startup +- one callback +- easier CubeMX integration +- easier synchronization between CAN buses + +The callback is: + +```c +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) +{ + if (htim == &CAN_TIMER_HANDLE) { + CAN_Timer_Tick(); + } +} +``` + +--- + +# 5. Servicing Multiple CAN Buses + +## Priority-based scheduling + +This approach always checks CAN1 first: + +```c +void CAN_Timer_Tick(void) +{ +#ifdef USECAN1 + if (CAN_ProcessOneTxMessage(&can1)) { + return; + } +#endif + +#ifdef USECAN2 + if (CAN_ProcessOneTxMessage(&can2)) { + return; + } +#endif + +#ifdef USECAN3 + (void)CAN_ProcessOneTxMessage(&can3); +#endif +} +``` + +Advantages: + +- simple +- supports bus priority + +Disadvantage: + +- lower-priority buses can be starved + +## Round-robin scheduling + +This approach alternates between buses: + +```c +void CAN_Timer_Tick(void) +{ + static uint8_t next_bus = 0U; + +#if defined(USECAN1) && defined(USECAN2) + + if (next_bus == 0U) { + (void)CAN_ProcessOneTxMessage(&can1); + next_bus = 1U; + } else { + (void)CAN_ProcessOneTxMessage(&can2); + next_bus = 0U; + } + +#elif defined(USECAN1) + + (void)CAN_ProcessOneTxMessage(&can1); + +#elif defined(USECAN2) + + (void)CAN_ProcessOneTxMessage(&can2); + +#endif +} +``` + +Advantages: + +- fairer between buses +- avoids CAN1 permanently dominating CAN2 + +--- + +# 6. Using Multiple Active Timers + +The current template selects one shared timer. + +It can be extended so each CAN peripheral uses a separate timer. + +Example: + +```text +TIM1 services CAN1 +TIM2 services CAN2 +TIM3 services CAN3 +``` + +Use separate configuration values: + +```c +#define CAN1_TIMER_INDEX 1 +#define CAN2_TIMER_INDEX 2 +#define CAN3_TIMER_INDEX 3 + +#define CAN1_TIMER_TICK_US 100U +#define CAN2_TIMER_TICK_US 100U +#define CAN3_TIMER_TICK_US 100U + +#define CAN1_DEQUEUE_PERIOD_US 500U +#define CAN2_DEQUEUE_PERIOD_US 1000U +#define CAN3_DEQUEUE_PERIOD_US 2000U +``` + +Create separate timer handles: + +```c +#define CAN1_TIMER_HANDLE \ + CAN_CFG_JOIN(htim, CAN1_TIMER_INDEX) + +#define CAN2_TIMER_HANDLE \ + CAN_CFG_JOIN(htim, CAN2_TIMER_INDEX) + +#define CAN3_TIMER_HANDLE \ + CAN_CFG_JOIN(htim, CAN3_TIMER_INDEX) +``` + +Create separate auto-reload values: + +```c +#define CAN1_TIMER_AUTORELOAD \ + ((CAN1_DEQUEUE_PERIOD_US / CAN1_TIMER_TICK_US) - 1U) + +#define CAN2_TIMER_AUTORELOAD \ + ((CAN2_DEQUEUE_PERIOD_US / CAN2_TIMER_TICK_US) - 1U) + +#define CAN3_TIMER_AUTORELOAD \ + ((CAN3_DEQUEUE_PERIOD_US / CAN3_TIMER_TICK_US) - 1U) +``` + +The callback would route each timer separately: + +```c +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) +{ +#ifdef USECAN1 + if (htim == &CAN1_TIMER_HANDLE) { + CAN1_Timer_Tick(); + return; + } +#endif + +#ifdef USECAN2 + if (htim == &CAN2_TIMER_HANDLE) { + CAN2_Timer_Tick(); + return; + } +#endif + +#ifdef USECAN3 + if (htim == &CAN3_TIMER_HANDLE) { + CAN3_Timer_Tick(); + return; + } +#endif +} +``` + +Each timer must also be started separately: + +```c +if (CAN1_Timer_Start() != HAL_OK) { + Error_Handler(); +} + +if (CAN2_Timer_Start() != HAL_OK) { + Error_Handler(); +} + +if (CAN3_Timer_Start() != HAL_OK) { + Error_Handler(); +} +``` + +Use multiple timers only when the buses require: + +- independent transmission periods +- strict timing separation +- different interrupt priorities +- separate bandwidth policies + +For most applications, one shared timer with round-robin queue servicing is simpler and more efficient. + +--- + +# 7. Timer Startup Function + +The shared-timer implementation should use: + +```c +HAL_StatusTypeDef CAN_Timer_Start(void) +{ + TIM_HandleTypeDef *htim = &CAN_TIMER_HANDLE; + + __HAL_TIM_DISABLE(htim); + + __HAL_TIM_SET_AUTORELOAD( + htim, + CAN_TIMER_AUTORELOAD); + + __HAL_TIM_SET_COUNTER(htim, 0U); + + htim->Instance->EGR = TIM_EGR_UG; + + __HAL_TIM_CLEAR_FLAG( + htim, + TIM_FLAG_UPDATE); + + return HAL_TIM_Base_Start_IT(htim); +} +``` + +This function changes only the runtime period. + +It does not configure: + +- timer clocks +- timer prescaler +- NVIC +- timer instance +- timer IRQ handler + +CubeMX owns those settings. + +--- + +# 8. Required Startup Order + +The hardware initialization must happen before custom runtime initialization. + +Correct: + +```c +MX_GPIO_Init(); +MX_FDCAN1_Init(); +MX_FDCAN2_Init(); +MX_TIM5_Init(); + +GR_CAN_Init(); + +if (CAN_Timer_Start() != HAL_OK) { + Error_Handler(); +} +``` + +Incorrect: + +```c +CAN_Timer_Start(); +MX_TIM5_Init(); +``` + +The timer handle is not ready until `MX_TIM5_Init()` runs. + +--- + +# 9. Files That May Be Edited + +## Edit directly + +These files contain custom code: + +```text +can.c +can.h +gr_can_init.c +gr_can_init.h +can_cfg_h.ftl +README +CMakeLists.txt +``` + +## Edit only inside user-code sections + +```text +main.c +``` + +Examples: + +```c +/* USER CODE BEGIN Includes */ +/* USER CODE END Includes */ +``` + +```c +/* USER CODE BEGIN 2 */ +/* USER CODE END 2 */ +``` + +```c +/* USER CODE BEGIN 4 */ +/* USER CODE END 4 */ +``` + +## Do not manually edit + +These files are generated by CubeMX: + +```text +fdcan.c +fdcan.h +tim.c +tim.h +gpio.c +gpio.h +stm32g4xx_it.c +stm32g4xx_it.h +stm32g4xx_hal_conf.h +``` + +Modify their settings through CubeMX and regenerate. + +--- + +# 10. Regeneration Checklist + +After regenerating CubeMX code, verify: + +```text +main.c includes tim.h +main.c calls MX_TIM5_Init() +tim.c exists +tim.h exists +htim5 is declared +TIM5_IRQHandler exists +HAL_TIM_MODULE_ENABLED is defined +FDCAN1 and FDCAN2 still initialize +can_cfg.h contains USECAN1 and USECAN2 +Core/Src/tim.c is included in the build +``` + +Also verify that custom code remains inside user-code sections. + +--- + +# 11. Testing Changes + +## Test timer frequency + +Temporarily replace queue servicing with a counter: + +```c +volatile uint32_t can_timer_ticks = 0U; + +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) +{ + if (htim == &CAN_TIMER_HANDLE) { + can_timer_ticks++; + } +} +``` + +With: + +```c +#define CAN_DEQUEUE_PERIOD_US 500U +``` + +the counter should increment approximately 2,000 times per second. + +For easier debugging: + +```c +#define CAN_DEQUEUE_PERIOD_US 100000U +``` + +This creates one callback every 100 ms. + +## Test CAN queue servicing + +1. Initialize FDCAN. +2. Initialize the custom CAN runtime. +3. Start the timer. +4. Add one frame to the software queue. +5. Confirm `CAN_Timer_Tick()` runs. +6. Confirm one frame is removed from the queue. +7. Confirm the HAL transmit function succeeds. +8. Confirm another CAN node receives the frame. + +--- + +# 12. Common Errors + +## Selected timer is not enabled + +Example: + +```text +CAN_TIMER_INDEX selects TIM2, but TIM2 is not enabled in CubeMX +``` + +Fix: + +- enable TIM2 in CubeMX, or +- select an enabled timer + +## `htim5` linker error + +Confirm: + +```text +Core/Src/tim.c +``` + +is included in the build. + +## No timer callback + +Confirm: + +- `MX_TIM5_Init()` is called +- `CAN_Timer_Start()` returns `HAL_OK` +- the timer interrupt is enabled +- `TIM5_IRQHandler()` exists +- `HAL_TIM_IRQHandler(&htim5)` is called +- global interrupts are enabled + +## Wrong timer rate + +Confirm: + +- timer input clock +- prescaler +- `CAN_TIMER_TICK_US` +- `CAN_DEQUEUE_PERIOD_US` + +All four values must agree. + +## Duplicate interrupt handler + +Do not define another: + +```c +TIM5_IRQHandler() +``` + +inside the CAN library. + +CubeMX owns the IRQ handler. + +## Messages remain queued + +Check: + +- FDCAN was started +- the hardware TX FIFO has room +- another node is present to acknowledge messages +- the transceiver is enabled +- bus bit timing matches +- queue dequeue code is being called + +--- + +# 13. Current Default Configuration + +Current timer: + +```text +TIM5 +``` + +Timer input clock: + +```text +160 MHz +``` + +Prescaler: + +```text +15999 +``` + +Timer tick: + +```text +100 us +``` + +CAN dequeue period: + +```text +500 us +``` + +Calculated auto-reload: + +```text +4 +``` + +Default queue sizes: + +```text +CAN1: 20 messages +CAN2: 20 messages +CAN3: 20 messages +``` + +The current implementation uses one shared timer for all enabled CAN buses. diff --git a/G4SPITESTING/Core/Inc/stm32_assert.h b/G4SPITESTING/Core/Inc/stm32_assert.h deleted file mode 100644 index 92460620f..000000000 --- a/G4SPITESTING/Core/Inc/stm32_assert.h +++ /dev/null @@ -1,52 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file stm32_assert.h - * @author MCD Application Team - * @brief STM32 assert file. - ****************************************************************************** - * @attention - * - * Copyright (c) 2019 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 */ -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __STM32_ASSERT_H -#define __STM32_ASSERT_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Exported types ------------------------------------------------------------*/ -/* Exported constants --------------------------------------------------------*/ -/* Includes ------------------------------------------------------------------*/ -/* Exported macro ------------------------------------------------------------*/ -#ifdef USE_FULL_ASSERT -/** - * @brief The assert_param macro is used for function's parameters check. - * @param expr: If expr is false, it calls assert_failed function - * which reports the name of the source file and the source - * line number of the call that failed. - * If expr is true, it returns no value. - * @retval None - */ -#define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__)) -/* Exported functions ------------------------------------------------------- */ -void assert_failed(uint8_t *file, uint32_t line); -#else -#define assert_param(expr) ((void)0U) -#endif /* USE_FULL_ASSERT */ - -#ifdef __cplusplus -} -#endif - -#endif /* __STM32_ASSERT_H */ diff --git a/Lib/GlobalShare/Inc/CriticalSection.h b/Lib/GlobalShare/Inc/CriticalSection.h new file mode 100644 index 000000000..7e53dd0b5 --- /dev/null +++ b/Lib/GlobalShare/Inc/CriticalSection.h @@ -0,0 +1,73 @@ +#include + +#include "Stringification.h" + +#ifndef CRITICAL_SECTION_H +#define CRITICAL_SECTION_H + +#ifdef HOOTL_TEST +#include + +extern pthread_mutex_t __mock_global_irq_mutex; +extern _Thread_local uint32_t __mock_primask_state; +extern _Thread_local uint32_t __mock_irq_nesting_depth; + +static inline uint32_t __get_PRIMASK(void) +{ + return __mock_primask_state; +} + +static inline void __disable_irq(void) +{ + pthread_mutex_lock(&__mock_global_irq_mutex); + if (__mock_irq_nesting_depth == 0) { + __mock_primask_state = 1; + } + __mock_irq_nesting_depth++; +} + +static inline void __set_PRIMASK(uint32_t state) +{ + if (__mock_irq_nesting_depth > 0) { + __mock_irq_nesting_depth--; + if (__mock_irq_nesting_depth == 0 || state == 0) { + __mock_primask_state = 0; + __mock_irq_nesting_depth = 0; + } + } + pthread_mutex_unlock(&__mock_global_irq_mutex); +} +#else +#include "main.h" +#endif + +/** + * @brief Internal function to automatically restore the interrupt state when exiting a critical section. + * + * This function is intended to be used with the GCC cleanup attribute to automatically restore the interrupt state when a critical section is exited. It takes a pointer to a state variable that holds + * the previous interrupt state and restores it using the __set_PRIMASK function. + * + * @warning This function is intended for internal use only and should not be called directly by user code. Use the CRITICAL_SECTION macro instead. + */ +static inline void _magic_auto_critical_exit(uint32_t *state_var) +{ + if (state_var) { + __set_PRIMASK(*state_var); + } +} + +/** + * @brief Macro to create a critical section that disables interrupts for the duration of the block. + * + * This macro uses the GCC cleanup attribute to automatically restore the interrupt state when the block is exited, regardless of how the block is exited (e.g., return, break, continue). + * + * @warning This macro should be used with caution, as it can lead to deadlocks or other issues if not used correctly. It is recommended to use this macro only in situations where it is necessary to + * disable interrupts for a short period of time. + * @warning If you use goto statements it will not automatically cleanup. However, you should not use goto at all. + */ +#define CRITICAL_SECTION \ + for (__attribute__((cleanup(_magic_auto_critical_exit))) uint32_t CONCAT(_magic_auto_critical_state_, __LINE__) = __get_PRIMASK(), \ + CONCAT(_magic_auto_critical_run_, __LINE__) = (__disable_irq(), 1); \ + CONCAT(_magic_auto_critical_run_, __LINE__); CONCAT(_magic_auto_critical_run_, __LINE__) = 0) + +#endif diff --git a/Lib/GlobalShare/Test/criticalsection.c b/Lib/GlobalShare/Test/criticalsection.c new file mode 100644 index 000000000..68ebae386 --- /dev/null +++ b/Lib/GlobalShare/Test/criticalsection.c @@ -0,0 +1,110 @@ +#include "CriticalSection.h" + +#include +#include +#include + +#include "Unused.h" + +#define NUM_THREADS 40 +#define INCREMENTS_PER_THREAD 100000 + +pthread_mutex_t __mock_global_irq_mutex; +_Thread_local uint32_t __mock_primask_state = 0; +_Thread_local uint32_t __mock_irq_nesting_depth = 0; + +__attribute__((constructor)) static void init_recursive_mock_mutex(void) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&__mock_global_irq_mutex, &attr); + pthread_mutexattr_destroy(&attr); +} + +volatile long long global_counter = 0; + +void *thread_worker_good(void *arg) +{ + UNUSED(arg); + + for (int i = 0; i < INCREMENTS_PER_THREAD; i++) { + CRITICAL_SECTION + { + CRITICAL_SECTION + { + global_counter++; + } + } + } + + return NULL; +} + +void *thread_worker_bad(void *arg) +{ + UNUSED(arg); + + for (int i = 0; i < INCREMENTS_PER_THREAD; i++) { + global_counter++; + } + + return NULL; +} + +int main(void) +{ + pthread_t threads[NUM_THREADS]; + int thread_ids[NUM_THREADS]; + + printf("Starting %d 'good' threads, each incrementing %d times...\n", NUM_THREADS, INCREMENTS_PER_THREAD); + global_counter = 0; + + for (int i = 0; i < NUM_THREADS; i++) { + thread_ids[i] = i; + if (pthread_create(&threads[i], NULL, thread_worker_good, &thread_ids[i]) != 0) { + printf("Failed to create thread"); + return 1; + } + } + + for (int i = 0; i < NUM_THREADS; i++) { + pthread_join(threads[i], NULL); + } + + long long expected_value = (long long)NUM_THREADS * INCREMENTS_PER_THREAD; + printf("\tExpected final counter value: %lld\n", expected_value); + printf("\tActual global_counter value: %lld\n", global_counter); + + if (global_counter == expected_value) { + printf("\tMocks and nested critical sections passed!\n"); + } else { + printf("\tData race detected! Your critical section failed.\n"); + return 2; + } + + printf("\nStarting %d 'bad' threads, each incrementing %d times...\n", NUM_THREADS, INCREMENTS_PER_THREAD); + global_counter = 0; + + for (int i = 0; i < NUM_THREADS; i++) { + thread_ids[i] = i; + if (pthread_create(&threads[i], NULL, thread_worker_bad, &thread_ids[i]) != 0) { + printf("Failed to create thread"); + return 3; + } + } + + for (int i = 0; i < NUM_THREADS; i++) { + pthread_join(threads[i], NULL); + } + + printf("\tExpected final counter value: %lld\n", expected_value); + printf("\tActual global_counter value: %lld\n", global_counter); + if (global_counter == expected_value) { + printf("\tUnexpectedly, the counter is correct without critical sections. This is highly unlikely and may indicate a problem with the test setup.\n"); + return 4; + } + + printf("\tAs expected, the counter is incorrect without critical sections. Data race detected!\n"); + return 0; +} diff --git a/Lib/GlobalShare/common.cmake b/Lib/GlobalShare/common.cmake index c47123510..f2fd1b082 100644 --- a/Lib/GlobalShare/common.cmake +++ b/Lib/GlobalShare/common.cmake @@ -51,4 +51,19 @@ if(CMAKE_PRESET_NAME STREQUAL "HOOTLTest") ) target_link_libraries(verify_min_max PRIVATE GLOBALSHARE_LIB) add_test(verify_min_max_test verify_min_max) + + find_package(Threads REQUIRED) + add_executable(criticalsection) + target_sources( + criticalsection + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/Test/criticalsection.c + ) + target_link_libraries( + criticalsection + PRIVATE + GLOBALSHARE_LIB + Threads::Threads + ) + add_test(criticalsection_test criticalsection) endif() diff --git a/Lib/Peripherals/CAN/common.cmake b/Lib/Peripherals/CAN/common.cmake index ad9709a07..61aacaf39 100644 --- a/Lib/Peripherals/CAN/common.cmake +++ b/Lib/Peripherals/CAN/common.cmake @@ -58,3 +58,10 @@ target_include_directories( # PERIPHERAL_CAN_LIB_init_test # ) #endif() + +set_target_properties( + PERIPHERAL_CAN_LIB + PROPERTIES + DEPRECATION + "PERIPHERAL_CAN_LIB is deprecated and should be replaced by CUBE_CAN_LIB." +) diff --git a/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h b/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h new file mode 100644 index 000000000..df16b5a82 --- /dev/null +++ b/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h @@ -0,0 +1,140 @@ +#include +#include +#include + +#include "CubeCAN_Config.h" +#include "GRCAN_BUS_ID.h" +#include "GRCAN_CUSTOM_ID.h" +#include "GRCAN_MSG_ID.h" +#include "GRCAN_NODE_ID.h" +#include "main.h" + +#ifndef CUBEMX_CAN_H +#define CUBEMX_CAN_H + +/** + * @brief CAN handle for CubeCAN CAN. + * @warning Do not access directly, use the provided API functions. + */ +typedef struct CubeCAN_Private_Handle CubeCAN_Handle; + +/** + * @brief CAN Identifier structure + * + * This structure is used to represent a CAN message identifier, which consists of a transmitting node ID, a receiving node ID, and a message ID. The structure is used in conjunction with the + * Construct_Message_ID and Deconstruct_Message_ID functions to convert between the structure representation and the 32-bit integer representation of the CAN message identifier. + * + * @warning The structure does not represent custom IDs. + * @warning The structure does not represent actual bit depth + */ +typedef struct { + GRCAN_NODE_ID tx_node_id; + GRCAN_NODE_ID rx_node_id; + GRCAN_MSG_ID msg_id; +} CAN_Identifier; + +/** + * @brief Union for user-defined context data in CubeCAN configuration. + * + * This union allows for flexible user-defined context data to be passed to the receive callback function. It can hold either a pointer to any user-defined data or a specific GRCAN_BUS_ID value. + * + * @warning The union should be used carefully to ensure that the correct type of data is accessed in the callback function. + * + * @note Compile time checks verify that GRCAN_BUS_ID is the same size or smaller than the pointer, ensuring safe usage of the union. + */ +typedef union { + /// @brief Pointer to user-defined context data. This can be used to pass additional data or state information to the receive callback function. + void *full_user_context; + /// @brief GRCAN_BUS_ID value for user-defined context data. This can be used to pass the bus ID to the receive callback function. + GRCAN_BUS_ID busid_user_context; +} CubeCAN_Config_Context; + +/** + * @brief Callback function type for receiving CAN messages. + * + * @warning The callback function should not perform blocking operations or take too long to execute. + * @warning The callback function should not call any CubeCAN functions, as it may lead to undefined behavior. + * @warning It is the responsibility of the callback function to verify the integrity of the received data and handle any errors or null inputs. + */ +typedef void (*CubeCAN_RxCallback)(const CubeCAN_Config_Context *const context, const CAN_Identifier *const identifier, const uint8_t *const data, const uint8_t size); + +/** + * @brief Configuration structure for CubeCAN CAN. + * + * This structure is used to configure the CubeCAN CAN handle, including the receive callback function and user context. The receive callback function is called when a CAN message is received, and the + * user context is a pointer to user-defined data that can be passed to the callback function. + * + * @warning Do not edit after initialization, as it may lead to undefined behavior. + */ +typedef struct { + /// @brief Union for user-defined context data. This can be used to pass additional data or state information to the receive callback function. + CubeCAN_Config_Context context; + /// @brief Callback function for receiving CAN messages. This function is called when a CAN message is received. + CubeCAN_RxCallback rx_callback; + /// @brief Node ID of the sending device. This is used to identify the source of the CAN messages and must be unique on the CAN bus. + GRCAN_NODE_ID sending_node_id; +} CubeCAN_Config; + +/** + * @brief Initializes a CubeCAN CAN handle with the given FDCAN handle and configuration, and starts the CAN peripheral. + * @param hfdcan Pointer to the FDCAN handle. + * @param config Pointer to the CubeCAN configuration structure. + * @return Pointer to the initialized CubeCAN CAN handle, or NULL if initialization fails. + * @note This functions wraps CubeCAN_Private_Init and CubeCAN_Private_Start into a single call. If either of those functions fails, this function will return NULL and the handle will be + * released. + */ +CubeCAN_Handle *CubeCAN_Entrance(FDCAN_HandleTypeDef *hfdcan, CubeCAN_Config *config); + +/** + * @brief Stops the CubeCAN CAN peripheral and releases the associated handle. + * @param handle Pointer to the CubeCAN CAN handle. + * @return HAL_StatusTypeDef indicating the success or failure of the operation. + * @note This function can be used regardless of if CubeCAN_Entrance was used to initialize the handle. + */ +HAL_StatusTypeDef CubeCAN_Exit(CubeCAN_Handle *handle); + +/** + * @brief Processes periodic tasks for the CubeCAN CAN handle, such as handling timeouts and managing the transmission queue. + * + * For each configured bus, this function will send one CAN message per call. It is recommended to call this function at a regular interval to ensure timely processing of CAN messages. + * + * @note This function sends one can message per configured bus per call. Not calling it will simply not send any messages. + * @warning It is recommended to call this function no more than and no less than once every 625 us. + * @warning This function checks during early boot that it is not called more than ABSOLUTE_MAX_INVOCATIONS_PER_TICK times per millisecond, failing if so + */ +void CubeCAN_Tick(void); + +/** + * @brief Sends a CAN message using the CubeCAN CAN handle, with the specified receive node, message ID, data payload, and size. + * @param handle Pointer to the CubeCAN CAN handle. + * @param rx_node The receive node identifier for the message. + * @param msg_id The message ID for the message. + * @param data Pointer to the data payload of the message. + * @param size The size of the data payload in bytes. + * @return HAL_StatusTypeDef indicating the success or failure of the operation. + * @note The size of the data payload must not exceed FDCAN_MAX_DATA_BYTES (64 bytes). If the size exceeds this limit, the function will return HAL_ERROR. + */ +HAL_StatusTypeDef CubeCAN_Send(CubeCAN_Handle *const handle, const GRCAN_NODE_ID rx_node, const GRCAN_MSG_ID msg_id, const void *const data, const uint8_t size); + +/** + * @brief Constructs a CAN message identifier from the given transmitting node ID, receiving node ID, and message ID. + * + * @param identifier Pointer to the CAN_Identifier structure containing the node and message IDs. + * + * @return The constructed 29-bit CAN message extended identifier. + */ +uint32_t CubeCAN_Construct_Identifier(const CAN_Identifier *const identifier); + +/** + * @brief Deconstructs a 29-bit CAN message extended identifier into its constituent transmitting node ID, receiving node ID, and message ID. + * + * @param message_id The 29-bit CAN message extended identifier to be deconstructed. + * + * @return A CAN_Identifier structure containing the deconstructed node and message IDs. + * + * @warning The function does not guarantee that the returned structure will represent a valid CAN message identifier. + * @warning The function does not support custom IDs. + */ +CAN_Identifier CubeCAN_Deconstruct_Identifier(const uint32_t message_id); + +#endif diff --git a/Lib/Peripherals/CubeCAN/README.md b/Lib/Peripherals/CubeCAN/README.md new file mode 100644 index 000000000..8b03ca245 --- /dev/null +++ b/Lib/Peripherals/CubeCAN/README.md @@ -0,0 +1,212 @@ +# Cube CAN + +Support for STM32CubeMX and CAN. See [`CUBEMX.md`](../../../CUBEMX.md) for background. + +## Summary + +Use STM32CubeMX to setup the CAN peripheral, then call `CubeCAN_Entrance()` on it after HAL initializes the peripheral. + +Save the handle that function returns and use that to send messages. Receive messages on your function callback. + +> [!IMPORTANT] +> We recommend that your received message callback function is a switch-case off of the message ID that does nothing more than writing to memory. +> +> Your `CubeCAN_RxCallback()` function must run quickly, it is inside of a low level ISR. +> Read the library code yourself if you wish to call a CubeCAN function from within an ISR, but you really should not. + +## Setup + +### 1. CMake + +Add `CUBE_CAN_LIB` as an interface target link library within your project. + +### 2. Configuration + +Add a file named `CubeCAN_Config.h` to your project, generally `Application/Inc/CubeCAN_Config.h`, that looks something like: + +```c +#ifndef CUBE_CAN_CONFIG_H +#define CUBE_CAN_CONFIG_H + +#define CUBEMX_CAN_TX_QUEUE_SIZE 16U +#define CUBEMX_CAN_MAX_INSTANCES 1U + +#endif +``` + +**Configuration Requirements:** + +- `CUBEMX_CAN_TX_QUEUE_SIZE`: Must be a power of two. Defines the transmission queue depth; when the queue fills, the oldest message is dropped and `CubeCAN_Send()` returns `HAL_BUSY`. +- `CUBEMX_CAN_MAX_INSTANCES`: Must be 1-3 depending on your chip's available FDCAN peripherals. Omit this definition to automatically select the maximum supported by your MCU. + +From there just ensure that you have configured CAN through STM32CubeMX and setup a global variable for each `CubeCAN_Handle` you wish to use. + +### 3. Entrance + +Make sure to `#include "CubeCAN.h"` where needed. Do not include `PrivateInc/internal.h` outside of the peripheral source files. + +At the end of your STM32CubeMX-generated `MX_FDCAN#_Init()` function, configure your filters as needed with `HAL_FDCAN_ConfigFilter()`. It is recommended to setup a filter to ignore messages not intended for your board. + +Within `main` after `MX_FDCAN#_Init()` and `LOGOMATIC()` are setup (see [Logomatic](../../Utils/Logomatic/README.md)), call `CubeCAN_Entrance()` saving the handle for global use later (one entrance and handle per peripheral). + +Ensure you have setup some mechanism to call `CubeCAN_Tick()` frequently (a STM32CubeMX timer interrupt is recommended). This function: + +- Sends one CAN message per bus per call (limiting to one prevents overwhelming the ISR) +- Must be called regularly for messages to be transmitted +- Can start running at any point in execution + +### 4. Operation + +#### Transmission + +Call `CubeCAN_Send()` to queue a message for transmission. Messages are queued in a lock-free ring buffer and processed in FIFO order. + +- **Returns `HAL_OK`**: Message successfully queued +- **Returns `HAL_BUSY`**: Transmission queue is full; the oldest queued message was dropped to make room for the new one +- **Returns `HAL_ERROR`**: Invalid parameters (null handle/data, size > 64 bytes, invalid frame format) + +Each call to `CubeCAN_Tick()` transmits one message from the queue per peripheral. If you need higher throughput, call `CubeCAN_Tick()` more frequently or increase `CUBEMX_CAN_TX_QUEUE_SIZE`. + +#### Reception + +Your provided callback `CubeCAN_RxCallback()` is triggered for messages that pass your configured filters. + +- Keep execution time minimal, you are in a hardware ISR +- Avoid calling CubeCAN functions or performing blocking operations +- Typical pattern is to decode the message and write results to shared memory (protected with atomic operations or critical sections as needed) + +## Architecture + +```mermaid +flowchart TD + classDef lib fill:#1e40af,stroke:#bfdbfe,color:#ffffff; + classDef cubemx fill:#5b21b6,stroke:#ddd6fe,color:#ffffff; + classDef internal fill:#F0E442,stroke:#333,color:#333; + classDef other fill:#c2410c,stroke:#fed7aa,color:#ffffff; + + MXInit(["MX_FDCAN#_Init()"]) -->Entrance("CubeCAN_Entrance()") + MXInit --> Filter("HAL_FDCAN_\nConfigFilter()") + Filter --> Filter + Filter --> Entrance + Entrance --> Choice{"Functions"} + Choice --> Exit(["CubeCAN_Exit()"]) + Exit -.-> |"AVOID"| Entrance + Choice --> Send(["CubeCAN_Send()"]) + Choice --> |"Call Often\n(User Setup)"| Tick(["CubeCAN_Tick()"]) + Send -.-> Queue("CubeCAN_Private_QueueTx()") + Data -.-> Tick + Queue -.-> Data{"Internal Queue"} + Tick -.-> Tx(["CubeCAN_Private\n_SendQueuedMessage()"]) + Data -.-> Tx + Tx -.->|"HAL_FDCAN_\nAddMessageToTxFifoQ()"| Sent(["Transmitted over bus!"]) + Choice -.->|"HAL Interrupt"| Recieved + Recieved(["Recieved over bus!"]) -.->|"HAL_FDCAN_\nRxFifo0Callback()\n\nHAL_FDCAN_\nGetRxMessage()"| Get(["CubeCAN_RxCallback()"]) + + class MXInit cubemx; + class Filter cubemx; + class Entrance lib; + class Exit lib; + class Send lib; + class Queue internal; + class Tick lib; + class Tx internal; + class Choice other; + class Data other; + class Sent other; + class Recieved other; + class Get lib; +``` + +## Advanced + +### Interrupt Safety + +CubeCAN is designed to handle concurrent access from multiple execution contexts (main loop and ISR). + +#### Overview + +- `tx_head` and `tx_tail` are atomic `uint32_t` variables that implement a lock-free ring buffer +- `CubeCAN_Send()` and `CubeCAN_Tick()` use critical sections (interrupt blocking) when necessary +- Atomic operations use `memory_order_relaxed` within ISRs and `memory_order_release`/`memory_order_acquire` for inter-context synchronization +- The queue size must be a power of two, allowing fast wraparound via bitwise AND instead of expensive modulo operations + +#### Critical Sections + +Interrupt protection is used only in: + +- `CubeCAN_Private_QueueTx()` while updating the queue +- `HAL_FDCAN_RxFifo0Callback` while reading handle state + +#### ISR Interaction + +- Safe to call `CubeCAN_Send()` from `main()` or any ISR +- Safe to call `CubeCAN_Tick()` from `main()` or any ISR +- Do not call any CubeCAN function from `CubeCAN_RxCallback()`, use it only to write data to shared memory (with concurrency safety as needed) + +### Filters + +You are responsible for configuring CAN filters. + +Use `HAL_FDCAN_ConfigFilter()` in `MX_FDCAN#_Init()` to setup any filters you need. Configure the number and type of filters in STM32CubeMX. + +#### Best Practices + +Create a filter to accept only messages intended for your board + +The library uses only `HAL_FDCAN_RxFifo0Callback()` for message reception, you can define your own `HAL_FDCAN_RxFifo1Callback()` for additional needs + +#### Message Overflow + +If your received message FIFO overflows (messages arrive faster than they're processed), the library logs a warning and drops the oldest messages. + +Improve this by increasing callback efficiency or adding more CAN filters to reduce unwanted messages. + +### Rx Callback Context + +You may consolidate multiple receive callbacks into a single function by passing different context data to each handle. + +Use the context union for this, it can point to arbitrary data or to a bus ID. It is your responsibility to be consistent the peripheral will just return input. + +### Tx Message Marker + +Currently we do not do anything cool with this, but we could! + +If you would like to see this do something please open an Issue (and if you want to code it, a Pull Request as well) + +### Assertions + +We currently provide compile time static assertions to validate: + +- Memory alignment of 32 bits for `tx_head` and `tx_tail` +- Struct alignment of >=4 bytes for `GRCAN_Private_TxMessage` and `CubeCAN_Private_Handle` +- Union compatibility for types `GRCAN_BUS_ID` and `void*` +- Lock free atomic operations for integers and booleans guaranteed by the compiler +- `CUBEMX_CAN_TX_QUEUE_SIZE` is a power of two and non-zero (allows faster ring-buffer wrapping) +- `CUBEMX_CAN_MAX_INSTANCES` requires management of 1 to 3 interfaces (optional parameter, otherwise automatically chooses the maximum possible) + +### Globals + +CubeCAN maintains a global private array of handles (sized to `CUBEMX_CAN_MAX_INSTANCES`). + +- Each handle is 28 bytes (overhead) +- Plus transmission queue: `CUBEMX_CAN_TX_QUEUE_SIZE` times `sizeof(GRCAN_Private_TxMessage)` (4 byte header and 64 bytes of data, at print time) + +If your chip supports multiple FDCAN peripherals but you need fewer, set `CUBEMX_CAN_MAX_INSTANCES` lower to avoid wasting memory on unused handles. Similarly, reduce `CUBEMX_CAN_TX_QUEUE_SIZE` if you send infrequently and have frequent ticks. + +### Timer Rate Checker + +In the interest of preventing improper bus actions, during a period after early boot and ending a few seconds later we monitor how often `CubeCAN_Tick()` is callled to verify the sender is not going to bring down the bus. + +Ideally, you should call `CubeCAN_Tick()` about every `625 us` and you will not run into any issues with it. + +If for some reason you wish to go past that, such as some extreme debugging case, then you may define `RELAXED_TIMER_GATE` inside of your `CubeCAN_Config.h` file and this will turn the error handler call into a logged violation. + +If for some reason you wish to go even farther pas that, such as some extreme debugging case where you do not want to even check for violations, then you may define `DISABLED_TIMER_GATE` inside of your `CubeCAN_Config.h` file and this will skip running the violation check at all. + +However, you really should fix your timers so that it is not possible to spam the bus, needing to use either of these flags means you should reevaluate other settings, try sending larger messages instead of many smaller ones and increasing `CUBEMX_CAN_TX_QUEUE_SIZE`. + +## Troubleshooting + +TODO + +As issues are found we will extend this section. For now, please contact the Firmware Manager if you are unable to get this working. diff --git a/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h b/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h new file mode 100644 index 000000000..7abb7899d --- /dev/null +++ b/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h @@ -0,0 +1,188 @@ +#include +#include +#include + +#include "CubeCAN_Config.h" +#include "Logomatic.h" +#include "Stringification.h" +#include "fdcan.h" +#include "main.h" + +#ifndef PRIVATE_CUBE_MX_CAN_H +#define PRIVATE_CUBE_MX_CAN_H + +/** + * @brief Maximum number of CubeCAN CAN instances supported by the library. + * + * This value is determined based on the available FDCAN peripherals in the STM32 microcontroller. + * + * @warning You can override it, but you should only reduce it to a lower value, there is no point to increase it beyond what their is hardware for. + */ +#ifndef CUBEMX_CAN_MAX_INSTANCES +#if (defined(FDCAN3) || defined(CAN3)) && (defined(FDCAN2) || defined(CAN2)) && (defined(FDCAN1) || defined(CAN1)) +#define CUBEMX_CAN_MAX_INSTANCES (3U) +#elif (defined(FDCAN2) || defined(CAN2)) && (defined(FDCAN1) || defined(CAN1)) +#define CUBEMX_CAN_MAX_INSTANCES (2U) +#elif (defined(FDCAN1) || defined(CAN1)) +#define CUBEMX_CAN_MAX_INSTANCES (1U) +#else +#error "No CAN or FDCAN instances defined. Please check your CubeCAN configuration." +#endif +#endif + +/** + * @brief Mask for the transmission queue index, used to wrap around the queue when it reaches its maximum size. + * + * Application code should not use this macro directly. It is intended for internal use only + * + * @warning The transmission queue size must be a power of two for this mask to work correctly + * @warning The transmission queue size must be defined as CUBEMX_CAN_TX_QUEUE_SIZE in the CubeCAN CAN configuration header file + */ +#define TX_QUEUE_MASK (CUBEMX_CAN_TX_QUEUE_SIZE - 1U) + +/** + * @brief Rx events mask for FDCAN notifications. + * + * This mask is used to enable notifications for new messages, full FIFO, and message lost events in the FDCAN peripheral. + */ +#define FDCAN_IT_RX_EVENTS (FDCAN_IT_RX_FIFO0_NEW_MESSAGE | FDCAN_IT_RX_FIFO0_FULL | FDCAN_IT_RX_FIFO0_MESSAGE_LOST) + +/** + * @brief Maximum number of data bytes in an FDCAN message. + * + * This macro defines the maximum number of data bytes that can be included in an FDCAN message. + */ +#define FDCAN_MAX_DATA_BYTES (64U) + +/** + * @brief Absolute maximum number of CubeCAN_Tick invocations per millisecond. + * + * This value is used to detect if the CubeCAN_Tick function is being called too frequently, which could indicate a problem with the invocation method. + * + * @note If you need to think about increasing this, you need to evaluate + * @warning If this value is exceeded, the system will trigger an error handler to prevent potential issues but this is checked only during a short duration starting after early boot and ending after + * a few seconds + */ +#define ABSOLUTE_MAX_INVOCATIONS_PER_TICK 3U + +/** + * @brief Transmission CAN message structure + * + * This structure is used to represent a transmission CAN message, which consists of a header and data payload. The header contains information about the message, such as its identifier, data length, + * and other relevant parameters. The data payload contains the actual data bytes of the message. + * + * @warning The length of the data array may be longer than the actual CAN message. + */ +typedef struct { + FDCAN_TxHeaderTypeDef tx_header; + uint8_t data[FDCAN_MAX_DATA_BYTES]; +} GRCAN_Private_TxMessage; + +/** + * @brief CubeCAN CAN handle structure + * + * This structure is used to represent a CubeCAN CAN handle, which consists of a pointer to the FDCAN handle, a configuration structure, a transmission queue, and other relevant parameters. + * + * @warning Use the provided API functions to interact with the CubeCAN CAN handle. + * @warning The structure is intended for internal use only and should not be accessed directly by user code. + */ +struct CubeCAN_Private_Handle { + /// @brief Pointer to the FDCAN handle associated with this CubeCAN CAN handle. + FDCAN_HandleTypeDef *hfdcan; + /// @brief Configuration structure for the CubeCAN CAN handle, containing the receive callback and user context. + CubeCAN_Config config; + /// @brief Transmission queue for the CubeCAN CAN handle, containing the messages to be transmitted. + GRCAN_Private_TxMessage tx_queue[CUBEMX_CAN_TX_QUEUE_SIZE]; + /// @brief Atomic head index for the transmission queue, indicating the next message to be transmitted. + _Atomic uint32_t tx_head; + /// @brief Atomic tail index for the transmission queue, indicating the next available slot for a new message. + _Atomic uint32_t tx_tail; + /// @brief Flag indicating whether the CubeCAN CAN handle has been started. + bool started; +}; + +/** + * @brief Array of CubeCAN CAN handles, one for each supported instance. + * @note The number of instances is defined by CUBEMX_CAN_MAX_INSTANCES. + */ +extern struct CubeCAN_Private_Handle handles[CUBEMX_CAN_MAX_INSTANCES]; + +/** + * @brief Sends a queued message from the CubeCAN CAN handle. + * @param handle Pointer to the CubeCAN CAN handle. + * @return HAL_StatusTypeDef indicating the success or failure of the operation. + */ +HAL_StatusTypeDef CubeCAN_Private_SendQueuedMessage(const CubeCAN_Handle *const handle); + +/** + * @brief Attempts to recover the FDCAN peripheral associated with the given CubeCAN CAN handle. + * + * Handles recovery from bus off or restricted operation mode by reinitializing the FDCAN peripheral and restoring its configuration. + * + * @param handle Pointer to the CubeCAN CAN handle associated with the FDCAN peripheral to be recovered. + * @return HAL_StatusTypeDef indicating the success or failure of the recovery operation. + */ +HAL_StatusTypeDef CubeCAN_Private_RecoverPeripheral(const CubeCAN_Handle *const handle); + +/** + * @brief Converts a Data Length Code (DLC) to the corresponding number of data bytes. + * @param dlc The Data Length Code to be converted. + * @return The number of data bytes corresponding to the given DLC. + * @warning The DLC value must be between 0 and 15, inclusive. Values outside this range will result in undefined behavior. + */ +bool CubeCAN_Private_IsDisabled(const CubeCAN_Handle *const handle); + +/** + * @brief Queues a transmission message for the CubeCAN CAN handle, allowing for asynchronous message transmission. + * @param handle Pointer to the CubeCAN CAN handle. + * @param message Pointer to the GRCAN transmission message structure. + * @return HAL_StatusTypeDef if the parameters are non-null or the queue is full. + */ +HAL_StatusTypeDef CubeCAN_Private_QueueTx(CubeCAN_Handle *const handle, const GRCAN_Private_TxMessage *const message); + +/** + * @brief Converts a Data Length Code (DLC) to the corresponding number of bytes. + * @param dlc The Data Length Code to be converted. + * @return The number of bytes corresponding to the given DLC. + * @warning The DLC value must be between 0 and 15, inclusive. Values outside this range will result in undefined behavior. + */ +uint8_t CubeCAN_Private_DlcToBytes(const uint32_t dlc); + +/** + * @brief Converts a number of bytes to the corresponding Data Length Code (DLC). + * @param bytes The number of bytes to be converted. + * @return The Data Length Code corresponding to the given number of bytes. + * @warning The number of bytes must be between 0 and 8, inclusive. Values outside this range will result in undefined behavior. + */ +uint8_t CubeCAN_Private_BytesToDlc(const uint8_t bytes); + +/** + * @brief Initializes a CubeCAN CAN handle with the given FDCAN handle and configuration. + * @param hfdcan Pointer to the FDCAN handle. + * @param config Pointer to the CubeCAN configuration structure. + * @return Pointer to the initialized CubeCAN CAN handle, or NULL if initialization fails. + */ +CubeCAN_Handle *CubeCAN_Private_Init(FDCAN_HandleTypeDef *hfdcan, CubeCAN_Config *config); + +/** + * @brief Starts the CubeCAN CAN handle, enabling message transmission and reception. + * @param handle Pointer to the CubeCAN CAN handle. + * @return HAL_StatusTypeDef indicating the success or failure of the operation. + */ +HAL_StatusTypeDef CubeCAN_Private_Start(CubeCAN_Handle *const handle); + +/** + * @brief Stops the CubeCAN CAN handle, disabling message transmission and reception. + * @param handle Pointer to the CubeCAN CAN handle. + * @return HAL_StatusTypeDef indicating the success or failure of the operation. + */ +HAL_StatusTypeDef CubeCAN_Private_Stop(CubeCAN_Handle *const handle); + +/** + * @brief Releases the resources associated with the CubeCAN CAN handle. + * @param handle Pointer to the CubeCAN CAN handle. + * @return HAL_StatusTypeDef indicating the success or failure of the operation. + */ +HAL_StatusTypeDef CubeCAN_Private_Release(CubeCAN_Handle *const handle); + +#endif diff --git a/Lib/Peripherals/CubeCAN/Src/can_assert.c b/Lib/Peripherals/CubeCAN/Src/can_assert.c new file mode 100644 index 000000000..107db57b6 --- /dev/null +++ b/Lib/Peripherals/CubeCAN/Src/can_assert.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +#include "CriticalSection.h" +#include "CubeCAN.h" +#include "CubeCAN_Config.h" +#include "GRCAN_BUS_ID.h" +#include "PrivateInc/internal.h" +#include "main.h" + +static_assert(sizeof(((struct CubeCAN_Private_Handle *)0)->tx_head) == sizeof(uint32_t), "CubeCAN internal tx_head must be 32 bits"); +static_assert(sizeof(((struct CubeCAN_Private_Handle *)0)->tx_tail) == sizeof(uint32_t), "CubeCAN internal tx_tail must be 32 bits"); + +static_assert(alignof(GRCAN_Private_TxMessage) >= 4, "CubeCAN internal GRCAN_Private_TxMessage must have at least 4-byte alignment"); +static_assert(alignof(struct CubeCAN_Private_Handle) >= 4, "CubeCAN internal CubeCAN_Private_Handle must have at least 4-byte alignment"); + +static_assert((offsetof(struct CubeCAN_Private_Handle, tx_head) % 4U) == 0U, "CubeCAN internal atomic tx_head is unaligned"); +static_assert((offsetof(struct CubeCAN_Private_Handle, tx_tail) % 4U) == 0U, "CubeCAN internal atomic tx_tail is unaligned"); + +static_assert(ATOMIC_INT_LOCK_FREE == 2, "CubeCAN internal ATOMIC_INT_LOCK_FREE must be enabled for atomic operations"); +static_assert(ATOMIC_BOOL_LOCK_FREE == 2, "CubeCAN internal ATOMIC_BOOL_LOCK_FREE must be enabled for atomic operations"); + +static_assert((CUBEMX_CAN_TX_QUEUE_SIZE > 0) && !(CUBEMX_CAN_TX_QUEUE_SIZE & (CUBEMX_CAN_TX_QUEUE_SIZE - 1U)), "CUBEMX_CAN_TX_QUEUE_SIZE must be a power of two and greater than zero"); +static_assert(CUBEMX_CAN_MAX_INSTANCES >= 1 && CUBEMX_CAN_MAX_INSTANCES <= 3, "CUBEMX_CAN_MAX_INSTANCES must be configured between 1 and 3 inclusive"); + +static_assert(sizeof(GRCAN_BUS_ID) <= sizeof(void *), "GRCAN_BUS_ID must fit within a pointer-sized type to support being unioned with a pointer in CubeCAN_Config"); diff --git a/Lib/Peripherals/CubeCAN/Src/can_global.c b/Lib/Peripherals/CubeCAN/Src/can_global.c new file mode 100644 index 000000000..254508f25 --- /dev/null +++ b/Lib/Peripherals/CubeCAN/Src/can_global.c @@ -0,0 +1,9 @@ +#include +#include +#include + +#include "CriticalSection.h" +#include "CubeCAN.h" +#include "PrivateInc/internal.h" + +struct CubeCAN_Private_Handle handles[CUBEMX_CAN_MAX_INSTANCES] = {0}; diff --git a/Lib/Peripherals/CubeCAN/Src/can_init.c b/Lib/Peripherals/CubeCAN/Src/can_init.c new file mode 100644 index 000000000..c3ef59e68 --- /dev/null +++ b/Lib/Peripherals/CubeCAN/Src/can_init.c @@ -0,0 +1,209 @@ +#include +#include +#include +#include + +#include "CriticalSection.h" +#include "CubeCAN.h" +#include "Logomatic.h" +#include "PrivateInc/internal.h" +#include "main.h" + +CubeCAN_Handle *CubeCAN_Entrance(FDCAN_HandleTypeDef *hfdcan, CubeCAN_Config *config) +{ + CubeCAN_Handle *handle = CubeCAN_Private_Init(hfdcan, config); + + if (handle == NULL) { + LOGOMATIC("CubeCAN_Entrance: failed to initialize handle\n"); + return NULL; + } + + if (CubeCAN_Private_Start(handle) != HAL_OK) { + LOGOMATIC("CubeCAN_Entrance: failed to start handle\n"); + CubeCAN_Private_Release(handle); + return NULL; + } + + return handle; +} + +HAL_StatusTypeDef CubeCAN_Exit(CubeCAN_Handle *handle) +{ + if (handle == NULL) { + LOGOMATIC("CubeCAN_Exit: invalid null parameter\n"); + return HAL_ERROR; + } + + HAL_StatusTypeDef status = CubeCAN_Private_Stop(handle); + + if (status != HAL_OK) { + LOGOMATIC("CubeCAN_Exit: failed to stop FDCAN instance\n"); + return status; + } + + return CubeCAN_Private_Release(handle); +} + +CubeCAN_Handle *CubeCAN_Private_Init(FDCAN_HandleTypeDef *hfdcan, CubeCAN_Config *config) +{ + if (hfdcan == NULL || config == NULL) { + LOGOMATIC("CubeCAN_Private_Init: invalid null parameters\n"); + return NULL; + } + + CubeCAN_Handle *handle = NULL; + uint8_t free_handle_index = (uint8_t)-1; + + CRITICAL_SECTION + { + for (uint8_t i = 0U; i < CUBEMX_CAN_MAX_INSTANCES; ++i) { + if (handles[i].hfdcan == hfdcan) { + handle = &handles[i]; + break; + } else if (handles[i].hfdcan == NULL && free_handle_index == (uint8_t)-1) { + free_handle_index = i; + } + } + + if (handle != NULL) { + LOGOMATIC("CubeCAN_Private_Init: handle already initialized for this FDCAN instance\n"); + return NULL; + } else if (free_handle_index != (uint8_t)-1) { + handle = &handles[free_handle_index]; + memset(handle, 0, sizeof(*handle)); + handle->hfdcan = hfdcan; + handle->config = *config; + atomic_init(&handle->tx_head, 0U); + atomic_init(&handle->tx_tail, 0U); + handle->started = false; + } + } + + if (handle == NULL) { + LOGOMATIC("CubeCAN_Private_Init: no free handle slots\n"); + return NULL; + } + + if (HAL_FDCAN_ActivateNotification(hfdcan, FDCAN_IT_RX_EVENTS, 0U) != HAL_OK) { + LOGOMATIC("CubeCAN_Private_Init: failed to activate RX notifications\n"); + CRITICAL_SECTION + { + memset(handle, 0, sizeof(*handle)); + } + return NULL; + } + + return handle; +} + +HAL_StatusTypeDef CubeCAN_Private_Release(CubeCAN_Handle *handle) +{ + if (handle == NULL) { + return HAL_ERROR; + } + + FDCAN_HandleTypeDef *hfdcan_to_stop = NULL; + + CRITICAL_SECTION + { + if (handle->hfdcan != NULL) { + hfdcan_to_stop = handle->hfdcan; + handle->hfdcan = NULL; + handle->started = false; + } + } + + if (hfdcan_to_stop == NULL) { + return HAL_ERROR; + } + + HAL_StatusTypeDef status = HAL_FDCAN_Stop(hfdcan_to_stop); + + if (status == HAL_OK) { + status = HAL_FDCAN_DeactivateNotification(hfdcan_to_stop, FDCAN_IT_RX_EVENTS); + } + + if (status != HAL_OK) { + LOGOMATIC("CubeCAN_Release: peripheral deactivation failed\n"); + + CRITICAL_SECTION + { + handle->hfdcan = hfdcan_to_stop; + } + + return status; + } + + CRITICAL_SECTION + { + memset(handle, 0, sizeof(*handle)); + } + + return HAL_OK; +} + +HAL_StatusTypeDef CubeCAN_Private_Start(CubeCAN_Handle *const handle) +{ + if (handle == NULL) { + return HAL_ERROR; + } + + bool dynamic_start = false; + FDCAN_HandleTypeDef *hfdcan = NULL; + + CRITICAL_SECTION + { + if (handle->hfdcan != NULL && !handle->started) { + hfdcan = handle->hfdcan; + dynamic_start = true; + } + } + + if (!dynamic_start) { + return HAL_ERROR; + } + + HAL_StatusTypeDef status = HAL_FDCAN_Start(hfdcan); + + if (status == HAL_OK) { + CRITICAL_SECTION + { + handle->started = true; + } + } + + return status; +} + +HAL_StatusTypeDef CubeCAN_Private_Stop(CubeCAN_Handle *const handle) +{ + if (handle == NULL) { + return HAL_ERROR; + } + + bool dynamic_stop = false; + FDCAN_HandleTypeDef *hfdcan = NULL; + + CRITICAL_SECTION + { + if (handle->hfdcan != NULL && handle->started) { + hfdcan = handle->hfdcan; + dynamic_stop = true; + } + } + + if (!dynamic_stop) { + return HAL_ERROR; + } + + HAL_StatusTypeDef status = HAL_FDCAN_Stop(hfdcan); + + if (status == HAL_OK) { + CRITICAL_SECTION + { + handle->started = false; + } + } + + return status; +} \ No newline at end of file diff --git a/Lib/Peripherals/CubeCAN/Src/can_rx.c b/Lib/Peripherals/CubeCAN/Src/can_rx.c new file mode 100644 index 000000000..f37912f01 --- /dev/null +++ b/Lib/Peripherals/CubeCAN/Src/can_rx.c @@ -0,0 +1,62 @@ +#include +#include + +#include "CriticalSection.h" +#include "CubeCAN.h" +#include "Logomatic.h" +#include "PrivateInc/internal.h" +#include "Unused.h" +#include "main.h" + +void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) +{ + if (RxFifo0ITs & FDCAN_IT_RX_FIFO0_MESSAGE_LOST) { + LOGOMATIC("WARNING: CAN Rx FIFO is overflowing, messages are being lost!\n"); + } + + if (hfdcan == NULL) { + return; + } + + CubeCAN_Config_Context context; + CubeCAN_RxCallback rx_callback = NULL; + bool is_started = false; + + CRITICAL_SECTION + { + for (uint8_t i = 0U; i < CUBEMX_CAN_MAX_INSTANCES; ++i) { + if (handles[i].hfdcan == hfdcan) { + context = handles[i].config.context; + rx_callback = handles[i].config.rx_callback; + is_started = handles[i].started; + break; + } + } + } + + if (!is_started || rx_callback == NULL) { + FDCAN_RxHeaderTypeDef dump_header; + uint8_t dump_data[FDCAN_MAX_DATA_BYTES]; + + while (HAL_FDCAN_GetRxFifoFillLevel(hfdcan, FDCAN_RX_FIFO0) > 0U) { + (void)HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &dump_header, dump_data); + } + + LOGOMATIC("HAL_FDCAN_RxFifo0Callback: Message dropped, unmapped handle or instance stopped\n"); + return; + } + + FDCAN_RxHeaderTypeDef rx_header; + uint8_t rx_data[FDCAN_MAX_DATA_BYTES]; + + while (HAL_FDCAN_GetRxFifoFillLevel(hfdcan, FDCAN_RX_FIFO0) > 0U) { + if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &rx_header, rx_data) == HAL_OK) { + CAN_Identifier id = CubeCAN_Deconstruct_Identifier(rx_header.Identifier); + uint8_t size = CubeCAN_Private_DlcToBytes(rx_header.DataLength); + rx_callback(&context, &id, rx_data, size); + } else { + LOGOMATIC("HAL_FDCAN_RxFifo0Callback: Failed to get Rx message from FIFO\n"); + break; + } + } +} diff --git a/Lib/Peripherals/CubeCAN/Src/can_tx.c b/Lib/Peripherals/CubeCAN/Src/can_tx.c new file mode 100644 index 000000000..cff1bdce0 --- /dev/null +++ b/Lib/Peripherals/CubeCAN/Src/can_tx.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include + +#include "CriticalSection.h" +#include "CubeCAN.h" +#include "CubeCAN_Config.h" +#include "Logomatic.h" +#include "PrivateInc/internal.h" +#include "main.h" + +void CubeCAN_Private_RateChecker(void) +{ + static uint32_t last_tick = 0; + static uint32_t call_count_in_tick = 0; + static uint32_t violations = 0; + + const uint32_t current_tick = HAL_GetTick(); + + if (current_tick == last_tick) { + call_count_in_tick++; + } else { + last_tick = current_tick; + call_count_in_tick = 1; + } + + if (current_tick > 10U && call_count_in_tick > ABSOLUTE_MAX_INVOCATIONS_PER_TICK) { + LOGOMATIC("CubeCAN_Tick: called too frequently (%" PRIu32 " times in 1 ms), you NEED to fix your timers!\tViolation %" PRIu32 "\n", call_count_in_tick, ++violations); +#ifdef RELAXED_TIMER_GATE // Hidden feature flag if you REALLY know what you are doing but you should fix your timers instead +#warning "CubeCAN_Tick: Compiled with RELAXED_TIMER_GATE, this is a hidden feature flag that allows you to ignore the timer violation but you really should fix your timers" +#else + Error_Handler(); +#endif + } +} + +void CubeCAN_Tick(void) +{ +#ifdef DISABLED_TIMER_GATE +#warning "CubeCAN_Tick: Compiled with DISABLED_TIMER_GATE, this is a hidden feature flag that disables the timer violation check but you really should fix your timers" + static bool rate_checker_gate = true; +#else + static bool rate_checker_gate = false; +#endif + + if (__builtin_expect(!rate_checker_gate, 0)) { + CubeCAN_Private_RateChecker(); + + if (HAL_GetTick() > 5000U) { // Disable check after 5 seconds + rate_checker_gate = true; + } + } + + for (uint8_t i = 0U; i < CUBEMX_CAN_MAX_INSTANCES; ++i) { + CubeCAN_Handle *handle = &handles[i]; + + if (handle->hfdcan == NULL || !handle->started) { + continue; + } + + if (CubeCAN_Private_IsDisabled(handle)) { + LOGOMATIC("CubeCAN_Tick: currently in restricted operation mode\n"); + if (CubeCAN_Private_RecoverPeripheral(handle) != HAL_OK) { + LOGOMATIC("CubeCAN_Tick %d: failed to recover peripheral\n", (int)i); + continue; + } + } + + (void)CubeCAN_Private_SendQueuedMessage(handle); + } +} + +HAL_StatusTypeDef CubeCAN_Send(CubeCAN_Handle *const handle, const GRCAN_NODE_ID rx_node, const GRCAN_MSG_ID msg_id, const void *const data, const uint8_t size) +{ + if (handle == NULL || data == NULL || size > FDCAN_MAX_DATA_BYTES) { + return HAL_ERROR; + } + + const uint32_t dlc = CubeCAN_Private_BytesToDlc(size); + if (dlc == FDCAN_DLC_BYTES_0 && size != 0) { + LOGOMATIC("CubeCAN_Send: invalid data length code\n"); + return HAL_ERROR; + } + + uint32_t fdformat = 0; + uint32_t brs = 0; + switch (handle->hfdcan->Init.FrameFormat) { + case FDCAN_FRAME_CLASSIC: + fdformat = FDCAN_CLASSIC_CAN; + brs = FDCAN_BRS_OFF; + break; + case FDCAN_FRAME_FD_NO_BRS: + fdformat = FDCAN_FD_CAN; + brs = FDCAN_BRS_OFF; + break; + case FDCAN_FRAME_FD_BRS: + fdformat = FDCAN_FD_CAN; + brs = FDCAN_BRS_ON; + break; + default: + LOGOMATIC("CubeCAN_Send: unsupported frame format\n"); + return HAL_ERROR; + } + + const CAN_Identifier identifier_struct = {.tx_node_id = handle->config.sending_node_id, .rx_node_id = rx_node, .msg_id = msg_id}; + + const FDCAN_TxHeaderTypeDef header = {.BitRateSwitch = brs, + .DataLength = CubeCAN_Private_BytesToDlc(size), + .ErrorStateIndicator = FDCAN_ESI_ACTIVE, + .FDFormat = fdformat, + .Identifier = CubeCAN_Construct_Identifier(&identifier_struct), + .IdType = FDCAN_EXTENDED_ID, + .MessageMarker = 0U, // TODO We can do cool things with this to track transmission queue statistics + .TxEventFifoControl = FDCAN_NO_TX_EVENTS, + .TxFrameType = FDCAN_DATA_FRAME}; + + GRCAN_Private_TxMessage message = {.tx_header = header}; + memcpy(message.data, data, size); + + return CubeCAN_Private_QueueTx(handle, &message); +} + +HAL_StatusTypeDef CubeCAN_Private_QueueTx(CubeCAN_Handle *handle, const GRCAN_Private_TxMessage *message) +{ + if (handle == NULL || message == NULL) { + return HAL_ERROR; + } + + HAL_StatusTypeDef status = HAL_OK; + + CRITICAL_SECTION + { + const uint32_t tail = atomic_load_explicit(&handle->tx_tail, memory_order_relaxed); + uint32_t head = atomic_load_explicit(&handle->tx_head, memory_order_relaxed); + + if ((tail - head) >= CUBEMX_CAN_TX_QUEUE_SIZE) { + head++; // Drop oldest message to make room for the new one + atomic_store_explicit(&handle->tx_head, head, memory_order_relaxed); + status = HAL_BUSY; + } + handle->tx_queue[tail & TX_QUEUE_MASK] = *message; + + atomic_store_explicit(&handle->tx_tail, tail + 1U, memory_order_release); + } + + return status; +} + +HAL_StatusTypeDef CubeCAN_Private_SendQueuedMessage(const CubeCAN_Handle *const handle) +{ + if (handle == NULL || handle->hfdcan == NULL) { + LOGOMATIC("CubeCAN_Private_SendQueuedMessage: invalid null parameter\n"); + return HAL_ERROR; + } + + if (HAL_FDCAN_GetTxFifoFreeLevel(handle->hfdcan) == 0U) { + LOGOMATIC("CubeCAN_Private_SendQueuedMessage: Tx FIFO full, cannot send message\n"); + return HAL_BUSY; + } + + const uint32_t current_head = atomic_load_explicit(&handle->tx_head, memory_order_relaxed); + const uint32_t current_tail = atomic_load_explicit(&handle->tx_tail, memory_order_acquire); + + if (current_head == current_tail) { + return HAL_OK; + } + + const GRCAN_Private_TxMessage *const message_ptr = &handle->tx_queue[current_head & TX_QUEUE_MASK]; + + if (HAL_FDCAN_AddMessageToTxFifoQ(handle->hfdcan, &message_ptr->tx_header, message_ptr->data) != HAL_OK) { + return HAL_ERROR; + } + + atomic_store_explicit(&handle->tx_head, (current_head + 1U), memory_order_release); + return HAL_OK; +} diff --git a/Lib/Peripherals/CubeCAN/Src/can_utils.c b/Lib/Peripherals/CubeCAN/Src/can_utils.c new file mode 100644 index 000000000..f1959b8dc --- /dev/null +++ b/Lib/Peripherals/CubeCAN/Src/can_utils.c @@ -0,0 +1,190 @@ +#include +#include +#include + +#include "CriticalSection.h" +#include "CubeCAN.h" +#include "GRCAN_MSG_ID.h" +#include "GRCAN_NODE_ID.h" +#include "Logomatic.h" +#include "PrivateInc/internal.h" + +#define CAN_TX_NODE_SHIFT 20U +#define CAN_MSG_SHIFT 8U +#define CAN_RX_NODE_SHIFT 0U +#define CAN_NODE_MASK 0xFFU +#define CAN_MSG_MASK 0xFFFU + +uint32_t CubeCAN_Construct_Identifier(const CAN_Identifier *identifier) +{ + if (identifier == NULL) { + return 0U; + } + + const uint32_t tx_node_id = identifier->tx_node_id & CAN_NODE_MASK; + const uint32_t msg_id = identifier->msg_id & CAN_MSG_MASK; + const uint32_t rx_node_id = identifier->rx_node_id & CAN_NODE_MASK; + + return (tx_node_id << CAN_TX_NODE_SHIFT) | (msg_id << CAN_MSG_SHIFT) | (rx_node_id << CAN_RX_NODE_SHIFT); +} + +CAN_Identifier CubeCAN_Deconstruct_Identifier(const uint32_t message_id) +{ + const GRCAN_NODE_ID tx_node_id = (message_id >> CAN_TX_NODE_SHIFT) & CAN_NODE_MASK; + const GRCAN_MSG_ID msg_id = (message_id >> CAN_MSG_SHIFT) & CAN_MSG_MASK; + const GRCAN_NODE_ID rx_node_id = (message_id >> CAN_RX_NODE_SHIFT) & CAN_NODE_MASK; + + return (CAN_Identifier){.tx_node_id = tx_node_id, .msg_id = msg_id, .rx_node_id = rx_node_id}; +} + +uint8_t CubeCAN_Private_BytesToDlc(const uint8_t bytes) +{ + switch (bytes) { + case 0U: + return FDCAN_DLC_BYTES_0; + case 1U: + return FDCAN_DLC_BYTES_1; + case 2U: + return FDCAN_DLC_BYTES_2; + case 3U: + return FDCAN_DLC_BYTES_3; + case 4U: + return FDCAN_DLC_BYTES_4; + case 5U: + return FDCAN_DLC_BYTES_5; + case 6U: + return FDCAN_DLC_BYTES_6; + case 7U: + return FDCAN_DLC_BYTES_7; + case 8U: + return FDCAN_DLC_BYTES_8; + case 12U: + return FDCAN_DLC_BYTES_12; + case 16U: + return FDCAN_DLC_BYTES_16; + case 20U: + return FDCAN_DLC_BYTES_20; + case 24U: + return FDCAN_DLC_BYTES_24; + case 32U: + return FDCAN_DLC_BYTES_32; + case 48U: + return FDCAN_DLC_BYTES_48; + case 64U: + return FDCAN_DLC_BYTES_64; + default: + LOGOMATIC("CubeCAN_Private_BytesToDlc: Invalid byte count\n"); + return FDCAN_DLC_BYTES_0; + } +} + +uint8_t CubeCAN_Private_DlcToBytes(const uint32_t dlc) +{ + switch (dlc) { + case FDCAN_DLC_BYTES_0: + return 0U; + case FDCAN_DLC_BYTES_1: + return 1U; + case FDCAN_DLC_BYTES_2: + return 2U; + case FDCAN_DLC_BYTES_3: + return 3U; + case FDCAN_DLC_BYTES_4: + return 4U; + case FDCAN_DLC_BYTES_5: + return 5U; + case FDCAN_DLC_BYTES_6: + return 6U; + case FDCAN_DLC_BYTES_7: + return 7U; + case FDCAN_DLC_BYTES_8: + return 8U; + case FDCAN_DLC_BYTES_12: + return 12U; + case FDCAN_DLC_BYTES_16: + return 16U; + case FDCAN_DLC_BYTES_20: + return 20U; + case FDCAN_DLC_BYTES_24: + return 24U; + case FDCAN_DLC_BYTES_32: + return 32U; + case FDCAN_DLC_BYTES_48: + return 48U; + case FDCAN_DLC_BYTES_64: + return 64U; + default: + LOGOMATIC("CubeCAN_Private_DlcToBytes: Invalid DLC value\n"); + return 0U; + } +} + +bool CubeCAN_Private_IsDisabled(const CubeCAN_Handle *const handle) +{ + if (handle == NULL) { + return true; + } + + bool disabled = true; + + CRITICAL_SECTION + { + if (handle->hfdcan != NULL) { + const HAL_FDCAN_StateTypeDef state = HAL_FDCAN_GetState(handle->hfdcan); + FDCAN_ProtocolStatusTypeDef protocol_status = {0}; + + bool is_bus_off = false; + + if (HAL_FDCAN_GetProtocolStatus(handle->hfdcan, &protocol_status) == HAL_OK) { + is_bus_off = protocol_status.BusOff; + } + + disabled = (state == HAL_FDCAN_STATE_ERROR || state == HAL_FDCAN_STATE_READY || is_bus_off); + } + } + + return disabled; +} + +HAL_StatusTypeDef CubeCAN_Private_RecoverPeripheral(const CubeCAN_Handle *const handle) +{ + if (handle == NULL) { + return HAL_ERROR; + } + + HAL_StatusTypeDef status = HAL_OK; + + CRITICAL_SECTION + { + if (handle->hfdcan != NULL && handle->started) { + FDCAN_ProtocolStatusTypeDef protocol_status = {0}; + + if (HAL_FDCAN_GetProtocolStatus(handle->hfdcan, &protocol_status) == HAL_OK && protocol_status.BusOff) { + LOGOMATIC("CubeCAN_Private_RecoverPeripheral: CRITICAL BUS-OFF DETECTED. Forcing hardware reset...\n"); + + HAL_FDCAN_Stop(handle->hfdcan); + + status = HAL_FDCAN_Init(handle->hfdcan); + + if (status == HAL_OK) { + status = HAL_FDCAN_ActivateNotification(handle->hfdcan, FDCAN_IT_RX_EVENTS, 0U); + } + + if (status == HAL_OK) { + status = HAL_FDCAN_Start(handle->hfdcan); + } + + if (status != HAL_OK) { + LOGOMATIC("CubeCAN_Private_RecoverPeripheral: Aggressive hardware start failed!\n"); + } + } + + if (HAL_FDCAN_IsRestrictedOperationMode(handle->hfdcan)) { + LOGOMATIC("CubeCAN_Private_RecoverPeripheral: Forcing exit from restricted operation mode\n"); + HAL_FDCAN_ExitRestrictedOperationMode(handle->hfdcan); + } + } + } + + return status; +} diff --git a/Lib/Peripherals/CubeCAN/cube_can.cmake b/Lib/Peripherals/CubeCAN/cube_can.cmake new file mode 100644 index 000000000..0168d0d8c --- /dev/null +++ b/Lib/Peripherals/CubeCAN/cube_can.cmake @@ -0,0 +1,21 @@ +add_library(CUBE_CAN_LIB INTERFACE) + +target_include_directories(CUBE_CAN_LIB INTERFACE ${CMAKE_CURRENT_LIST_DIR}/Inc) + +target_sources( + CUBE_CAN_LIB + INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/Src/can_assert.c + ${CMAKE_CURRENT_LIST_DIR}/Src/can_global.c + ${CMAKE_CURRENT_LIST_DIR}/Src/can_init.c + ${CMAKE_CURRENT_LIST_DIR}/Src/can_rx.c + ${CMAKE_CURRENT_LIST_DIR}/Src/can_tx.c + ${CMAKE_CURRENT_LIST_DIR}/Src/can_utils.c +) + +target_link_libraries( + CUBE_CAN_LIB + INTERFACE + CANfigurator + LOGOMATIC_LIB +) diff --git a/Lib/Peripherals/TimedCAN/common.cmake b/Lib/Peripherals/TimedCAN/common.cmake index f2b227054..12a7334dd 100644 --- a/Lib/Peripherals/TimedCAN/common.cmake +++ b/Lib/Peripherals/TimedCAN/common.cmake @@ -11,3 +11,10 @@ target_sources( INTERFACE ${CMAKE_CURRENT_LIST_DIR}/Src/can.c ) + +set_target_properties( + PERIPHERAL_TIMEDCAN_LIB + PROPERTIES + DEPRECATION + "PERIPHERAL_TIMEDCAN_LIB is deprecated and should be replaced by CUBE_CAN_LIB." +) diff --git a/Lib/Utils/Logomatic/Inc/Logomatic.h b/Lib/Utils/Logomatic/Inc/Logomatic.h index d291f296a..21734ba64 100644 --- a/Lib/Utils/Logomatic/Inc/Logomatic.h +++ b/Lib/Utils/Logomatic/Inc/Logomatic.h @@ -3,8 +3,8 @@ #include "main.h" -#ifndef _LOGOMATIC_H_ -#define _LOGOMATIC_H_ +#ifndef LOGOMATIC_H +#define LOGOMATIC_H #if defined(ITM) && defined(LL_GPIO_MODE_ALTERNATE) typedef enum { @@ -113,10 +113,8 @@ void Setup_Logomatic(LogomaticConfig *config); */ #define LOGOMATIC(...) \ do { \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wdouble-promotion\""); \ - printf(__VA_ARGS__); \ - _Pragma("GCC diagnostic pop"); \ + _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wdouble-promotion\"") printf(__VA_ARGS__); \ + _Pragma("GCC diagnostic pop") \ } while (0) #else @@ -129,10 +127,8 @@ void Setup_Logomatic(LogomaticConfig *config); #define LOGOMATIC(...) \ do { \ if (0) { \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wdouble-promotion\""); \ - printf(__VA_ARGS__); \ - _Pragma("GCC diagnostic pop"); \ + _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wdouble-promotion\"") printf(__VA_ARGS__); \ + _Pragma("GCC diagnostic pop") \ } \ } while (0) #endif diff --git a/Lib/cmake/HOOTL.cmake b/Lib/cmake/HOOTL.cmake index e9fc8b613..5689d4ce9 100644 --- a/Lib/cmake/HOOTL.cmake +++ b/Lib/cmake/HOOTL.cmake @@ -29,6 +29,7 @@ add_compile_options( -Wvla -Wdouble-promotion -g + -DHOOTL_TEST ) if(APPLE) # MacOS has a different syntax for linker fatal warnings @@ -58,11 +59,13 @@ if(ADDRESS_SANITIZER) -fsanitize=address -fsanitize=undefined -fsanitize=leak + # -fsanitize=thread ) add_link_options( -fsanitize=address -fsanitize=undefined -fsanitize=leak + # -fsanitize=thread ) endif() endif()