diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b5c60d558..3e5fd3c28 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -9,6 +9,7 @@ "github.vscode-pull-request-github", "eamodio.gitlens", "davidanson.vscode-markdownlint", - "ms-vscode.vscode-serial-monitor" + "ms-vscode.vscode-serial-monitor", + "dcortes92.freemarker" ] } 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..bd67e5959 --- /dev/null +++ b/CUBEMXTESTING/Application/Inc/CANdler.h @@ -0,0 +1,8 @@ +#include "CubeCAN.h" + +#ifndef CANDLER_H +#define CANDLER_H + +void CANdler_Callback(CubeCAN_Handle *const handle, void *const user_context); + +#endif diff --git a/CUBEMXTESTING/Application/Inc/CubeCAN_Config.h b/CUBEMXTESTING/Application/Inc/CubeCAN_Config.h new file mode 100644 index 000000000..db74ce3b5 --- /dev/null +++ b/CUBEMXTESTING/Application/Inc/CubeCAN_Config.h @@ -0,0 +1,10 @@ +#ifndef CUBE_CAN_CONFIG_H +#define CUBE_CAN_CONFIG_H + +#include "GRCAN_NODE_ID.h" +#include "main.h" + +#define CUBEMX_CAN_TX_QUEUE_SIZE 32U +#define CUBEMX_SENDING_NODE_ID GRCAN_ECU + +#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/Inc/vcp_config.h b/CUBEMXTESTING/Application/Inc/vcp_config.h new file mode 100644 index 000000000..77a62a7f7 --- /dev/null +++ b/CUBEMXTESTING/Application/Inc/vcp_config.h @@ -0,0 +1,8 @@ +#ifndef VCP_CONFIG +#define VCP_CONFIG + +#define VCP_CONFIG_CLAIM_USART2 + +#define VCP_TX_BUFFER_SIZE 128 + +#endif diff --git a/CUBEMXTESTING/Application/Src/CANdler.c b/CUBEMXTESTING/Application/Src/CANdler.c new file mode 100644 index 000000000..524b95617 --- /dev/null +++ b/CUBEMXTESTING/Application/Src/CANdler.c @@ -0,0 +1,9 @@ +#include "CANdler.h" + +#include "CubeCAN.h" + +void CANdler_Callback(CubeCAN_Handle *const handle, void *const user_context) +{ + LOGOMATIC("CANdler_Callback: Received CAN message on handle %p with user context %p\n", (void *)handle, user_context); + // TODO +} diff --git a/CUBEMXTESTING/Application/Src/loop.c b/CUBEMXTESTING/Application/Src/loop.c new file mode 100644 index 000000000..5e1d17751 --- /dev/null +++ b/CUBEMXTESTING/Application/Src/loop.c @@ -0,0 +1,17 @@ +#include "loop.h" + +#include "Logomatic.h" +#include "main.h" + +void MainLoop(void) +{ + LL_GPIO_TogglePin(USER_LED_GPIO_Port, USER_LED_Pin); + + if (LL_GPIO_IsInputPinSet(USER_BUTTON_GPIO_Port, USER_BUTTON_Pin)) { + LL_GPIO_SetOutputPin(USER_LED_GPIO_Port, USER_LED_Pin); + } else { + LL_GPIO_ResetOutputPin(USER_LED_GPIO_Port, USER_LED_Pin); + } + + LOGOMATIC("Doing things!\n"); +} diff --git a/CUBEMXTESTING/CMakeLists.txt b/CUBEMXTESTING/CMakeLists.txt new file mode 100644 index 000000000..81566a37d --- /dev/null +++ b/CUBEMXTESTING/CMakeLists.txt @@ -0,0 +1,44 @@ +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/loop.c + Core/Src/fdcan.c + Core/Src/gpio.c + Core/Src/main.c + Core/Src/stm32g4xx_it.c + Core/Src/tim.c +) + +target_link_libraries(${PROJECT_NAME}_USER_CODE INTERFACE VCP_LIB CUBEMX_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..098aab57a --- /dev/null +++ b/CUBEMXTESTING/CUBEMXTESTING.ioc @@ -0,0 +1,199 @@ +#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 +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 +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 +File.Version=6 +GPIO.groupedBy=Group By Peripherals +KeepUserPlacement=false +Mcu.CPN=STM32G474RET6 +Mcu.Family=STM32G4 +Mcu.IP0=FDCAN1 +Mcu.IP1=FDCAN2 +Mcu.IP2=FDCAN3 +Mcu.IP3=NVIC +Mcu.IP4=RCC +Mcu.IP5=SYS +Mcu.IP6=TIM5 +Mcu.IPNb=7 +Mcu.Name=STM32G474R(B-C-E)Tx +Mcu.Package=LQFP64 +Mcu.Pin0=PC13 +Mcu.Pin1=PA5 +Mcu.Pin10=VP_TIM5_VS_ClockSourceINT +Mcu.Pin2=PB12 +Mcu.Pin3=PB13 +Mcu.Pin4=PA8 +Mcu.Pin5=PA15 +Mcu.Pin6=PB8-BOOT0 +Mcu.Pin7=PB9 +Mcu.Pin8=VP_SYS_VS_Systick +Mcu.Pin9=VP_SYS_VS_DBSignals +Mcu.PinsNb=11 +Mcu.ThirdPartyNb=0 +Mcu.UserConstants= +Mcu.UserName=STM32G474RETx +MxCube.Version=6.18.0 +MxDb.Version=DB.6.0.180 +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.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 +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 +PB8-BOOT0.Mode=FDCAN_Activate +PB8-BOOT0.Signal=FDCAN1_RX +PB9.Mode=FDCAN_Activate +PB9.Signal=FDCAN1_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 +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.IPParameters=Prescaler,PeriodNoDither +TIM5.PeriodNoDither=999 +TIM5.Prescaler=15999 +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..4b2cb4094 --- /dev/null +++ b/CUBEMXTESTING/Core/Inc/fdcan.h @@ -0,0 +1,58 @@ +/* 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..3c91c4dd6 --- /dev/null +++ b/CUBEMXTESTING/Core/Inc/gpio.h @@ -0,0 +1,49 @@ +/* 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..2ca19b1c6 --- /dev/null +++ b/CUBEMXTESTING/Core/Inc/main.h @@ -0,0 +1,85 @@ +/* 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 "stm32g4xx_ll_lpuart.h" +#include "stm32g4xx_ll_rcc.h" +#include "stm32g4xx_ll_usart.h" +/* 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 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/stm32_assert.h b/CUBEMXTESTING/Core/Inc/stm32_assert.h new file mode 100644 index 000000000..61631c41e --- /dev/null +++ b/CUBEMXTESTING/Core/Inc/stm32_assert.h @@ -0,0 +1,53 @@ +/* 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/CUBEMXTESTING/Core/Inc/stm32g4xx_hal_conf.h b/CUBEMXTESTING/Core/Inc/stm32g4xx_hal_conf.h new file mode 100644 index 000000000..33793dda8 --- /dev/null +++ b/CUBEMXTESTING/Core/Inc/stm32g4xx_hal_conf.h @@ -0,0 +1,380 @@ +/* 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..980377814 --- /dev/null +++ b/CUBEMXTESTING/Core/Inc/stm32g4xx_it.h @@ -0,0 +1,67 @@ +/* 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 TIM5_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/Src/fdcan.c b/CUBEMXTESTING/Core/Src/fdcan.c new file mode 100644 index 000000000..d1f107673 --- /dev/null +++ b/CUBEMXTESTING/Core/Src/fdcan.c @@ -0,0 +1,314 @@ +/* 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_NORMAL; + 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_NORMAL; + 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_NORMAL; + 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); + + /* 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); + + /* 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); + + /* 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); + + /* 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); + + /* 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); + + /* 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..5d1912e85 --- /dev/null +++ b/CUBEMXTESTING/Core/Src/gpio.c @@ -0,0 +1,335 @@ +/* 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 +*/ +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_0; + 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_1; + 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(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_13; + 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_14; + 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_3; + 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_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..ab6643b56 --- /dev/null +++ b/CUBEMXTESTING/Core/Src/main.c @@ -0,0 +1,233 @@ +/* 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" + +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ +#include "CANdler.h" +#include "CubeCAN.h" +#include "Logomatic.h" +#include "loop.h" +#include "tim.h" +#include "vcp.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 */ +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}; + +VCP_Config vcp_config = {.baud_rate = 2000000, + .clock_source = VCP_CLOCK_PCLK, + .gpio_tx_rx_pin_mask = LL_GPIO_PIN_2 | LL_GPIO_PIN_3, + .bus_port = VCP_Port_A, + .parity = VCP_Parity_None, + .prescaler = VCP_Prescalar_Div2, + .stop_bits = VCP_StopBits_1, + .oversampling = VCP_Oversampling_16, + .tx_fifo_threshold = VCP_Threshold_1_8, + .rx_fifo_threshold = VCP_Threshold_1_8, + .alternate_function = LL_GPIO_AF_7, + .rx_callback = NULL}; +/* 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(); + /* USER CODE BEGIN 2 */ + Logomatic_Init(&logomaticConfig); + VCP_Init(&vcp_config); + + CubeCAN_Config can_config = {.rx_callback = CANdler_Callback, .user_context = (void *)1}; + + CubeCAN_Init(&hfdcan1, &can_config); + /* USER CODE END 2 */ + + /* Infinite loop */ + /* USER CODE BEGIN WHILE */ + LOGOMATIC("Hello World!\n"); + while (1) { + /* USER CODE END WHILE */ + + /* USER CODE BEGIN 3 */ + } + /* 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) +{ + if (htim == &CAN_TIMER_HANDLE) { + 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 */ + printf("Assert failed! File %s on line %ld\n", file, line); + /* USER CODE END 6 */ +} +#endif /* USE_FULL_ASSERT */ diff --git a/CUBEMXTESTING/Core/Src/stm32g4xx_it.c b/CUBEMXTESTING/Core/Src/stm32g4xx_it.c new file mode 100644 index 000000000..b4fb77dff --- /dev/null +++ b/CUBEMXTESTING/Core/Src/stm32g4xx_it.c @@ -0,0 +1,217 @@ +/* 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 "main.h" +#include "stm32g4xx_it.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 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 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 */ +} + +/* 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..d1aa2063a --- /dev/null +++ b/CUBEMXTESTING/Core/Src/tim.c @@ -0,0 +1,112 @@ +/* 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 = 15999; + htim5.Init.CounterMode = TIM_COUNTERMODE_UP; + htim5.Init.Period = 999; + 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 */ + + /* 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/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/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..116b5485b 100644 --- a/Lib/GlobalShare/common.cmake +++ b/Lib/GlobalShare/common.cmake @@ -51,4 +51,14 @@ 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/CubeCAN/Inc/CubeCAN.h b/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h new file mode 100644 index 000000000..9b0eddd6e --- /dev/null +++ b/Lib/Peripherals/CubeCAN/Inc/CubeCAN.h @@ -0,0 +1,161 @@ +#include +#include +#include + +#include "CubeCAN_Config.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 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 void *const user_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 User-defined context pointer that can be passed to the receive callback function. This can be used to pass additional data or state information to the callback function. + void *user_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_Init and CubeCAN_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_OneShotInitStart(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 functions wraps CubeCAN_Stop and CubeCAN_Release into a single call. If either of those functions fails, this function will return the error code and the handle will + * not be released. + * @note This function can be used regardless of if CubeCAN_OneShotInitStart was used to initialize the handle. + */ +HAL_StatusTypeDef CubeCAN_OneShotReleaseStop(CubeCAN_Handle *handle); + +/** + * @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_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_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_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_Release(CubeCAN_Handle *const handle); + +/** + * @brief Adds a filter to the CubeCAN CAN handle, allowing for selective message reception based on the specified filter criteria. + * @param handle Pointer to the CubeCAN CAN handle. + * @param filter Pointer to the FDCAN filter configuration structure. + * @return HAL_StatusTypeDef indicating the success or failure of the operation. + */ +HAL_StatusTypeDef CubeCAN_AddFilter(const CubeCAN_Handle *const handle, const FDCAN_FilterTypeDef *const filter); + +/** + * @brief Processes periodic tasks for the CubeCAN CAN handle, such as handling timeouts and managing the transmission queue. + * @warning This function sends one can message per configured bus per call. Not calling it will simply not send any messages. + */ +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 Construct_CAN_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 Deconstruct_CAN_Identifier(const uint32_t message_id); + +/** + * @brief Builds an exact-match extended-ID filter for a given CAN identifier. + */ +HAL_StatusTypeDef CubeCANExt_BuildExtendedFilter(const CAN_Identifier *const identifier, const uint32_t filter_index, const uint32_t fifo, FDCAN_FilterTypeDef *const filter); + +#endif diff --git a/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h b/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h new file mode 100644 index 000000000..93e15d892 --- /dev/null +++ b/Lib/Peripherals/CubeCAN/Src/PrivateInc/internal.h @@ -0,0 +1,148 @@ +#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 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); + +#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..05d94fa2b --- /dev/null +++ b/Lib/Peripherals/CubeCAN/Src/can_assert.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +#include "CriticalSection.h" +#include "CubeCAN.h" +#include "CubeCAN_Config.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"); 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..469118e5a --- /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_OneShotInitStart(FDCAN_HandleTypeDef *hfdcan, CubeCAN_Config *config) +{ + CubeCAN_Handle *handle = CubeCAN_Init(hfdcan, config); + + if (handle == NULL) { + LOGOMATIC("CubeCAN_OneShotInit: failed to initialize handle\n"); + return NULL; + } + + if (CubeCAN_Start(handle) != HAL_OK) { + LOGOMATIC("CubeCAN_OneShotInit: failed to start handle\n"); + CubeCAN_Release(handle); + return NULL; + } + + return handle; +} + +HAL_StatusTypeDef CubeCAN_OneShotReleaseStop(CubeCAN_Handle *handle) +{ + if (handle == NULL) { + LOGOMATIC("CubeCAN_OneShotReleaseStop: invalid null parameter\n"); + return HAL_ERROR; + } + + HAL_StatusTypeDef status = CubeCAN_Stop(handle); + + if (status != HAL_OK) { + LOGOMATIC("CubeCAN_OneShotReleaseStop: failed to stop FDCAN instance\n"); + return status; + } + + return CubeCAN_Release(handle); +} + +CubeCAN_Handle *CubeCAN_Init(FDCAN_HandleTypeDef *hfdcan, CubeCAN_Config *config) +{ + if (hfdcan == NULL || config == NULL) { + LOGOMATIC("CubeCAN_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_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_Init: no free handle slots\n"); + return NULL; + } + + if (HAL_FDCAN_ActivateNotification(hfdcan, FDCAN_IT_RX_EVENTS, 0U) != HAL_OK) { + LOGOMATIC("CubeCAN_Init: failed to activate RX notifications\n"); + CRITICAL_SECTION + { + memset(handle, 0, sizeof(*handle)); + } + return NULL; + } + + return handle; +} + +HAL_StatusTypeDef CubeCAN_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_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_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; +} diff --git a/Lib/Peripherals/CubeCAN/Src/can_rx.c b/Lib/Peripherals/CubeCAN/Src/can_rx.c new file mode 100644 index 000000000..19c8f2899 --- /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; + } + + const void *user_context = NULL; + 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) { + user_context = handles[i].config.user_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 = Deconstruct_CAN_Identifier(rx_header.Identifier); + uint8_t size = CubeCAN_Private_DlcToBytes(rx_header.DataLength); + rx_callback(user_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..4ed3c207b --- /dev/null +++ b/Lib/Peripherals/CubeCAN/Src/can_tx.c @@ -0,0 +1,135 @@ +#include +#include +#include + +#include "CriticalSection.h" +#include "CubeCAN.h" +#include "Logomatic.h" +#include "PrivateInc/internal.h" +#include "main.h" + +void CubeCAN_Tick(void) +{ + 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 = Construct_CAN_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..b0233e1d6 --- /dev/null +++ b/Lib/Peripherals/CubeCAN/Src/can_utils.c @@ -0,0 +1,225 @@ +#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 Construct_CAN_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 Deconstruct_CAN_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}; +} + +HAL_StatusTypeDef CubeCANExt_BuildExtendedFilter(const CAN_Identifier *const identifier, const uint32_t filter_index, const uint32_t fifo, FDCAN_FilterTypeDef *const filter) +{ + if (identifier == NULL || filter == NULL) { + return HAL_ERROR; + } + + filter->IdType = FDCAN_EXTENDED_ID; + filter->FilterIndex = filter_index; + filter->FilterType = FDCAN_FILTER_MASK; + filter->FilterConfig = fifo; + filter->FilterID1 = Construct_CAN_Identifier(identifier); + filter->FilterID2 = 0x1FFFFFFFU; + + return HAL_OK; +} + +HAL_StatusTypeDef CubeCAN_AddFilter(const CubeCAN_Handle *const handle, const FDCAN_FilterTypeDef *filter) +{ + if (handle == NULL || filter == NULL) { + LOGOMATIC("CubeCAN_AddFilter: Invalid handle or filter pointer\n"); + return HAL_ERROR; + } + + HAL_StatusTypeDef status = HAL_ERROR; + + CRITICAL_SECTION + { + if (handle->hfdcan != NULL) { + status = HAL_FDCAN_ConfigFilter(handle->hfdcan, filter); + } + } + + return status; +} + +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 instant 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..998f3da0c --- /dev/null +++ b/Lib/Peripherals/CubeCAN/cube_can.cmake @@ -0,0 +1,29 @@ +add_library(CUBEMX_CAN_LIB INTERFACE) + +target_include_directories( + CUBEMX_CAN_LIB + INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/Inc +) + +target_sources( + CUBEMX_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( + CUBEMX_CAN_LIB + INTERFACE + CANfigurator + LOGOMATIC_LIB +) + +if(CMAKE_PRESET_NAME STREQUAL "HOOTLTest") + # FIXME Add HOOTL tests +endif() 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()