Index: ctrl/firmware/Main/CubeMX/AZURE_RTOS/App/app_azure_rtos.c
===================================================================
--- ctrl/firmware/Main/CubeMX/AZURE_RTOS/App/app_azure_rtos.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/AZURE_RTOS/App/app_azure_rtos.c	(revision 54)
@@ -0,0 +1,181 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    app_azure_rtos.c
+  * @author  MCD Application Team
+  * @brief   app_azure_rtos application implementation file
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2020-2021 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 "app_azure_rtos.h"
+#include "stm32h7xx.h"
+
+/* Private includes ----------------------------------------------------------*/
+/* USER CODE BEGIN Includes */
+
+/* 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 ---------------------------------------------------------*/
+#if (USE_STATIC_ALLOCATION == 1)
+/* USER CODE BEGIN TX_Pool_Buffer */
+/* USER CODE END TX_Pool_Buffer */
+#if defined ( __ICCARM__ )
+#pragma data_alignment=4
+#endif
+__ALIGN_BEGIN static UCHAR tx_byte_pool_buffer[TX_APP_MEM_POOL_SIZE] __ALIGN_END;
+static TX_BYTE_POOL tx_app_byte_pool;
+
+/* USER CODE BEGIN FX_Pool_Buffer */
+/* USER CODE END FX_Pool_Buffer */
+#if defined ( __ICCARM__ )
+#pragma data_alignment=4
+#endif
+__ALIGN_BEGIN static UCHAR fx_byte_pool_buffer[FX_APP_MEM_POOL_SIZE] __ALIGN_END;
+static TX_BYTE_POOL fx_app_byte_pool;
+
+#endif
+
+/* USER CODE BEGIN PV */
+
+/* USER CODE END PV */
+
+/* Private function prototypes -----------------------------------------------*/
+/* USER CODE BEGIN PFP */
+
+/* USER CODE END PFP */
+
+/**
+  * @brief  Define the initial system.
+  * @param  first_unused_memory : Pointer to the first unused memory
+  * @retval None
+  */
+VOID tx_application_define(VOID *first_unused_memory)
+{
+  /* USER CODE BEGIN  tx_application_define_1*/
+
+  /* USER CODE END  tx_application_define_1 */
+#if (USE_STATIC_ALLOCATION == 1)
+  UINT status = TX_SUCCESS;
+  VOID *memory_ptr;
+
+  if (tx_byte_pool_create(&tx_app_byte_pool, "Tx App memory pool", tx_byte_pool_buffer, TX_APP_MEM_POOL_SIZE) != TX_SUCCESS)
+  {
+    /* USER CODE BEGIN TX_Byte_Pool_Error */
+
+    /* USER CODE END TX_Byte_Pool_Error */
+  }
+  else
+  {
+    /* USER CODE BEGIN TX_Byte_Pool_Success */
+
+    /* USER CODE END TX_Byte_Pool_Success */
+
+    memory_ptr = (VOID *)&tx_app_byte_pool;
+    status = App_ThreadX_Init(memory_ptr);
+    if (status != TX_SUCCESS)
+    {
+      /* USER CODE BEGIN  App_ThreadX_Init_Error */
+      while(1)
+      {
+      }
+      /* USER CODE END  App_ThreadX_Init_Error */
+    }
+
+    /* USER CODE BEGIN  App_ThreadX_Init_Success */
+
+    /* USER CODE END  App_ThreadX_Init_Success */
+
+  }
+
+  if (tx_byte_pool_create(&fx_app_byte_pool, "Fx App memory pool", fx_byte_pool_buffer, FX_APP_MEM_POOL_SIZE) != TX_SUCCESS)
+  {
+    /* USER CODE BEGIN FX_Byte_Pool_Error */
+
+    /* USER CODE END FX_Byte_Pool_Error */
+  }
+  else
+  {
+    /* USER CODE BEGIN FX_Byte_Pool_Success */
+
+    /* USER CODE END FX_Byte_Pool_Success */
+
+    memory_ptr = (VOID *)&fx_app_byte_pool;
+    status = MX_FileX_Init(memory_ptr);
+    if (status != FX_SUCCESS)
+    {
+      /* USER CODE BEGIN  MX_FileX_Init_Error */
+      while(1)
+      {
+      }
+      /* USER CODE END  MX_FileX_Init_Error */
+    }
+
+    /* USER CODE BEGIN MX_FileX_Init_Success */
+
+    /* USER CODE END MX_FileX_Init_Success */
+  }
+
+#else
+  /*
+   * Using dynamic memory allocation requires to apply some changes to the linker file.
+   * ThreadX needs to pass a pointer to the first free memory location in RAM to the tx_application_define() function,
+   * using the "first_unused_memory" argument.
+   * This require changes in the linker files to expose this memory location.
+   * For EWARM add the following section into the .icf file:
+       place in RAM_region    { last section FREE_MEM };
+   * For MDK-ARM
+       - either define the RW_IRAM1 region in the ".sct" file
+       - or modify the line below in "tx_initialize_low_level.S to match the memory region being used
+          LDR r1, =|Image$$RW_IRAM1$$ZI$$Limit|
+
+   * For STM32CubeIDE add the following section into the .ld file:
+       ._threadx_heap :
+         {
+            . = ALIGN(8);
+            __RAM_segment_used_end__ = .;
+            . = . + 64K;
+            . = ALIGN(8);
+          } >RAM_D1 AT> RAM_D1
+      * The simplest way to provide memory for ThreadX is to define a new section, see ._threadx_heap above.
+      * In the example above the ThreadX heap size is set to 64KBytes.
+      * The ._threadx_heap must be located between the .bss and the ._user_heap_stack sections in the linker script.
+      * Caution: Make sure that ThreadX does not need more than the provided heap memory (64KBytes in this example).
+      * Read more in STM32CubeIDE User Guide, chapter: "Linker script".
+
+   * The "tx_initialize_low_level.S" should be also modified to enable the "USE_DYNAMIC_MEMORY_ALLOCATION" flag.
+   */
+
+  /* USER CODE BEGIN DYNAMIC_MEM_ALLOC */
+  (void)first_unused_memory;
+  /* USER CODE END DYNAMIC_MEM_ALLOC */
+#endif
+
+}
Index: ctrl/firmware/Main/CubeMX/Core/Inc/app_threadx.h
===================================================================
--- ctrl/firmware/Main/CubeMX/Core/Inc/app_threadx.h	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Core/Inc/app_threadx.h	(revision 54)
@@ -0,0 +1,76 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    app_threadx.h
+  * @author  MCD Application Team
+  * @brief   ThreadX applicative header file
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2020-2021 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 __APP_THREADX_H__
+#define __APP_THREADX_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Includes ------------------------------------------------------------------*/
+#include "tx_api.h"
+
+/* 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 */
+
+/* Private defines -----------------------------------------------------------*/
+/* USER CODE BEGIN PD */
+
+/* USER CODE END PD */
+
+/* Main thread defines -------------------------------------------------------*/
+/* USER CODE BEGIN MTD */
+
+/* USER CODE END MTD */
+
+/* Exported macro ------------------------------------------------------------*/
+
+/* USER CODE BEGIN EM */
+
+/* USER CODE END EM */
+
+/* Exported functions prototypes ---------------------------------------------*/
+UINT App_ThreadX_Init(VOID *memory_ptr);
+void MX_ThreadX_Init(void);
+/* USER CODE BEGIN EFP */
+
+/* USER CODE END EFP */
+
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __APP_THREADX_H__ */
Index: ctrl/firmware/Main/CubeMX/Core/Inc/main.h
===================================================================
--- ctrl/firmware/Main/CubeMX/Core/Inc/main.h	(revision 53)
+++ ctrl/firmware/Main/CubeMX/Core/Inc/main.h	(revision 54)
@@ -70,4 +70,6 @@
 #define RST_DISPLAY_Pin GPIO_PIN_15
 #define RST_DISPLAY_GPIO_Port GPIOE
+#define SD_DETECT_Pin GPIO_PIN_8
+#define SD_DETECT_GPIO_Port GPIOA
 #define OUTPUT_ON_LED_Pin GPIO_PIN_7
 #define OUTPUT_ON_LED_GPIO_Port GPIOD
Index: ctrl/firmware/Main/CubeMX/Core/Inc/stm32h7xx_hal_conf.h
===================================================================
--- ctrl/firmware/Main/CubeMX/Core/Inc/stm32h7xx_hal_conf.h	(revision 53)
+++ ctrl/firmware/Main/CubeMX/Core/Inc/stm32h7xx_hal_conf.h	(revision 54)
@@ -75,5 +75,5 @@
 #define HAL_SPI_MODULE_ENABLED
 /* #define HAL_SWPMI_MODULE_ENABLED   */
-/* #define HAL_TIM_MODULE_ENABLED   */
+#define HAL_TIM_MODULE_ENABLED
 /* #define HAL_UART_MODULE_ENABLED   */
 /* #define HAL_USART_MODULE_ENABLED   */
Index: ctrl/firmware/Main/CubeMX/Core/Inc/stm32h7xx_it.h
===================================================================
--- ctrl/firmware/Main/CubeMX/Core/Inc/stm32h7xx_it.h	(revision 53)
+++ ctrl/firmware/Main/CubeMX/Core/Inc/stm32h7xx_it.h	(revision 54)
@@ -52,10 +52,8 @@
 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 DMA1_Stream0_IRQHandler(void);
 void SDMMC1_IRQHandler(void);
+void TIM7_IRQHandler(void);
 void SPI4_IRQHandler(void);
 /* USER CODE BEGIN EFP */
Index: ctrl/firmware/Main/CubeMX/Core/Src/app_threadx.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Core/Src/app_threadx.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Core/Src/app_threadx.c	(revision 54)
@@ -0,0 +1,141 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    app_threadx.c
+  * @author  MCD Application Team
+  * @brief   ThreadX applicative file
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2020-2021 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 "app_threadx.h"
+
+/* Private includes ----------------------------------------------------------*/
+/* USER CODE BEGIN Includes */
+
+/* USER CODE END Includes */
+
+/* Private typedef -----------------------------------------------------------*/
+/* USER CODE BEGIN PTD */
+
+/* USER CODE END PTD */
+
+/* Private define ------------------------------------------------------------*/
+/* USER CODE BEGIN PD */
+
+/* USER CODE END PD */
+
+/* Private macro -------------------------------------------------------------*/
+/* USER CODE BEGIN PM */
+
+/* USER CODE END PM */
+
+/* Private variables ---------------------------------------------------------*/
+/* USER CODE BEGIN PV */
+
+/* USER CODE END PV */
+
+/* Private function prototypes -----------------------------------------------*/
+/* USER CODE BEGIN PFP */
+
+/* USER CODE END PFP */
+
+/**
+  * @brief  Application ThreadX Initialization.
+  * @param memory_ptr: memory pointer
+  * @retval int
+  */
+UINT App_ThreadX_Init(VOID *memory_ptr)
+{
+  UINT ret = TX_SUCCESS;
+  /* USER CODE BEGIN App_ThreadX_MEM_POOL */
+
+  /* USER CODE END App_ThreadX_MEM_POOL */
+
+  /* USER CODE BEGIN App_ThreadX_Init */
+
+  /* USER CODE END App_ThreadX_Init */
+
+  return ret;
+}
+
+  /**
+  * @brief  Function that implements the kernel's initialization.
+  * @param  None
+  * @retval None
+  */
+void MX_ThreadX_Init(void)
+{
+  /* USER CODE BEGIN  Before_Kernel_Start */
+
+  /* USER CODE END  Before_Kernel_Start */
+
+  tx_kernel_enter();
+
+  /* USER CODE BEGIN  Kernel_Start_Error */
+
+  /* USER CODE END  Kernel_Start_Error */
+}
+
+/**
+  * @brief  App_ThreadX_LowPower_Timer_Setup
+  * @param  count : TX timer count
+  * @retval None
+  */
+void App_ThreadX_LowPower_Timer_Setup(ULONG count)
+{
+  /* USER CODE BEGIN  App_ThreadX_LowPower_Timer_Setup */
+
+  /* USER CODE END  App_ThreadX_LowPower_Timer_Setup */
+}
+
+/**
+  * @brief  App_ThreadX_LowPower_Enter
+  * @param  None
+  * @retval None
+  */
+void App_ThreadX_LowPower_Enter(void)
+{
+  /* USER CODE BEGIN  App_ThreadX_LowPower_Enter */
+
+  /* USER CODE END  App_ThreadX_LowPower_Enter */
+}
+
+/**
+  * @brief  App_ThreadX_LowPower_Exit
+  * @param  None
+  * @retval None
+  */
+void App_ThreadX_LowPower_Exit(void)
+{
+  /* USER CODE BEGIN  App_ThreadX_LowPower_Exit */
+
+  /* USER CODE END  App_ThreadX_LowPower_Exit */
+}
+
+/**
+  * @brief  App_ThreadX_LowPower_Timer_Adjust
+  * @param  None
+  * @retval Amount of time (in ticks)
+  */
+ULONG App_ThreadX_LowPower_Timer_Adjust(void)
+{
+  /* USER CODE BEGIN  App_ThreadX_LowPower_Timer_Adjust */
+  return 0;
+  /* USER CODE END  App_ThreadX_LowPower_Timer_Adjust */
+}
+
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
Index: ctrl/firmware/Main/CubeMX/Core/Src/gpio.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Core/Src/gpio.c	(revision 53)
+++ ctrl/firmware/Main/CubeMX/Core/Src/gpio.c	(revision 54)
@@ -108,10 +108,10 @@
   /*Configure GPIO pins : PA0 PA1 PA2 PA3
                            PA4 PA5 PA6 PA7
-                           PA8 PA9 PA10 PA11
-                           PA12 PA15 */
+                           PA9 PA10 PA11 PA12
+                           PA15 */
   GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
                           |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7
-                          |GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
-                          |GPIO_PIN_12|GPIO_PIN_15;
+                          |GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12
+                          |GPIO_PIN_15;
   GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
   GPIO_InitStruct.Pull = GPIO_NOPULL;
@@ -152,4 +152,10 @@
   HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
 
+  /*Configure GPIO pin : SD_DETECT_Pin */
+  GPIO_InitStruct.Pin = SD_DETECT_Pin;
+  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
+  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
+  HAL_GPIO_Init(SD_DETECT_GPIO_Port, &GPIO_InitStruct);
+
   /*Configure GPIO pin : OUTPUT_ON_LED_Pin */
   GPIO_InitStruct.Pin = OUTPUT_ON_LED_Pin;
Index: ctrl/firmware/Main/CubeMX/Core/Src/main.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Core/Src/main.c	(revision 53)
+++ ctrl/firmware/Main/CubeMX/Core/Src/main.c	(revision 54)
@@ -18,4 +18,5 @@
 /* USER CODE END Header */
 /* Includes ------------------------------------------------------------------*/
+#include "app_threadx.h"
 #include "main.h"
 #include "dma.h"
@@ -118,11 +119,9 @@
   /* USER CODE BEGIN 2 */
 
-  //HAL_GPIO_WritePin(PWM_DISPLAY_LIGHT_GPIO_Port, PWM_DISPLAY_LIGHT_Pin, GPIO_PIN_SET);
-  //HAL_GPIO_WritePin(TX1_LED_GPIO_Port, TX1_LED_Pin, GPIO_PIN_SET);
-  //HAL_GPIO_WritePin(RX1_LED_GPIO_Port, RX1_LED_Pin, GPIO_PIN_SET);
-  //HAL_GPIO_WritePin(OUTPUT_ON_LED_GPIO_Port, OUTPUT_ON_LED_Pin, GPIO_PIN_RESET);
-
-
   /* USER CODE END 2 */
+
+  MX_ThreadX_Init();
+
+  /* We should never get here as control is now taken by the scheduler */
 
   /* Infinite loop */
@@ -260,4 +259,25 @@
 
 /**
+  * @brief  Period elapsed callback in non blocking mode
+  * @note   This function is called  when TIM7 interrupt took place, inside
+  * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
+  * a global variable "uwTick" used as application time base.
+  * @param  htim : TIM handle
+  * @retval None
+  */
+void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
+{
+  /* USER CODE BEGIN Callback 0 */
+
+  /* USER CODE END Callback 0 */
+  if (htim->Instance == TIM7) {
+    HAL_IncTick();
+  }
+  /* USER CODE BEGIN Callback 1 */
+
+  /* USER CODE END Callback 1 */
+}
+
+/**
   * @brief  This function is executed in case of error occurrence.
   * @retval None
Index: ctrl/firmware/Main/CubeMX/Core/Src/stm32h7xx_hal_timebase_tim.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Core/Src/stm32h7xx_hal_timebase_tim.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Core/Src/stm32h7xx_hal_timebase_tim.c	(revision 54)
@@ -0,0 +1,131 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    stm32h7xx_hal_timebase_tim.c
+  * @brief   HAL time base based on the hardware TIM.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2024 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32h7xx_hal.h"
+#include "stm32h7xx_hal_tim.h"
+
+/* Private typedef -----------------------------------------------------------*/
+/* Private define ------------------------------------------------------------*/
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+TIM_HandleTypeDef        htim7;
+/* Private function prototypes -----------------------------------------------*/
+/* Private functions ---------------------------------------------------------*/
+
+/**
+  * @brief  This function configures the TIM7 as a time base source.
+  *         The time source is configured  to have 1ms time base with a dedicated
+  *         Tick interrupt priority.
+  * @note   This function is called  automatically at the beginning of program after
+  *         reset by HAL_Init() or at any time when clock is configured, by HAL_RCC_ClockConfig().
+  * @param  TickPriority: Tick interrupt priority.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
+{
+  RCC_ClkInitTypeDef    clkconfig;
+  uint32_t              uwTimclock, uwAPB1Prescaler;
+  uint32_t              uwPrescalerValue;
+  uint32_t              pFLatency;
+
+  /*Configure the TIM7 IRQ priority */
+  if (TickPriority < (1UL << __NVIC_PRIO_BITS))
+   {
+     HAL_NVIC_SetPriority(TIM7_IRQn, TickPriority ,0);
+
+     /* Enable the TIM7 global Interrupt */
+     HAL_NVIC_EnableIRQ(TIM7_IRQn);
+     uwTickPrio = TickPriority;
+    }
+  else
+  {
+    return HAL_ERROR;
+  }
+
+  /* Enable TIM7 clock */
+  __HAL_RCC_TIM7_CLK_ENABLE();
+
+/* Get clock configuration */
+  HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);
+
+  /* Get APB1 prescaler */
+  uwAPB1Prescaler = clkconfig.APB1CLKDivider;
+  /* Compute TIM7 clock */
+  if (uwAPB1Prescaler == RCC_HCLK_DIV1)
+  {
+    uwTimclock = HAL_RCC_GetPCLK1Freq();
+  }
+  else
+  {
+    uwTimclock = 2UL * HAL_RCC_GetPCLK1Freq();
+  }
+
+  /* Compute the prescaler value to have TIM7 counter clock equal to 1MHz */
+  uwPrescalerValue = (uint32_t) ((uwTimclock / 1000000U) - 1U);
+
+  /* Initialize TIM7 */
+  htim7.Instance = TIM7;
+
+  /* Initialize TIMx peripheral as follow:
+
+  + Period = [(TIM7CLK/1000) - 1]. to have a (1/1000) s time base.
+  + Prescaler = (uwTimclock/1000000 - 1) to have a 1MHz counter clock.
+  + ClockDivision = 0
+  + Counter direction = Up
+  */
+  htim7.Init.Period = (1000000U / 1000U) - 1U;
+  htim7.Init.Prescaler = uwPrescalerValue;
+  htim7.Init.ClockDivision = 0;
+  htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
+
+  if(HAL_TIM_Base_Init(&htim7) == HAL_OK)
+  {
+    /* Start the TIM time Base generation in interrupt mode */
+    return HAL_TIM_Base_Start_IT(&htim7);
+  }
+
+  /* Return function status */
+  return HAL_ERROR;
+}
+
+/**
+  * @brief  Suspend Tick increment.
+  * @note   Disable the tick increment by disabling TIM7 update interrupt.
+  * @param  None
+  * @retval None
+  */
+void HAL_SuspendTick(void)
+{
+  /* Disable TIM7 update Interrupt */
+  __HAL_TIM_DISABLE_IT(&htim7, TIM_IT_UPDATE);
+}
+
+/**
+  * @brief  Resume Tick increment.
+  * @note   Enable the tick increment by Enabling TIM7 update interrupt.
+  * @param  None
+  * @retval None
+  */
+void HAL_ResumeTick(void)
+{
+  /* Enable TIM7 Update interrupt */
+  __HAL_TIM_ENABLE_IT(&htim7, TIM_IT_UPDATE);
+}
+
Index: ctrl/firmware/Main/CubeMX/Core/Src/stm32h7xx_it.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Core/Src/stm32h7xx_it.c	(revision 53)
+++ ctrl/firmware/Main/CubeMX/Core/Src/stm32h7xx_it.c	(revision 54)
@@ -59,4 +59,6 @@
 extern DMA_HandleTypeDef hdma_spi4_tx;
 extern SPI_HandleTypeDef hspi4;
+extern TIM_HandleTypeDef htim7;
+
 /* USER CODE BEGIN EV */
 
@@ -142,17 +144,4 @@
 
 /**
-  * @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.
   */
@@ -165,31 +154,4 @@
 
   /* 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 */
 }
 
@@ -230,4 +192,18 @@
 
 /**
+  * @brief This function handles TIM7 global interrupt.
+  */
+void TIM7_IRQHandler(void)
+{
+  /* USER CODE BEGIN TIM7_IRQn 0 */
+
+  /* USER CODE END TIM7_IRQn 0 */
+  HAL_TIM_IRQHandler(&htim7);
+  /* USER CODE BEGIN TIM7_IRQn 1 */
+
+  /* USER CODE END TIM7_IRQn 1 */
+}
+
+/**
   * @brief This function handles SPI4 global interrupt.
   */
Index: ctrl/firmware/Main/CubeMX/Core/Src/tx_initialize_low_level.S
===================================================================
--- ctrl/firmware/Main/CubeMX/Core/Src/tx_initialize_low_level.S	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Core/Src/tx_initialize_low_level.S	(revision 54)
@@ -0,0 +1,665 @@
+
+// by default AzureRTOS is configured to use static byte pool for
+// allocation, in case dynamic allocation is to be used, uncomment
+// the define below and update the linker files to define the following symbols
+// EWARM toolchain:
+//       place in RAM_region    { last section FREE_MEM};
+// MDK-ARM toolchain;
+//       either define the RW_IRAM1 region in the ".sct" file or modify this file by referring to the correct memory region.
+//         LDR r1, =|Image$$RW_IRAM1$$ZI$$Limit|
+// STM32CubeIDE toolchain:
+//       ._threadx_heap :
+//       {
+//        . = ALIGN(8);
+//        __RAM_segment_used_end__ = .;
+//        . = . + 64K;
+//        . = ALIGN(8);
+//       } >RAM_D1 AT> RAM_D1
+//  The simplest way to provide memory for ThreadX is to define a new section, see ._threadx_heap above.
+//  In the example above the ThreadX heap size is set to 64KBytes.
+//  The ._threadx_heap must be located between the .bss and the ._user_heap_stack sections in the linker script.
+//  Caution: Make sure that ThreadX does not need more than the provided heap memory (64KBytes in this example).
+//  Read more in STM32CubeIDE User Guide, chapter: "Linker script".
+
+//#define USE_DYNAMIC_MEMORY_ALLOCATION
+
+#if defined(__clang__)
+@/**************************************************************************/
+@/*                                                                        */
+@/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+@/*                                                                        */
+@/*       This software is licensed under the Microsoft Software License   */
+@/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+@/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+@/*       and in the root directory of this software.                      */
+@/*                                                                        */
+@/**************************************************************************/
+@
+@
+@/**************************************************************************/
+@/**************************************************************************/
+@/**                                                                       */
+@/** ThreadX Component                                                     */
+@/**                                                                       */
+@/**   Initialize                                                          */
+@/**                                                                       */
+@/**************************************************************************/
+@/**************************************************************************/
+@
+@
+    .global     _tx_thread_system_stack_ptr
+    .global     _tx_initialize_unused_memory
+    .global     _tx_timer_interrupt
+    .global     __main
+    .global     __tx_SVCallHandler
+    .global     __tx_PendSVHandler
+    .global     __tx_NMIHandler                     @ NMI
+    .global     __tx_BadHandler                     @ HardFault
+    .global     __tx_SVCallHandler                  @ SVCall
+    .global     __tx_DBGHandler                     @ Monitor
+    .global     __tx_PendSVHandler                  @ PendSV
+    .global     __tx_SysTickHandler                 @ SysTick
+    .global     __tx_IntHandler                     @ Int 0
+#ifdef USE_DYNAMIC_MEMORY_ALLOCATION
+    .global     Image$$RW_IRAM1$$ZI$$Limit
+#endif
+    .global     __Vectors
+@
+@
+SYSTEM_CLOCK      =   100000000
+SYSTICK_CYCLES    =   ((SYSTEM_CLOCK / 100) -1)
+
+    .text 32
+    .align 4
+    .syntax unified
+@/**************************************************************************/
+@/*                                                                        */
+@/*  FUNCTION                                               RELEASE        */
+@/*                                                                        */
+@/*    _tx_initialize_low_level                          Cortex-M7/AC6     */
+@/*                                                           6.1          */
+@/*  AUTHOR                                                                */
+@/*                                                                        */
+@/*    William E. Lamie, Microsoft Corporation                             */
+@/*                                                                        */
+@/*  DESCRIPTION                                                           */
+@/*                                                                        */
+@/*    This function is responsible for any low-level processor            */
+@/*    initialization, including setting up interrupt vectors, setting     */
+@/*    up a periodic timer interrupt source, saving the system stack       */
+@/*    pointer for use in ISR processing later, and finding the first      */
+@/*    available RAM memory address for tx_application_define.             */
+@/*                                                                        */
+@/*  INPUT                                                                 */
+@/*                                                                        */
+@/*    None                                                                */
+@/*                                                                        */
+@/*  OUTPUT                                                                */
+@/*                                                                        */
+@/*    None                                                                */
+@/*                                                                        */
+@/*  CALLS                                                                 */
+@/*                                                                        */
+@/*    None                                                                */
+@/*                                                                        */
+@/*  CALLED BY                                                             */
+@/*                                                                        */
+@/*    _tx_initialize_kernel_enter           ThreadX entry function        */
+@/*                                                                        */
+@/*  RELEASE HISTORY                                                       */
+@/*                                                                        */
+@/*    DATE              NAME                      DESCRIPTION             */
+@/*                                                                        */
+@/*  09-30-2020     William E. Lamie         Initial Version 6.1           */
+@/*                                                                        */
+@/**************************************************************************/
+@VOID   _tx_initialize_low_level(VOID)
+@{
+    .global  _tx_initialize_low_level
+    .thumb_func
+_tx_initialize_low_level:
+@
+@    /* Disable interrupts during ThreadX initialization.  */
+@
+    CPSID   i
+@
+@    /* Set base of available memory to end of non-initialised RAM area.  */
+@
+#ifdef USE_DYNAMIC_MEMORY_ALLOCATION
+    LDR     r0, =_tx_initialize_unused_memory       @ Build address of unused memory pointer
+    LDR     r1, = Image$$RW_IRAM1$$ZI$$Limit        @ Build first free address
+    ADD     r1, r1, #4                              @
+    STR     r1, [r0]                                @ Setup first unused memory pointer
+#endif
+@
+@    /* Setup Vector Table Offset Register.  */
+@
+    MOV     r0, #0xE000E000                         @ Build address of NVIC registers
+    LDR     r1, =__Vectors                          @ Pickup address of vector table
+    STR     r1, [r0, #0xD08]                        @ Set vector table address
+@
+@    /* Set system stack pointer from vector value.  */
+@
+    LDR     r0, =_tx_thread_system_stack_ptr        @ Build address of system stack pointer
+    LDR     r1, =__Vectors                          @ Pickup address of vector table
+    LDR     r1, [r1]                                @ Pickup reset stack pointer
+    STR     r1, [r0]                                @ Save system stack pointer
+@
+@    /* Enable the cycle count register.  */
+@
+    LDR     r0, =0xE0001000                         @ Build address of DWT register
+    LDR     r1, [r0]                                @ Pickup the current value
+    ORR     r1, r1, #1                              @ Set the CYCCNTENA bit
+    STR     r1, [r0]                                @ Enable the cycle count register
+@
+@    /* Configure SysTick for 100Hz clock, or 16384 cycles if no reference.  */
+@
+    MOV     r0, #0xE000E000                         @ Build address of NVIC registers
+    LDR     r1, =SYSTICK_CYCLES
+    STR     r1, [r0, #0x14]                         @ Setup SysTick Reload Value
+    MOV     r1, #0x7                                @ Build SysTick Control Enable Value
+    STR     r1, [r0, #0x10]                         @ Setup SysTick Control
+@
+@    /* Configure handler priorities.  */
+@
+    LDR     r1, =0x00000000                         @ Rsrv, UsgF, BusF, MemM
+    STR     r1, [r0, #0xD18]                        @ Setup System Handlers 4-7 Priority Registers
+
+    LDR     r1, =0xFF000000                         @ SVCl, Rsrv, Rsrv, Rsrv
+    STR     r1, [r0, #0xD1C]                        @ Setup System Handlers 8-11 Priority Registers
+                                                    @ Note: SVC must be lowest priority, which is 0xFF
+
+    LDR     r1, =0x40FF0000                         @ SysT, PnSV, Rsrv, DbgM
+    STR     r1, [r0, #0xD20]                        @ Setup System Handlers 12-15 Priority Registers
+                                                    @ Note: PnSV must be lowest priority, which is 0xFF
+@
+@    /* Return to caller.  */
+@
+    BX      lr
+@}
+@
+
+@/* Define shells for each of the unused vectors.  */
+@
+    .global  __tx_BadHandler
+    .thumb_func
+__tx_BadHandler:
+    B       __tx_BadHandler
+
+@ /* added to catch the hardfault */
+
+    .global  __tx_HardfaultHandler
+    .thumb_func
+__tx_HardfaultHandler:
+    B       __tx_HardfaultHandler
+
+@ /* added to catch the SVC */
+
+    .global  __tx_SVCallHandler
+    .thumb_func
+__tx_SVCallHandler:
+    B       __tx_SVCallHandler
+
+@ /* Generic interrupt handler template */
+    .global  __tx_IntHandler
+    .thumb_func
+__tx_IntHandler:
+@ VOID InterruptHandler (VOID)
+@ {
+    PUSH    {r0, lr}
+#ifdef TX_EXECUTION_PROFILE_ENABLE
+    BL      _tx_execution_isr_enter             @ Call the ISR enter function
+#endif
+
+@    /* Do interrupt handler work here */
+@    /* BL <your C Function>.... */
+
+#ifdef TX_EXECUTION_PROFILE_ENABLE
+    BL      _tx_execution_isr_exit              @ Call the ISR exit function
+#endif
+    POP     {r0, lr}
+    BX      LR
+@ }
+
+@ /* System Tick timer interrupt handler */
+    .global  __tx_SysTickHandler
+    .global  SysTick_Handler
+    .thumb_func
+__tx_SysTickHandler:
+    .thumb_func
+SysTick_Handler:
+@ VOID TimerInterruptHandler (VOID)
+@ {
+@
+    PUSH    {r0, lr}
+#ifdef TX_EXECUTION_PROFILE_ENABLE
+    BL      _tx_execution_isr_enter             @ Call the ISR enter function
+#endif
+    BL      _tx_timer_interrupt
+#ifdef TX_EXECUTION_PROFILE_ENABLE
+    BL      _tx_execution_isr_exit              @ Call the ISR exit function
+#endif
+    POP     {r0, lr}
+    BX      LR
+@ }
+
+@ /* NMI, DBG handlers */
+    .global  __tx_NMIHandler
+    .thumb_func
+__tx_NMIHandler:
+    B       __tx_NMIHandler
+
+    .global  __tx_DBGHandler
+    .thumb_func
+__tx_DBGHandler:
+    B       __tx_DBGHandler
+.end
+#endif
+
+#ifdef __IAR_SYSTEMS_ASM__
+;/**************************************************************************/
+;/*                                                                        */
+;/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+;/*                                                                        */
+;/*       This software is licensed under the Microsoft Software License   */
+;/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+;/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+;/*       and in the root directory of this software.                      */
+;/*                                                                        */
+;/**************************************************************************/
+;
+;
+;/**************************************************************************/
+;/**************************************************************************/
+;/**                                                                       */
+;/** ThreadX Component                                                     */
+;/**                                                                       */
+;/**   Initialize                                                          */
+;/**                                                                       */
+;/**************************************************************************/
+;/**************************************************************************/
+;
+    EXTERN  _tx_thread_system_stack_ptr
+    EXTERN  _tx_initialize_unused_memory
+    EXTERN  _tx_timer_interrupt
+    EXTERN  __vector_table
+    EXTERN  _tx_execution_isr_enter
+    EXTERN  _tx_execution_isr_exit
+;
+;
+SYSTEM_CLOCK      EQU   100000000
+SYSTICK_CYCLES    EQU   ((SYSTEM_CLOCK / 100) -1)
+#ifdef USE_DYNAMIC_MEMORY_ALLOCATION
+    RSEG    FREE_MEM:DATA
+    PUBLIC  __tx_free_memory_start
+__tx_free_memory_start
+    DS32    4
+#endif
+;
+;
+    SECTION `.text`:CODE:NOROOT(2)
+    THUMB
+;/**************************************************************************/
+;/*                                                                        */
+;/*  FUNCTION                                               RELEASE        */
+;/*                                                                        */
+;/*    _tx_initialize_low_level                          Cortex-M7/IAR     */
+;/*                                                           6.1          */
+;/*  AUTHOR                                                                */
+;/*                                                                        */
+;/*    William E. Lamie, Microsoft Corporation                             */
+;/*                                                                        */
+;/*  DESCRIPTION                                                           */
+;/*                                                                        */
+;/*    This function is responsible for any low-level processor            */
+;/*    initialization, including setting up interrupt vectors, setting     */
+;/*    up a periodic timer interrupt source, saving the system stack       */
+;/*    pointer for use in ISR processing later, and finding the first      */
+;/*    available RAM memory address for tx_application_define.             */
+;/*                                                                        */
+;/*  INPUT                                                                 */
+;/*                                                                        */
+;/*    None                                                                */
+;/*                                                                        */
+;/*  OUTPUT                                                                */
+;/*                                                                        */
+;/*    None                                                                */
+;/*                                                                        */
+;/*  CALLS                                                                 */
+;/*                                                                        */
+;/*    None                                                                */
+;/*                                                                        */
+;/*  CALLED BY                                                             */
+;/*                                                                        */
+;/*    _tx_initialize_kernel_enter           ThreadX entry function        */
+;/*                                                                        */
+;/*  RELEASE HISTORY                                                       */
+;/*                                                                        */
+;/*    DATE              NAME                      DESCRIPTION             */
+;/*                                                                        */
+;/*  09-30-2020     William E. Lamie         Initial Version 6.1           */
+;/*                                                                        */
+;/**************************************************************************/
+;VOID   _tx_initialize_low_level(VOID)
+;{
+    PUBLIC  _tx_initialize_low_level
+_tx_initialize_low_level:
+;
+;    /* Ensure that interrupts are disabled.  */
+;
+    CPSID   i                                       ; Disable interrupts
+;
+;
+;    /* Set base of available memory to end of non-initialised RAM area.  */
+;
+#ifdef USE_DYNAMIC_MEMORY_ALLOCATION
+
+    LDR     r0, =__tx_free_memory_start             ; Get end of non-initialized RAM area
+    LDR     r2, =_tx_initialize_unused_memory       ; Build address of unused memory pointer
+    STR     r0, [r2, #0]                            ; Save first free memory address
+#endif
+;
+;    /* Enable the cycle count register.  */
+;
+    LDR     r0, =0xE0001000                         ; Build address of DWT register
+    LDR     r1, [r0]                                ; Pickup the current value
+    ORR     r1, r1, #1                              ; Set the CYCCNTENA bit
+    STR     r1, [r0]                                ; Enable the cycle count register
+;
+;    /* Setup Vector Table Offset Register.  */
+;
+    MOV     r0, #0xE000E000                         ; Build address of NVIC registers
+    LDR     r1, =__vector_table                     ; Pickup address of vector table
+    STR     r1, [r0, #0xD08]                        ; Set vector table address
+;
+;    /* Set system stack pointer from vector value.  */
+;
+    LDR     r0, =_tx_thread_system_stack_ptr        ; Build address of system stack pointer
+    LDR     r1, =__vector_table                     ; Pickup address of vector table
+    LDR     r1, [r1]                                ; Pickup reset stack pointer
+    STR     r1, [r0]                                ; Save system stack pointer
+;
+;    /* Configure SysTick.  */
+;
+    MOV     r0, #0xE000E000                         ; Build address of NVIC registers
+    LDR     r1, =SYSTICK_CYCLES
+    STR     r1, [r0, #0x14]                         ; Setup SysTick Reload Value
+    MOV     r1, #0x7                                ; Build SysTick Control Enable Value
+    STR     r1, [r0, #0x10]                         ; Setup SysTick Control
+;
+;    /* Configure handler priorities.  */
+;
+    LDR     r1, =0x00000000                         ; Rsrv, UsgF, BusF, MemM
+    STR     r1, [r0, #0xD18]                        ; Setup System Handlers 4-7 Priority Registers
+
+    LDR     r1, =0xFF000000                         ; SVCl, Rsrv, Rsrv, Rsrv
+    STR     r1, [r0, #0xD1C]                        ; Setup System Handlers 8-11 Priority Registers
+                                                    ; Note: SVC must be lowest priority, which is 0xFF
+
+    LDR     r1, =0x40FF0000                         ; SysT, PnSV, Rsrv, DbgM
+    STR     r1, [r0, #0xD20]                        ; Setup System Handlers 12-15 Priority Registers
+                                                    ; Note: PnSV must be lowest priority, which is 0xFF
+;
+;    /* Return to caller.  */
+;
+    BX      lr
+;}
+;
+;
+    PUBLIC  SysTick_Handler
+    PUBLIC  __tx_SysTickHandler
+__tx_SysTickHandler:
+SysTick_Handler:
+;
+; VOID SysTick_Handler (VOID)
+; {
+;
+    PUSH    {r0, lr}
+#ifdef TX_EXECUTION_PROFILE_ENABLE
+    BL      _tx_execution_isr_enter             ; Call the ISR enter function
+#endif
+    BL      _tx_timer_interrupt
+#ifdef TX_EXECUTION_PROFILE_ENABLE
+    BL      _tx_execution_isr_exit              ; Call the ISR exit function
+#endif
+    POP     {r0, lr}
+    BX      LR
+; }
+    END
+#endif
+
+#if defined (__GNUC__) && !defined(__clang__)
+@/**************************************************************************/
+@/*                                                                        */
+@/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+@/*                                                                        */
+@/*       This software is licensed under the Microsoft Software License   */
+@/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+@/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+@/*       and in the root directory of this software.                      */
+@/*                                                                        */
+@/**************************************************************************/
+@
+@
+@/**************************************************************************/
+@/**************************************************************************/
+@/**                                                                       */
+@/** ThreadX Component                                                     */
+@/**                                                                       */
+@/**   Initialize                                                          */
+@/**                                                                       */
+@/**************************************************************************/
+@/**************************************************************************/
+@
+@
+    .global     _tx_thread_system_stack_ptr
+    .global     _tx_initialize_unused_memory
+    .global     __RAM_segment_used_end__
+    .global     _tx_timer_interrupt
+    .global     __main
+    .global     __tx_SVCallHandler
+    .global     __tx_PendSVHandler
+    .global     _vectors
+    .global     __tx_NMIHandler                     @ NMI
+    .global     __tx_BadHandler                     @ HardFault
+    .global     __tx_SVCallHandler                  @ SVCall
+    .global     __tx_DBGHandler                     @ Monitor
+    .global     __tx_PendSVHandler                  @ PendSV
+    .global     __tx_SysTickHandler                 @ SysTick
+    .global     __tx_IntHandler                     @ Int 0
+@
+@
+
+SYSTEM_CLOCK      =   100000000
+SYSTICK_CYCLES    =   ((SYSTEM_CLOCK / 100) -1)
+
+    .text 32
+    .align 4
+    .syntax unified
+@/**************************************************************************/
+@/*                                                                        */
+@/*  FUNCTION                                               RELEASE        */
+@/*                                                                        */
+@/*    _tx_initialize_low_level                          Cortex-M7/GNU     */
+@/*                                                           6.1          */
+@/*  AUTHOR                                                                */
+@/*                                                                        */
+@/*    William E. Lamie, Microsoft Corporation                             */
+@/*                                                                        */
+@/*  DESCRIPTION                                                           */
+@/*                                                                        */
+@/*    This function is responsible for any low-level processor            */
+@/*    initialization, including setting up interrupt vectors, setting     */
+@/*    up a periodic timer interrupt source, saving the system stack       */
+@/*    pointer for use in ISR processing later, and finding the first      */
+@/*    available RAM memory address for tx_application_define.             */
+@/*                                                                        */
+@/*  INPUT                                                                 */
+@/*                                                                        */
+@/*    None                                                                */
+@/*                                                                        */
+@/*  OUTPUT                                                                */
+@/*                                                                        */
+@/*    None                                                                */
+@/*                                                                        */
+@/*  CALLS                                                                 */
+@/*                                                                        */
+@/*    None                                                                */
+@/*                                                                        */
+@/*  CALLED BY                                                             */
+@/*                                                                        */
+@/*    _tx_initialize_kernel_enter           ThreadX entry function        */
+@/*                                                                        */
+@/*  RELEASE HISTORY                                                       */
+@/*                                                                        */
+@/*    DATE              NAME                      DESCRIPTION             */
+@/*                                                                        */
+@/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+@/*  09-30-2020     William E. Lamie         Modified Comment(s), fixed    */
+@/*                                            GNU assembly comment, clean */
+@/*                                            up whitespace, resulting    */
+@/*                                            in version 6.1              */
+@/*                                                                        */
+@/**************************************************************************/
+@VOID   _tx_initialize_low_level(VOID)
+@{
+    .global  _tx_initialize_low_level
+    .thumb_func
+_tx_initialize_low_level:
+@
+@    /* Disable interrupts during ThreadX initialization.  */
+@
+    CPSID   i
+@
+@    /* Set base of available memory to end of non-initialised RAM area.  */
+@
+#ifdef USE_DYNAMIC_MEMORY_ALLOCATION
+    LDR     r0, =_tx_initialize_unused_memory       @ Build address of unused memory pointer
+    LDR     r1, =__RAM_segment_used_end__           @ Build first free address
+    ADD     r1, r1, #4                              @
+    STR     r1, [r0]                                @ Setup first unused memory pointer
+#endif
+@
+@    /* Setup Vector Table Offset Register.  */
+@
+    MOV     r0, #0xE000E000                         @ Build address of NVIC registers
+    LDR     r1, =_vectors                           @ Pickup address of vector table
+    STR     r1, [r0, #0xD08]                        @ Set vector table address
+@
+@    /* Set system stack pointer from vector value.  */
+@
+    LDR     r0, =_tx_thread_system_stack_ptr        @ Build address of system stack pointer
+    LDR     r1, =_vectors                           @ Pickup address of vector table
+    LDR     r1, [r1]                                @ Pickup reset stack pointer
+    STR     r1, [r0]                                @ Save system stack pointer
+@
+@    /* Enable the cycle count register.  */
+@
+    LDR     r0, =0xE0001000                         @ Build address of DWT register
+    LDR     r1, [r0]                                @ Pickup the current value
+    ORR     r1, r1, #1                              @ Set the CYCCNTENA bit
+    STR     r1, [r0]                                @ Enable the cycle count register
+@
+@    /* Configure SysTick for 100Hz clock, or 16384 cycles if no reference.  */
+@
+    MOV     r0, #0xE000E000                         @ Build address of NVIC registers
+    LDR     r1, =SYSTICK_CYCLES
+    STR     r1, [r0, #0x14]                         @ Setup SysTick Reload Value
+    MOV     r1, #0x7                                @ Build SysTick Control Enable Value
+    STR     r1, [r0, #0x10]                         @ Setup SysTick Control
+@
+@    /* Configure handler priorities.  */
+@
+    LDR     r1, =0x00000000                         @ Rsrv, UsgF, BusF, MemM
+    STR     r1, [r0, #0xD18]                        @ Setup System Handlers 4-7 Priority Registers
+
+    LDR     r1, =0xFF000000                         @ SVCl, Rsrv, Rsrv, Rsrv
+    STR     r1, [r0, #0xD1C]                        @ Setup System Handlers 8-11 Priority Registers
+                                                    @ Note: SVC must be lowest priority, which is 0xFF
+
+    LDR     r1, =0x40FF0000                         @ SysT, PnSV, Rsrv, DbgM
+    STR     r1, [r0, #0xD20]                        @ Setup System Handlers 12-15 Priority Registers
+                                                    @ Note: PnSV must be lowest priority, which is 0xFF
+@
+@    /* Return to caller.  */
+@
+    BX      lr
+@}
+@
+
+@/* Define shells for each of the unused vectors.  */
+@
+    .global  __tx_BadHandler
+    .thumb_func
+__tx_BadHandler:
+    B       __tx_BadHandler
+
+@ /* added to catch the hardfault */
+
+    .global  __tx_HardfaultHandler
+    .thumb_func
+__tx_HardfaultHandler:
+    B       __tx_HardfaultHandler
+
+@ /* added to catch the SVC */
+
+    .global  __tx_SVCallHandler
+    .thumb_func
+__tx_SVCallHandler:
+    B       __tx_SVCallHandler
+
+@ /* Generic interrupt handler template */
+    .global  __tx_IntHandler
+    .thumb_func
+__tx_IntHandler:
+@ VOID InterruptHandler (VOID)
+@ {
+    PUSH    {r0, lr}
+#ifdef TX_EXECUTION_PROFILE_ENABLE
+    BL      _tx_execution_isr_enter             @ Call the ISR enter function
+#endif
+
+@    /* Do interrupt handler work here */
+@    /* BL <your C Function>.... */
+
+#ifdef TX_EXECUTION_PROFILE_ENABLE
+    BL      _tx_execution_isr_exit              @ Call the ISR exit function
+#endif
+    POP     {r0, lr}
+    BX      LR
+@ }
+
+@ /* System Tick timer interrupt handler */
+    .global  __tx_SysTickHandler
+    .global  SysTick_Handler
+    .thumb_func
+__tx_SysTickHandler:
+    .thumb_func
+SysTick_Handler:
+@ VOID TimerInterruptHandler (VOID)
+@ {
+@
+    PUSH    {r0, lr}
+#ifdef TX_EXECUTION_PROFILE_ENABLE
+    BL      _tx_execution_isr_enter             @ Call the ISR enter function
+#endif
+    BL      _tx_timer_interrupt
+#ifdef TX_EXECUTION_PROFILE_ENABLE
+    BL      _tx_execution_isr_exit              @ Call the ISR exit function
+#endif
+    POP     {r0, lr}
+    BX      LR
+@ }
+
+@ /* NMI, DBG handlers */
+    .global  __tx_NMIHandler
+    .thumb_func
+__tx_NMIHandler:
+    B       __tx_NMIHandler
+
+    .global  __tx_DBGHandler
+    .thumb_func
+__tx_DBGHandler:
+    B       __tx_DBGHandler
+
+#endif
Index: ctrl/firmware/Main/CubeMX/Core/Startup/startup_stm32h723zetx.s
===================================================================
--- ctrl/firmware/Main/CubeMX/Core/Startup/startup_stm32h723zetx.s	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Core/Startup/startup_stm32h723zetx.s	(revision 54)
@@ -0,0 +1,759 @@
+/**
+  ******************************************************************************
+  * @file      startup_stm32h723xx.s
+  * @author    MCD Application Team
+  * @brief     STM32H723xx Devices vector table for GCC based toolchain.
+  *            This module performs:
+  *                - Set the initial SP
+  *                - Set the initial PC == Reset_Handler,
+  *                - Set the vector table entries with the exceptions ISR address
+  *                - Branches to main in the C library (which eventually
+  *                  calls main()).
+  *            After Reset the Cortex-M processor is in Thread mode,
+  *            priority is Privileged, and the Stack is set to Main.
+  ******************************************************************************
+  * @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.
+  *
+  ******************************************************************************
+  */
+
+  .syntax unified
+  .cpu cortex-m7
+  .fpu softvfp
+  .thumb
+
+.global  g_pfnVectors
+.global  Default_Handler
+
+/* start address for the initialization values of the .data section.
+defined in linker script */
+.word  _sidata
+/* start address for the .data section. defined in linker script */
+.word  _sdata
+/* end address for the .data section. defined in linker script */
+.word  _edata
+/* start address for the .bss section. defined in linker script */
+.word  _sbss
+/* end address for the .bss section. defined in linker script */
+.word  _ebss
+/* stack used for SystemInit_ExtMemCtl; always internal RAM used */
+
+/**
+ * @brief  This is the code that gets called when the processor first
+ *          starts execution following a reset event. Only the absolutely
+ *          necessary set is performed, after which the application
+ *          supplied main() routine is called.
+ * @param  None
+ * @retval : None
+*/
+
+    .section  .text.Reset_Handler
+  .weak  Reset_Handler
+  .type  Reset_Handler, %function
+Reset_Handler:
+  ldr   sp, =_estack      /* set stack pointer */
+
+/* Call the ExitRun0Mode function to configure the power supply */
+  bl  ExitRun0Mode
+/* Call the clock system initialization function.*/
+  bl  SystemInit
+
+/* Copy the data segment initializers from flash to SRAM */
+  ldr r0, =_sdata
+  ldr r1, =_edata
+  ldr r2, =_sidata
+  movs r3, #0
+  b LoopCopyDataInit
+
+CopyDataInit:
+  ldr r4, [r2, r3]
+  str r4, [r0, r3]
+  adds r3, r3, #4
+
+LoopCopyDataInit:
+  adds r4, r0, r3
+  cmp r4, r1
+  bcc CopyDataInit
+/* Zero fill the bss segment. */
+  ldr r2, =_sbss
+  ldr r4, =_ebss
+  movs r3, #0
+  b LoopFillZerobss
+
+FillZerobss:
+  str  r3, [r2]
+  adds r2, r2, #4
+
+LoopFillZerobss:
+  cmp r2, r4
+  bcc FillZerobss
+
+/* Call static constructors */
+    bl __libc_init_array
+/* Call the application's entry point.*/
+  bl  main
+  bx  lr
+.size  Reset_Handler, .-Reset_Handler
+
+/**
+ * @brief  This is the code that gets called when the processor receives an
+ *         unexpected interrupt.  This simply enters an infinite loop, preserving
+ *         the system state for examination by a debugger.
+ * @param  None
+ * @retval None
+*/
+    .section  .text.Default_Handler,"ax",%progbits
+Default_Handler:
+Infinite_Loop:
+  b  Infinite_Loop
+  .size  Default_Handler, .-Default_Handler
+/******************************************************************************
+*
+* The minimal vector table for a Cortex M. Note that the proper constructs
+* must be placed on this to ensure that it ends up at physical address
+* 0x0000.0000.
+*
+*******************************************************************************/
+   .section  .isr_vector,"a",%progbits
+  .type  g_pfnVectors, %object
+
+
+g_pfnVectors:
+  .word  _estack
+  .word  Reset_Handler
+
+  .word  NMI_Handler
+  .word  HardFault_Handler
+  .word  MemManage_Handler
+  .word  BusFault_Handler
+  .word  UsageFault_Handler
+  .word  0
+  .word  0
+  .word  0
+  .word  0
+  .word  SVC_Handler
+  .word  DebugMon_Handler
+  .word  0
+  .word  PendSV_Handler
+  .word  SysTick_Handler
+
+  /* External Interrupts */
+  .word     WWDG_IRQHandler                   /* Window WatchDog              */
+  .word     PVD_AVD_IRQHandler                /* PVD/AVD through EXTI Line detection */
+  .word     TAMP_STAMP_IRQHandler             /* Tamper and TimeStamps through the EXTI line */
+  .word     RTC_WKUP_IRQHandler               /* RTC Wakeup through the EXTI line */
+  .word     FLASH_IRQHandler                  /* FLASH                        */
+  .word     RCC_IRQHandler                    /* RCC                          */
+  .word     EXTI0_IRQHandler                  /* EXTI Line0                   */
+  .word     EXTI1_IRQHandler                  /* EXTI Line1                   */
+  .word     EXTI2_IRQHandler                  /* EXTI Line2                   */
+  .word     EXTI3_IRQHandler                  /* EXTI Line3                   */
+  .word     EXTI4_IRQHandler                  /* EXTI Line4                   */
+  .word     DMA1_Stream0_IRQHandler           /* DMA1 Stream 0                */
+  .word     DMA1_Stream1_IRQHandler           /* DMA1 Stream 1                */
+  .word     DMA1_Stream2_IRQHandler           /* DMA1 Stream 2                */
+  .word     DMA1_Stream3_IRQHandler           /* DMA1 Stream 3                */
+  .word     DMA1_Stream4_IRQHandler           /* DMA1 Stream 4                */
+  .word     DMA1_Stream5_IRQHandler           /* DMA1 Stream 5                */
+  .word     DMA1_Stream6_IRQHandler           /* DMA1 Stream 6                */
+  .word     ADC_IRQHandler                    /* ADC1, ADC2 and ADC3s         */
+  .word     FDCAN1_IT0_IRQHandler             /* FDCAN1 interrupt line 0      */
+  .word     FDCAN2_IT0_IRQHandler             /* FDCAN2 interrupt line 0      */
+  .word     FDCAN1_IT1_IRQHandler             /* FDCAN1 interrupt line 1      */
+  .word     FDCAN2_IT1_IRQHandler             /* FDCAN2 interrupt line 1      */
+  .word     EXTI9_5_IRQHandler                /* External Line[9:5]s          */
+  .word     TIM1_BRK_IRQHandler               /* TIM1 Break interrupt         */
+  .word     TIM1_UP_IRQHandler                /* TIM1 Update interrupt        */
+  .word     TIM1_TRG_COM_IRQHandler           /* TIM1 Trigger and Commutation interrupt */
+  .word     TIM1_CC_IRQHandler                /* TIM1 Capture Compare         */
+  .word     TIM2_IRQHandler                   /* TIM2                         */
+  .word     TIM3_IRQHandler                   /* TIM3                         */
+  .word     TIM4_IRQHandler                   /* TIM4                         */
+  .word     I2C1_EV_IRQHandler                /* I2C1 Event                   */
+  .word     I2C1_ER_IRQHandler                /* I2C1 Error                   */
+  .word     I2C2_EV_IRQHandler                /* I2C2 Event                   */
+  .word     I2C2_ER_IRQHandler                /* I2C2 Error                   */
+  .word     SPI1_IRQHandler                   /* SPI1                         */
+  .word     SPI2_IRQHandler                   /* SPI2                         */
+  .word     USART1_IRQHandler                 /* USART1                       */
+  .word     USART2_IRQHandler                 /* USART2                       */
+  .word     USART3_IRQHandler                 /* USART3                       */
+  .word     EXTI15_10_IRQHandler              /* External Line[15:10]s        */
+  .word     RTC_Alarm_IRQHandler              /* RTC Alarm (A and B) through EXTI Line */
+  .word     0                                 /* Reserved                     */
+  .word     TIM8_BRK_TIM12_IRQHandler         /* TIM8 Break and TIM12         */
+  .word     TIM8_UP_TIM13_IRQHandler          /* TIM8 Update and TIM13        */
+  .word     TIM8_TRG_COM_TIM14_IRQHandler     /* TIM8 Trigger and Commutation and TIM14 */
+  .word     TIM8_CC_IRQHandler                /* TIM8 Capture Compare         */
+  .word     DMA1_Stream7_IRQHandler           /* DMA1 Stream7                 */
+  .word     FMC_IRQHandler                    /* FMC                          */
+  .word     SDMMC1_IRQHandler                 /* SDMMC1                       */
+  .word     TIM5_IRQHandler                   /* TIM5                         */
+  .word     SPI3_IRQHandler                   /* SPI3                         */
+  .word     UART4_IRQHandler                  /* UART4                        */
+  .word     UART5_IRQHandler                  /* UART5                        */
+  .word     TIM6_DAC_IRQHandler               /* TIM6 and DAC1&2 underrun errors */
+  .word     TIM7_IRQHandler                   /* TIM7                         */
+  .word     DMA2_Stream0_IRQHandler           /* DMA2 Stream 0                */
+  .word     DMA2_Stream1_IRQHandler           /* DMA2 Stream 1                */
+  .word     DMA2_Stream2_IRQHandler           /* DMA2 Stream 2                */
+  .word     DMA2_Stream3_IRQHandler           /* DMA2 Stream 3                */
+  .word     DMA2_Stream4_IRQHandler           /* DMA2 Stream 4                */
+  .word     ETH_IRQHandler                    /* Ethernet                     */
+  .word     ETH_WKUP_IRQHandler               /* Ethernet Wakeup through EXTI line */
+  .word     FDCAN_CAL_IRQHandler              /* FDCAN calibration unit interrupt*/
+  .word     0                                 /* Reserved                     */
+  .word     0                                 /* Reserved                     */
+  .word     0                                 /* Reserved                     */
+  .word     0                                 /* Reserved                     */
+  .word     DMA2_Stream5_IRQHandler           /* DMA2 Stream 5                */
+  .word     DMA2_Stream6_IRQHandler           /* DMA2 Stream 6                */
+  .word     DMA2_Stream7_IRQHandler           /* DMA2 Stream 7                */
+  .word     USART6_IRQHandler                 /* USART6                       */
+  .word     I2C3_EV_IRQHandler                /* I2C3 event                   */
+  .word     I2C3_ER_IRQHandler                /* I2C3 error                   */
+  .word     OTG_HS_EP1_OUT_IRQHandler         /* USB OTG HS End Point 1 Out   */
+  .word     OTG_HS_EP1_IN_IRQHandler          /* USB OTG HS End Point 1 In    */
+  .word     OTG_HS_WKUP_IRQHandler            /* USB OTG HS Wakeup through EXTI */
+  .word     OTG_HS_IRQHandler                 /* USB OTG HS                   */
+  .word     DCMI_PSSI_IRQHandler              /* DCMI, PSSI                   */
+  .word     0                                 /* Reserved                     */
+  .word     RNG_IRQHandler                    /* Rng                          */
+  .word     FPU_IRQHandler                    /* FPU                          */
+  .word     UART7_IRQHandler                  /* UART7                        */
+  .word     UART8_IRQHandler                  /* UART8                        */
+  .word     SPI4_IRQHandler                   /* SPI4                         */
+  .word     SPI5_IRQHandler                   /* SPI5                         */
+  .word     SPI6_IRQHandler                   /* SPI6                         */
+  .word     SAI1_IRQHandler                   /* SAI1                         */
+  .word     LTDC_IRQHandler                   /* LTDC                         */
+  .word     LTDC_ER_IRQHandler                /* LTDC error                   */
+  .word     DMA2D_IRQHandler                  /* DMA2D                        */
+  .word     0                                 /* Reserved                     */
+  .word     OCTOSPI1_IRQHandler               /* OCTOSPI1                     */
+  .word     LPTIM1_IRQHandler                 /* LPTIM1                       */
+  .word     CEC_IRQHandler                    /* HDMI_CEC                     */
+  .word     I2C4_EV_IRQHandler                /* I2C4 Event                   */
+  .word     I2C4_ER_IRQHandler                /* I2C4 Error                   */
+  .word     SPDIF_RX_IRQHandler               /* SPDIF_RX                     */
+  .word     0                                 /* Reserved                     */
+  .word     0                                 /* Reserved                     */
+  .word     0                                 /* Reserved                     */
+  .word     0                                 /* Reserved                     */
+  .word     DMAMUX1_OVR_IRQHandler            /* DMAMUX1 Overrun interrupt    */
+  .word     0                                 /* Reserved                     */
+  .word     0                                 /* Reserved                     */
+  .word     0                                 /* Reserved                     */
+  .word     0                                 /* Reserved                     */
+  .word     0                                 /* Reserved                     */
+  .word     0                                 /* Reserved                     */
+  .word     0                                 /* Reserved                     */
+  .word     DFSDM1_FLT0_IRQHandler            /* DFSDM Filter0 Interrupt      */
+  .word     DFSDM1_FLT1_IRQHandler            /* DFSDM Filter1 Interrupt      */
+  .word     DFSDM1_FLT2_IRQHandler            /* DFSDM Filter2 Interrupt      */
+  .word     DFSDM1_FLT3_IRQHandler            /* DFSDM Filter3 Interrupt      */
+  .word     0                                 /* Reserved                     */
+  .word     SWPMI1_IRQHandler                 /* Serial Wire Interface 1 global interrupt */
+  .word     TIM15_IRQHandler                  /* TIM15 global Interrupt          */
+  .word     TIM16_IRQHandler                  /* TIM16 global Interrupt          */
+  .word     TIM17_IRQHandler                  /* TIM17 global Interrupt          */
+  .word     MDIOS_WKUP_IRQHandler             /* MDIOS Wakeup  Interrupt         */
+  .word     MDIOS_IRQHandler                  /* MDIOS global Interrupt          */
+  .word     0                                 /* Reserved                        */
+  .word     MDMA_IRQHandler                   /* MDMA global Interrupt           */
+  .word     0                                 /* Reserved                        */
+  .word     SDMMC2_IRQHandler                 /* SDMMC2 global Interrupt         */
+  .word     HSEM1_IRQHandler                  /* HSEM1 global Interrupt          */
+  .word     0                                 /* Reserved                        */
+  .word     ADC3_IRQHandler                   /* ADC3 global Interrupt           */
+  .word     DMAMUX2_OVR_IRQHandler            /* DMAMUX Overrun interrupt        */
+  .word     BDMA_Channel0_IRQHandler          /* BDMA Channel 0 global Interrupt */
+  .word     BDMA_Channel1_IRQHandler          /* BDMA Channel 1 global Interrupt */
+  .word     BDMA_Channel2_IRQHandler          /* BDMA Channel 2 global Interrupt */
+  .word     BDMA_Channel3_IRQHandler          /* BDMA Channel 3 global Interrupt */
+  .word     BDMA_Channel4_IRQHandler          /* BDMA Channel 4 global Interrupt */
+  .word     BDMA_Channel5_IRQHandler          /* BDMA Channel 5 global Interrupt */
+  .word     BDMA_Channel6_IRQHandler          /* BDMA Channel 6 global Interrupt */
+  .word     BDMA_Channel7_IRQHandler          /* BDMA Channel 7 global Interrupt */
+  .word     COMP1_IRQHandler                  /* COMP1 global Interrupt          */
+  .word     LPTIM2_IRQHandler                 /* LP TIM2 global interrupt        */
+  .word     LPTIM3_IRQHandler                 /* LP TIM3 global interrupt        */
+  .word     LPTIM4_IRQHandler                 /* LP TIM4 global interrupt        */
+  .word     LPTIM5_IRQHandler                 /* LP TIM5 global interrupt        */
+  .word     LPUART1_IRQHandler                /* LP UART1 interrupt              */
+  .word     0                                 /* Reserved                        */
+  .word     CRS_IRQHandler                    /* Clock Recovery Global Interrupt */
+  .word     ECC_IRQHandler                    /* ECC diagnostic Global Interrupt */
+  .word     SAI4_IRQHandler                   /* SAI4 global interrupt           */
+  .word     DTS_IRQHandler                    /* Digital Temperature Sensor  interrupt */
+  .word     0                                 /* Reserved                              */
+  .word     WAKEUP_PIN_IRQHandler             /* Interrupt for all 6 wake-up pins      */
+  .word     OCTOSPI2_IRQHandler               /* OCTOSPI2 Interrupt       */
+  .word     0                                 /* Reserved                 */
+  .word     0                                 /* Reserved                 */
+  .word     FMAC_IRQHandler                   /* FMAC Interrupt           */
+  .word     CORDIC_IRQHandler                 /* CORDIC Interrupt         */
+  .word     UART9_IRQHandler                  /* UART9 Interrupt          */
+  .word     USART10_IRQHandler                /* UART10 Interrupt         */
+  .word     I2C5_EV_IRQHandler                /* I2C5 Event Interrupt     */
+  .word     I2C5_ER_IRQHandler                /* I2C5 Error Interrupt     */
+  .word     FDCAN3_IT0_IRQHandler             /* FDCAN3 interrupt line 0  */
+  .word     FDCAN3_IT1_IRQHandler             /* FDCAN3 interrupt line 1  */
+  .word     TIM23_IRQHandler                  /* TIM23 global interrupt   */
+  .word     TIM24_IRQHandler                  /* TIM24 global interrupt   */
+
+  .size  g_pfnVectors, .-g_pfnVectors
+
+/*******************************************************************************
+*
+* Provide weak aliases for each Exception handler to the Default_Handler.
+* As they are weak aliases, any function with the same name will override
+* this definition.
+*
+*******************************************************************************/
+   .weak      NMI_Handler
+   .thumb_set NMI_Handler,Default_Handler
+
+   .weak      HardFault_Handler
+   .thumb_set HardFault_Handler,Default_Handler
+
+   .weak      MemManage_Handler
+   .thumb_set MemManage_Handler,Default_Handler
+
+   .weak      BusFault_Handler
+   .thumb_set BusFault_Handler,Default_Handler
+
+   .weak      UsageFault_Handler
+   .thumb_set UsageFault_Handler,Default_Handler
+
+   .weak      SVC_Handler
+   .thumb_set SVC_Handler,Default_Handler
+
+   .weak      DebugMon_Handler
+   .thumb_set DebugMon_Handler,Default_Handler
+
+   .weak      PendSV_Handler
+   .thumb_set PendSV_Handler,Default_Handler
+
+   .weak      SysTick_Handler
+   .thumb_set SysTick_Handler,Default_Handler
+
+   .weak      WWDG_IRQHandler
+   .thumb_set WWDG_IRQHandler,Default_Handler
+
+   .weak      PVD_AVD_IRQHandler
+   .thumb_set PVD_AVD_IRQHandler,Default_Handler
+
+   .weak      TAMP_STAMP_IRQHandler
+   .thumb_set TAMP_STAMP_IRQHandler,Default_Handler
+
+   .weak      RTC_WKUP_IRQHandler
+   .thumb_set RTC_WKUP_IRQHandler,Default_Handler
+
+   .weak      FLASH_IRQHandler
+   .thumb_set FLASH_IRQHandler,Default_Handler
+
+   .weak      RCC_IRQHandler
+   .thumb_set RCC_IRQHandler,Default_Handler
+
+   .weak      EXTI0_IRQHandler
+   .thumb_set EXTI0_IRQHandler,Default_Handler
+
+   .weak      EXTI1_IRQHandler
+   .thumb_set EXTI1_IRQHandler,Default_Handler
+
+   .weak      EXTI2_IRQHandler
+   .thumb_set EXTI2_IRQHandler,Default_Handler
+
+   .weak      EXTI3_IRQHandler
+   .thumb_set EXTI3_IRQHandler,Default_Handler
+
+   .weak      EXTI4_IRQHandler
+   .thumb_set EXTI4_IRQHandler,Default_Handler
+
+   .weak      DMA1_Stream0_IRQHandler
+   .thumb_set DMA1_Stream0_IRQHandler,Default_Handler
+
+   .weak      DMA1_Stream1_IRQHandler
+   .thumb_set DMA1_Stream1_IRQHandler,Default_Handler
+
+   .weak      DMA1_Stream2_IRQHandler
+   .thumb_set DMA1_Stream2_IRQHandler,Default_Handler
+
+   .weak      DMA1_Stream3_IRQHandler
+   .thumb_set DMA1_Stream3_IRQHandler,Default_Handler
+
+   .weak      DMA1_Stream4_IRQHandler
+   .thumb_set DMA1_Stream4_IRQHandler,Default_Handler
+
+   .weak      DMA1_Stream5_IRQHandler
+   .thumb_set DMA1_Stream5_IRQHandler,Default_Handler
+
+   .weak      DMA1_Stream6_IRQHandler
+   .thumb_set DMA1_Stream6_IRQHandler,Default_Handler
+
+   .weak      ADC_IRQHandler
+   .thumb_set ADC_IRQHandler,Default_Handler
+
+   .weak      FDCAN1_IT0_IRQHandler
+   .thumb_set FDCAN1_IT0_IRQHandler,Default_Handler
+
+   .weak      FDCAN2_IT0_IRQHandler
+   .thumb_set FDCAN2_IT0_IRQHandler,Default_Handler
+
+   .weak      FDCAN1_IT1_IRQHandler
+   .thumb_set FDCAN1_IT1_IRQHandler,Default_Handler
+
+   .weak      FDCAN2_IT1_IRQHandler
+   .thumb_set FDCAN2_IT1_IRQHandler,Default_Handler
+
+   .weak      EXTI9_5_IRQHandler
+   .thumb_set EXTI9_5_IRQHandler,Default_Handler
+
+   .weak      TIM1_BRK_IRQHandler
+   .thumb_set TIM1_BRK_IRQHandler,Default_Handler
+
+   .weak      TIM1_UP_IRQHandler
+   .thumb_set TIM1_UP_IRQHandler,Default_Handler
+
+   .weak      TIM1_TRG_COM_IRQHandler
+   .thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler
+
+   .weak      TIM1_CC_IRQHandler
+   .thumb_set TIM1_CC_IRQHandler,Default_Handler
+
+   .weak      TIM2_IRQHandler
+   .thumb_set TIM2_IRQHandler,Default_Handler
+
+   .weak      TIM3_IRQHandler
+   .thumb_set TIM3_IRQHandler,Default_Handler
+
+   .weak      TIM4_IRQHandler
+   .thumb_set TIM4_IRQHandler,Default_Handler
+
+   .weak      I2C1_EV_IRQHandler
+   .thumb_set I2C1_EV_IRQHandler,Default_Handler
+
+   .weak      I2C1_ER_IRQHandler
+   .thumb_set I2C1_ER_IRQHandler,Default_Handler
+
+   .weak      I2C2_EV_IRQHandler
+   .thumb_set I2C2_EV_IRQHandler,Default_Handler
+
+   .weak      I2C2_ER_IRQHandler
+   .thumb_set I2C2_ER_IRQHandler,Default_Handler
+
+   .weak      SPI1_IRQHandler
+   .thumb_set SPI1_IRQHandler,Default_Handler
+
+   .weak      SPI2_IRQHandler
+   .thumb_set SPI2_IRQHandler,Default_Handler
+
+   .weak      USART1_IRQHandler
+   .thumb_set USART1_IRQHandler,Default_Handler
+
+   .weak      USART2_IRQHandler
+   .thumb_set USART2_IRQHandler,Default_Handler
+
+   .weak      USART3_IRQHandler
+   .thumb_set USART3_IRQHandler,Default_Handler
+
+   .weak      EXTI15_10_IRQHandler
+   .thumb_set EXTI15_10_IRQHandler,Default_Handler
+
+   .weak      RTC_Alarm_IRQHandler
+   .thumb_set RTC_Alarm_IRQHandler,Default_Handler
+
+   .weak      TIM8_BRK_TIM12_IRQHandler
+   .thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler
+
+   .weak      TIM8_UP_TIM13_IRQHandler
+   .thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler
+
+   .weak      TIM8_TRG_COM_TIM14_IRQHandler
+   .thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler
+
+   .weak      TIM8_CC_IRQHandler
+   .thumb_set TIM8_CC_IRQHandler,Default_Handler
+
+   .weak      DMA1_Stream7_IRQHandler
+   .thumb_set DMA1_Stream7_IRQHandler,Default_Handler
+
+   .weak      FMC_IRQHandler
+   .thumb_set FMC_IRQHandler,Default_Handler
+
+   .weak      SDMMC1_IRQHandler
+   .thumb_set SDMMC1_IRQHandler,Default_Handler
+
+   .weak      TIM5_IRQHandler
+   .thumb_set TIM5_IRQHandler,Default_Handler
+
+   .weak      SPI3_IRQHandler
+   .thumb_set SPI3_IRQHandler,Default_Handler
+
+   .weak      UART4_IRQHandler
+   .thumb_set UART4_IRQHandler,Default_Handler
+
+   .weak      UART5_IRQHandler
+   .thumb_set UART5_IRQHandler,Default_Handler
+
+   .weak      TIM6_DAC_IRQHandler
+   .thumb_set TIM6_DAC_IRQHandler,Default_Handler
+
+   .weak      TIM7_IRQHandler
+   .thumb_set TIM7_IRQHandler,Default_Handler
+
+   .weak      DMA2_Stream0_IRQHandler
+   .thumb_set DMA2_Stream0_IRQHandler,Default_Handler
+
+   .weak      DMA2_Stream1_IRQHandler
+   .thumb_set DMA2_Stream1_IRQHandler,Default_Handler
+
+   .weak      DMA2_Stream2_IRQHandler
+   .thumb_set DMA2_Stream2_IRQHandler,Default_Handler
+
+   .weak      DMA2_Stream3_IRQHandler
+   .thumb_set DMA2_Stream3_IRQHandler,Default_Handler
+
+   .weak      DMA2_Stream4_IRQHandler
+   .thumb_set DMA2_Stream4_IRQHandler,Default_Handler
+
+   .weak      ETH_IRQHandler
+   .thumb_set ETH_IRQHandler,Default_Handler
+
+   .weak      ETH_WKUP_IRQHandler
+   .thumb_set ETH_WKUP_IRQHandler,Default_Handler
+
+   .weak      FDCAN_CAL_IRQHandler
+   .thumb_set FDCAN_CAL_IRQHandler,Default_Handler
+
+   .weak      DMA2_Stream5_IRQHandler
+   .thumb_set DMA2_Stream5_IRQHandler,Default_Handler
+
+   .weak      DMA2_Stream6_IRQHandler
+   .thumb_set DMA2_Stream6_IRQHandler,Default_Handler
+
+   .weak      DMA2_Stream7_IRQHandler
+   .thumb_set DMA2_Stream7_IRQHandler,Default_Handler
+
+   .weak      USART6_IRQHandler
+   .thumb_set USART6_IRQHandler,Default_Handler
+
+   .weak      I2C3_EV_IRQHandler
+   .thumb_set I2C3_EV_IRQHandler,Default_Handler
+
+   .weak      I2C3_ER_IRQHandler
+   .thumb_set I2C3_ER_IRQHandler,Default_Handler
+
+   .weak      OTG_HS_EP1_OUT_IRQHandler
+   .thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler
+
+   .weak      OTG_HS_EP1_IN_IRQHandler
+   .thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler
+
+   .weak      OTG_HS_WKUP_IRQHandler
+   .thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler
+
+   .weak      OTG_HS_IRQHandler
+   .thumb_set OTG_HS_IRQHandler,Default_Handler
+
+   .weak      DCMI_PSSI_IRQHandler
+   .thumb_set DCMI_PSSI_IRQHandler,Default_Handler
+
+   .weak      RNG_IRQHandler
+   .thumb_set RNG_IRQHandler,Default_Handler
+
+   .weak      FPU_IRQHandler
+   .thumb_set FPU_IRQHandler,Default_Handler
+
+   .weak      UART7_IRQHandler
+   .thumb_set UART7_IRQHandler,Default_Handler
+
+   .weak      UART8_IRQHandler
+   .thumb_set UART8_IRQHandler,Default_Handler
+
+   .weak      SPI4_IRQHandler
+   .thumb_set SPI4_IRQHandler,Default_Handler
+
+   .weak      SPI5_IRQHandler
+   .thumb_set SPI5_IRQHandler,Default_Handler
+
+   .weak      SPI6_IRQHandler
+   .thumb_set SPI6_IRQHandler,Default_Handler
+
+   .weak      SAI1_IRQHandler
+   .thumb_set SAI1_IRQHandler,Default_Handler
+
+   .weak      LTDC_IRQHandler
+   .thumb_set LTDC_IRQHandler,Default_Handler
+
+   .weak      LTDC_ER_IRQHandler
+   .thumb_set LTDC_ER_IRQHandler,Default_Handler
+
+   .weak      DMA2D_IRQHandler
+   .thumb_set DMA2D_IRQHandler,Default_Handler
+
+   .weak      OCTOSPI1_IRQHandler
+   .thumb_set OCTOSPI1_IRQHandler,Default_Handler
+
+   .weak      LPTIM1_IRQHandler
+   .thumb_set LPTIM1_IRQHandler,Default_Handler
+
+   .weak      CEC_IRQHandler
+   .thumb_set CEC_IRQHandler,Default_Handler
+
+   .weak      I2C4_EV_IRQHandler
+   .thumb_set I2C4_EV_IRQHandler,Default_Handler
+
+   .weak      I2C4_ER_IRQHandler
+   .thumb_set I2C4_ER_IRQHandler,Default_Handler
+
+   .weak      SPDIF_RX_IRQHandler
+   .thumb_set SPDIF_RX_IRQHandler,Default_Handler
+
+   .weak      DMAMUX1_OVR_IRQHandler
+   .thumb_set DMAMUX1_OVR_IRQHandler,Default_Handler
+
+   .weak      DFSDM1_FLT0_IRQHandler
+   .thumb_set DFSDM1_FLT0_IRQHandler,Default_Handler
+
+   .weak      DFSDM1_FLT1_IRQHandler
+   .thumb_set DFSDM1_FLT1_IRQHandler,Default_Handler
+
+   .weak      DFSDM1_FLT2_IRQHandler
+   .thumb_set DFSDM1_FLT2_IRQHandler,Default_Handler
+
+   .weak      DFSDM1_FLT3_IRQHandler
+   .thumb_set DFSDM1_FLT3_IRQHandler,Default_Handler
+
+   .weak      SWPMI1_IRQHandler
+   .thumb_set SWPMI1_IRQHandler,Default_Handler
+
+   .weak      TIM15_IRQHandler
+   .thumb_set TIM15_IRQHandler,Default_Handler
+
+   .weak      TIM16_IRQHandler
+   .thumb_set TIM16_IRQHandler,Default_Handler
+
+   .weak      TIM17_IRQHandler
+   .thumb_set TIM17_IRQHandler,Default_Handler
+
+   .weak      MDIOS_WKUP_IRQHandler
+   .thumb_set MDIOS_WKUP_IRQHandler,Default_Handler
+
+   .weak      MDIOS_IRQHandler
+   .thumb_set MDIOS_IRQHandler,Default_Handler
+
+   .weak      MDMA_IRQHandler
+   .thumb_set MDMA_IRQHandler,Default_Handler
+
+   .weak      SDMMC2_IRQHandler
+   .thumb_set SDMMC2_IRQHandler,Default_Handler
+
+   .weak      HSEM1_IRQHandler
+   .thumb_set HSEM1_IRQHandler,Default_Handler
+
+   .weak      ADC3_IRQHandler
+   .thumb_set ADC3_IRQHandler,Default_Handler
+
+   .weak      DMAMUX2_OVR_IRQHandler
+   .thumb_set DMAMUX2_OVR_IRQHandler,Default_Handler
+
+   .weak      BDMA_Channel0_IRQHandler
+   .thumb_set BDMA_Channel0_IRQHandler,Default_Handler
+
+   .weak      BDMA_Channel1_IRQHandler
+   .thumb_set BDMA_Channel1_IRQHandler,Default_Handler
+
+   .weak      BDMA_Channel2_IRQHandler
+   .thumb_set BDMA_Channel2_IRQHandler,Default_Handler
+
+   .weak      BDMA_Channel3_IRQHandler
+   .thumb_set BDMA_Channel3_IRQHandler,Default_Handler
+
+   .weak      BDMA_Channel4_IRQHandler
+   .thumb_set BDMA_Channel4_IRQHandler,Default_Handler
+
+   .weak      BDMA_Channel5_IRQHandler
+   .thumb_set BDMA_Channel5_IRQHandler,Default_Handler
+
+   .weak      BDMA_Channel6_IRQHandler
+   .thumb_set BDMA_Channel6_IRQHandler,Default_Handler
+
+   .weak      BDMA_Channel7_IRQHandler
+   .thumb_set BDMA_Channel7_IRQHandler,Default_Handler
+
+   .weak      COMP1_IRQHandler
+   .thumb_set COMP1_IRQHandler,Default_Handler
+
+   .weak      LPTIM2_IRQHandler
+   .thumb_set LPTIM2_IRQHandler,Default_Handler
+
+   .weak      LPTIM3_IRQHandler
+   .thumb_set LPTIM3_IRQHandler,Default_Handler
+
+   .weak      LPTIM4_IRQHandler
+   .thumb_set LPTIM4_IRQHandler,Default_Handler
+
+   .weak      LPTIM5_IRQHandler
+   .thumb_set LPTIM5_IRQHandler,Default_Handler
+
+   .weak      LPUART1_IRQHandler
+   .thumb_set LPUART1_IRQHandler,Default_Handler
+
+   .weak      CRS_IRQHandler
+   .thumb_set CRS_IRQHandler,Default_Handler
+
+   .weak      ECC_IRQHandler
+   .thumb_set ECC_IRQHandler,Default_Handler
+
+   .weak      SAI4_IRQHandler
+   .thumb_set SAI4_IRQHandler,Default_Handler
+
+   .weak      DTS_IRQHandler
+   .thumb_set DTS_IRQHandler,Default_Handler
+
+   .weak      WAKEUP_PIN_IRQHandler
+   .thumb_set WAKEUP_PIN_IRQHandler,Default_Handler
+
+   .weak      OCTOSPI2_IRQHandler
+   .thumb_set OCTOSPI2_IRQHandler,Default_Handler
+
+   .weak      FMAC_IRQHandler
+   .thumb_set FMAC_IRQHandler,Default_Handler
+
+   .weak      CORDIC_IRQHandler
+   .thumb_set CORDIC_IRQHandler,Default_Handler
+
+   .weak      UART9_IRQHandler
+   .thumb_set UART9_IRQHandler,Default_Handler
+
+   .weak      USART10_IRQHandler
+   .thumb_set USART10_IRQHandler,Default_Handler
+
+   .weak      I2C5_EV_IRQHandler
+   .thumb_set I2C5_EV_IRQHandler,Default_Handler
+
+   .weak      I2C5_ER_IRQHandler
+   .thumb_set I2C5_ER_IRQHandler,Default_Handler
+
+   .weak      FDCAN3_IT0_IRQHandler
+   .thumb_set FDCAN3_IT0_IRQHandler,Default_Handler
+
+   .weak      FDCAN3_IT1_IRQHandler
+   .thumb_set FDCAN3_IT1_IRQHandler,Default_Handler
+
+   .weak      TIM23_IRQHandler
+   .thumb_set TIM23_IRQHandler,Default_Handler
+
+   .weak      TIM24_IRQHandler
+   .thumb_set TIM24_IRQHandler,Default_Handler
+
+
Index: ctrl/firmware/Main/CubeMX/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_tim.h
===================================================================
--- ctrl/firmware/Main/CubeMX/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_tim.h	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_tim.h	(revision 54)
@@ -0,0 +1,2466 @@
+/**
+  ******************************************************************************
+  * @file    stm32h7xx_hal_tim.h
+  * @author  MCD Application Team
+  * @brief   Header file of TIM HAL module.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2017 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.
+  *
+  ******************************************************************************
+  */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef STM32H7xx_HAL_TIM_H
+#define STM32H7xx_HAL_TIM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32h7xx_hal_def.h"
+
+/** @addtogroup STM32H7xx_HAL_Driver
+  * @{
+  */
+
+/** @addtogroup TIM
+  * @{
+  */
+
+/* Exported types ------------------------------------------------------------*/
+/** @defgroup TIM_Exported_Types TIM Exported Types
+  * @{
+  */
+
+/**
+  * @brief  TIM Time base Configuration Structure definition
+  */
+typedef struct
+{
+  uint32_t Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.
+                                   This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
+
+  uint32_t CounterMode;       /*!< Specifies the counter mode.
+                                   This parameter can be a value of @ref TIM_Counter_Mode */
+
+  uint32_t Period;            /*!< Specifies the period value to be loaded into the active
+                                   Auto-Reload Register at the next update event.
+                                   This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF.  */
+
+  uint32_t ClockDivision;     /*!< Specifies the clock division.
+                                   This parameter can be a value of @ref TIM_ClockDivision */
+
+  uint32_t RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter
+                                    reaches zero, an update event is generated and counting restarts
+                                    from the RCR value (N).
+                                    This means in PWM mode that (N+1) corresponds to:
+                                        - the number of PWM periods in edge-aligned mode
+                                        - the number of half PWM period in center-aligned mode
+                                     GP timers: this parameter must be a number between Min_Data = 0x00 and
+                                     Max_Data = 0xFF.
+                                     Advanced timers: this parameter must be a number between Min_Data = 0x0000 and
+                                     Max_Data = 0xFFFF. */
+
+  uint32_t AutoReloadPreload;  /*!< Specifies the auto-reload preload.
+                                   This parameter can be a value of @ref TIM_AutoReloadPreload */
+} TIM_Base_InitTypeDef;
+
+/**
+  * @brief  TIM Output Compare Configuration Structure definition
+  */
+typedef struct
+{
+  uint32_t OCMode;        /*!< Specifies the TIM mode.
+                               This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */
+
+  uint32_t Pulse;         /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
+                               This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
+
+  uint32_t OCPolarity;    /*!< Specifies the output polarity.
+                               This parameter can be a value of @ref TIM_Output_Compare_Polarity */
+
+  uint32_t OCNPolarity;   /*!< Specifies the complementary output polarity.
+                               This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
+                               @note This parameter is valid only for timer instances supporting break feature. */
+
+  uint32_t OCFastMode;    /*!< Specifies the Fast mode state.
+                               This parameter can be a value of @ref TIM_Output_Fast_State
+                               @note This parameter is valid only in PWM1 and PWM2 mode. */
+
+
+  uint32_t OCIdleState;   /*!< Specifies the TIM Output Compare pin state during Idle state.
+                               This parameter can be a value of @ref TIM_Output_Compare_Idle_State
+                               @note This parameter is valid only for timer instances supporting break feature. */
+
+  uint32_t OCNIdleState;  /*!< Specifies the TIM Output Compare pin state during Idle state.
+                               This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
+                               @note This parameter is valid only for timer instances supporting break feature. */
+} TIM_OC_InitTypeDef;
+
+/**
+  * @brief  TIM One Pulse Mode Configuration Structure definition
+  */
+typedef struct
+{
+  uint32_t OCMode;        /*!< Specifies the TIM mode.
+                               This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */
+
+  uint32_t Pulse;         /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
+                               This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
+
+  uint32_t OCPolarity;    /*!< Specifies the output polarity.
+                               This parameter can be a value of @ref TIM_Output_Compare_Polarity */
+
+  uint32_t OCNPolarity;   /*!< Specifies the complementary output polarity.
+                               This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
+                               @note This parameter is valid only for timer instances supporting break feature. */
+
+  uint32_t OCIdleState;   /*!< Specifies the TIM Output Compare pin state during Idle state.
+                               This parameter can be a value of @ref TIM_Output_Compare_Idle_State
+                               @note This parameter is valid only for timer instances supporting break feature. */
+
+  uint32_t OCNIdleState;  /*!< Specifies the TIM Output Compare pin state during Idle state.
+                               This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
+                               @note This parameter is valid only for timer instances supporting break feature. */
+
+  uint32_t ICPolarity;    /*!< Specifies the active edge of the input signal.
+                               This parameter can be a value of @ref TIM_Input_Capture_Polarity */
+
+  uint32_t ICSelection;   /*!< Specifies the input.
+                              This parameter can be a value of @ref TIM_Input_Capture_Selection */
+
+  uint32_t ICFilter;      /*!< Specifies the input capture filter.
+                              This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
+} TIM_OnePulse_InitTypeDef;
+
+/**
+  * @brief  TIM Input Capture Configuration Structure definition
+  */
+typedef struct
+{
+  uint32_t  ICPolarity;  /*!< Specifies the active edge of the input signal.
+                              This parameter can be a value of @ref TIM_Input_Capture_Polarity */
+
+  uint32_t ICSelection;  /*!< Specifies the input.
+                              This parameter can be a value of @ref TIM_Input_Capture_Selection */
+
+  uint32_t ICPrescaler;  /*!< Specifies the Input Capture Prescaler.
+                              This parameter can be a value of @ref TIM_Input_Capture_Prescaler */
+
+  uint32_t ICFilter;     /*!< Specifies the input capture filter.
+                              This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
+} TIM_IC_InitTypeDef;
+
+/**
+  * @brief  TIM Encoder Configuration Structure definition
+  */
+typedef struct
+{
+  uint32_t EncoderMode;   /*!< Specifies the active edge of the input signal.
+                               This parameter can be a value of @ref TIM_Encoder_Mode */
+
+  uint32_t IC1Polarity;   /*!< Specifies the active edge of the input signal.
+                               This parameter can be a value of @ref TIM_Encoder_Input_Polarity */
+
+  uint32_t IC1Selection;  /*!< Specifies the input.
+                               This parameter can be a value of @ref TIM_Input_Capture_Selection */
+
+  uint32_t IC1Prescaler;  /*!< Specifies the Input Capture Prescaler.
+                               This parameter can be a value of @ref TIM_Input_Capture_Prescaler */
+
+  uint32_t IC1Filter;     /*!< Specifies the input capture filter.
+                               This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
+
+  uint32_t IC2Polarity;   /*!< Specifies the active edge of the input signal.
+                               This parameter can be a value of @ref TIM_Encoder_Input_Polarity */
+
+  uint32_t IC2Selection;  /*!< Specifies the input.
+                              This parameter can be a value of @ref TIM_Input_Capture_Selection */
+
+  uint32_t IC2Prescaler;  /*!< Specifies the Input Capture Prescaler.
+                               This parameter can be a value of @ref TIM_Input_Capture_Prescaler */
+
+  uint32_t IC2Filter;     /*!< Specifies the input capture filter.
+                               This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
+} TIM_Encoder_InitTypeDef;
+
+/**
+  * @brief  Clock Configuration Handle Structure definition
+  */
+typedef struct
+{
+  uint32_t ClockSource;     /*!< TIM clock sources
+                                 This parameter can be a value of @ref TIM_Clock_Source */
+  uint32_t ClockPolarity;   /*!< TIM clock polarity
+                                 This parameter can be a value of @ref TIM_Clock_Polarity */
+  uint32_t ClockPrescaler;  /*!< TIM clock prescaler
+                                 This parameter can be a value of @ref TIM_Clock_Prescaler */
+  uint32_t ClockFilter;     /*!< TIM clock filter
+                                 This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
+} TIM_ClockConfigTypeDef;
+
+/**
+  * @brief  TIM Clear Input Configuration Handle Structure definition
+  */
+typedef struct
+{
+  uint32_t ClearInputState;      /*!< TIM clear Input state
+                                      This parameter can be ENABLE or DISABLE */
+  uint32_t ClearInputSource;     /*!< TIM clear Input sources
+                                      This parameter can be a value of @ref TIM_ClearInput_Source */
+  uint32_t ClearInputPolarity;   /*!< TIM Clear Input polarity
+                                      This parameter can be a value of @ref TIM_ClearInput_Polarity */
+  uint32_t ClearInputPrescaler;  /*!< TIM Clear Input prescaler
+                                      This parameter must be 0: When OCRef clear feature is used with ETR source,
+                                      ETR prescaler must be off */
+  uint32_t ClearInputFilter;     /*!< TIM Clear Input filter
+                                      This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
+} TIM_ClearInputConfigTypeDef;
+
+/**
+  * @brief  TIM Master configuration Structure definition
+  * @note   Advanced timers provide TRGO2 internal line which is redirected
+  *         to the ADC
+  */
+typedef struct
+{
+  uint32_t  MasterOutputTrigger;   /*!< Trigger output (TRGO) selection
+                                        This parameter can be a value of @ref TIM_Master_Mode_Selection */
+  uint32_t  MasterOutputTrigger2;  /*!< Trigger output2 (TRGO2) selection
+                                        This parameter can be a value of @ref TIM_Master_Mode_Selection_2 */
+  uint32_t  MasterSlaveMode;       /*!< Master/slave mode selection
+                                        This parameter can be a value of @ref TIM_Master_Slave_Mode
+                                        @note When the Master/slave mode is enabled, the effect of
+                                        an event on the trigger input (TRGI) is delayed to allow a
+                                        perfect synchronization between the current timer and its
+                                        slaves (through TRGO). It is not mandatory in case of timer
+                                        synchronization mode. */
+} TIM_MasterConfigTypeDef;
+
+/**
+  * @brief  TIM Slave configuration Structure definition
+  */
+typedef struct
+{
+  uint32_t  SlaveMode;         /*!< Slave mode selection
+                                    This parameter can be a value of @ref TIM_Slave_Mode */
+  uint32_t  InputTrigger;      /*!< Input Trigger source
+                                    This parameter can be a value of @ref TIM_Trigger_Selection */
+  uint32_t  TriggerPolarity;   /*!< Input Trigger polarity
+                                    This parameter can be a value of @ref TIM_Trigger_Polarity */
+  uint32_t  TriggerPrescaler;  /*!< Input trigger prescaler
+                                    This parameter can be a value of @ref TIM_Trigger_Prescaler */
+  uint32_t  TriggerFilter;     /*!< Input trigger filter
+                                    This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF  */
+
+} TIM_SlaveConfigTypeDef;
+
+/**
+  * @brief  TIM Break input(s) and Dead time configuration Structure definition
+  * @note   2 break inputs can be configured (BKIN and BKIN2) with configurable
+  *        filter and polarity.
+  */
+typedef struct
+{
+  uint32_t OffStateRunMode;      /*!< TIM off state in run mode, This parameter can be a value of @ref TIM_OSSR_Off_State_Selection_for_Run_mode_state */
+
+  uint32_t OffStateIDLEMode;     /*!< TIM off state in IDLE mode, This parameter can be a value of @ref TIM_OSSI_Off_State_Selection_for_Idle_mode_state */
+
+  uint32_t LockLevel;            /*!< TIM Lock level, This parameter can be a value of @ref TIM_Lock_level */
+
+  uint32_t DeadTime;             /*!< TIM dead Time, This parameter can be a number between Min_Data = 0x00 and Max_Data = 0xFF */
+
+  uint32_t BreakState;           /*!< TIM Break State, This parameter can be a value of @ref TIM_Break_Input_enable_disable */
+
+  uint32_t BreakPolarity;        /*!< TIM Break input polarity, This parameter can be a value of @ref TIM_Break_Polarity */
+
+  uint32_t BreakFilter;          /*!< Specifies the break input filter.This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
+
+#if defined(TIM_BDTR_BKBID)
+  uint32_t BreakAFMode;          /*!< Specifies the alternate function mode of the break input.This parameter can be a value of @ref TIM_Break_Input_AF_Mode */
+
+#endif /* TIM_BDTR_BKBID */
+  uint32_t Break2State;          /*!< TIM Break2 State, This parameter can be a value of @ref TIM_Break2_Input_enable_disable */
+
+  uint32_t Break2Polarity;       /*!< TIM Break2 input polarity, This parameter can be a value of @ref TIM_Break2_Polarity */
+
+  uint32_t Break2Filter;         /*!< TIM break2 input filter.This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
+
+#if defined(TIM_BDTR_BKBID)
+  uint32_t Break2AFMode;         /*!< Specifies the alternate function mode of the break2 input.This parameter can be a value of @ref TIM_Break2_Input_AF_Mode */
+
+#endif /* TIM_BDTR_BKBID */
+  uint32_t AutomaticOutput;      /*!< TIM Automatic Output Enable state, This parameter can be a value of @ref TIM_AOE_Bit_Set_Reset */
+
+} TIM_BreakDeadTimeConfigTypeDef;
+
+/**
+  * @brief  HAL State structures definition
+  */
+typedef enum
+{
+  HAL_TIM_STATE_RESET             = 0x00U,    /*!< Peripheral not yet initialized or disabled  */
+  HAL_TIM_STATE_READY             = 0x01U,    /*!< Peripheral Initialized and ready for use    */
+  HAL_TIM_STATE_BUSY              = 0x02U,    /*!< An internal process is ongoing              */
+  HAL_TIM_STATE_TIMEOUT           = 0x03U,    /*!< Timeout state                               */
+  HAL_TIM_STATE_ERROR             = 0x04U     /*!< Reception process is ongoing                */
+} HAL_TIM_StateTypeDef;
+
+/**
+  * @brief  TIM Channel States definition
+  */
+typedef enum
+{
+  HAL_TIM_CHANNEL_STATE_RESET             = 0x00U,    /*!< TIM Channel initial state                         */
+  HAL_TIM_CHANNEL_STATE_READY             = 0x01U,    /*!< TIM Channel ready for use                         */
+  HAL_TIM_CHANNEL_STATE_BUSY              = 0x02U,    /*!< An internal process is ongoing on the TIM channel */
+} HAL_TIM_ChannelStateTypeDef;
+
+/**
+  * @brief  DMA Burst States definition
+  */
+typedef enum
+{
+  HAL_DMA_BURST_STATE_RESET             = 0x00U,    /*!< DMA Burst initial state */
+  HAL_DMA_BURST_STATE_READY             = 0x01U,    /*!< DMA Burst ready for use */
+  HAL_DMA_BURST_STATE_BUSY              = 0x02U,    /*!< Ongoing DMA Burst       */
+} HAL_TIM_DMABurstStateTypeDef;
+
+/**
+  * @brief  HAL Active channel structures definition
+  */
+typedef enum
+{
+  HAL_TIM_ACTIVE_CHANNEL_1        = 0x01U,    /*!< The active channel is 1     */
+  HAL_TIM_ACTIVE_CHANNEL_2        = 0x02U,    /*!< The active channel is 2     */
+  HAL_TIM_ACTIVE_CHANNEL_3        = 0x04U,    /*!< The active channel is 3     */
+  HAL_TIM_ACTIVE_CHANNEL_4        = 0x08U,    /*!< The active channel is 4     */
+  HAL_TIM_ACTIVE_CHANNEL_5        = 0x10U,    /*!< The active channel is 5     */
+  HAL_TIM_ACTIVE_CHANNEL_6        = 0x20U,    /*!< The active channel is 6     */
+  HAL_TIM_ACTIVE_CHANNEL_CLEARED  = 0x00U     /*!< All active channels cleared */
+} HAL_TIM_ActiveChannel;
+
+/**
+  * @brief  TIM Time Base Handle Structure definition
+  */
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+typedef struct __TIM_HandleTypeDef
+#else
+typedef struct
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+{
+  TIM_TypeDef                        *Instance;         /*!< Register base address                             */
+  TIM_Base_InitTypeDef               Init;              /*!< TIM Time Base required parameters                 */
+  HAL_TIM_ActiveChannel              Channel;           /*!< Active channel                                    */
+  DMA_HandleTypeDef                  *hdma[7];          /*!< DMA Handlers array
+                                                             This array is accessed by a @ref DMA_Handle_index */
+  HAL_LockTypeDef                    Lock;              /*!< Locking object                                    */
+  __IO HAL_TIM_StateTypeDef          State;             /*!< TIM operation state                               */
+  __IO HAL_TIM_ChannelStateTypeDef   ChannelState[6];   /*!< TIM channel operation state                       */
+  __IO HAL_TIM_ChannelStateTypeDef   ChannelNState[4];  /*!< TIM complementary channel operation state         */
+  __IO HAL_TIM_DMABurstStateTypeDef  DMABurstState;     /*!< DMA burst operation state                         */
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  void (* Base_MspInitCallback)(struct __TIM_HandleTypeDef *htim);              /*!< TIM Base Msp Init Callback                              */
+  void (* Base_MspDeInitCallback)(struct __TIM_HandleTypeDef *htim);            /*!< TIM Base Msp DeInit Callback                            */
+  void (* IC_MspInitCallback)(struct __TIM_HandleTypeDef *htim);                /*!< TIM IC Msp Init Callback                                */
+  void (* IC_MspDeInitCallback)(struct __TIM_HandleTypeDef *htim);              /*!< TIM IC Msp DeInit Callback                              */
+  void (* OC_MspInitCallback)(struct __TIM_HandleTypeDef *htim);                /*!< TIM OC Msp Init Callback                                */
+  void (* OC_MspDeInitCallback)(struct __TIM_HandleTypeDef *htim);              /*!< TIM OC Msp DeInit Callback                              */
+  void (* PWM_MspInitCallback)(struct __TIM_HandleTypeDef *htim);               /*!< TIM PWM Msp Init Callback                               */
+  void (* PWM_MspDeInitCallback)(struct __TIM_HandleTypeDef *htim);             /*!< TIM PWM Msp DeInit Callback                             */
+  void (* OnePulse_MspInitCallback)(struct __TIM_HandleTypeDef *htim);          /*!< TIM One Pulse Msp Init Callback                         */
+  void (* OnePulse_MspDeInitCallback)(struct __TIM_HandleTypeDef *htim);        /*!< TIM One Pulse Msp DeInit Callback                       */
+  void (* Encoder_MspInitCallback)(struct __TIM_HandleTypeDef *htim);           /*!< TIM Encoder Msp Init Callback                           */
+  void (* Encoder_MspDeInitCallback)(struct __TIM_HandleTypeDef *htim);         /*!< TIM Encoder Msp DeInit Callback                         */
+  void (* HallSensor_MspInitCallback)(struct __TIM_HandleTypeDef *htim);        /*!< TIM Hall Sensor Msp Init Callback                       */
+  void (* HallSensor_MspDeInitCallback)(struct __TIM_HandleTypeDef *htim);      /*!< TIM Hall Sensor Msp DeInit Callback                     */
+  void (* PeriodElapsedCallback)(struct __TIM_HandleTypeDef *htim);             /*!< TIM Period Elapsed Callback                             */
+  void (* PeriodElapsedHalfCpltCallback)(struct __TIM_HandleTypeDef *htim);     /*!< TIM Period Elapsed half complete Callback               */
+  void (* TriggerCallback)(struct __TIM_HandleTypeDef *htim);                   /*!< TIM Trigger Callback                                    */
+  void (* TriggerHalfCpltCallback)(struct __TIM_HandleTypeDef *htim);           /*!< TIM Trigger half complete Callback                      */
+  void (* IC_CaptureCallback)(struct __TIM_HandleTypeDef *htim);                /*!< TIM Input Capture Callback                              */
+  void (* IC_CaptureHalfCpltCallback)(struct __TIM_HandleTypeDef *htim);        /*!< TIM Input Capture half complete Callback                */
+  void (* OC_DelayElapsedCallback)(struct __TIM_HandleTypeDef *htim);           /*!< TIM Output Compare Delay Elapsed Callback               */
+  void (* PWM_PulseFinishedCallback)(struct __TIM_HandleTypeDef *htim);         /*!< TIM PWM Pulse Finished Callback                         */
+  void (* PWM_PulseFinishedHalfCpltCallback)(struct __TIM_HandleTypeDef *htim); /*!< TIM PWM Pulse Finished half complete Callback           */
+  void (* ErrorCallback)(struct __TIM_HandleTypeDef *htim);                     /*!< TIM Error Callback                                      */
+  void (* CommutationCallback)(struct __TIM_HandleTypeDef *htim);               /*!< TIM Commutation Callback                                */
+  void (* CommutationHalfCpltCallback)(struct __TIM_HandleTypeDef *htim);       /*!< TIM Commutation half complete Callback                  */
+  void (* BreakCallback)(struct __TIM_HandleTypeDef *htim);                     /*!< TIM Break Callback                                      */
+  void (* Break2Callback)(struct __TIM_HandleTypeDef *htim);                    /*!< TIM Break2 Callback                                     */
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+} TIM_HandleTypeDef;
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+/**
+  * @brief  HAL TIM Callback ID enumeration definition
+  */
+typedef enum
+{
+  HAL_TIM_BASE_MSPINIT_CB_ID              = 0x00U   /*!< TIM Base MspInit Callback ID                               */
+  , HAL_TIM_BASE_MSPDEINIT_CB_ID          = 0x01U   /*!< TIM Base MspDeInit Callback ID                             */
+  , HAL_TIM_IC_MSPINIT_CB_ID              = 0x02U   /*!< TIM IC MspInit Callback ID                                 */
+  , HAL_TIM_IC_MSPDEINIT_CB_ID            = 0x03U   /*!< TIM IC MspDeInit Callback ID                               */
+  , HAL_TIM_OC_MSPINIT_CB_ID              = 0x04U   /*!< TIM OC MspInit Callback ID                                 */
+  , HAL_TIM_OC_MSPDEINIT_CB_ID            = 0x05U   /*!< TIM OC MspDeInit Callback ID                               */
+  , HAL_TIM_PWM_MSPINIT_CB_ID             = 0x06U   /*!< TIM PWM MspInit Callback ID                                */
+  , HAL_TIM_PWM_MSPDEINIT_CB_ID           = 0x07U   /*!< TIM PWM MspDeInit Callback ID                              */
+  , HAL_TIM_ONE_PULSE_MSPINIT_CB_ID       = 0x08U   /*!< TIM One Pulse MspInit Callback ID                          */
+  , HAL_TIM_ONE_PULSE_MSPDEINIT_CB_ID     = 0x09U   /*!< TIM One Pulse MspDeInit Callback ID                        */
+  , HAL_TIM_ENCODER_MSPINIT_CB_ID         = 0x0AU   /*!< TIM Encoder MspInit Callback ID                            */
+  , HAL_TIM_ENCODER_MSPDEINIT_CB_ID       = 0x0BU   /*!< TIM Encoder MspDeInit Callback ID                          */
+  , HAL_TIM_HALL_SENSOR_MSPINIT_CB_ID     = 0x0CU   /*!< TIM Hall Sensor MspDeInit Callback ID                      */
+  , HAL_TIM_HALL_SENSOR_MSPDEINIT_CB_ID   = 0x0DU   /*!< TIM Hall Sensor MspDeInit Callback ID                      */
+  , HAL_TIM_PERIOD_ELAPSED_CB_ID          = 0x0EU   /*!< TIM Period Elapsed Callback ID                             */
+  , HAL_TIM_PERIOD_ELAPSED_HALF_CB_ID     = 0x0FU   /*!< TIM Period Elapsed half complete Callback ID               */
+  , HAL_TIM_TRIGGER_CB_ID                 = 0x10U   /*!< TIM Trigger Callback ID                                    */
+  , HAL_TIM_TRIGGER_HALF_CB_ID            = 0x11U   /*!< TIM Trigger half complete Callback ID                      */
+  , HAL_TIM_IC_CAPTURE_CB_ID              = 0x12U   /*!< TIM Input Capture Callback ID                              */
+  , HAL_TIM_IC_CAPTURE_HALF_CB_ID         = 0x13U   /*!< TIM Input Capture half complete Callback ID                */
+  , HAL_TIM_OC_DELAY_ELAPSED_CB_ID        = 0x14U   /*!< TIM Output Compare Delay Elapsed Callback ID               */
+  , HAL_TIM_PWM_PULSE_FINISHED_CB_ID      = 0x15U   /*!< TIM PWM Pulse Finished Callback ID                         */
+  , HAL_TIM_PWM_PULSE_FINISHED_HALF_CB_ID = 0x16U   /*!< TIM PWM Pulse Finished half complete Callback ID           */
+  , HAL_TIM_ERROR_CB_ID                   = 0x17U   /*!< TIM Error Callback ID                                      */
+  , HAL_TIM_COMMUTATION_CB_ID             = 0x18U   /*!< TIM Commutation Callback ID                                */
+  , HAL_TIM_COMMUTATION_HALF_CB_ID        = 0x19U   /*!< TIM Commutation half complete Callback ID                  */
+  , HAL_TIM_BREAK_CB_ID                   = 0x1AU   /*!< TIM Break Callback ID                                      */
+  , HAL_TIM_BREAK2_CB_ID                  = 0x1BU   /*!< TIM Break2 Callback ID                                     */
+} HAL_TIM_CallbackIDTypeDef;
+
+/**
+  * @brief  HAL TIM Callback pointer definition
+  */
+typedef  void (*pTIM_CallbackTypeDef)(TIM_HandleTypeDef *htim);  /*!< pointer to the TIM callback function */
+
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+/**
+  * @}
+  */
+/* End of exported types -----------------------------------------------------*/
+
+/* Exported constants --------------------------------------------------------*/
+/** @defgroup TIM_Exported_Constants TIM Exported Constants
+  * @{
+  */
+
+/** @defgroup TIM_ClearInput_Source TIM Clear Input Source
+  * @{
+  */
+#define TIM_CLEARINPUTSOURCE_NONE           0x00000000U   /*!< OCREF_CLR is disabled */
+#define TIM_CLEARINPUTSOURCE_ETR            0x00000001U   /*!< OCREF_CLR is connected to ETRF input */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_DMA_Base_address TIM DMA Base Address
+  * @{
+  */
+#define TIM_DMABASE_CR1                    0x00000000U
+#define TIM_DMABASE_CR2                    0x00000001U
+#define TIM_DMABASE_SMCR                   0x00000002U
+#define TIM_DMABASE_DIER                   0x00000003U
+#define TIM_DMABASE_SR                     0x00000004U
+#define TIM_DMABASE_EGR                    0x00000005U
+#define TIM_DMABASE_CCMR1                  0x00000006U
+#define TIM_DMABASE_CCMR2                  0x00000007U
+#define TIM_DMABASE_CCER                   0x00000008U
+#define TIM_DMABASE_CNT                    0x00000009U
+#define TIM_DMABASE_PSC                    0x0000000AU
+#define TIM_DMABASE_ARR                    0x0000000BU
+#define TIM_DMABASE_RCR                    0x0000000CU
+#define TIM_DMABASE_CCR1                   0x0000000DU
+#define TIM_DMABASE_CCR2                   0x0000000EU
+#define TIM_DMABASE_CCR3                   0x0000000FU
+#define TIM_DMABASE_CCR4                   0x00000010U
+#define TIM_DMABASE_BDTR                   0x00000011U
+#define TIM_DMABASE_DCR                    0x00000012U
+#define TIM_DMABASE_DMAR                   0x00000013U
+#define TIM_DMABASE_CCMR3                  0x00000015U
+#define TIM_DMABASE_CCR5                   0x00000016U
+#define TIM_DMABASE_CCR6                   0x00000017U
+#if   defined(TIM_BREAK_INPUT_SUPPORT)
+#define TIM_DMABASE_AF1                    0x00000018U
+#define TIM_DMABASE_AF2                    0x00000019U
+#endif /* TIM_BREAK_INPUT_SUPPORT */
+#define TIM_DMABASE_TISEL                  0x0000001AU
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Event_Source TIM Event Source
+  * @{
+  */
+#define TIM_EVENTSOURCE_UPDATE              TIM_EGR_UG     /*!< Reinitialize the counter and generates an update of the registers */
+#define TIM_EVENTSOURCE_CC1                 TIM_EGR_CC1G   /*!< A capture/compare event is generated on channel 1 */
+#define TIM_EVENTSOURCE_CC2                 TIM_EGR_CC2G   /*!< A capture/compare event is generated on channel 2 */
+#define TIM_EVENTSOURCE_CC3                 TIM_EGR_CC3G   /*!< A capture/compare event is generated on channel 3 */
+#define TIM_EVENTSOURCE_CC4                 TIM_EGR_CC4G   /*!< A capture/compare event is generated on channel 4 */
+#define TIM_EVENTSOURCE_COM                 TIM_EGR_COMG   /*!< A commutation event is generated */
+#define TIM_EVENTSOURCE_TRIGGER             TIM_EGR_TG     /*!< A trigger event is generated */
+#define TIM_EVENTSOURCE_BREAK               TIM_EGR_BG     /*!< A break event is generated */
+#define TIM_EVENTSOURCE_BREAK2              TIM_EGR_B2G    /*!< A break 2 event is generated */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Input_Channel_Polarity TIM Input Channel polarity
+  * @{
+  */
+#define  TIM_INPUTCHANNELPOLARITY_RISING      0x00000000U                       /*!< Polarity for TIx source */
+#define  TIM_INPUTCHANNELPOLARITY_FALLING     TIM_CCER_CC1P                     /*!< Polarity for TIx source */
+#define  TIM_INPUTCHANNELPOLARITY_BOTHEDGE    (TIM_CCER_CC1P | TIM_CCER_CC1NP)  /*!< Polarity for TIx source */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_ETR_Polarity TIM ETR Polarity
+  * @{
+  */
+#define TIM_ETRPOLARITY_INVERTED              TIM_SMCR_ETP                      /*!< Polarity for ETR source */
+#define TIM_ETRPOLARITY_NONINVERTED           0x00000000U                       /*!< Polarity for ETR source */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_ETR_Prescaler TIM ETR Prescaler
+  * @{
+  */
+#define TIM_ETRPRESCALER_DIV1                 0x00000000U                       /*!< No prescaler is used */
+#define TIM_ETRPRESCALER_DIV2                 TIM_SMCR_ETPS_0                   /*!< ETR input source is divided by 2 */
+#define TIM_ETRPRESCALER_DIV4                 TIM_SMCR_ETPS_1                   /*!< ETR input source is divided by 4 */
+#define TIM_ETRPRESCALER_DIV8                 TIM_SMCR_ETPS                     /*!< ETR input source is divided by 8 */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Counter_Mode TIM Counter Mode
+  * @{
+  */
+#define TIM_COUNTERMODE_UP                 0x00000000U                          /*!< Counter used as up-counter   */
+#define TIM_COUNTERMODE_DOWN               TIM_CR1_DIR                          /*!< Counter used as down-counter */
+#define TIM_COUNTERMODE_CENTERALIGNED1     TIM_CR1_CMS_0                        /*!< Center-aligned mode 1        */
+#define TIM_COUNTERMODE_CENTERALIGNED2     TIM_CR1_CMS_1                        /*!< Center-aligned mode 2        */
+#define TIM_COUNTERMODE_CENTERALIGNED3     TIM_CR1_CMS                          /*!< Center-aligned mode 3        */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Update_Interrupt_Flag_Remap TIM Update Interrupt Flag Remap
+  * @{
+  */
+#define TIM_UIFREMAP_DISABLE               0x00000000U                          /*!< Update interrupt flag remap disabled */
+#define TIM_UIFREMAP_ENABLE                TIM_CR1_UIFREMAP                     /*!< Update interrupt flag remap enabled */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_ClockDivision TIM Clock Division
+  * @{
+  */
+#define TIM_CLOCKDIVISION_DIV1             0x00000000U                          /*!< Clock division: tDTS=tCK_INT   */
+#define TIM_CLOCKDIVISION_DIV2             TIM_CR1_CKD_0                        /*!< Clock division: tDTS=2*tCK_INT */
+#define TIM_CLOCKDIVISION_DIV4             TIM_CR1_CKD_1                        /*!< Clock division: tDTS=4*tCK_INT */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Output_Compare_State TIM Output Compare State
+  * @{
+  */
+#define TIM_OUTPUTSTATE_DISABLE            0x00000000U                          /*!< Capture/Compare 1 output disabled */
+#define TIM_OUTPUTSTATE_ENABLE             TIM_CCER_CC1E                        /*!< Capture/Compare 1 output enabled */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_AutoReloadPreload TIM Auto-Reload Preload
+  * @{
+  */
+#define TIM_AUTORELOAD_PRELOAD_DISABLE                0x00000000U               /*!< TIMx_ARR register is not buffered */
+#define TIM_AUTORELOAD_PRELOAD_ENABLE                 TIM_CR1_ARPE              /*!< TIMx_ARR register is buffered */
+
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Output_Fast_State TIM Output Fast State
+  * @{
+  */
+#define TIM_OCFAST_DISABLE                 0x00000000U                          /*!< Output Compare fast disable */
+#define TIM_OCFAST_ENABLE                  TIM_CCMR1_OC1FE                      /*!< Output Compare fast enable  */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Output_Compare_N_State TIM Complementary Output Compare State
+  * @{
+  */
+#define TIM_OUTPUTNSTATE_DISABLE           0x00000000U                          /*!< OCxN is disabled  */
+#define TIM_OUTPUTNSTATE_ENABLE            TIM_CCER_CC1NE                       /*!< OCxN is enabled   */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Output_Compare_Polarity TIM Output Compare Polarity
+  * @{
+  */
+#define TIM_OCPOLARITY_HIGH                0x00000000U                          /*!< Capture/Compare output polarity  */
+#define TIM_OCPOLARITY_LOW                 TIM_CCER_CC1P                        /*!< Capture/Compare output polarity  */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Output_Compare_N_Polarity TIM Complementary Output Compare Polarity
+  * @{
+  */
+#define TIM_OCNPOLARITY_HIGH               0x00000000U                          /*!< Capture/Compare complementary output polarity */
+#define TIM_OCNPOLARITY_LOW                TIM_CCER_CC1NP                       /*!< Capture/Compare complementary output polarity */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Output_Compare_Idle_State TIM Output Compare Idle State
+  * @{
+  */
+#define TIM_OCIDLESTATE_SET                TIM_CR2_OIS1                         /*!< Output Idle state: OCx=1 when MOE=0 */
+#define TIM_OCIDLESTATE_RESET              0x00000000U                          /*!< Output Idle state: OCx=0 when MOE=0 */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Output_Compare_N_Idle_State TIM Complementary Output Compare Idle State
+  * @{
+  */
+#define TIM_OCNIDLESTATE_SET               TIM_CR2_OIS1N                        /*!< Complementary output Idle state: OCxN=1 when MOE=0 */
+#define TIM_OCNIDLESTATE_RESET             0x00000000U                          /*!< Complementary output Idle state: OCxN=0 when MOE=0 */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Input_Capture_Polarity TIM Input Capture Polarity
+  * @{
+  */
+#define  TIM_ICPOLARITY_RISING             TIM_INPUTCHANNELPOLARITY_RISING      /*!< Capture triggered by rising edge on timer input                  */
+#define  TIM_ICPOLARITY_FALLING            TIM_INPUTCHANNELPOLARITY_FALLING     /*!< Capture triggered by falling edge on timer input                 */
+#define  TIM_ICPOLARITY_BOTHEDGE           TIM_INPUTCHANNELPOLARITY_BOTHEDGE    /*!< Capture triggered by both rising and falling edges on timer input*/
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Encoder_Input_Polarity TIM Encoder Input Polarity
+  * @{
+  */
+#define  TIM_ENCODERINPUTPOLARITY_RISING   TIM_INPUTCHANNELPOLARITY_RISING      /*!< Encoder input with rising edge polarity  */
+#define  TIM_ENCODERINPUTPOLARITY_FALLING  TIM_INPUTCHANNELPOLARITY_FALLING     /*!< Encoder input with falling edge polarity */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Input_Capture_Selection TIM Input Capture Selection
+  * @{
+  */
+#define TIM_ICSELECTION_DIRECTTI           TIM_CCMR1_CC1S_0                     /*!< TIM Input 1, 2, 3 or 4 is selected to be connected to IC1, IC2, IC3 or IC4, respectively */
+#define TIM_ICSELECTION_INDIRECTTI         TIM_CCMR1_CC1S_1                     /*!< TIM Input 1, 2, 3 or 4 is selected to be connected to IC2, IC1, IC4 or IC3, respectively */
+#define TIM_ICSELECTION_TRC                TIM_CCMR1_CC1S                       /*!< TIM Input 1, 2, 3 or 4 is selected to be connected to TRC */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Input_Capture_Prescaler TIM Input Capture Prescaler
+  * @{
+  */
+#define TIM_ICPSC_DIV1                     0x00000000U                          /*!< Capture performed each time an edge is detected on the capture input */
+#define TIM_ICPSC_DIV2                     TIM_CCMR1_IC1PSC_0                   /*!< Capture performed once every 2 events                                */
+#define TIM_ICPSC_DIV4                     TIM_CCMR1_IC1PSC_1                   /*!< Capture performed once every 4 events                                */
+#define TIM_ICPSC_DIV8                     TIM_CCMR1_IC1PSC                     /*!< Capture performed once every 8 events                                */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_One_Pulse_Mode TIM One Pulse Mode
+  * @{
+  */
+#define TIM_OPMODE_SINGLE                  TIM_CR1_OPM                          /*!< Counter stops counting at the next update event */
+#define TIM_OPMODE_REPETITIVE              0x00000000U                          /*!< Counter is not stopped at update event          */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Encoder_Mode TIM Encoder Mode
+  * @{
+  */
+#define TIM_ENCODERMODE_TI1                      TIM_SMCR_SMS_0                                                      /*!< Quadrature encoder mode 1, x2 mode, counts up/down on TI1FP1 edge depending on TI2FP2 level  */
+#define TIM_ENCODERMODE_TI2                      TIM_SMCR_SMS_1                                                      /*!< Quadrature encoder mode 2, x2 mode, counts up/down on TI2FP2 edge depending on TI1FP1 level. */
+#define TIM_ENCODERMODE_TI12                     (TIM_SMCR_SMS_1 | TIM_SMCR_SMS_0)                                   /*!< Quadrature encoder mode 3, x4 mode, counts up/down on both TI1FP1 and TI2FP2 edges depending on the level of the other input. */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Interrupt_definition TIM interrupt Definition
+  * @{
+  */
+#define TIM_IT_UPDATE                      TIM_DIER_UIE                         /*!< Update interrupt            */
+#define TIM_IT_CC1                         TIM_DIER_CC1IE                       /*!< Capture/Compare 1 interrupt */
+#define TIM_IT_CC2                         TIM_DIER_CC2IE                       /*!< Capture/Compare 2 interrupt */
+#define TIM_IT_CC3                         TIM_DIER_CC3IE                       /*!< Capture/Compare 3 interrupt */
+#define TIM_IT_CC4                         TIM_DIER_CC4IE                       /*!< Capture/Compare 4 interrupt */
+#define TIM_IT_COM                         TIM_DIER_COMIE                       /*!< Commutation interrupt       */
+#define TIM_IT_TRIGGER                     TIM_DIER_TIE                         /*!< Trigger interrupt           */
+#define TIM_IT_BREAK                       TIM_DIER_BIE                         /*!< Break interrupt             */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Commutation_Source  TIM Commutation Source
+  * @{
+  */
+#define TIM_COMMUTATION_TRGI              TIM_CR2_CCUS                          /*!< When Capture/compare control bits are preloaded, they are updated by setting the COMG bit or when an rising edge occurs on trigger input */
+#define TIM_COMMUTATION_SOFTWARE          0x00000000U                           /*!< When Capture/compare control bits are preloaded, they are updated by setting the COMG bit */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_DMA_sources TIM DMA Sources
+  * @{
+  */
+#define TIM_DMA_UPDATE                     TIM_DIER_UDE                         /*!< DMA request is triggered by the update event */
+#define TIM_DMA_CC1                        TIM_DIER_CC1DE                       /*!< DMA request is triggered by the capture/compare macth 1 event */
+#define TIM_DMA_CC2                        TIM_DIER_CC2DE                       /*!< DMA request is triggered by the capture/compare macth 2 event event */
+#define TIM_DMA_CC3                        TIM_DIER_CC3DE                       /*!< DMA request is triggered by the capture/compare macth 3 event event */
+#define TIM_DMA_CC4                        TIM_DIER_CC4DE                       /*!< DMA request is triggered by the capture/compare macth 4 event event */
+#define TIM_DMA_COM                        TIM_DIER_COMDE                       /*!< DMA request is triggered by the commutation event */
+#define TIM_DMA_TRIGGER                    TIM_DIER_TDE                         /*!< DMA request is triggered by the trigger event */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_CC_DMA_Request CCx DMA request selection
+  * @{
+  */
+#define TIM_CCDMAREQUEST_CC                 0x00000000U                         /*!< CCx DMA request sent when capture or compare match event occurs */
+#define TIM_CCDMAREQUEST_UPDATE             TIM_CR2_CCDS                        /*!< CCx DMA requests sent when update event occurs */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Flag_definition TIM Flag Definition
+  * @{
+  */
+#define TIM_FLAG_UPDATE                    TIM_SR_UIF                           /*!< Update interrupt flag         */
+#define TIM_FLAG_CC1                       TIM_SR_CC1IF                         /*!< Capture/Compare 1 interrupt flag */
+#define TIM_FLAG_CC2                       TIM_SR_CC2IF                         /*!< Capture/Compare 2 interrupt flag */
+#define TIM_FLAG_CC3                       TIM_SR_CC3IF                         /*!< Capture/Compare 3 interrupt flag */
+#define TIM_FLAG_CC4                       TIM_SR_CC4IF                         /*!< Capture/Compare 4 interrupt flag */
+#define TIM_FLAG_CC5                       TIM_SR_CC5IF                         /*!< Capture/Compare 5 interrupt flag */
+#define TIM_FLAG_CC6                       TIM_SR_CC6IF                         /*!< Capture/Compare 6 interrupt flag */
+#define TIM_FLAG_COM                       TIM_SR_COMIF                         /*!< Commutation interrupt flag    */
+#define TIM_FLAG_TRIGGER                   TIM_SR_TIF                           /*!< Trigger interrupt flag        */
+#define TIM_FLAG_BREAK                     TIM_SR_BIF                           /*!< Break interrupt flag          */
+#define TIM_FLAG_BREAK2                    TIM_SR_B2IF                          /*!< Break 2 interrupt flag        */
+#define TIM_FLAG_SYSTEM_BREAK              TIM_SR_SBIF                          /*!< System Break interrupt flag   */
+#define TIM_FLAG_CC1OF                     TIM_SR_CC1OF                         /*!< Capture 1 overcapture flag    */
+#define TIM_FLAG_CC2OF                     TIM_SR_CC2OF                         /*!< Capture 2 overcapture flag    */
+#define TIM_FLAG_CC3OF                     TIM_SR_CC3OF                         /*!< Capture 3 overcapture flag    */
+#define TIM_FLAG_CC4OF                     TIM_SR_CC4OF                         /*!< Capture 4 overcapture flag    */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Channel TIM Channel
+  * @{
+  */
+#define TIM_CHANNEL_1                      0x00000000U                          /*!< Capture/compare channel 1 identifier      */
+#define TIM_CHANNEL_2                      0x00000004U                          /*!< Capture/compare channel 2 identifier      */
+#define TIM_CHANNEL_3                      0x00000008U                          /*!< Capture/compare channel 3 identifier      */
+#define TIM_CHANNEL_4                      0x0000000CU                          /*!< Capture/compare channel 4 identifier      */
+#define TIM_CHANNEL_5                      0x00000010U                          /*!< Compare channel 5 identifier              */
+#define TIM_CHANNEL_6                      0x00000014U                          /*!< Compare channel 6 identifier              */
+#define TIM_CHANNEL_ALL                    0x0000003CU                          /*!< Global Capture/compare channel identifier  */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Clock_Source TIM Clock Source
+  * @{
+  */
+#define TIM_CLOCKSOURCE_INTERNAL    TIM_SMCR_ETPS_0      /*!< Internal clock source                                 */
+#define TIM_CLOCKSOURCE_ETRMODE1    TIM_TS_ETRF          /*!< External clock source mode 1 (ETRF)                   */
+#define TIM_CLOCKSOURCE_ETRMODE2    TIM_SMCR_ETPS_1      /*!< External clock source mode 2                          */
+#define TIM_CLOCKSOURCE_TI1ED       TIM_TS_TI1F_ED       /*!< External clock source mode 1 (TTI1FP1 + edge detect.) */
+#define TIM_CLOCKSOURCE_TI1         TIM_TS_TI1FP1        /*!< External clock source mode 1 (TTI1FP1)                */
+#define TIM_CLOCKSOURCE_TI2         TIM_TS_TI2FP2        /*!< External clock source mode 1 (TTI2FP2)                */
+#define TIM_CLOCKSOURCE_ITR0        TIM_TS_ITR0          /*!< External clock source mode 1 (ITR0)                   */
+#define TIM_CLOCKSOURCE_ITR1        TIM_TS_ITR1          /*!< External clock source mode 1 (ITR1)                   */
+#define TIM_CLOCKSOURCE_ITR2        TIM_TS_ITR2          /*!< External clock source mode 1 (ITR2)                   */
+#define TIM_CLOCKSOURCE_ITR3        TIM_TS_ITR3          /*!< External clock source mode 1 (ITR3)                   */
+#define TIM_CLOCKSOURCE_ITR4        TIM_TS_ITR4          /*!< External clock source mode 1 (ITR4)                   */
+#define TIM_CLOCKSOURCE_ITR5        TIM_TS_ITR5          /*!< External clock source mode 1 (ITR5)                   */
+#define TIM_CLOCKSOURCE_ITR6        TIM_TS_ITR6          /*!< External clock source mode 1 (ITR6)                   */
+#define TIM_CLOCKSOURCE_ITR7        TIM_TS_ITR7          /*!< External clock source mode 1 (ITR7)                   */
+#define TIM_CLOCKSOURCE_ITR8        TIM_TS_ITR8          /*!< External clock source mode 1 (ITR8)                   */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Clock_Polarity TIM Clock Polarity
+  * @{
+  */
+#define TIM_CLOCKPOLARITY_INVERTED           TIM_ETRPOLARITY_INVERTED           /*!< Polarity for ETRx clock sources */
+#define TIM_CLOCKPOLARITY_NONINVERTED        TIM_ETRPOLARITY_NONINVERTED        /*!< Polarity for ETRx clock sources */
+#define TIM_CLOCKPOLARITY_RISING             TIM_INPUTCHANNELPOLARITY_RISING    /*!< Polarity for TIx clock sources */
+#define TIM_CLOCKPOLARITY_FALLING            TIM_INPUTCHANNELPOLARITY_FALLING   /*!< Polarity for TIx clock sources */
+#define TIM_CLOCKPOLARITY_BOTHEDGE           TIM_INPUTCHANNELPOLARITY_BOTHEDGE  /*!< Polarity for TIx clock sources */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Clock_Prescaler TIM Clock Prescaler
+  * @{
+  */
+#define TIM_CLOCKPRESCALER_DIV1                 TIM_ETRPRESCALER_DIV1           /*!< No prescaler is used                                                     */
+#define TIM_CLOCKPRESCALER_DIV2                 TIM_ETRPRESCALER_DIV2           /*!< Prescaler for External ETR Clock: Capture performed once every 2 events. */
+#define TIM_CLOCKPRESCALER_DIV4                 TIM_ETRPRESCALER_DIV4           /*!< Prescaler for External ETR Clock: Capture performed once every 4 events. */
+#define TIM_CLOCKPRESCALER_DIV8                 TIM_ETRPRESCALER_DIV8           /*!< Prescaler for External ETR Clock: Capture performed once every 8 events. */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_ClearInput_Polarity TIM Clear Input Polarity
+  * @{
+  */
+#define TIM_CLEARINPUTPOLARITY_INVERTED           TIM_ETRPOLARITY_INVERTED      /*!< Polarity for ETRx pin */
+#define TIM_CLEARINPUTPOLARITY_NONINVERTED        TIM_ETRPOLARITY_NONINVERTED   /*!< Polarity for ETRx pin */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_ClearInput_Prescaler TIM Clear Input Prescaler
+  * @{
+  */
+#define TIM_CLEARINPUTPRESCALER_DIV1              TIM_ETRPRESCALER_DIV1         /*!< No prescaler is used                                                   */
+#define TIM_CLEARINPUTPRESCALER_DIV2              TIM_ETRPRESCALER_DIV2         /*!< Prescaler for External ETR pin: Capture performed once every 2 events. */
+#define TIM_CLEARINPUTPRESCALER_DIV4              TIM_ETRPRESCALER_DIV4         /*!< Prescaler for External ETR pin: Capture performed once every 4 events. */
+#define TIM_CLEARINPUTPRESCALER_DIV8              TIM_ETRPRESCALER_DIV8         /*!< Prescaler for External ETR pin: Capture performed once every 8 events. */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_OSSR_Off_State_Selection_for_Run_mode_state TIM OSSR OffState Selection for Run mode state
+  * @{
+  */
+#define TIM_OSSR_ENABLE                          TIM_BDTR_OSSR                  /*!< When inactive, OC/OCN outputs are enabled (still controlled by the timer)           */
+#define TIM_OSSR_DISABLE                         0x00000000U                    /*!< When inactive, OC/OCN outputs are disabled (not controlled any longer by the timer) */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_OSSI_Off_State_Selection_for_Idle_mode_state TIM OSSI OffState Selection for Idle mode state
+  * @{
+  */
+#define TIM_OSSI_ENABLE                          TIM_BDTR_OSSI                  /*!< When inactive, OC/OCN outputs are enabled (still controlled by the timer)           */
+#define TIM_OSSI_DISABLE                         0x00000000U                    /*!< When inactive, OC/OCN outputs are disabled (not controlled any longer by the timer) */
+/**
+  * @}
+  */
+/** @defgroup TIM_Lock_level  TIM Lock level
+  * @{
+  */
+#define TIM_LOCKLEVEL_OFF                  0x00000000U                          /*!< LOCK OFF     */
+#define TIM_LOCKLEVEL_1                    TIM_BDTR_LOCK_0                      /*!< LOCK Level 1 */
+#define TIM_LOCKLEVEL_2                    TIM_BDTR_LOCK_1                      /*!< LOCK Level 2 */
+#define TIM_LOCKLEVEL_3                    TIM_BDTR_LOCK                        /*!< LOCK Level 3 */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Break_Input_enable_disable TIM Break Input Enable
+  * @{
+  */
+#define TIM_BREAK_ENABLE                   TIM_BDTR_BKE                         /*!< Break input BRK is enabled  */
+#define TIM_BREAK_DISABLE                  0x00000000U                          /*!< Break input BRK is disabled */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Break_Polarity TIM Break Input Polarity
+  * @{
+  */
+#define TIM_BREAKPOLARITY_LOW              0x00000000U                          /*!< Break input BRK is active low  */
+#define TIM_BREAKPOLARITY_HIGH             TIM_BDTR_BKP                         /*!< Break input BRK is active high */
+/**
+  * @}
+  */
+#if  defined(TIM_BDTR_BKBID)
+
+/** @defgroup TIM_Break_Input_AF_Mode TIM Break Input Alternate Function Mode
+  * @{
+  */
+#define TIM_BREAK_AFMODE_INPUT             0x00000000U                          /*!< Break input BRK in input mode */
+#define TIM_BREAK_AFMODE_BIDIRECTIONAL     TIM_BDTR_BKBID                       /*!< Break input BRK in bidirectional mode */
+/**
+  * @}
+  */
+#endif /*TIM_BDTR_BKBID */
+
+/** @defgroup TIM_Break2_Input_enable_disable TIM Break input 2 Enable
+  * @{
+  */
+#define TIM_BREAK2_DISABLE                 0x00000000U                          /*!< Break input BRK2 is disabled  */
+#define TIM_BREAK2_ENABLE                  TIM_BDTR_BK2E                        /*!< Break input BRK2 is enabled  */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Break2_Polarity TIM Break Input 2 Polarity
+  * @{
+  */
+#define TIM_BREAK2POLARITY_LOW             0x00000000U                          /*!< Break input BRK2 is active low   */
+#define TIM_BREAK2POLARITY_HIGH            TIM_BDTR_BK2P                        /*!< Break input BRK2 is active high  */
+/**
+  * @}
+  */
+#if defined(TIM_BDTR_BKBID)
+
+/** @defgroup TIM_Break2_Input_AF_Mode TIM Break2 Input Alternate Function Mode
+  * @{
+  */
+#define TIM_BREAK2_AFMODE_INPUT            0x00000000U                          /*!< Break2 input BRK2 in input mode */
+#define TIM_BREAK2_AFMODE_BIDIRECTIONAL    TIM_BDTR_BK2BID                      /*!< Break2 input BRK2 in bidirectional mode */
+/**
+  * @}
+  */
+#endif /* TIM_BDTR_BKBID */
+
+/** @defgroup TIM_AOE_Bit_Set_Reset TIM Automatic Output Enable
+  * @{
+  */
+#define TIM_AUTOMATICOUTPUT_DISABLE        0x00000000U                          /*!< MOE can be set only by software */
+#define TIM_AUTOMATICOUTPUT_ENABLE         TIM_BDTR_AOE                         /*!< MOE can be set by software or automatically at the next update event (if none of the break inputs BRK and BRK2 is active) */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Group_Channel5 TIM Group Channel 5 and Channel 1, 2 or 3
+  * @{
+  */
+#define TIM_GROUPCH5_NONE                  0x00000000U                          /*!< No effect of OC5REF on OC1REFC, OC2REFC and OC3REFC */
+#define TIM_GROUPCH5_OC1REFC               TIM_CCR5_GC5C1                       /*!< OC1REFC is the logical AND of OC1REFC and OC5REF    */
+#define TIM_GROUPCH5_OC2REFC               TIM_CCR5_GC5C2                       /*!< OC2REFC is the logical AND of OC2REFC and OC5REF    */
+#define TIM_GROUPCH5_OC3REFC               TIM_CCR5_GC5C3                       /*!< OC3REFC is the logical AND of OC3REFC and OC5REF    */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Master_Mode_Selection TIM Master Mode Selection
+  * @{
+  */
+#define TIM_TRGO_RESET            0x00000000U                                      /*!< TIMx_EGR.UG bit is used as trigger output (TRGO)              */
+#define TIM_TRGO_ENABLE           TIM_CR2_MMS_0                                    /*!< TIMx_CR1.CEN bit is used as trigger output (TRGO)             */
+#define TIM_TRGO_UPDATE           TIM_CR2_MMS_1                                    /*!< Update event is used as trigger output (TRGO)                 */
+#define TIM_TRGO_OC1              (TIM_CR2_MMS_1 | TIM_CR2_MMS_0)                  /*!< Capture or a compare match 1 is used as trigger output (TRGO) */
+#define TIM_TRGO_OC1REF           TIM_CR2_MMS_2                                    /*!< OC1REF signal is used as trigger output (TRGO)                */
+#define TIM_TRGO_OC2REF           (TIM_CR2_MMS_2 | TIM_CR2_MMS_0)                  /*!< OC2REF signal is used as trigger output(TRGO)                 */
+#define TIM_TRGO_OC3REF           (TIM_CR2_MMS_2 | TIM_CR2_MMS_1)                  /*!< OC3REF signal is used as trigger output(TRGO)                 */
+#define TIM_TRGO_OC4REF           (TIM_CR2_MMS_2 | TIM_CR2_MMS_1 | TIM_CR2_MMS_0)  /*!< OC4REF signal is used as trigger output(TRGO)                 */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Master_Mode_Selection_2 TIM Master Mode Selection 2 (TRGO2)
+  * @{
+  */
+#define TIM_TRGO2_RESET                          0x00000000U                                                         /*!< TIMx_EGR.UG bit is used as trigger output (TRGO2)              */
+#define TIM_TRGO2_ENABLE                         TIM_CR2_MMS2_0                                                      /*!< TIMx_CR1.CEN bit is used as trigger output (TRGO2)             */
+#define TIM_TRGO2_UPDATE                         TIM_CR2_MMS2_1                                                      /*!< Update event is used as trigger output (TRGO2)                 */
+#define TIM_TRGO2_OC1                            (TIM_CR2_MMS2_1 | TIM_CR2_MMS2_0)                                   /*!< Capture or a compare match 1 is used as trigger output (TRGO2) */
+#define TIM_TRGO2_OC1REF                         TIM_CR2_MMS2_2                                                      /*!< OC1REF signal is used as trigger output (TRGO2)                */
+#define TIM_TRGO2_OC2REF                         (TIM_CR2_MMS2_2 | TIM_CR2_MMS2_0)                                   /*!< OC2REF signal is used as trigger output (TRGO2)                */
+#define TIM_TRGO2_OC3REF                         (TIM_CR2_MMS2_2 | TIM_CR2_MMS2_1)                                   /*!< OC3REF signal is used as trigger output (TRGO2)                */
+#define TIM_TRGO2_OC4REF                         (TIM_CR2_MMS2_2 | TIM_CR2_MMS2_1 | TIM_CR2_MMS2_0)                  /*!< OC4REF signal is used as trigger output (TRGO2)                */
+#define TIM_TRGO2_OC5REF                         TIM_CR2_MMS2_3                                                      /*!< OC5REF signal is used as trigger output (TRGO2)                */
+#define TIM_TRGO2_OC6REF                         (TIM_CR2_MMS2_3 | TIM_CR2_MMS2_0)                                   /*!< OC6REF signal is used as trigger output (TRGO2)                */
+#define TIM_TRGO2_OC4REF_RISINGFALLING           (TIM_CR2_MMS2_3 | TIM_CR2_MMS2_1)                                   /*!< OC4REF rising or falling edges generate pulses on TRGO2        */
+#define TIM_TRGO2_OC6REF_RISINGFALLING           (TIM_CR2_MMS2_3 | TIM_CR2_MMS2_1 | TIM_CR2_MMS2_0)                  /*!< OC6REF rising or falling edges generate pulses on TRGO2        */
+#define TIM_TRGO2_OC4REF_RISING_OC6REF_RISING    (TIM_CR2_MMS2_3 | TIM_CR2_MMS2_2)                                   /*!< OC4REF or OC6REF rising edges generate pulses on TRGO2         */
+#define TIM_TRGO2_OC4REF_RISING_OC6REF_FALLING   (TIM_CR2_MMS2_3 | TIM_CR2_MMS2_2 | TIM_CR2_MMS2_0)                  /*!< OC4REF rising or OC6REF falling edges generate pulses on TRGO2 */
+#define TIM_TRGO2_OC5REF_RISING_OC6REF_RISING    (TIM_CR2_MMS2_3 | TIM_CR2_MMS2_2 |TIM_CR2_MMS2_1)                   /*!< OC5REF or OC6REF rising edges generate pulses on TRGO2         */
+#define TIM_TRGO2_OC5REF_RISING_OC6REF_FALLING   (TIM_CR2_MMS2_3 | TIM_CR2_MMS2_2 | TIM_CR2_MMS2_1 | TIM_CR2_MMS2_0) /*!< OC5REF or OC6REF rising edges generate pulses on TRGO2         */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Master_Slave_Mode TIM Master/Slave Mode
+  * @{
+  */
+#define TIM_MASTERSLAVEMODE_ENABLE         TIM_SMCR_MSM                         /*!< No action */
+#define TIM_MASTERSLAVEMODE_DISABLE        0x00000000U                          /*!< Master/slave mode is selected */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Slave_Mode TIM Slave mode
+  * @{
+  */
+#define TIM_SLAVEMODE_DISABLE                0x00000000U                                        /*!< Slave mode disabled           */
+#define TIM_SLAVEMODE_RESET                  TIM_SMCR_SMS_2                                     /*!< Reset Mode                    */
+#define TIM_SLAVEMODE_GATED                  (TIM_SMCR_SMS_2 | TIM_SMCR_SMS_0)                  /*!< Gated Mode                    */
+#define TIM_SLAVEMODE_TRIGGER                (TIM_SMCR_SMS_2 | TIM_SMCR_SMS_1)                  /*!< Trigger Mode                  */
+#define TIM_SLAVEMODE_EXTERNAL1              (TIM_SMCR_SMS_2 | TIM_SMCR_SMS_1 | TIM_SMCR_SMS_0) /*!< External Clock Mode 1         */
+#define TIM_SLAVEMODE_COMBINED_RESETTRIGGER  TIM_SMCR_SMS_3                                     /*!< Combined reset + trigger mode */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Output_Compare_and_PWM_modes TIM Output Compare and PWM Modes
+  * @{
+  */
+#define TIM_OCMODE_TIMING                   0x00000000U                                              /*!< Frozen                                 */
+#define TIM_OCMODE_ACTIVE                   TIM_CCMR1_OC1M_0                                         /*!< Set channel to active level on match   */
+#define TIM_OCMODE_INACTIVE                 TIM_CCMR1_OC1M_1                                         /*!< Set channel to inactive level on match */
+#define TIM_OCMODE_TOGGLE                   (TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0)                    /*!< Toggle                                 */
+#define TIM_OCMODE_PWM1                     (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1)                    /*!< PWM mode 1                             */
+#define TIM_OCMODE_PWM2                     (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0) /*!< PWM mode 2                             */
+#define TIM_OCMODE_FORCED_ACTIVE            (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0)                    /*!< Force active level                     */
+#define TIM_OCMODE_FORCED_INACTIVE          TIM_CCMR1_OC1M_2                                         /*!< Force inactive level                   */
+#define TIM_OCMODE_RETRIGERRABLE_OPM1      TIM_CCMR1_OC1M_3                                          /*!< Retrigerrable OPM mode 1               */
+#define TIM_OCMODE_RETRIGERRABLE_OPM2      (TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M_0)                     /*!< Retrigerrable OPM mode 2               */
+#define TIM_OCMODE_COMBINED_PWM1           (TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M_2)                     /*!< Combined PWM mode 1                    */
+#define TIM_OCMODE_COMBINED_PWM2           (TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC1M_2)  /*!< Combined PWM mode 2                    */
+#define TIM_OCMODE_ASYMMETRIC_PWM1         (TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2)  /*!< Asymmetric PWM mode 1                  */
+#define TIM_OCMODE_ASYMMETRIC_PWM2         TIM_CCMR1_OC1M                                            /*!< Asymmetric PWM mode 2                  */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Trigger_Selection TIM Trigger Selection
+  * @{
+  */
+#define TIM_TS_ITR0          0x00000000U                                                       /*!< Internal Trigger 0 (ITR0)              */
+#define TIM_TS_ITR1          TIM_SMCR_TS_0                                                     /*!< Internal Trigger 1 (ITR1)              */
+#define TIM_TS_ITR2          TIM_SMCR_TS_1                                                     /*!< Internal Trigger 2 (ITR2)              */
+#define TIM_TS_ITR3          (TIM_SMCR_TS_0 | TIM_SMCR_TS_1)                                   /*!< Internal Trigger 3 (ITR3)              */
+#define TIM_TS_ITR4          (TIM_SMCR_TS_3)                                                   /*!< Internal Trigger 4 (ITR4)              */
+#define TIM_TS_ITR5          (TIM_SMCR_TS_0 | TIM_SMCR_TS_3)                                   /*!< Internal Trigger 5 (ITR5)              */
+#define TIM_TS_ITR6          (TIM_SMCR_TS_1 | TIM_SMCR_TS_3)                                   /*!< Internal Trigger 6 (ITR6)              */
+#define TIM_TS_ITR7          (TIM_SMCR_TS_0 | TIM_SMCR_TS_1 | TIM_SMCR_TS_3)                   /*!< Internal Trigger 7 (ITR7)              */
+#define TIM_TS_ITR8          (TIM_SMCR_TS_2 | TIM_SMCR_TS_3)                                   /*!< Internal Trigger 8 (ITR8)              */
+#define TIM_TS_ITR9          (TIM_SMCR_TS_0 | TIM_SMCR_TS_2 | TIM_SMCR_TS_3)                   /*!< Internal Trigger 9 (ITR9)              */
+#define TIM_TS_ITR10         (TIM_SMCR_TS_1 | TIM_SMCR_TS_2 | TIM_SMCR_TS_3)                   /*!< Internal Trigger 10 (ITR10)            */
+#define TIM_TS_ITR11         (TIM_SMCR_TS_0 | TIM_SMCR_TS_1 | TIM_SMCR_TS_2 | TIM_SMCR_TS_3)   /*!< Internal Trigger 11 (ITR11)            */
+#define TIM_TS_ITR12         (TIM_SMCR_TS_4)                                                   /*!< Internal Trigger 12 (ITR12)            */
+#define TIM_TS_ITR13         (TIM_SMCR_TS_0 | TIM_SMCR_TS_4)                                   /*!< Internal Trigger 13 (ITR13)            */
+#define TIM_TS_TI1F_ED       TIM_SMCR_TS_2                                                     /*!< TI1 Edge Detector (TI1F_ED)            */
+#define TIM_TS_TI1FP1        (TIM_SMCR_TS_0 | TIM_SMCR_TS_2)                                   /*!< Filtered Timer Input 1 (TI1FP1)        */
+#define TIM_TS_TI2FP2        (TIM_SMCR_TS_1 | TIM_SMCR_TS_2)                                   /*!< Filtered Timer Input 2 (TI2FP2)        */
+#define TIM_TS_ETRF          (TIM_SMCR_TS_0 | TIM_SMCR_TS_1 | TIM_SMCR_TS_2)                   /*!< Filtered External Trigger input (ETRF) */
+#define TIM_TS_NONE          0x0000FFFFU                                                       /*!< No trigger selected                    */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Trigger_Polarity TIM Trigger Polarity
+  * @{
+  */
+#define TIM_TRIGGERPOLARITY_INVERTED           TIM_ETRPOLARITY_INVERTED               /*!< Polarity for ETRx trigger sources             */
+#define TIM_TRIGGERPOLARITY_NONINVERTED        TIM_ETRPOLARITY_NONINVERTED            /*!< Polarity for ETRx trigger sources             */
+#define TIM_TRIGGERPOLARITY_RISING             TIM_INPUTCHANNELPOLARITY_RISING        /*!< Polarity for TIxFPx or TI1_ED trigger sources */
+#define TIM_TRIGGERPOLARITY_FALLING            TIM_INPUTCHANNELPOLARITY_FALLING       /*!< Polarity for TIxFPx or TI1_ED trigger sources */
+#define TIM_TRIGGERPOLARITY_BOTHEDGE           TIM_INPUTCHANNELPOLARITY_BOTHEDGE      /*!< Polarity for TIxFPx or TI1_ED trigger sources */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Trigger_Prescaler TIM Trigger Prescaler
+  * @{
+  */
+#define TIM_TRIGGERPRESCALER_DIV1             TIM_ETRPRESCALER_DIV1             /*!< No prescaler is used                                                       */
+#define TIM_TRIGGERPRESCALER_DIV2             TIM_ETRPRESCALER_DIV2             /*!< Prescaler for External ETR Trigger: Capture performed once every 2 events. */
+#define TIM_TRIGGERPRESCALER_DIV4             TIM_ETRPRESCALER_DIV4             /*!< Prescaler for External ETR Trigger: Capture performed once every 4 events. */
+#define TIM_TRIGGERPRESCALER_DIV8             TIM_ETRPRESCALER_DIV8             /*!< Prescaler for External ETR Trigger: Capture performed once every 8 events. */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_TI1_Selection TIM TI1 Input Selection
+  * @{
+  */
+#define TIM_TI1SELECTION_CH1               0x00000000U                          /*!< The TIMx_CH1 pin is connected to TI1 input */
+#define TIM_TI1SELECTION_XORCOMBINATION    TIM_CR2_TI1S                         /*!< The TIMx_CH1, CH2 and CH3 pins are connected to the TI1 input (XOR combination) */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_DMA_Burst_Length TIM DMA Burst Length
+  * @{
+  */
+#define TIM_DMABURSTLENGTH_1TRANSFER       0x00000000U                          /*!< The transfer is done to 1 register starting from TIMx_CR1 + TIMx_DCR.DBA   */
+#define TIM_DMABURSTLENGTH_2TRANSFERS      0x00000100U                          /*!< The transfer is done to 2 registers starting from TIMx_CR1 + TIMx_DCR.DBA  */
+#define TIM_DMABURSTLENGTH_3TRANSFERS      0x00000200U                          /*!< The transfer is done to 3 registers starting from TIMx_CR1 + TIMx_DCR.DBA  */
+#define TIM_DMABURSTLENGTH_4TRANSFERS      0x00000300U                          /*!< The transfer is done to 4 registers starting from TIMx_CR1 + TIMx_DCR.DBA  */
+#define TIM_DMABURSTLENGTH_5TRANSFERS      0x00000400U                          /*!< The transfer is done to 5 registers starting from TIMx_CR1 + TIMx_DCR.DBA  */
+#define TIM_DMABURSTLENGTH_6TRANSFERS      0x00000500U                          /*!< The transfer is done to 6 registers starting from TIMx_CR1 + TIMx_DCR.DBA  */
+#define TIM_DMABURSTLENGTH_7TRANSFERS      0x00000600U                          /*!< The transfer is done to 7 registers starting from TIMx_CR1 + TIMx_DCR.DBA  */
+#define TIM_DMABURSTLENGTH_8TRANSFERS      0x00000700U                          /*!< The transfer is done to 8 registers starting from TIMx_CR1 + TIMx_DCR.DBA  */
+#define TIM_DMABURSTLENGTH_9TRANSFERS      0x00000800U                          /*!< The transfer is done to 9 registers starting from TIMx_CR1 + TIMx_DCR.DBA  */
+#define TIM_DMABURSTLENGTH_10TRANSFERS     0x00000900U                          /*!< The transfer is done to 10 registers starting from TIMx_CR1 + TIMx_DCR.DBA */
+#define TIM_DMABURSTLENGTH_11TRANSFERS     0x00000A00U                          /*!< The transfer is done to 11 registers starting from TIMx_CR1 + TIMx_DCR.DBA */
+#define TIM_DMABURSTLENGTH_12TRANSFERS     0x00000B00U                          /*!< The transfer is done to 12 registers starting from TIMx_CR1 + TIMx_DCR.DBA */
+#define TIM_DMABURSTLENGTH_13TRANSFERS     0x00000C00U                          /*!< The transfer is done to 13 registers starting from TIMx_CR1 + TIMx_DCR.DBA */
+#define TIM_DMABURSTLENGTH_14TRANSFERS     0x00000D00U                          /*!< The transfer is done to 14 registers starting from TIMx_CR1 + TIMx_DCR.DBA */
+#define TIM_DMABURSTLENGTH_15TRANSFERS     0x00000E00U                          /*!< The transfer is done to 15 registers starting from TIMx_CR1 + TIMx_DCR.DBA */
+#define TIM_DMABURSTLENGTH_16TRANSFERS     0x00000F00U                          /*!< The transfer is done to 16 registers starting from TIMx_CR1 + TIMx_DCR.DBA */
+#define TIM_DMABURSTLENGTH_17TRANSFERS     0x00001000U                          /*!< The transfer is done to 17 registers starting from TIMx_CR1 + TIMx_DCR.DBA */
+#define TIM_DMABURSTLENGTH_18TRANSFERS     0x00001100U                          /*!< The transfer is done to 18 registers starting from TIMx_CR1 + TIMx_DCR.DBA */
+/**
+  * @}
+  */
+
+/** @defgroup DMA_Handle_index TIM DMA Handle Index
+  * @{
+  */
+#define TIM_DMA_ID_UPDATE                ((uint16_t) 0x0000)       /*!< Index of the DMA handle used for Update DMA requests */
+#define TIM_DMA_ID_CC1                   ((uint16_t) 0x0001)       /*!< Index of the DMA handle used for Capture/Compare 1 DMA requests */
+#define TIM_DMA_ID_CC2                   ((uint16_t) 0x0002)       /*!< Index of the DMA handle used for Capture/Compare 2 DMA requests */
+#define TIM_DMA_ID_CC3                   ((uint16_t) 0x0003)       /*!< Index of the DMA handle used for Capture/Compare 3 DMA requests */
+#define TIM_DMA_ID_CC4                   ((uint16_t) 0x0004)       /*!< Index of the DMA handle used for Capture/Compare 4 DMA requests */
+#define TIM_DMA_ID_COMMUTATION           ((uint16_t) 0x0005)       /*!< Index of the DMA handle used for Commutation DMA requests */
+#define TIM_DMA_ID_TRIGGER               ((uint16_t) 0x0006)       /*!< Index of the DMA handle used for Trigger DMA requests */
+/**
+  * @}
+  */
+
+/** @defgroup Channel_CC_State TIM Capture/Compare Channel State
+  * @{
+  */
+#define TIM_CCx_ENABLE                   0x00000001U                            /*!< Input or output channel is enabled */
+#define TIM_CCx_DISABLE                  0x00000000U                            /*!< Input or output channel is disabled */
+#define TIM_CCxN_ENABLE                  0x00000004U                            /*!< Complementary output channel is enabled */
+#define TIM_CCxN_DISABLE                 0x00000000U                            /*!< Complementary output channel is enabled */
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Break_System TIM Break System
+  * @{
+  */
+#define TIM_BREAK_SYSTEM_ECC                 SYSCFG_CFGR2_ECCL   /*!< Enables and locks the ECC error signal with Break Input of TIM1/8/15/16/17 */
+#define TIM_BREAK_SYSTEM_PVD                 SYSCFG_CFGR2_PVDL   /*!< Enables and locks the PVD connection with TIM1/8/15/16/17 Break Input and also the PVDE and PLS bits of the Power Control Interface */
+#define TIM_BREAK_SYSTEM_SRAM_PARITY_ERROR   SYSCFG_CFGR2_SPL    /*!< Enables and locks the SRAM_PARITY error signal with Break Input of TIM1/8/15/16/17 */
+#define TIM_BREAK_SYSTEM_LOCKUP              SYSCFG_CFGR2_CLL    /*!< Enables and locks the LOCKUP output of CortexM4 with Break Input of TIM1/8/15/16/17 */
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+/* End of exported constants -------------------------------------------------*/
+
+/* Exported macros -----------------------------------------------------------*/
+/** @defgroup TIM_Exported_Macros TIM Exported Macros
+  * @{
+  */
+
+/** @brief  Reset TIM handle state.
+  * @param  __HANDLE__ TIM handle.
+  * @retval None
+  */
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+#define __HAL_TIM_RESET_HANDLE_STATE(__HANDLE__) do {                                                               \
+                                                      (__HANDLE__)->State            = HAL_TIM_STATE_RESET;         \
+                                                      (__HANDLE__)->ChannelState[0]  = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelState[1]  = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelState[2]  = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelState[3]  = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelState[4]  = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelState[5]  = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelNState[0] = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelNState[1] = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelNState[2] = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelNState[3] = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->DMABurstState    = HAL_DMA_BURST_STATE_RESET;   \
+                                                      (__HANDLE__)->Base_MspInitCallback         = NULL;            \
+                                                      (__HANDLE__)->Base_MspDeInitCallback       = NULL;            \
+                                                      (__HANDLE__)->IC_MspInitCallback           = NULL;            \
+                                                      (__HANDLE__)->IC_MspDeInitCallback         = NULL;            \
+                                                      (__HANDLE__)->OC_MspInitCallback           = NULL;            \
+                                                      (__HANDLE__)->OC_MspDeInitCallback         = NULL;            \
+                                                      (__HANDLE__)->PWM_MspInitCallback          = NULL;            \
+                                                      (__HANDLE__)->PWM_MspDeInitCallback        = NULL;            \
+                                                      (__HANDLE__)->OnePulse_MspInitCallback     = NULL;            \
+                                                      (__HANDLE__)->OnePulse_MspDeInitCallback   = NULL;            \
+                                                      (__HANDLE__)->Encoder_MspInitCallback      = NULL;            \
+                                                      (__HANDLE__)->Encoder_MspDeInitCallback    = NULL;            \
+                                                      (__HANDLE__)->HallSensor_MspInitCallback   = NULL;            \
+                                                      (__HANDLE__)->HallSensor_MspDeInitCallback = NULL;            \
+                                                     } while(0)
+#else
+#define __HAL_TIM_RESET_HANDLE_STATE(__HANDLE__) do {                                                               \
+                                                      (__HANDLE__)->State            = HAL_TIM_STATE_RESET;         \
+                                                      (__HANDLE__)->ChannelState[0]  = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelState[1]  = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelState[2]  = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelState[3]  = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelState[4]  = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelState[5]  = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelNState[0] = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelNState[1] = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelNState[2] = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->ChannelNState[3] = HAL_TIM_CHANNEL_STATE_RESET; \
+                                                      (__HANDLE__)->DMABurstState    = HAL_DMA_BURST_STATE_RESET;   \
+                                                     } while(0)
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+/**
+  * @brief  Enable the TIM peripheral.
+  * @param  __HANDLE__ TIM handle
+  * @retval None
+  */
+#define __HAL_TIM_ENABLE(__HANDLE__)                 ((__HANDLE__)->Instance->CR1|=(TIM_CR1_CEN))
+
+/**
+  * @brief  Enable the TIM main Output.
+  * @param  __HANDLE__ TIM handle
+  * @retval None
+  */
+#define __HAL_TIM_MOE_ENABLE(__HANDLE__)             ((__HANDLE__)->Instance->BDTR|=(TIM_BDTR_MOE))
+
+/**
+  * @brief  Disable the TIM peripheral.
+  * @param  __HANDLE__ TIM handle
+  * @retval None
+  */
+#define __HAL_TIM_DISABLE(__HANDLE__) \
+  do { \
+    if (((__HANDLE__)->Instance->CCER & TIM_CCER_CCxE_MASK) == 0UL) \
+    { \
+      if(((__HANDLE__)->Instance->CCER & TIM_CCER_CCxNE_MASK) == 0UL) \
+      { \
+        (__HANDLE__)->Instance->CR1 &= ~(TIM_CR1_CEN); \
+      } \
+    } \
+  } while(0)
+
+/**
+  * @brief  Disable the TIM main Output.
+  * @param  __HANDLE__ TIM handle
+  * @retval None
+  * @note The Main Output Enable of a timer instance is disabled only if all the CCx and CCxN channels have been
+  *       disabled
+  */
+#define __HAL_TIM_MOE_DISABLE(__HANDLE__) \
+  do { \
+    if (((__HANDLE__)->Instance->CCER & TIM_CCER_CCxE_MASK) == 0UL) \
+    { \
+      if(((__HANDLE__)->Instance->CCER & TIM_CCER_CCxNE_MASK) == 0UL) \
+      { \
+        (__HANDLE__)->Instance->BDTR &= ~(TIM_BDTR_MOE); \
+      } \
+    } \
+  } while(0)
+
+/**
+  * @brief  Disable the TIM main Output.
+  * @param  __HANDLE__ TIM handle
+  * @retval None
+  * @note The Main Output Enable of a timer instance is disabled unconditionally
+  */
+#define __HAL_TIM_MOE_DISABLE_UNCONDITIONALLY(__HANDLE__)  (__HANDLE__)->Instance->BDTR &= ~(TIM_BDTR_MOE)
+
+/** @brief  Enable the specified TIM interrupt.
+  * @param  __HANDLE__ specifies the TIM Handle.
+  * @param  __INTERRUPT__ specifies the TIM interrupt source to enable.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_IT_UPDATE: Update interrupt
+  *            @arg TIM_IT_CC1:   Capture/Compare 1 interrupt
+  *            @arg TIM_IT_CC2:  Capture/Compare 2 interrupt
+  *            @arg TIM_IT_CC3:  Capture/Compare 3 interrupt
+  *            @arg TIM_IT_CC4:  Capture/Compare 4 interrupt
+  *            @arg TIM_IT_COM:   Commutation interrupt
+  *            @arg TIM_IT_TRIGGER: Trigger interrupt
+  *            @arg TIM_IT_BREAK: Break interrupt
+  * @retval None
+  */
+#define __HAL_TIM_ENABLE_IT(__HANDLE__, __INTERRUPT__)    ((__HANDLE__)->Instance->DIER |= (__INTERRUPT__))
+
+/** @brief  Disable the specified TIM interrupt.
+  * @param  __HANDLE__ specifies the TIM Handle.
+  * @param  __INTERRUPT__ specifies the TIM interrupt source to disable.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_IT_UPDATE: Update interrupt
+  *            @arg TIM_IT_CC1:   Capture/Compare 1 interrupt
+  *            @arg TIM_IT_CC2:  Capture/Compare 2 interrupt
+  *            @arg TIM_IT_CC3:  Capture/Compare 3 interrupt
+  *            @arg TIM_IT_CC4:  Capture/Compare 4 interrupt
+  *            @arg TIM_IT_COM:   Commutation interrupt
+  *            @arg TIM_IT_TRIGGER: Trigger interrupt
+  *            @arg TIM_IT_BREAK: Break interrupt
+  * @retval None
+  */
+#define __HAL_TIM_DISABLE_IT(__HANDLE__, __INTERRUPT__)   ((__HANDLE__)->Instance->DIER &= ~(__INTERRUPT__))
+
+/** @brief  Enable the specified DMA request.
+  * @param  __HANDLE__ specifies the TIM Handle.
+  * @param  __DMA__ specifies the TIM DMA request to enable.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_DMA_UPDATE: Update DMA request
+  *            @arg TIM_DMA_CC1:   Capture/Compare 1 DMA request
+  *            @arg TIM_DMA_CC2:  Capture/Compare 2 DMA request
+  *            @arg TIM_DMA_CC3:  Capture/Compare 3 DMA request
+  *            @arg TIM_DMA_CC4:  Capture/Compare 4 DMA request
+  *            @arg TIM_DMA_COM:   Commutation DMA request
+  *            @arg TIM_DMA_TRIGGER: Trigger DMA request
+  * @retval None
+  */
+#define __HAL_TIM_ENABLE_DMA(__HANDLE__, __DMA__)         ((__HANDLE__)->Instance->DIER |= (__DMA__))
+
+/** @brief  Disable the specified DMA request.
+  * @param  __HANDLE__ specifies the TIM Handle.
+  * @param  __DMA__ specifies the TIM DMA request to disable.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_DMA_UPDATE: Update DMA request
+  *            @arg TIM_DMA_CC1:   Capture/Compare 1 DMA request
+  *            @arg TIM_DMA_CC2:  Capture/Compare 2 DMA request
+  *            @arg TIM_DMA_CC3:  Capture/Compare 3 DMA request
+  *            @arg TIM_DMA_CC4:  Capture/Compare 4 DMA request
+  *            @arg TIM_DMA_COM:   Commutation DMA request
+  *            @arg TIM_DMA_TRIGGER: Trigger DMA request
+  * @retval None
+  */
+#define __HAL_TIM_DISABLE_DMA(__HANDLE__, __DMA__)        ((__HANDLE__)->Instance->DIER &= ~(__DMA__))
+
+/** @brief  Check whether the specified TIM interrupt flag is set or not.
+  * @param  __HANDLE__ specifies the TIM Handle.
+  * @param  __FLAG__ specifies the TIM interrupt flag to check.
+  *        This parameter can be one of the following values:
+  *            @arg TIM_FLAG_UPDATE: Update interrupt flag
+  *            @arg TIM_FLAG_CC1: Capture/Compare 1 interrupt flag
+  *            @arg TIM_FLAG_CC2: Capture/Compare 2 interrupt flag
+  *            @arg TIM_FLAG_CC3: Capture/Compare 3 interrupt flag
+  *            @arg TIM_FLAG_CC4: Capture/Compare 4 interrupt flag
+  *            @arg TIM_FLAG_CC5: Compare 5 interrupt flag
+  *            @arg TIM_FLAG_CC6: Compare 6 interrupt flag
+  *            @arg TIM_FLAG_COM:  Commutation interrupt flag
+  *            @arg TIM_FLAG_TRIGGER: Trigger interrupt flag
+  *            @arg TIM_FLAG_BREAK: Break interrupt flag
+  *            @arg TIM_FLAG_BREAK2: Break 2 interrupt flag
+  *            @arg TIM_FLAG_SYSTEM_BREAK: System Break interrupt flag
+  *            @arg TIM_FLAG_CC1OF: Capture/Compare 1 overcapture flag
+  *            @arg TIM_FLAG_CC2OF: Capture/Compare 2 overcapture flag
+  *            @arg TIM_FLAG_CC3OF: Capture/Compare 3 overcapture flag
+  *            @arg TIM_FLAG_CC4OF: Capture/Compare 4 overcapture flag
+  * @retval The new state of __FLAG__ (TRUE or FALSE).
+  */
+#define __HAL_TIM_GET_FLAG(__HANDLE__, __FLAG__)          (((__HANDLE__)->Instance->SR &(__FLAG__)) == (__FLAG__))
+
+/** @brief  Clear the specified TIM interrupt flag.
+  * @param  __HANDLE__ specifies the TIM Handle.
+  * @param  __FLAG__ specifies the TIM interrupt flag to clear.
+  *        This parameter can be one of the following values:
+  *            @arg TIM_FLAG_UPDATE: Update interrupt flag
+  *            @arg TIM_FLAG_CC1: Capture/Compare 1 interrupt flag
+  *            @arg TIM_FLAG_CC2: Capture/Compare 2 interrupt flag
+  *            @arg TIM_FLAG_CC3: Capture/Compare 3 interrupt flag
+  *            @arg TIM_FLAG_CC4: Capture/Compare 4 interrupt flag
+  *            @arg TIM_FLAG_CC5: Compare 5 interrupt flag
+  *            @arg TIM_FLAG_CC6: Compare 6 interrupt flag
+  *            @arg TIM_FLAG_COM:  Commutation interrupt flag
+  *            @arg TIM_FLAG_TRIGGER: Trigger interrupt flag
+  *            @arg TIM_FLAG_BREAK: Break interrupt flag
+  *            @arg TIM_FLAG_BREAK2: Break 2 interrupt flag
+  *            @arg TIM_FLAG_SYSTEM_BREAK: System Break interrupt flag
+  *            @arg TIM_FLAG_CC1OF: Capture/Compare 1 overcapture flag
+  *            @arg TIM_FLAG_CC2OF: Capture/Compare 2 overcapture flag
+  *            @arg TIM_FLAG_CC3OF: Capture/Compare 3 overcapture flag
+  *            @arg TIM_FLAG_CC4OF: Capture/Compare 4 overcapture flag
+  * @retval The new state of __FLAG__ (TRUE or FALSE).
+  */
+#define __HAL_TIM_CLEAR_FLAG(__HANDLE__, __FLAG__)        ((__HANDLE__)->Instance->SR = ~(__FLAG__))
+
+/**
+  * @brief  Check whether the specified TIM interrupt source is enabled or not.
+  * @param  __HANDLE__ TIM handle
+  * @param  __INTERRUPT__ specifies the TIM interrupt source to check.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_IT_UPDATE: Update interrupt
+  *            @arg TIM_IT_CC1:   Capture/Compare 1 interrupt
+  *            @arg TIM_IT_CC2:  Capture/Compare 2 interrupt
+  *            @arg TIM_IT_CC3:  Capture/Compare 3 interrupt
+  *            @arg TIM_IT_CC4:  Capture/Compare 4 interrupt
+  *            @arg TIM_IT_COM:   Commutation interrupt
+  *            @arg TIM_IT_TRIGGER: Trigger interrupt
+  *            @arg TIM_IT_BREAK: Break interrupt
+  * @retval The state of TIM_IT (SET or RESET).
+  */
+#define __HAL_TIM_GET_IT_SOURCE(__HANDLE__, __INTERRUPT__) ((((__HANDLE__)->Instance->DIER & (__INTERRUPT__)) \
+                                                             == (__INTERRUPT__)) ? SET : RESET)
+
+/** @brief Clear the TIM interrupt pending bits.
+  * @param  __HANDLE__ TIM handle
+  * @param  __INTERRUPT__ specifies the interrupt pending bit to clear.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_IT_UPDATE: Update interrupt
+  *            @arg TIM_IT_CC1:   Capture/Compare 1 interrupt
+  *            @arg TIM_IT_CC2:  Capture/Compare 2 interrupt
+  *            @arg TIM_IT_CC3:  Capture/Compare 3 interrupt
+  *            @arg TIM_IT_CC4:  Capture/Compare 4 interrupt
+  *            @arg TIM_IT_COM:   Commutation interrupt
+  *            @arg TIM_IT_TRIGGER: Trigger interrupt
+  *            @arg TIM_IT_BREAK: Break interrupt
+  * @retval None
+  */
+#define __HAL_TIM_CLEAR_IT(__HANDLE__, __INTERRUPT__)      ((__HANDLE__)->Instance->SR = ~(__INTERRUPT__))
+
+/**
+  * @brief  Force a continuous copy of the update interrupt flag (UIF) into the timer counter register (bit 31).
+  * @note This allows both the counter value and a potential roll-over condition signalled by the UIFCPY flag to be read
+  *       in an atomic way.
+  * @param  __HANDLE__ TIM handle.
+  * @retval None
+mode.
+  */
+#define __HAL_TIM_UIFREMAP_ENABLE(__HANDLE__)    (((__HANDLE__)->Instance->CR1 |= TIM_CR1_UIFREMAP))
+
+/**
+  * @brief  Disable update interrupt flag (UIF) remapping.
+  * @param  __HANDLE__ TIM handle.
+  * @retval None
+mode.
+  */
+#define __HAL_TIM_UIFREMAP_DISABLE(__HANDLE__)    (((__HANDLE__)->Instance->CR1 &= ~TIM_CR1_UIFREMAP))
+
+/**
+  * @brief  Get update interrupt flag (UIF) copy status.
+  * @param  __COUNTER__ Counter value.
+  * @retval The state of UIFCPY (TRUE or FALSE).
+mode.
+  */
+#define __HAL_TIM_GET_UIFCPY(__COUNTER__)    (((__COUNTER__) & (TIM_CNT_UIFCPY)) == (TIM_CNT_UIFCPY))
+
+/**
+  * @brief  Indicates whether or not the TIM Counter is used as downcounter.
+  * @param  __HANDLE__ TIM handle.
+  * @retval False (Counter used as upcounter) or True (Counter used as downcounter)
+  * @note This macro is particularly useful to get the counting mode when the timer operates in Center-aligned mode
+  *       or Encoder mode.
+  */
+#define __HAL_TIM_IS_TIM_COUNTING_DOWN(__HANDLE__)    (((__HANDLE__)->Instance->CR1 &(TIM_CR1_DIR)) == (TIM_CR1_DIR))
+
+/**
+  * @brief  Set the TIM Prescaler on runtime.
+  * @param  __HANDLE__ TIM handle.
+  * @param  __PRESC__ specifies the Prescaler new value.
+  * @retval None
+  */
+#define __HAL_TIM_SET_PRESCALER(__HANDLE__, __PRESC__)       ((__HANDLE__)->Instance->PSC = (__PRESC__))
+
+/**
+  * @brief  Set the TIM Counter Register value on runtime.
+  * Note Please check if the bit 31 of CNT register is used as UIF copy or not, this may affect the counter range in
+  *      case of 32 bits counter TIM instance.
+  *      Bit 31 of CNT can be enabled/disabled using __HAL_TIM_UIFREMAP_ENABLE()/__HAL_TIM_UIFREMAP_DISABLE() macros.
+  * @param  __HANDLE__ TIM handle.
+  * @param  __COUNTER__ specifies the Counter register new value.
+  * @retval None
+  */
+#define __HAL_TIM_SET_COUNTER(__HANDLE__, __COUNTER__)  ((__HANDLE__)->Instance->CNT = (__COUNTER__))
+
+/**
+  * @brief  Get the TIM Counter Register value on runtime.
+  * @param  __HANDLE__ TIM handle.
+  * @retval 16-bit or 32-bit value of the timer counter register (TIMx_CNT)
+  */
+#define __HAL_TIM_GET_COUNTER(__HANDLE__)  ((__HANDLE__)->Instance->CNT)
+
+/**
+  * @brief  Set the TIM Autoreload Register value on runtime without calling another time any Init function.
+  * @param  __HANDLE__ TIM handle.
+  * @param  __AUTORELOAD__ specifies the Counter register new value.
+  * @retval None
+  */
+#define __HAL_TIM_SET_AUTORELOAD(__HANDLE__, __AUTORELOAD__) \
+  do{                                                    \
+    (__HANDLE__)->Instance->ARR = (__AUTORELOAD__);  \
+    (__HANDLE__)->Init.Period = (__AUTORELOAD__);    \
+  } while(0)
+
+/**
+  * @brief  Get the TIM Autoreload Register value on runtime.
+  * @param  __HANDLE__ TIM handle.
+  * @retval 16-bit or 32-bit value of the timer auto-reload register(TIMx_ARR)
+  */
+#define __HAL_TIM_GET_AUTORELOAD(__HANDLE__)  ((__HANDLE__)->Instance->ARR)
+
+/**
+  * @brief  Set the TIM Clock Division value on runtime without calling another time any Init function.
+  * @param  __HANDLE__ TIM handle.
+  * @param  __CKD__ specifies the clock division value.
+  *          This parameter can be one of the following value:
+  *            @arg TIM_CLOCKDIVISION_DIV1: tDTS=tCK_INT
+  *            @arg TIM_CLOCKDIVISION_DIV2: tDTS=2*tCK_INT
+  *            @arg TIM_CLOCKDIVISION_DIV4: tDTS=4*tCK_INT
+  * @retval None
+  */
+#define __HAL_TIM_SET_CLOCKDIVISION(__HANDLE__, __CKD__) \
+  do{                                                   \
+    (__HANDLE__)->Instance->CR1 &= (~TIM_CR1_CKD);  \
+    (__HANDLE__)->Instance->CR1 |= (__CKD__);       \
+    (__HANDLE__)->Init.ClockDivision = (__CKD__);   \
+  } while(0)
+
+/**
+  * @brief  Get the TIM Clock Division value on runtime.
+  * @param  __HANDLE__ TIM handle.
+  * @retval The clock division can be one of the following values:
+  *            @arg TIM_CLOCKDIVISION_DIV1: tDTS=tCK_INT
+  *            @arg TIM_CLOCKDIVISION_DIV2: tDTS=2*tCK_INT
+  *            @arg TIM_CLOCKDIVISION_DIV4: tDTS=4*tCK_INT
+  */
+#define __HAL_TIM_GET_CLOCKDIVISION(__HANDLE__)  ((__HANDLE__)->Instance->CR1 & TIM_CR1_CKD)
+
+/**
+  * @brief  Set the TIM Input Capture prescaler on runtime without calling another time HAL_TIM_IC_ConfigChannel()
+  *         function.
+  * @param  __HANDLE__ TIM handle.
+  * @param  __CHANNEL__ TIM Channels to be configured.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @param  __ICPSC__ specifies the Input Capture4 prescaler new value.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_ICPSC_DIV1: no prescaler
+  *            @arg TIM_ICPSC_DIV2: capture is done once every 2 events
+  *            @arg TIM_ICPSC_DIV4: capture is done once every 4 events
+  *            @arg TIM_ICPSC_DIV8: capture is done once every 8 events
+  * @retval None
+  */
+#define __HAL_TIM_SET_ICPRESCALER(__HANDLE__, __CHANNEL__, __ICPSC__) \
+  do{                                                    \
+    TIM_RESET_ICPRESCALERVALUE((__HANDLE__), (__CHANNEL__));  \
+    TIM_SET_ICPRESCALERVALUE((__HANDLE__), (__CHANNEL__), (__ICPSC__)); \
+  } while(0)
+
+/**
+  * @brief  Get the TIM Input Capture prescaler on runtime.
+  * @param  __HANDLE__ TIM handle.
+  * @param  __CHANNEL__ TIM Channels to be configured.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: get input capture 1 prescaler value
+  *            @arg TIM_CHANNEL_2: get input capture 2 prescaler value
+  *            @arg TIM_CHANNEL_3: get input capture 3 prescaler value
+  *            @arg TIM_CHANNEL_4: get input capture 4 prescaler value
+  * @retval The input capture prescaler can be one of the following values:
+  *            @arg TIM_ICPSC_DIV1: no prescaler
+  *            @arg TIM_ICPSC_DIV2: capture is done once every 2 events
+  *            @arg TIM_ICPSC_DIV4: capture is done once every 4 events
+  *            @arg TIM_ICPSC_DIV8: capture is done once every 8 events
+  */
+#define __HAL_TIM_GET_ICPRESCALER(__HANDLE__, __CHANNEL__)  \
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCMR1 & TIM_CCMR1_IC1PSC) :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? (((__HANDLE__)->Instance->CCMR1 & TIM_CCMR1_IC2PSC) >> 8U) :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCMR2 & TIM_CCMR2_IC3PSC) :\
+   (((__HANDLE__)->Instance->CCMR2 & TIM_CCMR2_IC4PSC)) >> 8U)
+
+/**
+  * @brief  Set the TIM Capture Compare Register value on runtime without calling another time ConfigChannel function.
+  * @param  __HANDLE__ TIM handle.
+  * @param  __CHANNEL__ TIM Channels to be configured.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
+  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
+  * @param  __COMPARE__ specifies the Capture Compare register new value.
+  * @retval None
+  */
+#define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCR1 = (__COMPARE__)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCR2 = (__COMPARE__)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCR3 = (__COMPARE__)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_4) ? ((__HANDLE__)->Instance->CCR4 = (__COMPARE__)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_5) ? ((__HANDLE__)->Instance->CCR5 = (__COMPARE__)) :\
+   ((__HANDLE__)->Instance->CCR6 = (__COMPARE__)))
+
+/**
+  * @brief  Get the TIM Capture Compare Register value on runtime.
+  * @param  __HANDLE__ TIM handle.
+  * @param  __CHANNEL__ TIM Channel associated with the capture compare register
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: get capture/compare 1 register value
+  *            @arg TIM_CHANNEL_2: get capture/compare 2 register value
+  *            @arg TIM_CHANNEL_3: get capture/compare 3 register value
+  *            @arg TIM_CHANNEL_4: get capture/compare 4 register value
+  *            @arg TIM_CHANNEL_5: get capture/compare 5 register value
+  *            @arg TIM_CHANNEL_6: get capture/compare 6 register value
+  * @retval 16-bit or 32-bit value of the capture/compare register (TIMx_CCRy)
+  */
+#define __HAL_TIM_GET_COMPARE(__HANDLE__, __CHANNEL__) \
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCR1) :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCR2) :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCR3) :\
+   ((__CHANNEL__) == TIM_CHANNEL_4) ? ((__HANDLE__)->Instance->CCR4) :\
+   ((__CHANNEL__) == TIM_CHANNEL_5) ? ((__HANDLE__)->Instance->CCR5) :\
+   ((__HANDLE__)->Instance->CCR6))
+
+/**
+  * @brief  Set the TIM Output compare preload.
+  * @param  __HANDLE__ TIM handle.
+  * @param  __CHANNEL__ TIM Channels to be configured.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
+  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
+  * @retval None
+  */
+#define __HAL_TIM_ENABLE_OCxPRELOAD(__HANDLE__, __CHANNEL__)    \
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCMR1 |= TIM_CCMR1_OC1PE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCMR1 |= TIM_CCMR1_OC2PE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCMR2 |= TIM_CCMR2_OC3PE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_4) ? ((__HANDLE__)->Instance->CCMR2 |= TIM_CCMR2_OC4PE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_5) ? ((__HANDLE__)->Instance->CCMR3 |= TIM_CCMR3_OC5PE) :\
+   ((__HANDLE__)->Instance->CCMR3 |= TIM_CCMR3_OC6PE))
+
+/**
+  * @brief  Reset the TIM Output compare preload.
+  * @param  __HANDLE__ TIM handle.
+  * @param  __CHANNEL__ TIM Channels to be configured.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
+  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
+  * @retval None
+  */
+#define __HAL_TIM_DISABLE_OCxPRELOAD(__HANDLE__, __CHANNEL__)    \
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCMR1 &= ~TIM_CCMR1_OC1PE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCMR1 &= ~TIM_CCMR1_OC2PE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCMR2 &= ~TIM_CCMR2_OC3PE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_4) ? ((__HANDLE__)->Instance->CCMR2 &= ~TIM_CCMR2_OC4PE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_5) ? ((__HANDLE__)->Instance->CCMR3 &= ~TIM_CCMR3_OC5PE) :\
+   ((__HANDLE__)->Instance->CCMR3 &= ~TIM_CCMR3_OC6PE))
+
+/**
+  * @brief  Enable fast mode for a given channel.
+  * @param  __HANDLE__ TIM handle.
+  * @param  __CHANNEL__ TIM Channels to be configured.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
+  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
+  * @note  When fast mode is enabled an active edge on the trigger input acts
+  *        like a compare match on CCx output. Delay to sample the trigger
+  *        input and to activate CCx output is reduced to 3 clock cycles.
+  * @note  Fast mode acts only if the channel is configured in PWM1 or PWM2 mode.
+  * @retval None
+  */
+#define __HAL_TIM_ENABLE_OCxFAST(__HANDLE__, __CHANNEL__)    \
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCMR1 |= TIM_CCMR1_OC1FE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCMR1 |= TIM_CCMR1_OC2FE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCMR2 |= TIM_CCMR2_OC3FE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_4) ? ((__HANDLE__)->Instance->CCMR2 |= TIM_CCMR2_OC4FE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_5) ? ((__HANDLE__)->Instance->CCMR3 |= TIM_CCMR3_OC5FE) :\
+   ((__HANDLE__)->Instance->CCMR3 |= TIM_CCMR3_OC6FE))
+
+/**
+  * @brief  Disable fast mode for a given channel.
+  * @param  __HANDLE__ TIM handle.
+  * @param  __CHANNEL__ TIM Channels to be configured.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
+  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
+  * @note  When fast mode is disabled CCx output behaves normally depending
+  *        on counter and CCRx values even when the trigger is ON. The minimum
+  *        delay to activate CCx output when an active edge occurs on the
+  *        trigger input is 5 clock cycles.
+  * @retval None
+  */
+#define __HAL_TIM_DISABLE_OCxFAST(__HANDLE__, __CHANNEL__)    \
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCMR1 &= ~TIM_CCMR1_OC1FE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCMR1 &= ~TIM_CCMR1_OC2FE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCMR2 &= ~TIM_CCMR2_OC3FE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_4) ? ((__HANDLE__)->Instance->CCMR2 &= ~TIM_CCMR2_OC4FE) :\
+   ((__CHANNEL__) == TIM_CHANNEL_5) ? ((__HANDLE__)->Instance->CCMR3 &= ~TIM_CCMR3_OC5FE) :\
+   ((__HANDLE__)->Instance->CCMR3 &= ~TIM_CCMR3_OC6FE))
+
+/**
+  * @brief  Set the Update Request Source (URS) bit of the TIMx_CR1 register.
+  * @param  __HANDLE__ TIM handle.
+  * @note  When the URS bit of the TIMx_CR1 register is set, only counter
+  *        overflow/underflow generates an update interrupt or DMA request (if
+  *        enabled)
+  * @retval None
+  */
+#define __HAL_TIM_URS_ENABLE(__HANDLE__)  ((__HANDLE__)->Instance->CR1|= TIM_CR1_URS)
+
+/**
+  * @brief  Reset the Update Request Source (URS) bit of the TIMx_CR1 register.
+  * @param  __HANDLE__ TIM handle.
+  * @note  When the URS bit of the TIMx_CR1 register is reset, any of the
+  *        following events generate an update interrupt or DMA request (if
+  *        enabled):
+  *           _ Counter overflow underflow
+  *           _ Setting the UG bit
+  *           _ Update generation through the slave mode controller
+  * @retval None
+  */
+#define __HAL_TIM_URS_DISABLE(__HANDLE__)  ((__HANDLE__)->Instance->CR1&=~TIM_CR1_URS)
+
+/**
+  * @brief  Set the TIM Capture x input polarity on runtime.
+  * @param  __HANDLE__ TIM handle.
+  * @param  __CHANNEL__ TIM Channels to be configured.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @param  __POLARITY__ Polarity for TIx source
+  *            @arg TIM_INPUTCHANNELPOLARITY_RISING: Rising Edge
+  *            @arg TIM_INPUTCHANNELPOLARITY_FALLING: Falling Edge
+  *            @arg TIM_INPUTCHANNELPOLARITY_BOTHEDGE: Rising and Falling Edge
+  * @retval None
+  */
+#define __HAL_TIM_SET_CAPTUREPOLARITY(__HANDLE__, __CHANNEL__, __POLARITY__)    \
+  do{                                                                     \
+    TIM_RESET_CAPTUREPOLARITY((__HANDLE__), (__CHANNEL__));               \
+    TIM_SET_CAPTUREPOLARITY((__HANDLE__), (__CHANNEL__), (__POLARITY__)); \
+  }while(0)
+
+/** @brief  Select the Capture/compare DMA request source.
+  * @param  __HANDLE__ specifies the TIM Handle.
+  * @param  __CCDMA__ specifies Capture/compare DMA request source
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CCDMAREQUEST_CC: CCx DMA request generated on Capture/Compare event
+  *            @arg TIM_CCDMAREQUEST_UPDATE: CCx DMA request generated on Update event
+  * @retval None
+  */
+#define __HAL_TIM_SELECT_CCDMAREQUEST(__HANDLE__, __CCDMA__)    \
+  MODIFY_REG((__HANDLE__)->Instance->CR2, TIM_CR2_CCDS, (__CCDMA__))
+
+/**
+  * @}
+  */
+/* End of exported macros ----------------------------------------------------*/
+
+/* Private constants ---------------------------------------------------------*/
+/** @defgroup TIM_Private_Constants TIM Private Constants
+  * @{
+  */
+/* The counter of a timer instance is disabled only if all the CCx and CCxN
+   channels have been disabled */
+#define TIM_CCER_CCxE_MASK  ((uint32_t)(TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E))
+#define TIM_CCER_CCxNE_MASK ((uint32_t)(TIM_CCER_CC1NE | TIM_CCER_CC2NE | TIM_CCER_CC3NE))
+/**
+  * @}
+  */
+/* End of private constants --------------------------------------------------*/
+
+/* Private macros ------------------------------------------------------------*/
+/** @defgroup TIM_Private_Macros TIM Private Macros
+  * @{
+  */
+#define IS_TIM_CLEARINPUT_SOURCE(__MODE__)  (((__MODE__) == TIM_CLEARINPUTSOURCE_NONE)      || \
+                                             ((__MODE__) == TIM_CLEARINPUTSOURCE_ETR))
+
+#define IS_TIM_DMA_BASE(__BASE__) (((__BASE__) == TIM_DMABASE_CR1)   || \
+                                   ((__BASE__) == TIM_DMABASE_CR2)   || \
+                                   ((__BASE__) == TIM_DMABASE_SMCR)  || \
+                                   ((__BASE__) == TIM_DMABASE_DIER)  || \
+                                   ((__BASE__) == TIM_DMABASE_SR)    || \
+                                   ((__BASE__) == TIM_DMABASE_EGR)   || \
+                                   ((__BASE__) == TIM_DMABASE_CCMR1) || \
+                                   ((__BASE__) == TIM_DMABASE_CCMR2) || \
+                                   ((__BASE__) == TIM_DMABASE_CCER)  || \
+                                   ((__BASE__) == TIM_DMABASE_CNT)   || \
+                                   ((__BASE__) == TIM_DMABASE_PSC)   || \
+                                   ((__BASE__) == TIM_DMABASE_ARR)   || \
+                                   ((__BASE__) == TIM_DMABASE_RCR)   || \
+                                   ((__BASE__) == TIM_DMABASE_CCR1)  || \
+                                   ((__BASE__) == TIM_DMABASE_CCR2)  || \
+                                   ((__BASE__) == TIM_DMABASE_CCR3)  || \
+                                   ((__BASE__) == TIM_DMABASE_CCR4)  || \
+                                   ((__BASE__) == TIM_DMABASE_BDTR)  || \
+                                   ((__BASE__) == TIM_DMABASE_CCMR3) || \
+                                   ((__BASE__) == TIM_DMABASE_CCR5)  || \
+                                   ((__BASE__) == TIM_DMABASE_CCR6)  || \
+                                   ((__BASE__) == TIM_DMABASE_AF1)   || \
+                                   ((__BASE__) == TIM_DMABASE_AF2)   || \
+                                   ((__BASE__) == TIM_DMABASE_TISEL))
+
+
+#define IS_TIM_EVENT_SOURCE(__SOURCE__) ((((__SOURCE__) & 0xFFFFFE00U) == 0x00000000U) && ((__SOURCE__) != 0x00000000U))
+
+#define IS_TIM_COUNTER_MODE(__MODE__)      (((__MODE__) == TIM_COUNTERMODE_UP)              || \
+                                            ((__MODE__) == TIM_COUNTERMODE_DOWN)            || \
+                                            ((__MODE__) == TIM_COUNTERMODE_CENTERALIGNED1)  || \
+                                            ((__MODE__) == TIM_COUNTERMODE_CENTERALIGNED2)  || \
+                                            ((__MODE__) == TIM_COUNTERMODE_CENTERALIGNED3))
+
+#define IS_TIM_UIFREMAP_MODE(__MODE__)     (((__MODE__) == TIM_UIFREMAP_DISABLE) || \
+                                            ((__MODE__) == TIM_UIFREMAP_ENABLE))
+
+#define IS_TIM_CLOCKDIVISION_DIV(__DIV__)  (((__DIV__) == TIM_CLOCKDIVISION_DIV1) || \
+                                            ((__DIV__) == TIM_CLOCKDIVISION_DIV2) || \
+                                            ((__DIV__) == TIM_CLOCKDIVISION_DIV4))
+
+#define IS_TIM_AUTORELOAD_PRELOAD(PRELOAD) (((PRELOAD) == TIM_AUTORELOAD_PRELOAD_DISABLE) || \
+                                            ((PRELOAD) == TIM_AUTORELOAD_PRELOAD_ENABLE))
+
+#define IS_TIM_FAST_STATE(__STATE__)       (((__STATE__) == TIM_OCFAST_DISABLE) || \
+                                            ((__STATE__) == TIM_OCFAST_ENABLE))
+
+#define IS_TIM_OC_POLARITY(__POLARITY__)   (((__POLARITY__) == TIM_OCPOLARITY_HIGH) || \
+                                            ((__POLARITY__) == TIM_OCPOLARITY_LOW))
+
+#define IS_TIM_OCN_POLARITY(__POLARITY__)  (((__POLARITY__) == TIM_OCNPOLARITY_HIGH) || \
+                                            ((__POLARITY__) == TIM_OCNPOLARITY_LOW))
+
+#define IS_TIM_OCIDLE_STATE(__STATE__)     (((__STATE__) == TIM_OCIDLESTATE_SET) || \
+                                            ((__STATE__) == TIM_OCIDLESTATE_RESET))
+
+#define IS_TIM_OCNIDLE_STATE(__STATE__)    (((__STATE__) == TIM_OCNIDLESTATE_SET) || \
+                                            ((__STATE__) == TIM_OCNIDLESTATE_RESET))
+
+#define IS_TIM_ENCODERINPUT_POLARITY(__POLARITY__)   (((__POLARITY__) == TIM_ENCODERINPUTPOLARITY_RISING)   || \
+                                                      ((__POLARITY__) == TIM_ENCODERINPUTPOLARITY_FALLING))
+
+#define IS_TIM_IC_POLARITY(__POLARITY__)   (((__POLARITY__) == TIM_ICPOLARITY_RISING)   || \
+                                            ((__POLARITY__) == TIM_ICPOLARITY_FALLING)  || \
+                                            ((__POLARITY__) == TIM_ICPOLARITY_BOTHEDGE))
+
+#define IS_TIM_IC_SELECTION(__SELECTION__) (((__SELECTION__) == TIM_ICSELECTION_DIRECTTI) || \
+                                            ((__SELECTION__) == TIM_ICSELECTION_INDIRECTTI) || \
+                                            ((__SELECTION__) == TIM_ICSELECTION_TRC))
+
+#define IS_TIM_IC_PRESCALER(__PRESCALER__) (((__PRESCALER__) == TIM_ICPSC_DIV1) || \
+                                            ((__PRESCALER__) == TIM_ICPSC_DIV2) || \
+                                            ((__PRESCALER__) == TIM_ICPSC_DIV4) || \
+                                            ((__PRESCALER__) == TIM_ICPSC_DIV8))
+
+#define IS_TIM_CCX_CHANNEL(__INSTANCE__, __CHANNEL__) (IS_TIM_CCX_INSTANCE(__INSTANCE__, __CHANNEL__) && \
+                                                       ((__CHANNEL__) != (TIM_CHANNEL_5)) && \
+                                                       ((__CHANNEL__) != (TIM_CHANNEL_6)))
+
+#define IS_TIM_OPM_MODE(__MODE__)          (((__MODE__) == TIM_OPMODE_SINGLE) || \
+                                            ((__MODE__) == TIM_OPMODE_REPETITIVE))
+
+#define IS_TIM_ENCODER_MODE(__MODE__)      (((__MODE__) == TIM_ENCODERMODE_TI1) || \
+                                            ((__MODE__) == TIM_ENCODERMODE_TI2) || \
+                                            ((__MODE__) == TIM_ENCODERMODE_TI12))
+
+#define IS_TIM_DMA_SOURCE(__SOURCE__) ((((__SOURCE__) & 0xFFFF80FFU) == 0x00000000U) && ((__SOURCE__) != 0x00000000U))
+
+#define IS_TIM_CHANNELS(__CHANNEL__)       (((__CHANNEL__) == TIM_CHANNEL_1) || \
+                                            ((__CHANNEL__) == TIM_CHANNEL_2) || \
+                                            ((__CHANNEL__) == TIM_CHANNEL_3) || \
+                                            ((__CHANNEL__) == TIM_CHANNEL_4) || \
+                                            ((__CHANNEL__) == TIM_CHANNEL_5) || \
+                                            ((__CHANNEL__) == TIM_CHANNEL_6) || \
+                                            ((__CHANNEL__) == TIM_CHANNEL_ALL))
+
+#define IS_TIM_OPM_CHANNELS(__CHANNEL__)   (((__CHANNEL__) == TIM_CHANNEL_1) || \
+                                            ((__CHANNEL__) == TIM_CHANNEL_2))
+
+#define IS_TIM_PERIOD(__HANDLE__, __PERIOD__) ((IS_TIM_32B_COUNTER_INSTANCE(((__HANDLE__)->Instance)) == 0U) ? \
+                                               (((__PERIOD__) > 0U) && ((__PERIOD__) <= 0x0000FFFFU)) :        \
+                                               ((__PERIOD__) > 0U))
+
+#define IS_TIM_COMPLEMENTARY_CHANNELS(__CHANNEL__) (((__CHANNEL__) == TIM_CHANNEL_1) || \
+                                                    ((__CHANNEL__) == TIM_CHANNEL_2) || \
+                                                    ((__CHANNEL__) == TIM_CHANNEL_3))
+
+#define IS_TIM_CLOCKSOURCE(__CLOCK__) (((__CLOCK__) == TIM_CLOCKSOURCE_INTERNAL) || \
+                                       ((__CLOCK__) == TIM_CLOCKSOURCE_ETRMODE1) || \
+                                       ((__CLOCK__) == TIM_CLOCKSOURCE_ETRMODE2) || \
+                                       ((__CLOCK__) == TIM_CLOCKSOURCE_TI1ED)    || \
+                                       ((__CLOCK__) == TIM_CLOCKSOURCE_TI1)      || \
+                                       ((__CLOCK__) == TIM_CLOCKSOURCE_TI2)      || \
+                                       ((__CLOCK__) == TIM_CLOCKSOURCE_ITR0)     || \
+                                       ((__CLOCK__) == TIM_CLOCKSOURCE_ITR1)     || \
+                                       ((__CLOCK__) == TIM_CLOCKSOURCE_ITR2)     || \
+                                       ((__CLOCK__) == TIM_CLOCKSOURCE_ITR3))
+
+#define IS_TIM_CLOCKPOLARITY(__POLARITY__) (((__POLARITY__) == TIM_CLOCKPOLARITY_INVERTED)    || \
+                                            ((__POLARITY__) == TIM_CLOCKPOLARITY_NONINVERTED) || \
+                                            ((__POLARITY__) == TIM_CLOCKPOLARITY_RISING)      || \
+                                            ((__POLARITY__) == TIM_CLOCKPOLARITY_FALLING)     || \
+                                            ((__POLARITY__) == TIM_CLOCKPOLARITY_BOTHEDGE))
+
+#define IS_TIM_CLOCKPRESCALER(__PRESCALER__) (((__PRESCALER__) == TIM_CLOCKPRESCALER_DIV1) || \
+                                              ((__PRESCALER__) == TIM_CLOCKPRESCALER_DIV2) || \
+                                              ((__PRESCALER__) == TIM_CLOCKPRESCALER_DIV4) || \
+                                              ((__PRESCALER__) == TIM_CLOCKPRESCALER_DIV8))
+
+#define IS_TIM_CLOCKFILTER(__ICFILTER__)      ((__ICFILTER__) <= 0xFU)
+
+#define IS_TIM_CLEARINPUT_POLARITY(__POLARITY__) (((__POLARITY__) == TIM_CLEARINPUTPOLARITY_INVERTED) || \
+                                                  ((__POLARITY__) == TIM_CLEARINPUTPOLARITY_NONINVERTED))
+
+#define IS_TIM_CLEARINPUT_PRESCALER(__PRESCALER__) (((__PRESCALER__) == TIM_CLEARINPUTPRESCALER_DIV1) || \
+                                                    ((__PRESCALER__) == TIM_CLEARINPUTPRESCALER_DIV2) || \
+                                                    ((__PRESCALER__) == TIM_CLEARINPUTPRESCALER_DIV4) || \
+                                                    ((__PRESCALER__) == TIM_CLEARINPUTPRESCALER_DIV8))
+
+#define IS_TIM_CLEARINPUT_FILTER(__ICFILTER__) ((__ICFILTER__) <= 0xFU)
+
+#define IS_TIM_OSSR_STATE(__STATE__)       (((__STATE__) == TIM_OSSR_ENABLE) || \
+                                            ((__STATE__) == TIM_OSSR_DISABLE))
+
+#define IS_TIM_OSSI_STATE(__STATE__)       (((__STATE__) == TIM_OSSI_ENABLE) || \
+                                            ((__STATE__) == TIM_OSSI_DISABLE))
+
+#define IS_TIM_LOCK_LEVEL(__LEVEL__)       (((__LEVEL__) == TIM_LOCKLEVEL_OFF) || \
+                                            ((__LEVEL__) == TIM_LOCKLEVEL_1)   || \
+                                            ((__LEVEL__) == TIM_LOCKLEVEL_2)   || \
+                                            ((__LEVEL__) == TIM_LOCKLEVEL_3))
+
+#define IS_TIM_BREAK_FILTER(__BRKFILTER__) ((__BRKFILTER__) <= 0xFUL)
+
+#define IS_TIM_BREAK_STATE(__STATE__)      (((__STATE__) == TIM_BREAK_ENABLE) || \
+                                            ((__STATE__) == TIM_BREAK_DISABLE))
+
+#define IS_TIM_BREAK_POLARITY(__POLARITY__) (((__POLARITY__) == TIM_BREAKPOLARITY_LOW) || \
+                                             ((__POLARITY__) == TIM_BREAKPOLARITY_HIGH))
+#if  defined(TIM_BDTR_BKBID)
+
+#define IS_TIM_BREAK_AFMODE(__AFMODE__) (((__AFMODE__) == TIM_BREAK_AFMODE_INPUT) || \
+                                         ((__AFMODE__) == TIM_BREAK_AFMODE_BIDIRECTIONAL))
+
+#endif /* TIM_BDTR_BKBID */
+
+#define IS_TIM_BREAK2_STATE(__STATE__)     (((__STATE__) == TIM_BREAK2_ENABLE) || \
+                                            ((__STATE__) == TIM_BREAK2_DISABLE))
+
+#define IS_TIM_BREAK2_POLARITY(__POLARITY__) (((__POLARITY__) == TIM_BREAK2POLARITY_LOW) || \
+                                              ((__POLARITY__) == TIM_BREAK2POLARITY_HIGH))
+#if  defined(TIM_BDTR_BKBID)
+
+#define IS_TIM_BREAK2_AFMODE(__AFMODE__) (((__AFMODE__) == TIM_BREAK2_AFMODE_INPUT) || \
+                                          ((__AFMODE__) == TIM_BREAK2_AFMODE_BIDIRECTIONAL))
+
+#endif /* TIM_BDTR_BKBID */
+
+#define IS_TIM_AUTOMATIC_OUTPUT_STATE(__STATE__) (((__STATE__) == TIM_AUTOMATICOUTPUT_ENABLE) || \
+                                                  ((__STATE__) == TIM_AUTOMATICOUTPUT_DISABLE))
+
+#define IS_TIM_GROUPCH5(__OCREF__) ((((__OCREF__) & 0x1FFFFFFFU) == 0x00000000U))
+
+#define IS_TIM_TRGO_SOURCE(__SOURCE__) (((__SOURCE__) == TIM_TRGO_RESET)  || \
+                                        ((__SOURCE__) == TIM_TRGO_ENABLE) || \
+                                        ((__SOURCE__) == TIM_TRGO_UPDATE) || \
+                                        ((__SOURCE__) == TIM_TRGO_OC1)    || \
+                                        ((__SOURCE__) == TIM_TRGO_OC1REF) || \
+                                        ((__SOURCE__) == TIM_TRGO_OC2REF) || \
+                                        ((__SOURCE__) == TIM_TRGO_OC3REF) || \
+                                        ((__SOURCE__) == TIM_TRGO_OC4REF))
+
+#define IS_TIM_TRGO2_SOURCE(__SOURCE__) (((__SOURCE__) == TIM_TRGO2_RESET)                        || \
+                                         ((__SOURCE__) == TIM_TRGO2_ENABLE)                       || \
+                                         ((__SOURCE__) == TIM_TRGO2_UPDATE)                       || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC1)                          || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC1REF)                       || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC2REF)                       || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC3REF)                       || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC3REF)                       || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC4REF)                       || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC5REF)                       || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC6REF)                       || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC4REF_RISINGFALLING)         || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC6REF_RISINGFALLING)         || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC4REF_RISING_OC6REF_RISING)  || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC4REF_RISING_OC6REF_FALLING) || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC5REF_RISING_OC6REF_RISING)  || \
+                                         ((__SOURCE__) == TIM_TRGO2_OC5REF_RISING_OC6REF_FALLING))
+
+#define IS_TIM_MSM_STATE(__STATE__)      (((__STATE__) == TIM_MASTERSLAVEMODE_ENABLE) || \
+                                          ((__STATE__) == TIM_MASTERSLAVEMODE_DISABLE))
+
+#define IS_TIM_SLAVE_MODE(__MODE__) (((__MODE__) == TIM_SLAVEMODE_DISABLE)   || \
+                                     ((__MODE__) == TIM_SLAVEMODE_RESET)     || \
+                                     ((__MODE__) == TIM_SLAVEMODE_GATED)     || \
+                                     ((__MODE__) == TIM_SLAVEMODE_TRIGGER)   || \
+                                     ((__MODE__) == TIM_SLAVEMODE_EXTERNAL1) || \
+                                     ((__MODE__) == TIM_SLAVEMODE_COMBINED_RESETTRIGGER))
+
+#define IS_TIM_PWM_MODE(__MODE__) (((__MODE__) == TIM_OCMODE_PWM1)               || \
+                                   ((__MODE__) == TIM_OCMODE_PWM2)               || \
+                                   ((__MODE__) == TIM_OCMODE_COMBINED_PWM1)      || \
+                                   ((__MODE__) == TIM_OCMODE_COMBINED_PWM2)      || \
+                                   ((__MODE__) == TIM_OCMODE_ASYMMETRIC_PWM1)    || \
+                                   ((__MODE__) == TIM_OCMODE_ASYMMETRIC_PWM2))
+
+#define IS_TIM_OC_MODE(__MODE__)  (((__MODE__) == TIM_OCMODE_TIMING)             || \
+                                   ((__MODE__) == TIM_OCMODE_ACTIVE)             || \
+                                   ((__MODE__) == TIM_OCMODE_INACTIVE)           || \
+                                   ((__MODE__) == TIM_OCMODE_TOGGLE)             || \
+                                   ((__MODE__) == TIM_OCMODE_FORCED_ACTIVE)      || \
+                                   ((__MODE__) == TIM_OCMODE_FORCED_INACTIVE)    || \
+                                   ((__MODE__) == TIM_OCMODE_RETRIGERRABLE_OPM1) || \
+                                   ((__MODE__) == TIM_OCMODE_RETRIGERRABLE_OPM2))
+
+#define IS_TIM_TRIGGER_SELECTION(__SELECTION__) (((__SELECTION__) == TIM_TS_ITR0)    || \
+                                                 ((__SELECTION__) == TIM_TS_ITR1)    || \
+                                                 ((__SELECTION__) == TIM_TS_ITR2)    || \
+                                                 ((__SELECTION__) == TIM_TS_ITR3)    || \
+                                                 ((__SELECTION__) == TIM_TS_ITR4)    || \
+                                                 ((__SELECTION__) == TIM_TS_ITR5)    || \
+                                                 ((__SELECTION__) == TIM_TS_ITR6)    || \
+                                                 ((__SELECTION__) == TIM_TS_ITR7)    || \
+                                                 ((__SELECTION__) == TIM_TS_ITR8)    || \
+                                                 ((__SELECTION__) == TIM_TS_ITR12)   || \
+                                                 ((__SELECTION__) == TIM_TS_ITR13)   || \
+                                                 ((__SELECTION__) == TIM_TS_TI1F_ED) || \
+                                                 ((__SELECTION__) == TIM_TS_TI1FP1)  || \
+                                                 ((__SELECTION__) == TIM_TS_TI2FP2)  || \
+                                                 ((__SELECTION__) == TIM_TS_ETRF))
+
+#define IS_TIM_INTERNAL_TRIGGEREVENT_SELECTION(__SELECTION__) (((__SELECTION__) == TIM_TS_ITR0)  || \
+                                                               ((__SELECTION__) == TIM_TS_ITR1)  || \
+                                                               ((__SELECTION__) == TIM_TS_ITR2)  || \
+                                                               ((__SELECTION__) == TIM_TS_ITR3)  || \
+                                                               ((__SELECTION__) == TIM_TS_ITR4)  || \
+                                                               ((__SELECTION__) == TIM_TS_ITR5)  || \
+                                                               ((__SELECTION__) == TIM_TS_ITR6)  || \
+                                                               ((__SELECTION__) == TIM_TS_ITR7)  || \
+                                                               ((__SELECTION__) == TIM_TS_ITR8)  || \
+                                                               ((__SELECTION__) == TIM_TS_ITR12) || \
+                                                               ((__SELECTION__) == TIM_TS_ITR13) || \
+                                                               ((__SELECTION__) == TIM_TS_NONE))
+
+#define IS_TIM_TRIGGERPOLARITY(__POLARITY__)   (((__POLARITY__) == TIM_TRIGGERPOLARITY_INVERTED   ) || \
+                                                ((__POLARITY__) == TIM_TRIGGERPOLARITY_NONINVERTED) || \
+                                                ((__POLARITY__) == TIM_TRIGGERPOLARITY_RISING     ) || \
+                                                ((__POLARITY__) == TIM_TRIGGERPOLARITY_FALLING    ) || \
+                                                ((__POLARITY__) == TIM_TRIGGERPOLARITY_BOTHEDGE   ))
+
+#define IS_TIM_TRIGGERPRESCALER(__PRESCALER__) (((__PRESCALER__) == TIM_TRIGGERPRESCALER_DIV1) || \
+                                                ((__PRESCALER__) == TIM_TRIGGERPRESCALER_DIV2) || \
+                                                ((__PRESCALER__) == TIM_TRIGGERPRESCALER_DIV4) || \
+                                                ((__PRESCALER__) == TIM_TRIGGERPRESCALER_DIV8))
+
+#define IS_TIM_TRIGGERFILTER(__ICFILTER__) ((__ICFILTER__) <= 0xFU)
+
+#define IS_TIM_TI1SELECTION(__TI1SELECTION__)  (((__TI1SELECTION__) == TIM_TI1SELECTION_CH1) || \
+                                                ((__TI1SELECTION__) == TIM_TI1SELECTION_XORCOMBINATION))
+
+#define IS_TIM_DMA_LENGTH(__LENGTH__)      (((__LENGTH__) == TIM_DMABURSTLENGTH_1TRANSFER)   || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_2TRANSFERS)  || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_3TRANSFERS)  || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_4TRANSFERS)  || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_5TRANSFERS)  || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_6TRANSFERS)  || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_7TRANSFERS)  || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_8TRANSFERS)  || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_9TRANSFERS)  || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_10TRANSFERS) || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_11TRANSFERS) || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_12TRANSFERS) || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_13TRANSFERS) || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_14TRANSFERS) || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_15TRANSFERS) || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_16TRANSFERS) || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_17TRANSFERS) || \
+                                            ((__LENGTH__) == TIM_DMABURSTLENGTH_18TRANSFERS))
+
+#define IS_TIM_DMA_DATA_LENGTH(LENGTH) (((LENGTH) >= 0x1U) && ((LENGTH) < 0x10000U))
+
+#define IS_TIM_IC_FILTER(__ICFILTER__)   ((__ICFILTER__) <= 0xFU)
+
+#define IS_TIM_DEADTIME(__DEADTIME__)    ((__DEADTIME__) <= 0xFFU)
+
+#define IS_TIM_BREAK_SYSTEM(__CONFIG__)    (((__CONFIG__) == TIM_BREAK_SYSTEM_ECC)                  || \
+                                            ((__CONFIG__) == TIM_BREAK_SYSTEM_PVD)                  || \
+                                            ((__CONFIG__) == TIM_BREAK_SYSTEM_SRAM_PARITY_ERROR)    || \
+                                            ((__CONFIG__) == TIM_BREAK_SYSTEM_LOCKUP))
+
+#define IS_TIM_SLAVEMODE_TRIGGER_ENABLED(__TRIGGER__) (((__TRIGGER__) == TIM_SLAVEMODE_TRIGGER) || \
+                                                       ((__TRIGGER__) == TIM_SLAVEMODE_COMBINED_RESETTRIGGER))
+
+#define TIM_SET_ICPRESCALERVALUE(__HANDLE__, __CHANNEL__, __ICPSC__) \
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCMR1 |= (__ICPSC__)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCMR1 |= ((__ICPSC__) << 8U)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCMR2 |= (__ICPSC__)) :\
+   ((__HANDLE__)->Instance->CCMR2 |= ((__ICPSC__) << 8U)))
+
+#define TIM_RESET_ICPRESCALERVALUE(__HANDLE__, __CHANNEL__) \
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCMR1 &= ~TIM_CCMR1_IC1PSC) :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCMR1 &= ~TIM_CCMR1_IC2PSC) :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCMR2 &= ~TIM_CCMR2_IC3PSC) :\
+   ((__HANDLE__)->Instance->CCMR2 &= ~TIM_CCMR2_IC4PSC))
+
+#define TIM_SET_CAPTUREPOLARITY(__HANDLE__, __CHANNEL__, __POLARITY__) \
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCER |= (__POLARITY__)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCER |= ((__POLARITY__) << 4U)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCER |= ((__POLARITY__) << 8U)) :\
+   ((__HANDLE__)->Instance->CCER |= (((__POLARITY__) << 12U))))
+
+#define TIM_RESET_CAPTUREPOLARITY(__HANDLE__, __CHANNEL__) \
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC1P | TIM_CCER_CC1NP)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC2P | TIM_CCER_CC2NP)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC3P | TIM_CCER_CC3NP)) :\
+   ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC4P | TIM_CCER_CC4NP)))
+
+#define TIM_CHANNEL_STATE_GET(__HANDLE__, __CHANNEL__)\
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? (__HANDLE__)->ChannelState[0] :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? (__HANDLE__)->ChannelState[1] :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? (__HANDLE__)->ChannelState[2] :\
+   ((__CHANNEL__) == TIM_CHANNEL_4) ? (__HANDLE__)->ChannelState[3] :\
+   ((__CHANNEL__) == TIM_CHANNEL_5) ? (__HANDLE__)->ChannelState[4] :\
+   (__HANDLE__)->ChannelState[5])
+
+#define TIM_CHANNEL_STATE_SET(__HANDLE__, __CHANNEL__, __CHANNEL_STATE__) \
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->ChannelState[0] = (__CHANNEL_STATE__)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->ChannelState[1] = (__CHANNEL_STATE__)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->ChannelState[2] = (__CHANNEL_STATE__)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_4) ? ((__HANDLE__)->ChannelState[3] = (__CHANNEL_STATE__)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_5) ? ((__HANDLE__)->ChannelState[4] = (__CHANNEL_STATE__)) :\
+   ((__HANDLE__)->ChannelState[5] = (__CHANNEL_STATE__)))
+
+#define TIM_CHANNEL_STATE_SET_ALL(__HANDLE__,  __CHANNEL_STATE__) do { \
+                                                                       (__HANDLE__)->ChannelState[0]  = \
+                                                                       (__CHANNEL_STATE__);  \
+                                                                       (__HANDLE__)->ChannelState[1]  = \
+                                                                       (__CHANNEL_STATE__);  \
+                                                                       (__HANDLE__)->ChannelState[2]  = \
+                                                                       (__CHANNEL_STATE__);  \
+                                                                       (__HANDLE__)->ChannelState[3]  = \
+                                                                       (__CHANNEL_STATE__);  \
+                                                                       (__HANDLE__)->ChannelState[4]  = \
+                                                                       (__CHANNEL_STATE__);  \
+                                                                       (__HANDLE__)->ChannelState[5]  = \
+                                                                       (__CHANNEL_STATE__);  \
+                                                                     } while(0)
+
+#define TIM_CHANNEL_N_STATE_GET(__HANDLE__, __CHANNEL__)\
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? (__HANDLE__)->ChannelNState[0] :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? (__HANDLE__)->ChannelNState[1] :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? (__HANDLE__)->ChannelNState[2] :\
+   (__HANDLE__)->ChannelNState[3])
+
+#define TIM_CHANNEL_N_STATE_SET(__HANDLE__, __CHANNEL__, __CHANNEL_STATE__) \
+  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->ChannelNState[0] = (__CHANNEL_STATE__)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->ChannelNState[1] = (__CHANNEL_STATE__)) :\
+   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->ChannelNState[2] = (__CHANNEL_STATE__)) :\
+   ((__HANDLE__)->ChannelNState[3] = (__CHANNEL_STATE__)))
+
+#define TIM_CHANNEL_N_STATE_SET_ALL(__HANDLE__,  __CHANNEL_STATE__) do { \
+                                                                         (__HANDLE__)->ChannelNState[0] = \
+                                                                         (__CHANNEL_STATE__);  \
+                                                                         (__HANDLE__)->ChannelNState[1] = \
+                                                                         (__CHANNEL_STATE__);  \
+                                                                         (__HANDLE__)->ChannelNState[2] = \
+                                                                         (__CHANNEL_STATE__);  \
+                                                                         (__HANDLE__)->ChannelNState[3] = \
+                                                                         (__CHANNEL_STATE__);  \
+                                                                       } while(0)
+
+/**
+  * @}
+  */
+/* End of private macros -----------------------------------------------------*/
+
+/* Include TIM HAL Extended module */
+#include "stm32h7xx_hal_tim_ex.h"
+
+/* Exported functions --------------------------------------------------------*/
+/** @addtogroup TIM_Exported_Functions TIM Exported Functions
+  * @{
+  */
+
+/** @addtogroup TIM_Exported_Functions_Group1 TIM Time Base functions
+  *  @brief   Time Base functions
+  * @{
+  */
+/* Time Base functions ********************************************************/
+HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim);
+HAL_StatusTypeDef HAL_TIM_Base_DeInit(TIM_HandleTypeDef *htim);
+void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim);
+void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *htim);
+/* Blocking mode: Polling */
+HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim);
+HAL_StatusTypeDef HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim);
+/* Non-Blocking mode: Interrupt */
+HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);
+HAL_StatusTypeDef HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim);
+/* Non-Blocking mode: DMA */
+HAL_StatusTypeDef HAL_TIM_Base_Start_DMA(TIM_HandleTypeDef *htim, const uint32_t *pData, uint16_t Length);
+HAL_StatusTypeDef HAL_TIM_Base_Stop_DMA(TIM_HandleTypeDef *htim);
+/**
+  * @}
+  */
+
+/** @addtogroup TIM_Exported_Functions_Group2 TIM Output Compare functions
+  *  @brief   TIM Output Compare functions
+  * @{
+  */
+/* Timer Output Compare functions *********************************************/
+HAL_StatusTypeDef HAL_TIM_OC_Init(TIM_HandleTypeDef *htim);
+HAL_StatusTypeDef HAL_TIM_OC_DeInit(TIM_HandleTypeDef *htim);
+void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim);
+void HAL_TIM_OC_MspDeInit(TIM_HandleTypeDef *htim);
+/* Blocking mode: Polling */
+HAL_StatusTypeDef HAL_TIM_OC_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
+HAL_StatusTypeDef HAL_TIM_OC_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
+/* Non-Blocking mode: Interrupt */
+HAL_StatusTypeDef HAL_TIM_OC_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
+HAL_StatusTypeDef HAL_TIM_OC_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
+/* Non-Blocking mode: DMA */
+HAL_StatusTypeDef HAL_TIM_OC_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, const uint32_t *pData,
+                                       uint16_t Length);
+HAL_StatusTypeDef HAL_TIM_OC_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);
+/**
+  * @}
+  */
+
+/** @addtogroup TIM_Exported_Functions_Group3 TIM PWM functions
+  *  @brief   TIM PWM functions
+  * @{
+  */
+/* Timer PWM functions ********************************************************/
+HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim);
+HAL_StatusTypeDef HAL_TIM_PWM_DeInit(TIM_HandleTypeDef *htim);
+void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim);
+void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef *htim);
+/* Blocking mode: Polling */
+HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
+HAL_StatusTypeDef HAL_TIM_PWM_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
+/* Non-Blocking mode: Interrupt */
+HAL_StatusTypeDef HAL_TIM_PWM_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
+HAL_StatusTypeDef HAL_TIM_PWM_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
+/* Non-Blocking mode: DMA */
+HAL_StatusTypeDef HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, const uint32_t *pData,
+                                        uint16_t Length);
+HAL_StatusTypeDef HAL_TIM_PWM_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);
+/**
+  * @}
+  */
+
+/** @addtogroup TIM_Exported_Functions_Group4 TIM Input Capture functions
+  *  @brief   TIM Input Capture functions
+  * @{
+  */
+/* Timer Input Capture functions **********************************************/
+HAL_StatusTypeDef HAL_TIM_IC_Init(TIM_HandleTypeDef *htim);
+HAL_StatusTypeDef HAL_TIM_IC_DeInit(TIM_HandleTypeDef *htim);
+void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim);
+void HAL_TIM_IC_MspDeInit(TIM_HandleTypeDef *htim);
+/* Blocking mode: Polling */
+HAL_StatusTypeDef HAL_TIM_IC_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
+HAL_StatusTypeDef HAL_TIM_IC_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
+/* Non-Blocking mode: Interrupt */
+HAL_StatusTypeDef HAL_TIM_IC_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
+HAL_StatusTypeDef HAL_TIM_IC_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
+/* Non-Blocking mode: DMA */
+HAL_StatusTypeDef HAL_TIM_IC_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length);
+HAL_StatusTypeDef HAL_TIM_IC_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);
+/**
+  * @}
+  */
+
+/** @addtogroup TIM_Exported_Functions_Group5 TIM One Pulse functions
+  *  @brief   TIM One Pulse functions
+  * @{
+  */
+/* Timer One Pulse functions **************************************************/
+HAL_StatusTypeDef HAL_TIM_OnePulse_Init(TIM_HandleTypeDef *htim, uint32_t OnePulseMode);
+HAL_StatusTypeDef HAL_TIM_OnePulse_DeInit(TIM_HandleTypeDef *htim);
+void HAL_TIM_OnePulse_MspInit(TIM_HandleTypeDef *htim);
+void HAL_TIM_OnePulse_MspDeInit(TIM_HandleTypeDef *htim);
+/* Blocking mode: Polling */
+HAL_StatusTypeDef HAL_TIM_OnePulse_Start(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
+HAL_StatusTypeDef HAL_TIM_OnePulse_Stop(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
+/* Non-Blocking mode: Interrupt */
+HAL_StatusTypeDef HAL_TIM_OnePulse_Start_IT(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
+HAL_StatusTypeDef HAL_TIM_OnePulse_Stop_IT(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
+/**
+  * @}
+  */
+
+/** @addtogroup TIM_Exported_Functions_Group6 TIM Encoder functions
+  *  @brief   TIM Encoder functions
+  * @{
+  */
+/* Timer Encoder functions ****************************************************/
+HAL_StatusTypeDef HAL_TIM_Encoder_Init(TIM_HandleTypeDef *htim, const TIM_Encoder_InitTypeDef *sConfig);
+HAL_StatusTypeDef HAL_TIM_Encoder_DeInit(TIM_HandleTypeDef *htim);
+void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef *htim);
+void HAL_TIM_Encoder_MspDeInit(TIM_HandleTypeDef *htim);
+/* Blocking mode: Polling */
+HAL_StatusTypeDef HAL_TIM_Encoder_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
+HAL_StatusTypeDef HAL_TIM_Encoder_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
+/* Non-Blocking mode: Interrupt */
+HAL_StatusTypeDef HAL_TIM_Encoder_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
+HAL_StatusTypeDef HAL_TIM_Encoder_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
+/* Non-Blocking mode: DMA */
+HAL_StatusTypeDef HAL_TIM_Encoder_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData1,
+                                            uint32_t *pData2, uint16_t Length);
+HAL_StatusTypeDef HAL_TIM_Encoder_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);
+/**
+  * @}
+  */
+
+/** @addtogroup TIM_Exported_Functions_Group7 TIM IRQ handler management
+  *  @brief   IRQ handler management
+  * @{
+  */
+/* Interrupt Handler functions  ***********************************************/
+void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim);
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Exported_Functions_Group8 TIM Peripheral Control functions
+  *  @brief   Peripheral Control functions
+  * @{
+  */
+/* Control functions  *********************************************************/
+HAL_StatusTypeDef HAL_TIM_OC_ConfigChannel(TIM_HandleTypeDef *htim, const TIM_OC_InitTypeDef *sConfig,
+                                           uint32_t Channel);
+HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim, const TIM_OC_InitTypeDef *sConfig,
+                                            uint32_t Channel);
+HAL_StatusTypeDef HAL_TIM_IC_ConfigChannel(TIM_HandleTypeDef *htim, const TIM_IC_InitTypeDef *sConfig,
+                                           uint32_t Channel);
+HAL_StatusTypeDef HAL_TIM_OnePulse_ConfigChannel(TIM_HandleTypeDef *htim, TIM_OnePulse_InitTypeDef *sConfig,
+                                                 uint32_t OutputChannel,  uint32_t InputChannel);
+HAL_StatusTypeDef HAL_TIM_ConfigOCrefClear(TIM_HandleTypeDef *htim,
+                                           const TIM_ClearInputConfigTypeDef *sClearInputConfig,
+                                           uint32_t Channel);
+HAL_StatusTypeDef HAL_TIM_ConfigClockSource(TIM_HandleTypeDef *htim, const TIM_ClockConfigTypeDef *sClockSourceConfig);
+HAL_StatusTypeDef HAL_TIM_ConfigTI1Input(TIM_HandleTypeDef *htim, uint32_t TI1_Selection);
+HAL_StatusTypeDef HAL_TIM_SlaveConfigSynchro(TIM_HandleTypeDef *htim, const TIM_SlaveConfigTypeDef *sSlaveConfig);
+HAL_StatusTypeDef HAL_TIM_SlaveConfigSynchro_IT(TIM_HandleTypeDef *htim, const TIM_SlaveConfigTypeDef *sSlaveConfig);
+HAL_StatusTypeDef HAL_TIM_DMABurst_WriteStart(TIM_HandleTypeDef *htim, uint32_t BurstBaseAddress,
+                                              uint32_t BurstRequestSrc, const uint32_t  *BurstBuffer,
+                                              uint32_t  BurstLength);
+HAL_StatusTypeDef HAL_TIM_DMABurst_MultiWriteStart(TIM_HandleTypeDef *htim, uint32_t BurstBaseAddress,
+                                                   uint32_t BurstRequestSrc, const uint32_t *BurstBuffer,
+                                                   uint32_t BurstLength,  uint32_t DataLength);
+HAL_StatusTypeDef HAL_TIM_DMABurst_WriteStop(TIM_HandleTypeDef *htim, uint32_t BurstRequestSrc);
+HAL_StatusTypeDef HAL_TIM_DMABurst_ReadStart(TIM_HandleTypeDef *htim, uint32_t BurstBaseAddress,
+                                             uint32_t BurstRequestSrc, uint32_t  *BurstBuffer, uint32_t  BurstLength);
+HAL_StatusTypeDef HAL_TIM_DMABurst_MultiReadStart(TIM_HandleTypeDef *htim, uint32_t BurstBaseAddress,
+                                                  uint32_t BurstRequestSrc, uint32_t  *BurstBuffer,
+                                                  uint32_t  BurstLength, uint32_t  DataLength);
+HAL_StatusTypeDef HAL_TIM_DMABurst_ReadStop(TIM_HandleTypeDef *htim, uint32_t BurstRequestSrc);
+HAL_StatusTypeDef HAL_TIM_GenerateEvent(TIM_HandleTypeDef *htim, uint32_t EventSource);
+uint32_t HAL_TIM_ReadCapturedValue(const TIM_HandleTypeDef *htim, uint32_t Channel);
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Exported_Functions_Group9 TIM Callbacks functions
+  *  @brief   TIM Callbacks functions
+  * @{
+  */
+/* Callback in non blocking modes (Interrupt and DMA) *************************/
+void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
+void HAL_TIM_PeriodElapsedHalfCpltCallback(TIM_HandleTypeDef *htim);
+void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim);
+void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);
+void HAL_TIM_IC_CaptureHalfCpltCallback(TIM_HandleTypeDef *htim);
+void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim);
+void HAL_TIM_PWM_PulseFinishedHalfCpltCallback(TIM_HandleTypeDef *htim);
+void HAL_TIM_TriggerCallback(TIM_HandleTypeDef *htim);
+void HAL_TIM_TriggerHalfCpltCallback(TIM_HandleTypeDef *htim);
+void HAL_TIM_ErrorCallback(TIM_HandleTypeDef *htim);
+
+/* Callbacks Register/UnRegister functions  ***********************************/
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+HAL_StatusTypeDef HAL_TIM_RegisterCallback(TIM_HandleTypeDef *htim, HAL_TIM_CallbackIDTypeDef CallbackID,
+                                           pTIM_CallbackTypeDef pCallback);
+HAL_StatusTypeDef HAL_TIM_UnRegisterCallback(TIM_HandleTypeDef *htim, HAL_TIM_CallbackIDTypeDef CallbackID);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Exported_Functions_Group10 TIM Peripheral State functions
+  *  @brief  Peripheral State functions
+  * @{
+  */
+/* Peripheral State functions  ************************************************/
+HAL_TIM_StateTypeDef HAL_TIM_Base_GetState(const TIM_HandleTypeDef *htim);
+HAL_TIM_StateTypeDef HAL_TIM_OC_GetState(const TIM_HandleTypeDef *htim);
+HAL_TIM_StateTypeDef HAL_TIM_PWM_GetState(const TIM_HandleTypeDef *htim);
+HAL_TIM_StateTypeDef HAL_TIM_IC_GetState(const TIM_HandleTypeDef *htim);
+HAL_TIM_StateTypeDef HAL_TIM_OnePulse_GetState(const TIM_HandleTypeDef *htim);
+HAL_TIM_StateTypeDef HAL_TIM_Encoder_GetState(const TIM_HandleTypeDef *htim);
+
+/* Peripheral Channel state functions  ************************************************/
+HAL_TIM_ActiveChannel HAL_TIM_GetActiveChannel(const TIM_HandleTypeDef *htim);
+HAL_TIM_ChannelStateTypeDef HAL_TIM_GetChannelState(const TIM_HandleTypeDef *htim,  uint32_t Channel);
+HAL_TIM_DMABurstStateTypeDef HAL_TIM_DMABurstState(const TIM_HandleTypeDef *htim);
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+/* End of exported functions -------------------------------------------------*/
+
+/* Private functions----------------------------------------------------------*/
+/** @defgroup TIM_Private_Functions TIM Private Functions
+  * @{
+  */
+void TIM_Base_SetConfig(TIM_TypeDef *TIMx, const TIM_Base_InitTypeDef *Structure);
+void TIM_TI1_SetConfig(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICSelection, uint32_t TIM_ICFilter);
+void TIM_OC2_SetConfig(TIM_TypeDef *TIMx, const TIM_OC_InitTypeDef *OC_Config);
+void TIM_ETR_SetConfig(TIM_TypeDef *TIMx, uint32_t TIM_ExtTRGPrescaler,
+                       uint32_t TIM_ExtTRGPolarity, uint32_t ExtTRGFilter);
+
+void TIM_DMADelayPulseHalfCplt(DMA_HandleTypeDef *hdma);
+void TIM_DMAError(DMA_HandleTypeDef *hdma);
+void TIM_DMACaptureCplt(DMA_HandleTypeDef *hdma);
+void TIM_DMACaptureHalfCplt(DMA_HandleTypeDef *hdma);
+void TIM_CCxChannelCmd(TIM_TypeDef *TIMx, uint32_t Channel, uint32_t ChannelState);
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+void TIM_ResetCallback(TIM_HandleTypeDef *htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+/**
+  * @}
+  */
+/* End of private functions --------------------------------------------------*/
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* STM32H7xx_HAL_TIM_H */
Index: ctrl/firmware/Main/CubeMX/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_tim_ex.h
===================================================================
--- ctrl/firmware/Main/CubeMX/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_tim_ex.h	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_tim_ex.h	(revision 54)
@@ -0,0 +1,533 @@
+/**
+  ******************************************************************************
+  * @file    stm32h7xx_hal_tim_ex.h
+  * @author  MCD Application Team
+  * @brief   Header file of TIM HAL Extended module.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2017 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.
+  *
+  ******************************************************************************
+  */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef STM32H7xx_HAL_TIM_EX_H
+#define STM32H7xx_HAL_TIM_EX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32h7xx_hal_def.h"
+
+/** @addtogroup STM32H7xx_HAL_Driver
+  * @{
+  */
+
+/** @addtogroup TIMEx
+  * @{
+  */
+
+/* Exported types ------------------------------------------------------------*/
+/** @defgroup TIMEx_Exported_Types TIM Extended Exported Types
+  * @{
+  */
+
+/**
+  * @brief  TIM Hall sensor Configuration Structure definition
+  */
+
+typedef struct
+{
+  uint32_t IC1Polarity;         /*!< Specifies the active edge of the input signal.
+                                     This parameter can be a value of @ref TIM_Input_Capture_Polarity */
+
+  uint32_t IC1Prescaler;        /*!< Specifies the Input Capture Prescaler.
+                                     This parameter can be a value of @ref TIM_Input_Capture_Prescaler */
+
+  uint32_t IC1Filter;           /*!< Specifies the input capture filter.
+                                     This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
+
+  uint32_t Commutation_Delay;   /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
+                                     This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
+} TIM_HallSensor_InitTypeDef;
+#if defined(TIM_BREAK_INPUT_SUPPORT)
+
+/**
+  * @brief  TIM Break/Break2 input configuration
+  */
+typedef struct
+{
+  uint32_t Source;         /*!< Specifies the source of the timer break input.
+                                This parameter can be a value of @ref TIMEx_Break_Input_Source */
+  uint32_t Enable;         /*!< Specifies whether or not the break input source is enabled.
+                                This parameter can be a value of @ref TIMEx_Break_Input_Source_Enable */
+  uint32_t Polarity;       /*!< Specifies the break input source polarity.
+                                This parameter can be a value of @ref TIMEx_Break_Input_Source_Polarity
+                                Not relevant when analog watchdog output of the DFSDM1 used as break input source */
+} TIMEx_BreakInputConfigTypeDef;
+
+#endif /* TIM_BREAK_INPUT_SUPPORT */
+/**
+  * @}
+  */
+/* End of exported types -----------------------------------------------------*/
+
+/* Exported constants --------------------------------------------------------*/
+/** @defgroup TIMEx_Exported_Constants TIM Extended Exported Constants
+  * @{
+  */
+
+/** @defgroup TIMEx_Remap TIM Extended Remapping
+  * @{
+  */
+#define TIM_TIM1_ETR_GPIO        0x00000000U                                                 /*!< TIM1_ETR is connected to GPIO */
+#define TIM_TIM1_ETR_COMP1       TIM1_AF1_ETRSEL_0                                           /*!< TIM1_ETR is connected to COMP1 OUT */
+#define TIM_TIM1_ETR_COMP2       TIM1_AF1_ETRSEL_1                                           /*!< TIM1_ETR is connected to COMP2 OUT */
+#define TIM_TIM1_ETR_ADC1_AWD1   (TIM1_AF1_ETRSEL_1 | TIM1_AF1_ETRSEL_0)                     /*!< TIM1_ETR is connected to ADC1 AWD1 */
+#define TIM_TIM1_ETR_ADC1_AWD2   (TIM1_AF1_ETRSEL_2)                                         /*!< TIM1_ETR is connected to ADC1 AWD2 */
+#define TIM_TIM1_ETR_ADC1_AWD3   (TIM1_AF1_ETRSEL_2 | TIM1_AF1_ETRSEL_0)                     /*!< TIM1_ETR is connected to ADC1 AWD3 */
+#define TIM_TIM1_ETR_ADC3_AWD1   (TIM1_AF1_ETRSEL_2 | TIM1_AF1_ETRSEL_1)                     /*!< TIM1_ETR is connected to ADC3 AWD1 */
+#define TIM_TIM1_ETR_ADC3_AWD2   (TIM1_AF1_ETRSEL_2 | TIM1_AF1_ETRSEL_1 | TIM1_AF1_ETRSEL_0) /*!< TIM1_ETR is connected to ADC3 AWD2 */
+#define TIM_TIM1_ETR_ADC3_AWD3   TIM1_AF1_ETRSEL_3                                           /*!< TIM1_ETR is connected to ADC3 AWD3 */
+
+#define TIM_TIM8_ETR_GPIO        0x00000000U                                                 /*!< TIM8_ETR is connected to GPIO */
+#define TIM_TIM8_ETR_COMP1       TIM8_AF1_ETRSEL_0                                           /*!< TIM8_ETR is connected to COMP1 OUT */
+#define TIM_TIM8_ETR_COMP2       TIM8_AF1_ETRSEL_1                                           /*!< TIM8_ETR is connected to COMP2 OUT */
+#define TIM_TIM8_ETR_ADC2_AWD1   (TIM8_AF1_ETRSEL_1 | TIM8_AF1_ETRSEL_0)                     /*!< TIM8_ETR is connected to ADC2 AWD1 */
+#define TIM_TIM8_ETR_ADC2_AWD2   (TIM8_AF1_ETRSEL_2)                                         /*!< TIM8_ETR is connected to ADC2 AWD2 */
+#define TIM_TIM8_ETR_ADC2_AWD3   (TIM8_AF1_ETRSEL_2 | TIM8_AF1_ETRSEL_0)                     /*!< TIM8_ETR is connected to ADC2 AWD3 */
+#define TIM_TIM8_ETR_ADC3_AWD1   (TIM8_AF1_ETRSEL_2 | TIM8_AF1_ETRSEL_1)                     /*!< TIM8_ETR is connected to ADC3 AWD1 */
+#define TIM_TIM8_ETR_ADC3_AWD2   (TIM8_AF1_ETRSEL_2 | TIM8_AF1_ETRSEL_1 | TIM8_AF1_ETRSEL_0) /*!< TIM8_ETR is connected to ADC3 AWD2 */
+#define TIM_TIM8_ETR_ADC3_AWD3   TIM8_AF1_ETRSEL_3                                           /*!< TIM8_ETR is connected to ADC3 AWD3 */
+
+#define TIM_TIM2_ETR_GPIO        0x00000000U                             /*!< TIM2_ETR is connected to GPIO */
+#define TIM_TIM2_ETR_COMP1       (TIM2_AF1_ETRSEL_0)                     /*!< TIM2_ETR is connected to COMP1 OUT */
+#define TIM_TIM2_ETR_COMP2       (TIM2_AF1_ETRSEL_1)                     /*!< TIM2_ETR is connected to COMP2 OUT */
+#define TIM_TIM2_ETR_RCC_LSE     (TIM2_AF1_ETRSEL_1 | TIM8_AF1_ETRSEL_0) /*!< TIM2_ETR is connected to RCC LSE */
+#define TIM_TIM2_ETR_SAI1_FSA    TIM2_AF1_ETRSEL_2                       /*!< TIM2_ETR is connected to SAI1 FS_A */
+#define TIM_TIM2_ETR_SAI1_FSB    (TIM2_AF1_ETRSEL_2 | TIM8_AF1_ETRSEL_0) /*!< TIM2_ETR is connected to SAI1 FS_B */
+
+#define TIM_TIM3_ETR_GPIO        0x00000000U          /*!< TIM3_ETR is connected to GPIO */
+#define TIM_TIM3_ETR_COMP1       TIM3_AF1_ETRSEL_0    /*!< TIM3_ETR is connected to COMP1 OUT */
+
+#define TIM_TIM5_ETR_GPIO        0x00000000U          /*!< TIM5_ETR is connected to GPIO */
+#define TIM_TIM5_ETR_SAI2_FSA    TIM5_AF1_ETRSEL_0    /*!< TIM5_ETR is connected to SAI2 FS_A */
+#define TIM_TIM5_ETR_SAI2_FSB    TIM5_AF1_ETRSEL_1    /*!< TIM5_ETR is connected to SAI2 FS_B */
+#define TIM_TIM5_ETR_SAI4_FSA    TIM5_AF1_ETRSEL_0    /*!< TIM5_ETR is connected to SAI4 FS_A */
+#define TIM_TIM5_ETR_SAI4_FSB    TIM5_AF1_ETRSEL_1    /*!< TIM5_ETR is connected to SAI4 FS_B */
+
+#define TIM_TIM23_ETR_GPIO       0x00000000U          /*!< TIM23_ETR is connected to GPIO */
+#define TIM_TIM23_ETR_COMP1      (TIM2_AF1_ETRSEL_0)  /*!< TIM23_ETR is connected to COMP1 OUT */
+#define TIM_TIM23_ETR_COMP2      (TIM2_AF1_ETRSEL_1)  /*!< TIM23_ETR is connected to COMP2 OUT */
+
+#define TIM_TIM24_ETR_GPIO        0x00000000U                                /*!< TIM24_ETR is connected to GPIO */
+#define TIM_TIM24_ETR_SAI4_FSA    TIM5_AF1_ETRSEL_0                          /*!< TIM24_ETR is connected to SAI4 FS_A */
+#define TIM_TIM24_ETR_SAI4_FSB    TIM5_AF1_ETRSEL_1                          /*!< TIM24_ETR is connected to SAI4 FS_B */
+#define TIM_TIM24_ETR_SAI1_FSA    (TIM2_AF1_ETRSEL_1 | TIM8_AF1_ETRSEL_0)    /*!< TIM24_ETR is connected to SAI1 FS_A */
+#define TIM_TIM24_ETR_SAI1_FSB    TIM2_AF1_ETRSEL_2                          /*!< TIM24_ETR is connected to SAI1 FS_B */
+/**
+  * @}
+  */
+#if defined(TIM_BREAK_INPUT_SUPPORT)
+
+/** @defgroup TIMEx_Break_Input TIM Extended Break input
+  * @{
+  */
+#define TIM_BREAKINPUT_BRK     0x00000001U                                      /*!< Timer break input  */
+#define TIM_BREAKINPUT_BRK2    0x00000002U                                      /*!< Timer break2 input */
+/**
+  * @}
+  */
+
+/** @defgroup TIMEx_Break_Input_Source TIM Extended Break input source
+  * @{
+  */
+#define TIM_BREAKINPUTSOURCE_BKIN     0x00000001U                               /*!< An external source (GPIO) is connected to the BKIN pin  */
+#define TIM_BREAKINPUTSOURCE_COMP1    0x00000002U                               /*!< The COMP1 output is connected to the break input */
+#define TIM_BREAKINPUTSOURCE_COMP2    0x00000004U                               /*!< The COMP2 output is connected to the break input */
+#define TIM_BREAKINPUTSOURCE_DFSDM1   0x00000008U                               /*!< The analog watchdog output of the DFSDM1 peripheral is connected to the break input */
+/**
+  * @}
+  */
+
+/** @defgroup TIMEx_Break_Input_Source_Enable TIM Extended Break input source enabling
+  * @{
+  */
+#define TIM_BREAKINPUTSOURCE_DISABLE     0x00000000U                            /*!< Break input source is disabled */
+#define TIM_BREAKINPUTSOURCE_ENABLE      0x00000001U                            /*!< Break input source is enabled */
+/**
+  * @}
+  */
+
+/** @defgroup TIMEx_Break_Input_Source_Polarity TIM Extended Break input polarity
+  * @{
+  */
+#define TIM_BREAKINPUTSOURCE_POLARITY_LOW     0x00000001U                       /*!< Break input source is active low */
+#define TIM_BREAKINPUTSOURCE_POLARITY_HIGH    0x00000000U                       /*!< Break input source is active_high */
+/**
+  * @}
+  */
+#endif /* TIM_BREAK_INPUT_SUPPORT */
+
+/** @defgroup TIMEx_Timer_Input_Selection TIM Extended Timer input selection
+  * @{
+  */
+#define TIM_TIM1_TI1_GPIO                          0x00000000U                               /*!< TIM1_TI1 is connected to GPIO */
+#define TIM_TIM1_TI1_COMP1                         TIM_TISEL_TI1SEL_0                        /*!< TIM1_TI1 is connected to COMP1 OUT */
+
+#define TIM_TIM8_TI1_GPIO                          0x00000000U                               /*!< TIM8_TI1 is connected to GPIO */
+#define TIM_TIM8_TI1_COMP2                         TIM_TISEL_TI1SEL_0                        /*!< TIM8_TI1 is connected to COMP2 OUT */
+
+#define TIM_TIM2_TI4_GPIO                          0x00000000U                               /*!< TIM2_TI4 is connected to GPIO */
+#define TIM_TIM2_TI4_COMP1                         TIM_TISEL_TI4SEL_0                        /*!< TIM2_TI4 is connected to COMP1 OUT */
+#define TIM_TIM2_TI4_COMP2                         TIM_TISEL_TI4SEL_1                        /*!< TIM2_TI4 is connected to COMP2 OUT */
+#define TIM_TIM2_TI4_COMP1_COMP2                   (TIM_TISEL_TI4SEL_0 | TIM_TISEL_TI4SEL_1) /*!< TIM2_TI4 is connected to COMP2 OUT OR COMP2 OUT */
+
+#define TIM_TIM3_TI1_GPIO                          0x00000000U                               /*!< TIM3_TI1 is connected to GPIO */
+#define TIM_TIM3_TI1_COMP1                         TIM_TISEL_TI1SEL_0                        /*!< TIM3_TI1 is connected to COMP1 OUT */
+#define TIM_TIM3_TI1_COMP2                         TIM_TISEL_TI1SEL_1                        /*!< TIM3_TI1 is connected to COMP2 OUT */
+#define TIM_TIM3_TI1_COMP1_COMP2                   (TIM_TISEL_TI1SEL_0 | TIM_TISEL_TI1SEL_1) /*!< TIM3_TI1 is connected to COMP1 OUT or COMP2 OUT */
+
+#define TIM_TIM5_TI1_GPIO                          0x00000000U                               /*!< TIM5_TI1 is connected to GPIO */
+#define TIM_TIM5_TI1_CAN_TMP                       TIM_TISEL_TI1SEL_0                        /*!< TIM5_TI1 is connected to CAN TMP */
+#define TIM_TIM5_TI1_CAN_RTP                       TIM_TISEL_TI1SEL_1                        /*!< TIM5_TI1 is connected to CAN RTP */
+
+#define TIM_TIM12_TI1_GPIO                         0x00000000U                               /*!< TIM12 TI1 is connected to GPIO */
+#define TIM_TIM12_TI1_SPDIF_FS                     TIM_TISEL_TI1SEL_0                        /*!< TIM12 TI1 is connected to SPDIF FS */
+
+#define TIM_TIM15_TI1_GPIO                         0x00000000U                               /*!< TIM15_TI1 is connected to GPIO */
+#define TIM_TIM15_TI1_TIM2_CH1                     TIM_TISEL_TI1SEL_0                        /*!< TIM15_TI1 is connected to TIM2 CH1 */
+#define TIM_TIM15_TI1_TIM3_CH1                     TIM_TISEL_TI1SEL_1                        /*!< TIM15_TI1 is connected to TIM3 CH1 */
+#define TIM_TIM15_TI1_TIM4_CH1                     (TIM_TISEL_TI1SEL_0 | TIM_TISEL_TI1SEL_1) /*!< TIM15_TI1 is connected to TIM4 CH1 */
+#define TIM_TIM15_TI1_RCC_LSE                      (TIM_TISEL_TI1SEL_2)                      /*!< TIM15_TI1 is connected to RCC LSE  */
+#define TIM_TIM15_TI1_RCC_CSI                      (TIM_TISEL_TI1SEL_2 | TIM_TISEL_TI1SEL_0) /*!< TIM15_TI1 is connected to RCC CSI  */
+#define TIM_TIM15_TI1_RCC_MCO2                     (TIM_TISEL_TI1SEL_2 | TIM_TISEL_TI1SEL_1) /*!< TIM15_TI1 is connected to RCC MCO2 */
+
+#define TIM_TIM15_TI2_GPIO                         0x00000000U                               /*!< TIM15_TI2 is connected to GPIO */
+#define TIM_TIM15_TI2_TIM2_CH2                     (TIM_TISEL_TI2SEL_0)                      /*!< TIM15_TI2 is connected to TIM2 CH2 */
+#define TIM_TIM15_TI2_TIM3_CH2                     (TIM_TISEL_TI2SEL_1)                      /*!< TIM15_TI2 is connected to TIM3 CH2 */
+#define TIM_TIM15_TI2_TIM4_CH2                     (TIM_TISEL_TI2SEL_0 | TIM_TISEL_TI2SEL_1) /*!< TIM15_TI2 is connected to TIM4 CH2 */
+
+#define TIM_TIM16_TI1_GPIO                         0x00000000U                               /*!< TIM16 TI1 is connected to GPIO */
+#define TIM_TIM16_TI1_RCC_LSI                      TIM_TISEL_TI1SEL_0                        /*!< TIM16 TI1 is connected to RCC LSI */
+#define TIM_TIM16_TI1_RCC_LSE                      TIM_TISEL_TI1SEL_1                        /*!< TIM16 TI1 is connected to RCC LSE */
+#define TIM_TIM16_TI1_WKUP_IT                      (TIM_TISEL_TI1SEL_0 | TIM_TISEL_TI1SEL_1) /*!< TIM16 TI1 is connected to WKUP_IT */
+
+#define TIM_TIM17_TI1_GPIO                         0x00000000U                               /*!< TIM17 TI1 is connected to GPIO */
+#define TIM_TIM17_TI1_SPDIF_FS                     TIM_TISEL_TI1SEL_0                        /*!< TIM17 TI1 is connected to SPDIF FS */
+#define TIM_TIM17_TI1_RCC_HSE1MHZ                  TIM_TISEL_TI1SEL_1                        /*!< TIM17 TI1 is connected to RCC HSE 1Mhz */
+#define TIM_TIM17_TI1_RCC_MCO1                     (TIM_TISEL_TI1SEL_0 | TIM_TISEL_TI1SEL_1) /*!< TIM17 TI1 is connected to RCC MCO1 */
+
+#define TIM_TIM23_TI4_GPIO                         0x00000000U                               /*!< TIM23_TI4 is connected to GPIO */
+#define TIM_TIM23_TI4_COMP1                        TIM_TISEL_TI4SEL_0                        /*!< TIM23_TI4 is connected to COMP1 OUT */
+#define TIM_TIM23_TI4_COMP2                        TIM_TISEL_TI4SEL_1                        /*!< TIM23_TI4 is connected to COMP2 OUT */
+#define TIM_TIM23_TI4_COMP1_COMP2                  (TIM_TISEL_TI4SEL_0 | TIM_TISEL_TI4SEL_1) /*!< TIM23_TI4 is connected to COMP1 OUT or COMP2 OUT */
+
+#define TIM_TIM24_TI1_GPIO                         0x00000000U                               /*!< TIM24_TI1 is connected to GPIO */
+#define TIM_TIM24_TI1_CAN_TMP                      TIM_TISEL_TI1SEL_0                        /*!< TIM24_TI1 is connected to CAN TMP  */
+#define TIM_TIM24_TI1_CAN_RTP                      TIM_TISEL_TI1SEL_1                        /*!< TIM24_TI1 is connected to CAN RTP  */
+#define TIM_TIM24_TI1_CAN_SOC                      (TIM_TISEL_TI4SEL_0 | TIM_TISEL_TI4SEL_1) /*!< TIM24_TI1 is connected to CAN SOC */
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+/* End of exported constants -------------------------------------------------*/
+
+/* Exported macro ------------------------------------------------------------*/
+/** @defgroup TIMEx_Exported_Macros TIM Extended Exported Macros
+  * @{
+  */
+
+/**
+  * @}
+  */
+/* End of exported macro -----------------------------------------------------*/
+
+/* Private macro -------------------------------------------------------------*/
+/** @defgroup TIMEx_Private_Macros TIM Extended Private Macros
+  * @{
+  */
+#define IS_TIM_BREAKINPUT(__BREAKINPUT__)  (((__BREAKINPUT__) == TIM_BREAKINPUT_BRK)  || \
+                                            ((__BREAKINPUT__) == TIM_BREAKINPUT_BRK2))
+
+#define IS_TIM_BREAKINPUTSOURCE(__SOURCE__)  (((__SOURCE__) == TIM_BREAKINPUTSOURCE_BKIN)  || \
+                                              ((__SOURCE__) == TIM_BREAKINPUTSOURCE_COMP1) || \
+                                              ((__SOURCE__) == TIM_BREAKINPUTSOURCE_COMP2) || \
+                                              ((__SOURCE__) == TIM_BREAKINPUTSOURCE_DFSDM1))
+
+#define IS_TIM_BREAKINPUTSOURCE_STATE(__STATE__)  (((__STATE__) == TIM_BREAKINPUTSOURCE_DISABLE)  || \
+                                                   ((__STATE__) == TIM_BREAKINPUTSOURCE_ENABLE))
+
+#define IS_TIM_BREAKINPUTSOURCE_POLARITY(__POLARITY__)  (((__POLARITY__) == TIM_BREAKINPUTSOURCE_POLARITY_LOW)  || \
+                                                         ((__POLARITY__) == TIM_BREAKINPUTSOURCE_POLARITY_HIGH))
+
+#define IS_TIM_TISEL(__TISEL__)  (((__TISEL__) == TIM_TIM1_TI1_GPIO)         ||\
+                                  ((__TISEL__) == TIM_TIM1_TI1_COMP1)        ||\
+                                  ((__TISEL__) == TIM_TIM8_TI1_GPIO)         ||\
+                                  ((__TISEL__) == TIM_TIM8_TI1_COMP2)        ||\
+                                  ((__TISEL__) == TIM_TIM2_TI4_GPIO)         ||\
+                                  ((__TISEL__) == TIM_TIM2_TI4_COMP1)        ||\
+                                  ((__TISEL__) == TIM_TIM2_TI4_COMP2)        ||\
+                                  ((__TISEL__) == TIM_TIM2_TI4_COMP1_COMP2)  ||\
+                                  ((__TISEL__) == TIM_TIM3_TI1_GPIO)         ||\
+                                  ((__TISEL__) == TIM_TIM3_TI1_COMP1)        ||\
+                                  ((__TISEL__) == TIM_TIM3_TI1_COMP2)        ||\
+                                  ((__TISEL__) == TIM_TIM3_TI1_COMP1_COMP2)  ||\
+                                  ((__TISEL__) == TIM_TIM5_TI1_GPIO)         ||\
+                                  ((__TISEL__) == TIM_TIM5_TI1_CAN_TMP)      ||\
+                                  ((__TISEL__) == TIM_TIM5_TI1_CAN_RTP)      ||\
+                                  ((__TISEL__) == TIM_TIM12_TI1_SPDIF_FS)    ||\
+                                  ((__TISEL__) == TIM_TIM12_TI1_GPIO)        ||\
+                                  ((__TISEL__) == TIM_TIM15_TI1_GPIO)        ||\
+                                  ((__TISEL__) == TIM_TIM15_TI1_TIM2_CH1)    ||\
+                                  ((__TISEL__) == TIM_TIM15_TI1_TIM3_CH1)    ||\
+                                  ((__TISEL__) == TIM_TIM15_TI1_TIM4_CH1)    ||\
+                                  ((__TISEL__) == TIM_TIM15_TI1_RCC_LSE)     ||\
+                                  ((__TISEL__) == TIM_TIM15_TI1_RCC_CSI)     ||\
+                                  ((__TISEL__) == TIM_TIM15_TI1_RCC_MCO2)    ||\
+                                  ((__TISEL__) == TIM_TIM15_TI2_GPIO)        ||\
+                                  ((__TISEL__) == TIM_TIM15_TI2_TIM2_CH2)    ||\
+                                  ((__TISEL__) == TIM_TIM15_TI2_TIM3_CH2)    ||\
+                                  ((__TISEL__) == TIM_TIM15_TI2_TIM4_CH2)    ||\
+                                  ((__TISEL__) == TIM_TIM16_TI1_GPIO)        ||\
+                                  ((__TISEL__) == TIM_TIM16_TI1_RCC_LSI)     ||\
+                                  ((__TISEL__) == TIM_TIM16_TI1_RCC_LSE)     ||\
+                                  ((__TISEL__) == TIM_TIM16_TI1_WKUP_IT)     ||\
+                                  ((__TISEL__) == TIM_TIM17_TI1_GPIO)        ||\
+                                  ((__TISEL__) == TIM_TIM17_TI1_SPDIF_FS)    ||\
+                                  ((__TISEL__) == TIM_TIM17_TI1_RCC_HSE1MHZ) ||\
+                                  ((__TISEL__) == TIM_TIM17_TI1_RCC_MCO1)    ||\
+                                  ((__TISEL__) == TIM_TIM23_TI4_GPIO)        ||\
+                                  ((__TISEL__) == TIM_TIM23_TI4_COMP1)       ||\
+                                  ((__TISEL__) == TIM_TIM23_TI4_COMP2)       ||\
+                                  ((__TISEL__) == TIM_TIM23_TI4_COMP1_COMP2) ||\
+                                  ((__TISEL__) == TIM_TIM24_TI1_GPIO)        ||\
+                                  ((__TISEL__) == TIM_TIM24_TI1_CAN_TMP)     ||\
+                                  ((__TISEL__) == TIM_TIM24_TI1_CAN_RTP)     ||\
+                                  ((__TISEL__) == TIM_TIM24_TI1_CAN_SOC))
+
+#define IS_TIM_REMAP(__RREMAP__)     (((__RREMAP__) == TIM_TIM1_ETR_GPIO)      ||\
+                                      ((__RREMAP__) == TIM_TIM1_ETR_ADC1_AWD1) ||\
+                                      ((__RREMAP__) == TIM_TIM1_ETR_ADC1_AWD2) ||\
+                                      ((__RREMAP__) == TIM_TIM1_ETR_ADC1_AWD3) ||\
+                                      ((__RREMAP__) == TIM_TIM1_ETR_ADC3_AWD1) ||\
+                                      ((__RREMAP__) == TIM_TIM1_ETR_ADC3_AWD2) ||\
+                                      ((__RREMAP__) == TIM_TIM1_ETR_ADC3_AWD3) ||\
+                                      ((__RREMAP__) == TIM_TIM1_ETR_COMP1)     ||\
+                                      ((__RREMAP__) == TIM_TIM1_ETR_COMP2)     ||\
+                                      ((__RREMAP__) == TIM_TIM8_ETR_GPIO)      ||\
+                                      ((__RREMAP__) == TIM_TIM8_ETR_ADC2_AWD1) ||\
+                                      ((__RREMAP__) == TIM_TIM8_ETR_ADC2_AWD2) ||\
+                                      ((__RREMAP__) == TIM_TIM8_ETR_ADC2_AWD3) ||\
+                                      ((__RREMAP__) == TIM_TIM8_ETR_ADC3_AWD1) ||\
+                                      ((__RREMAP__) == TIM_TIM8_ETR_ADC3_AWD2) ||\
+                                      ((__RREMAP__) == TIM_TIM8_ETR_ADC3_AWD3) ||\
+                                      ((__RREMAP__) == TIM_TIM8_ETR_COMP1)     ||\
+                                      ((__RREMAP__) == TIM_TIM8_ETR_COMP2)     ||\
+                                      ((__RREMAP__) == TIM_TIM2_ETR_GPIO)      ||\
+                                      ((__RREMAP__) == TIM_TIM2_ETR_COMP1)     ||\
+                                      ((__RREMAP__) == TIM_TIM2_ETR_COMP2)     ||\
+                                      ((__RREMAP__) == TIM_TIM2_ETR_RCC_LSE)   ||\
+                                      ((__RREMAP__) == TIM_TIM2_ETR_SAI1_FSA)  ||\
+                                      ((__RREMAP__) == TIM_TIM2_ETR_SAI1_FSB)  ||\
+                                      ((__RREMAP__) == TIM_TIM3_ETR_GPIO)      ||\
+                                      ((__RREMAP__) == TIM_TIM3_ETR_COMP1)     ||\
+                                      ((__RREMAP__) == TIM_TIM5_ETR_GPIO)      ||\
+                                      ((__RREMAP__) == TIM_TIM5_ETR_SAI2_FSA)  ||\
+                                      ((__RREMAP__) == TIM_TIM5_ETR_SAI2_FSB)  ||\
+                                      ((__RREMAP__) == TIM_TIM23_ETR_GPIO)     ||\
+                                      ((__RREMAP__) == TIM_TIM23_ETR_COMP1)    ||\
+                                      ((__RREMAP__) == TIM_TIM23_ETR_COMP2)    ||\
+                                      ((__RREMAP__) == TIM_TIM24_ETR_GPIO)     ||\
+                                      ((__RREMAP__) == TIM_TIM24_ETR_SAI4_FSA) ||\
+                                      ((__RREMAP__) == TIM_TIM24_ETR_SAI4_FSB) ||\
+                                      ((__RREMAP__) == TIM_TIM24_ETR_SAI1_FSA) ||\
+                                      ((__RREMAP__) == TIM_TIM24_ETR_SAI1_FSB))
+
+/**
+  * @}
+  */
+/* End of private macro ------------------------------------------------------*/
+
+/* Exported functions --------------------------------------------------------*/
+/** @addtogroup TIMEx_Exported_Functions TIM Extended Exported Functions
+  * @{
+  */
+
+/** @addtogroup TIMEx_Exported_Functions_Group1 Extended Timer Hall Sensor functions
+  *  @brief    Timer Hall Sensor functions
+  * @{
+  */
+/*  Timer Hall Sensor functions  **********************************************/
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Init(TIM_HandleTypeDef *htim, const TIM_HallSensor_InitTypeDef *sConfig);
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_DeInit(TIM_HandleTypeDef *htim);
+
+void HAL_TIMEx_HallSensor_MspInit(TIM_HandleTypeDef *htim);
+void HAL_TIMEx_HallSensor_MspDeInit(TIM_HandleTypeDef *htim);
+
+/* Blocking mode: Polling */
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Start(TIM_HandleTypeDef *htim);
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Stop(TIM_HandleTypeDef *htim);
+/* Non-Blocking mode: Interrupt */
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Start_IT(TIM_HandleTypeDef *htim);
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Stop_IT(TIM_HandleTypeDef *htim);
+/* Non-Blocking mode: DMA */
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Start_DMA(TIM_HandleTypeDef *htim, uint32_t *pData, uint16_t Length);
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Stop_DMA(TIM_HandleTypeDef *htim);
+/**
+  * @}
+  */
+
+/** @addtogroup TIMEx_Exported_Functions_Group2 Extended Timer Complementary Output Compare functions
+  *  @brief   Timer Complementary Output Compare functions
+  * @{
+  */
+/*  Timer Complementary Output Compare functions  *****************************/
+/* Blocking mode: Polling */
+HAL_StatusTypeDef HAL_TIMEx_OCN_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
+HAL_StatusTypeDef HAL_TIMEx_OCN_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
+
+/* Non-Blocking mode: Interrupt */
+HAL_StatusTypeDef HAL_TIMEx_OCN_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
+HAL_StatusTypeDef HAL_TIMEx_OCN_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
+
+/* Non-Blocking mode: DMA */
+HAL_StatusTypeDef HAL_TIMEx_OCN_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, const uint32_t *pData,
+                                          uint16_t Length);
+HAL_StatusTypeDef HAL_TIMEx_OCN_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);
+/**
+  * @}
+  */
+
+/** @addtogroup TIMEx_Exported_Functions_Group3 Extended Timer Complementary PWM functions
+  *  @brief    Timer Complementary PWM functions
+  * @{
+  */
+/*  Timer Complementary PWM functions  ****************************************/
+/* Blocking mode: Polling */
+HAL_StatusTypeDef HAL_TIMEx_PWMN_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
+HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
+
+/* Non-Blocking mode: Interrupt */
+HAL_StatusTypeDef HAL_TIMEx_PWMN_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
+HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
+/* Non-Blocking mode: DMA */
+HAL_StatusTypeDef HAL_TIMEx_PWMN_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, const uint32_t *pData,
+                                           uint16_t Length);
+HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);
+/**
+  * @}
+  */
+
+/** @addtogroup TIMEx_Exported_Functions_Group4 Extended Timer Complementary One Pulse functions
+  *  @brief    Timer Complementary One Pulse functions
+  * @{
+  */
+/*  Timer Complementary One Pulse functions  **********************************/
+/* Blocking mode: Polling */
+HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Start(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
+HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Stop(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
+
+/* Non-Blocking mode: Interrupt */
+HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Start_IT(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
+HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Stop_IT(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
+/**
+  * @}
+  */
+
+/** @addtogroup TIMEx_Exported_Functions_Group5 Extended Peripheral Control functions
+  *  @brief    Peripheral Control functions
+  * @{
+  */
+/* Extended Control functions  ************************************************/
+HAL_StatusTypeDef HAL_TIMEx_ConfigCommutEvent(TIM_HandleTypeDef *htim, uint32_t  InputTrigger,
+                                              uint32_t  CommutationSource);
+HAL_StatusTypeDef HAL_TIMEx_ConfigCommutEvent_IT(TIM_HandleTypeDef *htim, uint32_t  InputTrigger,
+                                                 uint32_t  CommutationSource);
+HAL_StatusTypeDef HAL_TIMEx_ConfigCommutEvent_DMA(TIM_HandleTypeDef *htim, uint32_t  InputTrigger,
+                                                  uint32_t  CommutationSource);
+HAL_StatusTypeDef HAL_TIMEx_MasterConfigSynchronization(TIM_HandleTypeDef *htim,
+                                                        const TIM_MasterConfigTypeDef *sMasterConfig);
+HAL_StatusTypeDef HAL_TIMEx_ConfigBreakDeadTime(TIM_HandleTypeDef *htim,
+                                                const TIM_BreakDeadTimeConfigTypeDef *sBreakDeadTimeConfig);
+#if defined(TIM_BREAK_INPUT_SUPPORT)
+HAL_StatusTypeDef HAL_TIMEx_ConfigBreakInput(TIM_HandleTypeDef *htim, uint32_t BreakInput,
+                                             const TIMEx_BreakInputConfigTypeDef *sBreakInputConfig);
+#endif /* TIM_BREAK_INPUT_SUPPORT */
+HAL_StatusTypeDef HAL_TIMEx_GroupChannel5(TIM_HandleTypeDef *htim, uint32_t Channels);
+HAL_StatusTypeDef HAL_TIMEx_RemapConfig(TIM_HandleTypeDef *htim, uint32_t Remap);
+HAL_StatusTypeDef  HAL_TIMEx_TISelection(TIM_HandleTypeDef *htim, uint32_t TISelection, uint32_t Channel);
+#if defined(TIM_BDTR_BKBID)
+
+HAL_StatusTypeDef HAL_TIMEx_DisarmBreakInput(TIM_HandleTypeDef *htim, uint32_t BreakInput);
+HAL_StatusTypeDef HAL_TIMEx_ReArmBreakInput(const TIM_HandleTypeDef *htim, uint32_t BreakInput);
+#endif /* TIM_BDTR_BKBID */
+/**
+  * @}
+  */
+
+/** @addtogroup TIMEx_Exported_Functions_Group6 Extended Callbacks functions
+  * @brief    Extended Callbacks functions
+  * @{
+  */
+/* Extended Callback **********************************************************/
+void HAL_TIMEx_CommutCallback(TIM_HandleTypeDef *htim);
+void HAL_TIMEx_CommutHalfCpltCallback(TIM_HandleTypeDef *htim);
+void HAL_TIMEx_BreakCallback(TIM_HandleTypeDef *htim);
+void HAL_TIMEx_Break2Callback(TIM_HandleTypeDef *htim);
+/**
+  * @}
+  */
+
+/** @addtogroup TIMEx_Exported_Functions_Group7 Extended Peripheral State functions
+  * @brief    Extended Peripheral State functions
+  * @{
+  */
+/* Extended Peripheral State functions  ***************************************/
+HAL_TIM_StateTypeDef HAL_TIMEx_HallSensor_GetState(const TIM_HandleTypeDef *htim);
+HAL_TIM_ChannelStateTypeDef HAL_TIMEx_GetChannelNState(const TIM_HandleTypeDef *htim,  uint32_t ChannelN);
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+/* End of exported functions -------------------------------------------------*/
+
+/* Private functions----------------------------------------------------------*/
+/** @addtogroup TIMEx_Private_Functions TIM Extended Private Functions
+  * @{
+  */
+void TIMEx_DMACommutationCplt(DMA_HandleTypeDef *hdma);
+void TIMEx_DMACommutationHalfCplt(DMA_HandleTypeDef *hdma);
+/**
+  * @}
+  */
+/* End of private functions --------------------------------------------------*/
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* STM32H7xx_HAL_TIM_EX_H */
Index: ctrl/firmware/Main/CubeMX/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim.c	(revision 54)
@@ -0,0 +1,7922 @@
+/**
+  ******************************************************************************
+  * @file    stm32h7xx_hal_tim.c
+  * @author  MCD Application Team
+  * @brief   TIM HAL module driver.
+  *          This file provides firmware functions to manage the following
+  *          functionalities of the Timer (TIM) peripheral:
+  *           + TIM Time Base Initialization
+  *           + TIM Time Base Start
+  *           + TIM Time Base Start Interruption
+  *           + TIM Time Base Start DMA
+  *           + TIM Output Compare/PWM Initialization
+  *           + TIM Output Compare/PWM Channel Configuration
+  *           + TIM Output Compare/PWM  Start
+  *           + TIM Output Compare/PWM  Start Interruption
+  *           + TIM Output Compare/PWM Start DMA
+  *           + TIM Input Capture Initialization
+  *           + TIM Input Capture Channel Configuration
+  *           + TIM Input Capture Start
+  *           + TIM Input Capture Start Interruption
+  *           + TIM Input Capture Start DMA
+  *           + TIM One Pulse Initialization
+  *           + TIM One Pulse Channel Configuration
+  *           + TIM One Pulse Start
+  *           + TIM Encoder Interface Initialization
+  *           + TIM Encoder Interface Start
+  *           + TIM Encoder Interface Start Interruption
+  *           + TIM Encoder Interface Start DMA
+  *           + Commutation Event configuration with Interruption and DMA
+  *           + TIM OCRef clear configuration
+  *           + TIM External Clock configuration
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2017 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.
+  *
+  ******************************************************************************
+  @verbatim
+  ==============================================================================
+                      ##### TIMER Generic features #####
+  ==============================================================================
+  [..] The Timer features include:
+       (#) 16-bit up, down, up/down auto-reload counter.
+       (#) 16-bit programmable prescaler allowing dividing (also on the fly) the
+           counter clock frequency either by any factor between 1 and 65536.
+       (#) Up to 4 independent channels for:
+           (++) Input Capture
+           (++) Output Compare
+           (++) PWM generation (Edge and Center-aligned Mode)
+           (++) One-pulse mode output
+       (#) Synchronization circuit to control the timer with external signals and to interconnect
+            several timers together.
+       (#) Supports incremental encoder for positioning purposes
+
+            ##### How to use this driver #####
+  ==============================================================================
+    [..]
+     (#) Initialize the TIM low level resources by implementing the following functions
+         depending on the selected feature:
+           (++) Time Base : HAL_TIM_Base_MspInit()
+           (++) Input Capture : HAL_TIM_IC_MspInit()
+           (++) Output Compare : HAL_TIM_OC_MspInit()
+           (++) PWM generation : HAL_TIM_PWM_MspInit()
+           (++) One-pulse mode output : HAL_TIM_OnePulse_MspInit()
+           (++) Encoder mode output : HAL_TIM_Encoder_MspInit()
+
+     (#) Initialize the TIM low level resources :
+        (##) Enable the TIM interface clock using __HAL_RCC_TIMx_CLK_ENABLE();
+        (##) TIM pins configuration
+            (+++) Enable the clock for the TIM GPIOs using the following function:
+             __HAL_RCC_GPIOx_CLK_ENABLE();
+            (+++) Configure these TIM pins in Alternate function mode using HAL_GPIO_Init();
+
+     (#) The external Clock can be configured, if needed (the default clock is the
+         internal clock from the APBx), using the following function:
+         HAL_TIM_ConfigClockSource, the clock configuration should be done before
+         any start function.
+
+     (#) Configure the TIM in the desired functioning mode using one of the
+       Initialization function of this driver:
+       (++) HAL_TIM_Base_Init: to use the Timer to generate a simple time base
+       (++) HAL_TIM_OC_Init and HAL_TIM_OC_ConfigChannel: to use the Timer to generate an
+            Output Compare signal.
+       (++) HAL_TIM_PWM_Init and HAL_TIM_PWM_ConfigChannel: to use the Timer to generate a
+            PWM signal.
+       (++) HAL_TIM_IC_Init and HAL_TIM_IC_ConfigChannel: to use the Timer to measure an
+            external signal.
+       (++) HAL_TIM_OnePulse_Init and HAL_TIM_OnePulse_ConfigChannel: to use the Timer
+            in One Pulse Mode.
+       (++) HAL_TIM_Encoder_Init: to use the Timer Encoder Interface.
+
+     (#) Activate the TIM peripheral using one of the start functions depending from the feature used:
+           (++) Time Base : HAL_TIM_Base_Start(), HAL_TIM_Base_Start_DMA(), HAL_TIM_Base_Start_IT()
+           (++) Input Capture :  HAL_TIM_IC_Start(), HAL_TIM_IC_Start_DMA(), HAL_TIM_IC_Start_IT()
+           (++) Output Compare : HAL_TIM_OC_Start(), HAL_TIM_OC_Start_DMA(), HAL_TIM_OC_Start_IT()
+           (++) PWM generation : HAL_TIM_PWM_Start(), HAL_TIM_PWM_Start_DMA(), HAL_TIM_PWM_Start_IT()
+           (++) One-pulse mode output : HAL_TIM_OnePulse_Start(), HAL_TIM_OnePulse_Start_IT()
+           (++) Encoder mode output : HAL_TIM_Encoder_Start(), HAL_TIM_Encoder_Start_DMA(), HAL_TIM_Encoder_Start_IT().
+
+     (#) The DMA Burst is managed with the two following functions:
+         HAL_TIM_DMABurst_WriteStart()
+         HAL_TIM_DMABurst_ReadStart()
+
+    *** Callback registration ***
+  =============================================
+
+  [..]
+  The compilation define  USE_HAL_TIM_REGISTER_CALLBACKS when set to 1
+  allows the user to configure dynamically the driver callbacks.
+
+  [..]
+  Use Function HAL_TIM_RegisterCallback() to register a callback.
+  HAL_TIM_RegisterCallback() takes as parameters the HAL peripheral handle,
+  the Callback ID and a pointer to the user callback function.
+
+  [..]
+  Use function HAL_TIM_UnRegisterCallback() to reset a callback to the default
+  weak function.
+  HAL_TIM_UnRegisterCallback takes as parameters the HAL peripheral handle,
+  and the Callback ID.
+
+  [..]
+  These functions allow to register/unregister following callbacks:
+    (+) Base_MspInitCallback              : TIM Base Msp Init Callback.
+    (+) Base_MspDeInitCallback            : TIM Base Msp DeInit Callback.
+    (+) IC_MspInitCallback                : TIM IC Msp Init Callback.
+    (+) IC_MspDeInitCallback              : TIM IC Msp DeInit Callback.
+    (+) OC_MspInitCallback                : TIM OC Msp Init Callback.
+    (+) OC_MspDeInitCallback              : TIM OC Msp DeInit Callback.
+    (+) PWM_MspInitCallback               : TIM PWM Msp Init Callback.
+    (+) PWM_MspDeInitCallback             : TIM PWM Msp DeInit Callback.
+    (+) OnePulse_MspInitCallback          : TIM One Pulse Msp Init Callback.
+    (+) OnePulse_MspDeInitCallback        : TIM One Pulse Msp DeInit Callback.
+    (+) Encoder_MspInitCallback           : TIM Encoder Msp Init Callback.
+    (+) Encoder_MspDeInitCallback         : TIM Encoder Msp DeInit Callback.
+    (+) HallSensor_MspInitCallback        : TIM Hall Sensor Msp Init Callback.
+    (+) HallSensor_MspDeInitCallback      : TIM Hall Sensor Msp DeInit Callback.
+    (+) PeriodElapsedCallback             : TIM Period Elapsed Callback.
+    (+) PeriodElapsedHalfCpltCallback     : TIM Period Elapsed half complete Callback.
+    (+) TriggerCallback                   : TIM Trigger Callback.
+    (+) TriggerHalfCpltCallback           : TIM Trigger half complete Callback.
+    (+) IC_CaptureCallback                : TIM Input Capture Callback.
+    (+) IC_CaptureHalfCpltCallback        : TIM Input Capture half complete Callback.
+    (+) OC_DelayElapsedCallback           : TIM Output Compare Delay Elapsed Callback.
+    (+) PWM_PulseFinishedCallback         : TIM PWM Pulse Finished Callback.
+    (+) PWM_PulseFinishedHalfCpltCallback : TIM PWM Pulse Finished half complete Callback.
+    (+) ErrorCallback                     : TIM Error Callback.
+    (+) CommutationCallback               : TIM Commutation Callback.
+    (+) CommutationHalfCpltCallback       : TIM Commutation half complete Callback.
+    (+) BreakCallback                     : TIM Break Callback.
+    (+) Break2Callback                    : TIM Break2 Callback.
+
+  [..]
+By default, after the Init and when the state is HAL_TIM_STATE_RESET
+all interrupt callbacks are set to the corresponding weak functions:
+  examples HAL_TIM_TriggerCallback(), HAL_TIM_ErrorCallback().
+
+  [..]
+  Exception done for MspInit and MspDeInit functions that are reset to the legacy weak
+  functionalities in the Init / DeInit only when these callbacks are null
+  (not registered beforehand). If not, MspInit or MspDeInit are not null, the Init / DeInit
+    keep and use the user MspInit / MspDeInit callbacks(registered beforehand)
+
+  [..]
+    Callbacks can be registered / unregistered in HAL_TIM_STATE_READY state only.
+    Exception done MspInit / MspDeInit that can be registered / unregistered
+    in HAL_TIM_STATE_READY or HAL_TIM_STATE_RESET state,
+    thus registered(user) MspInit / DeInit callbacks can be used during the Init / DeInit.
+  In that case first register the MspInit/MspDeInit user callbacks
+      using HAL_TIM_RegisterCallback() before calling DeInit or Init function.
+
+  [..]
+      When The compilation define USE_HAL_TIM_REGISTER_CALLBACKS is set to 0 or
+      not defined, the callback registration feature is not available and all callbacks
+      are set to the corresponding weak functions.
+
+  @endverbatim
+  ******************************************************************************
+  */
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32h7xx_hal.h"
+
+/** @addtogroup STM32H7xx_HAL_Driver
+  * @{
+  */
+
+/** @defgroup TIM TIM
+  * @brief TIM HAL module driver
+  * @{
+  */
+
+#ifdef HAL_TIM_MODULE_ENABLED
+
+/* Private typedef -----------------------------------------------------------*/
+/* Private define ------------------------------------------------------------*/
+/* Private macros ------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+/** @addtogroup TIM_Private_Functions
+  * @{
+  */
+static void TIM_OC1_SetConfig(TIM_TypeDef *TIMx, const TIM_OC_InitTypeDef *OC_Config);
+static void TIM_OC3_SetConfig(TIM_TypeDef *TIMx, const TIM_OC_InitTypeDef *OC_Config);
+static void TIM_OC4_SetConfig(TIM_TypeDef *TIMx, const TIM_OC_InitTypeDef *OC_Config);
+static void TIM_OC5_SetConfig(TIM_TypeDef *TIMx, const TIM_OC_InitTypeDef *OC_Config);
+static void TIM_OC6_SetConfig(TIM_TypeDef *TIMx, const TIM_OC_InitTypeDef *OC_Config);
+static void TIM_TI1_ConfigInputStage(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICFilter);
+static void TIM_TI2_SetConfig(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICSelection,
+                              uint32_t TIM_ICFilter);
+static void TIM_TI2_ConfigInputStage(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICFilter);
+static void TIM_TI3_SetConfig(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICSelection,
+                              uint32_t TIM_ICFilter);
+static void TIM_TI4_SetConfig(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICSelection,
+                              uint32_t TIM_ICFilter);
+static void TIM_ITRx_SetConfig(TIM_TypeDef *TIMx, uint32_t InputTriggerSource);
+static void TIM_DMAPeriodElapsedCplt(DMA_HandleTypeDef *hdma);
+static void TIM_DMAPeriodElapsedHalfCplt(DMA_HandleTypeDef *hdma);
+static void TIM_DMADelayPulseCplt(DMA_HandleTypeDef *hdma);
+static void TIM_DMATriggerCplt(DMA_HandleTypeDef *hdma);
+static void TIM_DMATriggerHalfCplt(DMA_HandleTypeDef *hdma);
+static HAL_StatusTypeDef TIM_SlaveTimer_SetConfig(TIM_HandleTypeDef *htim,
+                                                  const TIM_SlaveConfigTypeDef *sSlaveConfig);
+/**
+  * @}
+  */
+/* Exported functions --------------------------------------------------------*/
+
+/** @defgroup TIM_Exported_Functions TIM Exported Functions
+  * @{
+  */
+
+/** @defgroup TIM_Exported_Functions_Group1 TIM Time Base functions
+  *  @brief    Time Base functions
+  *
+@verbatim
+  ==============================================================================
+              ##### Time Base functions #####
+  ==============================================================================
+  [..]
+    This section provides functions allowing to:
+    (+) Initialize and configure the TIM base.
+    (+) De-initialize the TIM base.
+    (+) Start the Time Base.
+    (+) Stop the Time Base.
+    (+) Start the Time Base and enable interrupt.
+    (+) Stop the Time Base and disable interrupt.
+    (+) Start the Time Base and enable DMA transfer.
+    (+) Stop the Time Base and disable DMA transfer.
+
+@endverbatim
+  * @{
+  */
+/**
+  * @brief  Initializes the TIM Time base Unit according to the specified
+  *         parameters in the TIM_HandleTypeDef and initialize the associated handle.
+  * @note   Switching from Center Aligned counter mode to Edge counter mode (or reverse)
+  *         requires a timer reset to avoid unexpected direction
+  *         due to DIR bit readonly in center aligned mode.
+  *         Ex: call @ref HAL_TIM_Base_DeInit() before HAL_TIM_Base_Init()
+  * @param  htim TIM Base handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim)
+{
+  /* Check the TIM handle allocation */
+  if (htim == NULL)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
+  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
+  assert_param(IS_TIM_PERIOD(htim, htim->Init.Period));
+  assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));
+
+  if (htim->State == HAL_TIM_STATE_RESET)
+  {
+    /* Allocate lock resource and initialize it */
+    htim->Lock = HAL_UNLOCKED;
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+    /* Reset interrupt callbacks to legacy weak callbacks */
+    TIM_ResetCallback(htim);
+
+    if (htim->Base_MspInitCallback == NULL)
+    {
+      htim->Base_MspInitCallback = HAL_TIM_Base_MspInit;
+    }
+    /* Init the low level hardware : GPIO, CLOCK, NVIC */
+    htim->Base_MspInitCallback(htim);
+#else
+    /* Init the low level hardware : GPIO, CLOCK, NVIC */
+    HAL_TIM_Base_MspInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+  }
+
+  /* Set the TIM state */
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Set the Time Base configuration */
+  TIM_Base_SetConfig(htim->Instance, &htim->Init);
+
+  /* Initialize the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_READY;
+
+  /* Initialize the TIM channels state */
+  TIM_CHANNEL_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Initialize the TIM state*/
+  htim->State = HAL_TIM_STATE_READY;
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  DeInitializes the TIM Base peripheral
+  * @param  htim TIM Base handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Base_DeInit(TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Disable the TIM Peripheral Clock */
+  __HAL_TIM_DISABLE(htim);
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  if (htim->Base_MspDeInitCallback == NULL)
+  {
+    htim->Base_MspDeInitCallback = HAL_TIM_Base_MspDeInit;
+  }
+  /* DeInit the low level hardware */
+  htim->Base_MspDeInitCallback(htim);
+#else
+  /* DeInit the low level hardware: GPIO, CLOCK, NVIC */
+  HAL_TIM_Base_MspDeInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  /* Change the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_RESET;
+
+  /* Change the TIM channels state */
+  TIM_CHANNEL_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_RESET);
+  TIM_CHANNEL_N_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_RESET);
+
+  /* Change TIM state */
+  htim->State = HAL_TIM_STATE_RESET;
+
+  /* Release Lock */
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Initializes the TIM Base MSP.
+  * @param  htim TIM Base handle
+  * @retval None
+  */
+__weak void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_Base_MspInit could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  DeInitializes TIM Base MSP.
+  * @param  htim TIM Base handle
+  * @retval None
+  */
+__weak void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_Base_MspDeInit could be implemented in the user file
+   */
+}
+
+
+/**
+  * @brief  Starts the TIM Base generation.
+  * @param  htim TIM Base handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim)
+{
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+
+  /* Check the TIM state */
+  if (htim->State != HAL_TIM_STATE_READY)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM state */
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+  if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+  {
+    tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+    if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+  else
+  {
+    __HAL_TIM_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM Base generation.
+  * @param  htim TIM Base handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM state */
+  htim->State = HAL_TIM_STATE_READY;
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Starts the TIM Base generation in interrupt mode.
+  * @param  htim TIM Base handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)
+{
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+
+  /* Check the TIM state */
+  if (htim->State != HAL_TIM_STATE_READY)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM state */
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Enable the TIM Update interrupt */
+  __HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);
+
+  /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+  if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+  {
+    tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+    if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+  else
+  {
+    __HAL_TIM_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM Base generation in interrupt mode.
+  * @param  htim TIM Base handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+
+  /* Disable the TIM Update interrupt */
+  __HAL_TIM_DISABLE_IT(htim, TIM_IT_UPDATE);
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM state */
+  htim->State = HAL_TIM_STATE_READY;
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Starts the TIM Base generation in DMA mode.
+  * @param  htim TIM Base handle
+  * @param  pData The source Buffer address.
+  * @param  Length The length of data to be transferred from memory to peripheral.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Base_Start_DMA(TIM_HandleTypeDef *htim, const uint32_t *pData, uint16_t Length)
+{
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_DMA_INSTANCE(htim->Instance));
+
+  /* Set the TIM state */
+  if (htim->State == HAL_TIM_STATE_BUSY)
+  {
+    return HAL_BUSY;
+  }
+  else if (htim->State == HAL_TIM_STATE_READY)
+  {
+    if ((pData == NULL) || (Length == 0U))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      htim->State = HAL_TIM_STATE_BUSY;
+    }
+  }
+  else
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the DMA Period elapsed callbacks */
+  htim->hdma[TIM_DMA_ID_UPDATE]->XferCpltCallback = TIM_DMAPeriodElapsedCplt;
+  htim->hdma[TIM_DMA_ID_UPDATE]->XferHalfCpltCallback = TIM_DMAPeriodElapsedHalfCplt;
+
+  /* Set the DMA error callback */
+  htim->hdma[TIM_DMA_ID_UPDATE]->XferErrorCallback = TIM_DMAError ;
+
+  /* Enable the DMA stream */
+  if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_UPDATE], (uint32_t)pData, (uint32_t)&htim->Instance->ARR,
+                       Length) != HAL_OK)
+  {
+    /* Return error status */
+    return HAL_ERROR;
+  }
+
+  /* Enable the TIM Update DMA request */
+  __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_UPDATE);
+
+  /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+  if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+  {
+    tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+    if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+  else
+  {
+    __HAL_TIM_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM Base generation in DMA mode.
+  * @param  htim TIM Base handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Base_Stop_DMA(TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_DMA_INSTANCE(htim->Instance));
+
+  /* Disable the TIM Update DMA request */
+  __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_UPDATE);
+
+  (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_UPDATE]);
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM state */
+  htim->State = HAL_TIM_STATE_READY;
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Exported_Functions_Group2 TIM Output Compare functions
+  *  @brief    TIM Output Compare functions
+  *
+@verbatim
+  ==============================================================================
+                  ##### TIM Output Compare functions #####
+  ==============================================================================
+  [..]
+    This section provides functions allowing to:
+    (+) Initialize and configure the TIM Output Compare.
+    (+) De-initialize the TIM Output Compare.
+    (+) Start the TIM Output Compare.
+    (+) Stop the TIM Output Compare.
+    (+) Start the TIM Output Compare and enable interrupt.
+    (+) Stop the TIM Output Compare and disable interrupt.
+    (+) Start the TIM Output Compare and enable DMA transfer.
+    (+) Stop the TIM Output Compare and disable DMA transfer.
+
+@endverbatim
+  * @{
+  */
+/**
+  * @brief  Initializes the TIM Output Compare according to the specified
+  *         parameters in the TIM_HandleTypeDef and initializes the associated handle.
+  * @note   Switching from Center Aligned counter mode to Edge counter mode (or reverse)
+  *         requires a timer reset to avoid unexpected direction
+  *         due to DIR bit readonly in center aligned mode.
+  *         Ex: call @ref HAL_TIM_OC_DeInit() before HAL_TIM_OC_Init()
+  * @param  htim TIM Output Compare handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OC_Init(TIM_HandleTypeDef *htim)
+{
+  /* Check the TIM handle allocation */
+  if (htim == NULL)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
+  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
+  assert_param(IS_TIM_PERIOD(htim, htim->Init.Period));
+  assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));
+
+  if (htim->State == HAL_TIM_STATE_RESET)
+  {
+    /* Allocate lock resource and initialize it */
+    htim->Lock = HAL_UNLOCKED;
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+    /* Reset interrupt callbacks to legacy weak callbacks */
+    TIM_ResetCallback(htim);
+
+    if (htim->OC_MspInitCallback == NULL)
+    {
+      htim->OC_MspInitCallback = HAL_TIM_OC_MspInit;
+    }
+    /* Init the low level hardware : GPIO, CLOCK, NVIC */
+    htim->OC_MspInitCallback(htim);
+#else
+    /* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
+    HAL_TIM_OC_MspInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+  }
+
+  /* Set the TIM state */
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Init the base time for the Output Compare */
+  TIM_Base_SetConfig(htim->Instance,  &htim->Init);
+
+  /* Initialize the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_READY;
+
+  /* Initialize the TIM channels state */
+  TIM_CHANNEL_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Initialize the TIM state*/
+  htim->State = HAL_TIM_STATE_READY;
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  DeInitializes the TIM peripheral
+  * @param  htim TIM Output Compare handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OC_DeInit(TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Disable the TIM Peripheral Clock */
+  __HAL_TIM_DISABLE(htim);
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  if (htim->OC_MspDeInitCallback == NULL)
+  {
+    htim->OC_MspDeInitCallback = HAL_TIM_OC_MspDeInit;
+  }
+  /* DeInit the low level hardware */
+  htim->OC_MspDeInitCallback(htim);
+#else
+  /* DeInit the low level hardware: GPIO, CLOCK, NVIC and DMA */
+  HAL_TIM_OC_MspDeInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  /* Change the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_RESET;
+
+  /* Change the TIM channels state */
+  TIM_CHANNEL_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_RESET);
+  TIM_CHANNEL_N_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_RESET);
+
+  /* Change TIM state */
+  htim->State = HAL_TIM_STATE_RESET;
+
+  /* Release Lock */
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Initializes the TIM Output Compare MSP.
+  * @param  htim TIM Output Compare handle
+  * @retval None
+  */
+__weak void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_OC_MspInit could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  DeInitializes TIM Output Compare MSP.
+  * @param  htim TIM Output Compare handle
+  * @retval None
+  */
+__weak void HAL_TIM_OC_MspDeInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_OC_MspDeInit could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Starts the TIM Output Compare signal generation.
+  * @param  htim TIM Output Compare handle
+  * @param  Channel TIM Channel to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
+  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OC_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));
+
+  /* Check the TIM channel state */
+  if (TIM_CHANNEL_STATE_GET(htim, Channel) != HAL_TIM_CHANNEL_STATE_READY)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM channel state */
+  TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  /* Enable the Output compare channel */
+  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
+
+  if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+  {
+    /* Enable the main output */
+    __HAL_TIM_MOE_ENABLE(htim);
+  }
+
+  /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+  if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+  {
+    tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+    if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+  else
+  {
+    __HAL_TIM_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM Output Compare signal generation.
+  * @param  htim TIM Output Compare handle
+  * @param  Channel TIM Channel to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
+  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OC_Stop(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));
+
+  /* Disable the Output compare channel */
+  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_DISABLE);
+
+  if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+  {
+    /* Disable the Main Output */
+    __HAL_TIM_MOE_DISABLE(htim);
+  }
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM channel state */
+  TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Starts the TIM Output Compare signal generation in interrupt mode.
+  * @param  htim TIM Output Compare handle
+  * @param  Channel TIM Channel to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OC_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+
+  /* Check the TIM channel state */
+  if (TIM_CHANNEL_STATE_GET(htim, Channel) != HAL_TIM_CHANNEL_STATE_READY)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM channel state */
+  TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Enable the TIM Capture/Compare 1 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Enable the TIM Capture/Compare 2 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Enable the TIM Capture/Compare 3 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC3);
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Enable the TIM Capture/Compare 4 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC4);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Enable the Output compare channel */
+    TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
+
+    if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+    {
+      /* Enable the main output */
+      __HAL_TIM_MOE_ENABLE(htim);
+    }
+
+    /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+    if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+    {
+      tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+      if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+      {
+        __HAL_TIM_ENABLE(htim);
+      }
+    }
+    else
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Stops the TIM Output Compare signal generation in interrupt mode.
+  * @param  htim TIM Output Compare handle
+  * @param  Channel TIM Channel to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OC_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Disable the TIM Capture/Compare 1 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Disable the TIM Capture/Compare 2 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Disable the TIM Capture/Compare 3 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC3);
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Disable the TIM Capture/Compare 4 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC4);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Disable the Output compare channel */
+    TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_DISABLE);
+
+    if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+    {
+      /* Disable the Main Output */
+      __HAL_TIM_MOE_DISABLE(htim);
+    }
+
+    /* Disable the Peripheral */
+    __HAL_TIM_DISABLE(htim);
+
+    /* Set the TIM channel state */
+    TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Starts the TIM Output Compare signal generation in DMA mode.
+  * @param  htim TIM Output Compare handle
+  * @param  Channel TIM Channel to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @param  pData The source Buffer address.
+  * @param  Length The length of data to be transferred from memory to TIM peripheral
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OC_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, const uint32_t *pData,
+                                       uint16_t Length)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+
+  /* Set the TIM channel state */
+  if (TIM_CHANNEL_STATE_GET(htim, Channel) == HAL_TIM_CHANNEL_STATE_BUSY)
+  {
+    return HAL_BUSY;
+  }
+  else if (TIM_CHANNEL_STATE_GET(htim, Channel) == HAL_TIM_CHANNEL_STATE_READY)
+  {
+    if ((pData == NULL) || (Length == 0U))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+    }
+  }
+  else
+  {
+    return HAL_ERROR;
+  }
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC1]->XferCpltCallback = TIM_DMADelayPulseCplt;
+      htim->hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC1]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)pData, (uint32_t)&htim->Instance->CCR1,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+
+      /* Enable the TIM Capture/Compare 1 DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC2]->XferCpltCallback = TIM_DMADelayPulseCplt;
+      htim->hdma[TIM_DMA_ID_CC2]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC2]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC2], (uint32_t)pData, (uint32_t)&htim->Instance->CCR2,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+
+      /* Enable the TIM Capture/Compare 2 DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC3]->XferCpltCallback = TIM_DMADelayPulseCplt;
+      htim->hdma[TIM_DMA_ID_CC3]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC3]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC3], (uint32_t)pData, (uint32_t)&htim->Instance->CCR3,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Capture/Compare 3 DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC3);
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC4]->XferCpltCallback = TIM_DMADelayPulseCplt;
+      htim->hdma[TIM_DMA_ID_CC4]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC4]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC4], (uint32_t)pData, (uint32_t)&htim->Instance->CCR4,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Capture/Compare 4 DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC4);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Enable the Output compare channel */
+    TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
+
+    if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+    {
+      /* Enable the main output */
+      __HAL_TIM_MOE_ENABLE(htim);
+    }
+
+    /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+    if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+    {
+      tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+      if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+      {
+        __HAL_TIM_ENABLE(htim);
+      }
+    }
+    else
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Stops the TIM Output Compare signal generation in DMA mode.
+  * @param  htim TIM Output Compare handle
+  * @param  Channel TIM Channel to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OC_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Disable the TIM Capture/Compare 1 DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC1);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC1]);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Disable the TIM Capture/Compare 2 DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC2);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC2]);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Disable the TIM Capture/Compare 3 DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC3);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC3]);
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Disable the TIM Capture/Compare 4 interrupt */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC4);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC4]);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Disable the Output compare channel */
+    TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_DISABLE);
+
+    if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+    {
+      /* Disable the Main Output */
+      __HAL_TIM_MOE_DISABLE(htim);
+    }
+
+    /* Disable the Peripheral */
+    __HAL_TIM_DISABLE(htim);
+
+    /* Set the TIM channel state */
+    TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Exported_Functions_Group3 TIM PWM functions
+  *  @brief    TIM PWM functions
+  *
+@verbatim
+  ==============================================================================
+                          ##### TIM PWM functions #####
+  ==============================================================================
+  [..]
+    This section provides functions allowing to:
+    (+) Initialize and configure the TIM PWM.
+    (+) De-initialize the TIM PWM.
+    (+) Start the TIM PWM.
+    (+) Stop the TIM PWM.
+    (+) Start the TIM PWM and enable interrupt.
+    (+) Stop the TIM PWM and disable interrupt.
+    (+) Start the TIM PWM and enable DMA transfer.
+    (+) Stop the TIM PWM and disable DMA transfer.
+
+@endverbatim
+  * @{
+  */
+/**
+  * @brief  Initializes the TIM PWM Time Base according to the specified
+  *         parameters in the TIM_HandleTypeDef and initializes the associated handle.
+  * @note   Switching from Center Aligned counter mode to Edge counter mode (or reverse)
+  *         requires a timer reset to avoid unexpected direction
+  *         due to DIR bit readonly in center aligned mode.
+  *         Ex: call @ref HAL_TIM_PWM_DeInit() before HAL_TIM_PWM_Init()
+  * @param  htim TIM PWM handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim)
+{
+  /* Check the TIM handle allocation */
+  if (htim == NULL)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
+  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
+  assert_param(IS_TIM_PERIOD(htim, htim->Init.Period));
+  assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));
+
+  if (htim->State == HAL_TIM_STATE_RESET)
+  {
+    /* Allocate lock resource and initialize it */
+    htim->Lock = HAL_UNLOCKED;
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+    /* Reset interrupt callbacks to legacy weak callbacks */
+    TIM_ResetCallback(htim);
+
+    if (htim->PWM_MspInitCallback == NULL)
+    {
+      htim->PWM_MspInitCallback = HAL_TIM_PWM_MspInit;
+    }
+    /* Init the low level hardware : GPIO, CLOCK, NVIC */
+    htim->PWM_MspInitCallback(htim);
+#else
+    /* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
+    HAL_TIM_PWM_MspInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+  }
+
+  /* Set the TIM state */
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Init the base time for the PWM */
+  TIM_Base_SetConfig(htim->Instance, &htim->Init);
+
+  /* Initialize the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_READY;
+
+  /* Initialize the TIM channels state */
+  TIM_CHANNEL_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Initialize the TIM state*/
+  htim->State = HAL_TIM_STATE_READY;
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  DeInitializes the TIM peripheral
+  * @param  htim TIM PWM handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_PWM_DeInit(TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Disable the TIM Peripheral Clock */
+  __HAL_TIM_DISABLE(htim);
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  if (htim->PWM_MspDeInitCallback == NULL)
+  {
+    htim->PWM_MspDeInitCallback = HAL_TIM_PWM_MspDeInit;
+  }
+  /* DeInit the low level hardware */
+  htim->PWM_MspDeInitCallback(htim);
+#else
+  /* DeInit the low level hardware: GPIO, CLOCK, NVIC and DMA */
+  HAL_TIM_PWM_MspDeInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  /* Change the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_RESET;
+
+  /* Change the TIM channels state */
+  TIM_CHANNEL_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_RESET);
+  TIM_CHANNEL_N_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_RESET);
+
+  /* Change TIM state */
+  htim->State = HAL_TIM_STATE_RESET;
+
+  /* Release Lock */
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Initializes the TIM PWM MSP.
+  * @param  htim TIM PWM handle
+  * @retval None
+  */
+__weak void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_PWM_MspInit could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  DeInitializes TIM PWM MSP.
+  * @param  htim TIM PWM handle
+  * @retval None
+  */
+__weak void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_PWM_MspDeInit could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Starts the PWM signal generation.
+  * @param  htim TIM handle
+  * @param  Channel TIM Channels to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
+  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));
+
+  /* Check the TIM channel state */
+  if (TIM_CHANNEL_STATE_GET(htim, Channel) != HAL_TIM_CHANNEL_STATE_READY)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM channel state */
+  TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  /* Enable the Capture compare channel */
+  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
+
+  if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+  {
+    /* Enable the main output */
+    __HAL_TIM_MOE_ENABLE(htim);
+  }
+
+  /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+  if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+  {
+    tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+    if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+  else
+  {
+    __HAL_TIM_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the PWM signal generation.
+  * @param  htim TIM PWM handle
+  * @param  Channel TIM Channels to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
+  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_PWM_Stop(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));
+
+  /* Disable the Capture compare channel */
+  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_DISABLE);
+
+  if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+  {
+    /* Disable the Main Output */
+    __HAL_TIM_MOE_DISABLE(htim);
+  }
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM channel state */
+  TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Starts the PWM signal generation in interrupt mode.
+  * @param  htim TIM PWM handle
+  * @param  Channel TIM Channel to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_PWM_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+
+  /* Check the TIM channel state */
+  if (TIM_CHANNEL_STATE_GET(htim, Channel) != HAL_TIM_CHANNEL_STATE_READY)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM channel state */
+  TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Enable the TIM Capture/Compare 1 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Enable the TIM Capture/Compare 2 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Enable the TIM Capture/Compare 3 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC3);
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Enable the TIM Capture/Compare 4 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC4);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Enable the Capture compare channel */
+    TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
+
+    if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+    {
+      /* Enable the main output */
+      __HAL_TIM_MOE_ENABLE(htim);
+    }
+
+    /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+    if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+    {
+      tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+      if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+      {
+        __HAL_TIM_ENABLE(htim);
+      }
+    }
+    else
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Stops the PWM signal generation in interrupt mode.
+  * @param  htim TIM PWM handle
+  * @param  Channel TIM Channels to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_PWM_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Disable the TIM Capture/Compare 1 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Disable the TIM Capture/Compare 2 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Disable the TIM Capture/Compare 3 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC3);
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Disable the TIM Capture/Compare 4 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC4);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Disable the Capture compare channel */
+    TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_DISABLE);
+
+    if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+    {
+      /* Disable the Main Output */
+      __HAL_TIM_MOE_DISABLE(htim);
+    }
+
+    /* Disable the Peripheral */
+    __HAL_TIM_DISABLE(htim);
+
+    /* Set the TIM channel state */
+    TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Starts the TIM PWM signal generation in DMA mode.
+  * @param  htim TIM PWM handle
+  * @param  Channel TIM Channels to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @param  pData The source Buffer address.
+  * @param  Length The length of data to be transferred from memory to TIM peripheral
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, const uint32_t *pData,
+                                        uint16_t Length)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+
+  /* Set the TIM channel state */
+  if (TIM_CHANNEL_STATE_GET(htim, Channel) == HAL_TIM_CHANNEL_STATE_BUSY)
+  {
+    return HAL_BUSY;
+  }
+  else if (TIM_CHANNEL_STATE_GET(htim, Channel) == HAL_TIM_CHANNEL_STATE_READY)
+  {
+    if ((pData == NULL) || (Length == 0U))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+    }
+  }
+  else
+  {
+    return HAL_ERROR;
+  }
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC1]->XferCpltCallback = TIM_DMADelayPulseCplt;
+      htim->hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC1]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)pData, (uint32_t)&htim->Instance->CCR1,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+
+      /* Enable the TIM Capture/Compare 1 DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC2]->XferCpltCallback = TIM_DMADelayPulseCplt;
+      htim->hdma[TIM_DMA_ID_CC2]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC2]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC2], (uint32_t)pData, (uint32_t)&htim->Instance->CCR2,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Capture/Compare 2 DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC3]->XferCpltCallback = TIM_DMADelayPulseCplt;
+      htim->hdma[TIM_DMA_ID_CC3]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC3]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC3], (uint32_t)pData, (uint32_t)&htim->Instance->CCR3,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Output Capture/Compare 3 request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC3);
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC4]->XferCpltCallback = TIM_DMADelayPulseCplt;
+      htim->hdma[TIM_DMA_ID_CC4]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC4]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC4], (uint32_t)pData, (uint32_t)&htim->Instance->CCR4,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Capture/Compare 4 DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC4);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Enable the Capture compare channel */
+    TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
+
+    if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+    {
+      /* Enable the main output */
+      __HAL_TIM_MOE_ENABLE(htim);
+    }
+
+    /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+    if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+    {
+      tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+      if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+      {
+        __HAL_TIM_ENABLE(htim);
+      }
+    }
+    else
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Stops the TIM PWM signal generation in DMA mode.
+  * @param  htim TIM PWM handle
+  * @param  Channel TIM Channels to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_PWM_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Disable the TIM Capture/Compare 1 DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC1);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC1]);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Disable the TIM Capture/Compare 2 DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC2);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC2]);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Disable the TIM Capture/Compare 3 DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC3);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC3]);
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Disable the TIM Capture/Compare 4 interrupt */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC4);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC4]);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Disable the Capture compare channel */
+    TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_DISABLE);
+
+    if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+    {
+      /* Disable the Main Output */
+      __HAL_TIM_MOE_DISABLE(htim);
+    }
+
+    /* Disable the Peripheral */
+    __HAL_TIM_DISABLE(htim);
+
+    /* Set the TIM channel state */
+    TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Exported_Functions_Group4 TIM Input Capture functions
+  *  @brief    TIM Input Capture functions
+  *
+@verbatim
+  ==============================================================================
+              ##### TIM Input Capture functions #####
+  ==============================================================================
+ [..]
+   This section provides functions allowing to:
+   (+) Initialize and configure the TIM Input Capture.
+   (+) De-initialize the TIM Input Capture.
+   (+) Start the TIM Input Capture.
+   (+) Stop the TIM Input Capture.
+   (+) Start the TIM Input Capture and enable interrupt.
+   (+) Stop the TIM Input Capture and disable interrupt.
+   (+) Start the TIM Input Capture and enable DMA transfer.
+   (+) Stop the TIM Input Capture and disable DMA transfer.
+
+@endverbatim
+  * @{
+  */
+/**
+  * @brief  Initializes the TIM Input Capture Time base according to the specified
+  *         parameters in the TIM_HandleTypeDef and initializes the associated handle.
+  * @note   Switching from Center Aligned counter mode to Edge counter mode (or reverse)
+  *         requires a timer reset to avoid unexpected direction
+  *         due to DIR bit readonly in center aligned mode.
+  *         Ex: call @ref HAL_TIM_IC_DeInit() before HAL_TIM_IC_Init()
+  * @param  htim TIM Input Capture handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_IC_Init(TIM_HandleTypeDef *htim)
+{
+  /* Check the TIM handle allocation */
+  if (htim == NULL)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
+  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
+  assert_param(IS_TIM_PERIOD(htim, htim->Init.Period));
+  assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));
+
+  if (htim->State == HAL_TIM_STATE_RESET)
+  {
+    /* Allocate lock resource and initialize it */
+    htim->Lock = HAL_UNLOCKED;
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+    /* Reset interrupt callbacks to legacy weak callbacks */
+    TIM_ResetCallback(htim);
+
+    if (htim->IC_MspInitCallback == NULL)
+    {
+      htim->IC_MspInitCallback = HAL_TIM_IC_MspInit;
+    }
+    /* Init the low level hardware : GPIO, CLOCK, NVIC */
+    htim->IC_MspInitCallback(htim);
+#else
+    /* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
+    HAL_TIM_IC_MspInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+  }
+
+  /* Set the TIM state */
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Init the base time for the input capture */
+  TIM_Base_SetConfig(htim->Instance, &htim->Init);
+
+  /* Initialize the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_READY;
+
+  /* Initialize the TIM channels state */
+  TIM_CHANNEL_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Initialize the TIM state*/
+  htim->State = HAL_TIM_STATE_READY;
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  DeInitializes the TIM peripheral
+  * @param  htim TIM Input Capture handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_IC_DeInit(TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Disable the TIM Peripheral Clock */
+  __HAL_TIM_DISABLE(htim);
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  if (htim->IC_MspDeInitCallback == NULL)
+  {
+    htim->IC_MspDeInitCallback = HAL_TIM_IC_MspDeInit;
+  }
+  /* DeInit the low level hardware */
+  htim->IC_MspDeInitCallback(htim);
+#else
+  /* DeInit the low level hardware: GPIO, CLOCK, NVIC and DMA */
+  HAL_TIM_IC_MspDeInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  /* Change the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_RESET;
+
+  /* Change the TIM channels state */
+  TIM_CHANNEL_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_RESET);
+  TIM_CHANNEL_N_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_RESET);
+
+  /* Change TIM state */
+  htim->State = HAL_TIM_STATE_RESET;
+
+  /* Release Lock */
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Initializes the TIM Input Capture MSP.
+  * @param  htim TIM Input Capture handle
+  * @retval None
+  */
+__weak void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_IC_MspInit could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  DeInitializes TIM Input Capture MSP.
+  * @param  htim TIM handle
+  * @retval None
+  */
+__weak void HAL_TIM_IC_MspDeInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_IC_MspDeInit could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Starts the TIM Input Capture measurement.
+  * @param  htim TIM Input Capture handle
+  * @param  Channel TIM Channels to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_IC_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  uint32_t tmpsmcr;
+  HAL_TIM_ChannelStateTypeDef channel_state = TIM_CHANNEL_STATE_GET(htim, Channel);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_state = TIM_CHANNEL_N_STATE_GET(htim, Channel);
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+
+  /* Check the TIM channel state */
+  if ((channel_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_state != HAL_TIM_CHANNEL_STATE_READY))
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM channel state */
+  TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  /* Enable the Input Capture channel */
+  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
+
+  /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+  if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+  {
+    tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+    if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+  else
+  {
+    __HAL_TIM_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM Input Capture measurement.
+  * @param  htim TIM Input Capture handle
+  * @param  Channel TIM Channels to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_IC_Stop(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+
+  /* Disable the Input Capture channel */
+  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_DISABLE);
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM channel state */
+  TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Starts the TIM Input Capture measurement in interrupt mode.
+  * @param  htim TIM Input Capture handle
+  * @param  Channel TIM Channels to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_IC_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpsmcr;
+
+  HAL_TIM_ChannelStateTypeDef channel_state = TIM_CHANNEL_STATE_GET(htim, Channel);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_state = TIM_CHANNEL_N_STATE_GET(htim, Channel);
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+
+  /* Check the TIM channel state */
+  if ((channel_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_state != HAL_TIM_CHANNEL_STATE_READY))
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM channel state */
+  TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Enable the TIM Capture/Compare 1 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Enable the TIM Capture/Compare 2 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Enable the TIM Capture/Compare 3 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC3);
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Enable the TIM Capture/Compare 4 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC4);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Enable the Input Capture channel */
+    TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
+
+    /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+    if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+    {
+      tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+      if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+      {
+        __HAL_TIM_ENABLE(htim);
+      }
+    }
+    else
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Stops the TIM Input Capture measurement in interrupt mode.
+  * @param  htim TIM Input Capture handle
+  * @param  Channel TIM Channels to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_IC_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Disable the TIM Capture/Compare 1 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Disable the TIM Capture/Compare 2 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Disable the TIM Capture/Compare 3 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC3);
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Disable the TIM Capture/Compare 4 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC4);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Disable the Input Capture channel */
+    TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_DISABLE);
+
+    /* Disable the Peripheral */
+    __HAL_TIM_DISABLE(htim);
+
+    /* Set the TIM channel state */
+    TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Starts the TIM Input Capture measurement in DMA mode.
+  * @param  htim TIM Input Capture handle
+  * @param  Channel TIM Channels to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @param  pData The destination Buffer address.
+  * @param  Length The length of data to be transferred from TIM peripheral to memory.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_IC_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpsmcr;
+
+  HAL_TIM_ChannelStateTypeDef channel_state = TIM_CHANNEL_STATE_GET(htim, Channel);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_state = TIM_CHANNEL_N_STATE_GET(htim, Channel);
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+  assert_param(IS_TIM_DMA_CC_INSTANCE(htim->Instance));
+
+  /* Set the TIM channel state */
+  if ((channel_state == HAL_TIM_CHANNEL_STATE_BUSY)
+      || (complementary_channel_state == HAL_TIM_CHANNEL_STATE_BUSY))
+  {
+    return HAL_BUSY;
+  }
+  else if ((channel_state == HAL_TIM_CHANNEL_STATE_READY)
+           && (complementary_channel_state == HAL_TIM_CHANNEL_STATE_READY))
+  {
+    if ((pData == NULL) || (Length == 0U))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+      TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+    }
+  }
+  else
+  {
+    return HAL_ERROR;
+  }
+
+  /* Enable the Input Capture channel */
+  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Set the DMA capture callbacks */
+      htim->hdma[TIM_DMA_ID_CC1]->XferCpltCallback = TIM_DMACaptureCplt;
+      htim->hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = TIM_DMACaptureHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC1]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)&htim->Instance->CCR1, (uint32_t)pData,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Capture/Compare 1 DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Set the DMA capture callbacks */
+      htim->hdma[TIM_DMA_ID_CC2]->XferCpltCallback = TIM_DMACaptureCplt;
+      htim->hdma[TIM_DMA_ID_CC2]->XferHalfCpltCallback = TIM_DMACaptureHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC2]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC2], (uint32_t)&htim->Instance->CCR2, (uint32_t)pData,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Capture/Compare 2  DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Set the DMA capture callbacks */
+      htim->hdma[TIM_DMA_ID_CC3]->XferCpltCallback = TIM_DMACaptureCplt;
+      htim->hdma[TIM_DMA_ID_CC3]->XferHalfCpltCallback = TIM_DMACaptureHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC3]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC3], (uint32_t)&htim->Instance->CCR3, (uint32_t)pData,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Capture/Compare 3  DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC3);
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Set the DMA capture callbacks */
+      htim->hdma[TIM_DMA_ID_CC4]->XferCpltCallback = TIM_DMACaptureCplt;
+      htim->hdma[TIM_DMA_ID_CC4]->XferHalfCpltCallback = TIM_DMACaptureHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC4]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC4], (uint32_t)&htim->Instance->CCR4, (uint32_t)pData,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Capture/Compare 4  DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC4);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+  if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+  {
+    tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+    if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+  else
+  {
+    __HAL_TIM_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Stops the TIM Input Capture measurement in DMA mode.
+  * @param  htim TIM Input Capture handle
+  * @param  Channel TIM Channels to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_IC_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_CHANNEL(htim->Instance, Channel));
+  assert_param(IS_TIM_DMA_CC_INSTANCE(htim->Instance));
+
+  /* Disable the Input Capture channel */
+  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_DISABLE);
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Disable the TIM Capture/Compare 1 DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC1);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC1]);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Disable the TIM Capture/Compare 2 DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC2);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC2]);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Disable the TIM Capture/Compare 3  DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC3);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC3]);
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Disable the TIM Capture/Compare 4  DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC4);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC4]);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Disable the Peripheral */
+    __HAL_TIM_DISABLE(htim);
+
+    /* Set the TIM channel state */
+    TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  }
+
+  /* Return function status */
+  return status;
+}
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Exported_Functions_Group5 TIM One Pulse functions
+  *  @brief    TIM One Pulse functions
+  *
+@verbatim
+  ==============================================================================
+                        ##### TIM One Pulse functions #####
+  ==============================================================================
+  [..]
+    This section provides functions allowing to:
+    (+) Initialize and configure the TIM One Pulse.
+    (+) De-initialize the TIM One Pulse.
+    (+) Start the TIM One Pulse.
+    (+) Stop the TIM One Pulse.
+    (+) Start the TIM One Pulse and enable interrupt.
+    (+) Stop the TIM One Pulse and disable interrupt.
+    (+) Start the TIM One Pulse and enable DMA transfer.
+    (+) Stop the TIM One Pulse and disable DMA transfer.
+
+@endverbatim
+  * @{
+  */
+/**
+  * @brief  Initializes the TIM One Pulse Time Base according to the specified
+  *         parameters in the TIM_HandleTypeDef and initializes the associated handle.
+  * @note   Switching from Center Aligned counter mode to Edge counter mode (or reverse)
+  *         requires a timer reset to avoid unexpected direction
+  *         due to DIR bit readonly in center aligned mode.
+  *         Ex: call @ref HAL_TIM_OnePulse_DeInit() before HAL_TIM_OnePulse_Init()
+  * @note   When the timer instance is initialized in One Pulse mode, timer
+  *         channels 1 and channel 2 are reserved and cannot be used for other
+  *         purpose.
+  * @param  htim TIM One Pulse handle
+  * @param  OnePulseMode Select the One pulse mode.
+  *         This parameter can be one of the following values:
+  *            @arg TIM_OPMODE_SINGLE: Only one pulse will be generated.
+  *            @arg TIM_OPMODE_REPETITIVE: Repetitive pulses will be generated.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OnePulse_Init(TIM_HandleTypeDef *htim, uint32_t OnePulseMode)
+{
+  /* Check the TIM handle allocation */
+  if (htim == NULL)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
+  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
+  assert_param(IS_TIM_OPM_MODE(OnePulseMode));
+  assert_param(IS_TIM_PERIOD(htim, htim->Init.Period));
+  assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));
+
+  if (htim->State == HAL_TIM_STATE_RESET)
+  {
+    /* Allocate lock resource and initialize it */
+    htim->Lock = HAL_UNLOCKED;
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+    /* Reset interrupt callbacks to legacy weak callbacks */
+    TIM_ResetCallback(htim);
+
+    if (htim->OnePulse_MspInitCallback == NULL)
+    {
+      htim->OnePulse_MspInitCallback = HAL_TIM_OnePulse_MspInit;
+    }
+    /* Init the low level hardware : GPIO, CLOCK, NVIC */
+    htim->OnePulse_MspInitCallback(htim);
+#else
+    /* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
+    HAL_TIM_OnePulse_MspInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+  }
+
+  /* Set the TIM state */
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Configure the Time base in the One Pulse Mode */
+  TIM_Base_SetConfig(htim->Instance, &htim->Init);
+
+  /* Reset the OPM Bit */
+  htim->Instance->CR1 &= ~TIM_CR1_OPM;
+
+  /* Configure the OPM Mode */
+  htim->Instance->CR1 |= OnePulseMode;
+
+  /* Initialize the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_READY;
+
+  /* Initialize the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Initialize the TIM state*/
+  htim->State = HAL_TIM_STATE_READY;
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  DeInitializes the TIM One Pulse
+  * @param  htim TIM One Pulse handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OnePulse_DeInit(TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Disable the TIM Peripheral Clock */
+  __HAL_TIM_DISABLE(htim);
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  if (htim->OnePulse_MspDeInitCallback == NULL)
+  {
+    htim->OnePulse_MspDeInitCallback = HAL_TIM_OnePulse_MspDeInit;
+  }
+  /* DeInit the low level hardware */
+  htim->OnePulse_MspDeInitCallback(htim);
+#else
+  /* DeInit the low level hardware: GPIO, CLOCK, NVIC */
+  HAL_TIM_OnePulse_MspDeInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  /* Change the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_RESET;
+
+  /* Set the TIM channel state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_RESET);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_RESET);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_RESET);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_RESET);
+
+  /* Change TIM state */
+  htim->State = HAL_TIM_STATE_RESET;
+
+  /* Release Lock */
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Initializes the TIM One Pulse MSP.
+  * @param  htim TIM One Pulse handle
+  * @retval None
+  */
+__weak void HAL_TIM_OnePulse_MspInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_OnePulse_MspInit could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  DeInitializes TIM One Pulse MSP.
+  * @param  htim TIM One Pulse handle
+  * @retval None
+  */
+__weak void HAL_TIM_OnePulse_MspDeInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_OnePulse_MspDeInit could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Starts the TIM One Pulse signal generation.
+  * @note Though OutputChannel parameter is deprecated and ignored by the function
+  *        it has been kept to avoid HAL_TIM API compatibility break.
+  * @note The pulse output channel is determined when calling
+  *       @ref HAL_TIM_OnePulse_ConfigChannel().
+  * @param  htim TIM One Pulse handle
+  * @param  OutputChannel See note above
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OnePulse_Start(TIM_HandleTypeDef *htim, uint32_t OutputChannel)
+{
+  HAL_TIM_ChannelStateTypeDef channel_1_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef channel_2_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_2);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_1_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_2_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_2);
+
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(OutputChannel);
+
+  /* Check the TIM channels state */
+  if ((channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (channel_2_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_2_state != HAL_TIM_CHANNEL_STATE_READY))
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  /* Enable the Capture compare and the Input Capture channels
+    (in the OPM Mode the two possible channels that can be used are TIM_CHANNEL_1 and TIM_CHANNEL_2)
+    if TIM_CHANNEL_1 is used as output, the TIM_CHANNEL_2 will be used as input and
+    if TIM_CHANNEL_1 is used as input, the TIM_CHANNEL_2 will be used as output
+    whatever the combination, the TIM_CHANNEL_1 and TIM_CHANNEL_2 should be enabled together
+
+    No need to enable the counter, it's enabled automatically by hardware
+    (the counter starts in response to a stimulus and generate a pulse */
+
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
+
+  if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+  {
+    /* Enable the main output */
+    __HAL_TIM_MOE_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM One Pulse signal generation.
+  * @note Though OutputChannel parameter is deprecated and ignored by the function
+  *        it has been kept to avoid HAL_TIM API compatibility break.
+  * @note The pulse output channel is determined when calling
+  *       @ref HAL_TIM_OnePulse_ConfigChannel().
+  * @param  htim TIM One Pulse handle
+  * @param  OutputChannel See note above
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OnePulse_Stop(TIM_HandleTypeDef *htim, uint32_t OutputChannel)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(OutputChannel);
+
+  /* Disable the Capture compare and the Input Capture channels
+  (in the OPM Mode the two possible channels that can be used are TIM_CHANNEL_1 and TIM_CHANNEL_2)
+  if TIM_CHANNEL_1 is used as output, the TIM_CHANNEL_2 will be used as input and
+  if TIM_CHANNEL_1 is used as input, the TIM_CHANNEL_2 will be used as output
+  whatever the combination, the TIM_CHANNEL_1 and TIM_CHANNEL_2 should be disabled together */
+
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_DISABLE);
+
+  if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+  {
+    /* Disable the Main Output */
+    __HAL_TIM_MOE_DISABLE(htim);
+  }
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Starts the TIM One Pulse signal generation in interrupt mode.
+  * @note Though OutputChannel parameter is deprecated and ignored by the function
+  *        it has been kept to avoid HAL_TIM API compatibility break.
+  * @note The pulse output channel is determined when calling
+  *       @ref HAL_TIM_OnePulse_ConfigChannel().
+  * @param  htim TIM One Pulse handle
+  * @param  OutputChannel See note above
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OnePulse_Start_IT(TIM_HandleTypeDef *htim, uint32_t OutputChannel)
+{
+  HAL_TIM_ChannelStateTypeDef channel_1_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef channel_2_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_2);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_1_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_2_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_2);
+
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(OutputChannel);
+
+  /* Check the TIM channels state */
+  if ((channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (channel_2_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_2_state != HAL_TIM_CHANNEL_STATE_READY))
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  /* Enable the Capture compare and the Input Capture channels
+    (in the OPM Mode the two possible channels that can be used are TIM_CHANNEL_1 and TIM_CHANNEL_2)
+    if TIM_CHANNEL_1 is used as output, the TIM_CHANNEL_2 will be used as input and
+    if TIM_CHANNEL_1 is used as input, the TIM_CHANNEL_2 will be used as output
+    whatever the combination, the TIM_CHANNEL_1 and TIM_CHANNEL_2 should be enabled together
+
+    No need to enable the counter, it's enabled automatically by hardware
+    (the counter starts in response to a stimulus and generate a pulse */
+
+  /* Enable the TIM Capture/Compare 1 interrupt */
+  __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
+
+  /* Enable the TIM Capture/Compare 2 interrupt */
+  __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC2);
+
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
+
+  if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+  {
+    /* Enable the main output */
+    __HAL_TIM_MOE_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM One Pulse signal generation in interrupt mode.
+  * @note Though OutputChannel parameter is deprecated and ignored by the function
+  *        it has been kept to avoid HAL_TIM API compatibility break.
+  * @note The pulse output channel is determined when calling
+  *       @ref HAL_TIM_OnePulse_ConfigChannel().
+  * @param  htim TIM One Pulse handle
+  * @param  OutputChannel See note above
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OnePulse_Stop_IT(TIM_HandleTypeDef *htim, uint32_t OutputChannel)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(OutputChannel);
+
+  /* Disable the TIM Capture/Compare 1 interrupt */
+  __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC1);
+
+  /* Disable the TIM Capture/Compare 2 interrupt */
+  __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC2);
+
+  /* Disable the Capture compare and the Input Capture channels
+  (in the OPM Mode the two possible channels that can be used are TIM_CHANNEL_1 and TIM_CHANNEL_2)
+  if TIM_CHANNEL_1 is used as output, the TIM_CHANNEL_2 will be used as input and
+  if TIM_CHANNEL_1 is used as input, the TIM_CHANNEL_2 will be used as output
+  whatever the combination, the TIM_CHANNEL_1 and TIM_CHANNEL_2 should be disabled together */
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_DISABLE);
+
+  if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
+  {
+    /* Disable the Main Output */
+    __HAL_TIM_MOE_DISABLE(htim);
+  }
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Exported_Functions_Group6 TIM Encoder functions
+  *  @brief    TIM Encoder functions
+  *
+@verbatim
+  ==============================================================================
+                          ##### TIM Encoder functions #####
+  ==============================================================================
+  [..]
+    This section provides functions allowing to:
+    (+) Initialize and configure the TIM Encoder.
+    (+) De-initialize the TIM Encoder.
+    (+) Start the TIM Encoder.
+    (+) Stop the TIM Encoder.
+    (+) Start the TIM Encoder and enable interrupt.
+    (+) Stop the TIM Encoder and disable interrupt.
+    (+) Start the TIM Encoder and enable DMA transfer.
+    (+) Stop the TIM Encoder and disable DMA transfer.
+
+@endverbatim
+  * @{
+  */
+/**
+  * @brief  Initializes the TIM Encoder Interface and initialize the associated handle.
+  * @note   Switching from Center Aligned counter mode to Edge counter mode (or reverse)
+  *         requires a timer reset to avoid unexpected direction
+  *         due to DIR bit readonly in center aligned mode.
+  *         Ex: call @ref HAL_TIM_Encoder_DeInit() before HAL_TIM_Encoder_Init()
+  * @note   Encoder mode and External clock mode 2 are not compatible and must not be selected together
+  *         Ex: A call for @ref HAL_TIM_Encoder_Init will erase the settings of @ref HAL_TIM_ConfigClockSource
+  *         using TIM_CLOCKSOURCE_ETRMODE2 and vice versa
+  * @note   When the timer instance is initialized in Encoder mode, timer
+  *         channels 1 and channel 2 are reserved and cannot be used for other
+  *         purpose.
+  * @param  htim TIM Encoder Interface handle
+  * @param  sConfig TIM Encoder Interface configuration structure
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Encoder_Init(TIM_HandleTypeDef *htim, const TIM_Encoder_InitTypeDef *sConfig)
+{
+  uint32_t tmpsmcr;
+  uint32_t tmpccmr1;
+  uint32_t tmpccer;
+
+  /* Check the TIM handle allocation */
+  if (htim == NULL)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Check the parameters */
+  assert_param(IS_TIM_ENCODER_INTERFACE_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
+  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
+  assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));
+  assert_param(IS_TIM_ENCODER_MODE(sConfig->EncoderMode));
+  assert_param(IS_TIM_IC_SELECTION(sConfig->IC1Selection));
+  assert_param(IS_TIM_IC_SELECTION(sConfig->IC2Selection));
+  assert_param(IS_TIM_ENCODERINPUT_POLARITY(sConfig->IC1Polarity));
+  assert_param(IS_TIM_ENCODERINPUT_POLARITY(sConfig->IC2Polarity));
+  assert_param(IS_TIM_IC_PRESCALER(sConfig->IC1Prescaler));
+  assert_param(IS_TIM_IC_PRESCALER(sConfig->IC2Prescaler));
+  assert_param(IS_TIM_IC_FILTER(sConfig->IC1Filter));
+  assert_param(IS_TIM_IC_FILTER(sConfig->IC2Filter));
+  assert_param(IS_TIM_PERIOD(htim, htim->Init.Period));
+
+  if (htim->State == HAL_TIM_STATE_RESET)
+  {
+    /* Allocate lock resource and initialize it */
+    htim->Lock = HAL_UNLOCKED;
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+    /* Reset interrupt callbacks to legacy weak callbacks */
+    TIM_ResetCallback(htim);
+
+    if (htim->Encoder_MspInitCallback == NULL)
+    {
+      htim->Encoder_MspInitCallback = HAL_TIM_Encoder_MspInit;
+    }
+    /* Init the low level hardware : GPIO, CLOCK, NVIC */
+    htim->Encoder_MspInitCallback(htim);
+#else
+    /* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
+    HAL_TIM_Encoder_MspInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+  }
+
+  /* Set the TIM state */
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Reset the SMS and ECE bits */
+  htim->Instance->SMCR &= ~(TIM_SMCR_SMS | TIM_SMCR_ECE);
+
+  /* Configure the Time base in the Encoder Mode */
+  TIM_Base_SetConfig(htim->Instance, &htim->Init);
+
+  /* Get the TIMx SMCR register value */
+  tmpsmcr = htim->Instance->SMCR;
+
+  /* Get the TIMx CCMR1 register value */
+  tmpccmr1 = htim->Instance->CCMR1;
+
+  /* Get the TIMx CCER register value */
+  tmpccer = htim->Instance->CCER;
+
+  /* Set the encoder Mode */
+  tmpsmcr |= sConfig->EncoderMode;
+
+  /* Select the Capture Compare 1 and the Capture Compare 2 as input */
+  tmpccmr1 &= ~(TIM_CCMR1_CC1S | TIM_CCMR1_CC2S);
+  tmpccmr1 |= (sConfig->IC1Selection | (sConfig->IC2Selection << 8U));
+
+  /* Set the Capture Compare 1 and the Capture Compare 2 prescalers and filters */
+  tmpccmr1 &= ~(TIM_CCMR1_IC1PSC | TIM_CCMR1_IC2PSC);
+  tmpccmr1 &= ~(TIM_CCMR1_IC1F | TIM_CCMR1_IC2F);
+  tmpccmr1 |= sConfig->IC1Prescaler | (sConfig->IC2Prescaler << 8U);
+  tmpccmr1 |= (sConfig->IC1Filter << 4U) | (sConfig->IC2Filter << 12U);
+
+  /* Set the TI1 and the TI2 Polarities */
+  tmpccer &= ~(TIM_CCER_CC1P | TIM_CCER_CC2P);
+  tmpccer &= ~(TIM_CCER_CC1NP | TIM_CCER_CC2NP);
+  tmpccer |= sConfig->IC1Polarity | (sConfig->IC2Polarity << 4U);
+
+  /* Write to TIMx SMCR */
+  htim->Instance->SMCR = tmpsmcr;
+
+  /* Write to TIMx CCMR1 */
+  htim->Instance->CCMR1 = tmpccmr1;
+
+  /* Write to TIMx CCER */
+  htim->Instance->CCER = tmpccer;
+
+  /* Initialize the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_READY;
+
+  /* Set the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Initialize the TIM state*/
+  htim->State = HAL_TIM_STATE_READY;
+
+  return HAL_OK;
+}
+
+
+/**
+  * @brief  DeInitializes the TIM Encoder interface
+  * @param  htim TIM Encoder Interface handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Encoder_DeInit(TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Disable the TIM Peripheral Clock */
+  __HAL_TIM_DISABLE(htim);
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  if (htim->Encoder_MspDeInitCallback == NULL)
+  {
+    htim->Encoder_MspDeInitCallback = HAL_TIM_Encoder_MspDeInit;
+  }
+  /* DeInit the low level hardware */
+  htim->Encoder_MspDeInitCallback(htim);
+#else
+  /* DeInit the low level hardware: GPIO, CLOCK, NVIC */
+  HAL_TIM_Encoder_MspDeInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  /* Change the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_RESET;
+
+  /* Set the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_RESET);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_RESET);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_RESET);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_RESET);
+
+  /* Change TIM state */
+  htim->State = HAL_TIM_STATE_RESET;
+
+  /* Release Lock */
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Initializes the TIM Encoder Interface MSP.
+  * @param  htim TIM Encoder Interface handle
+  * @retval None
+  */
+__weak void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_Encoder_MspInit could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  DeInitializes TIM Encoder Interface MSP.
+  * @param  htim TIM Encoder Interface handle
+  * @retval None
+  */
+__weak void HAL_TIM_Encoder_MspDeInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_Encoder_MspDeInit could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Starts the TIM Encoder Interface.
+  * @param  htim TIM Encoder Interface handle
+  * @param  Channel TIM Channels to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_ALL: TIM Channel 1 and TIM Channel 2 are selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Encoder_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_TIM_ChannelStateTypeDef channel_1_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef channel_2_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_2);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_1_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_2_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_2);
+
+  /* Check the parameters */
+  assert_param(IS_TIM_ENCODER_INTERFACE_INSTANCE(htim->Instance));
+
+  /* Set the TIM channel(s) state */
+  if (Channel == TIM_CHANNEL_1)
+  {
+    if ((channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+        || (complementary_channel_1_state != HAL_TIM_CHANNEL_STATE_READY))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+    }
+  }
+  else if (Channel == TIM_CHANNEL_2)
+  {
+    if ((channel_2_state != HAL_TIM_CHANNEL_STATE_READY)
+        || (complementary_channel_2_state != HAL_TIM_CHANNEL_STATE_READY))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+    }
+  }
+  else
+  {
+    if ((channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+        || (channel_2_state != HAL_TIM_CHANNEL_STATE_READY)
+        || (complementary_channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+        || (complementary_channel_2_state != HAL_TIM_CHANNEL_STATE_READY))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+    }
+  }
+
+  /* Enable the encoder interface channels */
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
+      break;
+    }
+
+    default :
+    {
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
+      break;
+    }
+  }
+  /* Enable the Peripheral */
+  __HAL_TIM_ENABLE(htim);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM Encoder Interface.
+  * @param  htim TIM Encoder Interface handle
+  * @param  Channel TIM Channels to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_ALL: TIM Channel 1 and TIM Channel 2 are selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Encoder_Stop(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_ENCODER_INTERFACE_INSTANCE(htim->Instance));
+
+  /* Disable the Input Capture channels 1 and 2
+    (in the EncoderInterface the two possible channels that can be used are TIM_CHANNEL_1 and TIM_CHANNEL_2) */
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_DISABLE);
+      break;
+    }
+
+    default :
+    {
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_DISABLE);
+      break;
+    }
+  }
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM channel(s) state */
+  if ((Channel == TIM_CHANNEL_1) || (Channel == TIM_CHANNEL_2))
+  {
+    TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  }
+  else
+  {
+    TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Starts the TIM Encoder Interface in interrupt mode.
+  * @param  htim TIM Encoder Interface handle
+  * @param  Channel TIM Channels to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_ALL: TIM Channel 1 and TIM Channel 2 are selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Encoder_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_TIM_ChannelStateTypeDef channel_1_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef channel_2_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_2);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_1_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_2_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_2);
+
+  /* Check the parameters */
+  assert_param(IS_TIM_ENCODER_INTERFACE_INSTANCE(htim->Instance));
+
+  /* Set the TIM channel(s) state */
+  if (Channel == TIM_CHANNEL_1)
+  {
+    if ((channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+        || (complementary_channel_1_state != HAL_TIM_CHANNEL_STATE_READY))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+    }
+  }
+  else if (Channel == TIM_CHANNEL_2)
+  {
+    if ((channel_2_state != HAL_TIM_CHANNEL_STATE_READY)
+        || (complementary_channel_2_state != HAL_TIM_CHANNEL_STATE_READY))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+    }
+  }
+  else
+  {
+    if ((channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+        || (channel_2_state != HAL_TIM_CHANNEL_STATE_READY)
+        || (complementary_channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+        || (complementary_channel_2_state != HAL_TIM_CHANNEL_STATE_READY))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+    }
+  }
+
+  /* Enable the encoder interface channels */
+  /* Enable the capture compare Interrupts 1 and/or 2 */
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC2);
+      break;
+    }
+
+    default :
+    {
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC2);
+      break;
+    }
+  }
+
+  /* Enable the Peripheral */
+  __HAL_TIM_ENABLE(htim);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM Encoder Interface in interrupt mode.
+  * @param  htim TIM Encoder Interface handle
+  * @param  Channel TIM Channels to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_ALL: TIM Channel 1 and TIM Channel 2 are selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Encoder_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_ENCODER_INTERFACE_INSTANCE(htim->Instance));
+
+  /* Disable the Input Capture channels 1 and 2
+    (in the EncoderInterface the two possible channels that can be used are TIM_CHANNEL_1 and TIM_CHANNEL_2) */
+  if (Channel == TIM_CHANNEL_1)
+  {
+    TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
+
+    /* Disable the capture compare Interrupts 1 */
+    __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC1);
+  }
+  else if (Channel == TIM_CHANNEL_2)
+  {
+    TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_DISABLE);
+
+    /* Disable the capture compare Interrupts 2 */
+    __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC2);
+  }
+  else
+  {
+    TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
+    TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_DISABLE);
+
+    /* Disable the capture compare Interrupts 1 and 2 */
+    __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC1);
+    __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC2);
+  }
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM channel(s) state */
+  if ((Channel == TIM_CHANNEL_1) || (Channel == TIM_CHANNEL_2))
+  {
+    TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  }
+  else
+  {
+    TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Starts the TIM Encoder Interface in DMA mode.
+  * @param  htim TIM Encoder Interface handle
+  * @param  Channel TIM Channels to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_ALL: TIM Channel 1 and TIM Channel 2 are selected
+  * @param  pData1 The destination Buffer address for IC1.
+  * @param  pData2 The destination Buffer address for IC2.
+  * @param  Length The length of data to be transferred from TIM peripheral to memory.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Encoder_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData1,
+                                            uint32_t *pData2, uint16_t Length)
+{
+  HAL_TIM_ChannelStateTypeDef channel_1_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef channel_2_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_2);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_1_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_2_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_2);
+
+  /* Check the parameters */
+  assert_param(IS_TIM_ENCODER_INTERFACE_INSTANCE(htim->Instance));
+
+  /* Set the TIM channel(s) state */
+  if (Channel == TIM_CHANNEL_1)
+  {
+    if ((channel_1_state == HAL_TIM_CHANNEL_STATE_BUSY)
+        || (complementary_channel_1_state == HAL_TIM_CHANNEL_STATE_BUSY))
+    {
+      return HAL_BUSY;
+    }
+    else if ((channel_1_state == HAL_TIM_CHANNEL_STATE_READY)
+             && (complementary_channel_1_state == HAL_TIM_CHANNEL_STATE_READY))
+    {
+      if ((pData1 == NULL) || (Length == 0U))
+      {
+        return HAL_ERROR;
+      }
+      else
+      {
+        TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+        TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+      }
+    }
+    else
+    {
+      return HAL_ERROR;
+    }
+  }
+  else if (Channel == TIM_CHANNEL_2)
+  {
+    if ((channel_2_state == HAL_TIM_CHANNEL_STATE_BUSY)
+        || (complementary_channel_2_state == HAL_TIM_CHANNEL_STATE_BUSY))
+    {
+      return HAL_BUSY;
+    }
+    else if ((channel_2_state == HAL_TIM_CHANNEL_STATE_READY)
+             && (complementary_channel_2_state == HAL_TIM_CHANNEL_STATE_READY))
+    {
+      if ((pData2 == NULL) || (Length == 0U))
+      {
+        return HAL_ERROR;
+      }
+      else
+      {
+        TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+        TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+      }
+    }
+    else
+    {
+      return HAL_ERROR;
+    }
+  }
+  else
+  {
+    if ((channel_1_state == HAL_TIM_CHANNEL_STATE_BUSY)
+        || (channel_2_state == HAL_TIM_CHANNEL_STATE_BUSY)
+        || (complementary_channel_1_state == HAL_TIM_CHANNEL_STATE_BUSY)
+        || (complementary_channel_2_state == HAL_TIM_CHANNEL_STATE_BUSY))
+    {
+      return HAL_BUSY;
+    }
+    else if ((channel_1_state == HAL_TIM_CHANNEL_STATE_READY)
+             && (channel_2_state == HAL_TIM_CHANNEL_STATE_READY)
+             && (complementary_channel_1_state == HAL_TIM_CHANNEL_STATE_READY)
+             && (complementary_channel_2_state == HAL_TIM_CHANNEL_STATE_READY))
+    {
+      if ((((pData1 == NULL) || (pData2 == NULL))) || (Length == 0U))
+      {
+        return HAL_ERROR;
+      }
+      else
+      {
+        TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+        TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+        TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+        TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+      }
+    }
+    else
+    {
+      return HAL_ERROR;
+    }
+  }
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Set the DMA capture callbacks */
+      htim->hdma[TIM_DMA_ID_CC1]->XferCpltCallback = TIM_DMACaptureCplt;
+      htim->hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = TIM_DMACaptureHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC1]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)&htim->Instance->CCR1, (uint32_t)pData1,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Input Capture DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC1);
+
+      /* Enable the Capture compare channel */
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
+
+      /* Enable the Peripheral */
+      __HAL_TIM_ENABLE(htim);
+
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Set the DMA capture callbacks */
+      htim->hdma[TIM_DMA_ID_CC2]->XferCpltCallback = TIM_DMACaptureCplt;
+      htim->hdma[TIM_DMA_ID_CC2]->XferHalfCpltCallback = TIM_DMACaptureHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC2]->XferErrorCallback = TIM_DMAError;
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC2], (uint32_t)&htim->Instance->CCR2, (uint32_t)pData2,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Input Capture  DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC2);
+
+      /* Enable the Capture compare channel */
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
+
+      /* Enable the Peripheral */
+      __HAL_TIM_ENABLE(htim);
+
+      break;
+    }
+
+    default:
+    {
+      /* Set the DMA capture callbacks */
+      htim->hdma[TIM_DMA_ID_CC1]->XferCpltCallback = TIM_DMACaptureCplt;
+      htim->hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = TIM_DMACaptureHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC1]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)&htim->Instance->CCR1, (uint32_t)pData1,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+
+      /* Set the DMA capture callbacks */
+      htim->hdma[TIM_DMA_ID_CC2]->XferCpltCallback = TIM_DMACaptureCplt;
+      htim->hdma[TIM_DMA_ID_CC2]->XferHalfCpltCallback = TIM_DMACaptureHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC2]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC2], (uint32_t)&htim->Instance->CCR2, (uint32_t)pData2,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+
+      /* Enable the TIM Input Capture  DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC1);
+      /* Enable the TIM Input Capture  DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC2);
+
+      /* Enable the Capture compare channel */
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
+      TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
+
+      /* Enable the Peripheral */
+      __HAL_TIM_ENABLE(htim);
+
+      break;
+    }
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM Encoder Interface in DMA mode.
+  * @param  htim TIM Encoder Interface handle
+  * @param  Channel TIM Channels to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_ALL: TIM Channel 1 and TIM Channel 2 are selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_Encoder_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_ENCODER_INTERFACE_INSTANCE(htim->Instance));
+
+  /* Disable the Input Capture channels 1 and 2
+    (in the EncoderInterface the two possible channels that can be used are TIM_CHANNEL_1 and TIM_CHANNEL_2) */
+  if (Channel == TIM_CHANNEL_1)
+  {
+    TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
+
+    /* Disable the capture compare DMA Request 1 */
+    __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC1);
+    (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC1]);
+  }
+  else if (Channel == TIM_CHANNEL_2)
+  {
+    TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_DISABLE);
+
+    /* Disable the capture compare DMA Request 2 */
+    __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC2);
+    (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC2]);
+  }
+  else
+  {
+    TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
+    TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_DISABLE);
+
+    /* Disable the capture compare DMA Request 1 and 2 */
+    __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC1);
+    __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC2);
+    (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC1]);
+    (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC2]);
+  }
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM channel(s) state */
+  if ((Channel == TIM_CHANNEL_1) || (Channel == TIM_CHANNEL_2))
+  {
+    TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  }
+  else
+  {
+    TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+    TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @}
+  */
+/** @defgroup TIM_Exported_Functions_Group7 TIM IRQ handler management
+  *  @brief    TIM IRQ handler management
+  *
+@verbatim
+  ==============================================================================
+                        ##### IRQ handler management #####
+  ==============================================================================
+  [..]
+    This section provides Timer IRQ handler function.
+
+@endverbatim
+  * @{
+  */
+/**
+  * @brief  This function handles TIM interrupts requests.
+  * @param  htim TIM  handle
+  * @retval None
+  */
+void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
+{
+  uint32_t itsource = htim->Instance->DIER;
+  uint32_t itflag   = htim->Instance->SR;
+
+  /* Capture compare 1 event */
+  if ((itflag & (TIM_FLAG_CC1)) == (TIM_FLAG_CC1))
+  {
+    if ((itsource & (TIM_IT_CC1)) == (TIM_IT_CC1))
+    {
+      {
+        __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_CC1);
+        htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;
+
+        /* Input capture event */
+        if ((htim->Instance->CCMR1 & TIM_CCMR1_CC1S) != 0x00U)
+        {
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+          htim->IC_CaptureCallback(htim);
+#else
+          HAL_TIM_IC_CaptureCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+        }
+        /* Output compare event */
+        else
+        {
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+          htim->OC_DelayElapsedCallback(htim);
+          htim->PWM_PulseFinishedCallback(htim);
+#else
+          HAL_TIM_OC_DelayElapsedCallback(htim);
+          HAL_TIM_PWM_PulseFinishedCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+        }
+        htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
+      }
+    }
+  }
+  /* Capture compare 2 event */
+  if ((itflag & (TIM_FLAG_CC2)) == (TIM_FLAG_CC2))
+  {
+    if ((itsource & (TIM_IT_CC2)) == (TIM_IT_CC2))
+    {
+      __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_CC2);
+      htim->Channel = HAL_TIM_ACTIVE_CHANNEL_2;
+      /* Input capture event */
+      if ((htim->Instance->CCMR1 & TIM_CCMR1_CC2S) != 0x00U)
+      {
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+        htim->IC_CaptureCallback(htim);
+#else
+        HAL_TIM_IC_CaptureCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+      }
+      /* Output compare event */
+      else
+      {
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+        htim->OC_DelayElapsedCallback(htim);
+        htim->PWM_PulseFinishedCallback(htim);
+#else
+        HAL_TIM_OC_DelayElapsedCallback(htim);
+        HAL_TIM_PWM_PulseFinishedCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+      }
+      htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
+    }
+  }
+  /* Capture compare 3 event */
+  if ((itflag & (TIM_FLAG_CC3)) == (TIM_FLAG_CC3))
+  {
+    if ((itsource & (TIM_IT_CC3)) == (TIM_IT_CC3))
+    {
+      __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_CC3);
+      htim->Channel = HAL_TIM_ACTIVE_CHANNEL_3;
+      /* Input capture event */
+      if ((htim->Instance->CCMR2 & TIM_CCMR2_CC3S) != 0x00U)
+      {
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+        htim->IC_CaptureCallback(htim);
+#else
+        HAL_TIM_IC_CaptureCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+      }
+      /* Output compare event */
+      else
+      {
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+        htim->OC_DelayElapsedCallback(htim);
+        htim->PWM_PulseFinishedCallback(htim);
+#else
+        HAL_TIM_OC_DelayElapsedCallback(htim);
+        HAL_TIM_PWM_PulseFinishedCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+      }
+      htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
+    }
+  }
+  /* Capture compare 4 event */
+  if ((itflag & (TIM_FLAG_CC4)) == (TIM_FLAG_CC4))
+  {
+    if ((itsource & (TIM_IT_CC4)) == (TIM_IT_CC4))
+    {
+      __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_CC4);
+      htim->Channel = HAL_TIM_ACTIVE_CHANNEL_4;
+      /* Input capture event */
+      if ((htim->Instance->CCMR2 & TIM_CCMR2_CC4S) != 0x00U)
+      {
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+        htim->IC_CaptureCallback(htim);
+#else
+        HAL_TIM_IC_CaptureCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+      }
+      /* Output compare event */
+      else
+      {
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+        htim->OC_DelayElapsedCallback(htim);
+        htim->PWM_PulseFinishedCallback(htim);
+#else
+        HAL_TIM_OC_DelayElapsedCallback(htim);
+        HAL_TIM_PWM_PulseFinishedCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+      }
+      htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
+    }
+  }
+  /* TIM Update event */
+  if ((itflag & (TIM_FLAG_UPDATE)) == (TIM_FLAG_UPDATE))
+  {
+    if ((itsource & (TIM_IT_UPDATE)) == (TIM_IT_UPDATE))
+    {
+      __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_UPDATE);
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+      htim->PeriodElapsedCallback(htim);
+#else
+      HAL_TIM_PeriodElapsedCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+    }
+  }
+  /* TIM Break input event */
+  if (((itflag & (TIM_FLAG_BREAK)) == (TIM_FLAG_BREAK)) || \
+      ((itflag & (TIM_FLAG_SYSTEM_BREAK)) == (TIM_FLAG_SYSTEM_BREAK)))
+  {
+    if ((itsource & (TIM_IT_BREAK)) == (TIM_IT_BREAK))
+    {
+      __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_BREAK | TIM_FLAG_SYSTEM_BREAK);
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+      htim->BreakCallback(htim);
+#else
+      HAL_TIMEx_BreakCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+    }
+  }
+  /* TIM Break2 input event */
+  if ((itflag & (TIM_FLAG_BREAK2)) == (TIM_FLAG_BREAK2))
+  {
+    if ((itsource & (TIM_IT_BREAK)) == (TIM_IT_BREAK))
+    {
+      __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_BREAK2);
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+      htim->Break2Callback(htim);
+#else
+      HAL_TIMEx_Break2Callback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+    }
+  }
+  /* TIM Trigger detection event */
+  if ((itflag & (TIM_FLAG_TRIGGER)) == (TIM_FLAG_TRIGGER))
+  {
+    if ((itsource & (TIM_IT_TRIGGER)) == (TIM_IT_TRIGGER))
+    {
+      __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_TRIGGER);
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+      htim->TriggerCallback(htim);
+#else
+      HAL_TIM_TriggerCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+    }
+  }
+  /* TIM commutation event */
+  if ((itflag & (TIM_FLAG_COM)) == (TIM_FLAG_COM))
+  {
+    if ((itsource & (TIM_IT_COM)) == (TIM_IT_COM))
+    {
+      __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_COM);
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+      htim->CommutationCallback(htim);
+#else
+      HAL_TIMEx_CommutCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+    }
+  }
+}
+
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Exported_Functions_Group8 TIM Peripheral Control functions
+  *  @brief    TIM Peripheral Control functions
+  *
+@verbatim
+  ==============================================================================
+                   ##### Peripheral Control functions #####
+  ==============================================================================
+ [..]
+   This section provides functions allowing to:
+      (+) Configure The Input Output channels for OC, PWM, IC or One Pulse mode.
+      (+) Configure External Clock source.
+      (+) Configure Complementary channels, break features and dead time.
+      (+) Configure Master and the Slave synchronization.
+      (+) Configure the DMA Burst Mode.
+
+@endverbatim
+  * @{
+  */
+
+/**
+  * @brief  Initializes the TIM Output Compare Channels according to the specified
+  *         parameters in the TIM_OC_InitTypeDef.
+  * @param  htim TIM Output Compare handle
+  * @param  sConfig TIM Output Compare configuration structure
+  * @param  Channel TIM Channels to configure
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
+  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OC_ConfigChannel(TIM_HandleTypeDef *htim,
+                                           const TIM_OC_InitTypeDef *sConfig,
+                                           uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CHANNELS(Channel));
+  assert_param(IS_TIM_OC_MODE(sConfig->OCMode));
+  assert_param(IS_TIM_OC_POLARITY(sConfig->OCPolarity));
+
+  /* Process Locked */
+  __HAL_LOCK(htim);
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
+
+      /* Configure the TIM Channel 1 in Output Compare */
+      TIM_OC1_SetConfig(htim->Instance, sConfig);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
+
+      /* Configure the TIM Channel 2 in Output Compare */
+      TIM_OC2_SetConfig(htim->Instance, sConfig);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC3_INSTANCE(htim->Instance));
+
+      /* Configure the TIM Channel 3 in Output Compare */
+      TIM_OC3_SetConfig(htim->Instance, sConfig);
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC4_INSTANCE(htim->Instance));
+
+      /* Configure the TIM Channel 4 in Output Compare */
+      TIM_OC4_SetConfig(htim->Instance, sConfig);
+      break;
+    }
+
+    case TIM_CHANNEL_5:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC5_INSTANCE(htim->Instance));
+
+      /* Configure the TIM Channel 5 in Output Compare */
+      TIM_OC5_SetConfig(htim->Instance, sConfig);
+      break;
+    }
+
+    case TIM_CHANNEL_6:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC6_INSTANCE(htim->Instance));
+
+      /* Configure the TIM Channel 6 in Output Compare */
+      TIM_OC6_SetConfig(htim->Instance, sConfig);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  __HAL_UNLOCK(htim);
+
+  return status;
+}
+
+/**
+  * @brief  Initializes the TIM Input Capture Channels according to the specified
+  *         parameters in the TIM_IC_InitTypeDef.
+  * @param  htim TIM IC handle
+  * @param  sConfig TIM Input Capture configuration structure
+  * @param  Channel TIM Channel to configure
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_IC_ConfigChannel(TIM_HandleTypeDef *htim, const TIM_IC_InitTypeDef *sConfig, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_IC_POLARITY(sConfig->ICPolarity));
+  assert_param(IS_TIM_IC_SELECTION(sConfig->ICSelection));
+  assert_param(IS_TIM_IC_PRESCALER(sConfig->ICPrescaler));
+  assert_param(IS_TIM_IC_FILTER(sConfig->ICFilter));
+
+  /* Process Locked */
+  __HAL_LOCK(htim);
+
+  if (Channel == TIM_CHANNEL_1)
+  {
+    /* TI1 Configuration */
+    TIM_TI1_SetConfig(htim->Instance,
+                      sConfig->ICPolarity,
+                      sConfig->ICSelection,
+                      sConfig->ICFilter);
+
+    /* Reset the IC1PSC Bits */
+    htim->Instance->CCMR1 &= ~TIM_CCMR1_IC1PSC;
+
+    /* Set the IC1PSC value */
+    htim->Instance->CCMR1 |= sConfig->ICPrescaler;
+  }
+  else if (Channel == TIM_CHANNEL_2)
+  {
+    /* TI2 Configuration */
+    assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
+
+    TIM_TI2_SetConfig(htim->Instance,
+                      sConfig->ICPolarity,
+                      sConfig->ICSelection,
+                      sConfig->ICFilter);
+
+    /* Reset the IC2PSC Bits */
+    htim->Instance->CCMR1 &= ~TIM_CCMR1_IC2PSC;
+
+    /* Set the IC2PSC value */
+    htim->Instance->CCMR1 |= (sConfig->ICPrescaler << 8U);
+  }
+  else if (Channel == TIM_CHANNEL_3)
+  {
+    /* TI3 Configuration */
+    assert_param(IS_TIM_CC3_INSTANCE(htim->Instance));
+
+    TIM_TI3_SetConfig(htim->Instance,
+                      sConfig->ICPolarity,
+                      sConfig->ICSelection,
+                      sConfig->ICFilter);
+
+    /* Reset the IC3PSC Bits */
+    htim->Instance->CCMR2 &= ~TIM_CCMR2_IC3PSC;
+
+    /* Set the IC3PSC value */
+    htim->Instance->CCMR2 |= sConfig->ICPrescaler;
+  }
+  else if (Channel == TIM_CHANNEL_4)
+  {
+    /* TI4 Configuration */
+    assert_param(IS_TIM_CC4_INSTANCE(htim->Instance));
+
+    TIM_TI4_SetConfig(htim->Instance,
+                      sConfig->ICPolarity,
+                      sConfig->ICSelection,
+                      sConfig->ICFilter);
+
+    /* Reset the IC4PSC Bits */
+    htim->Instance->CCMR2 &= ~TIM_CCMR2_IC4PSC;
+
+    /* Set the IC4PSC value */
+    htim->Instance->CCMR2 |= (sConfig->ICPrescaler << 8U);
+  }
+  else
+  {
+    status = HAL_ERROR;
+  }
+
+  __HAL_UNLOCK(htim);
+
+  return status;
+}
+
+/**
+  * @brief  Initializes the TIM PWM  channels according to the specified
+  *         parameters in the TIM_OC_InitTypeDef.
+  * @param  htim TIM PWM handle
+  * @param  sConfig TIM PWM configuration structure
+  * @param  Channel TIM Channels to be configured
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
+  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim,
+                                            const TIM_OC_InitTypeDef *sConfig,
+                                            uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CHANNELS(Channel));
+  assert_param(IS_TIM_PWM_MODE(sConfig->OCMode));
+  assert_param(IS_TIM_OC_POLARITY(sConfig->OCPolarity));
+  assert_param(IS_TIM_FAST_STATE(sConfig->OCFastMode));
+
+  /* Process Locked */
+  __HAL_LOCK(htim);
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
+
+      /* Configure the Channel 1 in PWM mode */
+      TIM_OC1_SetConfig(htim->Instance, sConfig);
+
+      /* Set the Preload enable bit for channel1 */
+      htim->Instance->CCMR1 |= TIM_CCMR1_OC1PE;
+
+      /* Configure the Output Fast mode */
+      htim->Instance->CCMR1 &= ~TIM_CCMR1_OC1FE;
+      htim->Instance->CCMR1 |= sConfig->OCFastMode;
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
+
+      /* Configure the Channel 2 in PWM mode */
+      TIM_OC2_SetConfig(htim->Instance, sConfig);
+
+      /* Set the Preload enable bit for channel2 */
+      htim->Instance->CCMR1 |= TIM_CCMR1_OC2PE;
+
+      /* Configure the Output Fast mode */
+      htim->Instance->CCMR1 &= ~TIM_CCMR1_OC2FE;
+      htim->Instance->CCMR1 |= sConfig->OCFastMode << 8U;
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC3_INSTANCE(htim->Instance));
+
+      /* Configure the Channel 3 in PWM mode */
+      TIM_OC3_SetConfig(htim->Instance, sConfig);
+
+      /* Set the Preload enable bit for channel3 */
+      htim->Instance->CCMR2 |= TIM_CCMR2_OC3PE;
+
+      /* Configure the Output Fast mode */
+      htim->Instance->CCMR2 &= ~TIM_CCMR2_OC3FE;
+      htim->Instance->CCMR2 |= sConfig->OCFastMode;
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC4_INSTANCE(htim->Instance));
+
+      /* Configure the Channel 4 in PWM mode */
+      TIM_OC4_SetConfig(htim->Instance, sConfig);
+
+      /* Set the Preload enable bit for channel4 */
+      htim->Instance->CCMR2 |= TIM_CCMR2_OC4PE;
+
+      /* Configure the Output Fast mode */
+      htim->Instance->CCMR2 &= ~TIM_CCMR2_OC4FE;
+      htim->Instance->CCMR2 |= sConfig->OCFastMode << 8U;
+      break;
+    }
+
+    case TIM_CHANNEL_5:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC5_INSTANCE(htim->Instance));
+
+      /* Configure the Channel 5 in PWM mode */
+      TIM_OC5_SetConfig(htim->Instance, sConfig);
+
+      /* Set the Preload enable bit for channel5*/
+      htim->Instance->CCMR3 |= TIM_CCMR3_OC5PE;
+
+      /* Configure the Output Fast mode */
+      htim->Instance->CCMR3 &= ~TIM_CCMR3_OC5FE;
+      htim->Instance->CCMR3 |= sConfig->OCFastMode;
+      break;
+    }
+
+    case TIM_CHANNEL_6:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC6_INSTANCE(htim->Instance));
+
+      /* Configure the Channel 6 in PWM mode */
+      TIM_OC6_SetConfig(htim->Instance, sConfig);
+
+      /* Set the Preload enable bit for channel6 */
+      htim->Instance->CCMR3 |= TIM_CCMR3_OC6PE;
+
+      /* Configure the Output Fast mode */
+      htim->Instance->CCMR3 &= ~TIM_CCMR3_OC6FE;
+      htim->Instance->CCMR3 |= sConfig->OCFastMode << 8U;
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  __HAL_UNLOCK(htim);
+
+  return status;
+}
+
+/**
+  * @brief  Initializes the TIM One Pulse Channels according to the specified
+  *         parameters in the TIM_OnePulse_InitTypeDef.
+  * @param  htim TIM One Pulse handle
+  * @param  sConfig TIM One Pulse configuration structure
+  * @param  OutputChannel TIM output channel to configure
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  * @param  InputChannel TIM input Channel to configure
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  * @note  To output a waveform with a minimum delay user can enable the fast
+  *        mode by calling the @ref __HAL_TIM_ENABLE_OCxFAST macro. Then CCx
+  *        output is forced in response to the edge detection on TIx input,
+  *        without taking in account the comparison.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_OnePulse_ConfigChannel(TIM_HandleTypeDef *htim,  TIM_OnePulse_InitTypeDef *sConfig,
+                                                 uint32_t OutputChannel,  uint32_t InputChannel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  TIM_OC_InitTypeDef temp1;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_OPM_CHANNELS(OutputChannel));
+  assert_param(IS_TIM_OPM_CHANNELS(InputChannel));
+
+  if (OutputChannel != InputChannel)
+  {
+    /* Process Locked */
+    __HAL_LOCK(htim);
+
+    htim->State = HAL_TIM_STATE_BUSY;
+
+    /* Extract the Output compare configuration from sConfig structure */
+    temp1.OCMode = sConfig->OCMode;
+    temp1.Pulse = sConfig->Pulse;
+    temp1.OCPolarity = sConfig->OCPolarity;
+    temp1.OCNPolarity = sConfig->OCNPolarity;
+    temp1.OCIdleState = sConfig->OCIdleState;
+    temp1.OCNIdleState = sConfig->OCNIdleState;
+
+    switch (OutputChannel)
+    {
+      case TIM_CHANNEL_1:
+      {
+        assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
+
+        TIM_OC1_SetConfig(htim->Instance, &temp1);
+        break;
+      }
+
+      case TIM_CHANNEL_2:
+      {
+        assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
+
+        TIM_OC2_SetConfig(htim->Instance, &temp1);
+        break;
+      }
+
+      default:
+        status = HAL_ERROR;
+        break;
+    }
+
+    if (status == HAL_OK)
+    {
+      switch (InputChannel)
+      {
+        case TIM_CHANNEL_1:
+        {
+          assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
+
+          TIM_TI1_SetConfig(htim->Instance, sConfig->ICPolarity,
+                            sConfig->ICSelection, sConfig->ICFilter);
+
+          /* Reset the IC1PSC Bits */
+          htim->Instance->CCMR1 &= ~TIM_CCMR1_IC1PSC;
+
+          /* Select the Trigger source */
+          htim->Instance->SMCR &= ~TIM_SMCR_TS;
+          htim->Instance->SMCR |= TIM_TS_TI1FP1;
+
+          /* Select the Slave Mode */
+          htim->Instance->SMCR &= ~TIM_SMCR_SMS;
+          htim->Instance->SMCR |= TIM_SLAVEMODE_TRIGGER;
+          break;
+        }
+
+        case TIM_CHANNEL_2:
+        {
+          assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
+
+          TIM_TI2_SetConfig(htim->Instance, sConfig->ICPolarity,
+                            sConfig->ICSelection, sConfig->ICFilter);
+
+          /* Reset the IC2PSC Bits */
+          htim->Instance->CCMR1 &= ~TIM_CCMR1_IC2PSC;
+
+          /* Select the Trigger source */
+          htim->Instance->SMCR &= ~TIM_SMCR_TS;
+          htim->Instance->SMCR |= TIM_TS_TI2FP2;
+
+          /* Select the Slave Mode */
+          htim->Instance->SMCR &= ~TIM_SMCR_SMS;
+          htim->Instance->SMCR |= TIM_SLAVEMODE_TRIGGER;
+          break;
+        }
+
+        default:
+          status = HAL_ERROR;
+          break;
+      }
+    }
+
+    htim->State = HAL_TIM_STATE_READY;
+
+    __HAL_UNLOCK(htim);
+
+    return status;
+  }
+  else
+  {
+    return HAL_ERROR;
+  }
+}
+
+/**
+  * @brief  Configure the DMA Burst to transfer Data from the memory to the TIM peripheral
+  * @param  htim TIM handle
+  * @param  BurstBaseAddress TIM Base address from where the DMA  will start the Data write
+  *         This parameter can be one of the following values:
+  *            @arg TIM_DMABASE_CR1
+  *            @arg TIM_DMABASE_CR2
+  *            @arg TIM_DMABASE_SMCR
+  *            @arg TIM_DMABASE_DIER
+  *            @arg TIM_DMABASE_SR
+  *            @arg TIM_DMABASE_EGR
+  *            @arg TIM_DMABASE_CCMR1
+  *            @arg TIM_DMABASE_CCMR2
+  *            @arg TIM_DMABASE_CCER
+  *            @arg TIM_DMABASE_CNT
+  *            @arg TIM_DMABASE_PSC
+  *            @arg TIM_DMABASE_ARR
+  *            @arg TIM_DMABASE_RCR
+  *            @arg TIM_DMABASE_CCR1
+  *            @arg TIM_DMABASE_CCR2
+  *            @arg TIM_DMABASE_CCR3
+  *            @arg TIM_DMABASE_CCR4
+  *            @arg TIM_DMABASE_BDTR
+  *            @arg TIM_DMABASE_CCMR3
+  *            @arg TIM_DMABASE_CCR5
+  *            @arg TIM_DMABASE_CCR6
+  *            @arg TIM_DMABASE_AF1
+  *            @arg TIM_DMABASE_AF2
+  *            @arg TIM_DMABASE_TISEL
+  *
+  * @param  BurstRequestSrc TIM DMA Request sources
+  *         This parameter can be one of the following values:
+  *            @arg TIM_DMA_UPDATE: TIM update Interrupt source
+  *            @arg TIM_DMA_CC1: TIM Capture Compare 1 DMA source
+  *            @arg TIM_DMA_CC2: TIM Capture Compare 2 DMA source
+  *            @arg TIM_DMA_CC3: TIM Capture Compare 3 DMA source
+  *            @arg TIM_DMA_CC4: TIM Capture Compare 4 DMA source
+  *            @arg TIM_DMA_COM: TIM Commutation DMA source
+  *            @arg TIM_DMA_TRIGGER: TIM Trigger DMA source
+  * @param  BurstBuffer The Buffer address.
+  * @param  BurstLength DMA Burst length. This parameter can be one value
+  *         between: TIM_DMABURSTLENGTH_1TRANSFER and TIM_DMABURSTLENGTH_18TRANSFERS.
+  * @note   This function should be used only when BurstLength is equal to DMA data transfer length.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_DMABurst_WriteStart(TIM_HandleTypeDef *htim, uint32_t BurstBaseAddress,
+                                              uint32_t BurstRequestSrc, const uint32_t *BurstBuffer,
+                                              uint32_t  BurstLength)
+{
+  HAL_StatusTypeDef status;
+
+  status = HAL_TIM_DMABurst_MultiWriteStart(htim, BurstBaseAddress, BurstRequestSrc, BurstBuffer, BurstLength,
+                                            ((BurstLength) >> 8U) + 1U);
+
+
+
+  return status;
+}
+
+/**
+  * @brief  Configure the DMA Burst to transfer multiple Data from the memory to the TIM peripheral
+  * @param  htim TIM handle
+  * @param  BurstBaseAddress TIM Base address from where the DMA will start the Data write
+  *         This parameter can be one of the following values:
+  *            @arg TIM_DMABASE_CR1
+  *            @arg TIM_DMABASE_CR2
+  *            @arg TIM_DMABASE_SMCR
+  *            @arg TIM_DMABASE_DIER
+  *            @arg TIM_DMABASE_SR
+  *            @arg TIM_DMABASE_EGR
+  *            @arg TIM_DMABASE_CCMR1
+  *            @arg TIM_DMABASE_CCMR2
+  *            @arg TIM_DMABASE_CCER
+  *            @arg TIM_DMABASE_CNT
+  *            @arg TIM_DMABASE_PSC
+  *            @arg TIM_DMABASE_ARR
+  *            @arg TIM_DMABASE_RCR
+  *            @arg TIM_DMABASE_CCR1
+  *            @arg TIM_DMABASE_CCR2
+  *            @arg TIM_DMABASE_CCR3
+  *            @arg TIM_DMABASE_CCR4
+  *            @arg TIM_DMABASE_BDTR
+  *            @arg TIM_DMABASE_CCMR3
+  *            @arg TIM_DMABASE_CCR5
+  *            @arg TIM_DMABASE_CCR6
+  *            @arg TIM_DMABASE_AF1
+  *            @arg TIM_DMABASE_AF2
+  *            @arg TIM_DMABASE_TISEL
+  *
+  * @param  BurstRequestSrc TIM DMA Request sources
+  *         This parameter can be one of the following values:
+  *            @arg TIM_DMA_UPDATE: TIM update Interrupt source
+  *            @arg TIM_DMA_CC1: TIM Capture Compare 1 DMA source
+  *            @arg TIM_DMA_CC2: TIM Capture Compare 2 DMA source
+  *            @arg TIM_DMA_CC3: TIM Capture Compare 3 DMA source
+  *            @arg TIM_DMA_CC4: TIM Capture Compare 4 DMA source
+  *            @arg TIM_DMA_COM: TIM Commutation DMA source
+  *            @arg TIM_DMA_TRIGGER: TIM Trigger DMA source
+  * @param  BurstBuffer The Buffer address.
+  * @param  BurstLength DMA Burst length. This parameter can be one value
+  *         between: TIM_DMABURSTLENGTH_1TRANSFER and TIM_DMABURSTLENGTH_18TRANSFERS.
+  * @param  DataLength Data length. This parameter can be one value
+  *         between 1 and 0xFFFF.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_DMABurst_MultiWriteStart(TIM_HandleTypeDef *htim, uint32_t BurstBaseAddress,
+                                                   uint32_t BurstRequestSrc, const uint32_t *BurstBuffer,
+                                                   uint32_t  BurstLength,  uint32_t  DataLength)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_DMABURST_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_DMA_BASE(BurstBaseAddress));
+  assert_param(IS_TIM_DMA_SOURCE(BurstRequestSrc));
+  assert_param(IS_TIM_DMA_LENGTH(BurstLength));
+  assert_param(IS_TIM_DMA_DATA_LENGTH(DataLength));
+
+  if (htim->DMABurstState == HAL_DMA_BURST_STATE_BUSY)
+  {
+    return HAL_BUSY;
+  }
+  else if (htim->DMABurstState == HAL_DMA_BURST_STATE_READY)
+  {
+    if ((BurstBuffer == NULL) && (BurstLength > 0U))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      htim->DMABurstState = HAL_DMA_BURST_STATE_BUSY;
+    }
+  }
+  else
+  {
+    /* nothing to do */
+  }
+
+  switch (BurstRequestSrc)
+  {
+    case TIM_DMA_UPDATE:
+    {
+      /* Set the DMA Period elapsed callbacks */
+      htim->hdma[TIM_DMA_ID_UPDATE]->XferCpltCallback = TIM_DMAPeriodElapsedCplt;
+      htim->hdma[TIM_DMA_ID_UPDATE]->XferHalfCpltCallback = TIM_DMAPeriodElapsedHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_UPDATE]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_UPDATE], (uint32_t)BurstBuffer,
+                           (uint32_t)&htim->Instance->DMAR, DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    case TIM_DMA_CC1:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC1]->XferCpltCallback = TIM_DMADelayPulseCplt;
+      htim->hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC1]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)BurstBuffer,
+                           (uint32_t)&htim->Instance->DMAR, DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    case TIM_DMA_CC2:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC2]->XferCpltCallback = TIM_DMADelayPulseCplt;
+      htim->hdma[TIM_DMA_ID_CC2]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC2]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC2], (uint32_t)BurstBuffer,
+                           (uint32_t)&htim->Instance->DMAR, DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    case TIM_DMA_CC3:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC3]->XferCpltCallback = TIM_DMADelayPulseCplt;
+      htim->hdma[TIM_DMA_ID_CC3]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC3]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC3], (uint32_t)BurstBuffer,
+                           (uint32_t)&htim->Instance->DMAR, DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    case TIM_DMA_CC4:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC4]->XferCpltCallback = TIM_DMADelayPulseCplt;
+      htim->hdma[TIM_DMA_ID_CC4]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC4]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC4], (uint32_t)BurstBuffer,
+                           (uint32_t)&htim->Instance->DMAR, DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    case TIM_DMA_COM:
+    {
+      /* Set the DMA commutation callbacks */
+      htim->hdma[TIM_DMA_ID_COMMUTATION]->XferCpltCallback =  TIMEx_DMACommutationCplt;
+      htim->hdma[TIM_DMA_ID_COMMUTATION]->XferHalfCpltCallback =  TIMEx_DMACommutationHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_COMMUTATION]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_COMMUTATION], (uint32_t)BurstBuffer,
+                           (uint32_t)&htim->Instance->DMAR, DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    case TIM_DMA_TRIGGER:
+    {
+      /* Set the DMA trigger callbacks */
+      htim->hdma[TIM_DMA_ID_TRIGGER]->XferCpltCallback = TIM_DMATriggerCplt;
+      htim->hdma[TIM_DMA_ID_TRIGGER]->XferHalfCpltCallback = TIM_DMATriggerHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_TRIGGER]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_TRIGGER], (uint32_t)BurstBuffer,
+                           (uint32_t)&htim->Instance->DMAR, DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Configure the DMA Burst Mode */
+    htim->Instance->DCR = (BurstBaseAddress | BurstLength);
+    /* Enable the TIM DMA Request */
+    __HAL_TIM_ENABLE_DMA(htim, BurstRequestSrc);
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Stops the TIM DMA Burst mode
+  * @param  htim TIM handle
+  * @param  BurstRequestSrc TIM DMA Request sources to disable
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_DMABurst_WriteStop(TIM_HandleTypeDef *htim, uint32_t BurstRequestSrc)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_DMA_SOURCE(BurstRequestSrc));
+
+  /* Abort the DMA transfer (at least disable the DMA stream) */
+  switch (BurstRequestSrc)
+  {
+    case TIM_DMA_UPDATE:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_UPDATE]);
+      break;
+    }
+    case TIM_DMA_CC1:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC1]);
+      break;
+    }
+    case TIM_DMA_CC2:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC2]);
+      break;
+    }
+    case TIM_DMA_CC3:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC3]);
+      break;
+    }
+    case TIM_DMA_CC4:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC4]);
+      break;
+    }
+    case TIM_DMA_COM:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_COMMUTATION]);
+      break;
+    }
+    case TIM_DMA_TRIGGER:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_TRIGGER]);
+      break;
+    }
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Disable the TIM Update DMA request */
+    __HAL_TIM_DISABLE_DMA(htim, BurstRequestSrc);
+
+    /* Change the DMA burst operation state */
+    htim->DMABurstState = HAL_DMA_BURST_STATE_READY;
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Configure the DMA Burst to transfer Data from the TIM peripheral to the memory
+  * @param  htim TIM handle
+  * @param  BurstBaseAddress TIM Base address from where the DMA  will start the Data read
+  *         This parameter can be one of the following values:
+  *            @arg TIM_DMABASE_CR1
+  *            @arg TIM_DMABASE_CR2
+  *            @arg TIM_DMABASE_SMCR
+  *            @arg TIM_DMABASE_DIER
+  *            @arg TIM_DMABASE_SR
+  *            @arg TIM_DMABASE_EGR
+  *            @arg TIM_DMABASE_CCMR1
+  *            @arg TIM_DMABASE_CCMR2
+  *            @arg TIM_DMABASE_CCER
+  *            @arg TIM_DMABASE_CNT
+  *            @arg TIM_DMABASE_PSC
+  *            @arg TIM_DMABASE_ARR
+  *            @arg TIM_DMABASE_RCR
+  *            @arg TIM_DMABASE_CCR1
+  *            @arg TIM_DMABASE_CCR2
+  *            @arg TIM_DMABASE_CCR3
+  *            @arg TIM_DMABASE_CCR4
+  *            @arg TIM_DMABASE_BDTR
+  *            @arg TIM_DMABASE_CCMR3
+  *            @arg TIM_DMABASE_CCR5
+  *            @arg TIM_DMABASE_CCR6
+  *            @arg TIM_DMABASE_AF1
+  *            @arg TIM_DMABASE_AF2
+  *            @arg TIM_DMABASE_TISEL
+  *
+  * @param  BurstRequestSrc TIM DMA Request sources
+  *         This parameter can be one of the following values:
+  *            @arg TIM_DMA_UPDATE: TIM update Interrupt source
+  *            @arg TIM_DMA_CC1: TIM Capture Compare 1 DMA source
+  *            @arg TIM_DMA_CC2: TIM Capture Compare 2 DMA source
+  *            @arg TIM_DMA_CC3: TIM Capture Compare 3 DMA source
+  *            @arg TIM_DMA_CC4: TIM Capture Compare 4 DMA source
+  *            @arg TIM_DMA_COM: TIM Commutation DMA source
+  *            @arg TIM_DMA_TRIGGER: TIM Trigger DMA source
+  * @param  BurstBuffer The Buffer address.
+  * @param  BurstLength DMA Burst length. This parameter can be one value
+  *         between: TIM_DMABURSTLENGTH_1TRANSFER and TIM_DMABURSTLENGTH_18TRANSFERS.
+  * @note   This function should be used only when BurstLength is equal to DMA data transfer length.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_DMABurst_ReadStart(TIM_HandleTypeDef *htim, uint32_t BurstBaseAddress,
+                                             uint32_t BurstRequestSrc, uint32_t  *BurstBuffer, uint32_t  BurstLength)
+{
+  HAL_StatusTypeDef status;
+
+  status = HAL_TIM_DMABurst_MultiReadStart(htim, BurstBaseAddress, BurstRequestSrc, BurstBuffer, BurstLength,
+                                           ((BurstLength) >> 8U) + 1U);
+
+
+  return status;
+}
+
+/**
+  * @brief  Configure the DMA Burst to transfer Data from the TIM peripheral to the memory
+  * @param  htim TIM handle
+  * @param  BurstBaseAddress TIM Base address from where the DMA  will start the Data read
+  *         This parameter can be one of the following values:
+  *            @arg TIM_DMABASE_CR1
+  *            @arg TIM_DMABASE_CR2
+  *            @arg TIM_DMABASE_SMCR
+  *            @arg TIM_DMABASE_DIER
+  *            @arg TIM_DMABASE_SR
+  *            @arg TIM_DMABASE_EGR
+  *            @arg TIM_DMABASE_CCMR1
+  *            @arg TIM_DMABASE_CCMR2
+  *            @arg TIM_DMABASE_CCER
+  *            @arg TIM_DMABASE_CNT
+  *            @arg TIM_DMABASE_PSC
+  *            @arg TIM_DMABASE_ARR
+  *            @arg TIM_DMABASE_RCR
+  *            @arg TIM_DMABASE_CCR1
+  *            @arg TIM_DMABASE_CCR2
+  *            @arg TIM_DMABASE_CCR3
+  *            @arg TIM_DMABASE_CCR4
+  *            @arg TIM_DMABASE_BDTR
+  *            @arg TIM_DMABASE_CCMR3
+  *            @arg TIM_DMABASE_CCR5
+  *            @arg TIM_DMABASE_CCR6
+  *            @arg TIM_DMABASE_AF1
+  *            @arg TIM_DMABASE_AF2
+  *            @arg TIM_DMABASE_TISEL
+  *
+  * @param  BurstRequestSrc TIM DMA Request sources
+  *         This parameter can be one of the following values:
+  *            @arg TIM_DMA_UPDATE: TIM update Interrupt source
+  *            @arg TIM_DMA_CC1: TIM Capture Compare 1 DMA source
+  *            @arg TIM_DMA_CC2: TIM Capture Compare 2 DMA source
+  *            @arg TIM_DMA_CC3: TIM Capture Compare 3 DMA source
+  *            @arg TIM_DMA_CC4: TIM Capture Compare 4 DMA source
+  *            @arg TIM_DMA_COM: TIM Commutation DMA source
+  *            @arg TIM_DMA_TRIGGER: TIM Trigger DMA source
+  * @param  BurstBuffer The Buffer address.
+  * @param  BurstLength DMA Burst length. This parameter can be one value
+  *         between: TIM_DMABURSTLENGTH_1TRANSFER and TIM_DMABURSTLENGTH_18TRANSFERS.
+  * @param  DataLength Data length. This parameter can be one value
+  *         between 1 and 0xFFFF.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_DMABurst_MultiReadStart(TIM_HandleTypeDef *htim, uint32_t BurstBaseAddress,
+                                                  uint32_t BurstRequestSrc, uint32_t  *BurstBuffer,
+                                                  uint32_t  BurstLength, uint32_t  DataLength)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_DMABURST_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_DMA_BASE(BurstBaseAddress));
+  assert_param(IS_TIM_DMA_SOURCE(BurstRequestSrc));
+  assert_param(IS_TIM_DMA_LENGTH(BurstLength));
+  assert_param(IS_TIM_DMA_DATA_LENGTH(DataLength));
+
+  if (htim->DMABurstState == HAL_DMA_BURST_STATE_BUSY)
+  {
+    return HAL_BUSY;
+  }
+  else if (htim->DMABurstState == HAL_DMA_BURST_STATE_READY)
+  {
+    if ((BurstBuffer == NULL) && (BurstLength > 0U))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      htim->DMABurstState = HAL_DMA_BURST_STATE_BUSY;
+    }
+  }
+  else
+  {
+    /* nothing to do */
+  }
+  switch (BurstRequestSrc)
+  {
+    case TIM_DMA_UPDATE:
+    {
+      /* Set the DMA Period elapsed callbacks */
+      htim->hdma[TIM_DMA_ID_UPDATE]->XferCpltCallback = TIM_DMAPeriodElapsedCplt;
+      htim->hdma[TIM_DMA_ID_UPDATE]->XferHalfCpltCallback = TIM_DMAPeriodElapsedHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_UPDATE]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_UPDATE], (uint32_t)&htim->Instance->DMAR, (uint32_t)BurstBuffer,
+                           DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    case TIM_DMA_CC1:
+    {
+      /* Set the DMA capture callbacks */
+      htim->hdma[TIM_DMA_ID_CC1]->XferCpltCallback = TIM_DMACaptureCplt;
+      htim->hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = TIM_DMACaptureHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC1]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)&htim->Instance->DMAR, (uint32_t)BurstBuffer,
+                           DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    case TIM_DMA_CC2:
+    {
+      /* Set the DMA capture callbacks */
+      htim->hdma[TIM_DMA_ID_CC2]->XferCpltCallback = TIM_DMACaptureCplt;
+      htim->hdma[TIM_DMA_ID_CC2]->XferHalfCpltCallback = TIM_DMACaptureHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC2]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC2], (uint32_t)&htim->Instance->DMAR, (uint32_t)BurstBuffer,
+                           DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    case TIM_DMA_CC3:
+    {
+      /* Set the DMA capture callbacks */
+      htim->hdma[TIM_DMA_ID_CC3]->XferCpltCallback = TIM_DMACaptureCplt;
+      htim->hdma[TIM_DMA_ID_CC3]->XferHalfCpltCallback = TIM_DMACaptureHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC3]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC3], (uint32_t)&htim->Instance->DMAR, (uint32_t)BurstBuffer,
+                           DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    case TIM_DMA_CC4:
+    {
+      /* Set the DMA capture callbacks */
+      htim->hdma[TIM_DMA_ID_CC4]->XferCpltCallback = TIM_DMACaptureCplt;
+      htim->hdma[TIM_DMA_ID_CC4]->XferHalfCpltCallback = TIM_DMACaptureHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC4]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC4], (uint32_t)&htim->Instance->DMAR, (uint32_t)BurstBuffer,
+                           DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    case TIM_DMA_COM:
+    {
+      /* Set the DMA commutation callbacks */
+      htim->hdma[TIM_DMA_ID_COMMUTATION]->XferCpltCallback =  TIMEx_DMACommutationCplt;
+      htim->hdma[TIM_DMA_ID_COMMUTATION]->XferHalfCpltCallback =  TIMEx_DMACommutationHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_COMMUTATION]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_COMMUTATION], (uint32_t)&htim->Instance->DMAR, (uint32_t)BurstBuffer,
+                           DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    case TIM_DMA_TRIGGER:
+    {
+      /* Set the DMA trigger callbacks */
+      htim->hdma[TIM_DMA_ID_TRIGGER]->XferCpltCallback = TIM_DMATriggerCplt;
+      htim->hdma[TIM_DMA_ID_TRIGGER]->XferHalfCpltCallback = TIM_DMATriggerHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_TRIGGER]->XferErrorCallback = TIM_DMAError ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_TRIGGER], (uint32_t)&htim->Instance->DMAR, (uint32_t)BurstBuffer,
+                           DataLength) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      break;
+    }
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Configure the DMA Burst Mode */
+    htim->Instance->DCR = (BurstBaseAddress | BurstLength);
+
+    /* Enable the TIM DMA Request */
+    __HAL_TIM_ENABLE_DMA(htim, BurstRequestSrc);
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Stop the DMA burst reading
+  * @param  htim TIM handle
+  * @param  BurstRequestSrc TIM DMA Request sources to disable.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_DMABurst_ReadStop(TIM_HandleTypeDef *htim, uint32_t BurstRequestSrc)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_DMA_SOURCE(BurstRequestSrc));
+
+  /* Abort the DMA transfer (at least disable the DMA stream) */
+  switch (BurstRequestSrc)
+  {
+    case TIM_DMA_UPDATE:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_UPDATE]);
+      break;
+    }
+    case TIM_DMA_CC1:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC1]);
+      break;
+    }
+    case TIM_DMA_CC2:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC2]);
+      break;
+    }
+    case TIM_DMA_CC3:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC3]);
+      break;
+    }
+    case TIM_DMA_CC4:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC4]);
+      break;
+    }
+    case TIM_DMA_COM:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_COMMUTATION]);
+      break;
+    }
+    case TIM_DMA_TRIGGER:
+    {
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_TRIGGER]);
+      break;
+    }
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Disable the TIM Update DMA request */
+    __HAL_TIM_DISABLE_DMA(htim, BurstRequestSrc);
+
+    /* Change the DMA burst operation state */
+    htim->DMABurstState = HAL_DMA_BURST_STATE_READY;
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Generate a software event
+  * @param  htim TIM handle
+  * @param  EventSource specifies the event source.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_EVENTSOURCE_UPDATE: Timer update Event source
+  *            @arg TIM_EVENTSOURCE_CC1: Timer Capture Compare 1 Event source
+  *            @arg TIM_EVENTSOURCE_CC2: Timer Capture Compare 2 Event source
+  *            @arg TIM_EVENTSOURCE_CC3: Timer Capture Compare 3 Event source
+  *            @arg TIM_EVENTSOURCE_CC4: Timer Capture Compare 4 Event source
+  *            @arg TIM_EVENTSOURCE_COM: Timer COM event source
+  *            @arg TIM_EVENTSOURCE_TRIGGER: Timer Trigger Event source
+  *            @arg TIM_EVENTSOURCE_BREAK: Timer Break event source
+  *            @arg TIM_EVENTSOURCE_BREAK2: Timer Break2 event source
+  * @note   Basic timers can only generate an update event.
+  * @note   TIM_EVENTSOURCE_COM is relevant only with advanced timer instances.
+  * @note   TIM_EVENTSOURCE_BREAK and TIM_EVENTSOURCE_BREAK2 are relevant
+  *         only for timer instances supporting break input(s).
+  * @retval HAL status
+  */
+
+HAL_StatusTypeDef HAL_TIM_GenerateEvent(TIM_HandleTypeDef *htim, uint32_t EventSource)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_EVENT_SOURCE(EventSource));
+
+  /* Process Locked */
+  __HAL_LOCK(htim);
+
+  /* Change the TIM state */
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Set the event sources */
+  htim->Instance->EGR = EventSource;
+
+  /* Change the TIM state */
+  htim->State = HAL_TIM_STATE_READY;
+
+  __HAL_UNLOCK(htim);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Configures the OCRef clear feature
+  * @param  htim TIM handle
+  * @param  sClearInputConfig pointer to a TIM_ClearInputConfigTypeDef structure that
+  *         contains the OCREF clear feature and parameters for the TIM peripheral.
+  * @param  Channel specifies the TIM Channel
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1
+  *            @arg TIM_CHANNEL_2: TIM Channel 2
+  *            @arg TIM_CHANNEL_3: TIM Channel 3
+  *            @arg TIM_CHANNEL_4: TIM Channel 4
+  *            @arg TIM_CHANNEL_5: TIM Channel 5
+  *            @arg TIM_CHANNEL_6: TIM Channel 6
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_ConfigOCrefClear(TIM_HandleTypeDef *htim,
+                                           const TIM_ClearInputConfigTypeDef *sClearInputConfig,
+                                           uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_OCXREF_CLEAR_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_CLEARINPUT_SOURCE(sClearInputConfig->ClearInputSource));
+
+  /* Process Locked */
+  __HAL_LOCK(htim);
+
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  switch (sClearInputConfig->ClearInputSource)
+  {
+    case TIM_CLEARINPUTSOURCE_NONE:
+    {
+      /* Clear the OCREF clear selection bit and the the ETR Bits */
+      CLEAR_BIT(htim->Instance->SMCR, (TIM_SMCR_ETF | TIM_SMCR_ETPS | TIM_SMCR_ECE | TIM_SMCR_ETP));
+      break;
+    }
+
+    case TIM_CLEARINPUTSOURCE_ETR:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CLEARINPUT_POLARITY(sClearInputConfig->ClearInputPolarity));
+      assert_param(IS_TIM_CLEARINPUT_PRESCALER(sClearInputConfig->ClearInputPrescaler));
+      assert_param(IS_TIM_CLEARINPUT_FILTER(sClearInputConfig->ClearInputFilter));
+
+      /* When OCRef clear feature is used with ETR source, ETR prescaler must be off */
+      if (sClearInputConfig->ClearInputPrescaler != TIM_CLEARINPUTPRESCALER_DIV1)
+      {
+        htim->State = HAL_TIM_STATE_READY;
+        __HAL_UNLOCK(htim);
+        return HAL_ERROR;
+      }
+
+      TIM_ETR_SetConfig(htim->Instance,
+                        sClearInputConfig->ClearInputPrescaler,
+                        sClearInputConfig->ClearInputPolarity,
+                        sClearInputConfig->ClearInputFilter);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    switch (Channel)
+    {
+      case TIM_CHANNEL_1:
+      {
+        if (sClearInputConfig->ClearInputState != (uint32_t)DISABLE)
+        {
+          /* Enable the OCREF clear feature for Channel 1 */
+          SET_BIT(htim->Instance->CCMR1, TIM_CCMR1_OC1CE);
+        }
+        else
+        {
+          /* Disable the OCREF clear feature for Channel 1 */
+          CLEAR_BIT(htim->Instance->CCMR1, TIM_CCMR1_OC1CE);
+        }
+        break;
+      }
+      case TIM_CHANNEL_2:
+      {
+        if (sClearInputConfig->ClearInputState != (uint32_t)DISABLE)
+        {
+          /* Enable the OCREF clear feature for Channel 2 */
+          SET_BIT(htim->Instance->CCMR1, TIM_CCMR1_OC2CE);
+        }
+        else
+        {
+          /* Disable the OCREF clear feature for Channel 2 */
+          CLEAR_BIT(htim->Instance->CCMR1, TIM_CCMR1_OC2CE);
+        }
+        break;
+      }
+      case TIM_CHANNEL_3:
+      {
+        if (sClearInputConfig->ClearInputState != (uint32_t)DISABLE)
+        {
+          /* Enable the OCREF clear feature for Channel 3 */
+          SET_BIT(htim->Instance->CCMR2, TIM_CCMR2_OC3CE);
+        }
+        else
+        {
+          /* Disable the OCREF clear feature for Channel 3 */
+          CLEAR_BIT(htim->Instance->CCMR2, TIM_CCMR2_OC3CE);
+        }
+        break;
+      }
+      case TIM_CHANNEL_4:
+      {
+        if (sClearInputConfig->ClearInputState != (uint32_t)DISABLE)
+        {
+          /* Enable the OCREF clear feature for Channel 4 */
+          SET_BIT(htim->Instance->CCMR2, TIM_CCMR2_OC4CE);
+        }
+        else
+        {
+          /* Disable the OCREF clear feature for Channel 4 */
+          CLEAR_BIT(htim->Instance->CCMR2, TIM_CCMR2_OC4CE);
+        }
+        break;
+      }
+      case TIM_CHANNEL_5:
+      {
+        if (sClearInputConfig->ClearInputState != (uint32_t)DISABLE)
+        {
+          /* Enable the OCREF clear feature for Channel 5 */
+          SET_BIT(htim->Instance->CCMR3, TIM_CCMR3_OC5CE);
+        }
+        else
+        {
+          /* Disable the OCREF clear feature for Channel 5 */
+          CLEAR_BIT(htim->Instance->CCMR3, TIM_CCMR3_OC5CE);
+        }
+        break;
+      }
+      case TIM_CHANNEL_6:
+      {
+        if (sClearInputConfig->ClearInputState != (uint32_t)DISABLE)
+        {
+          /* Enable the OCREF clear feature for Channel 6 */
+          SET_BIT(htim->Instance->CCMR3, TIM_CCMR3_OC6CE);
+        }
+        else
+        {
+          /* Disable the OCREF clear feature for Channel 6 */
+          CLEAR_BIT(htim->Instance->CCMR3, TIM_CCMR3_OC6CE);
+        }
+        break;
+      }
+      default:
+        break;
+    }
+  }
+
+  htim->State = HAL_TIM_STATE_READY;
+
+  __HAL_UNLOCK(htim);
+
+  return status;
+}
+
+/**
+  * @brief   Configures the clock source to be used
+  * @param  htim TIM handle
+  * @param  sClockSourceConfig pointer to a TIM_ClockConfigTypeDef structure that
+  *         contains the clock source information for the TIM peripheral.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_ConfigClockSource(TIM_HandleTypeDef *htim, const TIM_ClockConfigTypeDef *sClockSourceConfig)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpsmcr;
+
+  /* Process Locked */
+  __HAL_LOCK(htim);
+
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CLOCKSOURCE(sClockSourceConfig->ClockSource));
+
+  /* Reset the SMS, TS, ECE, ETPS and ETRF bits */
+  tmpsmcr = htim->Instance->SMCR;
+  tmpsmcr &= ~(TIM_SMCR_SMS | TIM_SMCR_TS);
+  tmpsmcr &= ~(TIM_SMCR_ETF | TIM_SMCR_ETPS | TIM_SMCR_ECE | TIM_SMCR_ETP);
+  htim->Instance->SMCR = tmpsmcr;
+
+  switch (sClockSourceConfig->ClockSource)
+  {
+    case TIM_CLOCKSOURCE_INTERNAL:
+    {
+      assert_param(IS_TIM_INSTANCE(htim->Instance));
+      break;
+    }
+
+    case TIM_CLOCKSOURCE_ETRMODE1:
+    {
+      /* Check whether or not the timer instance supports external trigger input mode 1 (ETRF)*/
+      assert_param(IS_TIM_CLOCKSOURCE_ETRMODE1_INSTANCE(htim->Instance));
+
+      /* Check ETR input conditioning related parameters */
+      assert_param(IS_TIM_CLOCKPRESCALER(sClockSourceConfig->ClockPrescaler));
+      assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
+      assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
+
+      /* Configure the ETR Clock source */
+      TIM_ETR_SetConfig(htim->Instance,
+                        sClockSourceConfig->ClockPrescaler,
+                        sClockSourceConfig->ClockPolarity,
+                        sClockSourceConfig->ClockFilter);
+
+      /* Select the External clock mode1 and the ETRF trigger */
+      tmpsmcr = htim->Instance->SMCR;
+      tmpsmcr |= (TIM_SLAVEMODE_EXTERNAL1 | TIM_CLOCKSOURCE_ETRMODE1);
+      /* Write to TIMx SMCR */
+      htim->Instance->SMCR = tmpsmcr;
+      break;
+    }
+
+    case TIM_CLOCKSOURCE_ETRMODE2:
+    {
+      /* Check whether or not the timer instance supports external trigger input mode 2 (ETRF)*/
+      assert_param(IS_TIM_CLOCKSOURCE_ETRMODE2_INSTANCE(htim->Instance));
+
+      /* Check ETR input conditioning related parameters */
+      assert_param(IS_TIM_CLOCKPRESCALER(sClockSourceConfig->ClockPrescaler));
+      assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
+      assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
+
+      /* Configure the ETR Clock source */
+      TIM_ETR_SetConfig(htim->Instance,
+                        sClockSourceConfig->ClockPrescaler,
+                        sClockSourceConfig->ClockPolarity,
+                        sClockSourceConfig->ClockFilter);
+      /* Enable the External clock mode2 */
+      htim->Instance->SMCR |= TIM_SMCR_ECE;
+      break;
+    }
+
+    case TIM_CLOCKSOURCE_TI1:
+    {
+      /* Check whether or not the timer instance supports external clock mode 1 */
+      assert_param(IS_TIM_CLOCKSOURCE_TIX_INSTANCE(htim->Instance));
+
+      /* Check TI1 input conditioning related parameters */
+      assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
+      assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
+
+      TIM_TI1_ConfigInputStage(htim->Instance,
+                               sClockSourceConfig->ClockPolarity,
+                               sClockSourceConfig->ClockFilter);
+      TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_TI1);
+      break;
+    }
+
+    case TIM_CLOCKSOURCE_TI2:
+    {
+      /* Check whether or not the timer instance supports external clock mode 1 (ETRF)*/
+      assert_param(IS_TIM_CLOCKSOURCE_TIX_INSTANCE(htim->Instance));
+
+      /* Check TI2 input conditioning related parameters */
+      assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
+      assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
+
+      TIM_TI2_ConfigInputStage(htim->Instance,
+                               sClockSourceConfig->ClockPolarity,
+                               sClockSourceConfig->ClockFilter);
+      TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_TI2);
+      break;
+    }
+
+    case TIM_CLOCKSOURCE_TI1ED:
+    {
+      /* Check whether or not the timer instance supports external clock mode 1 */
+      assert_param(IS_TIM_CLOCKSOURCE_TIX_INSTANCE(htim->Instance));
+
+      /* Check TI1 input conditioning related parameters */
+      assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
+      assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
+
+      TIM_TI1_ConfigInputStage(htim->Instance,
+                               sClockSourceConfig->ClockPolarity,
+                               sClockSourceConfig->ClockFilter);
+      TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_TI1ED);
+      break;
+    }
+
+    case TIM_CLOCKSOURCE_ITR0:
+    case TIM_CLOCKSOURCE_ITR1:
+    case TIM_CLOCKSOURCE_ITR2:
+    case TIM_CLOCKSOURCE_ITR3:
+    case TIM_CLOCKSOURCE_ITR4:
+    case TIM_CLOCKSOURCE_ITR5:
+    case TIM_CLOCKSOURCE_ITR6:
+    case TIM_CLOCKSOURCE_ITR7:
+    case TIM_CLOCKSOURCE_ITR8:
+    {
+      /* Check whether or not the timer instance supports internal trigger input */
+      assert_param(IS_TIM_CLOCKSOURCE_ITRX_INSTANCE(htim->Instance));
+
+      TIM_ITRx_SetConfig(htim->Instance, sClockSourceConfig->ClockSource);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+  htim->State = HAL_TIM_STATE_READY;
+
+  __HAL_UNLOCK(htim);
+
+  return status;
+}
+
+/**
+  * @brief  Selects the signal connected to the TI1 input: direct from CH1_input
+  *         or a XOR combination between CH1_input, CH2_input & CH3_input
+  * @param  htim TIM handle.
+  * @param  TI1_Selection Indicate whether or not channel 1 is connected to the
+  *         output of a XOR gate.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_TI1SELECTION_CH1: The TIMx_CH1 pin is connected to TI1 input
+  *            @arg TIM_TI1SELECTION_XORCOMBINATION: The TIMx_CH1, CH2 and CH3
+  *            pins are connected to the TI1 input (XOR combination)
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_ConfigTI1Input(TIM_HandleTypeDef *htim, uint32_t TI1_Selection)
+{
+  uint32_t tmpcr2;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_XOR_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_TI1SELECTION(TI1_Selection));
+
+  /* Get the TIMx CR2 register value */
+  tmpcr2 = htim->Instance->CR2;
+
+  /* Reset the TI1 selection */
+  tmpcr2 &= ~TIM_CR2_TI1S;
+
+  /* Set the TI1 selection */
+  tmpcr2 |= TI1_Selection;
+
+  /* Write to TIMxCR2 */
+  htim->Instance->CR2 = tmpcr2;
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Configures the TIM in Slave mode
+  * @param  htim TIM handle.
+  * @param  sSlaveConfig pointer to a TIM_SlaveConfigTypeDef structure that
+  *         contains the selected trigger (internal trigger input, filtered
+  *         timer input or external trigger input) and the Slave mode
+  *         (Disable, Reset, Gated, Trigger, External clock mode 1).
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_SlaveConfigSynchro(TIM_HandleTypeDef *htim, const TIM_SlaveConfigTypeDef *sSlaveConfig)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_SLAVE_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_SLAVE_MODE(sSlaveConfig->SlaveMode));
+  assert_param(IS_TIM_TRIGGER_SELECTION(sSlaveConfig->InputTrigger));
+
+  __HAL_LOCK(htim);
+
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  if (TIM_SlaveTimer_SetConfig(htim, sSlaveConfig) != HAL_OK)
+  {
+    htim->State = HAL_TIM_STATE_READY;
+    __HAL_UNLOCK(htim);
+    return HAL_ERROR;
+  }
+
+  /* Disable Trigger Interrupt */
+  __HAL_TIM_DISABLE_IT(htim, TIM_IT_TRIGGER);
+
+  /* Disable Trigger DMA request */
+  __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_TRIGGER);
+
+  htim->State = HAL_TIM_STATE_READY;
+
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Configures the TIM in Slave mode in interrupt mode
+  * @param  htim TIM handle.
+  * @param  sSlaveConfig pointer to a TIM_SlaveConfigTypeDef structure that
+  *         contains the selected trigger (internal trigger input, filtered
+  *         timer input or external trigger input) and the Slave mode
+  *         (Disable, Reset, Gated, Trigger, External clock mode 1).
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIM_SlaveConfigSynchro_IT(TIM_HandleTypeDef *htim,
+                                                const TIM_SlaveConfigTypeDef *sSlaveConfig)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_SLAVE_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_SLAVE_MODE(sSlaveConfig->SlaveMode));
+  assert_param(IS_TIM_TRIGGER_SELECTION(sSlaveConfig->InputTrigger));
+
+  __HAL_LOCK(htim);
+
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  if (TIM_SlaveTimer_SetConfig(htim, sSlaveConfig) != HAL_OK)
+  {
+    htim->State = HAL_TIM_STATE_READY;
+    __HAL_UNLOCK(htim);
+    return HAL_ERROR;
+  }
+
+  /* Enable Trigger Interrupt */
+  __HAL_TIM_ENABLE_IT(htim, TIM_IT_TRIGGER);
+
+  /* Disable Trigger DMA request */
+  __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_TRIGGER);
+
+  htim->State = HAL_TIM_STATE_READY;
+
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Read the captured value from Capture Compare unit
+  * @param  htim TIM handle.
+  * @param  Channel TIM Channels to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
+  * @retval Captured value
+  */
+uint32_t HAL_TIM_ReadCapturedValue(const TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  uint32_t tmpreg = 0U;
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
+
+      /* Return the capture 1 value */
+      tmpreg =  htim->Instance->CCR1;
+
+      break;
+    }
+    case TIM_CHANNEL_2:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
+
+      /* Return the capture 2 value */
+      tmpreg =   htim->Instance->CCR2;
+
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC3_INSTANCE(htim->Instance));
+
+      /* Return the capture 3 value */
+      tmpreg =   htim->Instance->CCR3;
+
+      break;
+    }
+
+    case TIM_CHANNEL_4:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC4_INSTANCE(htim->Instance));
+
+      /* Return the capture 4 value */
+      tmpreg =   htim->Instance->CCR4;
+
+      break;
+    }
+
+    default:
+      break;
+  }
+
+  return tmpreg;
+}
+
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Exported_Functions_Group9 TIM Callbacks functions
+  *  @brief    TIM Callbacks functions
+  *
+@verbatim
+  ==============================================================================
+                        ##### TIM Callbacks functions #####
+  ==============================================================================
+ [..]
+   This section provides TIM callback functions:
+   (+) TIM Period elapsed callback
+   (+) TIM Output Compare callback
+   (+) TIM Input capture callback
+   (+) TIM Trigger callback
+   (+) TIM Error callback
+
+@endverbatim
+  * @{
+  */
+
+/**
+  * @brief  Period elapsed callback in non-blocking mode
+  * @param  htim TIM handle
+  * @retval None
+  */
+__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_PeriodElapsedCallback could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Period elapsed half complete callback in non-blocking mode
+  * @param  htim TIM handle
+  * @retval None
+  */
+__weak void HAL_TIM_PeriodElapsedHalfCpltCallback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_PeriodElapsedHalfCpltCallback could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Output Compare callback in non-blocking mode
+  * @param  htim TIM OC handle
+  * @retval None
+  */
+__weak void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_OC_DelayElapsedCallback could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Input Capture callback in non-blocking mode
+  * @param  htim TIM IC handle
+  * @retval None
+  */
+__weak void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_IC_CaptureCallback could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Input Capture half complete callback in non-blocking mode
+  * @param  htim TIM IC handle
+  * @retval None
+  */
+__weak void HAL_TIM_IC_CaptureHalfCpltCallback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_IC_CaptureHalfCpltCallback could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  PWM Pulse finished callback in non-blocking mode
+  * @param  htim TIM handle
+  * @retval None
+  */
+__weak void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_PWM_PulseFinishedCallback could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  PWM Pulse finished half complete callback in non-blocking mode
+  * @param  htim TIM handle
+  * @retval None
+  */
+__weak void HAL_TIM_PWM_PulseFinishedHalfCpltCallback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_PWM_PulseFinishedHalfCpltCallback could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Hall Trigger detection callback in non-blocking mode
+  * @param  htim TIM handle
+  * @retval None
+  */
+__weak void HAL_TIM_TriggerCallback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_TriggerCallback could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Hall Trigger detection half complete callback in non-blocking mode
+  * @param  htim TIM handle
+  * @retval None
+  */
+__weak void HAL_TIM_TriggerHalfCpltCallback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_TriggerHalfCpltCallback could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Timer error callback in non-blocking mode
+  * @param  htim TIM handle
+  * @retval None
+  */
+__weak void HAL_TIM_ErrorCallback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIM_ErrorCallback could be implemented in the user file
+   */
+}
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+/**
+  * @brief  Register a User TIM callback to be used instead of the weak predefined callback
+  * @param htim tim handle
+  * @param CallbackID ID of the callback to be registered
+  *        This parameter can be one of the following values:
+  *          @arg @ref HAL_TIM_BASE_MSPINIT_CB_ID Base MspInit Callback ID
+  *          @arg @ref HAL_TIM_BASE_MSPDEINIT_CB_ID Base MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_IC_MSPINIT_CB_ID IC MspInit Callback ID
+  *          @arg @ref HAL_TIM_IC_MSPDEINIT_CB_ID IC MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_OC_MSPINIT_CB_ID OC MspInit Callback ID
+  *          @arg @ref HAL_TIM_OC_MSPDEINIT_CB_ID OC MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_PWM_MSPINIT_CB_ID PWM MspInit Callback ID
+  *          @arg @ref HAL_TIM_PWM_MSPDEINIT_CB_ID PWM MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_ONE_PULSE_MSPINIT_CB_ID One Pulse MspInit Callback ID
+  *          @arg @ref HAL_TIM_ONE_PULSE_MSPDEINIT_CB_ID One Pulse MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_ENCODER_MSPINIT_CB_ID Encoder MspInit Callback ID
+  *          @arg @ref HAL_TIM_ENCODER_MSPDEINIT_CB_ID Encoder MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_HALL_SENSOR_MSPINIT_CB_ID Hall Sensor MspInit Callback ID
+  *          @arg @ref HAL_TIM_HALL_SENSOR_MSPDEINIT_CB_ID Hall Sensor MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_PERIOD_ELAPSED_CB_ID Period Elapsed Callback ID
+  *          @arg @ref HAL_TIM_PERIOD_ELAPSED_HALF_CB_ID Period Elapsed half complete Callback ID
+  *          @arg @ref HAL_TIM_TRIGGER_CB_ID Trigger Callback ID
+  *          @arg @ref HAL_TIM_TRIGGER_HALF_CB_ID Trigger half complete Callback ID
+  *          @arg @ref HAL_TIM_IC_CAPTURE_CB_ID Input Capture Callback ID
+  *          @arg @ref HAL_TIM_IC_CAPTURE_HALF_CB_ID Input Capture half complete Callback ID
+  *          @arg @ref HAL_TIM_OC_DELAY_ELAPSED_CB_ID Output Compare Delay Elapsed Callback ID
+  *          @arg @ref HAL_TIM_PWM_PULSE_FINISHED_CB_ID PWM Pulse Finished Callback ID
+  *          @arg @ref HAL_TIM_PWM_PULSE_FINISHED_HALF_CB_ID PWM Pulse Finished half complete Callback ID
+  *          @arg @ref HAL_TIM_ERROR_CB_ID Error Callback ID
+  *          @arg @ref HAL_TIM_COMMUTATION_CB_ID Commutation Callback ID
+  *          @arg @ref HAL_TIM_COMMUTATION_HALF_CB_ID Commutation half complete Callback ID
+  *          @arg @ref HAL_TIM_BREAK_CB_ID Break Callback ID
+  *          @arg @ref HAL_TIM_BREAK2_CB_ID Break2 Callback ID
+  *          @param pCallback pointer to the callback function
+  *          @retval status
+  */
+HAL_StatusTypeDef HAL_TIM_RegisterCallback(TIM_HandleTypeDef *htim, HAL_TIM_CallbackIDTypeDef CallbackID,
+                                           pTIM_CallbackTypeDef pCallback)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  if (pCallback == NULL)
+  {
+    return HAL_ERROR;
+  }
+
+  if (htim->State == HAL_TIM_STATE_READY)
+  {
+    switch (CallbackID)
+    {
+      case HAL_TIM_BASE_MSPINIT_CB_ID :
+        htim->Base_MspInitCallback                 = pCallback;
+        break;
+
+      case HAL_TIM_BASE_MSPDEINIT_CB_ID :
+        htim->Base_MspDeInitCallback               = pCallback;
+        break;
+
+      case HAL_TIM_IC_MSPINIT_CB_ID :
+        htim->IC_MspInitCallback                   = pCallback;
+        break;
+
+      case HAL_TIM_IC_MSPDEINIT_CB_ID :
+        htim->IC_MspDeInitCallback                 = pCallback;
+        break;
+
+      case HAL_TIM_OC_MSPINIT_CB_ID :
+        htim->OC_MspInitCallback                   = pCallback;
+        break;
+
+      case HAL_TIM_OC_MSPDEINIT_CB_ID :
+        htim->OC_MspDeInitCallback                 = pCallback;
+        break;
+
+      case HAL_TIM_PWM_MSPINIT_CB_ID :
+        htim->PWM_MspInitCallback                  = pCallback;
+        break;
+
+      case HAL_TIM_PWM_MSPDEINIT_CB_ID :
+        htim->PWM_MspDeInitCallback                = pCallback;
+        break;
+
+      case HAL_TIM_ONE_PULSE_MSPINIT_CB_ID :
+        htim->OnePulse_MspInitCallback             = pCallback;
+        break;
+
+      case HAL_TIM_ONE_PULSE_MSPDEINIT_CB_ID :
+        htim->OnePulse_MspDeInitCallback           = pCallback;
+        break;
+
+      case HAL_TIM_ENCODER_MSPINIT_CB_ID :
+        htim->Encoder_MspInitCallback              = pCallback;
+        break;
+
+      case HAL_TIM_ENCODER_MSPDEINIT_CB_ID :
+        htim->Encoder_MspDeInitCallback            = pCallback;
+        break;
+
+      case HAL_TIM_HALL_SENSOR_MSPINIT_CB_ID :
+        htim->HallSensor_MspInitCallback           = pCallback;
+        break;
+
+      case HAL_TIM_HALL_SENSOR_MSPDEINIT_CB_ID :
+        htim->HallSensor_MspDeInitCallback         = pCallback;
+        break;
+
+      case HAL_TIM_PERIOD_ELAPSED_CB_ID :
+        htim->PeriodElapsedCallback                = pCallback;
+        break;
+
+      case HAL_TIM_PERIOD_ELAPSED_HALF_CB_ID :
+        htim->PeriodElapsedHalfCpltCallback        = pCallback;
+        break;
+
+      case HAL_TIM_TRIGGER_CB_ID :
+        htim->TriggerCallback                      = pCallback;
+        break;
+
+      case HAL_TIM_TRIGGER_HALF_CB_ID :
+        htim->TriggerHalfCpltCallback              = pCallback;
+        break;
+
+      case HAL_TIM_IC_CAPTURE_CB_ID :
+        htim->IC_CaptureCallback                   = pCallback;
+        break;
+
+      case HAL_TIM_IC_CAPTURE_HALF_CB_ID :
+        htim->IC_CaptureHalfCpltCallback           = pCallback;
+        break;
+
+      case HAL_TIM_OC_DELAY_ELAPSED_CB_ID :
+        htim->OC_DelayElapsedCallback              = pCallback;
+        break;
+
+      case HAL_TIM_PWM_PULSE_FINISHED_CB_ID :
+        htim->PWM_PulseFinishedCallback            = pCallback;
+        break;
+
+      case HAL_TIM_PWM_PULSE_FINISHED_HALF_CB_ID :
+        htim->PWM_PulseFinishedHalfCpltCallback    = pCallback;
+        break;
+
+      case HAL_TIM_ERROR_CB_ID :
+        htim->ErrorCallback                        = pCallback;
+        break;
+
+      case HAL_TIM_COMMUTATION_CB_ID :
+        htim->CommutationCallback                  = pCallback;
+        break;
+
+      case HAL_TIM_COMMUTATION_HALF_CB_ID :
+        htim->CommutationHalfCpltCallback          = pCallback;
+        break;
+
+      case HAL_TIM_BREAK_CB_ID :
+        htim->BreakCallback                        = pCallback;
+        break;
+
+      case HAL_TIM_BREAK2_CB_ID :
+        htim->Break2Callback                       = pCallback;
+        break;
+
+      default :
+        /* Return error status */
+        status = HAL_ERROR;
+        break;
+    }
+  }
+  else if (htim->State == HAL_TIM_STATE_RESET)
+  {
+    switch (CallbackID)
+    {
+      case HAL_TIM_BASE_MSPINIT_CB_ID :
+        htim->Base_MspInitCallback         = pCallback;
+        break;
+
+      case HAL_TIM_BASE_MSPDEINIT_CB_ID :
+        htim->Base_MspDeInitCallback       = pCallback;
+        break;
+
+      case HAL_TIM_IC_MSPINIT_CB_ID :
+        htim->IC_MspInitCallback           = pCallback;
+        break;
+
+      case HAL_TIM_IC_MSPDEINIT_CB_ID :
+        htim->IC_MspDeInitCallback         = pCallback;
+        break;
+
+      case HAL_TIM_OC_MSPINIT_CB_ID :
+        htim->OC_MspInitCallback           = pCallback;
+        break;
+
+      case HAL_TIM_OC_MSPDEINIT_CB_ID :
+        htim->OC_MspDeInitCallback         = pCallback;
+        break;
+
+      case HAL_TIM_PWM_MSPINIT_CB_ID :
+        htim->PWM_MspInitCallback          = pCallback;
+        break;
+
+      case HAL_TIM_PWM_MSPDEINIT_CB_ID :
+        htim->PWM_MspDeInitCallback        = pCallback;
+        break;
+
+      case HAL_TIM_ONE_PULSE_MSPINIT_CB_ID :
+        htim->OnePulse_MspInitCallback     = pCallback;
+        break;
+
+      case HAL_TIM_ONE_PULSE_MSPDEINIT_CB_ID :
+        htim->OnePulse_MspDeInitCallback   = pCallback;
+        break;
+
+      case HAL_TIM_ENCODER_MSPINIT_CB_ID :
+        htim->Encoder_MspInitCallback      = pCallback;
+        break;
+
+      case HAL_TIM_ENCODER_MSPDEINIT_CB_ID :
+        htim->Encoder_MspDeInitCallback    = pCallback;
+        break;
+
+      case HAL_TIM_HALL_SENSOR_MSPINIT_CB_ID :
+        htim->HallSensor_MspInitCallback   = pCallback;
+        break;
+
+      case HAL_TIM_HALL_SENSOR_MSPDEINIT_CB_ID :
+        htim->HallSensor_MspDeInitCallback = pCallback;
+        break;
+
+      default :
+        /* Return error status */
+        status = HAL_ERROR;
+        break;
+    }
+  }
+  else
+  {
+    /* Return error status */
+    status = HAL_ERROR;
+  }
+
+  return status;
+}
+
+/**
+  * @brief  Unregister a TIM callback
+  *         TIM callback is redirected to the weak predefined callback
+  * @param htim tim handle
+  * @param CallbackID ID of the callback to be unregistered
+  *        This parameter can be one of the following values:
+  *          @arg @ref HAL_TIM_BASE_MSPINIT_CB_ID Base MspInit Callback ID
+  *          @arg @ref HAL_TIM_BASE_MSPDEINIT_CB_ID Base MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_IC_MSPINIT_CB_ID IC MspInit Callback ID
+  *          @arg @ref HAL_TIM_IC_MSPDEINIT_CB_ID IC MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_OC_MSPINIT_CB_ID OC MspInit Callback ID
+  *          @arg @ref HAL_TIM_OC_MSPDEINIT_CB_ID OC MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_PWM_MSPINIT_CB_ID PWM MspInit Callback ID
+  *          @arg @ref HAL_TIM_PWM_MSPDEINIT_CB_ID PWM MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_ONE_PULSE_MSPINIT_CB_ID One Pulse MspInit Callback ID
+  *          @arg @ref HAL_TIM_ONE_PULSE_MSPDEINIT_CB_ID One Pulse MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_ENCODER_MSPINIT_CB_ID Encoder MspInit Callback ID
+  *          @arg @ref HAL_TIM_ENCODER_MSPDEINIT_CB_ID Encoder MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_HALL_SENSOR_MSPINIT_CB_ID Hall Sensor MspInit Callback ID
+  *          @arg @ref HAL_TIM_HALL_SENSOR_MSPDEINIT_CB_ID Hall Sensor MspDeInit Callback ID
+  *          @arg @ref HAL_TIM_PERIOD_ELAPSED_CB_ID Period Elapsed Callback ID
+  *          @arg @ref HAL_TIM_PERIOD_ELAPSED_HALF_CB_ID Period Elapsed half complete Callback ID
+  *          @arg @ref HAL_TIM_TRIGGER_CB_ID Trigger Callback ID
+  *          @arg @ref HAL_TIM_TRIGGER_HALF_CB_ID Trigger half complete Callback ID
+  *          @arg @ref HAL_TIM_IC_CAPTURE_CB_ID Input Capture Callback ID
+  *          @arg @ref HAL_TIM_IC_CAPTURE_HALF_CB_ID Input Capture half complete Callback ID
+  *          @arg @ref HAL_TIM_OC_DELAY_ELAPSED_CB_ID Output Compare Delay Elapsed Callback ID
+  *          @arg @ref HAL_TIM_PWM_PULSE_FINISHED_CB_ID PWM Pulse Finished Callback ID
+  *          @arg @ref HAL_TIM_PWM_PULSE_FINISHED_HALF_CB_ID PWM Pulse Finished half complete Callback ID
+  *          @arg @ref HAL_TIM_ERROR_CB_ID Error Callback ID
+  *          @arg @ref HAL_TIM_COMMUTATION_CB_ID Commutation Callback ID
+  *          @arg @ref HAL_TIM_COMMUTATION_HALF_CB_ID Commutation half complete Callback ID
+  *          @arg @ref HAL_TIM_BREAK_CB_ID Break Callback ID
+  *          @arg @ref HAL_TIM_BREAK2_CB_ID Break2 Callback ID
+  *          @retval status
+  */
+HAL_StatusTypeDef HAL_TIM_UnRegisterCallback(TIM_HandleTypeDef *htim, HAL_TIM_CallbackIDTypeDef CallbackID)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  if (htim->State == HAL_TIM_STATE_READY)
+  {
+    switch (CallbackID)
+    {
+      case HAL_TIM_BASE_MSPINIT_CB_ID :
+        /* Legacy weak Base MspInit Callback */
+        htim->Base_MspInitCallback              = HAL_TIM_Base_MspInit;
+        break;
+
+      case HAL_TIM_BASE_MSPDEINIT_CB_ID :
+        /* Legacy weak Base Msp DeInit Callback */
+        htim->Base_MspDeInitCallback            = HAL_TIM_Base_MspDeInit;
+        break;
+
+      case HAL_TIM_IC_MSPINIT_CB_ID :
+        /* Legacy weak IC Msp Init Callback */
+        htim->IC_MspInitCallback                = HAL_TIM_IC_MspInit;
+        break;
+
+      case HAL_TIM_IC_MSPDEINIT_CB_ID :
+        /* Legacy weak IC Msp DeInit Callback */
+        htim->IC_MspDeInitCallback              = HAL_TIM_IC_MspDeInit;
+        break;
+
+      case HAL_TIM_OC_MSPINIT_CB_ID :
+        /* Legacy weak OC Msp Init Callback */
+        htim->OC_MspInitCallback                = HAL_TIM_OC_MspInit;
+        break;
+
+      case HAL_TIM_OC_MSPDEINIT_CB_ID :
+        /* Legacy weak OC Msp DeInit Callback */
+        htim->OC_MspDeInitCallback              = HAL_TIM_OC_MspDeInit;
+        break;
+
+      case HAL_TIM_PWM_MSPINIT_CB_ID :
+        /* Legacy weak PWM Msp Init Callback */
+        htim->PWM_MspInitCallback               = HAL_TIM_PWM_MspInit;
+        break;
+
+      case HAL_TIM_PWM_MSPDEINIT_CB_ID :
+        /* Legacy weak PWM Msp DeInit Callback */
+        htim->PWM_MspDeInitCallback             = HAL_TIM_PWM_MspDeInit;
+        break;
+
+      case HAL_TIM_ONE_PULSE_MSPINIT_CB_ID :
+        /* Legacy weak One Pulse Msp Init Callback */
+        htim->OnePulse_MspInitCallback          = HAL_TIM_OnePulse_MspInit;
+        break;
+
+      case HAL_TIM_ONE_PULSE_MSPDEINIT_CB_ID :
+        /* Legacy weak One Pulse Msp DeInit Callback */
+        htim->OnePulse_MspDeInitCallback        = HAL_TIM_OnePulse_MspDeInit;
+        break;
+
+      case HAL_TIM_ENCODER_MSPINIT_CB_ID :
+        /* Legacy weak Encoder Msp Init Callback */
+        htim->Encoder_MspInitCallback           = HAL_TIM_Encoder_MspInit;
+        break;
+
+      case HAL_TIM_ENCODER_MSPDEINIT_CB_ID :
+        /* Legacy weak Encoder Msp DeInit Callback */
+        htim->Encoder_MspDeInitCallback         = HAL_TIM_Encoder_MspDeInit;
+        break;
+
+      case HAL_TIM_HALL_SENSOR_MSPINIT_CB_ID :
+        /* Legacy weak Hall Sensor Msp Init Callback */
+        htim->HallSensor_MspInitCallback        = HAL_TIMEx_HallSensor_MspInit;
+        break;
+
+      case HAL_TIM_HALL_SENSOR_MSPDEINIT_CB_ID :
+        /* Legacy weak Hall Sensor Msp DeInit Callback */
+        htim->HallSensor_MspDeInitCallback      = HAL_TIMEx_HallSensor_MspDeInit;
+        break;
+
+      case HAL_TIM_PERIOD_ELAPSED_CB_ID :
+        /* Legacy weak Period Elapsed Callback */
+        htim->PeriodElapsedCallback             = HAL_TIM_PeriodElapsedCallback;
+        break;
+
+      case HAL_TIM_PERIOD_ELAPSED_HALF_CB_ID :
+        /* Legacy weak Period Elapsed half complete Callback */
+        htim->PeriodElapsedHalfCpltCallback     = HAL_TIM_PeriodElapsedHalfCpltCallback;
+        break;
+
+      case HAL_TIM_TRIGGER_CB_ID :
+        /* Legacy weak Trigger Callback */
+        htim->TriggerCallback                   = HAL_TIM_TriggerCallback;
+        break;
+
+      case HAL_TIM_TRIGGER_HALF_CB_ID :
+        /* Legacy weak Trigger half complete Callback */
+        htim->TriggerHalfCpltCallback           = HAL_TIM_TriggerHalfCpltCallback;
+        break;
+
+      case HAL_TIM_IC_CAPTURE_CB_ID :
+        /* Legacy weak IC Capture Callback */
+        htim->IC_CaptureCallback                = HAL_TIM_IC_CaptureCallback;
+        break;
+
+      case HAL_TIM_IC_CAPTURE_HALF_CB_ID :
+        /* Legacy weak IC Capture half complete Callback */
+        htim->IC_CaptureHalfCpltCallback        = HAL_TIM_IC_CaptureHalfCpltCallback;
+        break;
+
+      case HAL_TIM_OC_DELAY_ELAPSED_CB_ID :
+        /* Legacy weak OC Delay Elapsed Callback */
+        htim->OC_DelayElapsedCallback           = HAL_TIM_OC_DelayElapsedCallback;
+        break;
+
+      case HAL_TIM_PWM_PULSE_FINISHED_CB_ID :
+        /* Legacy weak PWM Pulse Finished Callback */
+        htim->PWM_PulseFinishedCallback         = HAL_TIM_PWM_PulseFinishedCallback;
+        break;
+
+      case HAL_TIM_PWM_PULSE_FINISHED_HALF_CB_ID :
+        /* Legacy weak PWM Pulse Finished half complete Callback */
+        htim->PWM_PulseFinishedHalfCpltCallback = HAL_TIM_PWM_PulseFinishedHalfCpltCallback;
+        break;
+
+      case HAL_TIM_ERROR_CB_ID :
+        /* Legacy weak Error Callback */
+        htim->ErrorCallback                     = HAL_TIM_ErrorCallback;
+        break;
+
+      case HAL_TIM_COMMUTATION_CB_ID :
+        /* Legacy weak Commutation Callback */
+        htim->CommutationCallback               = HAL_TIMEx_CommutCallback;
+        break;
+
+      case HAL_TIM_COMMUTATION_HALF_CB_ID :
+        /* Legacy weak Commutation half complete Callback */
+        htim->CommutationHalfCpltCallback       = HAL_TIMEx_CommutHalfCpltCallback;
+        break;
+
+      case HAL_TIM_BREAK_CB_ID :
+        /* Legacy weak Break Callback */
+        htim->BreakCallback                     = HAL_TIMEx_BreakCallback;
+        break;
+
+      case HAL_TIM_BREAK2_CB_ID :
+        /* Legacy weak Break2 Callback */
+        htim->Break2Callback                    = HAL_TIMEx_Break2Callback;
+        break;
+
+      default :
+        /* Return error status */
+        status = HAL_ERROR;
+        break;
+    }
+  }
+  else if (htim->State == HAL_TIM_STATE_RESET)
+  {
+    switch (CallbackID)
+    {
+      case HAL_TIM_BASE_MSPINIT_CB_ID :
+        /* Legacy weak Base MspInit Callback */
+        htim->Base_MspInitCallback         = HAL_TIM_Base_MspInit;
+        break;
+
+      case HAL_TIM_BASE_MSPDEINIT_CB_ID :
+        /* Legacy weak Base Msp DeInit Callback */
+        htim->Base_MspDeInitCallback       = HAL_TIM_Base_MspDeInit;
+        break;
+
+      case HAL_TIM_IC_MSPINIT_CB_ID :
+        /* Legacy weak IC Msp Init Callback */
+        htim->IC_MspInitCallback           = HAL_TIM_IC_MspInit;
+        break;
+
+      case HAL_TIM_IC_MSPDEINIT_CB_ID :
+        /* Legacy weak IC Msp DeInit Callback */
+        htim->IC_MspDeInitCallback         = HAL_TIM_IC_MspDeInit;
+        break;
+
+      case HAL_TIM_OC_MSPINIT_CB_ID :
+        /* Legacy weak OC Msp Init Callback */
+        htim->OC_MspInitCallback           = HAL_TIM_OC_MspInit;
+        break;
+
+      case HAL_TIM_OC_MSPDEINIT_CB_ID :
+        /* Legacy weak OC Msp DeInit Callback */
+        htim->OC_MspDeInitCallback         = HAL_TIM_OC_MspDeInit;
+        break;
+
+      case HAL_TIM_PWM_MSPINIT_CB_ID :
+        /* Legacy weak PWM Msp Init Callback */
+        htim->PWM_MspInitCallback          = HAL_TIM_PWM_MspInit;
+        break;
+
+      case HAL_TIM_PWM_MSPDEINIT_CB_ID :
+        /* Legacy weak PWM Msp DeInit Callback */
+        htim->PWM_MspDeInitCallback        = HAL_TIM_PWM_MspDeInit;
+        break;
+
+      case HAL_TIM_ONE_PULSE_MSPINIT_CB_ID :
+        /* Legacy weak One Pulse Msp Init Callback */
+        htim->OnePulse_MspInitCallback     = HAL_TIM_OnePulse_MspInit;
+        break;
+
+      case HAL_TIM_ONE_PULSE_MSPDEINIT_CB_ID :
+        /* Legacy weak One Pulse Msp DeInit Callback */
+        htim->OnePulse_MspDeInitCallback   = HAL_TIM_OnePulse_MspDeInit;
+        break;
+
+      case HAL_TIM_ENCODER_MSPINIT_CB_ID :
+        /* Legacy weak Encoder Msp Init Callback */
+        htim->Encoder_MspInitCallback      = HAL_TIM_Encoder_MspInit;
+        break;
+
+      case HAL_TIM_ENCODER_MSPDEINIT_CB_ID :
+        /* Legacy weak Encoder Msp DeInit Callback */
+        htim->Encoder_MspDeInitCallback    = HAL_TIM_Encoder_MspDeInit;
+        break;
+
+      case HAL_TIM_HALL_SENSOR_MSPINIT_CB_ID :
+        /* Legacy weak Hall Sensor Msp Init Callback */
+        htim->HallSensor_MspInitCallback   = HAL_TIMEx_HallSensor_MspInit;
+        break;
+
+      case HAL_TIM_HALL_SENSOR_MSPDEINIT_CB_ID :
+        /* Legacy weak Hall Sensor Msp DeInit Callback */
+        htim->HallSensor_MspDeInitCallback = HAL_TIMEx_HallSensor_MspDeInit;
+        break;
+
+      default :
+        /* Return error status */
+        status = HAL_ERROR;
+        break;
+    }
+  }
+  else
+  {
+    /* Return error status */
+    status = HAL_ERROR;
+  }
+
+  return status;
+}
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Exported_Functions_Group10 TIM Peripheral State functions
+  *  @brief   TIM Peripheral State functions
+  *
+@verbatim
+  ==============================================================================
+                        ##### Peripheral State functions #####
+  ==============================================================================
+    [..]
+    This subsection permits to get in run-time the status of the peripheral
+    and the data flow.
+
+@endverbatim
+  * @{
+  */
+
+/**
+  * @brief  Return the TIM Base handle state.
+  * @param  htim TIM Base handle
+  * @retval HAL state
+  */
+HAL_TIM_StateTypeDef HAL_TIM_Base_GetState(const TIM_HandleTypeDef *htim)
+{
+  return htim->State;
+}
+
+/**
+  * @brief  Return the TIM OC handle state.
+  * @param  htim TIM Output Compare handle
+  * @retval HAL state
+  */
+HAL_TIM_StateTypeDef HAL_TIM_OC_GetState(const TIM_HandleTypeDef *htim)
+{
+  return htim->State;
+}
+
+/**
+  * @brief  Return the TIM PWM handle state.
+  * @param  htim TIM handle
+  * @retval HAL state
+  */
+HAL_TIM_StateTypeDef HAL_TIM_PWM_GetState(const TIM_HandleTypeDef *htim)
+{
+  return htim->State;
+}
+
+/**
+  * @brief  Return the TIM Input Capture handle state.
+  * @param  htim TIM IC handle
+  * @retval HAL state
+  */
+HAL_TIM_StateTypeDef HAL_TIM_IC_GetState(const TIM_HandleTypeDef *htim)
+{
+  return htim->State;
+}
+
+/**
+  * @brief  Return the TIM One Pulse Mode handle state.
+  * @param  htim TIM OPM handle
+  * @retval HAL state
+  */
+HAL_TIM_StateTypeDef HAL_TIM_OnePulse_GetState(const TIM_HandleTypeDef *htim)
+{
+  return htim->State;
+}
+
+/**
+  * @brief  Return the TIM Encoder Mode handle state.
+  * @param  htim TIM Encoder Interface handle
+  * @retval HAL state
+  */
+HAL_TIM_StateTypeDef HAL_TIM_Encoder_GetState(const TIM_HandleTypeDef *htim)
+{
+  return htim->State;
+}
+
+/**
+  * @brief  Return the TIM Encoder Mode handle state.
+  * @param  htim TIM handle
+  * @retval Active channel
+  */
+HAL_TIM_ActiveChannel HAL_TIM_GetActiveChannel(const TIM_HandleTypeDef *htim)
+{
+  return htim->Channel;
+}
+
+/**
+  * @brief  Return actual state of the TIM channel.
+  * @param  htim TIM handle
+  * @param  Channel TIM Channel
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1
+  *            @arg TIM_CHANNEL_2: TIM Channel 2
+  *            @arg TIM_CHANNEL_3: TIM Channel 3
+  *            @arg TIM_CHANNEL_4: TIM Channel 4
+  *            @arg TIM_CHANNEL_5: TIM Channel 5
+  *            @arg TIM_CHANNEL_6: TIM Channel 6
+  * @retval TIM Channel state
+  */
+HAL_TIM_ChannelStateTypeDef HAL_TIM_GetChannelState(const TIM_HandleTypeDef *htim,  uint32_t Channel)
+{
+  HAL_TIM_ChannelStateTypeDef channel_state;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));
+
+  channel_state = TIM_CHANNEL_STATE_GET(htim, Channel);
+
+  return channel_state;
+}
+
+/**
+  * @brief  Return actual state of a DMA burst operation.
+  * @param  htim TIM handle
+  * @retval DMA burst state
+  */
+HAL_TIM_DMABurstStateTypeDef HAL_TIM_DMABurstState(const TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_DMABURST_INSTANCE(htim->Instance));
+
+  return htim->DMABurstState;
+}
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+
+/** @defgroup TIM_Private_Functions TIM Private Functions
+  * @{
+  */
+
+/**
+  * @brief  TIM DMA error callback
+  * @param  hdma pointer to DMA handle.
+  * @retval None
+  */
+void TIM_DMAError(DMA_HandleTypeDef *hdma)
+{
+  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
+
+  if (hdma == htim->hdma[TIM_DMA_ID_CC1])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;
+    TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC2])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_2;
+    TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC3])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_3;
+    TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_3, HAL_TIM_CHANNEL_STATE_READY);
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC4])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_4;
+    TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_4, HAL_TIM_CHANNEL_STATE_READY);
+  }
+  else
+  {
+    htim->State = HAL_TIM_STATE_READY;
+  }
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  htim->ErrorCallback(htim);
+#else
+  HAL_TIM_ErrorCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
+}
+
+/**
+  * @brief  TIM DMA Delay Pulse complete callback.
+  * @param  hdma pointer to DMA handle.
+  * @retval None
+  */
+static void TIM_DMADelayPulseCplt(DMA_HandleTypeDef *hdma)
+{
+  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
+
+  if (hdma == htim->hdma[TIM_DMA_ID_CC1])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;
+
+    if (hdma->Init.Mode == DMA_NORMAL)
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+    }
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC2])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_2;
+
+    if (hdma->Init.Mode == DMA_NORMAL)
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+    }
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC3])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_3;
+
+    if (hdma->Init.Mode == DMA_NORMAL)
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_3, HAL_TIM_CHANNEL_STATE_READY);
+    }
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC4])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_4;
+
+    if (hdma->Init.Mode == DMA_NORMAL)
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_4, HAL_TIM_CHANNEL_STATE_READY);
+    }
+  }
+  else
+  {
+    /* nothing to do */
+  }
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  htim->PWM_PulseFinishedCallback(htim);
+#else
+  HAL_TIM_PWM_PulseFinishedCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
+}
+
+/**
+  * @brief  TIM DMA Delay Pulse half complete callback.
+  * @param  hdma pointer to DMA handle.
+  * @retval None
+  */
+void TIM_DMADelayPulseHalfCplt(DMA_HandleTypeDef *hdma)
+{
+  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
+
+  if (hdma == htim->hdma[TIM_DMA_ID_CC1])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC2])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_2;
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC3])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_3;
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC4])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_4;
+  }
+  else
+  {
+    /* nothing to do */
+  }
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  htim->PWM_PulseFinishedHalfCpltCallback(htim);
+#else
+  HAL_TIM_PWM_PulseFinishedHalfCpltCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
+}
+
+/**
+  * @brief  TIM DMA Capture complete callback.
+  * @param  hdma pointer to DMA handle.
+  * @retval None
+  */
+void TIM_DMACaptureCplt(DMA_HandleTypeDef *hdma)
+{
+  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
+
+  if (hdma == htim->hdma[TIM_DMA_ID_CC1])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;
+
+    if (hdma->Init.Mode == DMA_NORMAL)
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+    }
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC2])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_2;
+
+    if (hdma->Init.Mode == DMA_NORMAL)
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+    }
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC3])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_3;
+
+    if (hdma->Init.Mode == DMA_NORMAL)
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_3, HAL_TIM_CHANNEL_STATE_READY);
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_3, HAL_TIM_CHANNEL_STATE_READY);
+    }
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC4])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_4;
+
+    if (hdma->Init.Mode == DMA_NORMAL)
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_4, HAL_TIM_CHANNEL_STATE_READY);
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_4, HAL_TIM_CHANNEL_STATE_READY);
+    }
+  }
+  else
+  {
+    /* nothing to do */
+  }
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  htim->IC_CaptureCallback(htim);
+#else
+  HAL_TIM_IC_CaptureCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
+}
+
+/**
+  * @brief  TIM DMA Capture half complete callback.
+  * @param  hdma pointer to DMA handle.
+  * @retval None
+  */
+void TIM_DMACaptureHalfCplt(DMA_HandleTypeDef *hdma)
+{
+  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
+
+  if (hdma == htim->hdma[TIM_DMA_ID_CC1])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC2])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_2;
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC3])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_3;
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC4])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_4;
+  }
+  else
+  {
+    /* nothing to do */
+  }
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  htim->IC_CaptureHalfCpltCallback(htim);
+#else
+  HAL_TIM_IC_CaptureHalfCpltCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
+}
+
+/**
+  * @brief  TIM DMA Period Elapse complete callback.
+  * @param  hdma pointer to DMA handle.
+  * @retval None
+  */
+static void TIM_DMAPeriodElapsedCplt(DMA_HandleTypeDef *hdma)
+{
+  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
+
+  if (htim->hdma[TIM_DMA_ID_UPDATE]->Init.Mode == DMA_NORMAL)
+  {
+    htim->State = HAL_TIM_STATE_READY;
+  }
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  htim->PeriodElapsedCallback(htim);
+#else
+  HAL_TIM_PeriodElapsedCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+}
+
+/**
+  * @brief  TIM DMA Period Elapse half complete callback.
+  * @param  hdma pointer to DMA handle.
+  * @retval None
+  */
+static void TIM_DMAPeriodElapsedHalfCplt(DMA_HandleTypeDef *hdma)
+{
+  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  htim->PeriodElapsedHalfCpltCallback(htim);
+#else
+  HAL_TIM_PeriodElapsedHalfCpltCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+}
+
+/**
+  * @brief  TIM DMA Trigger callback.
+  * @param  hdma pointer to DMA handle.
+  * @retval None
+  */
+static void TIM_DMATriggerCplt(DMA_HandleTypeDef *hdma)
+{
+  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
+
+  if (htim->hdma[TIM_DMA_ID_TRIGGER]->Init.Mode == DMA_NORMAL)
+  {
+    htim->State = HAL_TIM_STATE_READY;
+  }
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  htim->TriggerCallback(htim);
+#else
+  HAL_TIM_TriggerCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+}
+
+/**
+  * @brief  TIM DMA Trigger half complete callback.
+  * @param  hdma pointer to DMA handle.
+  * @retval None
+  */
+static void TIM_DMATriggerHalfCplt(DMA_HandleTypeDef *hdma)
+{
+  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  htim->TriggerHalfCpltCallback(htim);
+#else
+  HAL_TIM_TriggerHalfCpltCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+}
+
+/**
+  * @brief  Time Base configuration
+  * @param  TIMx TIM peripheral
+  * @param  Structure TIM Base configuration structure
+  * @retval None
+  */
+void TIM_Base_SetConfig(TIM_TypeDef *TIMx, const TIM_Base_InitTypeDef *Structure)
+{
+  uint32_t tmpcr1;
+  tmpcr1 = TIMx->CR1;
+
+  /* Set TIM Time Base Unit parameters ---------------------------------------*/
+  if (IS_TIM_COUNTER_MODE_SELECT_INSTANCE(TIMx))
+  {
+    /* Select the Counter Mode */
+    tmpcr1 &= ~(TIM_CR1_DIR | TIM_CR1_CMS);
+    tmpcr1 |= Structure->CounterMode;
+  }
+
+  if (IS_TIM_CLOCK_DIVISION_INSTANCE(TIMx))
+  {
+    /* Set the clock division */
+    tmpcr1 &= ~TIM_CR1_CKD;
+    tmpcr1 |= (uint32_t)Structure->ClockDivision;
+  }
+
+  /* Set the auto-reload preload */
+  MODIFY_REG(tmpcr1, TIM_CR1_ARPE, Structure->AutoReloadPreload);
+
+  /* Set the Autoreload value */
+  TIMx->ARR = (uint32_t)Structure->Period ;
+
+  /* Set the Prescaler value */
+  TIMx->PSC = Structure->Prescaler;
+
+  if (IS_TIM_REPETITION_COUNTER_INSTANCE(TIMx))
+  {
+    /* Set the Repetition Counter value */
+    TIMx->RCR = Structure->RepetitionCounter;
+  }
+
+  /* Disable Update Event (UEV) with Update Generation (UG)
+     by changing Update Request Source (URS) to avoid Update flag (UIF) */
+  SET_BIT(TIMx->CR1, TIM_CR1_URS);
+
+  /* Generate an update event to reload the Prescaler
+     and the repetition counter (only for advanced timer) value immediately */
+  TIMx->EGR = TIM_EGR_UG;
+
+  TIMx->CR1 = tmpcr1;
+}
+
+/**
+  * @brief  Timer Output Compare 1 configuration
+  * @param  TIMx to select the TIM peripheral
+  * @param  OC_Config The output configuration structure
+  * @retval None
+  */
+static void TIM_OC1_SetConfig(TIM_TypeDef *TIMx, const TIM_OC_InitTypeDef *OC_Config)
+{
+  uint32_t tmpccmrx;
+  uint32_t tmpccer;
+  uint32_t tmpcr2;
+
+  /* Get the TIMx CCER register value */
+  tmpccer = TIMx->CCER;
+
+  /* Disable the Channel 1: Reset the CC1E Bit */
+  TIMx->CCER &= ~TIM_CCER_CC1E;
+
+  /* Get the TIMx CR2 register value */
+  tmpcr2 =  TIMx->CR2;
+
+  /* Get the TIMx CCMR1 register value */
+  tmpccmrx = TIMx->CCMR1;
+
+  /* Reset the Output Compare Mode Bits */
+  tmpccmrx &= ~TIM_CCMR1_OC1M;
+  tmpccmrx &= ~TIM_CCMR1_CC1S;
+  /* Select the Output Compare Mode */
+  tmpccmrx |= OC_Config->OCMode;
+
+  /* Reset the Output Polarity level */
+  tmpccer &= ~TIM_CCER_CC1P;
+  /* Set the Output Compare Polarity */
+  tmpccer |= OC_Config->OCPolarity;
+
+  if (IS_TIM_CCXN_INSTANCE(TIMx, TIM_CHANNEL_1))
+  {
+    /* Check parameters */
+    assert_param(IS_TIM_OCN_POLARITY(OC_Config->OCNPolarity));
+
+    /* Reset the Output N Polarity level */
+    tmpccer &= ~TIM_CCER_CC1NP;
+    /* Set the Output N Polarity */
+    tmpccer |= OC_Config->OCNPolarity;
+    /* Reset the Output N State */
+    tmpccer &= ~TIM_CCER_CC1NE;
+  }
+
+  if (IS_TIM_BREAK_INSTANCE(TIMx))
+  {
+    /* Check parameters */
+    assert_param(IS_TIM_OCNIDLE_STATE(OC_Config->OCNIdleState));
+    assert_param(IS_TIM_OCIDLE_STATE(OC_Config->OCIdleState));
+
+    /* Reset the Output Compare and Output Compare N IDLE State */
+    tmpcr2 &= ~TIM_CR2_OIS1;
+    tmpcr2 &= ~TIM_CR2_OIS1N;
+    /* Set the Output Idle state */
+    tmpcr2 |= OC_Config->OCIdleState;
+    /* Set the Output N Idle state */
+    tmpcr2 |= OC_Config->OCNIdleState;
+  }
+
+  /* Write to TIMx CR2 */
+  TIMx->CR2 = tmpcr2;
+
+  /* Write to TIMx CCMR1 */
+  TIMx->CCMR1 = tmpccmrx;
+
+  /* Set the Capture Compare Register value */
+  TIMx->CCR1 = OC_Config->Pulse;
+
+  /* Write to TIMx CCER */
+  TIMx->CCER = tmpccer;
+}
+
+/**
+  * @brief  Timer Output Compare 2 configuration
+  * @param  TIMx to select the TIM peripheral
+  * @param  OC_Config The output configuration structure
+  * @retval None
+  */
+void TIM_OC2_SetConfig(TIM_TypeDef *TIMx, const TIM_OC_InitTypeDef *OC_Config)
+{
+  uint32_t tmpccmrx;
+  uint32_t tmpccer;
+  uint32_t tmpcr2;
+
+  /* Get the TIMx CCER register value */
+  tmpccer = TIMx->CCER;
+
+  /* Disable the Channel 2: Reset the CC2E Bit */
+  TIMx->CCER &= ~TIM_CCER_CC2E;
+
+  /* Get the TIMx CR2 register value */
+  tmpcr2 =  TIMx->CR2;
+
+  /* Get the TIMx CCMR1 register value */
+  tmpccmrx = TIMx->CCMR1;
+
+  /* Reset the Output Compare mode and Capture/Compare selection Bits */
+  tmpccmrx &= ~TIM_CCMR1_OC2M;
+  tmpccmrx &= ~TIM_CCMR1_CC2S;
+
+  /* Select the Output Compare Mode */
+  tmpccmrx |= (OC_Config->OCMode << 8U);
+
+  /* Reset the Output Polarity level */
+  tmpccer &= ~TIM_CCER_CC2P;
+  /* Set the Output Compare Polarity */
+  tmpccer |= (OC_Config->OCPolarity << 4U);
+
+  if (IS_TIM_CCXN_INSTANCE(TIMx, TIM_CHANNEL_2))
+  {
+    assert_param(IS_TIM_OCN_POLARITY(OC_Config->OCNPolarity));
+
+    /* Reset the Output N Polarity level */
+    tmpccer &= ~TIM_CCER_CC2NP;
+    /* Set the Output N Polarity */
+    tmpccer |= (OC_Config->OCNPolarity << 4U);
+    /* Reset the Output N State */
+    tmpccer &= ~TIM_CCER_CC2NE;
+  }
+
+  if (IS_TIM_BREAK_INSTANCE(TIMx))
+  {
+    /* Check parameters */
+    assert_param(IS_TIM_OCNIDLE_STATE(OC_Config->OCNIdleState));
+    assert_param(IS_TIM_OCIDLE_STATE(OC_Config->OCIdleState));
+
+    /* Reset the Output Compare and Output Compare N IDLE State */
+    tmpcr2 &= ~TIM_CR2_OIS2;
+    tmpcr2 &= ~TIM_CR2_OIS2N;
+    /* Set the Output Idle state */
+    tmpcr2 |= (OC_Config->OCIdleState << 2U);
+    /* Set the Output N Idle state */
+    tmpcr2 |= (OC_Config->OCNIdleState << 2U);
+  }
+
+  /* Write to TIMx CR2 */
+  TIMx->CR2 = tmpcr2;
+
+  /* Write to TIMx CCMR1 */
+  TIMx->CCMR1 = tmpccmrx;
+
+  /* Set the Capture Compare Register value */
+  TIMx->CCR2 = OC_Config->Pulse;
+
+  /* Write to TIMx CCER */
+  TIMx->CCER = tmpccer;
+}
+
+/**
+  * @brief  Timer Output Compare 3 configuration
+  * @param  TIMx to select the TIM peripheral
+  * @param  OC_Config The output configuration structure
+  * @retval None
+  */
+static void TIM_OC3_SetConfig(TIM_TypeDef *TIMx, const TIM_OC_InitTypeDef *OC_Config)
+{
+  uint32_t tmpccmrx;
+  uint32_t tmpccer;
+  uint32_t tmpcr2;
+
+  /* Get the TIMx CCER register value */
+  tmpccer = TIMx->CCER;
+
+  /* Disable the Channel 3: Reset the CC2E Bit */
+  TIMx->CCER &= ~TIM_CCER_CC3E;
+
+  /* Get the TIMx CR2 register value */
+  tmpcr2 =  TIMx->CR2;
+
+  /* Get the TIMx CCMR2 register value */
+  tmpccmrx = TIMx->CCMR2;
+
+  /* Reset the Output Compare mode and Capture/Compare selection Bits */
+  tmpccmrx &= ~TIM_CCMR2_OC3M;
+  tmpccmrx &= ~TIM_CCMR2_CC3S;
+  /* Select the Output Compare Mode */
+  tmpccmrx |= OC_Config->OCMode;
+
+  /* Reset the Output Polarity level */
+  tmpccer &= ~TIM_CCER_CC3P;
+  /* Set the Output Compare Polarity */
+  tmpccer |= (OC_Config->OCPolarity << 8U);
+
+  if (IS_TIM_CCXN_INSTANCE(TIMx, TIM_CHANNEL_3))
+  {
+    assert_param(IS_TIM_OCN_POLARITY(OC_Config->OCNPolarity));
+
+    /* Reset the Output N Polarity level */
+    tmpccer &= ~TIM_CCER_CC3NP;
+    /* Set the Output N Polarity */
+    tmpccer |= (OC_Config->OCNPolarity << 8U);
+    /* Reset the Output N State */
+    tmpccer &= ~TIM_CCER_CC3NE;
+  }
+
+  if (IS_TIM_BREAK_INSTANCE(TIMx))
+  {
+    /* Check parameters */
+    assert_param(IS_TIM_OCNIDLE_STATE(OC_Config->OCNIdleState));
+    assert_param(IS_TIM_OCIDLE_STATE(OC_Config->OCIdleState));
+
+    /* Reset the Output Compare and Output Compare N IDLE State */
+    tmpcr2 &= ~TIM_CR2_OIS3;
+    tmpcr2 &= ~TIM_CR2_OIS3N;
+    /* Set the Output Idle state */
+    tmpcr2 |= (OC_Config->OCIdleState << 4U);
+    /* Set the Output N Idle state */
+    tmpcr2 |= (OC_Config->OCNIdleState << 4U);
+  }
+
+  /* Write to TIMx CR2 */
+  TIMx->CR2 = tmpcr2;
+
+  /* Write to TIMx CCMR2 */
+  TIMx->CCMR2 = tmpccmrx;
+
+  /* Set the Capture Compare Register value */
+  TIMx->CCR3 = OC_Config->Pulse;
+
+  /* Write to TIMx CCER */
+  TIMx->CCER = tmpccer;
+}
+
+/**
+  * @brief  Timer Output Compare 4 configuration
+  * @param  TIMx to select the TIM peripheral
+  * @param  OC_Config The output configuration structure
+  * @retval None
+  */
+static void TIM_OC4_SetConfig(TIM_TypeDef *TIMx, const TIM_OC_InitTypeDef *OC_Config)
+{
+  uint32_t tmpccmrx;
+  uint32_t tmpccer;
+  uint32_t tmpcr2;
+
+  /* Get the TIMx CCER register value */
+  tmpccer = TIMx->CCER;
+
+  /* Disable the Channel 4: Reset the CC4E Bit */
+  TIMx->CCER &= ~TIM_CCER_CC4E;
+
+  /* Get the TIMx CR2 register value */
+  tmpcr2 =  TIMx->CR2;
+
+  /* Get the TIMx CCMR2 register value */
+  tmpccmrx = TIMx->CCMR2;
+
+  /* Reset the Output Compare mode and Capture/Compare selection Bits */
+  tmpccmrx &= ~TIM_CCMR2_OC4M;
+  tmpccmrx &= ~TIM_CCMR2_CC4S;
+
+  /* Select the Output Compare Mode */
+  tmpccmrx |= (OC_Config->OCMode << 8U);
+
+  /* Reset the Output Polarity level */
+  tmpccer &= ~TIM_CCER_CC4P;
+  /* Set the Output Compare Polarity */
+  tmpccer |= (OC_Config->OCPolarity << 12U);
+
+  if (IS_TIM_BREAK_INSTANCE(TIMx))
+  {
+    /* Check parameters */
+    assert_param(IS_TIM_OCIDLE_STATE(OC_Config->OCIdleState));
+
+    /* Reset the Output Compare IDLE State */
+    tmpcr2 &= ~TIM_CR2_OIS4;
+
+    /* Set the Output Idle state */
+    tmpcr2 |= (OC_Config->OCIdleState << 6U);
+  }
+
+  /* Write to TIMx CR2 */
+  TIMx->CR2 = tmpcr2;
+
+  /* Write to TIMx CCMR2 */
+  TIMx->CCMR2 = tmpccmrx;
+
+  /* Set the Capture Compare Register value */
+  TIMx->CCR4 = OC_Config->Pulse;
+
+  /* Write to TIMx CCER */
+  TIMx->CCER = tmpccer;
+}
+
+/**
+  * @brief  Timer Output Compare 5 configuration
+  * @param  TIMx to select the TIM peripheral
+  * @param  OC_Config The output configuration structure
+  * @retval None
+  */
+static void TIM_OC5_SetConfig(TIM_TypeDef *TIMx,
+                              const TIM_OC_InitTypeDef *OC_Config)
+{
+  uint32_t tmpccmrx;
+  uint32_t tmpccer;
+  uint32_t tmpcr2;
+
+  /* Get the TIMx CCER register value */
+  tmpccer = TIMx->CCER;
+
+  /* Disable the output: Reset the CCxE Bit */
+  TIMx->CCER &= ~TIM_CCER_CC5E;
+
+  /* Get the TIMx CR2 register value */
+  tmpcr2 =  TIMx->CR2;
+  /* Get the TIMx CCMR1 register value */
+  tmpccmrx = TIMx->CCMR3;
+
+  /* Reset the Output Compare Mode Bits */
+  tmpccmrx &= ~(TIM_CCMR3_OC5M);
+  /* Select the Output Compare Mode */
+  tmpccmrx |= OC_Config->OCMode;
+
+  /* Reset the Output Polarity level */
+  tmpccer &= ~TIM_CCER_CC5P;
+  /* Set the Output Compare Polarity */
+  tmpccer |= (OC_Config->OCPolarity << 16U);
+
+  if (IS_TIM_BREAK_INSTANCE(TIMx))
+  {
+    /* Reset the Output Compare IDLE State */
+    tmpcr2 &= ~TIM_CR2_OIS5;
+    /* Set the Output Idle state */
+    tmpcr2 |= (OC_Config->OCIdleState << 8U);
+  }
+  /* Write to TIMx CR2 */
+  TIMx->CR2 = tmpcr2;
+
+  /* Write to TIMx CCMR3 */
+  TIMx->CCMR3 = tmpccmrx;
+
+  /* Set the Capture Compare Register value */
+  TIMx->CCR5 = OC_Config->Pulse;
+
+  /* Write to TIMx CCER */
+  TIMx->CCER = tmpccer;
+}
+
+/**
+  * @brief  Timer Output Compare 6 configuration
+  * @param  TIMx to select the TIM peripheral
+  * @param  OC_Config The output configuration structure
+  * @retval None
+  */
+static void TIM_OC6_SetConfig(TIM_TypeDef *TIMx,
+                              const TIM_OC_InitTypeDef *OC_Config)
+{
+  uint32_t tmpccmrx;
+  uint32_t tmpccer;
+  uint32_t tmpcr2;
+
+  /* Get the TIMx CCER register value */
+  tmpccer = TIMx->CCER;
+
+  /* Disable the output: Reset the CCxE Bit */
+  TIMx->CCER &= ~TIM_CCER_CC6E;
+
+  /* Get the TIMx CR2 register value */
+  tmpcr2 =  TIMx->CR2;
+  /* Get the TIMx CCMR1 register value */
+  tmpccmrx = TIMx->CCMR3;
+
+  /* Reset the Output Compare Mode Bits */
+  tmpccmrx &= ~(TIM_CCMR3_OC6M);
+  /* Select the Output Compare Mode */
+  tmpccmrx |= (OC_Config->OCMode << 8U);
+
+  /* Reset the Output Polarity level */
+  tmpccer &= (uint32_t)~TIM_CCER_CC6P;
+  /* Set the Output Compare Polarity */
+  tmpccer |= (OC_Config->OCPolarity << 20U);
+
+  if (IS_TIM_BREAK_INSTANCE(TIMx))
+  {
+    /* Reset the Output Compare IDLE State */
+    tmpcr2 &= ~TIM_CR2_OIS6;
+    /* Set the Output Idle state */
+    tmpcr2 |= (OC_Config->OCIdleState << 10U);
+  }
+
+  /* Write to TIMx CR2 */
+  TIMx->CR2 = tmpcr2;
+
+  /* Write to TIMx CCMR3 */
+  TIMx->CCMR3 = tmpccmrx;
+
+  /* Set the Capture Compare Register value */
+  TIMx->CCR6 = OC_Config->Pulse;
+
+  /* Write to TIMx CCER */
+  TIMx->CCER = tmpccer;
+}
+
+/**
+  * @brief  Slave Timer configuration function
+  * @param  htim TIM handle
+  * @param  sSlaveConfig Slave timer configuration
+  * @retval None
+  */
+static HAL_StatusTypeDef TIM_SlaveTimer_SetConfig(TIM_HandleTypeDef *htim,
+                                                  const TIM_SlaveConfigTypeDef *sSlaveConfig)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpsmcr;
+  uint32_t tmpccmr1;
+  uint32_t tmpccer;
+
+  /* Get the TIMx SMCR register value */
+  tmpsmcr = htim->Instance->SMCR;
+
+  /* Reset the Trigger Selection Bits */
+  tmpsmcr &= ~TIM_SMCR_TS;
+  /* Set the Input Trigger source */
+  tmpsmcr |= sSlaveConfig->InputTrigger;
+
+  /* Reset the slave mode Bits */
+  tmpsmcr &= ~TIM_SMCR_SMS;
+  /* Set the slave mode */
+  tmpsmcr |= sSlaveConfig->SlaveMode;
+
+  /* Write to TIMx SMCR */
+  htim->Instance->SMCR = tmpsmcr;
+
+  /* Configure the trigger prescaler, filter, and polarity */
+  switch (sSlaveConfig->InputTrigger)
+  {
+    case TIM_TS_ETRF:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CLOCKSOURCE_ETRMODE1_INSTANCE(htim->Instance));
+      assert_param(IS_TIM_TRIGGERPRESCALER(sSlaveConfig->TriggerPrescaler));
+      assert_param(IS_TIM_TRIGGERPOLARITY(sSlaveConfig->TriggerPolarity));
+      assert_param(IS_TIM_TRIGGERFILTER(sSlaveConfig->TriggerFilter));
+      /* Configure the ETR Trigger source */
+      TIM_ETR_SetConfig(htim->Instance,
+                        sSlaveConfig->TriggerPrescaler,
+                        sSlaveConfig->TriggerPolarity,
+                        sSlaveConfig->TriggerFilter);
+      break;
+    }
+
+    case TIM_TS_TI1F_ED:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
+      assert_param(IS_TIM_TRIGGERFILTER(sSlaveConfig->TriggerFilter));
+
+      if (sSlaveConfig->SlaveMode == TIM_SLAVEMODE_GATED)
+      {
+        return HAL_ERROR;
+      }
+
+      /* Disable the Channel 1: Reset the CC1E Bit */
+      tmpccer = htim->Instance->CCER;
+      htim->Instance->CCER &= ~TIM_CCER_CC1E;
+      tmpccmr1 = htim->Instance->CCMR1;
+
+      /* Set the filter */
+      tmpccmr1 &= ~TIM_CCMR1_IC1F;
+      tmpccmr1 |= ((sSlaveConfig->TriggerFilter) << 4U);
+
+      /* Write to TIMx CCMR1 and CCER registers */
+      htim->Instance->CCMR1 = tmpccmr1;
+      htim->Instance->CCER = tmpccer;
+      break;
+    }
+
+    case TIM_TS_TI1FP1:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
+      assert_param(IS_TIM_TRIGGERPOLARITY(sSlaveConfig->TriggerPolarity));
+      assert_param(IS_TIM_TRIGGERFILTER(sSlaveConfig->TriggerFilter));
+
+      /* Configure TI1 Filter and Polarity */
+      TIM_TI1_ConfigInputStage(htim->Instance,
+                               sSlaveConfig->TriggerPolarity,
+                               sSlaveConfig->TriggerFilter);
+      break;
+    }
+
+    case TIM_TS_TI2FP2:
+    {
+      /* Check the parameters */
+      assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
+      assert_param(IS_TIM_TRIGGERPOLARITY(sSlaveConfig->TriggerPolarity));
+      assert_param(IS_TIM_TRIGGERFILTER(sSlaveConfig->TriggerFilter));
+
+      /* Configure TI2 Filter and Polarity */
+      TIM_TI2_ConfigInputStage(htim->Instance,
+                               sSlaveConfig->TriggerPolarity,
+                               sSlaveConfig->TriggerFilter);
+      break;
+    }
+
+    case TIM_TS_ITR0:
+    case TIM_TS_ITR1:
+    case TIM_TS_ITR2:
+    case TIM_TS_ITR3:
+    case TIM_TS_ITR4:
+    case TIM_TS_ITR5:
+    case TIM_TS_ITR6:
+    case TIM_TS_ITR7:
+    case TIM_TS_ITR8:
+    case TIM_TS_ITR9:
+    case TIM_TS_ITR10:
+    case TIM_TS_ITR11:
+    case TIM_TS_ITR12:
+    case TIM_TS_ITR13:
+    {
+      /* Check the parameter */
+      assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  return status;
+}
+
+/**
+  * @brief  Configure the TI1 as Input.
+  * @param  TIMx to select the TIM peripheral.
+  * @param  TIM_ICPolarity The Input Polarity.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_ICPOLARITY_RISING
+  *            @arg TIM_ICPOLARITY_FALLING
+  *            @arg TIM_ICPOLARITY_BOTHEDGE
+  * @param  TIM_ICSelection specifies the input to be used.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_ICSELECTION_DIRECTTI: TIM Input 1 is selected to be connected to IC1.
+  *            @arg TIM_ICSELECTION_INDIRECTTI: TIM Input 1 is selected to be connected to IC2.
+  *            @arg TIM_ICSELECTION_TRC: TIM Input 1 is selected to be connected to TRC.
+  * @param  TIM_ICFilter Specifies the Input Capture Filter.
+  *          This parameter must be a value between 0x00 and 0x0F.
+  * @retval None
+  * @note TIM_ICFilter and TIM_ICPolarity are not used in INDIRECT mode as TI2FP1
+  *       (on channel2 path) is used as the input signal. Therefore CCMR1 must be
+  *        protected against un-initialized filter and polarity values.
+  */
+void TIM_TI1_SetConfig(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICSelection,
+                       uint32_t TIM_ICFilter)
+{
+  uint32_t tmpccmr1;
+  uint32_t tmpccer;
+
+  /* Disable the Channel 1: Reset the CC1E Bit */
+  tmpccer = TIMx->CCER;
+  TIMx->CCER &= ~TIM_CCER_CC1E;
+  tmpccmr1 = TIMx->CCMR1;
+
+  /* Select the Input */
+  if (IS_TIM_CC2_INSTANCE(TIMx) != RESET)
+  {
+    tmpccmr1 &= ~TIM_CCMR1_CC1S;
+    tmpccmr1 |= TIM_ICSelection;
+  }
+  else
+  {
+    tmpccmr1 |= TIM_CCMR1_CC1S_0;
+  }
+
+  /* Set the filter */
+  tmpccmr1 &= ~TIM_CCMR1_IC1F;
+  tmpccmr1 |= ((TIM_ICFilter << 4U) & TIM_CCMR1_IC1F);
+
+  /* Select the Polarity and set the CC1E Bit */
+  tmpccer &= ~(TIM_CCER_CC1P | TIM_CCER_CC1NP);
+  tmpccer |= (TIM_ICPolarity & (TIM_CCER_CC1P | TIM_CCER_CC1NP));
+
+  /* Write to TIMx CCMR1 and CCER registers */
+  TIMx->CCMR1 = tmpccmr1;
+  TIMx->CCER = tmpccer;
+}
+
+/**
+  * @brief  Configure the Polarity and Filter for TI1.
+  * @param  TIMx to select the TIM peripheral.
+  * @param  TIM_ICPolarity The Input Polarity.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_ICPOLARITY_RISING
+  *            @arg TIM_ICPOLARITY_FALLING
+  *            @arg TIM_ICPOLARITY_BOTHEDGE
+  * @param  TIM_ICFilter Specifies the Input Capture Filter.
+  *          This parameter must be a value between 0x00 and 0x0F.
+  * @retval None
+  */
+static void TIM_TI1_ConfigInputStage(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICFilter)
+{
+  uint32_t tmpccmr1;
+  uint32_t tmpccer;
+
+  /* Disable the Channel 1: Reset the CC1E Bit */
+  tmpccer = TIMx->CCER;
+  TIMx->CCER &= ~TIM_CCER_CC1E;
+  tmpccmr1 = TIMx->CCMR1;
+
+  /* Set the filter */
+  tmpccmr1 &= ~TIM_CCMR1_IC1F;
+  tmpccmr1 |= (TIM_ICFilter << 4U);
+
+  /* Select the Polarity and set the CC1E Bit */
+  tmpccer &= ~(TIM_CCER_CC1P | TIM_CCER_CC1NP);
+  tmpccer |= TIM_ICPolarity;
+
+  /* Write to TIMx CCMR1 and CCER registers */
+  TIMx->CCMR1 = tmpccmr1;
+  TIMx->CCER = tmpccer;
+}
+
+/**
+  * @brief  Configure the TI2 as Input.
+  * @param  TIMx to select the TIM peripheral
+  * @param  TIM_ICPolarity The Input Polarity.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_ICPOLARITY_RISING
+  *            @arg TIM_ICPOLARITY_FALLING
+  *            @arg TIM_ICPOLARITY_BOTHEDGE
+  * @param  TIM_ICSelection specifies the input to be used.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_ICSELECTION_DIRECTTI: TIM Input 2 is selected to be connected to IC2.
+  *            @arg TIM_ICSELECTION_INDIRECTTI: TIM Input 2 is selected to be connected to IC1.
+  *            @arg TIM_ICSELECTION_TRC: TIM Input 2 is selected to be connected to TRC.
+  * @param  TIM_ICFilter Specifies the Input Capture Filter.
+  *          This parameter must be a value between 0x00 and 0x0F.
+  * @retval None
+  * @note TIM_ICFilter and TIM_ICPolarity are not used in INDIRECT mode as TI1FP2
+  *       (on channel1 path) is used as the input signal. Therefore CCMR1 must be
+  *        protected against un-initialized filter and polarity values.
+  */
+static void TIM_TI2_SetConfig(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICSelection,
+                              uint32_t TIM_ICFilter)
+{
+  uint32_t tmpccmr1;
+  uint32_t tmpccer;
+
+  /* Disable the Channel 2: Reset the CC2E Bit */
+  tmpccer = TIMx->CCER;
+  TIMx->CCER &= ~TIM_CCER_CC2E;
+  tmpccmr1 = TIMx->CCMR1;
+
+  /* Select the Input */
+  tmpccmr1 &= ~TIM_CCMR1_CC2S;
+  tmpccmr1 |= (TIM_ICSelection << 8U);
+
+  /* Set the filter */
+  tmpccmr1 &= ~TIM_CCMR1_IC2F;
+  tmpccmr1 |= ((TIM_ICFilter << 12U) & TIM_CCMR1_IC2F);
+
+  /* Select the Polarity and set the CC2E Bit */
+  tmpccer &= ~(TIM_CCER_CC2P | TIM_CCER_CC2NP);
+  tmpccer |= ((TIM_ICPolarity << 4U) & (TIM_CCER_CC2P | TIM_CCER_CC2NP));
+
+  /* Write to TIMx CCMR1 and CCER registers */
+  TIMx->CCMR1 = tmpccmr1 ;
+  TIMx->CCER = tmpccer;
+}
+
+/**
+  * @brief  Configure the Polarity and Filter for TI2.
+  * @param  TIMx to select the TIM peripheral.
+  * @param  TIM_ICPolarity The Input Polarity.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_ICPOLARITY_RISING
+  *            @arg TIM_ICPOLARITY_FALLING
+  *            @arg TIM_ICPOLARITY_BOTHEDGE
+  * @param  TIM_ICFilter Specifies the Input Capture Filter.
+  *          This parameter must be a value between 0x00 and 0x0F.
+  * @retval None
+  */
+static void TIM_TI2_ConfigInputStage(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICFilter)
+{
+  uint32_t tmpccmr1;
+  uint32_t tmpccer;
+
+  /* Disable the Channel 2: Reset the CC2E Bit */
+  tmpccer = TIMx->CCER;
+  TIMx->CCER &= ~TIM_CCER_CC2E;
+  tmpccmr1 = TIMx->CCMR1;
+
+  /* Set the filter */
+  tmpccmr1 &= ~TIM_CCMR1_IC2F;
+  tmpccmr1 |= (TIM_ICFilter << 12U);
+
+  /* Select the Polarity and set the CC2E Bit */
+  tmpccer &= ~(TIM_CCER_CC2P | TIM_CCER_CC2NP);
+  tmpccer |= (TIM_ICPolarity << 4U);
+
+  /* Write to TIMx CCMR1 and CCER registers */
+  TIMx->CCMR1 = tmpccmr1 ;
+  TIMx->CCER = tmpccer;
+}
+
+/**
+  * @brief  Configure the TI3 as Input.
+  * @param  TIMx to select the TIM peripheral
+  * @param  TIM_ICPolarity The Input Polarity.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_ICPOLARITY_RISING
+  *            @arg TIM_ICPOLARITY_FALLING
+  *            @arg TIM_ICPOLARITY_BOTHEDGE
+  * @param  TIM_ICSelection specifies the input to be used.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_ICSELECTION_DIRECTTI: TIM Input 3 is selected to be connected to IC3.
+  *            @arg TIM_ICSELECTION_INDIRECTTI: TIM Input 3 is selected to be connected to IC4.
+  *            @arg TIM_ICSELECTION_TRC: TIM Input 3 is selected to be connected to TRC.
+  * @param  TIM_ICFilter Specifies the Input Capture Filter.
+  *          This parameter must be a value between 0x00 and 0x0F.
+  * @retval None
+  * @note TIM_ICFilter and TIM_ICPolarity are not used in INDIRECT mode as TI3FP4
+  *       (on channel1 path) is used as the input signal. Therefore CCMR2 must be
+  *        protected against un-initialized filter and polarity values.
+  */
+static void TIM_TI3_SetConfig(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICSelection,
+                              uint32_t TIM_ICFilter)
+{
+  uint32_t tmpccmr2;
+  uint32_t tmpccer;
+
+  /* Disable the Channel 3: Reset the CC3E Bit */
+  tmpccer = TIMx->CCER;
+  TIMx->CCER &= ~TIM_CCER_CC3E;
+  tmpccmr2 = TIMx->CCMR2;
+
+  /* Select the Input */
+  tmpccmr2 &= ~TIM_CCMR2_CC3S;
+  tmpccmr2 |= TIM_ICSelection;
+
+  /* Set the filter */
+  tmpccmr2 &= ~TIM_CCMR2_IC3F;
+  tmpccmr2 |= ((TIM_ICFilter << 4U) & TIM_CCMR2_IC3F);
+
+  /* Select the Polarity and set the CC3E Bit */
+  tmpccer &= ~(TIM_CCER_CC3P | TIM_CCER_CC3NP);
+  tmpccer |= ((TIM_ICPolarity << 8U) & (TIM_CCER_CC3P | TIM_CCER_CC3NP));
+
+  /* Write to TIMx CCMR2 and CCER registers */
+  TIMx->CCMR2 = tmpccmr2;
+  TIMx->CCER = tmpccer;
+}
+
+/**
+  * @brief  Configure the TI4 as Input.
+  * @param  TIMx to select the TIM peripheral
+  * @param  TIM_ICPolarity The Input Polarity.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_ICPOLARITY_RISING
+  *            @arg TIM_ICPOLARITY_FALLING
+  *            @arg TIM_ICPOLARITY_BOTHEDGE
+  * @param  TIM_ICSelection specifies the input to be used.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_ICSELECTION_DIRECTTI: TIM Input 4 is selected to be connected to IC4.
+  *            @arg TIM_ICSELECTION_INDIRECTTI: TIM Input 4 is selected to be connected to IC3.
+  *            @arg TIM_ICSELECTION_TRC: TIM Input 4 is selected to be connected to TRC.
+  * @param  TIM_ICFilter Specifies the Input Capture Filter.
+  *          This parameter must be a value between 0x00 and 0x0F.
+  * @note TIM_ICFilter and TIM_ICPolarity are not used in INDIRECT mode as TI4FP3
+  *       (on channel1 path) is used as the input signal. Therefore CCMR2 must be
+  *        protected against un-initialized filter and polarity values.
+  * @retval None
+  */
+static void TIM_TI4_SetConfig(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICSelection,
+                              uint32_t TIM_ICFilter)
+{
+  uint32_t tmpccmr2;
+  uint32_t tmpccer;
+
+  /* Disable the Channel 4: Reset the CC4E Bit */
+  tmpccer = TIMx->CCER;
+  TIMx->CCER &= ~TIM_CCER_CC4E;
+  tmpccmr2 = TIMx->CCMR2;
+
+  /* Select the Input */
+  tmpccmr2 &= ~TIM_CCMR2_CC4S;
+  tmpccmr2 |= (TIM_ICSelection << 8U);
+
+  /* Set the filter */
+  tmpccmr2 &= ~TIM_CCMR2_IC4F;
+  tmpccmr2 |= ((TIM_ICFilter << 12U) & TIM_CCMR2_IC4F);
+
+  /* Select the Polarity and set the CC4E Bit */
+  tmpccer &= ~(TIM_CCER_CC4P | TIM_CCER_CC4NP);
+  tmpccer |= ((TIM_ICPolarity << 12U) & (TIM_CCER_CC4P | TIM_CCER_CC4NP));
+
+  /* Write to TIMx CCMR2 and CCER registers */
+  TIMx->CCMR2 = tmpccmr2;
+  TIMx->CCER = tmpccer ;
+}
+
+/**
+  * @brief  Selects the Input Trigger source
+  * @param  TIMx to select the TIM peripheral
+  * @param  InputTriggerSource The Input Trigger source.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_TS_ITR0: Internal Trigger 0
+  *            @arg TIM_TS_ITR1: Internal Trigger 1
+  *            @arg TIM_TS_ITR2: Internal Trigger 2
+  *            @arg TIM_TS_ITR3: Internal Trigger 3
+  *            @arg TIM_TS_ITR4: Internal Trigger 4  (*)
+  *            @arg TIM_TS_ITR5: Internal Trigger 5
+  *            @arg TIM_TS_ITR6: Internal Trigger 6
+  *            @arg TIM_TS_ITR7: Internal Trigger 7
+  *            @arg TIM_TS_ITR8: Internal Trigger 8  (*)
+  *            @arg TIM_TS_ITR9: Internal Trigger 9  (*)
+  *            @arg TIM_TS_ITR10: Internal Trigger 10 (*)
+  *            @arg TIM_TS_ITR11: Internal Trigger 11 (*)
+  *            @arg TIM_TS_ITR12: Internal Trigger 12 (*)
+  *            @arg TIM_TS_ITR13: Internal Trigger 13 (*)
+  *            @arg TIM_TS_TI1F_ED: TI1 Edge Detector
+  *            @arg TIM_TS_TI1FP1: Filtered Timer Input 1
+  *            @arg TIM_TS_TI2FP2: Filtered Timer Input 2
+  *            @arg TIM_TS_ETRF: External Trigger input
+  *
+  *       (*)  Value not defined in all devices.
+  *
+  * @retval None
+  */
+static void TIM_ITRx_SetConfig(TIM_TypeDef *TIMx, uint32_t InputTriggerSource)
+{
+  uint32_t tmpsmcr;
+
+  /* Get the TIMx SMCR register value */
+  tmpsmcr = TIMx->SMCR;
+  /* Reset the TS Bits */
+  tmpsmcr &= ~TIM_SMCR_TS;
+  /* Set the Input Trigger source and the slave mode*/
+  tmpsmcr |= (InputTriggerSource | TIM_SLAVEMODE_EXTERNAL1);
+  /* Write to TIMx SMCR */
+  TIMx->SMCR = tmpsmcr;
+}
+/**
+  * @brief  Configures the TIMx External Trigger (ETR).
+  * @param  TIMx to select the TIM peripheral
+  * @param  TIM_ExtTRGPrescaler The external Trigger Prescaler.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_ETRPRESCALER_DIV1: ETRP Prescaler OFF.
+  *            @arg TIM_ETRPRESCALER_DIV2: ETRP frequency divided by 2.
+  *            @arg TIM_ETRPRESCALER_DIV4: ETRP frequency divided by 4.
+  *            @arg TIM_ETRPRESCALER_DIV8: ETRP frequency divided by 8.
+  * @param  TIM_ExtTRGPolarity The external Trigger Polarity.
+  *          This parameter can be one of the following values:
+  *            @arg TIM_ETRPOLARITY_INVERTED: active low or falling edge active.
+  *            @arg TIM_ETRPOLARITY_NONINVERTED: active high or rising edge active.
+  * @param  ExtTRGFilter External Trigger Filter.
+  *          This parameter must be a value between 0x00 and 0x0F
+  * @retval None
+  */
+void TIM_ETR_SetConfig(TIM_TypeDef *TIMx, uint32_t TIM_ExtTRGPrescaler,
+                       uint32_t TIM_ExtTRGPolarity, uint32_t ExtTRGFilter)
+{
+  uint32_t tmpsmcr;
+
+  tmpsmcr = TIMx->SMCR;
+
+  /* Reset the ETR Bits */
+  tmpsmcr &= ~(TIM_SMCR_ETF | TIM_SMCR_ETPS | TIM_SMCR_ECE | TIM_SMCR_ETP);
+
+  /* Set the Prescaler, the Filter value and the Polarity */
+  tmpsmcr |= (uint32_t)(TIM_ExtTRGPrescaler | (TIM_ExtTRGPolarity | (ExtTRGFilter << 8U)));
+
+  /* Write to TIMx SMCR */
+  TIMx->SMCR = tmpsmcr;
+}
+
+/**
+  * @brief  Enables or disables the TIM Capture Compare Channel x.
+  * @param  TIMx to select the TIM peripheral
+  * @param  Channel specifies the TIM Channel
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1
+  *            @arg TIM_CHANNEL_2: TIM Channel 2
+  *            @arg TIM_CHANNEL_3: TIM Channel 3
+  *            @arg TIM_CHANNEL_4: TIM Channel 4
+  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
+  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
+  * @param  ChannelState specifies the TIM Channel CCxE bit new state.
+  *          This parameter can be: TIM_CCx_ENABLE or TIM_CCx_DISABLE.
+  * @retval None
+  */
+void TIM_CCxChannelCmd(TIM_TypeDef *TIMx, uint32_t Channel, uint32_t ChannelState)
+{
+  uint32_t tmp;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CC1_INSTANCE(TIMx));
+  assert_param(IS_TIM_CHANNELS(Channel));
+
+  tmp = TIM_CCER_CC1E << (Channel & 0x1FU); /* 0x1FU = 31 bits max shift */
+
+  /* Reset the CCxE Bit */
+  TIMx->CCER &= ~tmp;
+
+  /* Set or reset the CCxE Bit */
+  TIMx->CCER |= (uint32_t)(ChannelState << (Channel & 0x1FU)); /* 0x1FU = 31 bits max shift */
+}
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+/**
+  * @brief  Reset interrupt callbacks to the legacy weak callbacks.
+  * @param  htim pointer to a TIM_HandleTypeDef structure that contains
+  *                the configuration information for TIM module.
+  * @retval None
+  */
+void TIM_ResetCallback(TIM_HandleTypeDef *htim)
+{
+  /* Reset the TIM callback to the legacy weak callbacks */
+  htim->PeriodElapsedCallback             = HAL_TIM_PeriodElapsedCallback;
+  htim->PeriodElapsedHalfCpltCallback     = HAL_TIM_PeriodElapsedHalfCpltCallback;
+  htim->TriggerCallback                   = HAL_TIM_TriggerCallback;
+  htim->TriggerHalfCpltCallback           = HAL_TIM_TriggerHalfCpltCallback;
+  htim->IC_CaptureCallback                = HAL_TIM_IC_CaptureCallback;
+  htim->IC_CaptureHalfCpltCallback        = HAL_TIM_IC_CaptureHalfCpltCallback;
+  htim->OC_DelayElapsedCallback           = HAL_TIM_OC_DelayElapsedCallback;
+  htim->PWM_PulseFinishedCallback         = HAL_TIM_PWM_PulseFinishedCallback;
+  htim->PWM_PulseFinishedHalfCpltCallback = HAL_TIM_PWM_PulseFinishedHalfCpltCallback;
+  htim->ErrorCallback                     = HAL_TIM_ErrorCallback;
+  htim->CommutationCallback               = HAL_TIMEx_CommutCallback;
+  htim->CommutationHalfCpltCallback       = HAL_TIMEx_CommutHalfCpltCallback;
+  htim->BreakCallback                     = HAL_TIMEx_BreakCallback;
+  htim->Break2Callback                    = HAL_TIMEx_Break2Callback;
+}
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+/**
+  * @}
+  */
+
+#endif /* HAL_TIM_MODULE_ENABLED */
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
Index: ctrl/firmware/Main/CubeMX/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim_ex.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim_ex.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim_ex.c	(revision 54)
@@ -0,0 +1,2921 @@
+/**
+  ******************************************************************************
+  * @file    stm32h7xx_hal_tim_ex.c
+  * @author  MCD Application Team
+  * @brief   TIM HAL module driver.
+  *          This file provides firmware functions to manage the following
+  *          functionalities of the Timer Extended peripheral:
+  *           + Time Hall Sensor Interface Initialization
+  *           + Time Hall Sensor Interface Start
+  *           + Time Complementary signal break and dead time configuration
+  *           + Time Master and Slave synchronization configuration
+  *           + Time Output Compare/PWM Channel Configuration (for channels 5 and 6)
+  *           + Timer remapping capabilities configuration
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2017 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.
+  *
+  ******************************************************************************
+  @verbatim
+  ==============================================================================
+                      ##### TIMER Extended features #####
+  ==============================================================================
+  [..]
+    The Timer Extended features include:
+    (#) Complementary outputs with programmable dead-time for :
+        (++) Output Compare
+        (++) PWM generation (Edge and Center-aligned Mode)
+        (++) One-pulse mode output
+    (#) Synchronization circuit to control the timer with external signals and to
+        interconnect several timers together.
+    (#) Break input to put the timer output signals in reset state or in a known state.
+    (#) Supports incremental (quadrature) encoder and hall-sensor circuitry for
+        positioning purposes
+
+            ##### How to use this driver #####
+  ==============================================================================
+    [..]
+     (#) Initialize the TIM low level resources by implementing the following functions
+         depending on the selected feature:
+           (++) Hall Sensor output : HAL_TIMEx_HallSensor_MspInit()
+
+     (#) Initialize the TIM low level resources :
+        (##) Enable the TIM interface clock using __HAL_RCC_TIMx_CLK_ENABLE();
+        (##) TIM pins configuration
+            (+++) Enable the clock for the TIM GPIOs using the following function:
+              __HAL_RCC_GPIOx_CLK_ENABLE();
+            (+++) Configure these TIM pins in Alternate function mode using HAL_GPIO_Init();
+
+     (#) The external Clock can be configured, if needed (the default clock is the
+         internal clock from the APBx), using the following function:
+         HAL_TIM_ConfigClockSource, the clock configuration should be done before
+         any start function.
+
+     (#) Configure the TIM in the desired functioning mode using one of the
+         initialization function of this driver:
+          (++) HAL_TIMEx_HallSensor_Init() and HAL_TIMEx_ConfigCommutEvent(): to use the
+               Timer Hall Sensor Interface and the commutation event with the corresponding
+               Interrupt and DMA request if needed (Note that One Timer is used to interface
+               with the Hall sensor Interface and another Timer should be used to use
+               the commutation event).
+
+     (#) Activate the TIM peripheral using one of the start functions:
+           (++) Complementary Output Compare : HAL_TIMEx_OCN_Start(), HAL_TIMEx_OCN_Start_DMA(),
+                HAL_TIMEx_OCN_Start_IT()
+           (++) Complementary PWM generation : HAL_TIMEx_PWMN_Start(), HAL_TIMEx_PWMN_Start_DMA(),
+                HAL_TIMEx_PWMN_Start_IT()
+           (++) Complementary One-pulse mode output : HAL_TIMEx_OnePulseN_Start(), HAL_TIMEx_OnePulseN_Start_IT()
+           (++) Hall Sensor output : HAL_TIMEx_HallSensor_Start(), HAL_TIMEx_HallSensor_Start_DMA(),
+                HAL_TIMEx_HallSensor_Start_IT().
+
+  @endverbatim
+  ******************************************************************************
+  */
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32h7xx_hal.h"
+
+/** @addtogroup STM32H7xx_HAL_Driver
+  * @{
+  */
+
+/** @defgroup TIMEx TIMEx
+  * @brief TIM Extended HAL module driver
+  * @{
+  */
+
+#ifdef HAL_TIM_MODULE_ENABLED
+
+/* Private typedef -----------------------------------------------------------*/
+/* Private define ------------------------------------------------------------*/
+#if defined(TIM_BDTR_BKBID)
+/* Private constants ---------------------------------------------------------*/
+/** @defgroup TIMEx_Private_Constants TIM Extended Private Constants
+  * @{
+  */
+/* Timeout for break input rearm */
+#define TIM_BREAKINPUT_REARM_TIMEOUT    5UL /* 5 milliseconds */
+/**
+  * @}
+  */
+/* End of private constants --------------------------------------------------*/
+
+#endif /* TIM_BDTR_BKBID */
+/* Private macros ------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+static void TIM_DMADelayPulseNCplt(DMA_HandleTypeDef *hdma);
+static void TIM_DMAErrorCCxN(DMA_HandleTypeDef *hdma);
+static void TIM_CCxNChannelCmd(TIM_TypeDef *TIMx, uint32_t Channel, uint32_t ChannelNState);
+
+/* Exported functions --------------------------------------------------------*/
+/** @defgroup TIMEx_Exported_Functions TIM Extended Exported Functions
+  * @{
+  */
+
+/** @defgroup TIMEx_Exported_Functions_Group1 Extended Timer Hall Sensor functions
+  * @brief    Timer Hall Sensor functions
+  *
+@verbatim
+  ==============================================================================
+                      ##### Timer Hall Sensor functions #####
+  ==============================================================================
+  [..]
+    This section provides functions allowing to:
+    (+) Initialize and configure TIM HAL Sensor.
+    (+) De-initialize TIM HAL Sensor.
+    (+) Start the Hall Sensor Interface.
+    (+) Stop the Hall Sensor Interface.
+    (+) Start the Hall Sensor Interface and enable interrupts.
+    (+) Stop the Hall Sensor Interface and disable interrupts.
+    (+) Start the Hall Sensor Interface and enable DMA transfers.
+    (+) Stop the Hall Sensor Interface and disable DMA transfers.
+
+@endverbatim
+  * @{
+  */
+/**
+  * @brief  Initializes the TIM Hall Sensor Interface and initialize the associated handle.
+  * @note   When the timer instance is initialized in Hall Sensor Interface mode,
+  *         timer channels 1 and channel 2 are reserved and cannot be used for
+  *         other purpose.
+  * @param  htim TIM Hall Sensor Interface handle
+  * @param  sConfig TIM Hall Sensor configuration structure
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Init(TIM_HandleTypeDef *htim, const TIM_HallSensor_InitTypeDef *sConfig)
+{
+  TIM_OC_InitTypeDef OC_Config;
+
+  /* Check the TIM handle allocation */
+  if (htim == NULL)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Check the parameters */
+  assert_param(IS_TIM_HALL_SENSOR_INTERFACE_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
+  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
+  assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));
+  assert_param(IS_TIM_IC_POLARITY(sConfig->IC1Polarity));
+  assert_param(IS_TIM_PERIOD(htim, htim->Init.Period));
+  assert_param(IS_TIM_IC_PRESCALER(sConfig->IC1Prescaler));
+  assert_param(IS_TIM_IC_FILTER(sConfig->IC1Filter));
+
+  if (htim->State == HAL_TIM_STATE_RESET)
+  {
+    /* Allocate lock resource and initialize it */
+    htim->Lock = HAL_UNLOCKED;
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+    /* Reset interrupt callbacks to legacy week callbacks */
+    TIM_ResetCallback(htim);
+
+    if (htim->HallSensor_MspInitCallback == NULL)
+    {
+      htim->HallSensor_MspInitCallback = HAL_TIMEx_HallSensor_MspInit;
+    }
+    /* Init the low level hardware : GPIO, CLOCK, NVIC */
+    htim->HallSensor_MspInitCallback(htim);
+#else
+    /* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
+    HAL_TIMEx_HallSensor_MspInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+  }
+
+  /* Set the TIM state */
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Configure the Time base in the Encoder Mode */
+  TIM_Base_SetConfig(htim->Instance, &htim->Init);
+
+  /* Configure the Channel 1 as Input Channel to interface with the three Outputs of the  Hall sensor */
+  TIM_TI1_SetConfig(htim->Instance, sConfig->IC1Polarity, TIM_ICSELECTION_TRC, sConfig->IC1Filter);
+
+  /* Reset the IC1PSC Bits */
+  htim->Instance->CCMR1 &= ~TIM_CCMR1_IC1PSC;
+  /* Set the IC1PSC value */
+  htim->Instance->CCMR1 |= sConfig->IC1Prescaler;
+
+  /* Enable the Hall sensor interface (XOR function of the three inputs) */
+  htim->Instance->CR2 |= TIM_CR2_TI1S;
+
+  /* Select the TIM_TS_TI1F_ED signal as Input trigger for the TIM */
+  htim->Instance->SMCR &= ~TIM_SMCR_TS;
+  htim->Instance->SMCR |= TIM_TS_TI1F_ED;
+
+  /* Use the TIM_TS_TI1F_ED signal to reset the TIM counter each edge detection */
+  htim->Instance->SMCR &= ~TIM_SMCR_SMS;
+  htim->Instance->SMCR |= TIM_SLAVEMODE_RESET;
+
+  /* Program channel 2 in PWM 2 mode with the desired Commutation_Delay*/
+  OC_Config.OCFastMode = TIM_OCFAST_DISABLE;
+  OC_Config.OCIdleState = TIM_OCIDLESTATE_RESET;
+  OC_Config.OCMode = TIM_OCMODE_PWM2;
+  OC_Config.OCNIdleState = TIM_OCNIDLESTATE_RESET;
+  OC_Config.OCNPolarity = TIM_OCNPOLARITY_HIGH;
+  OC_Config.OCPolarity = TIM_OCPOLARITY_HIGH;
+  OC_Config.Pulse = sConfig->Commutation_Delay;
+
+  TIM_OC2_SetConfig(htim->Instance, &OC_Config);
+
+  /* Select OC2REF as trigger output on TRGO: write the MMS bits in the TIMx_CR2
+    register to 101 */
+  htim->Instance->CR2 &= ~TIM_CR2_MMS;
+  htim->Instance->CR2 |= TIM_TRGO_OC2REF;
+
+  /* Initialize the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_READY;
+
+  /* Initialize the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Initialize the TIM state*/
+  htim->State = HAL_TIM_STATE_READY;
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  DeInitializes the TIM Hall Sensor interface
+  * @param  htim TIM Hall Sensor Interface handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_DeInit(TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_INSTANCE(htim->Instance));
+
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Disable the TIM Peripheral Clock */
+  __HAL_TIM_DISABLE(htim);
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  if (htim->HallSensor_MspDeInitCallback == NULL)
+  {
+    htim->HallSensor_MspDeInitCallback = HAL_TIMEx_HallSensor_MspDeInit;
+  }
+  /* DeInit the low level hardware */
+  htim->HallSensor_MspDeInitCallback(htim);
+#else
+  /* DeInit the low level hardware: GPIO, CLOCK, NVIC */
+  HAL_TIMEx_HallSensor_MspDeInit(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  /* Change the DMA burst operation state */
+  htim->DMABurstState = HAL_DMA_BURST_STATE_RESET;
+
+  /* Change the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_RESET);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_RESET);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_RESET);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_RESET);
+
+  /* Change TIM state */
+  htim->State = HAL_TIM_STATE_RESET;
+
+  /* Release Lock */
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Initializes the TIM Hall Sensor MSP.
+  * @param  htim TIM Hall Sensor Interface handle
+  * @retval None
+  */
+__weak void HAL_TIMEx_HallSensor_MspInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIMEx_HallSensor_MspInit could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  DeInitializes TIM Hall Sensor MSP.
+  * @param  htim TIM Hall Sensor Interface handle
+  * @retval None
+  */
+__weak void HAL_TIMEx_HallSensor_MspDeInit(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIMEx_HallSensor_MspDeInit could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Starts the TIM Hall Sensor Interface.
+  * @param  htim TIM Hall Sensor Interface handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Start(TIM_HandleTypeDef *htim)
+{
+  uint32_t tmpsmcr;
+  HAL_TIM_ChannelStateTypeDef channel_1_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef channel_2_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_2);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_1_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_2_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_2);
+
+  /* Check the parameters */
+  assert_param(IS_TIM_HALL_SENSOR_INTERFACE_INSTANCE(htim->Instance));
+
+  /* Check the TIM channels state */
+  if ((channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (channel_2_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_2_state != HAL_TIM_CHANNEL_STATE_READY))
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  /* Enable the Input Capture channel 1
+  (in the Hall Sensor Interface the three possible channels that can be used are TIM_CHANNEL_1,
+  TIM_CHANNEL_2 and TIM_CHANNEL_3) */
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
+
+  /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+  if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+  {
+    tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+    if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+  else
+  {
+    __HAL_TIM_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM Hall sensor Interface.
+  * @param  htim TIM Hall Sensor Interface handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Stop(TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_HALL_SENSOR_INTERFACE_INSTANCE(htim->Instance));
+
+  /* Disable the Input Capture channels 1, 2 and 3
+  (in the Hall Sensor Interface the three possible channels that can be used are TIM_CHANNEL_1,
+  TIM_CHANNEL_2 and TIM_CHANNEL_3) */
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Starts the TIM Hall Sensor Interface in interrupt mode.
+  * @param  htim TIM Hall Sensor Interface handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Start_IT(TIM_HandleTypeDef *htim)
+{
+  uint32_t tmpsmcr;
+  HAL_TIM_ChannelStateTypeDef channel_1_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef channel_2_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_2);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_1_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_2_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_2);
+
+  /* Check the parameters */
+  assert_param(IS_TIM_HALL_SENSOR_INTERFACE_INSTANCE(htim->Instance));
+
+  /* Check the TIM channels state */
+  if ((channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (channel_2_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_2_state != HAL_TIM_CHANNEL_STATE_READY))
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  /* Enable the capture compare Interrupts 1 event */
+  __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
+
+  /* Enable the Input Capture channel 1
+  (in the Hall Sensor Interface the three possible channels that can be used are TIM_CHANNEL_1,
+  TIM_CHANNEL_2 and TIM_CHANNEL_3) */
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
+
+  /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+  if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+  {
+    tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+    if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+  else
+  {
+    __HAL_TIM_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM Hall Sensor Interface in interrupt mode.
+  * @param  htim TIM Hall Sensor Interface handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Stop_IT(TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_HALL_SENSOR_INTERFACE_INSTANCE(htim->Instance));
+
+  /* Disable the Input Capture channel 1
+  (in the Hall Sensor Interface the three possible channels that can be used are TIM_CHANNEL_1,
+  TIM_CHANNEL_2 and TIM_CHANNEL_3) */
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
+
+  /* Disable the capture compare Interrupts event */
+  __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC1);
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Starts the TIM Hall Sensor Interface in DMA mode.
+  * @param  htim TIM Hall Sensor Interface handle
+  * @param  pData The destination Buffer address.
+  * @param  Length The length of data to be transferred from TIM peripheral to memory.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Start_DMA(TIM_HandleTypeDef *htim, uint32_t *pData, uint16_t Length)
+{
+  uint32_t tmpsmcr;
+  HAL_TIM_ChannelStateTypeDef channel_1_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_1_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_1);
+
+  /* Check the parameters */
+  assert_param(IS_TIM_HALL_SENSOR_INTERFACE_INSTANCE(htim->Instance));
+
+  /* Set the TIM channel state */
+  if ((channel_1_state == HAL_TIM_CHANNEL_STATE_BUSY)
+      || (complementary_channel_1_state == HAL_TIM_CHANNEL_STATE_BUSY))
+  {
+    return HAL_BUSY;
+  }
+  else if ((channel_1_state == HAL_TIM_CHANNEL_STATE_READY)
+           && (complementary_channel_1_state == HAL_TIM_CHANNEL_STATE_READY))
+  {
+    if ((pData == NULL) || (Length == 0U))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+    }
+  }
+  else
+  {
+    return HAL_ERROR;
+  }
+
+  /* Enable the Input Capture channel 1
+  (in the Hall Sensor Interface the three possible channels that can be used are TIM_CHANNEL_1,
+  TIM_CHANNEL_2 and TIM_CHANNEL_3) */
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
+
+  /* Set the DMA Input Capture 1 Callbacks */
+  htim->hdma[TIM_DMA_ID_CC1]->XferCpltCallback = TIM_DMACaptureCplt;
+  htim->hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = TIM_DMACaptureHalfCplt;
+  /* Set the DMA error callback */
+  htim->hdma[TIM_DMA_ID_CC1]->XferErrorCallback = TIM_DMAError ;
+
+  /* Enable the DMA stream for Capture 1*/
+  if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)&htim->Instance->CCR1, (uint32_t)pData, Length) != HAL_OK)
+  {
+    /* Return error status */
+    return HAL_ERROR;
+  }
+  /* Enable the capture compare 1 Interrupt */
+  __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC1);
+
+  /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+  if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+  {
+    tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+    if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+  else
+  {
+    __HAL_TIM_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM Hall Sensor Interface in DMA mode.
+  * @param  htim TIM Hall Sensor Interface handle
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_HallSensor_Stop_DMA(TIM_HandleTypeDef *htim)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_HALL_SENSOR_INTERFACE_INSTANCE(htim->Instance));
+
+  /* Disable the Input Capture channel 1
+  (in the Hall Sensor Interface the three possible channels that can be used are TIM_CHANNEL_1,
+  TIM_CHANNEL_2 and TIM_CHANNEL_3) */
+  TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
+
+
+  /* Disable the capture compare Interrupts 1 event */
+  __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC1);
+
+  (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC1]);
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM channel state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @}
+  */
+
+/** @defgroup TIMEx_Exported_Functions_Group2 Extended Timer Complementary Output Compare functions
+  *  @brief   Timer Complementary Output Compare functions
+  *
+@verbatim
+  ==============================================================================
+              ##### Timer Complementary Output Compare functions #####
+  ==============================================================================
+  [..]
+    This section provides functions allowing to:
+    (+) Start the Complementary Output Compare/PWM.
+    (+) Stop the Complementary Output Compare/PWM.
+    (+) Start the Complementary Output Compare/PWM and enable interrupts.
+    (+) Stop the Complementary Output Compare/PWM and disable interrupts.
+    (+) Start the Complementary Output Compare/PWM and enable DMA transfers.
+    (+) Stop the Complementary Output Compare/PWM and disable DMA transfers.
+
+@endverbatim
+  * @{
+  */
+
+/**
+  * @brief  Starts the TIM Output Compare signal generation on the complementary
+  *         output.
+  * @param  htim TIM Output Compare handle
+  * @param  Channel TIM Channel to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_OCN_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, Channel));
+
+  /* Check the TIM complementary channel state */
+  if (TIM_CHANNEL_N_STATE_GET(htim, Channel) != HAL_TIM_CHANNEL_STATE_READY)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM complementary channel state */
+  TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  /* Enable the Capture compare channel N */
+  TIM_CCxNChannelCmd(htim->Instance, Channel, TIM_CCxN_ENABLE);
+
+  /* Enable the Main Output */
+  __HAL_TIM_MOE_ENABLE(htim);
+
+  /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+  if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+  {
+    tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+    if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+  else
+  {
+    __HAL_TIM_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM Output Compare signal generation on the complementary
+  *         output.
+  * @param  htim TIM handle
+  * @param  Channel TIM Channel to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_OCN_Stop(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, Channel));
+
+  /* Disable the Capture compare channel N */
+  TIM_CCxNChannelCmd(htim->Instance, Channel, TIM_CCxN_DISABLE);
+
+  /* Disable the Main Output */
+  __HAL_TIM_MOE_DISABLE(htim);
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM complementary channel state */
+  TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Starts the TIM Output Compare signal generation in interrupt mode
+  *         on the complementary output.
+  * @param  htim TIM OC handle
+  * @param  Channel TIM Channel to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_OCN_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, Channel));
+
+  /* Check the TIM complementary channel state */
+  if (TIM_CHANNEL_N_STATE_GET(htim, Channel) != HAL_TIM_CHANNEL_STATE_READY)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM complementary channel state */
+  TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Enable the TIM Output Compare interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Enable the TIM Output Compare interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Enable the TIM Output Compare interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC3);
+      break;
+    }
+
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Enable the TIM Break interrupt */
+    __HAL_TIM_ENABLE_IT(htim, TIM_IT_BREAK);
+
+    /* Enable the Capture compare channel N */
+    TIM_CCxNChannelCmd(htim->Instance, Channel, TIM_CCxN_ENABLE);
+
+    /* Enable the Main Output */
+    __HAL_TIM_MOE_ENABLE(htim);
+
+    /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+    if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+    {
+      tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+      if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+      {
+        __HAL_TIM_ENABLE(htim);
+      }
+    }
+    else
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Stops the TIM Output Compare signal generation in interrupt mode
+  *         on the complementary output.
+  * @param  htim TIM Output Compare handle
+  * @param  Channel TIM Channel to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_OCN_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpccer;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, Channel));
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Disable the TIM Output Compare interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Disable the TIM Output Compare interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Disable the TIM Output Compare interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC3);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Disable the Capture compare channel N */
+    TIM_CCxNChannelCmd(htim->Instance, Channel, TIM_CCxN_DISABLE);
+
+    /* Disable the TIM Break interrupt (only if no more channel is active) */
+    tmpccer = htim->Instance->CCER;
+    if ((tmpccer & TIM_CCER_CCxNE_MASK) == (uint32_t)RESET)
+    {
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_BREAK);
+    }
+
+    /* Disable the Main Output */
+    __HAL_TIM_MOE_DISABLE(htim);
+
+    /* Disable the Peripheral */
+    __HAL_TIM_DISABLE(htim);
+
+    /* Set the TIM complementary channel state */
+    TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Starts the TIM Output Compare signal generation in DMA mode
+  *         on the complementary output.
+  * @param  htim TIM Output Compare handle
+  * @param  Channel TIM Channel to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  * @param  pData The source Buffer address.
+  * @param  Length The length of data to be transferred from memory to TIM peripheral
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_OCN_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, const uint32_t *pData,
+                                          uint16_t Length)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, Channel));
+
+  /* Set the TIM complementary channel state */
+  if (TIM_CHANNEL_N_STATE_GET(htim, Channel) == HAL_TIM_CHANNEL_STATE_BUSY)
+  {
+    return HAL_BUSY;
+  }
+  else if (TIM_CHANNEL_N_STATE_GET(htim, Channel) == HAL_TIM_CHANNEL_STATE_READY)
+  {
+    if ((pData == NULL) || (Length == 0U))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+    }
+  }
+  else
+  {
+    return HAL_ERROR;
+  }
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC1]->XferCpltCallback = TIM_DMADelayPulseNCplt;
+      htim->hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC1]->XferErrorCallback = TIM_DMAErrorCCxN ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)pData, (uint32_t)&htim->Instance->CCR1,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Output Compare DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC2]->XferCpltCallback = TIM_DMADelayPulseNCplt;
+      htim->hdma[TIM_DMA_ID_CC2]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC2]->XferErrorCallback = TIM_DMAErrorCCxN ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC2], (uint32_t)pData, (uint32_t)&htim->Instance->CCR2,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Output Compare DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC3]->XferCpltCallback = TIM_DMADelayPulseNCplt;
+      htim->hdma[TIM_DMA_ID_CC3]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC3]->XferErrorCallback = TIM_DMAErrorCCxN ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC3], (uint32_t)pData, (uint32_t)&htim->Instance->CCR3,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Output Compare DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC3);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Enable the Capture compare channel N */
+    TIM_CCxNChannelCmd(htim->Instance, Channel, TIM_CCxN_ENABLE);
+
+    /* Enable the Main Output */
+    __HAL_TIM_MOE_ENABLE(htim);
+
+    /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+    if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+    {
+      tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+      if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+      {
+        __HAL_TIM_ENABLE(htim);
+      }
+    }
+    else
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Stops the TIM Output Compare signal generation in DMA mode
+  *         on the complementary output.
+  * @param  htim TIM Output Compare handle
+  * @param  Channel TIM Channel to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_OCN_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, Channel));
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Disable the TIM Output Compare DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC1);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC1]);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Disable the TIM Output Compare DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC2);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC2]);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Disable the TIM Output Compare DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC3);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC3]);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Disable the Capture compare channel N */
+    TIM_CCxNChannelCmd(htim->Instance, Channel, TIM_CCxN_DISABLE);
+
+    /* Disable the Main Output */
+    __HAL_TIM_MOE_DISABLE(htim);
+
+    /* Disable the Peripheral */
+    __HAL_TIM_DISABLE(htim);
+
+    /* Set the TIM complementary channel state */
+    TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @}
+  */
+
+/** @defgroup TIMEx_Exported_Functions_Group3 Extended Timer Complementary PWM functions
+  * @brief    Timer Complementary PWM functions
+  *
+@verbatim
+  ==============================================================================
+                 ##### Timer Complementary PWM functions #####
+  ==============================================================================
+  [..]
+    This section provides functions allowing to:
+    (+) Start the Complementary PWM.
+    (+) Stop the Complementary PWM.
+    (+) Start the Complementary PWM and enable interrupts.
+    (+) Stop the Complementary PWM and disable interrupts.
+    (+) Start the Complementary PWM and enable DMA transfers.
+    (+) Stop the Complementary PWM and disable DMA transfers.
+@endverbatim
+  * @{
+  */
+
+/**
+  * @brief  Starts the PWM signal generation on the complementary output.
+  * @param  htim TIM handle
+  * @param  Channel TIM Channel to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_PWMN_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, Channel));
+
+  /* Check the TIM complementary channel state */
+  if (TIM_CHANNEL_N_STATE_GET(htim, Channel) != HAL_TIM_CHANNEL_STATE_READY)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM complementary channel state */
+  TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  /* Enable the complementary PWM output  */
+  TIM_CCxNChannelCmd(htim->Instance, Channel, TIM_CCxN_ENABLE);
+
+  /* Enable the Main Output */
+  __HAL_TIM_MOE_ENABLE(htim);
+
+  /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+  if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+  {
+    tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+    if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+  else
+  {
+    __HAL_TIM_ENABLE(htim);
+  }
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the PWM signal generation on the complementary output.
+  * @param  htim TIM handle
+  * @param  Channel TIM Channel to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, Channel));
+
+  /* Disable the complementary PWM output  */
+  TIM_CCxNChannelCmd(htim->Instance, Channel, TIM_CCxN_DISABLE);
+
+  /* Disable the Main Output */
+  __HAL_TIM_MOE_DISABLE(htim);
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM complementary channel state */
+  TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Starts the PWM signal generation in interrupt mode on the
+  *         complementary output.
+  * @param  htim TIM handle
+  * @param  Channel TIM Channel to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_PWMN_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, Channel));
+
+  /* Check the TIM complementary channel state */
+  if (TIM_CHANNEL_N_STATE_GET(htim, Channel) != HAL_TIM_CHANNEL_STATE_READY)
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM complementary channel state */
+  TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Enable the TIM Capture/Compare 1 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Enable the TIM Capture/Compare 2 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Enable the TIM Capture/Compare 3 interrupt */
+      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC3);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Enable the TIM Break interrupt */
+    __HAL_TIM_ENABLE_IT(htim, TIM_IT_BREAK);
+
+    /* Enable the complementary PWM output  */
+    TIM_CCxNChannelCmd(htim->Instance, Channel, TIM_CCxN_ENABLE);
+
+    /* Enable the Main Output */
+    __HAL_TIM_MOE_ENABLE(htim);
+
+    /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+    if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+    {
+      tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+      if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+      {
+        __HAL_TIM_ENABLE(htim);
+      }
+    }
+    else
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Stops the PWM signal generation in interrupt mode on the
+  *         complementary output.
+  * @param  htim TIM handle
+  * @param  Channel TIM Channel to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpccer;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, Channel));
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Disable the TIM Capture/Compare 1 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Disable the TIM Capture/Compare 2 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Disable the TIM Capture/Compare 3 interrupt */
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC3);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Disable the complementary PWM output  */
+    TIM_CCxNChannelCmd(htim->Instance, Channel, TIM_CCxN_DISABLE);
+
+    /* Disable the TIM Break interrupt (only if no more channel is active) */
+    tmpccer = htim->Instance->CCER;
+    if ((tmpccer & TIM_CCER_CCxNE_MASK) == (uint32_t)RESET)
+    {
+      __HAL_TIM_DISABLE_IT(htim, TIM_IT_BREAK);
+    }
+
+    /* Disable the Main Output */
+    __HAL_TIM_MOE_DISABLE(htim);
+
+    /* Disable the Peripheral */
+    __HAL_TIM_DISABLE(htim);
+
+    /* Set the TIM complementary channel state */
+    TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Starts the TIM PWM signal generation in DMA mode on the
+  *         complementary output
+  * @param  htim TIM handle
+  * @param  Channel TIM Channel to be enabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  * @param  pData The source Buffer address.
+  * @param  Length The length of data to be transferred from memory to TIM peripheral
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_PWMN_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, const uint32_t *pData,
+                                           uint16_t Length)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, Channel));
+
+  /* Set the TIM complementary channel state */
+  if (TIM_CHANNEL_N_STATE_GET(htim, Channel) == HAL_TIM_CHANNEL_STATE_BUSY)
+  {
+    return HAL_BUSY;
+  }
+  else if (TIM_CHANNEL_N_STATE_GET(htim, Channel) == HAL_TIM_CHANNEL_STATE_READY)
+  {
+    if ((pData == NULL) || (Length == 0U))
+    {
+      return HAL_ERROR;
+    }
+    else
+    {
+      TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);
+    }
+  }
+  else
+  {
+    return HAL_ERROR;
+  }
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC1]->XferCpltCallback = TIM_DMADelayPulseNCplt;
+      htim->hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC1]->XferErrorCallback = TIM_DMAErrorCCxN ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)pData, (uint32_t)&htim->Instance->CCR1,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Capture/Compare 1 DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC1);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC2]->XferCpltCallback = TIM_DMADelayPulseNCplt;
+      htim->hdma[TIM_DMA_ID_CC2]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC2]->XferErrorCallback = TIM_DMAErrorCCxN ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC2], (uint32_t)pData, (uint32_t)&htim->Instance->CCR2,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Capture/Compare 2 DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC2);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Set the DMA compare callbacks */
+      htim->hdma[TIM_DMA_ID_CC3]->XferCpltCallback = TIM_DMADelayPulseNCplt;
+      htim->hdma[TIM_DMA_ID_CC3]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;
+
+      /* Set the DMA error callback */
+      htim->hdma[TIM_DMA_ID_CC3]->XferErrorCallback = TIM_DMAErrorCCxN ;
+
+      /* Enable the DMA stream */
+      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC3], (uint32_t)pData, (uint32_t)&htim->Instance->CCR3,
+                           Length) != HAL_OK)
+      {
+        /* Return error status */
+        return HAL_ERROR;
+      }
+      /* Enable the TIM Capture/Compare 3 DMA request */
+      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC3);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Enable the complementary PWM output  */
+    TIM_CCxNChannelCmd(htim->Instance, Channel, TIM_CCxN_ENABLE);
+
+    /* Enable the Main Output */
+    __HAL_TIM_MOE_ENABLE(htim);
+
+    /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
+    if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+    {
+      tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
+      if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
+      {
+        __HAL_TIM_ENABLE(htim);
+      }
+    }
+    else
+    {
+      __HAL_TIM_ENABLE(htim);
+    }
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @brief  Stops the TIM PWM signal generation in DMA mode on the complementary
+  *         output
+  * @param  htim TIM handle
+  * @param  Channel TIM Channel to be disabled
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, Channel));
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+    {
+      /* Disable the TIM Capture/Compare 1 DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC1);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC1]);
+      break;
+    }
+
+    case TIM_CHANNEL_2:
+    {
+      /* Disable the TIM Capture/Compare 2 DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC2);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC2]);
+      break;
+    }
+
+    case TIM_CHANNEL_3:
+    {
+      /* Disable the TIM Capture/Compare 3 DMA request */
+      __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_CC3);
+      (void)HAL_DMA_Abort_IT(htim->hdma[TIM_DMA_ID_CC3]);
+      break;
+    }
+
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  if (status == HAL_OK)
+  {
+    /* Disable the complementary PWM output */
+    TIM_CCxNChannelCmd(htim->Instance, Channel, TIM_CCxN_DISABLE);
+
+    /* Disable the Main Output */
+    __HAL_TIM_MOE_DISABLE(htim);
+
+    /* Disable the Peripheral */
+    __HAL_TIM_DISABLE(htim);
+
+    /* Set the TIM complementary channel state */
+    TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_READY);
+  }
+
+  /* Return function status */
+  return status;
+}
+
+/**
+  * @}
+  */
+
+/** @defgroup TIMEx_Exported_Functions_Group4 Extended Timer Complementary One Pulse functions
+  * @brief    Timer Complementary One Pulse functions
+  *
+@verbatim
+  ==============================================================================
+                ##### Timer Complementary One Pulse functions #####
+  ==============================================================================
+  [..]
+    This section provides functions allowing to:
+    (+) Start the Complementary One Pulse generation.
+    (+) Stop the Complementary One Pulse.
+    (+) Start the Complementary One Pulse and enable interrupts.
+    (+) Stop the Complementary One Pulse and disable interrupts.
+
+@endverbatim
+  * @{
+  */
+
+/**
+  * @brief  Starts the TIM One Pulse signal generation on the complementary
+  *         output.
+  * @note OutputChannel must match the pulse output channel chosen when calling
+  *       @ref HAL_TIM_OnePulse_ConfigChannel().
+  * @param  htim TIM One Pulse handle
+  * @param  OutputChannel pulse output channel to enable
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Start(TIM_HandleTypeDef *htim, uint32_t OutputChannel)
+{
+  uint32_t input_channel = (OutputChannel == TIM_CHANNEL_1) ? TIM_CHANNEL_2 : TIM_CHANNEL_1;
+  HAL_TIM_ChannelStateTypeDef channel_1_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef channel_2_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_2);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_1_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_2_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_2);
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, OutputChannel));
+
+  /* Check the TIM channels state */
+  if ((channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (channel_2_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_2_state != HAL_TIM_CHANNEL_STATE_READY))
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  /* Enable the complementary One Pulse output channel and the Input Capture channel */
+  TIM_CCxNChannelCmd(htim->Instance, OutputChannel, TIM_CCxN_ENABLE);
+  TIM_CCxChannelCmd(htim->Instance, input_channel, TIM_CCx_ENABLE);
+
+  /* Enable the Main Output */
+  __HAL_TIM_MOE_ENABLE(htim);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM One Pulse signal generation on the complementary
+  *         output.
+  * @note OutputChannel must match the pulse output channel chosen when calling
+  *       @ref HAL_TIM_OnePulse_ConfigChannel().
+  * @param  htim TIM One Pulse handle
+  * @param  OutputChannel pulse output channel to disable
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Stop(TIM_HandleTypeDef *htim, uint32_t OutputChannel)
+{
+  uint32_t input_channel = (OutputChannel == TIM_CHANNEL_1) ? TIM_CHANNEL_2 : TIM_CHANNEL_1;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, OutputChannel));
+
+  /* Disable the complementary One Pulse output channel and the Input Capture channel */
+  TIM_CCxNChannelCmd(htim->Instance, OutputChannel, TIM_CCxN_DISABLE);
+  TIM_CCxChannelCmd(htim->Instance, input_channel, TIM_CCx_DISABLE);
+
+  /* Disable the Main Output */
+  __HAL_TIM_MOE_DISABLE(htim);
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM  channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Starts the TIM One Pulse signal generation in interrupt mode on the
+  *         complementary channel.
+  * @note OutputChannel must match the pulse output channel chosen when calling
+  *       @ref HAL_TIM_OnePulse_ConfigChannel().
+  * @param  htim TIM One Pulse handle
+  * @param  OutputChannel pulse output channel to enable
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Start_IT(TIM_HandleTypeDef *htim, uint32_t OutputChannel)
+{
+  uint32_t input_channel = (OutputChannel == TIM_CHANNEL_1) ? TIM_CHANNEL_2 : TIM_CHANNEL_1;
+  HAL_TIM_ChannelStateTypeDef channel_1_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef channel_2_state = TIM_CHANNEL_STATE_GET(htim, TIM_CHANNEL_2);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_1_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_1);
+  HAL_TIM_ChannelStateTypeDef complementary_channel_2_state = TIM_CHANNEL_N_STATE_GET(htim, TIM_CHANNEL_2);
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, OutputChannel));
+
+  /* Check the TIM channels state */
+  if ((channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (channel_2_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_1_state != HAL_TIM_CHANNEL_STATE_READY)
+      || (complementary_channel_2_state != HAL_TIM_CHANNEL_STATE_READY))
+  {
+    return HAL_ERROR;
+  }
+
+  /* Set the TIM channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_BUSY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_BUSY);
+
+  /* Enable the TIM Capture/Compare 1 interrupt */
+  __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
+
+  /* Enable the TIM Capture/Compare 2 interrupt */
+  __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC2);
+
+  /* Enable the complementary One Pulse output channel and the Input Capture channel */
+  TIM_CCxNChannelCmd(htim->Instance, OutputChannel, TIM_CCxN_ENABLE);
+  TIM_CCxChannelCmd(htim->Instance, input_channel, TIM_CCx_ENABLE);
+
+  /* Enable the Main Output */
+  __HAL_TIM_MOE_ENABLE(htim);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @brief  Stops the TIM One Pulse signal generation in interrupt mode on the
+  *         complementary channel.
+  * @note OutputChannel must match the pulse output channel chosen when calling
+  *       @ref HAL_TIM_OnePulse_ConfigChannel().
+  * @param  htim TIM One Pulse handle
+  * @param  OutputChannel pulse output channel to disable
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
+  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Stop_IT(TIM_HandleTypeDef *htim, uint32_t OutputChannel)
+{
+  uint32_t input_channel = (OutputChannel == TIM_CHANNEL_1) ? TIM_CHANNEL_2 : TIM_CHANNEL_1;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, OutputChannel));
+
+  /* Disable the TIM Capture/Compare 1 interrupt */
+  __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC1);
+
+  /* Disable the TIM Capture/Compare 2 interrupt */
+  __HAL_TIM_DISABLE_IT(htim, TIM_IT_CC2);
+
+  /* Disable the complementary One Pulse output channel and the Input Capture channel */
+  TIM_CCxNChannelCmd(htim->Instance, OutputChannel, TIM_CCxN_DISABLE);
+  TIM_CCxChannelCmd(htim->Instance, input_channel, TIM_CCx_DISABLE);
+
+  /* Disable the Main Output */
+  __HAL_TIM_MOE_DISABLE(htim);
+
+  /* Disable the Peripheral */
+  __HAL_TIM_DISABLE(htim);
+
+  /* Set the TIM  channels state */
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+
+  /* Return function status */
+  return HAL_OK;
+}
+
+/**
+  * @}
+  */
+
+/** @defgroup TIMEx_Exported_Functions_Group5 Extended Peripheral Control functions
+  * @brief    Peripheral Control functions
+  *
+@verbatim
+  ==============================================================================
+                    ##### Peripheral Control functions #####
+  ==============================================================================
+  [..]
+    This section provides functions allowing to:
+      (+) Configure the commutation event in case of use of the Hall sensor interface.
+      (+) Configure Output channels for OC and PWM mode.
+
+      (+) Configure Complementary channels, break features and dead time.
+      (+) Configure Master synchronization.
+      (+) Configure timer remapping capabilities.
+      (+) Select timer input source.
+      (+) Enable or disable channel grouping.
+
+@endverbatim
+  * @{
+  */
+
+/**
+  * @brief  Configure the TIM commutation event sequence.
+  * @note  This function is mandatory to use the commutation event in order to
+  *        update the configuration at each commutation detection on the TRGI input of the Timer,
+  *        the typical use of this feature is with the use of another Timer(interface Timer)
+  *        configured in Hall sensor interface, this interface Timer will generate the
+  *        commutation at its TRGO output (connected to Timer used in this function) each time
+  *        the TI1 of the Interface Timer detect a commutation at its input TI1.
+  * @param  htim TIM handle
+  * @param  InputTrigger the Internal trigger corresponding to the Timer Interfacing with the Hall sensor
+  *          This parameter can be one of the following values:
+  *            @arg TIM_TS_ITR0: Internal trigger 0 selected
+  *            @arg TIM_TS_ITR1: Internal trigger 1 selected
+  *            @arg TIM_TS_ITR2: Internal trigger 2 selected
+  *            @arg TIM_TS_ITR3: Internal trigger 3 selected
+  *            @arg TIM_TS_ITR12: Internal trigger 12 selected (*)
+  *            @arg TIM_TS_ITR13: Internal trigger 13 selected (*)
+  *            @arg TIM_TS_NONE: No trigger is needed
+  *
+  *         (*)  Value not defined in all devices.
+  *
+  * @param  CommutationSource the Commutation Event source
+  *          This parameter can be one of the following values:
+  *            @arg TIM_COMMUTATION_TRGI: Commutation source is the TRGI of the Interface Timer
+  *            @arg TIM_COMMUTATION_SOFTWARE:  Commutation source is set by software using the COMG bit
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_ConfigCommutEvent(TIM_HandleTypeDef *htim, uint32_t  InputTrigger,
+                                              uint32_t  CommutationSource)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_COMMUTATION_EVENT_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_INTERNAL_TRIGGEREVENT_SELECTION(InputTrigger));
+
+  __HAL_LOCK(htim);
+
+  if ((InputTrigger == TIM_TS_ITR0)  || (InputTrigger == TIM_TS_ITR1) ||
+      (InputTrigger == TIM_TS_ITR2)  || (InputTrigger == TIM_TS_ITR3) ||
+      (InputTrigger == TIM_TS_ITR12)  || (InputTrigger == TIM_TS_ITR13))
+  {
+    /* Select the Input trigger */
+    htim->Instance->SMCR &= ~TIM_SMCR_TS;
+    htim->Instance->SMCR |= InputTrigger;
+  }
+
+  /* Select the Capture Compare preload feature */
+  htim->Instance->CR2 |= TIM_CR2_CCPC;
+  /* Select the Commutation event source */
+  htim->Instance->CR2 &= ~TIM_CR2_CCUS;
+  htim->Instance->CR2 |= CommutationSource;
+
+  /* Disable Commutation Interrupt */
+  __HAL_TIM_DISABLE_IT(htim, TIM_IT_COM);
+
+  /* Disable Commutation DMA request */
+  __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_COM);
+
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Configure the TIM commutation event sequence with interrupt.
+  * @note  This function is mandatory to use the commutation event in order to
+  *        update the configuration at each commutation detection on the TRGI input of the Timer,
+  *        the typical use of this feature is with the use of another Timer(interface Timer)
+  *        configured in Hall sensor interface, this interface Timer will generate the
+  *        commutation at its TRGO output (connected to Timer used in this function) each time
+  *        the TI1 of the Interface Timer detect a commutation at its input TI1.
+  * @param  htim TIM handle
+  * @param  InputTrigger the Internal trigger corresponding to the Timer Interfacing with the Hall sensor
+  *          This parameter can be one of the following values:
+  *            @arg TIM_TS_ITR0: Internal trigger 0 selected
+  *            @arg TIM_TS_ITR1: Internal trigger 1 selected
+  *            @arg TIM_TS_ITR2: Internal trigger 2 selected
+  *            @arg TIM_TS_ITR3: Internal trigger 3 selected
+  *            @arg TIM_TS_ITR12: Internal trigger 12 selected (*)
+  *            @arg TIM_TS_ITR13: Internal trigger 13 selected (*)
+  *            @arg TIM_TS_NONE: No trigger is needed
+  *
+  *         (*)  Value not defined in all devices.
+  *
+  * @param  CommutationSource the Commutation Event source
+  *          This parameter can be one of the following values:
+  *            @arg TIM_COMMUTATION_TRGI: Commutation source is the TRGI of the Interface Timer
+  *            @arg TIM_COMMUTATION_SOFTWARE:  Commutation source is set by software using the COMG bit
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_ConfigCommutEvent_IT(TIM_HandleTypeDef *htim, uint32_t  InputTrigger,
+                                                 uint32_t  CommutationSource)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_COMMUTATION_EVENT_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_INTERNAL_TRIGGEREVENT_SELECTION(InputTrigger));
+
+  __HAL_LOCK(htim);
+
+  if ((InputTrigger == TIM_TS_ITR0)  || (InputTrigger == TIM_TS_ITR1) ||
+      (InputTrigger == TIM_TS_ITR2)  || (InputTrigger == TIM_TS_ITR3) ||
+      (InputTrigger == TIM_TS_ITR12)  || (InputTrigger == TIM_TS_ITR13))
+  {
+    /* Select the Input trigger */
+    htim->Instance->SMCR &= ~TIM_SMCR_TS;
+    htim->Instance->SMCR |= InputTrigger;
+  }
+
+  /* Select the Capture Compare preload feature */
+  htim->Instance->CR2 |= TIM_CR2_CCPC;
+  /* Select the Commutation event source */
+  htim->Instance->CR2 &= ~TIM_CR2_CCUS;
+  htim->Instance->CR2 |= CommutationSource;
+
+  /* Disable Commutation DMA request */
+  __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_COM);
+
+  /* Enable the Commutation Interrupt */
+  __HAL_TIM_ENABLE_IT(htim, TIM_IT_COM);
+
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Configure the TIM commutation event sequence with DMA.
+  * @note  This function is mandatory to use the commutation event in order to
+  *        update the configuration at each commutation detection on the TRGI input of the Timer,
+  *        the typical use of this feature is with the use of another Timer(interface Timer)
+  *        configured in Hall sensor interface, this interface Timer will generate the
+  *        commutation at its TRGO output (connected to Timer used in this function) each time
+  *        the TI1 of the Interface Timer detect a commutation at its input TI1.
+  * @note  The user should configure the DMA in his own software, in This function only the COMDE bit is set
+  * @param  htim TIM handle
+  * @param  InputTrigger the Internal trigger corresponding to the Timer Interfacing with the Hall sensor
+  *          This parameter can be one of the following values:
+  *            @arg TIM_TS_ITR0: Internal trigger 0 selected
+  *            @arg TIM_TS_ITR1: Internal trigger 1 selected
+  *            @arg TIM_TS_ITR2: Internal trigger 2 selected
+  *            @arg TIM_TS_ITR3: Internal trigger 3 selected
+  *            @arg TIM_TS_ITR12: Internal trigger 12 selected (*)
+  *            @arg TIM_TS_ITR13: Internal trigger 13 selected (*)
+  *            @arg TIM_TS_NONE: No trigger is needed
+  *
+  *         (*)  Value not defined in all devices.
+  *
+  * @param  CommutationSource the Commutation Event source
+  *          This parameter can be one of the following values:
+  *            @arg TIM_COMMUTATION_TRGI: Commutation source is the TRGI of the Interface Timer
+  *            @arg TIM_COMMUTATION_SOFTWARE:  Commutation source is set by software using the COMG bit
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_ConfigCommutEvent_DMA(TIM_HandleTypeDef *htim, uint32_t  InputTrigger,
+                                                  uint32_t  CommutationSource)
+{
+  /* Check the parameters */
+  assert_param(IS_TIM_COMMUTATION_EVENT_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_INTERNAL_TRIGGEREVENT_SELECTION(InputTrigger));
+
+  __HAL_LOCK(htim);
+
+  if ((InputTrigger == TIM_TS_ITR0)  || (InputTrigger == TIM_TS_ITR1) ||
+      (InputTrigger == TIM_TS_ITR2)  || (InputTrigger == TIM_TS_ITR3) ||
+      (InputTrigger == TIM_TS_ITR12)  || (InputTrigger == TIM_TS_ITR13))
+  {
+    /* Select the Input trigger */
+    htim->Instance->SMCR &= ~TIM_SMCR_TS;
+    htim->Instance->SMCR |= InputTrigger;
+  }
+
+  /* Select the Capture Compare preload feature */
+  htim->Instance->CR2 |= TIM_CR2_CCPC;
+  /* Select the Commutation event source */
+  htim->Instance->CR2 &= ~TIM_CR2_CCUS;
+  htim->Instance->CR2 |= CommutationSource;
+
+  /* Enable the Commutation DMA Request */
+  /* Set the DMA Commutation Callback */
+  htim->hdma[TIM_DMA_ID_COMMUTATION]->XferCpltCallback = TIMEx_DMACommutationCplt;
+  htim->hdma[TIM_DMA_ID_COMMUTATION]->XferHalfCpltCallback = TIMEx_DMACommutationHalfCplt;
+  /* Set the DMA error callback */
+  htim->hdma[TIM_DMA_ID_COMMUTATION]->XferErrorCallback = TIM_DMAError;
+
+  /* Disable Commutation Interrupt */
+  __HAL_TIM_DISABLE_IT(htim, TIM_IT_COM);
+
+  /* Enable the Commutation DMA Request */
+  __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_COM);
+
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Configures the TIM in master mode.
+  * @param  htim TIM handle.
+  * @param  sMasterConfig pointer to a TIM_MasterConfigTypeDef structure that
+  *         contains the selected trigger output (TRGO) and the Master/Slave
+  *         mode.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_MasterConfigSynchronization(TIM_HandleTypeDef *htim,
+                                                        const TIM_MasterConfigTypeDef *sMasterConfig)
+{
+  uint32_t tmpcr2;
+  uint32_t tmpsmcr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_MASTER_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_TRGO_SOURCE(sMasterConfig->MasterOutputTrigger));
+  assert_param(IS_TIM_MSM_STATE(sMasterConfig->MasterSlaveMode));
+
+  /* Check input state */
+  __HAL_LOCK(htim);
+
+  /* Change the handler state */
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Get the TIMx CR2 register value */
+  tmpcr2 = htim->Instance->CR2;
+
+  /* Get the TIMx SMCR register value */
+  tmpsmcr = htim->Instance->SMCR;
+
+  /* If the timer supports ADC synchronization through TRGO2, set the master mode selection 2 */
+  if (IS_TIM_TRGO2_INSTANCE(htim->Instance))
+  {
+    /* Check the parameters */
+    assert_param(IS_TIM_TRGO2_SOURCE(sMasterConfig->MasterOutputTrigger2));
+
+    /* Clear the MMS2 bits */
+    tmpcr2 &= ~TIM_CR2_MMS2;
+    /* Select the TRGO2 source*/
+    tmpcr2 |= sMasterConfig->MasterOutputTrigger2;
+  }
+
+  /* Reset the MMS Bits */
+  tmpcr2 &= ~TIM_CR2_MMS;
+  /* Select the TRGO source */
+  tmpcr2 |=  sMasterConfig->MasterOutputTrigger;
+
+  /* Update TIMx CR2 */
+  htim->Instance->CR2 = tmpcr2;
+
+  if (IS_TIM_SLAVE_INSTANCE(htim->Instance))
+  {
+    /* Reset the MSM Bit */
+    tmpsmcr &= ~TIM_SMCR_MSM;
+    /* Set master mode */
+    tmpsmcr |= sMasterConfig->MasterSlaveMode;
+
+    /* Update TIMx SMCR */
+    htim->Instance->SMCR = tmpsmcr;
+  }
+
+  /* Change the htim state */
+  htim->State = HAL_TIM_STATE_READY;
+
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Configures the Break feature, dead time, Lock level, OSSI/OSSR State
+  *         and the AOE(automatic output enable).
+  * @param  htim TIM handle
+  * @param  sBreakDeadTimeConfig pointer to a TIM_ConfigBreakDeadConfigTypeDef structure that
+  *         contains the BDTR Register configuration  information for the TIM peripheral.
+  * @note   Interrupts can be generated when an active level is detected on the
+  *         break input, the break 2 input or the system break input. Break
+  *         interrupt can be enabled by calling the @ref __HAL_TIM_ENABLE_IT macro.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_ConfigBreakDeadTime(TIM_HandleTypeDef *htim,
+                                                const TIM_BreakDeadTimeConfigTypeDef *sBreakDeadTimeConfig)
+{
+  /* Keep this variable initialized to 0 as it is used to configure BDTR register */
+  uint32_t tmpbdtr = 0U;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_BREAK_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_OSSR_STATE(sBreakDeadTimeConfig->OffStateRunMode));
+  assert_param(IS_TIM_OSSI_STATE(sBreakDeadTimeConfig->OffStateIDLEMode));
+  assert_param(IS_TIM_LOCK_LEVEL(sBreakDeadTimeConfig->LockLevel));
+  assert_param(IS_TIM_DEADTIME(sBreakDeadTimeConfig->DeadTime));
+  assert_param(IS_TIM_BREAK_STATE(sBreakDeadTimeConfig->BreakState));
+  assert_param(IS_TIM_BREAK_POLARITY(sBreakDeadTimeConfig->BreakPolarity));
+  assert_param(IS_TIM_BREAK_FILTER(sBreakDeadTimeConfig->BreakFilter));
+  assert_param(IS_TIM_AUTOMATIC_OUTPUT_STATE(sBreakDeadTimeConfig->AutomaticOutput));
+#if defined(TIM_BDTR_BKBID)
+  assert_param(IS_TIM_BREAK_AFMODE(sBreakDeadTimeConfig->BreakAFMode));
+#endif /* TIM_BDTR_BKBID */
+
+  /* Check input state */
+  __HAL_LOCK(htim);
+
+  /* Set the Lock level, the Break enable Bit and the Polarity, the OSSR State,
+     the OSSI State, the dead time value and the Automatic Output Enable Bit */
+
+  /* Set the BDTR bits */
+  MODIFY_REG(tmpbdtr, TIM_BDTR_DTG, sBreakDeadTimeConfig->DeadTime);
+  MODIFY_REG(tmpbdtr, TIM_BDTR_LOCK, sBreakDeadTimeConfig->LockLevel);
+  MODIFY_REG(tmpbdtr, TIM_BDTR_OSSI, sBreakDeadTimeConfig->OffStateIDLEMode);
+  MODIFY_REG(tmpbdtr, TIM_BDTR_OSSR, sBreakDeadTimeConfig->OffStateRunMode);
+  MODIFY_REG(tmpbdtr, TIM_BDTR_BKE, sBreakDeadTimeConfig->BreakState);
+  MODIFY_REG(tmpbdtr, TIM_BDTR_BKP, sBreakDeadTimeConfig->BreakPolarity);
+  MODIFY_REG(tmpbdtr, TIM_BDTR_AOE, sBreakDeadTimeConfig->AutomaticOutput);
+  MODIFY_REG(tmpbdtr, TIM_BDTR_BKF, (sBreakDeadTimeConfig->BreakFilter << TIM_BDTR_BKF_Pos));
+#if defined(TIM_BDTR_BKBID)
+  MODIFY_REG(tmpbdtr, TIM_BDTR_BKBID, sBreakDeadTimeConfig->BreakAFMode);
+#endif /* TIM_BDTR_BKBID */
+
+  if (IS_TIM_BKIN2_INSTANCE(htim->Instance))
+  {
+    /* Check the parameters */
+    assert_param(IS_TIM_BREAK2_STATE(sBreakDeadTimeConfig->Break2State));
+    assert_param(IS_TIM_BREAK2_POLARITY(sBreakDeadTimeConfig->Break2Polarity));
+    assert_param(IS_TIM_BREAK_FILTER(sBreakDeadTimeConfig->Break2Filter));
+#if defined(TIM_BDTR_BKBID)
+    assert_param(IS_TIM_BREAK2_AFMODE(sBreakDeadTimeConfig->Break2AFMode));
+#endif /* TIM_BDTR_BKBID */
+
+    /* Set the BREAK2 input related BDTR bits */
+    MODIFY_REG(tmpbdtr, TIM_BDTR_BK2F, (sBreakDeadTimeConfig->Break2Filter << TIM_BDTR_BK2F_Pos));
+    MODIFY_REG(tmpbdtr, TIM_BDTR_BK2E, sBreakDeadTimeConfig->Break2State);
+    MODIFY_REG(tmpbdtr, TIM_BDTR_BK2P, sBreakDeadTimeConfig->Break2Polarity);
+#if defined(TIM_BDTR_BKBID)
+    MODIFY_REG(tmpbdtr, TIM_BDTR_BK2BID, sBreakDeadTimeConfig->Break2AFMode);
+#endif /* TIM_BDTR_BKBID */
+  }
+
+  /* Set TIMx_BDTR */
+  htim->Instance->BDTR = tmpbdtr;
+
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+#if defined(TIM_BREAK_INPUT_SUPPORT)
+
+/**
+  * @brief  Configures the break input source.
+  * @param  htim TIM handle.
+  * @param  BreakInput Break input to configure
+  *          This parameter can be one of the following values:
+  *            @arg TIM_BREAKINPUT_BRK: Timer break input
+  *            @arg TIM_BREAKINPUT_BRK2: Timer break 2 input
+  * @param  sBreakInputConfig Break input source configuration
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_ConfigBreakInput(TIM_HandleTypeDef *htim,
+                                             uint32_t BreakInput,
+                                             const TIMEx_BreakInputConfigTypeDef *sBreakInputConfig)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmporx;
+  uint32_t bkin_enable_mask;
+  uint32_t bkin_polarity_mask;
+  uint32_t bkin_enable_bitpos;
+  uint32_t bkin_polarity_bitpos;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_BREAK_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_BREAKINPUT(BreakInput));
+  assert_param(IS_TIM_BREAKINPUTSOURCE(sBreakInputConfig->Source));
+  assert_param(IS_TIM_BREAKINPUTSOURCE_STATE(sBreakInputConfig->Enable));
+  if (sBreakInputConfig->Source != TIM_BREAKINPUTSOURCE_DFSDM1)
+  {
+    assert_param(IS_TIM_BREAKINPUTSOURCE_POLARITY(sBreakInputConfig->Polarity));
+  }
+
+  /* Check input state */
+  __HAL_LOCK(htim);
+
+  switch (sBreakInputConfig->Source)
+  {
+    case TIM_BREAKINPUTSOURCE_BKIN:
+    {
+      bkin_enable_mask = TIM1_AF1_BKINE;
+      bkin_enable_bitpos = TIM1_AF1_BKINE_Pos;
+      bkin_polarity_mask = TIM1_AF1_BKINP;
+      bkin_polarity_bitpos = TIM1_AF1_BKINP_Pos;
+      break;
+    }
+    case TIM_BREAKINPUTSOURCE_COMP1:
+    {
+      bkin_enable_mask = TIM1_AF1_BKCMP1E;
+      bkin_enable_bitpos = TIM1_AF1_BKCMP1E_Pos;
+      bkin_polarity_mask = TIM1_AF1_BKCMP1P;
+      bkin_polarity_bitpos = TIM1_AF1_BKCMP1P_Pos;
+      break;
+    }
+    case TIM_BREAKINPUTSOURCE_COMP2:
+    {
+      bkin_enable_mask = TIM1_AF1_BKCMP2E;
+      bkin_enable_bitpos = TIM1_AF1_BKCMP2E_Pos;
+      bkin_polarity_mask = TIM1_AF1_BKCMP2P;
+      bkin_polarity_bitpos = TIM1_AF1_BKCMP2P_Pos;
+      break;
+    }
+    case TIM_BREAKINPUTSOURCE_DFSDM1:
+    {
+      bkin_enable_mask = TIM1_AF1_BKDF1BK0E;
+      bkin_enable_bitpos = TIM1_AF1_BKDF1BK0E_Pos;
+      bkin_polarity_mask = 0U;
+      bkin_polarity_bitpos = 0U;
+      break;
+    }
+
+    default:
+    {
+      bkin_enable_mask = 0U;
+      bkin_polarity_mask = 0U;
+      bkin_enable_bitpos = 0U;
+      bkin_polarity_bitpos = 0U;
+      break;
+    }
+  }
+
+  switch (BreakInput)
+  {
+    case TIM_BREAKINPUT_BRK:
+    {
+      /* Get the TIMx_AF1 register value */
+      tmporx = htim->Instance->AF1;
+
+      /* Enable the break input */
+      tmporx &= ~bkin_enable_mask;
+      tmporx |= (sBreakInputConfig->Enable << bkin_enable_bitpos) & bkin_enable_mask;
+
+      /* Set the break input polarity */
+      if (sBreakInputConfig->Source != TIM_BREAKINPUTSOURCE_DFSDM1)
+      {
+        tmporx &= ~bkin_polarity_mask;
+        tmporx |= (sBreakInputConfig->Polarity << bkin_polarity_bitpos) & bkin_polarity_mask;
+      }
+
+      /* Set TIMx_AF1 */
+      htim->Instance->AF1 = tmporx;
+      break;
+    }
+    case TIM_BREAKINPUT_BRK2:
+    {
+      /* Get the TIMx_AF2 register value */
+      tmporx = htim->Instance->AF2;
+
+      /* Enable the break input */
+      tmporx &= ~bkin_enable_mask;
+      tmporx |= (sBreakInputConfig->Enable << bkin_enable_bitpos) & bkin_enable_mask;
+
+      /* Set the break input polarity */
+      if (sBreakInputConfig->Source != TIM_BREAKINPUTSOURCE_DFSDM1)
+      {
+        tmporx &= ~bkin_polarity_mask;
+        tmporx |= (sBreakInputConfig->Polarity << bkin_polarity_bitpos) & bkin_polarity_mask;
+      }
+
+      /* Set TIMx_AF2 */
+      htim->Instance->AF2 = tmporx;
+      break;
+    }
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  __HAL_UNLOCK(htim);
+
+  return status;
+}
+#endif /*TIM_BREAK_INPUT_SUPPORT */
+
+/**
+  * @brief  Configures the TIMx Remapping input capabilities.
+  * @param  htim TIM handle.
+  * @param  Remap specifies the TIM remapping source.
+  *         For TIM1, the parameter is one of the following values:
+  *            @arg TIM_TIM1_ETR_GPIO:               TIM1_ETR is connected to GPIO
+  *            @arg TIM_TIM1_ETR_COMP1:              TIM1_ETR is connected to COMP1 output
+  *            @arg TIM_TIM1_ETR_COMP2:              TIM1_ETR is connected to COMP2 output
+  *            @arg TIM_TIM1_ETR_ADC1_AWD1:          TIM1_ETR is connected to ADC1 AWD1
+  *            @arg TIM_TIM1_ETR_ADC1_AWD2:          TIM1_ETR is connected to ADC1 AWD2
+  *            @arg TIM_TIM1_ETR_ADC1_AWD3:          TIM1_ETR is connected to ADC1 AWD3
+  *            @arg TIM_TIM1_ETR_ADC3_AWD1:          TIM1_ETR is connected to ADC3 AWD1
+  *            @arg TIM_TIM1_ETR_ADC3_AWD2:          TIM1_ETR is connected to ADC3 AWD2
+  *            @arg TIM_TIM1_ETR_ADC3_AWD3:          TIM1_ETR is connected to ADC3 AWD3
+  *
+  *         For TIM2, the parameter is one of the following values:
+  *            @arg TIM_TIM2_ETR_GPIO:               TIM2_ETR is connected to GPIO
+  *            @arg TIM_TIM2_ETR_COMP1:              TIM2_ETR is connected to COMP1 output
+  *            @arg TIM_TIM2_ETR_COMP2:              TIM2_ETR is connected to COMP2 output
+  *            @arg TIM_TIM2_ETR_LSE:                TIM2_ETR is connected to LSE
+  *            @arg TIM_TIM2_ETR_SAI1_FSA:           TIM2_ETR is connected to SAI1 FS_A
+  *            @arg TIM_TIM2_ETR_SAI1_FSB:           TIM2_ETR is connected to SAI1 FS_B
+  *
+  *         For TIM3, the parameter is one of the following values:
+  *            @arg TIM_TIM3_ETR_GPIO:               TIM3_ETR is connected to GPIO
+  *            @arg TIM_TIM3_ETR_COMP1:              TIM3_ETR is connected to COMP1 output
+  *
+  *         For TIM5, the parameter is one of the following values:
+  *            @arg TIM_TIM5_ETR_GPIO:               TIM5_ETR is connected to GPIO
+  *            @arg TIM_TIM5_ETR_SAI2_FSA:           TIM5_ETR is connected to SAI2 FS_A (*)
+  *            @arg TIM_TIM5_ETR_SAI2_FSB:           TIM5_ETR is connected to SAI2 FS_B (*)
+  *            @arg TIM_TIM5_ETR_SAI4_FSA:           TIM5_ETR is connected to SAI2 FS_A (*)
+  *            @arg TIM_TIM5_ETR_SAI4_FSB:           TIM5_ETR is connected to SAI2 FS_B (*)
+  *
+  *         For TIM8, the parameter is one of the following values:
+  *            @arg TIM_TIM8_ETR_GPIO:               TIM8_ETR is connected to GPIO
+  *            @arg TIM_TIM8_ETR_COMP1:              TIM8_ETR is connected to COMP1 output
+  *            @arg TIM_TIM8_ETR_COMP2:              TIM8_ETR is connected to COMP2 output
+  *            @arg TIM_TIM8_ETR_ADC2_AWD1:          TIM8_ETR is connected to ADC2 AWD1
+  *            @arg TIM_TIM8_ETR_ADC2_AWD2:          TIM8_ETR is connected to ADC2 AWD2
+  *            @arg TIM_TIM8_ETR_ADC2_AWD3:          TIM8_ETR is connected to ADC2 AWD3
+  *            @arg TIM_TIM8_ETR_ADC3_AWD1:          TIM8_ETR is connected to ADC3 AWD1
+  *            @arg TIM_TIM8_ETR_ADC3_AWD2:          TIM8_ETR is connected to ADC3 AWD2
+  *            @arg TIM_TIM8_ETR_ADC3_AWD3:          TIM8_ETR is connected to ADC3 AWD3
+  *
+  *         For TIM23, the parameter is one of the following values: (*)
+  *            @arg TIM_TIM23_ETR_GPIO               TIM23_ETR is connected to GPIO
+  *            @arg TIM_TIM23_ETR_COMP1              TIM23_ETR is connected to COMP1 output
+  *            @arg TIM_TIM23_ETR_COMP2              TIM23_ETR is connected to COMP2 output
+  *
+  *         For TIM24, the parameter is one of the following values: (*)
+  *           @arg TIM_TIM24_ETR_GPIO                TIM24_ETR is connected to GPIO
+  *           @arg TIM_TIM24_ETR_SAI4_FSA            TIM24_ETR is connected to SAI4 FS_A
+  *           @arg TIM_TIM24_ETR_SAI4_FSB            TIM24_ETR is connected to SAI4 FS_B
+  *           @arg TIM_TIM24_ETR_SAI1_FSA            TIM24_ETR is connected to SAI1 FS_A
+  *           @arg TIM_TIM24_ETR_SAI1_FSB            TIM24_ETR is connected to SAI1 FS_B
+  *
+  *         (*)  Value not defined in all devices.
+  *
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_RemapConfig(TIM_HandleTypeDef *htim, uint32_t Remap)
+{
+  /* Check parameters */
+  assert_param(IS_TIM_REMAP_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_REMAP(Remap));
+
+  __HAL_LOCK(htim);
+
+  MODIFY_REG(htim->Instance->AF1, TIM1_AF1_ETRSEL_Msk, Remap);
+
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+
+/**
+  * @brief  Select the timer input source
+  * @param  htim TIM handle.
+  * @param  Channel specifies the TIM Channel
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TI1 input channel
+  *            @arg TIM_CHANNEL_2: TI2 input channel
+  *            @arg TIM_CHANNEL_3: TIM Channel 3
+  *            @arg TIM_CHANNEL_4: TIM Channel 4
+  * @param  TISelection parameter of the TIM_TISelectionStruct structure is detailed as follows:
+  *         For TIM1, the parameter is one of the following values:
+  *            @arg TIM_TIM1_TI1_GPIO:                TIM1 TI1 is connected to GPIO
+  *            @arg TIM_TIM1_TI1_COMP1:               TIM1 TI1 is connected to COMP1 output
+  *
+  *         For TIM2, the parameter is one of the following values:
+  *            @arg TIM_TIM2_TI4_GPIO:                TIM2 TI4 is connected to GPIO
+  *            @arg TIM_TIM2_TI4_COMP1:               TIM2 TI4 is connected to COMP1 output
+  *            @arg TIM_TIM2_TI4_COMP2:               TIM2 TI4 is connected to COMP2 output
+  *            @arg TIM_TIM2_TI4_COMP1_COMP2:         TIM2 TI4 is connected to logical OR between COMP1 and COMP2 output
+  *
+  *         For TIM3, the parameter is one of the following values:
+  *            @arg TIM_TIM3_TI1_GPIO:                TIM3 TI1 is connected to GPIO
+  *            @arg TIM_TIM3_TI1_COMP1:               TIM3 TI1 is connected to COMP1 output
+  *            @arg TIM_TIM3_TI1_COMP2:               TIM3 TI1 is connected to COMP2 output
+  *            @arg TIM_TIM3_TI1_COMP1_COMP2:         TIM3 TI1 is connected to logical OR between COMP1 and COMP2 output
+  *
+  *         For TIM5, the parameter is one of the following values:
+  *            @arg TIM_TIM5_TI1_GPIO:                TIM5 TI1 is connected to GPIO
+  *            @arg TIM_TIM5_TI1_CAN_TMP:             TIM5 TI1 is connected to CAN TMP
+  *            @arg TIM_TIM5_TI1_CAN_RTP:             TIM5 TI1 is connected to CAN RTP
+  *
+  *         For TIM8, the parameter is one of the following values:
+  *            @arg TIM_TIM8_TI1_GPIO:                TIM8 TI1 is connected to GPIO
+  *            @arg TIM_TIM8_TI1_COMP2:               TIM8 TI1 is connected to COMP2 output
+  *
+  *         For TIM12, the parameter can have the following values: (*)
+  *            @arg TIM_TIM12_TI1_GPIO:               TIM12 TI1 is connected to GPIO
+  *            @arg TIM_TIM12_TI1_SPDIF_FS:           TIM12 TI1 is connected to SPDIF FS
+  *
+  *         For TIM15, the parameter is one of the following values:
+  *            @arg TIM_TIM15_TI1_GPIO:               TIM15 TI1 is connected to GPIO
+  *            @arg TIM_TIM15_TI1_TIM2_CH1:           TIM15 TI1 is connected to TIM2 CH1
+  *            @arg TIM_TIM15_TI1_TIM3_CH1:           TIM15 TI1 is connected to TIM3 CH1
+  *            @arg TIM_TIM15_TI1_TIM4_CH1:           TIM15 TI1 is connected to TIM4 CH1
+  *            @arg TIM_TIM15_TI1_RCC_LSE:            TIM15 TI1 is connected to LSE
+  *            @arg TIM_TIM15_TI1_RCC_CSI:            TIM15 TI1 is connected to CSI
+  *            @arg TIM_TIM15_TI1_RCC_MCO2:           TIM15 TI1 is connected to MCO2
+  *            @arg TIM_TIM15_TI2_GPIO:               TIM15 TI2 is connected to GPIO
+  *            @arg TIM_TIM15_TI2_TIM2_CH2:           TIM15 TI2 is connected to TIM2 CH2
+  *            @arg TIM_TIM15_TI2_TIM3_CH2:           TIM15 TI2 is connected to TIM3 CH2
+  *            @arg TIM_TIM15_TI2_TIM4_CH2:           TIM15 TI2 is connected to TIM4 CH2
+  *
+  *         For TIM16, the parameter can have the following values:
+  *            @arg TIM_TIM16_TI1_GPIO:               TIM16 TI1 is connected to GPIO
+  *            @arg TIM_TIM16_TI1_RCC_LSI:            TIM16 TI1 is connected to LSI
+  *            @arg TIM_TIM16_TI1_RCC_LSE:            TIM16 TI1 is connected to LSE
+  *            @arg TIM_TIM16_TI1_WKUP_IT:            TIM16 TI1 is connected to RTC wakeup interrupt
+  *
+  *         For TIM17, the parameter can have the following values:
+  *            @arg TIM_TIM17_TI1_GPIO:               TIM17 TI1 is connected to GPIO
+  *            @arg TIM_TIM17_TI1_SPDIF_FS:           TIM17 TI1 is connected to SPDIF FS (*)
+  *            @arg TIM_TIM17_TI1_RCC_HSE1MHZ:        TIM17 TI1 is connected to HSE 1MHz
+  *            @arg TIM_TIM17_TI1_RCC_MCO1:           TIM17 TI1 is connected to MCO1
+  *
+  *         For TIM23, the parameter can have the following values: (*)
+  *            @arg TIM_TIM23_TI4_GPIO                TIM23_TI4 is connected to GPIO
+  *            @arg TIM_TIM23_TI4_COMP1               TIM23_TI4 is connected to COMP1 output
+  *            @arg TIM_TIM23_TI4_COMP2               TIM23_TI4 is connected to COMP2 output
+  *            @arg TIM_TIM23_TI4_COMP1_COMP2         TIM23_TI4 is connected to COMP2 output
+  *
+  *         For TIM24, the parameter can have the following values: (*)
+  *            @arg TIM_TIM24_TI1_GPIO                TIM24_TI1 is connected to GPIO
+  *            @arg TIM_TIM24_TI1_CAN_TMP             TIM24_TI1 is connected to CAN_TMP
+  *            @arg TIM_TIM24_TI1_CAN_RTP             TIM24_TI1 is connected to CAN_RTP
+  *            @arg TIM_TIM24_TI1_CAN_SOC             TIM24_TI1 is connected to CAN_SOC
+  *
+  *         (*)  Value not defined in all devices. \n
+  * @retval HAL status
+  */
+HAL_StatusTypeDef  HAL_TIMEx_TISelection(TIM_HandleTypeDef *htim, uint32_t TISelection, uint32_t Channel)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+
+  /* Check parameters */
+  assert_param(IS_TIM_TISEL_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_TISEL(TISelection));
+
+  __HAL_LOCK(htim);
+
+  switch (Channel)
+  {
+    case TIM_CHANNEL_1:
+      MODIFY_REG(htim->Instance->TISEL, TIM_TISEL_TI1SEL, TISelection);
+      break;
+    case TIM_CHANNEL_2:
+      MODIFY_REG(htim->Instance->TISEL, TIM_TISEL_TI2SEL, TISelection);
+      break;
+    case TIM_CHANNEL_3:
+      MODIFY_REG(htim->Instance->TISEL, TIM_TISEL_TI3SEL, TISelection);
+      break;
+    case TIM_CHANNEL_4:
+      MODIFY_REG(htim->Instance->TISEL, TIM_TISEL_TI4SEL, TISelection);
+      break;
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  __HAL_UNLOCK(htim);
+
+  return status;
+}
+
+/**
+  * @brief  Group channel 5 and channel 1, 2 or 3
+  * @param  htim TIM handle.
+  * @param  Channels specifies the reference signal(s) the OC5REF is combined with.
+  *         This parameter can be any combination of the following values:
+  *         TIM_GROUPCH5_NONE: No effect of OC5REF on OC1REFC, OC2REFC and OC3REFC
+  *         TIM_GROUPCH5_OC1REFC: OC1REFC is the logical AND of OC1REFC and OC5REF
+  *         TIM_GROUPCH5_OC2REFC: OC2REFC is the logical AND of OC2REFC and OC5REF
+  *         TIM_GROUPCH5_OC3REFC: OC3REFC is the logical AND of OC3REFC and OC5REF
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_GroupChannel5(TIM_HandleTypeDef *htim, uint32_t Channels)
+{
+  /* Check parameters */
+  assert_param(IS_TIM_COMBINED3PHASEPWM_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_GROUPCH5(Channels));
+
+  /* Process Locked */
+  __HAL_LOCK(htim);
+
+  htim->State = HAL_TIM_STATE_BUSY;
+
+  /* Clear GC5Cx bit fields */
+  htim->Instance->CCR5 &= ~(TIM_CCR5_GC5C3 | TIM_CCR5_GC5C2 | TIM_CCR5_GC5C1);
+
+  /* Set GC5Cx bit fields */
+  htim->Instance->CCR5 |= Channels;
+
+  /* Change the htim state */
+  htim->State = HAL_TIM_STATE_READY;
+
+  __HAL_UNLOCK(htim);
+
+  return HAL_OK;
+}
+#if defined(TIM_BDTR_BKBID)
+
+/**
+  * @brief  Disarm the designated break input (when it operates in bidirectional mode).
+  * @param  htim TIM handle.
+  * @param  BreakInput Break input to disarm
+  *          This parameter can be one of the following values:
+  *            @arg TIM_BREAKINPUT_BRK: Timer break input
+  *            @arg TIM_BREAKINPUT_BRK2: Timer break 2 input
+  * @note  The break input can be disarmed only when it is configured in
+  *        bidirectional mode and when when MOE is reset.
+  * @note  Purpose is to be able to have the input voltage back to high-state,
+  *        whatever the time constant on the output .
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_DisarmBreakInput(TIM_HandleTypeDef *htim, uint32_t BreakInput)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tmpbdtr;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_BREAK_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_BREAKINPUT(BreakInput));
+
+  switch (BreakInput)
+  {
+    case TIM_BREAKINPUT_BRK:
+    {
+      /* Check initial conditions */
+      tmpbdtr = READ_REG(htim->Instance->BDTR);
+      if ((READ_BIT(tmpbdtr, TIM_BDTR_BKBID) == TIM_BDTR_BKBID) &&
+          (READ_BIT(tmpbdtr, TIM_BDTR_MOE) == 0U))
+      {
+        /* Break input BRK is disarmed */
+        SET_BIT(htim->Instance->BDTR, TIM_BDTR_BKDSRM);
+      }
+      break;
+    }
+    case TIM_BREAKINPUT_BRK2:
+    {
+      /* Check initial conditions */
+      tmpbdtr = READ_REG(htim->Instance->BDTR);
+      if ((READ_BIT(tmpbdtr, TIM_BDTR_BK2BID) == TIM_BDTR_BK2BID) &&
+          (READ_BIT(tmpbdtr, TIM_BDTR_MOE) == 0U))
+      {
+        /* Break input BRK is disarmed */
+        SET_BIT(htim->Instance->BDTR, TIM_BDTR_BK2DSRM);
+      }
+      break;
+    }
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  return status;
+}
+
+/**
+  * @brief  Arm the designated break input (when it operates in bidirectional mode).
+  * @param  htim TIM handle.
+  * @param  BreakInput Break input to arm
+  *          This parameter can be one of the following values:
+  *            @arg TIM_BREAKINPUT_BRK: Timer break input
+  *            @arg TIM_BREAKINPUT_BRK2: Timer break 2 input
+  * @note  Arming is possible at anytime, even if fault is present.
+  * @note  Break input is automatically armed as soon as MOE bit is set.
+  * @retval HAL status
+  */
+HAL_StatusTypeDef HAL_TIMEx_ReArmBreakInput(const TIM_HandleTypeDef *htim, uint32_t BreakInput)
+{
+  HAL_StatusTypeDef status = HAL_OK;
+  uint32_t tickstart;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_BREAK_INSTANCE(htim->Instance));
+  assert_param(IS_TIM_BREAKINPUT(BreakInput));
+
+  switch (BreakInput)
+  {
+    case TIM_BREAKINPUT_BRK:
+    {
+      /* Check initial conditions */
+      if (READ_BIT(htim->Instance->BDTR, TIM_BDTR_BKBID) == TIM_BDTR_BKBID)
+      {
+        /* Break input BRK is re-armed automatically by hardware. Poll to check whether fault condition disappeared */
+        /* Init tickstart for timeout management */
+        tickstart = HAL_GetTick();
+        while (READ_BIT(htim->Instance->BDTR, TIM_BDTR_BKDSRM) != 0UL)
+        {
+          if ((HAL_GetTick() - tickstart) > TIM_BREAKINPUT_REARM_TIMEOUT)
+          {
+            /* New check to avoid false timeout detection in case of preemption */
+            if (READ_BIT(htim->Instance->BDTR, TIM_BDTR_BKDSRM) != 0UL)
+            {
+              return HAL_TIMEOUT;
+            }
+          }
+        }
+      }
+      break;
+    }
+
+    case TIM_BREAKINPUT_BRK2:
+    {
+      /* Check initial conditions */
+      if (READ_BIT(htim->Instance->BDTR, TIM_BDTR_BK2BID) == TIM_BDTR_BK2BID)
+      {
+        /* Break input BRK2 is re-armed automatically by hardware. Poll to check whether fault condition disappeared */
+        /* Init tickstart for timeout management */
+        tickstart = HAL_GetTick();
+        while (READ_BIT(htim->Instance->BDTR, TIM_BDTR_BK2DSRM) != 0UL)
+        {
+          if ((HAL_GetTick() - tickstart) > TIM_BREAKINPUT_REARM_TIMEOUT)
+          {
+            /* New check to avoid false timeout detection in case of preemption */
+            if (READ_BIT(htim->Instance->BDTR, TIM_BDTR_BK2DSRM) != 0UL)
+            {
+              return HAL_TIMEOUT;
+            }
+          }
+        }
+      }
+      break;
+    }
+    default:
+      status = HAL_ERROR;
+      break;
+  }
+
+  return status;
+}
+#endif /* TIM_BDTR_BKBID */
+
+/**
+  * @}
+  */
+
+/** @defgroup TIMEx_Exported_Functions_Group6 Extended Callbacks functions
+  * @brief    Extended Callbacks functions
+  *
+@verbatim
+  ==============================================================================
+                    ##### Extended Callbacks functions #####
+  ==============================================================================
+  [..]
+    This section provides Extended TIM callback functions:
+    (+) Timer Commutation callback
+    (+) Timer Break callback
+
+@endverbatim
+  * @{
+  */
+
+/**
+  * @brief  Commutation callback in non-blocking mode
+  * @param  htim TIM handle
+  * @retval None
+  */
+__weak void HAL_TIMEx_CommutCallback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIMEx_CommutCallback could be implemented in the user file
+   */
+}
+/**
+  * @brief  Commutation half complete callback in non-blocking mode
+  * @param  htim TIM handle
+  * @retval None
+  */
+__weak void HAL_TIMEx_CommutHalfCpltCallback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIMEx_CommutHalfCpltCallback could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Break detection callback in non-blocking mode
+  * @param  htim TIM handle
+  * @retval None
+  */
+__weak void HAL_TIMEx_BreakCallback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function should not be modified, when the callback is needed,
+            the HAL_TIMEx_BreakCallback could be implemented in the user file
+   */
+}
+
+/**
+  * @brief  Break2 detection callback in non blocking mode
+  * @param  htim: TIM handle
+  * @retval None
+  */
+__weak void HAL_TIMEx_Break2Callback(TIM_HandleTypeDef *htim)
+{
+  /* Prevent unused argument(s) compilation warning */
+  UNUSED(htim);
+
+  /* NOTE : This function Should not be modified, when the callback is needed,
+            the HAL_TIMEx_Break2Callback could be implemented in the user file
+   */
+}
+/**
+  * @}
+  */
+
+/** @defgroup TIMEx_Exported_Functions_Group7 Extended Peripheral State functions
+  * @brief    Extended Peripheral State functions
+  *
+@verbatim
+  ==============================================================================
+                ##### Extended Peripheral State functions #####
+  ==============================================================================
+  [..]
+    This subsection permits to get in run-time the status of the peripheral
+    and the data flow.
+
+@endverbatim
+  * @{
+  */
+
+/**
+  * @brief  Return the TIM Hall Sensor interface handle state.
+  * @param  htim TIM Hall Sensor handle
+  * @retval HAL state
+  */
+HAL_TIM_StateTypeDef HAL_TIMEx_HallSensor_GetState(const TIM_HandleTypeDef *htim)
+{
+  return htim->State;
+}
+
+/**
+  * @brief  Return actual state of the TIM complementary channel.
+  * @param  htim TIM handle
+  * @param  ChannelN TIM Complementary channel
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1
+  *            @arg TIM_CHANNEL_2: TIM Channel 2
+  *            @arg TIM_CHANNEL_3: TIM Channel 3
+  * @retval TIM Complementary channel state
+  */
+HAL_TIM_ChannelStateTypeDef HAL_TIMEx_GetChannelNState(const TIM_HandleTypeDef *htim,  uint32_t ChannelN)
+{
+  HAL_TIM_ChannelStateTypeDef channel_state;
+
+  /* Check the parameters */
+  assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, ChannelN));
+
+  channel_state = TIM_CHANNEL_N_STATE_GET(htim, ChannelN);
+
+  return channel_state;
+}
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+
+/* Private functions ---------------------------------------------------------*/
+/** @defgroup TIMEx_Private_Functions TIM Extended Private Functions
+  * @{
+  */
+
+/**
+  * @brief  TIM DMA Commutation callback.
+  * @param  hdma pointer to DMA handle.
+  * @retval None
+  */
+void TIMEx_DMACommutationCplt(DMA_HandleTypeDef *hdma)
+{
+  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
+
+  /* Change the htim state */
+  htim->State = HAL_TIM_STATE_READY;
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  htim->CommutationCallback(htim);
+#else
+  HAL_TIMEx_CommutCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+}
+
+/**
+  * @brief  TIM DMA Commutation half complete callback.
+  * @param  hdma pointer to DMA handle.
+  * @retval None
+  */
+void TIMEx_DMACommutationHalfCplt(DMA_HandleTypeDef *hdma)
+{
+  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
+
+  /* Change the htim state */
+  htim->State = HAL_TIM_STATE_READY;
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  htim->CommutationHalfCpltCallback(htim);
+#else
+  HAL_TIMEx_CommutHalfCpltCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+}
+
+
+/**
+  * @brief  TIM DMA Delay Pulse complete callback (complementary channel).
+  * @param  hdma pointer to DMA handle.
+  * @retval None
+  */
+static void TIM_DMADelayPulseNCplt(DMA_HandleTypeDef *hdma)
+{
+  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
+
+  if (hdma == htim->hdma[TIM_DMA_ID_CC1])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;
+
+    if (hdma->Init.Mode == DMA_NORMAL)
+    {
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+    }
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC2])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_2;
+
+    if (hdma->Init.Mode == DMA_NORMAL)
+    {
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+    }
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC3])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_3;
+
+    if (hdma->Init.Mode == DMA_NORMAL)
+    {
+      TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_3, HAL_TIM_CHANNEL_STATE_READY);
+    }
+  }
+  else
+  {
+    /* nothing to do */
+  }
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  htim->PWM_PulseFinishedCallback(htim);
+#else
+  HAL_TIM_PWM_PulseFinishedCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
+}
+
+/**
+  * @brief  TIM DMA error callback (complementary channel)
+  * @param  hdma pointer to DMA handle.
+  * @retval None
+  */
+static void TIM_DMAErrorCCxN(DMA_HandleTypeDef *hdma)
+{
+  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
+
+  if (hdma == htim->hdma[TIM_DMA_ID_CC1])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;
+    TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC2])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_2;
+    TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_2, HAL_TIM_CHANNEL_STATE_READY);
+  }
+  else if (hdma == htim->hdma[TIM_DMA_ID_CC3])
+  {
+    htim->Channel = HAL_TIM_ACTIVE_CHANNEL_3;
+    TIM_CHANNEL_N_STATE_SET(htim, TIM_CHANNEL_3, HAL_TIM_CHANNEL_STATE_READY);
+  }
+  else
+  {
+    /* nothing to do */
+  }
+
+#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
+  htim->ErrorCallback(htim);
+#else
+  HAL_TIM_ErrorCallback(htim);
+#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
+
+  htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
+}
+
+/**
+  * @brief  Enables or disables the TIM Capture Compare Channel xN.
+  * @param  TIMx to select the TIM peripheral
+  * @param  Channel specifies the TIM Channel
+  *          This parameter can be one of the following values:
+  *            @arg TIM_CHANNEL_1: TIM Channel 1
+  *            @arg TIM_CHANNEL_2: TIM Channel 2
+  *            @arg TIM_CHANNEL_3: TIM Channel 3
+  * @param  ChannelNState specifies the TIM Channel CCxNE bit new state.
+  *          This parameter can be: TIM_CCxN_ENABLE or TIM_CCxN_Disable.
+  * @retval None
+  */
+static void TIM_CCxNChannelCmd(TIM_TypeDef *TIMx, uint32_t Channel, uint32_t ChannelNState)
+{
+  uint32_t tmp;
+
+  tmp = TIM_CCER_CC1NE << (Channel & 0xFU); /* 0xFU = 15 bits max shift */
+
+  /* Reset the CCxNE Bit */
+  TIMx->CCER &=  ~tmp;
+
+  /* Set or reset the CCxNE Bit */
+  TIMx->CCER |= (uint32_t)(ChannelNState << (Channel & 0xFU)); /* 0xFU = 15 bits max shift */
+}
+/**
+  * @}
+  */
+
+#endif /* HAL_TIM_MODULE_ENABLED */
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
Index: ctrl/firmware/Main/CubeMX/FileX/App/app_filex.c
===================================================================
--- ctrl/firmware/Main/CubeMX/FileX/App/app_filex.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/FileX/App/app_filex.c	(revision 54)
@@ -0,0 +1,153 @@
+
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    app_filex.c
+  * @author  MCD Application Team
+  * @brief   FileX applicative file
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2020-2021 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 "app_filex.h"
+
+/* Private includes ----------------------------------------------------------*/
+/* USER CODE BEGIN Includes */
+
+/* USER CODE END Includes */
+
+/* Private typedef -----------------------------------------------------------*/
+/* USER CODE BEGIN PTD */
+
+/* USER CODE END PTD */
+
+/* Private define ------------------------------------------------------------*/
+/* Main thread stack size */
+#define FX_APP_THREAD_STACK_SIZE         1024
+/* Main thread priority */
+#define FX_APP_THREAD_PRIO               10
+
+/* USER CODE BEGIN PD */
+
+/* USER CODE END PD */
+
+/* Private macro -------------------------------------------------------------*/
+/* USER CODE BEGIN PM */
+
+/* USER CODE END PM */
+
+/* Private variables ---------------------------------------------------------*/
+/* Main thread global data structures.  */
+TX_THREAD       fx_app_thread;
+
+/* Buffer for FileX FX_MEDIA sector cache. */
+ALIGN_32BYTES (uint32_t fx_sd_media_memory[FX_STM32_SD_DEFAULT_SECTOR_SIZE / sizeof(uint32_t)]);
+/* Define FileX global data structures.  */
+FX_MEDIA        sdio_disk;
+
+/* USER CODE BEGIN PV */
+
+/* USER CODE END PV */
+
+/* Private function prototypes -----------------------------------------------*/
+/* Main thread entry function.  */
+void fx_app_thread_entry(ULONG thread_input);
+
+/* USER CODE BEGIN PFP */
+
+/* USER CODE END PFP */
+
+/**
+  * @brief  Application FileX Initialization.
+  * @param memory_ptr: memory pointer
+  * @retval int
+  */
+UINT MX_FileX_Init(VOID *memory_ptr)
+{
+  UINT ret = FX_SUCCESS;
+
+  TX_BYTE_POOL *byte_pool = (TX_BYTE_POOL*)memory_ptr;
+  VOID *pointer;
+
+  /* USER CODE BEGIN MX_FileX_MEM_POOL */
+
+  /* USER CODE END MX_FileX_MEM_POOL */
+
+  /* USER CODE BEGIN 0 */
+
+  /* USER CODE END 0 */
+
+  /*Allocate memory for the main thread's stack*/
+  ret = tx_byte_allocate(byte_pool, &pointer, FX_APP_THREAD_STACK_SIZE, TX_NO_WAIT);
+
+  /* Check FX_APP_THREAD_STACK_SIZE allocation*/
+  if (ret != FX_SUCCESS)
+  {
+    return TX_POOL_ERROR;
+  }
+
+  /* Create the main thread.  */
+  ret = tx_thread_create(&fx_app_thread, FX_APP_THREAD_NAME, fx_app_thread_entry, 0, pointer, FX_APP_THREAD_STACK_SIZE,
+                         FX_APP_THREAD_PRIO, FX_APP_PREEMPTION_THRESHOLD, FX_APP_THREAD_TIME_SLICE, FX_APP_THREAD_AUTO_START);
+
+  /* Check main thread creation */
+  if (ret != TX_SUCCESS)
+  {
+    return TX_THREAD_ERROR;
+  }
+  /* USER CODE BEGIN MX_FileX_Init */
+
+  /* USER CODE END MX_FileX_Init */
+
+  /* Initialize FileX.  */
+  fx_system_initialize();
+
+  /* USER CODE BEGIN MX_FileX_Init 1*/
+
+  /* USER CODE END MX_FileX_Init 1*/
+
+  return ret;
+}
+
+ /**
+ * @brief  Main thread entry.
+ * @param thread_input: ULONG user argument used by the thread entry
+ * @retval none
+ */
+void fx_app_thread_entry(ULONG thread_input)
+{
+  UINT sd_status = FX_SUCCESS;
+  /* USER CODE BEGIN fx_app_thread_entry 0 */
+
+  /* USER CODE END fx_app_thread_entry 0 */
+
+  /* Open the SD disk driver */
+  sd_status =  fx_media_open(&sdio_disk, FX_SD_VOLUME_NAME, fx_stm32_sd_driver, (VOID *)FX_NULL, (VOID *) fx_sd_media_memory, sizeof(fx_sd_media_memory));
+
+  /* Check the media open sd_status */
+  if (sd_status != FX_SUCCESS)
+  {
+    /* USER CODE BEGIN SD open error */
+    while(1);
+    /* USER CODE END SD open error */
+  }
+
+  /* USER CODE BEGIN fx_app_thread_entry 1 */
+
+  /* USER CODE END fx_app_thread_entry 1 */
+}
+
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
Index: ctrl/firmware/Main/CubeMX/FileX/Target/fx_stm32_sd_driver_glue.c
===================================================================
--- ctrl/firmware/Main/CubeMX/FileX/Target/fx_stm32_sd_driver_glue.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/FileX/Target/fx_stm32_sd_driver_glue.c	(revision 54)
@@ -0,0 +1,192 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+#include "fx_stm32_sd_driver.h"
+
+TX_SEMAPHORE sd_tx_semaphore;
+TX_SEMAPHORE sd_rx_semaphore;
+
+extern SD_HandleTypeDef hsd1;
+#if (FX_STM32_SD_INIT == 1)
+extern void MX_SDMMC1_SD_Init(void);
+#endif
+
+/* USER CODE BEGIN 0 */
+
+/* USER CODE END 0 */
+
+/**
+* @brief Initializes the SD IP instance
+* @param UINT instance SD instance to initialize
+* @retval 0 on success error value otherwise
+*/
+INT fx_stm32_sd_init(UINT instance)
+{
+  INT ret = 0;
+
+  /* USER CODE BEGIN PRE_FX_SD_INIT */
+  UNUSED(instance);
+  /* USER CODE END PRE_FX_SD_INIT */
+
+#if (FX_STM32_SD_INIT == 1)
+  MX_SDMMC1_SD_Init();
+#endif
+
+  /* USER CODE BEGIN POST_FX_SD_INIT */
+
+  /* USER CODE END POST_FX_SD_INIT */
+
+  return ret;
+}
+
+/**
+* @brief Deinitializes the SD IP instance
+* @param UINT instance SD instance to deinitialize
+* @retval 0 on success error value otherwise
+*/
+INT fx_stm32_sd_deinit(UINT instance)
+{
+  INT ret = 0;
+
+  /* USER CODE BEGIN PRE_FX_SD_DEINIT */
+  UNUSED(instance);
+  /* USER CODE END PRE_FX_SD_DEINIT */
+#if (FX_STM32_SD_INIT == 1)
+  if(HAL_SD_DeInit(&hsd1) != HAL_OK)
+  {
+    ret = 1;
+  }
+#endif
+  /* USER CODE BEGIN POST_FX_SD_DEINIT */
+
+  /* USER CODE END POST_FX_SD_DEINIT */
+
+  return ret;
+}
+
+/**
+* @brief Check the SD IP status.
+* @param UINT instance SD instance to check
+* @retval 0 when ready 1 when busy
+*/
+INT fx_stm32_sd_get_status(UINT instance)
+{
+  INT ret = 0;
+
+  /* USER CODE BEGIN PRE_GET_STATUS */
+  UNUSED(instance);
+  /* USER CODE END PRE_GET_STATUS */
+
+  if(HAL_SD_GetCardState(&hsd1) != HAL_SD_CARD_TRANSFER)
+  {
+    ret = 1;
+  }
+
+  /* USER CODE BEGIN POST_GET_STATUS */
+
+  /* USER CODE END POST_GET_STATUS */
+
+  return ret;
+}
+
+/**
+* @brief Read Data from the SD device into a buffer.
+* @param UINT instance SD IP instance to read from.
+* @param UINT *buffer buffer into which the data is to be read.
+* @param UINT start_block the first block to start reading from.
+* @param UINT total_blocks total number of blocks to read.
+* @retval 0 on success error code otherwise
+*/
+INT fx_stm32_sd_read_blocks(UINT instance, UINT *buffer, UINT start_block, UINT total_blocks)
+{
+  INT ret = 0;
+  /* USER CODE BEGIN PRE_READ_BLOCKS */
+  UNUSED(instance);
+  /* USER CODE END PRE_READ_BLOCKS */
+
+  if(HAL_SD_ReadBlocks_DMA(&hsd1, (uint8_t *)buffer, start_block, total_blocks) != HAL_OK)
+  {
+    ret = 1;
+  }
+
+  /* USER CODE BEGIN POST_READ_BLOCKS */
+
+  /* USER CODE END POST_READ_BLOCKS */
+
+  return ret;
+}
+
+/**
+* @brief Write data buffer into the SD device.
+* @param UINT instance SD IP instance to write into.
+* @param UINT *buffer buffer to write into the SD device.
+* @param UINT start_block the first block to start writing into.
+* @param UINT total_blocks total number of blocks to write.
+* @retval 0 on success error code otherwise
+*/
+INT fx_stm32_sd_write_blocks(UINT instance, UINT *buffer, UINT start_block, UINT total_blocks)
+{
+  INT ret = 0;
+  /* USER CODE BEGIN PRE_WRITE_BLOCKS */
+  UNUSED(instance);
+  /* USER CODE END PRE_WRITE_BLOCKS */
+
+  if(HAL_SD_WriteBlocks_DMA(&hsd1, (uint8_t *)buffer, start_block, total_blocks) != HAL_OK)
+  {
+    ret = 1;
+  }
+
+  /* USER CODE BEGIN POST_WRITE_BLOCKS */
+
+  /* USER CODE END POST_WRITE_BLOCKS */
+
+  return ret;
+}
+
+/**
+* @brief SD DMA Tx Transfer completed callbacks
+* @param Instance the sd instance
+* @retval None
+*/
+void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
+{
+  /* USER CODE BEGIN PRE_TX_CMPLT */
+
+  /* USER CODE END PRE_TX_CMPLT */
+
+  tx_semaphore_put(&sd_tx_semaphore);
+
+  /* USER CODE BEGIN POST_TX_CMPLT */
+
+  /* USER CODE END POST_TX_CMPLT */
+}
+
+/**
+* @brief SD DMA Rx Transfer completed callbacks
+* @param Instance the sd instance
+* @retval None
+*/
+void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
+{
+  /* USER CODE BEGIN PRE_RX_CMPLT */
+
+  /* USER CODE END PRE_RX_CMPLT */
+
+  tx_semaphore_put(&sd_rx_semaphore);
+
+  /* USER CODE BEGIN POST_RX_CMPLT */
+
+  /* USER CODE END POST_RX_CMPLT */
+}
+
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/drivers/fx_stm32_sd_driver.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/drivers/fx_stm32_sd_driver.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/drivers/fx_stm32_sd_driver.c	(revision 54)
@@ -0,0 +1,411 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Partial Copyright (c) Microsoft Corporation. All rights reserved.*/
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*      Partial Copyright (c) STMicroelctronics 2020. All rights reserved */
+/**************************************************************************/
+
+
+/* Include necessary system files.  */
+#include "fx_stm32_sd_driver.h"
+
+/*
+ * the scratch buffer is required when performing DMA transfers using unaligned addresses
+ * When CPU cache is enabled, the scratch buffer should be 32-byte aligned to match a whole cache line
+ * otherwise it is 4-byte aligned to match the DMA alignment constraints
+ */
+
+#if (FX_STM32_SD_CACHE_MAINTENANCE == 1)
+static UCHAR scratch[FX_STM32_SD_DEFAULT_SECTOR_SIZE] __attribute__ ((aligned (32)));
+#else
+static UCHAR scratch[FX_STM32_SD_DEFAULT_SECTOR_SIZE] __attribute__ ((aligned (4)));
+#endif
+
+UINT  _fx_partition_offset_calculate(void  *partition_sector, UINT partition, ULONG *partition_start, ULONG *partition_size);
+
+static UINT sd_read_data(FX_MEDIA *media_ptr, ULONG sector, UINT num_sectors, UINT use_scratch_buffer);
+static UINT sd_write_data(FX_MEDIA *media_ptr, ULONG sector, UINT num_sectors, UINT use_scratch_buffer);
+
+static UINT is_initialized = 0;
+
+
+static INT check_sd_status(uint32_t instance)
+{
+  uint32_t start = FX_STM32_SD_CURRENT_TIME();
+
+  while (FX_STM32_SD_CURRENT_TIME() - start < FX_STM32_SD_DEFAULT_TIMEOUT)
+  {
+    if (fx_stm32_sd_get_status(instance) == 0)
+    {
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+/**
+* @brief This function is the entry point to the STM32 SD disk driver.
+* It relies on the STM32 peripheral library from ST.
+* @param media_ptr: FileX's Media Config Block
+* @retval None
+*/
+VOID  fx_stm32_sd_driver(FX_MEDIA *media_ptr)
+{
+  UINT status;
+  UINT unaligned_buffer;
+  ULONG partition_start;
+  ULONG partition_size;
+
+#if (FX_STM32_SD_INIT == 0)
+ /* the SD was initialized by the application */
+  is_initialized = 1;
+#endif
+  /* before performing any operation, check the status of the SD IP */
+  if (is_initialized == 1)
+  {
+    if (check_sd_status(FX_STM32_SD_INSTANCE) != 0)
+    {
+      media_ptr->fx_media_driver_status =  FX_IO_ERROR;
+      return;
+    }
+  }
+
+#if (FX_STM32_SD_DMA_API == 1)
+  /* the SD DMA requires a 4-byte aligned buffers */
+  unaligned_buffer = (UINT)(media_ptr->fx_media_driver_buffer) & 0x3;
+#else
+  /* if the DMA is not used there isn't any constraint on buffer alignment */
+  unaligned_buffer = 0;
+#endif
+  /* Process the driver request specified in the media control block.  */
+  switch(media_ptr->fx_media_driver_request)
+  {
+  case FX_DRIVER_INIT:
+    {
+      media_ptr->fx_media_driver_status = FX_SUCCESS;
+
+      FX_STM32_SD_PRE_INIT(media_ptr);
+
+#if (FX_STM32_SD_INIT == 1)
+      /* Initialize the SD instance */
+      if (is_initialized == 0)
+      {
+        status = fx_stm32_sd_init(FX_STM32_SD_INSTANCE);
+
+        if (status == 0)
+        {
+          is_initialized = 1;
+        }
+        else
+        {
+          media_ptr->fx_media_driver_status =  FX_IO_ERROR;
+        }
+      }
+#endif
+      /* call post init user macro */
+      FX_STM32_SD_POST_INIT(media_ptr);
+      break;
+    }
+
+  case FX_DRIVER_UNINIT:
+    {
+      media_ptr->fx_media_driver_status = FX_SUCCESS;
+
+#if (FX_STM32_SD_INIT == 1)
+      status = fx_stm32_sd_deinit(FX_STM32_SD_INSTANCE);
+
+      if (status != 0)
+      {
+        media_ptr->fx_media_driver_status = FX_IO_ERROR;
+      }
+      else
+      {
+        is_initialized = 0;
+      }
+#endif
+      /* call post deinit processing  */
+      FX_STM32_SD_POST_DEINIT(media_ptr);
+
+      break;
+    }
+
+  case FX_DRIVER_READ:
+    {
+      media_ptr->fx_media_driver_status = FX_IO_ERROR;
+
+      if (sd_read_data(media_ptr, media_ptr->fx_media_driver_logical_sector + media_ptr->fx_media_hidden_sectors,
+                       media_ptr->fx_media_driver_sectors, unaligned_buffer) == FX_SUCCESS)
+      {
+        media_ptr->fx_media_driver_status = FX_SUCCESS;
+      }
+
+      break;
+    }
+
+  case FX_DRIVER_WRITE:
+    {
+      media_ptr->fx_media_driver_status = FX_IO_ERROR;
+
+      if (sd_write_data(media_ptr, media_ptr->fx_media_driver_logical_sector + media_ptr->fx_media_hidden_sectors,
+                        media_ptr->fx_media_driver_sectors, unaligned_buffer) == FX_SUCCESS)
+      {
+        media_ptr->fx_media_driver_status = FX_SUCCESS;
+      }
+
+      break;
+    }
+
+  case FX_DRIVER_FLUSH:
+    {
+      /* Return driver success.  */
+      media_ptr->fx_media_driver_status =  FX_SUCCESS;
+      break;
+    }
+
+  case FX_DRIVER_ABORT:
+    {
+      /* Return driver success.  */
+      media_ptr->fx_media_driver_status =  FX_SUCCESS;
+
+      FX_STM32_SD_POST_ABORT(media_ptr);
+      break;
+    }
+
+  case FX_DRIVER_BOOT_READ:
+    {
+      /* the boot sector is the sector zero */
+      status = sd_read_data(media_ptr, 0, media_ptr->fx_media_driver_sectors, unaligned_buffer);
+
+      if (status != FX_SUCCESS)
+      {
+        media_ptr->fx_media_driver_status = status;
+        break;
+      }
+
+      /* Check if the sector 0 is the actual boot sector, otherwise calculate the offset into it.
+      Please note that this should belong to higher level of MW to do this check and it is here
+      as a temporary work solution */
+
+      partition_start =  0;
+
+      status =  _fx_partition_offset_calculate(media_ptr -> fx_media_driver_buffer, 0,
+                                               &partition_start, &partition_size);
+
+      /* Check partition read error.  */
+      if (status)
+      {
+        /* Unsuccessful driver request.  */
+        media_ptr -> fx_media_driver_status =  FX_IO_ERROR;
+        break;
+      }
+
+      /* Now determine if there is a partition...   */
+      if (partition_start)
+      {
+
+        if (check_sd_status(FX_STM32_SD_INSTANCE) != 0)
+        {
+          media_ptr->fx_media_driver_status =  FX_IO_ERROR;
+          break;
+        }
+
+        /* Yes, now lets read the actual boot record.  */
+        status = sd_read_data(media_ptr, partition_start, media_ptr->fx_media_driver_sectors, unaligned_buffer);
+
+        if (status != FX_SUCCESS)
+        {
+          media_ptr->fx_media_driver_status = status;
+          break;
+        }
+      }
+
+      /* Successful driver request.  */
+      media_ptr -> fx_media_driver_status =  FX_SUCCESS;
+      break;
+    }
+
+  case FX_DRIVER_BOOT_WRITE:
+    {
+      status = sd_write_data(media_ptr, 0, media_ptr->fx_media_driver_sectors, unaligned_buffer);
+
+      media_ptr->fx_media_driver_status = status;
+
+      break;
+    }
+
+  default:
+    {
+      media_ptr->fx_media_driver_status =  FX_IO_ERROR;
+      break;
+    }
+  }
+}
+
+/**
+* @brief Read data from uSD into destination buffer
+* @param FX_MEDIA *media_ptr a pointer the main FileX structure
+* @param ULONG start_sector first sector to start reading from
+* @param UINT num_sectors number of sectors to be read
+* @param UINT use_scratch_buffer to enable scratch buffer usage or not.
+* @retval FX_SUCCESS on success FX_BUFFER_ERROR / FX_ACCESS_ERROR / FX_IO_ERROR otherwise
+*/
+
+static UINT sd_read_data(FX_MEDIA *media_ptr, ULONG start_sector, UINT num_sectors, UINT use_scratch_buffer)
+{
+  INT i = 0;
+  UINT status;
+  UCHAR *read_addr;
+
+ /* perform the Pre read operations */
+  FX_STM32_SD_PRE_READ_TRANSFER(media_ptr);
+
+  if (use_scratch_buffer)
+  {
+    read_addr = media_ptr->fx_media_driver_buffer;
+
+    for (i = 0; i < num_sectors; i++)
+    {
+      /* Start reading into the scratch buffer */
+      status = fx_stm32_sd_read_blocks(FX_STM32_SD_INSTANCE, (UINT *)scratch, (UINT)start_sector++, 1);
+
+      if (status != 0)
+      {
+        /* read error occurred, call the error handler code then return immediately */
+        FX_STM32_SD_READ_TRANSFER_ERROR(status);
+        return FX_IO_ERROR;
+      }
+
+    /* wait for read transfer notification */
+       FX_STM32_SD_READ_CPLT_NOTIFY();
+
+#if (FX_STM32_SD_CACHE_MAINTENANCE == 1)
+      invalidate_cache_by_addr((uint32_t*)scratch, FX_STM32_SD_DEFAULT_SECTOR_SIZE);
+#endif
+
+      _fx_utility_memory_copy(scratch, read_addr, FX_STM32_SD_DEFAULT_SECTOR_SIZE);
+      read_addr += FX_STM32_SD_DEFAULT_SECTOR_SIZE;
+    }
+
+    /* Check if all sectors were read */
+    if (i == num_sectors)
+    {
+      status = FX_SUCCESS;
+    }
+    else
+    {
+      status = FX_BUFFER_ERROR;
+    }
+  }
+  else
+  {
+
+    status = fx_stm32_sd_read_blocks(FX_STM32_SD_INSTANCE, (UINT *)media_ptr->fx_media_driver_buffer, (UINT)start_sector, num_sectors);
+
+    if (status != 0)
+    {
+      /* read error occurred, call the error handler code then return immediately */
+      FX_STM32_SD_READ_TRANSFER_ERROR(status);
+
+      return FX_IO_ERROR;
+    }
+
+    /* wait for read transfer notification */
+       FX_STM32_SD_READ_CPLT_NOTIFY();
+
+#if (FX_STM32_SD_CACHE_MAINTENANCE == 1)
+    invalidate_cache_by_addr((uint32_t*)media_ptr->fx_media_driver_buffer, num_sectors * FX_STM32_SD_DEFAULT_SECTOR_SIZE);
+#endif
+
+    status = FX_SUCCESS;
+  }
+
+  /* Operation finished, call the post read macro if defined */
+
+  FX_STM32_SD_POST_READ_TRANSFER(media_ptr);
+  return status;
+}
+
+/**
+* @brief write data buffer into the uSD
+* @param FX_MEDIA *media_ptr a pointer the main FileX structure
+* @param ULONG start_sector first sector to start writing from
+* @param UINT num_sectors number of sectors to be written
+* @param UINT use_scratch_buffer to enable scratch buffer usage or not.
+* @retval FX_SUCCESS on success FX_BUFFER_ERROR / FX_ACCESS_ERROR / FX_IO_ERROR otherwise
+*/
+
+static UINT sd_write_data(FX_MEDIA *media_ptr, ULONG start_sector, UINT num_sectors, UINT use_scratch_buffer)
+{
+  INT i = 0;
+  UINT status;
+  UCHAR *write_addr;
+
+  /* call Pre write operation macro */
+  FX_STM32_SD_PRE_WRITE_TRANSFER(media_ptr);
+
+  if (use_scratch_buffer)
+  {
+    write_addr = media_ptr->fx_media_driver_buffer;
+
+    for (i = 0; i < num_sectors; i++)
+    {
+      _fx_utility_memory_copy(write_addr, scratch, FX_STM32_SD_DEFAULT_SECTOR_SIZE);
+      write_addr += FX_STM32_SD_DEFAULT_SECTOR_SIZE;
+
+#if (FX_STM32_SD_CACHE_MAINTENANCE == 1)
+      /* Clean the DCache to make the SD DMA see the actual content of the scratch buffer */
+      clean_cache_by_addr((uint32_t*)scratch, FX_STM32_SD_DEFAULT_SECTOR_SIZE);
+#endif
+
+      status = fx_stm32_sd_write_blocks(FX_STM32_SD_INSTANCE, (UINT *)scratch, (UINT)start_sector++, 1);
+
+      if (status != 0)
+      {
+        /* in case of error call the error handling macro */
+        FX_STM32_SD_WRITE_TRANSFER_ERROR(status);
+        return FX_IO_ERROR;
+      }
+
+      /*  */
+       FX_STM32_SD_WRITE_CPLT_NOTIFY();
+    }
+
+    if (i == num_sectors)
+    {
+      status = FX_SUCCESS;
+    }
+    else
+    {
+      status = FX_BUFFER_ERROR;
+    }
+  }
+  else
+  {
+#if (FX_STM32_SD_CACHE_MAINTENANCE == 1)
+    clean_cache_by_addr((uint32_t*)media_ptr->fx_media_driver_buffer, num_sectors * FX_STM32_SD_DEFAULT_SECTOR_SIZE);
+#endif
+    status = fx_stm32_sd_write_blocks(FX_STM32_SD_INSTANCE, (UINT *)media_ptr->fx_media_driver_buffer, (UINT)start_sector, num_sectors);
+
+    if (status != 0)
+    {
+      FX_STM32_SD_WRITE_TRANSFER_ERROR(status);
+      return FX_IO_ERROR;
+    }
+
+    /* when defined, wait for the write notification */
+     FX_STM32_SD_WRITE_CPLT_NOTIFY();
+
+    status = FX_SUCCESS;
+  }
+
+  /* perform post write operations */
+  FX_STM32_SD_POST_WRITE_TRANSFER(media_ptr);
+
+
+  return status;
+}
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/inc/fx_api.h
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/inc/fx_api.h	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/inc/fx_api.h	(revision 54)
@@ -0,0 +1,1689 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Application Interface                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  APPLICATION INTERFACE DEFINITION                       RELEASE        */
+/*                                                                        */
+/*    fx_api.h                                            PORTABLE C      */
+/*                                                           6.4.0        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This file defines the basic Application Interface (API) to the      */
+/*    high-performance FileX FAT compatible embedded file system.         */
+/*    All service prototypes and data structure definitions are defined   */
+/*    in this file.                                                       */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s), and      */
+/*                                            updated product constants,  */
+/*                                            and added conditionals to   */
+/*                                            disable few declarations    */
+/*                                            for code size reduction,    */
+/*                                            resulting in version 6.1    */
+/*  11-09-2020     William E. Lamie         Modified comment(s),          */
+/*                                            resulting in version 6.1.2  */
+/*  12-31-2020     William E. Lamie         Modified comment(s), and      */
+/*                                            updated product constants,  */
+/*                                            resulting in version 6.1.3  */
+/*  03-02-2021     William E. Lamie         Modified comment(s), and      */
+/*                                            added standalone support,   */
+/*                                            resulting in version 6.1.5  */
+/*  04-02-2021     William E. Lamie         Modified comment(s), and      */
+/*                                            updated product constants,  */
+/*                                            resulting in version 6.1.6  */
+/*  06-02-2021     William E. Lamie         Modified comment(s), and      */
+/*                                            updated product constants,  */
+/*                                            resulting in version 6.1.7  */
+/*  08-02-2021     William E. Lamie         Modified comment(s), and      */
+/*                                            updated product constants,  */
+/*                                            resulting in version 6.1.8  */
+/*  01-31-2022     Bhupendra Naphade        Modified comment(s), and      */
+/*                                            removed fixed sector        */
+/*                                            size in exFAT, fixed        */
+/*                                            errors without cache,       */
+/*                                            resulting in version 6.1.10 */
+/*  04-25-2022     William E. Lamie         Modified comment(s), and      */
+/*                                            updated product constants,  */
+/*                                            resulting in version 6.1.11 */
+/*  07-29-2022     William E. Lamie         Modified comment(s), and      */
+/*                                            updated product constants,  */
+/*                                            resulting in version 6.1.12 */
+/*  10-31-2022     Xiuwen Cai               Modified comment(s), and      */
+/*                                            updated product constants,  */
+/*                                            resulting in version 6.2.0  */
+/*  03-08-2023     Xiuwen Cai               Modified comment(s), and      */
+/*                                            updated product constants,  */
+/*                                            resulting in version 6.2.1  */
+/*  10-31-2023     Xiuwen Cai               Modified comment(s), and      */
+/*                                            updated product constants,  */
+/*                                            resulting in version 6.3.0  */
+/*  12-31-2023     Xiuwen Cai               Modified comment(s), and      */
+/*                                            updated product constants,  */
+/*                                            resulting in version 6.4.0  */
+/*                                                                        */
+/**************************************************************************/
+
+#ifndef FX_API_H
+#define FX_API_H
+
+
+/* Determine if a C++ compiler is being used.  If so, ensure that standard
+   C is used to process the API information.  */
+
+#ifdef __cplusplus
+
+/* Yes, C++ compiler is present.  Use standard C.  */
+extern   "C" {
+
+#endif
+
+
+/* Disable warning of parameter not used. */
+#ifndef FX_PARAMETER_NOT_USED
+#define FX_PARAMETER_NOT_USED(p) ((void)(p))
+#endif /* FX_PARAMETER_NOT_USED */
+
+
+/* Disable ThreadX error checking for internal FileX source code.  */
+
+#ifdef FX_SOURCE_CODE
+#ifndef TX_DISABLE_ERROR_CHECKING
+#define TX_DISABLE_ERROR_CHECKING
+#endif
+#endif
+
+
+/* Include the FileX port-specific file.  */
+
+#include "fx_port.h"
+
+/* Define compiler library include files */
+ 
+#ifdef FX_STANDALONE_ENABLE
+#include   "string.h"
+#endif
+
+/* Define the major/minor version information that can be used by the application
+   and the FileX source as well.  */
+
+#define AZURE_RTOS_FILEX
+#define FILEX_MAJOR_VERSION     6
+#define FILEX_MINOR_VERSION     4
+#define FILEX_PATCH_VERSION     0
+
+/* Define the following symbols for backward compatibility */
+#define EL_PRODUCT_FILEX
+
+#ifdef FX_STANDALONE_ENABLE
+
+/* FileX will be used without Azure RTOS ThreadX */
+
+#ifndef FX_SINGLE_THREAD
+#define FX_SINGLE_THREAD
+#endif /* !FX_SINGLE_THREAD */
+
+
+/* FileX will be used with local path logic disabled */
+
+#ifndef FX_NO_LOCAL_PATH
+#define FX_NO_LOCAL_PATH
+#endif /* !FX_NO_LOCAL_PATH */
+
+
+/* FileX is built without update to the time parameters. */
+
+#ifndef FX_NO_TIMER
+#define FX_NO_TIMER
+#endif /* !FX_NO_TIMER */
+
+#endif
+
+
+/* Override the interrupt protection provided in FileX port files to simply use ThreadX protection,
+   which is often in-line assembly.  */
+
+#ifndef FX_LEGACY_INTERRUPT_PROTECTION
+#undef  FX_INT_SAVE_AREA
+#undef  FX_DISABLE_INTS
+#undef  FX_RESTORE_INTS
+#define FX_INT_SAVE_AREA    TX_INTERRUPT_SAVE_AREA
+#define FX_DISABLE_INTS     TX_DISABLE
+#define FX_RESTORE_INTS     TX_RESTORE
+#endif
+
+
+/* Define the update rate of the system timer.  These values may also be defined at the command
+   line when compiling the fx_system_initialize.c module in the FileX library build.  Alternatively, they can
+   be modified in this file.  Note: the update rate must be an even number of seconds greater
+   than or equal to 2, which is the minimal update rate for FAT time. */
+
+#ifndef FX_UPDATE_RATE_IN_SECONDS
+#define FX_UPDATE_RATE_IN_SECONDS       10     /* Update time at 10 second intervals */
+#endif
+
+#ifndef FX_UPDATE_RATE_IN_TICKS
+#define FX_UPDATE_RATE_IN_TICKS         1000   /* Same update rate, but in ticks  */
+#endif
+
+
+/* Determine if fault tolerance is selected. If so, turn on the old fault tolerant options -
+   if they are not defined already.  */
+
+#ifdef FX_ENABLE_FAULT_TOLERANT
+#ifndef FX_FAULT_TOLERANT_DATA
+#define FX_FAULT_TOLERANT_DATA
+#endif
+#ifndef FX_FAULT_TOLERANT
+#define FX_FAULT_TOLERANT
+#endif
+#endif
+
+
+/* Determine if cache is disabled. If so, disable direct read sector cache.  */
+#ifdef FX_DISABLE_CACHE
+#ifndef FX_DISABLE_DIRECT_DATA_READ_CACHE_FILL
+#define FX_DISABLE_DIRECT_DATA_READ_CACHE_FILL
+#endif
+#endif
+
+
+/* Determine if local paths are enabled and if the local path setup code has not been defined.
+   If so, define the default local path setup code for files that reference the local path.  */
+
+#ifndef FX_NO_LOCAL_PATH
+#ifndef FX_LOCAL_PATH_SETUP
+#ifndef FX_SINGLE_THREAD
+#define FX_LOCAL_PATH_SETUP     extern TX_THREAD *_tx_thread_current_ptr;
+#else
+#define FX_NO_LOCAL_PATH
+#endif
+#endif
+#endif
+
+/* Determine if tracing is enabled.  */
+
+#if defined(TX_ENABLE_EVENT_TRACE) && !defined(FX_STANDALONE_ENABLE)
+
+
+/* Trace is enabled. Remap calls so that interrupts can be disabled around the actual event logging.  */
+
+#include "tx_trace.h"
+
+
+/* Define the object types in FileX, if not defined.  */
+
+#ifndef FX_TRACE_OBJECT_TYPE_MEDIA
+#define FX_TRACE_OBJECT_TYPE_MEDIA                      9               /* P1 = FAT cache size, P2 = sector cache size       */
+#define FX_TRACE_OBJECT_TYPE_FILE                       10              /* none                                              */
+#endif
+
+
+/* Define event filters that can be used to selectively disable certain events or groups of events.  */
+
+#define FX_TRACE_ALL_EVENTS                             0x00007800      /* All FileX events                          */
+#define FX_TRACE_INTERNAL_EVENTS                        0x00000800      /* FileX internal events                     */
+#define FX_TRACE_MEDIA_EVENTS                           0x00001000      /* FileX media events                        */
+#define FX_TRACE_DIRECTORY_EVENTS                       0x00002000      /* FileX directory events                    */
+#define FX_TRACE_FILE_EVENTS                            0x00004000      /* FileX file events                         */
+
+
+/* Define the trace events in FileX, if not defined.  */
+
+/* Define FileX Trace Events, along with a brief description of the additional information fields,
+   where I1 -> Information Field 1, I2 -> Information Field 2, etc.  */
+
+/* Define the FileX internal events first.  */
+
+#ifndef FX_TRACE_INTERNAL_LOG_SECTOR_CACHE_MISS
+#define FX_TRACE_INTERNAL_LOG_SECTOR_CACHE_MISS         201             /* I1 = media ptr, I2 = sector, I3 = total misses, I4 = cache size          */
+#define FX_TRACE_INTERNAL_DIR_CACHE_MISS                202             /* I1 = media ptr, I2 = total misses                                        */
+#define FX_TRACE_INTERNAL_MEDIA_FLUSH                   203             /* I1 = media ptr, I2 = dirty sectors                                       */
+#define FX_TRACE_INTERNAL_DIR_ENTRY_READ                204             /* I1 = media ptr                                                           */
+#define FX_TRACE_INTERNAL_DIR_ENTRY_WRITE               205             /* I1 = media ptr                                                           */
+#define FX_TRACE_INTERNAL_IO_DRIVER_READ                206             /* I1 = media ptr, I2 = sector, I3 = number of sectors, I4 = buffer         */
+#define FX_TRACE_INTERNAL_IO_DRIVER_WRITE               207             /* I1 = media ptr, I2 = sector, I3 = number of sectors, I4 = buffer         */
+#define FX_TRACE_INTERNAL_IO_DRIVER_FLUSH               208             /* I1 = media ptr                                                           */
+#define FX_TRACE_INTERNAL_IO_DRIVER_ABORT               209             /* I1 = media ptr                                                           */
+#define FX_TRACE_INTERNAL_IO_DRIVER_INIT                210             /* I1 = media ptr                                                           */
+#define FX_TRACE_INTERNAL_IO_DRIVER_BOOT_READ           211             /* I1 = media ptr, I2 = buffer                                              */
+#define FX_TRACE_INTERNAL_IO_DRIVER_RELEASE_SECTORS     212             /* I1 = media ptr, I2 = sector, I3 = number of sectors                      */
+#define FX_TRACE_INTERNAL_IO_DRIVER_BOOT_WRITE          213             /* I1 = media ptr, I2 = buffer                                              */
+#define FX_TRACE_INTERNAL_IO_DRIVER_UNINIT              214             /* I1 = media ptr                                                           */
+
+
+/* Define the FileX API events.  */
+
+#define FX_TRACE_DIRECTORY_ATTRIBUTES_READ              220             /* I1 = media ptr, I2 = directory name, I3 = attributes                     */
+#define FX_TRACE_DIRECTORY_ATTRIBUTES_SET               221             /* I1 = media ptr, I2 = directory name, I3 = attributes                     */
+#define FX_TRACE_DIRECTORY_CREATE                       222             /* I1 = media ptr, I2 = directory name                                      */
+#define FX_TRACE_DIRECTORY_DEFAULT_GET                  223             /* I1 = media ptr, I2 = return path name                                    */
+#define FX_TRACE_DIRECTORY_DEFAULT_SET                  224             /* I1 = media ptr, I2 = new path name                                       */
+#define FX_TRACE_DIRECTORY_DELETE                       225             /* I1 = media ptr, I2 = directory name                                      */
+#define FX_TRACE_DIRECTORY_FIRST_ENTRY_FIND             226             /* I1 = media ptr, I2 = directory name                                      */
+#define FX_TRACE_DIRECTORY_FIRST_FULL_ENTRY_FIND        227             /* I1 = media ptr, I2 = directory name                                      */
+#define FX_TRACE_DIRECTORY_INFORMATION_GET              228             /* I1 = media ptr, I2 = directory name                                      */
+#define FX_TRACE_DIRECTORY_LOCAL_PATH_CLEAR             229             /* I1 = media ptr                                                           */
+#define FX_TRACE_DIRECTORY_LOCAL_PATH_GET               230             /* I1 = media ptr, I2 = return path name                                    */
+#define FX_TRACE_DIRECTORY_LOCAL_PATH_RESTORE           231             /* I1 = media ptr, I2 = local path ptr                                      */
+#define FX_TRACE_DIRECTORY_LOCAL_PATH_SET               232             /* I1 = media ptr, I2 = local path ptr, I3 = new path name                  */
+#define FX_TRACE_DIRECTORY_LONG_NAME_GET                233             /* I1 = media ptr, I2 = short file name, I3 = long file name                */
+#define FX_TRACE_DIRECTORY_NAME_TEST                    234             /* I1 = media ptr, I2 = directory name                                      */
+#define FX_TRACE_DIRECTORY_NEXT_ENTRY_FIND              235             /* I1 = media ptr, I2 = directory name                                      */
+#define FX_TRACE_DIRECTORY_NEXT_FULL_ENTRY_FIND         236             /* I1 = media ptr, I2 = directory name                                      */
+#define FX_TRACE_DIRECTORY_RENAME                       237             /* I1 = media ptr, I2 = old directory name, I3 = new directory name         */
+#define FX_TRACE_DIRECTORY_SHORT_NAME_GET               238             /* I1 = media ptr, I2 = long file name, I3 = short file name                */
+#define FX_TRACE_FILE_ALLOCATE                          239             /* I1 = file ptr, I2 = size I3 = previous size, I4 = new size               */
+#define FX_TRACE_FILE_ATTRIBUTES_READ                   240             /* I1 = media ptr, I2 = file name, I3 = attributes                          */
+#define FX_TRACE_FILE_ATTRIBUTES_SET                    241             /* I1 = media ptr, I2 = file name, I3 = attributes                          */
+#define FX_TRACE_FILE_BEST_EFFORT_ALLOCATE              242             /* I1 = file ptr, I2 = size, I3 = actual_size_allocated                     */
+#define FX_TRACE_FILE_CLOSE                             243             /* I1 = file ptr, I3 = file size                                            */
+#define FX_TRACE_FILE_CREATE                            244             /* I1 = media ptr, I2 = file name                                           */
+#define FX_TRACE_FILE_DATE_TIME_SET                     245             /* I1 = media ptr, I2 = file name, I3 = year, I4 = month                    */
+#define FX_TRACE_FILE_DELETE                            246             /* I1 = media ptr, I2 = file name                                           */
+#define FX_TRACE_FILE_OPEN                              247             /* I1 = media ptr, I2 = file ptr, I3 = file name, I4 = open type            */
+#define FX_TRACE_FILE_READ                              248             /* I1 = file ptr, I2 = buffer ptr, I3 = request size I4 = actual size       */
+#define FX_TRACE_FILE_RELATIVE_SEEK                     249             /* I1 = file ptr, I2 = byte offset, I3 = seek from, I4 = previous offset    */
+#define FX_TRACE_FILE_RENAME                            250             /* I1 = media ptr, I2 = old file name, I3 = new file name                   */
+#define FX_TRACE_FILE_SEEK                              251             /* I1 = file ptr, I2 = byte offset, I3 = previous offset                    */
+#define FX_TRACE_FILE_TRUNCATE                          252             /* I1 = file ptr, I2 = size, I3 = previous size, I4 = new size              */
+#define FX_TRACE_FILE_TRUNCATE_RELEASE                  253             /* I1 = file ptr, I2 = size, I3 = previous size, I4 = new size              */
+#define FX_TRACE_FILE_WRITE                             254             /* I1 = file ptr, I2 = buffer ptr, I3 = size, I4 = bytes written            */
+#define FX_TRACE_MEDIA_ABORT                            255             /* I1 = media ptr                                                           */
+#define FX_TRACE_MEDIA_CACHE_INVALIDATE                 256             /* I1 = media ptr                                                           */
+#define FX_TRACE_MEDIA_CHECK                            257             /* I1 = media ptr, I2 = scratch memory, I3 = scratch memory size, I4 =errors*/
+#define FX_TRACE_MEDIA_CLOSE                            258             /* I1 = media ptr                                                           */
+#define FX_TRACE_MEDIA_FLUSH                            259             /* I1 = media ptr                                                           */
+#define FX_TRACE_MEDIA_FORMAT                           260             /* I1 = media ptr, I2 = root entries, I3 = sectors, I4 = sectors per cluster*/
+#define FX_TRACE_MEDIA_OPEN                             261             /* I1 = media ptr, I2 = media driver, I3 = memory ptr, I4 = memory size     */
+#define FX_TRACE_MEDIA_READ                             262             /* I1 = media ptr, I2 = logical sector, I3 = buffer ptr, I4 = bytes read    */
+#define FX_TRACE_MEDIA_SPACE_AVAILABLE                  263             /* I1 = media ptr, I2 = available bytes ptr, I3 = available clusters        */
+#define FX_TRACE_MEDIA_VOLUME_GET                       264             /* I1 = media ptr, I2 = volume name, I3 = volume source                     */
+#define FX_TRACE_MEDIA_VOLUME_SET                       265             /* I1 = media ptr, I2 = volume name                                         */
+#define FX_TRACE_MEDIA_WRITE                            266             /* I1 = media ptr, I2 = logical_sector, I3 = buffer_ptr, I4 = byte written  */
+#define FX_TRACE_SYSTEM_DATE_GET                        267             /* I1 = year, I2 = month, I3 = day                                          */
+#define FX_TRACE_SYSTEM_DATE_SET                        268             /* I1 = year, I2 = month, I3 = day                                          */
+#define FX_TRACE_SYSTEM_INITIALIZE                      269             /* None                                                                     */
+#define FX_TRACE_SYSTEM_TIME_GET                        270             /* I1 = hour, I2 = minute, I3 = second                                      */
+#define FX_TRACE_SYSTEM_TIME_SET                        271             /* I1 = hour, I2 = minute, I3 = second                                      */
+#define FX_TRACE_UNICODE_DIRECTORY_CREATE               272             /* I1 = media ptr, I2 = source unicode, I3 = source length, I4 = short_name */
+#define FX_TRACE_UNICODE_DIRECTORY_RENAME               273             /* I1 = media ptr, I2 = source unicode, I3 = source length, I4 = new_name   */
+#define FX_TRACE_UNICODE_FILE_CREATE                    274             /* I1 = media ptr, I2 = source unicode, I3 = source length, I4 = short name */
+#define FX_TRACE_UNICODE_FILE_RENAME                    275             /* I1 = media ptr, I2 = source unicode, I3 = source length, I4 = new name   */
+#define FX_TRACE_UNICODE_LENGTH_GET                     276             /* I1 = unicode name, I2 = length                                           */
+#define FX_TRACE_UNICODE_NAME_GET                       277             /* I1 = media ptr, I2 = source short name, I3 = unicode name, I4 = length   */
+#define FX_TRACE_UNICODE_SHORT_NAME_GET                 278             /* I1 = media ptr, I2 = source unicode name, I3 = length, I4 =  short name  */
+#endif
+
+
+/* Map the trace macros to internal FileX versions so we can get interrupt protection.  */
+
+#ifdef FX_SOURCE_CODE
+
+#define FX_TRACE_OBJECT_REGISTER(t, p, n, a, b)         _fx_trace_object_register(t, (VOID *)p, (CHAR *)n, (ULONG)a, (ULONG)b);
+#define FX_TRACE_OBJECT_UNREGISTER(o)                   _fx_trace_object_unregister((VOID *)o);
+#define FX_TRACE_IN_LINE_INSERT(i, a, b, c, d, f, g, h) _fx_trace_event_insert((ULONG)i, (ULONG)a, (ULONG)b, (ULONG)c, (ULONG)d, (ULONG)f, g, h);
+#define FX_TRACE_EVENT_UPDATE(e, t, i, a, b, c, d)      _fx_trace_event_update((TX_TRACE_BUFFER_ENTRY *)e, (ULONG)t, (ULONG)i, (ULONG)a, (ULONG)b, (ULONG)c, (ULONG)d);
+
+
+/* Define FileX trace prototypes.  */
+
+VOID _fx_trace_object_register(UCHAR object_type, VOID *object_ptr, CHAR *object_name, ULONG parameter_1, ULONG parameter_2);
+VOID _fx_trace_object_unregister(VOID *object_ptr);
+VOID _fx_trace_event_insert(ULONG event_id, ULONG info_field_1, ULONG info_field_2, ULONG info_field_3, ULONG info_field_4, ULONG filter, TX_TRACE_BUFFER_ENTRY **current_event, ULONG *current_timestamp);
+VOID _fx_trace_event_update(TX_TRACE_BUFFER_ENTRY *event, ULONG timestamp, ULONG event_id, ULONG info_field_1, ULONG info_field_2, ULONG info_field_3, ULONG info_field_4);
+
+#endif
+
+#else
+#define FX_TRACE_OBJECT_REGISTER(t, p, n, a, b)
+#define FX_TRACE_OBJECT_UNREGISTER(o)
+#define FX_TRACE_IN_LINE_INSERT(i, a, b, c, d, f, g, h)
+#define FX_TRACE_EVENT_UPDATE(e, t, i, a, b, c, d)
+#endif
+
+
+/* Define basic constants for FileX.  */
+
+#define FX_MEDIA_ID                            ((ULONG)0x4D454449)
+#define FX_MEDIA_CLOSED_ID                     ((ULONG)0x4D454443)
+#define FX_MEDIA_ABORTED_ID                    ((ULONG)0x4D454441)
+
+#define FX_FILE_ID                             ((ULONG)0x46494C45)
+#define FX_FILE_CLOSED_ID                      ((ULONG)0x46494C43)
+#define FX_FILE_ABORTED_ID                     ((ULONG)0x46494C41)
+
+
+/* The maximum path includes the entire path and the file name.  */
+
+#ifndef FX_MAXIMUM_PATH
+#define FX_MAXIMUM_PATH                        256
+#endif
+
+
+/* Define directory entry name sizes. Directory entries are found in the
+   root directory and in directory files (sub-directories). Each directory
+   entry has a short file name which is a fixed maximum of 13 characters and
+   and optional 255 character long file name.  */
+
+#ifndef FX_MAX_LONG_NAME_LEN
+#define FX_MAX_LONG_NAME_LEN                   256      /* Minimum value is 13, Maximum value is 256.  */
+#endif
+
+#define FX_MAX_SHORT_NAME_LEN                  13       /* Only allowed value is 13.  */
+
+#ifndef FX_MAX_LAST_NAME_LEN
+#define FX_MAX_LAST_NAME_LEN                   256      /* Should be as large or larger than FX_MAX_LONG_NAME_LEN.  */
+#endif
+
+
+/* Define constants related to the logical sector cache. These constants represent the maximum
+   number of logical sectors that can be cached. The actual number of logical sectors in the
+   cache is determined by the amount of memory supplied to fx_media_open for the FX_MEDIA
+   instance.  */
+
+#ifndef FX_MAX_SECTOR_CACHE
+#define FX_MAX_SECTOR_CACHE                    256      /* Maximum size of logical sector cache,
+                                                           minimum value of 2 all other values must
+                                                           be a power of 2.  */
+#endif
+
+/* Define the mask for the hash index into the logical sector cache.  If the logical sector cache
+   determined by the amount of memory supplied by the application to fx_media_open is larger than
+   FX_SECTOR_CACHE_HASH_ENABLE (usually 16) and can satisfy FX_MAX_SECTOR_CACHE sectors, the cache
+   is divided into 4 entry pieces (FX_SECTOR_CACHE_DEPTH) that are indexed by the formula:
+
+        index =  (cluster & media_ptr -> fx_media_sector_cache_hash_mask) * FX_SECTOR_CACHE_LEVELS
+
+   The FX_SECTOR_CACHE_DEPTH define must not be changed unless the logical sector read/write
+   code is also changed.  */
+
+#define FX_SECTOR_CACHE_HASH_ENABLE            16
+#define FX_SECTOR_CACHE_DEPTH                  4
+
+#ifndef FX_FAT_MAP_SIZE
+#define FX_FAT_MAP_SIZE                        128  /* Minimum 1, maximum any. This represents how many 32-bit words used for the written FAT sector bit map. */
+#endif
+
+#ifndef FX_MAX_FAT_CACHE
+#define FX_MAX_FAT_CACHE                       16   /* Minimum value is 8, all values must be a power of 2.  */
+#endif
+
+
+/* Define the size of fault tolerant cache, which is used when freeing FAT chain. */
+
+#ifndef FX_FAULT_TOLERANT_CACHE_SIZE
+#define FX_FAULT_TOLERANT_CACHE_SIZE           1024
+#endif /* FX_FAULT_TOLERANT_CACHE_SIZE */
+
+
+/* Define the mask for the hash index into the FAT table.  The FAT cache is divided into 4 entry pieces
+   that are indexed by the formula:
+
+            index =  (cluster & FX_FAT_CACHE_HASH_MASK) * FX_FAT_CACHE_LEVELS
+
+   The FX_FAT_CACHE_DEPTH define must not be changed unless the FAT entry read/write code is also changed.  */
+
+#define FX_FAT_CACHE_DEPTH                     4
+#define FX_FAT_CACHE_HASH_MASK                 ((FX_MAX_FAT_CACHE / FX_FAT_CACHE_DEPTH) - 1)
+
+
+/* FileX API input parameters and general constants.  */
+
+#define FX_TRUE                                1
+#define FX_FALSE                               0
+#define FX_NULL                                0
+#define FX_OPEN_FOR_READ                       0
+#define FX_OPEN_FOR_WRITE                      1
+#define FX_OPEN_FOR_READ_FAST                  2
+
+#define FX_12_BIT_FAT_SIZE                     4086
+#define FX_16_BIT_FAT_SIZE                     65525
+#define FX_MAX_12BIT_CLUST                     0x0FF0
+#define FX_SIGN_EXTEND                         0xF000
+#define FX_12BIT_SIZE                          3    /* 2 FAT entries per 3 bytes  */
+
+#define FX_FAT_ENTRY_START                     2
+#define FX_DIR_ENTRY_SIZE                      32
+#define FX_DIR_NAME_SIZE                       8
+#define FX_DIR_EXT_SIZE                        3
+#define FX_DIR_RESERVED                        8
+#define FX_DIR_ENTRY_FREE                      0xE5
+#define FX_DIR_ENTRY_DONE                      0x00
+#define FX_READ_ONLY                           0x01
+#define FX_HIDDEN                              0x02
+#define FX_SYSTEM                              0x04
+#define FX_VOLUME                              0x08
+#define FX_DIRECTORY                           0x10
+#define FX_ARCHIVE                             0x20
+#define FX_LONG_NAME                           (FX_READ_ONLY | FX_HIDDEN | FX_SYSTEM | FX_VOLUME)
+#define FX_LONG_NAME_ENTRY_LEN                 13
+
+
+/* Define FAT FAT entry values.  */
+
+#define FX_FREE_CLUSTER                        0x0000
+#define FX_NOT_USED                            0x0001
+#define FX_RESERVED_1                          0xFFF0
+#define FX_RESERVED_2                          0xFFF6
+#define FX_BAD_CLUSTER                         0xFFF7
+#define FX_LAST_CLUSTER_1                      0xFFF8
+#define FX_LAST_CLUSTER_2                      0xFFFF
+
+#define FX_RESERVED_1_32                       0x0FFFFFF0
+#define FX_RESERVED_2_32                       0x0FFFFFF6
+#define FX_BAD_CLUSTER_32                      0x0FFFFFF7
+#define FX_LAST_CLUSTER_1_32                   0x0FFFFFF8
+#define FX_LAST_CLUSTER_2_32                   0x0FFFFFFF
+
+
+/* Define time/date FAT constants.  */
+
+#define FX_YEAR_SHIFT                          9
+#define FX_MONTH_SHIFT                         5
+#define FX_HOUR_SHIFT                          11
+#define FX_MINUTE_SHIFT                        5
+#define FX_YEAR_MASK                           0x7F
+#define FX_MONTH_MASK                          0x0F
+#define FX_DAY_MASK                            0x1F
+#define FX_HOUR_MASK                           0x1F
+#define FX_MINUTE_MASK                         0x3F
+#define FX_SECOND_MASK                         0x1F
+#define FX_BASE_YEAR                           1980
+#define FX_MAXIMUM_YEAR                        2107
+#define FX_MAXIMUM_MONTH                       12
+#define FX_MAXIMUM_HOUR                        23
+#define FX_MAXIMUM_MINUTE                      59
+#define FX_MAXIMUM_SECOND                      59
+#define FX_INITIAL_DATE                        0x4A21   /* 01-01-2017 */
+#define FX_INITIAL_TIME                        0x0000   /* 12:00 am   */
+
+/* FileX API return values.  */
+
+#define FX_SUCCESS                             0x00
+#define FX_BOOT_ERROR                          0x01
+#define FX_MEDIA_INVALID                       0x02
+#define FX_FAT_READ_ERROR                      0x03
+#define FX_NOT_FOUND                           0x04
+#define FX_NOT_A_FILE                          0x05
+#define FX_ACCESS_ERROR                        0x06
+#define FX_NOT_OPEN                            0x07
+#define FX_FILE_CORRUPT                        0x08
+#define FX_END_OF_FILE                         0x09
+#define FX_NO_MORE_SPACE                       0x0A
+#define FX_ALREADY_CREATED                     0x0B
+#define FX_INVALID_NAME                        0x0C
+#define FX_INVALID_PATH                        0x0D
+#define FX_NOT_DIRECTORY                       0x0E
+#define FX_NO_MORE_ENTRIES                     0x0F
+#define FX_DIR_NOT_EMPTY                       0x10
+#define FX_MEDIA_NOT_OPEN                      0x11
+#define FX_INVALID_YEAR                        0x12
+#define FX_INVALID_MONTH                       0x13
+#define FX_INVALID_DAY                         0x14
+#define FX_INVALID_HOUR                        0x15
+#define FX_INVALID_MINUTE                      0x16
+#define FX_INVALID_SECOND                      0x17
+#define FX_PTR_ERROR                           0x18
+#define FX_INVALID_ATTR                        0x19
+#define FX_CALLER_ERROR                        0x20
+#define FX_BUFFER_ERROR                        0x21
+#define FX_NOT_IMPLEMENTED                     0x22
+#define FX_WRITE_PROTECT                       0x23
+#define FX_INVALID_OPTION                      0x24
+#define FX_SECTOR_INVALID                      0x89
+#define FX_IO_ERROR                            0x90
+#define FX_NOT_ENOUGH_MEMORY                   0x91
+#define FX_ERROR_FIXED                         0x92
+#define FX_ERROR_NOT_FIXED                     0x93
+#define FX_NOT_AVAILABLE                       0x94
+#define FX_INVALID_CHECKSUM                    0x95
+#define FX_READ_CONTINUE                       0x96
+#define FX_INVALID_STATE                       0x97
+
+
+/* FileX driver interface constants.  */
+
+#define FX_DRIVER_READ                         0
+#define FX_DRIVER_WRITE                        1
+#define FX_DRIVER_FLUSH                        2
+#define FX_DRIVER_ABORT                        3
+#define FX_DRIVER_INIT                         4
+#define FX_DRIVER_BOOT_READ                    5
+#define FX_DRIVER_RELEASE_SECTORS              6
+#define FX_DRIVER_BOOT_WRITE                   7
+#define FX_DRIVER_UNINIT                       8
+
+
+/* Define relative seek constants.  */
+
+#define FX_SEEK_BEGIN                          0
+#define FX_SEEK_END                            1
+#define FX_SEEK_FORWARD                        2
+#define FX_SEEK_BACK                           3
+
+
+/* Define types for logical sectors. This information is passed to the driver
+   as additional information.  */
+
+#define FX_UNKNOWN_SECTOR                      0
+#define FX_BOOT_SECTOR                         1
+#define FX_FAT_SECTOR                          2
+#define FX_DIRECTORY_SECTOR                    3
+#define FX_DATA_SECTOR                         4
+
+
+/* Define media diagnostic constants.  */
+
+#define FX_FAT_CHAIN_ERROR                     0x01
+#define FX_DIRECTORY_ERROR                     0x02
+#define FX_LOST_CLUSTER_ERROR                  0x04
+#define FX_FILE_SIZE_ERROR                     0x08
+
+
+/* Define boot record offset constants.  */
+
+#define FX_JUMP_INSTR                          0x000
+#define FX_OEM_NAME                            0x003
+#define FX_BYTES_SECTOR                        0x00B
+#define FX_SECTORS_CLUSTER                     0x00D
+#define FX_RESERVED_SECTORS                    0x00E
+#define FX_NUMBER_OF_FATS                      0x010
+#define FX_ROOT_DIR_ENTRIES                    0x011
+#define FX_SECTORS                             0x013
+#define FX_MEDIA_TYPE                          0x015
+#define FX_SECTORS_PER_FAT                     0x016
+#define FX_SECTORS_PER_TRK                     0x018
+#define FX_HEADS                               0x01A
+#define FX_HIDDEN_SECTORS                      0x01C
+#define FX_HUGE_SECTORS                        0x020
+#define FX_DRIVE_NUMBER                        0x024
+#define FX_RESERVED                            0x025
+#define FX_BOOT_SIG                            0x026
+#define FX_BOOT_SIG_32                         0x042
+#define FX_VOLUME_ID                           0x027
+#define FX_VOLUME_ID_32                        0x043
+#define FX_VOLUME_LABEL                        0x02B
+#define FX_VOLUME_LABEL_32                     0x047
+#define FX_FILE_SYSTEM_TYPE                    0x036
+#define FX_SIG_OFFSET                          0x1FE
+#define FX_ROOT_CLUSTER_32                     0x02C
+#define FX_SECTORS_PER_FAT_32                  0x024
+#define FX_SIG_BYTE_1                          0x55
+#define FX_SIG_BYTE_2                          0xAA
+
+
+/* If not-defined, default port-specific processing extensions to white space.  */
+
+#ifndef FX_FILE_OPEN_EXTENSION
+#define FX_FILE_OPEN_EXTENSION
+#endif
+
+#ifndef FX_DIRECTORY_ENTRY_READ_EXTENSION
+#define FX_DIRECTORY_ENTRY_READ_EXTENSION
+#endif
+
+#ifndef FX_DIRECTORY_ENTRY_WRITE_EXTENSION
+#define FX_DIRECTORY_ENTRY_WRITE_EXTENSION
+#endif
+
+#ifndef FX_UTILITY_FAT_ENTRY_READ_EXTENSION
+#define FX_UTILITY_FAT_ENTRY_READ_EXTENSION
+#endif
+
+#ifndef FX_UTILITY_FAT_ENTRY_WRITE_EXTENSION
+#define FX_UTILITY_FAT_ENTRY_WRITE_EXTENSION
+#endif
+
+#ifndef FX_UTILITY_LOGICAL_SECTOR_FLUSH_EXTENSION
+#define FX_UTILITY_LOGICAL_SECTOR_FLUSH_EXTENSION
+#endif
+
+#ifndef FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION
+#define FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION
+#endif
+
+#ifndef FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION_1
+#define FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION_1
+#endif
+
+#ifndef FX_UTILITY_LOGICAL_SECTOR_WRITE_EXTENSION
+#define FX_UTILITY_LOGICAL_SECTOR_WRITE_EXTENSION
+#endif
+
+
+#ifdef FX_ENABLE_EXFAT
+
+
+/* Define exFAT specific constants.  */
+
+#define FX_EF_MUST_BE_ZERO                     11       /* Must be Zero Area                  053 bytes */
+#define FX_EF_PARTITION_OFFSET                 64       /* Partition Offset                   008 bytes */
+#define FX_EF_VOLUME_LENGTH                    72       /* Number of sectors on the Partition 008 bytes */
+#define FX_EF_FAT_OFFSET                       80       /* Number of sectors before exFAT     004 bytes */
+#define FX_EF_FAT_LENGTH                       84       /* Each FAT length in Sectors         004 bytes */
+#define FX_EF_CLUSTER_HEAP_OFFSET              88       /* Number of Sectors Before Cluster   004 bytes
+                                                           Heap */
+#define FX_EF_CLUSTER_COUNT                    92       /* Number of Clusters in the Cluster  004 bytes
+                                                           Heap */
+#define FX_EF_FIRST_CLUSTER_OF_ROOT_DIR        96       /* Cluster index of the First         004 bytes
+                                                           Cluster of the ROOT Directory                */
+#define FX_EF_VOLUME_SERIAL_NUMBER             100      /* Volume Serial Number               004 bytes */
+#define FX_EF_FILE_SYSTEM_REVISION             104      /* File System  Revision              002 bytes */
+#define FX_EF_VOLUME_FLAGS                     106      /* Status of exFAT structures         002 bytes */
+#define FX_EF_BYTE_PER_SECTOR_SHIFT            108      /* log2(N),where N is the Number of   001 byte
+                                                           Bytes per Sector */
+#define FX_EF_SECTOR_PER_CLUSTER_SHIFT         109      /* log2(N),where N is the Number of   001 byte
+                                                           Sectors per Cluster */
+#define FX_EF_NUMBER_OF_FATS                   110      /* Number of FATs                     001 byte */
+#define FX_EF_DRIVE_SELECT                     111      /* Extended INT13h driver number      001 byte */
+#define FX_EF_PERCENT_IN_USE                   112      /* Percentage of Allocated Clusters   001 byte */
+#define FX_EF_RESERVED                         113      /* Reserved                           007 bytes */
+#define FX_EF_BOOT_CODE                        120      /* Boot code area                     390 bytes */
+
+#define FX_RESERVED_1_exFAT                    0xFFFFFFF8
+#define FX_RESERVED_2_exFAT                    0xFFFFFFFE
+#define FX_BAD_CLUSTER_exFAT                   0xFFFFFFF7
+#define FX_LAST_CLUSTER_exFAT                  0xFFFFFFFF
+
+#ifndef FX_MAX_EX_FAT_NAME_LEN
+#define FX_MAX_EX_FAT_NAME_LEN                 255      /* Only allowed value is 255 */
+#endif
+
+#ifndef BITS_PER_BYTE
+#define BITS_PER_BYTE                          8
+#endif
+#define BITS_PER_BYTE_SHIFT                    3
+
+
+#define FX_EXFAT_MAX_DIRECTORY_SIZE            (256 * 1024 * 1024)  /* 256 MB */
+#define FX_EXFAT_SIZE_OF_FAT_ELEMENT_SHIFT     2
+
+#define FX_EXFAT_BIT_MAP_NUM_OF_CACHED_SECTORS 1
+
+#define FX_EXFAT_BITMAP_CLUSTER_FREE           0
+#define FX_EXFAT_BITMAP_CLUSTER_OCCUPIED       1
+
+#ifndef FX_EXFAT_MAX_CACHE_SIZE
+#define FX_EXFAT_MAX_CACHE_SIZE                512
+#endif
+#define FX_EXFAT_BITMAP_CACHE_SIZE             FX_EXFAT_MAX_CACHE_SIZE
+
+/* exFAT System Area Layout */
+
+#define FX_EXFAT_FAT_MAIN_SYSTEM_AREA_SIZE     12
+
+#define FX_EXFAT_FAT_MAIN_BOOT_SECTOR_OFFSET   0
+#define FX_EXFAT_FAT_BACKUP_BOOT_SECTOR_OFFSET FX_EXFAT_FAT_MAIN_SYSTEM_AREA_SIZE
+
+#define FX_EXFAT_FAT_EXT_BOOT_SECTOR_OFFSET    1
+#define FX_EXFAT_FAT_OEM_PARAM_OFFSET          9
+#define FX_EXFAT_FAT_CHECK_SUM_OFFSET          11
+
+#define FX_EXFAT_FAT_NUM_OF_SYSTEM_AREAS       2
+
+
+/* Define exFAT format parameters.  */
+
+#define  EXFAT_MIN_NUM_OF_RESERVED_SECTORS     1
+#define  EXFAT_BOOT_REGION_SIZE                24
+#define  EXFAT_FAT_BITS                        32
+#define  EXFAT_FAT_FILE_SYS_REVISION           0x100
+#define  EXFAT_FAT_VOLUME_FLAG                 0x000
+#define  EXFAT_FAT_NUM_OF_FATS                 0x001
+#define  EXFAT_FAT_DRIVE_SELECT                0x080
+#define  EXFAT_FAT_VOLUME_NAME_FIELD_SIZE      11
+#define  EXFAT_BIT_MAP_FIRST_TABLE             0
+#define  EXFAT_LAST_CLUSTER_MASK               0xFFFFFFFF
+#define  EXFAT_DEFAULT_BOUNDARY_UNIT           128
+#define  EXFAT_NUM_OF_DIR_ENTRIES              2
+
+
+#define DIVIDE_TO_CEILING(a, b)                (((a) + (b) - 1) / (b))
+#define ALIGN_UP(a, b)                         (DIVIDE_TO_CEILING(a, b) * (b))
+
+#define FX_FAT12                               0x01
+#define FX_FAT16                               0x04
+#define FX_BIGDOS                              0x06
+#define FX_exFAT                               0x07
+#define FX_FAT32                               0x0B
+#define FX_NO_FAT                              0xFF
+
+#endif /* FX_ENABLE_EXFAT */
+
+
+/* Define the control block definitions for all system objects.  */
+
+
+/* Define a single entry in the FAT cache.  The FAT cache is used to reduce the
+   number of times the actual FAT sectors need to be accessed, thereby improving
+   performance.  */
+
+typedef struct FX_FAT_CACHE_ENTRY_STRUCT
+{
+    ULONG fx_fat_cache_entry_cluster;
+    ULONG fx_fat_cache_entry_value;
+    ULONG fx_fat_cache_entry_dirty;
+} FX_FAT_CACHE_ENTRY;
+
+
+/* Define the directory entry structure that contains information about a specific
+   directory entry.  */
+
+typedef struct FX_DIR_ENTRY_STRUCT
+{
+
+    CHAR   *fx_dir_entry_name;
+    CHAR    fx_dir_entry_short_name[FX_MAX_SHORT_NAME_LEN];                 /* Short file name, if LFN is present                */
+    UINT    fx_dir_entry_long_name_present;                                 /* 0 (default) => LFN not present; 1 => LFN present  */
+    UINT    fx_dir_entry_long_name_shorted;                                 /* LFN too large, file name was made shorter         */
+    UCHAR   fx_dir_entry_attributes;                                        /* Directory entry attributes                        */
+    UCHAR   fx_dir_entry_reserved;                                          /* NT reserved, write 0 at create, otherwise ignore  */
+    UCHAR   fx_dir_entry_created_time_ms;                                   /* Create time in milliseconds, always write 0       */
+    UINT    fx_dir_entry_created_time;                                      /* Created time                                      */
+    UINT    fx_dir_entry_created_date;                                      /* Created date                                      */
+    UINT    fx_dir_entry_last_accessed_date;                                /* Last accessed date                                */
+    UINT    fx_dir_entry_time;                                              /* Modified time                                     */
+    UINT    fx_dir_entry_date;                                              /* Modified cluster                                  */
+    ULONG   fx_dir_entry_cluster;                                           /* File/directory's starting cluster                 */
+    ULONG64 fx_dir_entry_file_size;                                         /* Size of the file in bytes                         */
+    ULONG64 fx_dir_entry_log_sector;                                        /* Logical sector of this directory entry            */
+    ULONG   fx_dir_entry_byte_offset;                                       /* Offset in logical sector of this directory entry  */
+    ULONG   fx_dir_entry_number;                                            /* Index into the directory                          */
+    ULONG   fx_dir_entry_last_search_cluster;                               /* Last cluster searched                             */
+    ULONG   fx_dir_entry_last_search_relative_cluster;                      /* Last relative cluster searched                    */
+    ULONG64 fx_dir_entry_last_search_log_sector;                            /* Last logical sector searched                      */
+    ULONG   fx_dir_entry_last_search_byte_offset;                           /* Last offset in logical sector searched            */
+    ULONG64 fx_dir_entry_next_log_sector;
+
+#ifdef FX_ENABLE_EXFAT
+    /* for exFAT */
+    CHAR    fx_dir_entry_dont_use_fat;                                      /* 0 bit - for current, 1st bit - for parent         */
+    UCHAR   fx_dir_entry_type;
+    ULONG64 fx_dir_entry_available_file_size;
+    ULONG   fx_dir_entry_secondary_count;
+#endif /* FX_ENABLE_EXFAT */
+} FX_DIR_ENTRY;
+
+typedef FX_DIR_ENTRY  *FX_DIR_ENTRY_PTR;
+
+
+/* Define the path data structure.  This structure will contain
+   the current path and the information for performing directory
+   entry operations.  */
+
+typedef struct FX_PATH_STRUCT
+{
+    /* Define the path information.  */
+    FX_DIR_ENTRY fx_path_directory;
+    CHAR         fx_path_string[FX_MAXIMUM_PATH];
+    CHAR         fx_path_name_buffer[FX_MAX_LONG_NAME_LEN];
+    ULONG        fx_path_current_entry;
+} FX_PATH;
+
+typedef FX_PATH FX_LOCAL_PATH;
+
+
+/* Define the cache control data structure.  There are FX_MAX_SECTOR_CACHE
+   of these structures defined inside the FX_MEDIA structure.  Each entry
+   maintains a cache representation of a media sector.  */
+
+typedef struct FX_CACHED_SECTOR_STRUCT
+{
+
+    /* Define the buffer pointer associated with this cache entry.  */
+    UCHAR               *fx_cached_sector_memory_buffer;
+
+    /* Define the sector number that is cached.  */
+    ULONG64             fx_cached_sector;
+
+    /* Define the flag that indicates whether or not the cached sector
+       has been modified and needs to be written to the media.  */
+    UCHAR               fx_cached_sector_buffer_dirty;
+
+    /* Define the valid flag that indicates whether or not this entry is
+       still valid.  */
+    UCHAR               fx_cached_sector_valid;
+
+    /* Define the sector type, which indicates what type of sector is present.  */
+    UCHAR               fx_cached_sector_type;
+
+    /* Define a reserved byte, reserved for future use.  */
+    UCHAR               fx_cached_sector_reserved;
+
+    /* Define the next cached sector pointer.  This is used to implement
+       the "last used" algorithm when looking for cache entry to swap out to
+       the physical media.  */
+    struct FX_CACHED_SECTOR_STRUCT
+                        *fx_cached_sector_next_used;
+
+} FX_CACHED_SECTOR;
+
+
+/* Determine if the media control block has an extension defined. If not, 
+   define the extension to whitespace.  */
+
+#ifndef FX_MEDIA_MODULE_EXTENSION
+#define FX_MEDIA_MODULE_EXTENSION
+#endif
+
+
+/* Define the media control block.  All information about each open
+   media device are maintained in by the FX_MEDIA data type.  */
+
+typedef struct FX_MEDIA_STRUCT
+{
+
+    /* Define the media ID used for error checking.  */
+    ULONG               fx_media_id;
+
+    /* Define the media's name.  */
+    CHAR                *fx_media_name;
+
+    /* Remember the memory buffer area.  */
+    UCHAR               *fx_media_memory_buffer;
+    ULONG               fx_media_memory_size;
+
+#ifdef FX_DISABLE_CACHE
+    ULONG64             fx_media_memory_buffer_sector;
+#else
+
+    /* Define the flag that indicates whether the logical cache utilizes
+       a hash function or is a linear search. If set, the logical cache
+       is accessed via a hash function on the requested sector.  */
+    UINT                fx_media_sector_cache_hashed;
+
+    /* Define the number of sectors that can actually be cached based on the
+       user supplied buffer at media open time.  */
+    ULONG               fx_media_sector_cache_size;
+
+    /* Define the end of the cache area.  This is used to determine
+       if the I/O is for the internal memory of the media.  */
+    UCHAR               *fx_media_sector_cache_end;
+
+    /* Define the list head of the cached sector entries.  This
+       pointer points to the most recently used cache sector.  */
+    struct FX_CACHED_SECTOR_STRUCT
+                        *fx_media_sector_cache_list_ptr;
+
+    /* Define the bit map that represents the hashed cache sectors that are
+       valid. This bit map will help optimize the invalidation of the hashed
+       sector cache.  */
+    ULONG               fx_media_sector_cache_hashed_sector_valid;
+
+    /* Define the outstanding dirty sector counter. This is used to optimize
+       the searching of sectors to flush to the media.  */
+    ULONG               fx_media_sector_cache_dirty_count;
+#endif /* FX_DISABLE_CACHE */
+
+    /* Define the basic information about the associated media.  */
+    UINT                fx_media_bytes_per_sector;
+    UINT                fx_media_sectors_per_track;
+    UINT                fx_media_heads;
+
+    ULONG64             fx_media_total_sectors;
+    ULONG               fx_media_total_clusters;
+
+#ifdef FX_ENABLE_EXFAT
+    /* Define exFAT media information.  */
+    ULONG               fx_media_exfat_volume_serial_number;
+    UINT                fx_media_exfat_file_system_revision;
+    UINT                fx_media_exfat_volume_flag;
+    USHORT              fx_media_exfat_drive_select;
+    USHORT              fx_media_exfat_percent_in_use;
+    UINT                fx_media_exfat_bytes_per_sector_shift;
+    UINT                fx_media_exfat_sector_per_clusters_shift;
+
+    /* exFAT: Bitmap cache */
+    /* Pointer to Bitmap cache */
+    UCHAR               fx_media_exfat_bitmap_cache[FX_EXFAT_BITMAP_CACHE_SIZE];
+
+    /* Define beginning sector of Bitmap table.  */
+    ULONG               fx_media_exfat_bitmap_start_sector;
+
+    /* Define the cache size in sectors. Used for flash operation.  */
+    ULONG               fx_media_exfat_bitmap_cache_size_in_sectors;
+
+    /* Define the number of first cached cluster.  */
+    ULONG               fx_media_exfat_bitmap_cache_start_cluster;
+
+    /* Define the number of last cached cluster.  */
+    ULONG               fx_media_exfat_bitmap_cache_end_cluster;
+
+    /* Define how many clusters mapped in one sector.  */
+    UINT                fx_media_exfat_bitmap_clusters_per_sector_shift;
+
+    /* Define is Bitmap table was changed or not.  */
+    UINT                fx_media_exfat_bitmap_cache_dirty;
+#endif /* FX_ENABLE_EXFAT */
+
+    UINT                fx_media_reserved_sectors;
+    UINT                fx_media_root_sector_start;
+    UINT                fx_media_root_sectors;
+    UINT                fx_media_data_sector_start;
+    UINT                fx_media_sectors_per_cluster;
+    UINT                fx_media_sectors_per_FAT;
+    UINT                fx_media_number_of_FATs;
+    UINT                fx_media_12_bit_FAT;
+    UINT                fx_media_32_bit_FAT;
+    ULONG               fx_media_FAT32_additional_info_sector;
+    UINT                fx_media_FAT32_additional_info_last_available;
+#ifdef FX_DRIVER_USE_64BIT_LBA
+    ULONG64             fx_media_hidden_sectors;
+#else
+    ULONG               fx_media_hidden_sectors;
+#endif
+    ULONG               fx_media_root_cluster_32;
+    UINT                fx_media_root_directory_entries;
+    ULONG               fx_media_available_clusters;
+    ULONG               fx_media_cluster_search_start;
+
+    /* Define the information pertinent to the I/O driver interface.  */
+
+    VOID                *fx_media_driver_info;
+    UINT                fx_media_driver_request;
+    UINT                fx_media_driver_status;
+    UCHAR               *fx_media_driver_buffer;
+#ifdef FX_DRIVER_USE_64BIT_LBA
+    ULONG64             fx_media_driver_logical_sector;
+#else
+    ULONG               fx_media_driver_logical_sector;
+#endif
+    ULONG               fx_media_driver_sectors;
+    ULONG               fx_media_driver_physical_sector;
+    UINT                fx_media_driver_physical_track;
+    UINT                fx_media_driver_physical_head;
+    UINT                fx_media_driver_write_protect;      /* The driver sets this to FX_TRUE when media is write protected.  */
+    UINT                fx_media_driver_free_sector_update; /* The driver sets this to FX_TRUE when it needs to know freed clusters.  */
+    UINT                fx_media_driver_system_write;
+    UINT                fx_media_driver_data_sector_read;
+    UINT                fx_media_driver_sector_type;
+
+    /* Define the driver entry point.  */
+    VOID                (*fx_media_driver_entry)(struct FX_MEDIA_STRUCT *);
+
+    /* Define notify function called when media is open. */
+    VOID                (*fx_media_open_notify)(struct FX_MEDIA_STRUCT *);
+
+    /* Define notify function called when media is closed. */
+    VOID                (*fx_media_close_notify)(struct FX_MEDIA_STRUCT *);
+
+    /* Define the head pointer for the open files of this media.  */
+    struct FX_FILE_STRUCT
+                        *fx_media_opened_file_list;
+
+    /* Define the counter for keeping track of how many open files are
+       present.  */
+    ULONG               fx_media_opened_file_count;
+
+    /* Define the next and previous link pointers for the open media list.  */
+    struct FX_MEDIA_STRUCT
+                        *fx_media_opened_next,
+                        *fx_media_opened_previous;
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+    /* Define various statistics for this media instance. This information
+       should be useful in performance tuning and optimizing the application.  */
+
+    ULONG               fx_media_directory_attributes_reads;
+    ULONG               fx_media_directory_attributes_sets;
+    ULONG               fx_media_directory_creates;
+    ULONG               fx_media_directory_default_gets;
+    ULONG               fx_media_directory_default_sets;
+    ULONG               fx_media_directory_deletes;
+    ULONG               fx_media_directory_first_entry_finds;
+    ULONG               fx_media_directory_first_full_entry_finds;
+    ULONG               fx_media_directory_information_gets;
+    ULONG               fx_media_directory_local_path_clears;
+    ULONG               fx_media_directory_local_path_gets;
+    ULONG               fx_media_directory_local_path_restores;
+    ULONG               fx_media_directory_local_path_sets;
+    ULONG               fx_media_directory_name_tests;
+    ULONG               fx_media_directory_next_entry_finds;
+    ULONG               fx_media_directory_next_full_entry_finds;
+    ULONG               fx_media_directory_renames;
+    ULONG               fx_media_file_allocates;
+    ULONG               fx_media_file_attributes_reads;
+    ULONG               fx_media_file_attributes_sets;
+    ULONG               fx_media_file_best_effort_allocates;
+    ULONG               fx_media_file_closes;
+    ULONG               fx_media_file_creates;
+    ULONG               fx_media_file_deletes;
+    ULONG               fx_media_file_opens;
+    ULONG               fx_media_file_reads;
+    ULONG               fx_media_file_relative_seeks;
+    ULONG               fx_media_file_renames;
+    ULONG               fx_media_file_seeks;
+    ULONG               fx_media_file_truncates;
+    ULONG               fx_media_file_truncate_releases;
+    ULONG               fx_media_file_writes;
+    ULONG               fx_media_aborts;
+    ULONG               fx_media_flushes;
+    ULONG               fx_media_reads;
+    ULONG               fx_media_writes;
+    ULONG               fx_media_directory_entry_reads;
+    ULONG               fx_media_directory_entry_writes;
+    ULONG               fx_media_directory_searches;
+    ULONG               fx_media_directory_free_searches;
+    ULONG               fx_media_fat_entry_reads;
+    ULONG               fx_media_fat_entry_writes;
+    ULONG               fx_media_fat_entry_cache_read_hits;
+    ULONG               fx_media_fat_entry_cache_read_misses;
+    ULONG               fx_media_fat_entry_cache_write_hits;
+    ULONG               fx_media_fat_entry_cache_write_misses;
+    ULONG               fx_media_fat_cache_flushes;
+    ULONG               fx_media_fat_sector_reads;
+    ULONG               fx_media_fat_sector_writes;
+    ULONG               fx_media_logical_sector_reads;
+    ULONG               fx_media_logical_sector_writes;
+    ULONG               fx_media_logical_sector_cache_read_hits;
+    ULONG               fx_media_logical_sector_cache_read_misses;
+    ULONG               fx_media_driver_read_requests;
+    ULONG               fx_media_driver_write_requests;
+    ULONG               fx_media_driver_boot_read_requests;
+    ULONG               fx_media_driver_boot_write_requests;
+    ULONG               fx_media_driver_release_sectors_requests;
+    ULONG               fx_media_driver_flush_requests;
+#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
+    ULONG               fx_media_directory_search_cache_hits;
+#endif
+#endif
+
+    /* Define the media's protection object, which is a ThreadX mutex.
+       Only one thread is allowed to access any media or associated files
+       at a time.  If FX_SINGLE_THREAD is defined, the FileX services are
+       going to be called from only one thread, hence the protection is
+       not needed.  */
+#ifndef FX_SINGLE_THREAD
+    TX_MUTEX            fx_media_protect;
+#endif
+
+#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
+
+    /* Define the information used to remember the last directory entry found through
+       searching or walking the directory via directory entry next. This information
+       will be used to eliminate multiple searches for the same directory entry if
+       the accesses are done sequentially.  */
+    UINT                fx_media_last_found_directory_valid;
+    FX_DIR_ENTRY        fx_media_last_found_directory;
+    FX_DIR_ENTRY        fx_media_last_found_entry;
+    CHAR                fx_media_last_found_file_name[FX_MAX_LONG_NAME_LEN];
+    CHAR                fx_media_last_found_name[FX_MAX_LAST_NAME_LEN];
+#endif
+
+    /* Define the current directory information for the media.  */
+    FX_PATH             fx_media_default_path;
+
+    /* Define FAT entry cache and the variable used to index the cache.  */
+    FX_FAT_CACHE_ENTRY  fx_media_fat_cache[FX_MAX_FAT_CACHE];
+
+    /* Define the FAT secondary update map.  This will be used on flush and
+       close to update sectors of any secondary FATs in the media.  */
+    UCHAR               fx_media_fat_secondary_update_map[FX_FAT_MAP_SIZE];
+
+    /* Define a variable for the application's use.  */
+    ALIGN_TYPE          fx_media_reserved_for_user;
+
+    /* Define an area to allocate long file names so that local storage on
+       calling thread's stack is not used for long file names.  This helps
+       reduce the amount of thread stack space needed when using FileX.  */
+    CHAR                fx_media_name_buffer[4*FX_MAX_LONG_NAME_LEN];
+
+#ifdef FX_RENAME_PATH_INHERIT
+
+    /* Define the file and directory rename buffer that will be used to prepend
+       paths when necessary to the target file name.  */
+    CHAR                fx_media_rename_buffer[FX_MAXIMUM_PATH];
+#endif
+
+#ifndef FX_DISABLE_CACHE
+    /* Define the sector cache control structures for this media.  */
+    struct FX_CACHED_SECTOR_STRUCT
+                        fx_media_sector_cache[FX_MAX_SECTOR_CACHE];
+
+    /* Define the sector cache hash mask so that the hash algorithm can be used with
+       any power of 2 number of cache sectors.  */
+    ULONG               fx_media_sector_cache_hash_mask;
+#endif /* FX_DISABLE_CACHE */
+
+    /* Define a variable to disable burst cache. This is used by the underlying
+       driver.  */
+    ULONG               fx_media_disable_burst_cache;
+
+#ifdef FX_ENABLE_FAULT_TOLERANT
+
+    /* Fault tolerant information */
+    /* Indicate whether fault tolerant is enabled. */
+
+    UCHAR               fx_media_fault_tolerant_enabled;
+
+    /* State of fault tolerant operation. */
+    UCHAR               fx_media_fault_tolerant_state;
+
+    /* Transaction recursive count. */
+    USHORT              fx_media_fault_tolerant_transaction_count;
+
+    /* Start cluster of log. */
+    ULONG               fx_media_fault_tolerant_start_cluster;
+
+    /* Count of consecutive clusters of log. */
+    ULONG               fx_media_fault_tolerant_clusters;
+
+    /* Count of total logs. */
+    ULONG               fx_media_fault_tolerant_total_logs;
+
+    /* Pointer to the memory buffer area used for fault tolerant operations.  */
+    UCHAR              *fx_media_fault_tolerant_memory_buffer;
+
+    /* Size of memory buffer area used for fault tolerant operations. */
+    ULONG               fx_media_fault_tolerant_memory_buffer_size;
+
+    /* Size of log file. */
+    ULONG               fx_media_fault_tolerant_file_size;
+
+    /* Memory space used during the release of FAT list. */
+    ULONG               fx_media_fault_tolerant_cache[FX_FAULT_TOLERANT_CACHE_SIZE >> 2];
+
+    /* Sector number of cached FAT entries. */
+    ULONG               fx_media_fault_tolerant_cached_FAT_sector;
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+
+    /* Reserved value of FAT table. */
+    ULONG               fx_media_fat_reserved;
+
+    /* Last value of FAT table. */
+    ULONG               fx_media_fat_last;
+
+    /* Media geometry structure */
+    UCHAR               fx_media_FAT_type;
+
+    /* Define the module port extension in the media control block. This 
+       is typically defined to whitespace in fx_port.h.  */
+    FX_MEDIA_MODULE_EXTENSION
+
+} FX_MEDIA;
+
+typedef FX_MEDIA *      FX_MEDIA_PTR;
+
+
+/* Determine if the file control block has an extension defined. If not, 
+   define the extension to whitespace.  */
+
+#ifndef FX_FILE_MODULE_EXTENSION
+#define FX_FILE_MODULE_EXTENSION
+#endif
+
+
+/* Define the FileX file control block.  All information about open
+   files are found in this data type.  */
+
+typedef struct FX_FILE_STRUCT
+{
+
+    /* Define the file ID used for error checking.  */
+    ULONG               fx_file_id;
+
+    /* Define the file's name.  */
+    CHAR                *fx_file_name;
+
+    /* Define the open mode request.  */
+    ULONG               fx_file_open_mode;
+
+    /* Define the file modified field.  */
+    UCHAR               fx_file_modified;
+
+    /* Define the data storage parameters.  */
+    ULONG               fx_file_total_clusters;
+    ULONG               fx_file_first_physical_cluster;
+    ULONG               fx_file_consecutive_cluster;
+    ULONG               fx_file_last_physical_cluster;
+    ULONG               fx_file_current_physical_cluster;
+    ULONG64             fx_file_current_logical_sector;
+    ULONG               fx_file_current_logical_offset;
+    ULONG               fx_file_current_relative_cluster;
+    ULONG               fx_file_current_relative_sector;
+    ULONG64             fx_file_current_file_offset;
+    ULONG64             fx_file_current_file_size;
+    ULONG64             fx_file_current_available_size;
+#ifdef FX_ENABLE_FAULT_TOLERANT
+    ULONG64             fx_file_maximum_size_used;
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+
+    /* Remember the media that is associated with this file. */
+    FX_MEDIA            *fx_file_media_ptr;
+
+    /* Define the pointers necessary to maintain the open file on
+       the list of open files.  */
+    struct FX_FILE_STRUCT
+                        *fx_file_opened_next,
+                        *fx_file_opened_previous;
+
+    /* Define the complete directory entry structure.  */
+    FX_DIR_ENTRY        fx_file_dir_entry;
+    CHAR                fx_file_name_buffer[FX_MAX_LONG_NAME_LEN];
+
+    /* Define a variable for the application's use */
+    ULONG               fx_file_disable_burst_cache;
+
+    /* Define a notify function called when file is written to. */
+    VOID               (*fx_file_write_notify)(struct FX_FILE_STRUCT *);
+
+    /* Define the module port extension in the file control block. This 
+       is typically defined to whitespace in fx_port.h.  */
+    FX_FILE_MODULE_EXTENSION
+
+} FX_FILE;
+
+typedef FX_FILE  *FX_FILE_PTR;
+
+
+/* Define the FileX API mappings based on the error checking
+   selected by the user.  Note: this section is only applicable to
+   application source code, hence the conditional that turns off this
+   stuff when the include file is processed by the FileX source. */
+
+#ifndef FX_SOURCE_CODE
+
+#ifdef FX_DISABLE_ONE_LINE_FUNCTION
+#define fx_file_seek(f, b)                    fx_file_extended_seek(f, (ULONG64)b)
+#define fx_file_allocate(f, s)                fx_file_extended_allocate(f, (ULONG64)s);
+#define fx_file_truncate(f, s)                fx_file_extended_truncate(f, (ULONG64)s);
+#define fx_file_relative_seek(f, b, sf)       fx_file_extended_relative_seek(f, (ULONG64)b, sf);
+#define fx_file_truncate_release(f, s)        fx_file_extended_truncate_release(f, (ULONG64)s);
+#endif /* FX_DISABLE_ONE_LINE_FUNCTION */
+
+/* Determine if error checking is desired.  If so, map API functions
+   to the appropriate error checking front-ends.  Otherwise, map API
+   functions to the core functions that actually perform the work.
+   Note: error checking is enabled by default.  */
+
+#ifdef FX_DISABLE_ERROR_CHECKING
+
+/* Services without error checking.  */
+
+#define fx_directory_attributes_read          _fx_directory_attributes_read
+#define fx_directory_attributes_set           _fx_directory_attributes_set
+#define fx_directory_create                   _fx_directory_create
+#define fx_directory_default_get              _fx_directory_default_get
+#define fx_directory_default_get_copy         _fx_directory_default_get_copy
+#define fx_directory_default_set              _fx_directory_default_set
+#define fx_directory_delete                   _fx_directory_delete
+#define fx_directory_first_entry_find         _fx_directory_first_entry_find
+#define fx_directory_first_full_entry_find    _fx_directory_first_full_entry_find
+#define fx_directory_information_get          _fx_directory_information_get
+#define fx_directory_local_path_clear         _fx_directory_local_path_clear
+#define fx_directory_local_path_get           _fx_directory_local_path_get
+#define fx_directory_local_path_get_copy      _fx_directory_local_path_get_copy
+#define fx_directory_local_path_restore       _fx_directory_local_path_restore
+#define fx_directory_local_path_set           _fx_directory_local_path_set
+#define fx_directory_long_name_get            _fx_directory_long_name_get
+#define fx_directory_long_name_get_extended   _fx_directory_long_name_get_extended
+#define fx_directory_name_test                _fx_directory_name_test
+#define fx_directory_next_entry_find          _fx_directory_next_entry_find
+#define fx_directory_next_full_entry_find     _fx_directory_next_full_entry_find
+#define fx_directory_rename                   _fx_directory_rename
+#define fx_directory_short_name_get           _fx_directory_short_name_get
+#define fx_directory_short_name_get_extended  _fx_directory_short_name_get_extended
+
+#ifndef FX_DISABLE_ONE_LINE_FUNCTION
+#define fx_file_allocate                      _fx_file_allocate
+#endif /* FX_DISABLE_ONE_LINE_FUNCTION */
+#define fx_file_attributes_read               _fx_file_attributes_read
+#define fx_file_attributes_set                _fx_file_attributes_set
+#define fx_file_best_effort_allocate          _fx_file_best_effort_allocate
+#define fx_file_close                         _fx_file_close
+#define fx_file_create                        _fx_file_create
+#define fx_file_date_time_set                 _fx_file_date_time_set
+#define fx_file_delete                        _fx_file_delete
+#define fx_file_open                          _fx_file_open
+#define fx_file_read                          _fx_file_read
+#ifndef FX_DISABLE_ONE_LINE_FUNCTION
+#define fx_file_relative_seek                 _fx_file_relative_seek
+#endif /* FX_DISABLE_ONE_LINE_FUNCTION */
+#define fx_file_rename                        _fx_file_rename
+#ifndef FX_DISABLE_ONE_LINE_FUNCTION
+#define fx_file_seek                          _fx_file_seek
+#define fx_file_truncate                      _fx_file_truncate
+#define fx_file_truncate_release              _fx_file_truncate_release
+#endif /* FX_DISABLE_ONE_LINE_FUNCTION */
+#define fx_file_write                         _fx_file_write
+#define fx_file_write_notify_set              _fx_file_write_notify_set
+#define fx_file_extended_allocate             _fx_file_extended_allocate
+#define fx_file_extended_best_effort_allocate _fx_file_extended_best_effort_allocate
+#define fx_file_extended_relative_seek        _fx_file_extended_relative_seek
+#define fx_file_extended_seek                 _fx_file_extended_seek
+#define fx_file_extended_truncate             _fx_file_extended_truncate
+#define fx_file_extended_truncate_release     _fx_file_extended_truncate_release
+
+#define fx_media_abort                        _fx_media_abort
+#define fx_media_cache_invalidate             _fx_media_cache_invalidate
+#define fx_media_check                        _fx_media_check
+#define fx_media_close                        _fx_media_close
+#define fx_media_flush                        _fx_media_flush
+#define fx_media_format                       _fx_media_format
+#ifdef FX_ENABLE_EXFAT
+#define fx_media_exFAT_format                 _fx_media_exFAT_format
+#endif /* FX_ENABLE_EXFAT */
+#define fx_media_open                         _fx_media_open
+#define fx_media_read                         _fx_media_read
+#define fx_media_space_available              _fx_media_space_available
+#define fx_media_volume_get                   _fx_media_volume_get
+#define fx_media_volume_get_extended          _fx_media_volume_get_extended
+#define fx_media_volume_set                   _fx_media_volume_set
+#define fx_media_write                        _fx_media_write
+#define fx_media_open_notify_set              _fx_media_open_notify_set
+#define fx_media_close_notify_set             _fx_media_close_notify_set
+#define fx_media_extended_space_available     _fx_media_extended_space_available
+
+#define fx_unicode_directory_create           _fx_unicode_directory_create
+#define fx_unicode_directory_rename           _fx_unicode_directory_rename
+#define fx_unicode_file_create                _fx_unicode_file_create
+#define fx_unicode_file_rename                _fx_unicode_file_rename
+#define fx_unicode_length_get                 _fx_unicode_length_get
+#define fx_unicode_length_get_extended        _fx_unicode_length_get_extended
+#define fx_unicode_name_get                   _fx_unicode_name_get
+#define fx_unicode_name_get_extended          _fx_unicode_name_get_extended
+#define fx_unicode_short_name_get             _fx_unicode_short_name_get
+#define fx_unicode_short_name_get_extended    _fx_unicode_short_name_get_extended
+
+#define fx_system_date_get                    _fx_system_date_get
+#define fx_system_date_set                    _fx_system_date_set
+#define fx_system_time_get                    _fx_system_time_get
+#define fx_system_time_set                    _fx_system_time_set
+#define fx_system_initialize                  _fx_system_initialize
+
+#ifdef FX_ENABLE_FAULT_TOLERANT
+#define fx_fault_tolerant_enable              _fx_fault_tolerant_enable
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+
+#else
+
+/* Services with error checking.  */
+
+#define fx_directory_attributes_read          _fxe_directory_attributes_read
+#define fx_directory_attributes_set           _fxe_directory_attributes_set
+#define fx_directory_create                   _fxe_directory_create
+#define fx_directory_default_get              _fxe_directory_default_get
+#define fx_directory_default_get_copy         _fxe_directory_default_get_copy
+#define fx_directory_default_set              _fxe_directory_default_set
+#define fx_directory_delete                   _fxe_directory_delete
+#define fx_directory_first_entry_find         _fxe_directory_first_entry_find
+#define fx_directory_first_full_entry_find    _fxe_directory_first_full_entry_find
+#define fx_directory_information_get          _fxe_directory_information_get
+#define fx_directory_local_path_clear         _fxe_directory_local_path_clear
+#define fx_directory_local_path_get           _fxe_directory_local_path_get
+#define fx_directory_local_path_get_copy      _fxe_directory_local_path_get_copy
+#define fx_directory_local_path_restore       _fxe_directory_local_path_restore
+#define fx_directory_local_path_set(m, l, n)  _fxe_directory_local_path_set(m, l, n, sizeof(FX_LOCAL_PATH))
+#define fx_directory_long_name_get            _fxe_directory_long_name_get
+#define fx_directory_long_name_get_extended   _fxe_directory_long_name_get_extended
+#define fx_directory_name_test                _fxe_directory_name_test
+#define fx_directory_next_entry_find          _fxe_directory_next_entry_find
+#define fx_directory_next_full_entry_find     _fxe_directory_next_full_entry_find
+#define fx_directory_rename                   _fxe_directory_rename
+#define fx_directory_short_name_get           _fxe_directory_short_name_get
+#define fx_directory_short_name_get_extended  _fxe_directory_short_name_get_extended
+
+#ifndef FX_DISABLE_ONE_LINE_FUNCTION
+#define fx_file_allocate                      _fxe_file_allocate
+#endif  /* FX_DISABLE_ONE_LINE_FUNCTION */
+#define fx_file_attributes_read               _fxe_file_attributes_read
+#define fx_file_attributes_set                _fxe_file_attributes_set
+#define fx_file_best_effort_allocate          _fxe_file_best_effort_allocate
+#define fx_file_close                         _fxe_file_close
+#define fx_file_create                        _fxe_file_create
+#define fx_file_date_time_set                 _fxe_file_date_time_set
+#define fx_file_delete                        _fxe_file_delete
+#define fx_file_open(m, f, n, t)              _fxe_file_open(m, f, n, t, sizeof(FX_FILE))
+#define fx_file_read                          _fxe_file_read
+#ifndef FX_DISABLE_ONE_LINE_FUNCTION
+#define fx_file_relative_seek                 _fxe_file_relative_seek
+#endif /* FX_DISABLE_ONE_LINE_FUNCTION */
+#define fx_file_rename                        _fxe_file_rename
+#ifndef FX_DISABLE_ONE_LINE_FUNCTION
+#define fx_file_seek                          _fxe_file_seek
+#define fx_file_truncate                      _fxe_file_truncate
+#define fx_file_truncate_release              _fxe_file_truncate_release
+#endif /* FX_DISABLE_ONE_LINE_FUNCTION */
+#define fx_file_write                         _fxe_file_write
+#define fx_file_write_notify_set              _fxe_file_write_notify_set
+#define fx_file_extended_allocate             _fxe_file_extended_allocate
+#define fx_file_extended_best_effort_allocate _fxe_file_extended_best_effort_allocate
+#define fx_file_extended_relative_seek        _fxe_file_extended_relative_seek
+#define fx_file_extended_seek                 _fxe_file_extended_seek
+#define fx_file_extended_truncate             _fxe_file_extended_truncate
+#define fx_file_extended_truncate_release     _fxe_file_extended_truncate_release
+
+#define fx_media_abort                        _fxe_media_abort
+#define fx_media_cache_invalidate             _fxe_media_cache_invalidate
+#define fx_media_check                        _fxe_media_check
+#define fx_media_close                        _fxe_media_close
+#define fx_media_flush                        _fxe_media_flush
+#define fx_media_format                       _fxe_media_format
+#ifdef FX_ENABLE_EXFAT
+#define fx_media_exFAT_format                 _fxe_media_exFAT_format
+#endif /* FX_ENABLE_EXFAT */
+#define fx_media_open(m, n, d, i, p, s)       _fxe_media_open(m, n, d, i, p, s, sizeof(FX_MEDIA))
+#define fx_media_read                         _fxe_media_read
+#define fx_media_space_available              _fxe_media_space_available
+#define fx_media_volume_get                   _fxe_media_volume_get
+#define fx_media_volume_get_extended          _fxe_media_volume_get_extended
+#define fx_media_volume_set                   _fxe_media_volume_set
+#define fx_media_write                        _fxe_media_write
+#define fx_media_open_notify_set              _fxe_media_open_notify_set
+#define fx_media_close_notify_set             _fxe_media_close_notify_set
+#define fx_media_extended_space_available     _fxe_media_extended_space_available
+
+#define fx_unicode_directory_create           _fxe_unicode_directory_create
+#define fx_unicode_directory_rename           _fxe_unicode_directory_rename
+#define fx_unicode_file_create                _fxe_unicode_file_create
+#define fx_unicode_file_rename                _fxe_unicode_file_rename
+#define fx_unicode_length_get                 _fx_unicode_length_get
+#define fx_unicode_length_get_extended        _fx_unicode_length_get_extended
+#define fx_unicode_name_get                   _fxe_unicode_name_get
+#define fx_unicode_name_get_extended          _fxe_unicode_name_get_extended
+#define fx_unicode_short_name_get             _fxe_unicode_short_name_get
+#define fx_unicode_short_name_get_extended    _fxe_unicode_short_name_get_extended
+
+#define fx_system_date_get                    _fxe_system_date_get
+#define fx_system_date_set                    _fxe_system_date_set
+#define fx_system_time_get                    _fxe_system_time_get
+#define fx_system_time_set                    _fxe_system_time_set
+#define fx_system_initialize                  _fx_system_initialize
+
+#ifdef FX_ENABLE_FAULT_TOLERANT
+#define fx_fault_tolerant_enable              _fxe_fault_tolerant_enable
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+
+#endif
+
+/* Define the function prototypes of the FileX API.  */
+
+UINT fx_directory_attributes_read(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes_ptr);
+UINT fx_directory_attributes_set(FX_MEDIA *media_ptr, CHAR *directory_name, UINT attributes);
+UINT fx_directory_create(FX_MEDIA *media_ptr, CHAR *directory_name);
+UINT fx_directory_default_get(FX_MEDIA *media_ptr, CHAR **return_path_name);
+UINT fx_directory_default_get_copy(FX_MEDIA *media_ptr, CHAR *return_path_name_buffer, UINT return_path_name_buffer_size);
+UINT fx_directory_default_set(FX_MEDIA *media_ptr, CHAR *new_path_name);
+UINT fx_directory_delete(FX_MEDIA *media_ptr, CHAR *directory_name);
+UINT fx_directory_first_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name);
+UINT fx_directory_first_full_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes,
+                                        ULONG *size, UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second);
+UINT fx_directory_information_get(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes, ULONG *size,
+                                  UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second);
+UINT fx_directory_local_path_clear(FX_MEDIA *media_ptr);
+UINT fx_directory_local_path_get(FX_MEDIA *media_ptr, CHAR **return_path_name);
+UINT fx_directory_local_path_get_copy(FX_MEDIA *media_ptr, CHAR *return_path_name_buffer, UINT return_path_name_buffer_size);
+UINT fx_directory_local_path_restore(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr);
+#ifdef FX_DISABLE_ERROR_CHECKING
+UINT _fx_directory_local_path_set(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr, CHAR *new_path_name);
+#else
+UINT _fxe_directory_local_path_set(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr, CHAR *new_path_name, UINT local_path_control_block_size);
+#endif
+UINT fx_directory_long_name_get(FX_MEDIA *media_ptr, CHAR *short_file_name, CHAR *long_file_name);
+UINT fx_directory_long_name_get_extended(FX_MEDIA* media_ptr, CHAR* short_file_name, CHAR* long_file_name, UINT long_file_name_buffer_length);
+UINT fx_directory_name_test(FX_MEDIA *media_ptr, CHAR *directory_name);
+UINT fx_directory_next_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name);
+UINT fx_directory_next_full_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes,
+                                       ULONG *size, UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second);
+UINT fx_directory_rename(FX_MEDIA *media_ptr, CHAR *old_directory_name, CHAR *new_directory_name);
+UINT fx_directory_short_name_get(FX_MEDIA *media_ptr, CHAR *long_file_name, CHAR *short_file_name);
+UINT fx_directory_short_name_get_extended(FX_MEDIA* media_ptr, CHAR* long_file_name, CHAR* short_file_name, UINT short_file_name_length);
+
+#ifndef FX_DISABLE_ONE_LINE_FUNCTION
+UINT fx_file_allocate(FX_FILE *file_ptr, ULONG size);
+#endif /* FX_DISABLE_ONE_LINE_FUNCTION*/
+UINT fx_file_attributes_read(FX_MEDIA *media_ptr, CHAR *file_name, UINT *attributes_ptr);
+UINT fx_file_attributes_set(FX_MEDIA *media_ptr, CHAR *file_name, UINT attributes);
+UINT fx_file_best_effort_allocate(FX_FILE *file_ptr, ULONG size, ULONG *actual_size_allocated);
+UINT fx_file_close(FX_FILE *file_ptr);
+UINT fx_file_create(FX_MEDIA *media_ptr, CHAR *file_name);
+UINT fx_file_date_time_set(FX_MEDIA *media_ptr, CHAR *file_name,
+                           UINT year, UINT month, UINT day, UINT hour, UINT minute, UINT second);
+UINT fx_file_delete(FX_MEDIA *media_ptr, CHAR *file_name);
+#ifdef FX_DISABLE_ERROR_CHECKING
+UINT _fx_file_open(FX_MEDIA *media_ptr, FX_FILE *file_ptr, CHAR *file_name,
+                   UINT open_type);
+#else
+UINT _fxe_file_open(FX_MEDIA *media_ptr, FX_FILE *file_ptr, CHAR *file_name,
+                    UINT open_type, UINT file_control_block_size);
+#endif
+UINT fx_file_read(FX_FILE *file_ptr, VOID *buffer_ptr, ULONG request_size, ULONG *actual_size);
+#ifndef FX_DISABLE_ONE_LINE_FUNCTION
+UINT fx_file_relative_seek(FX_FILE *file_ptr, ULONG byte_offset, UINT seek_from);
+#endif /* FX_DISABLE_ONE_LINE_FUNCTION */
+UINT fx_file_rename(FX_MEDIA *media_ptr, CHAR *old_file_name, CHAR *new_file_name);
+#ifndef FX_DISABLE_ONE_LINE_FUNCTION
+UINT fx_file_seek(FX_FILE *file_ptr, ULONG byte_offset);
+UINT fx_file_truncate(FX_FILE *file_ptr, ULONG size);
+UINT fx_file_truncate_release(FX_FILE *file_ptr, ULONG size);
+#endif /* FX_DISABLE_ONE_LINE_FUNCTION */
+UINT fx_file_write(FX_FILE *file_ptr, VOID *buffer_ptr, ULONG size);
+UINT fx_file_write_notify_set(FX_FILE *file_ptr, VOID (*file_write_notify)(FX_FILE *));
+UINT fx_file_extended_allocate(FX_FILE *file_ptr, ULONG64 size);
+UINT fx_file_extended_best_effort_allocate(FX_FILE *file_ptr, ULONG64 size, ULONG64 *actual_size_allocated);
+UINT fx_file_extended_relative_seek(FX_FILE *file_ptr, ULONG64 byte_offset, UINT seek_from);
+UINT fx_file_extended_seek(FX_FILE *file_ptr, ULONG64 byte_offset);
+UINT fx_file_extended_truncate(FX_FILE *file_ptr, ULONG64 size);
+UINT fx_file_extended_truncate_release(FX_FILE *file_ptr, ULONG64 size);
+
+UINT fx_media_abort(FX_MEDIA *media_ptr);
+UINT fx_media_cache_invalidate(FX_MEDIA *media_ptr);
+UINT fx_media_check(FX_MEDIA *media_ptr, UCHAR *scratch_memory_ptr, ULONG scratch_memory_size, ULONG error_correction_option, ULONG *errors_detected);
+UINT fx_media_close(FX_MEDIA *media_ptr);
+UINT fx_media_flush(FX_MEDIA *media_ptr);
+UINT fx_media_format(FX_MEDIA *media_ptr, VOID (*driver)(FX_MEDIA *media), VOID *driver_info_ptr, UCHAR *memory_ptr, UINT memory_size,
+                     CHAR *volume_name, UINT number_of_fats, UINT directory_entries, UINT hidden_sectors,
+                     ULONG total_sectors, UINT bytes_per_sector, UINT sectors_per_cluster,
+                     UINT heads, UINT sectors_per_track);
+#ifdef FX_ENABLE_EXFAT
+UINT fx_media_exFAT_format(FX_MEDIA *media_ptr, VOID (*driver)(FX_MEDIA *media), VOID *driver_info_ptr, UCHAR *memory_ptr, UINT memory_size,
+                           CHAR *volume_name, UINT number_of_fats, ULONG64 hidden_sectors, ULONG64 total_sectors,
+                           UINT bytes_per_sector, UINT sectors_per_cluster, UINT volume_serial_number, UINT boundary_unit);
+#endif /* FX_ENABLE_EXFAT */
+#ifdef FX_DISABLE_ERROR_CHECKING
+UINT _fx_media_open(FX_MEDIA *media_ptr, CHAR *media_name,
+                    VOID (*media_driver)(FX_MEDIA *), VOID *driver_info_ptr,
+                    VOID *memory_ptr, ULONG memory_size);
+#else
+UINT _fxe_media_open(FX_MEDIA *media_ptr, CHAR *media_name,
+                     VOID (*media_driver)(FX_MEDIA *), VOID *driver_info_ptr,
+                     VOID *memory_ptr, ULONG memory_size, UINT media_control_block_size);
+#endif
+UINT fx_media_read(FX_MEDIA *media_ptr, ULONG logical_sector, VOID *buffer_ptr);
+UINT fx_media_space_available(FX_MEDIA *media_ptr, ULONG *available_bytes_ptr);
+UINT fx_media_volume_get(FX_MEDIA *media_ptr, CHAR *volume_name, UINT volume_source);
+UINT fx_media_volume_get_extended(FX_MEDIA *media_ptr, CHAR *volume_name, UINT volume_name_buffer_length, UINT volume_source);
+UINT fx_media_volume_set(FX_MEDIA *media_ptr, CHAR *volume_name);
+UINT fx_media_write(FX_MEDIA *media_ptr, ULONG logical_sector, VOID *buffer_ptr);
+UINT fx_media_open_notify_set(FX_MEDIA *media_ptr, VOID (*media_open_notify)(FX_MEDIA *));
+UINT fx_media_close_notify_set(FX_MEDIA *media_ptr, VOID (*media_close_notify)(FX_MEDIA *));
+UINT fx_media_extended_space_available(FX_MEDIA *media_ptr, ULONG64 *available_bytes_ptr);
+
+UINT fx_system_date_get(UINT *year, UINT *month, UINT *day);
+UINT fx_system_date_set(UINT year, UINT month, UINT day);
+VOID fx_system_initialize(VOID);
+UINT fx_system_time_get(UINT *hour, UINT *minute, UINT *second);
+UINT fx_system_time_set(UINT hour, UINT minute, UINT second);
+
+UINT fx_unicode_directory_create(FX_MEDIA *media_ptr,
+                                 UCHAR *source_unicode_name, ULONG source_unicode_length,
+                                 CHAR *short_name);
+UINT fx_unicode_directory_rename(FX_MEDIA *media_ptr,
+                                 UCHAR *old_unicode_name, ULONG old_unicode_length,
+                                 UCHAR *new_unicode_name, ULONG new_unicode_length,
+                                 CHAR *new_short_name);
+UINT fx_unicode_file_create(FX_MEDIA *media_ptr,
+                            UCHAR *source_unicode_name, ULONG source_unicode_length,
+                            CHAR *short_name);
+UINT  fx_unicode_file_rename(FX_MEDIA *media_ptr, UCHAR *old_unicode_name, ULONG old_unicode_length,
+                             UCHAR *new_unicode_name, ULONG new_unicode_length, CHAR *new_short_name);
+ULONG fx_unicode_length_get(UCHAR *unicode_name);
+ULONG fx_unicode_length_get_extended(UCHAR *unicode_name, UINT buffer_length);
+UINT  fx_unicode_name_get(FX_MEDIA *media_ptr, CHAR *source_short_name,
+                          UCHAR *destination_unicode_name, ULONG *destination_unicode_length);
+UINT  fx_unicode_name_get_extended(FX_MEDIA *media_ptr, CHAR *source_short_name,
+                          UCHAR *destination_unicode_name, ULONG *destination_unicode_length, ULONG unicode_name_buffer_length);
+UINT  fx_unicode_short_name_get(FX_MEDIA *media_ptr,
+                                UCHAR *source_unicode_name, ULONG source_unicode_length,
+                                CHAR *destination_short_name);
+UINT  fx_unicode_short_name_get_extended(FX_MEDIA *media_ptr,
+                                UCHAR *source_unicode_name, ULONG source_unicode_length,
+                                CHAR *destination_short_name, ULONG short_name_buffer_length);
+
+#ifdef FX_ENABLE_FAULT_TOLERANT
+UINT fx_fault_tolerant_enable(FX_MEDIA *media_ptr, VOID *memory_buffer, UINT memory_size);
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+
+
+/* Define prototype for utility services commonly used by FileX I/O Drivers.  This eliminates the
+   need to include internal FileX component files in I/O drivers.  */
+
+UINT    _fx_utility_16_unsigned_read(UCHAR *source_ptr);
+VOID    _fx_utility_16_unsigned_write(UCHAR *dest_ptr, UINT value);
+ULONG   _fx_utility_32_unsigned_read(UCHAR *source_ptr);
+VOID    _fx_utility_32_unsigned_write(UCHAR *dest_ptr, ULONG value);
+ULONG64 _fx_utility_64_unsigned_read(UCHAR *source_ptr);
+VOID    _fx_utility_64_unsigned_write(UCHAR *dest_ptr, ULONG64 value);
+VOID    _fx_utility_memory_copy(UCHAR *source_ptr, UCHAR *dest_ptr, ULONG size);
+VOID    _fx_utility_memory_set(UCHAR *dest_ptr, UCHAR value, ULONG size);
+
+#endif
+
+/* Determine if a C++ compiler is being used.  If so, complete the standard
+   C conditional started above.  */
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_media_boot_info_extract.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_media_boot_info_extract.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_media_boot_info_extract.c	(revision 54)
@@ -0,0 +1,331 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Media                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+#include "fx_media.h"
+#include "fx_utility.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_media_boot_info_extract                         PORTABLE C      */
+/*                                                           6.1.10       */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function extracts and validates the information from the boot  */
+/*    record found in the memory buffer.  If the boot record is invalid,  */
+/*    an FX_MEDIA_INVALID status is returned to the caller.               */
+/*                                                                        */
+/*    The FAT boot sector (512 bytes) that is operated on by this         */
+/*    function must look like the following:                              */
+/*                                                                        */
+/*          Byte Offset         Meaning             Size                  */
+/*                                                                        */
+/*            0x000         Jump Instructions        3                    */
+/*            0x003         OEM Name                 8                    */
+/*            0x00B        *Bytes per Sector         2                    */
+/*            0x00D        *Sectors per Cluster      1                    */
+/*            0x00E        *Reserved Sectors         2                    */
+/*            0x010        *Number of FATs           1                    */
+/*            0x011        *Max Root Dir Entries     2                    */
+/*            0x013        *Number of Sectors        2                    */
+/*            0x015         Media Type               1                    */
+/*            0x016        *Sectors per FAT          2                    */
+/*            0x018        *Sectors per Track        2                    */
+/*            0x01A        *Number of Heads          2                    */
+/*            0x01C        *Hidden Sectors           4                    */
+/*            0x020        *Huge Sectors             4                    */
+/*            0x024         Drive Number             1                    */
+/*            0x025         Reserved                 1                    */
+/*            0x026         Boot Signature           1                    */
+/*            0x027         Volume ID                4                    */
+/*            0x02B         Volume Label             11                   */
+/*            0x036         File System Type         8                    */
+/*             ...              ...                 ...                   */
+/*            0x1FE       **Signature (0x55aa)       2                    */
+/*                                                                        */
+/*            * Denotes which elements of the boot record                 */
+/*              FileX uses.                                               */
+/*                                                                        */
+/*            **Denotes the element is checked by the I/O                 */
+/*              driver.  This eliminates the need for a minimum           */
+/*              512-byte buffer for FileX.                                */
+/*                                                                        */
+/*  Note: All values above are in little endian format, i.e. the LSB is   */
+/*        in the lowest address.                                          */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    media_ptr                             Media control block pointer   */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    return status                                                       */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _fx_utility_16_unsigned_read          Read a UINT from buffer       */
+/*    _fx_utility_32_unsigned_read          Read a ULONG from buffer      */
+/*    _fx_utility_64_unsigned_read          Read a ULONG64 from memory    */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _fx_media_open                        Media open function           */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*  01-31-2022     Bhupendra Naphade        Modified comment(s), added    */
+/*                                            check for bimap cache size, */
+/*                                            resulting in version 6.1.10 */
+/*                                                                        */
+/**************************************************************************/
+UINT  _fx_media_boot_info_extract(FX_MEDIA *media_ptr)
+{
+
+UCHAR *boot_sector;
+
+
+    /* Move the buffer pointer into a local copy.  */
+    boot_sector =  media_ptr -> fx_media_driver_buffer;
+
+    /* Extract the number of bytes per sector.  */
+    media_ptr -> fx_media_bytes_per_sector =    _fx_utility_16_unsigned_read(&boot_sector[FX_BYTES_SECTOR]);
+    if (media_ptr -> fx_media_bytes_per_sector == 0)
+#ifdef FX_ENABLE_EXFAT
+    {
+        /* Treat as exFAT volume.  */
+        /* Extract the number of bytes per sector.  */
+        media_ptr -> fx_media_exfat_bytes_per_sector_shift = boot_sector[FX_EF_BYTE_PER_SECTOR_SHIFT];
+
+        /* exFAT requires minimal value 9 (512 bytes) and maximum value 12(4096 bytes) for bytes_per_sector_shift */
+        if((media_ptr -> fx_media_exfat_bytes_per_sector_shift < 9) || (media_ptr -> fx_media_exfat_bytes_per_sector_shift > 12))
+        {
+            return(FX_MEDIA_INVALID);
+        }
+
+        media_ptr -> fx_media_bytes_per_sector = (UINT)(1 << media_ptr -> fx_media_exfat_bytes_per_sector_shift);
+
+        /* Validate bytes per sector value: no more than bitmap cache size */
+        if (media_ptr -> fx_media_bytes_per_sector > sizeof(media_ptr -> fx_media_exfat_bitmap_cache))
+        {
+            return(FX_NOT_ENOUGH_MEMORY);
+        }
+
+        media_ptr -> fx_media_total_sectors = _fx_utility_64_unsigned_read(&boot_sector[FX_EF_VOLUME_LENGTH]);
+        if (media_ptr -> fx_media_total_sectors == 0)
+        {
+            return(FX_MEDIA_INVALID);
+        }
+
+        media_ptr -> fx_media_reserved_sectors = _fx_utility_32_unsigned_read(&boot_sector[FX_EF_FAT_OFFSET]);
+        if (media_ptr -> fx_media_reserved_sectors == 0)
+        {
+            return(FX_MEDIA_INVALID);
+        }
+
+        media_ptr -> fx_media_sectors_per_FAT = _fx_utility_32_unsigned_read(&boot_sector[FX_EF_FAT_LENGTH]);
+        if (media_ptr -> fx_media_sectors_per_FAT == 0)
+        {
+            return(FX_MEDIA_INVALID);
+        }
+
+        media_ptr -> fx_media_data_sector_start = _fx_utility_32_unsigned_read(&boot_sector[FX_EF_CLUSTER_HEAP_OFFSET]);
+        if (media_ptr -> fx_media_data_sector_start == 0)
+        {
+            return(FX_MEDIA_INVALID);
+        }
+
+        media_ptr -> fx_media_total_clusters = _fx_utility_32_unsigned_read(&boot_sector[FX_EF_CLUSTER_COUNT]);
+        if (media_ptr -> fx_media_total_clusters == 0)
+        {
+            return(FX_MEDIA_INVALID);
+        }
+
+        media_ptr -> fx_media_exfat_sector_per_clusters_shift = boot_sector[FX_EF_SECTOR_PER_CLUSTER_SHIFT];
+        if (media_ptr -> fx_media_exfat_sector_per_clusters_shift > 25 - media_ptr -> fx_media_exfat_bytes_per_sector_shift)
+        {
+            return(FX_MEDIA_INVALID);
+        }
+        media_ptr -> fx_media_sectors_per_cluster = (UINT)(1 << media_ptr -> fx_media_exfat_sector_per_clusters_shift);
+
+        media_ptr -> fx_media_number_of_FATs = boot_sector[FX_EF_NUMBER_OF_FATS];
+        if (media_ptr -> fx_media_number_of_FATs == 0)
+        {
+            return(FX_MEDIA_INVALID);
+        }
+
+        media_ptr -> fx_media_root_cluster_32    = _fx_utility_32_unsigned_read(&boot_sector[FX_EF_FIRST_CLUSTER_OF_ROOT_DIR]);
+        /* Root cluster starts from at least FX_FAT_ENTRY_START (2), or higher. */
+        if (media_ptr -> fx_media_root_cluster_32 < FX_FAT_ENTRY_START)
+        {
+            return(FX_MEDIA_INVALID);
+        }
+
+        /* Overflow check. */
+        if (((ULONG64)media_ptr -> fx_media_data_sector_start +
+             (ULONG64)(media_ptr -> fx_media_root_cluster_32 - FX_FAT_ENTRY_START) *
+             media_ptr -> fx_media_sectors_per_cluster) > 0xFFFFFFFF)
+        {
+
+            /* Return the invalid media error status.  */
+            return(FX_MEDIA_INVALID);
+        }
+
+        /* Calculate logical number of root dir sector.  */
+        media_ptr -> fx_media_root_sector_start = media_ptr -> fx_media_data_sector_start +
+            (media_ptr -> fx_media_root_cluster_32 - FX_FAT_ENTRY_START) *
+            media_ptr -> fx_media_sectors_per_cluster;
+
+        media_ptr -> fx_media_exfat_volume_serial_number  = _fx_utility_32_unsigned_read(&boot_sector[FX_EF_VOLUME_SERIAL_NUMBER]);
+
+        media_ptr -> fx_media_exfat_file_system_revision  = _fx_utility_16_unsigned_read(&boot_sector[FX_EF_FILE_SYSTEM_REVISION]);
+
+        media_ptr -> fx_media_exfat_volume_flag          = _fx_utility_16_unsigned_read(&boot_sector[FX_EF_VOLUME_FLAGS]);
+
+        media_ptr -> fx_media_number_of_FATs = boot_sector[FX_EF_NUMBER_OF_FATS];
+
+        if (0 == media_ptr -> fx_media_number_of_FATs)
+        {
+            return(FX_MEDIA_INVALID);
+        }
+
+        /* Extract the number of hidden sectors.  */
+#ifdef FX_DRIVER_USE_64BIT_LBA
+        media_ptr -> fx_media_hidden_sectors =      _fx_utility_64_unsigned_read(&boot_sector[FX_EF_PARTITION_OFFSET]);
+#else
+        media_ptr -> fx_media_hidden_sectors =      _fx_utility_32_unsigned_read(&boot_sector[FX_EF_PARTITION_OFFSET]);
+#endif
+
+        media_ptr -> fx_media_exfat_drive_select       = boot_sector[FX_EF_DRIVE_SELECT];
+        media_ptr -> fx_media_exfat_percent_in_use       = boot_sector[FX_EF_PERCENT_IN_USE];
+
+        media_ptr -> fx_media_12_bit_FAT = FX_FALSE;
+        media_ptr -> fx_media_32_bit_FAT = FX_FALSE;
+
+        /* Legacy code support:
+           We will use fx_media_FAT_type for determine FAT type instead of
+           fx_media_12_bit_FAT and fx_media_32_bit_FAT.  */
+        media_ptr -> fx_media_12_bit_FAT = FX_FALSE;
+        media_ptr -> fx_media_32_bit_FAT = FX_FALSE;
+
+        media_ptr -> fx_media_FAT_type = FX_exFAT;
+
+        return(_fx_utility_exFAT_geometry_check(media_ptr, boot_sector));
+    }
+    else
+    {
+#else
+        return(FX_MEDIA_INVALID);
+#endif /* FX_ENABLE_EXFAT */
+
+
+        /* FAT12/16/32 volume.  */
+        /* Extract the number of sectors per track.  */
+        media_ptr -> fx_media_sectors_per_track =   _fx_utility_16_unsigned_read(&boot_sector[FX_SECTORS_PER_TRK]);
+
+        /* Extract the number of heads.  */
+        media_ptr -> fx_media_heads =               _fx_utility_16_unsigned_read(&boot_sector[FX_HEADS]);
+
+        /* Extract the total number of sectors.  */
+        media_ptr -> fx_media_total_sectors =       _fx_utility_16_unsigned_read(&boot_sector[FX_SECTORS]);
+        if (media_ptr -> fx_media_total_sectors == 0)
+        {
+            media_ptr -> fx_media_total_sectors = _fx_utility_32_unsigned_read(&boot_sector[FX_HUGE_SECTORS]);
+        }
+
+        if (media_ptr -> fx_media_total_sectors == 0)
+        {
+            return(FX_MEDIA_INVALID);
+        }
+
+        /* Extract the number of reserved sectors before the first FAT.  */
+        media_ptr -> fx_media_reserved_sectors =    _fx_utility_16_unsigned_read(&boot_sector[FX_RESERVED_SECTORS]);
+        if (media_ptr -> fx_media_reserved_sectors == 0)
+        {
+            return(FX_MEDIA_INVALID);
+        }
+
+        /* Extract the number of sectors per cluster.  */
+        media_ptr -> fx_media_sectors_per_cluster = ((UINT)boot_sector[FX_SECTORS_CLUSTER] & 0xFF);
+
+        /* There should always be at least one reserved sector, representing the boot record itself.  */
+        if (media_ptr -> fx_media_sectors_per_cluster == 0)
+        {
+            return(FX_MEDIA_INVALID);
+        }
+
+        /* Extract the number of sectors per FAT.  */
+        media_ptr -> fx_media_sectors_per_FAT =     _fx_utility_16_unsigned_read(&boot_sector[FX_SECTORS_PER_FAT]);
+        if (media_ptr -> fx_media_sectors_per_FAT == 0)
+        {
+            media_ptr -> fx_media_sectors_per_FAT = _fx_utility_32_unsigned_read(&boot_sector[FX_SECTORS_PER_FAT_32]);
+        }
+
+        if (media_ptr -> fx_media_sectors_per_FAT == 0)
+        {
+            return(FX_MEDIA_INVALID);
+        }
+
+        /* Extract the number of FATs.  */
+        media_ptr -> fx_media_number_of_FATs =      ((UINT)boot_sector[FX_NUMBER_OF_FATS] & 0xFF);
+        if (media_ptr -> fx_media_number_of_FATs == 0)
+        {
+            return(FX_BOOT_ERROR);
+        }
+
+        /* Extract the number of hidden sectors.  */
+#ifdef FX_DRIVER_USE_64BIT_LBA
+        media_ptr -> fx_media_hidden_sectors =      _fx_utility_64_unsigned_read(&boot_sector[FX_HIDDEN_SECTORS]);
+#else
+        media_ptr -> fx_media_hidden_sectors =      _fx_utility_32_unsigned_read(&boot_sector[FX_HIDDEN_SECTORS]);
+#endif
+        /* Extract the number of root directory entries.  */
+        media_ptr -> fx_media_root_directory_entries =  _fx_utility_16_unsigned_read(&boot_sector[FX_ROOT_DIR_ENTRIES]);
+
+        /* Extract root directory starting cluster (32 bit only) and compute start sector */
+        media_ptr -> fx_media_root_cluster_32 = _fx_utility_32_unsigned_read(&boot_sector[FX_ROOT_CLUSTER_32]);
+
+#ifdef FX_ENABLE_EXFAT
+    }
+#endif /* FX_ENABLE_EXFAT */
+
+    /* Return a successful status.  */
+    return(FX_SUCCESS);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_media_open.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_media_open.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_media_open.c	(revision 54)
@@ -0,0 +1,1064 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Media                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+#include "fx_media.h"
+#include "fx_utility.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_media_open                                      PORTABLE C      */
+/*                                                           6.2.0        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function opens the specified media with the supplied device    */
+/*    driver.  The specified media must conform to the FAT compatible     */
+/*    file format, which is verified during the media open process.  In   */
+/*    addition, the supplied FileX device driver must also conform to     */
+/*    the FileX device driver specification.                              */
+/*                                                                        */
+/*    The FAT boot sector (512 bytes) that is verified by this            */
+/*    function must look like the following:                              */
+/*                                                                        */
+/*          Byte Offset         Meaning             Size                  */
+/*                                                                        */
+/*            0x000         Jump Instructions        3                    */
+/*            0x003         OEM Name                 8                    */
+/*            0x00B        *Bytes per Sector         2                    */
+/*            0x00D        *Sectors per Cluster      1                    */
+/*            0x00E        *Reserved Sectors         2                    */
+/*            0x010        *Number of FATs           1                    */
+/*            0x011        *Max Root Dir Entries     2                    */
+/*            0x013        *Number of Sectors        2                    */
+/*            0x015         Media Type               1                    */
+/*            0x016        *Sectors per FAT          2                    */
+/*            0x018        *Sectors per Track        2                    */
+/*            0x01A        *Number of Heads          2                    */
+/*            0x01C        *Hidden Sectors           4                    */
+/*            0x020        *Huge Sectors             4                    */
+/*            0x024         Drive Number             1                    */
+/*            0x025         Reserved                 1                    */
+/*            0x026         Boot Signature           1                    */
+/*            0x027         Volume ID                4                    */
+/*            0x02B         Volume Label             11                   */
+/*            0x036         File System Type         8                    */
+/*             ...              ...                 ...                   */
+/*            0x1FE       **Signature (0x55aa)       2                    */
+/*                                                                        */
+/*            * Denotes which elements of the boot record                 */
+/*              FileX uses.                                               */
+/*                                                                        */
+/*            **Denotes the element is checked by the I/O                 */
+/*              driver.  This eliminates the need for a minimum           */
+/*              512-byte buffer for FileX.                                */
+/*                                                                        */
+/*  Note: All values above are in little endian format, i.e. the LSB is   */
+/*        in the lowest address.                                          */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    media_ptr                             Media control block pointer   */
+/*    media_name                            Pointer to media name string  */
+/*    media_driver                          Media driver entry function   */
+/*    driver_info_ptr                       Optional information pointer  */
+/*                                            supplied to media driver    */
+/*    memory_ptr                            Pointer to memory used by the */
+/*                                            FileX for this media.       */
+/*    memory_size                           Size of media memory - must   */
+/*                                            at least 512 bytes and      */
+/*                                            one sector size.            */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    return status                                                       */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    I/O Driver                                                          */
+/*    _fx_utility_exFAT_bitmap_initialize   Initialize exFAT bitmap       */
+/*    _fx_utility_16_unsigned_read          Read 16-bit unsigned value    */
+/*    _fx_utility_32_unsigned_read          Read 32-bit unsigned value    */
+/*    _fx_utility_logical_sector_flush      Invalidate log sector cache   */
+/*    _fx_media_boot_info_extract           Extract media information     */
+/*    _fx_utility_FAT_entry_read            Pickup FAT entry contents     */
+/*    tx_mutex_create                       Create protection mutex       */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s), and      */
+/*                                            added conditional to        */
+/*                                            disable force memset,       */
+/*                                            build options and cache,    */
+/*                                            resulting in version 6.1    */
+/*  01-31-2022     William E. Lamie         Modified comment(s), fixed    */
+/*                                            errors without cache,       */
+/*                                            resulting in version 6.1.10 */
+/*  10-31-2022     Tiejun Zhou              Modified comment(s),          */
+/*                                            fixed memory buffer when    */
+/*                                            cache is disabled,          */
+/*                                            resulting in version 6.2.0  */
+/*                                                                        */
+/**************************************************************************/
+UINT  _fx_media_open(FX_MEDIA *media_ptr, CHAR *media_name,
+                     VOID (*media_driver)(FX_MEDIA *), VOID *driver_info_ptr,
+                     VOID *memory_ptr, ULONG memory_size)
+{
+
+FX_MEDIA_PTR      tail_ptr;
+ULONG             cluster_number;
+ULONG             FAT_entry, FAT_sector, FAT_read_sectors;
+ULONG             i, j;
+#ifndef FX_DISABLE_CACHE
+FX_CACHED_SECTOR *cache_entry_ptr;
+#endif /* FX_DISABLE_CACHE */
+UINT              status;
+UINT              additional_info_sector;
+UCHAR            *original_memory_ptr;
+ULONG             bytes_in_buffer;
+FX_INT_SAVE_AREA
+
+
+#ifndef FX_DISABLE_BUILD_OPTIONS
+    /* Reference the version ID and option words to ensure they are linked in.  */
+    if ((_fx_system_build_options_1 | _fx_system_build_options_2 | _fx_system_build_options_3) == 0 ||
+        _fx_version_id[0] == 0)
+    {
+
+        /* We should never get here!  */
+        return(FX_NOT_IMPLEMENTED);
+    }
+#endif /* FX_DISABLE_BUILD_OPTIONS */
+
+#ifdef FX_DISABLE_FORCE_MEMORY_OPERATION
+    _fx_utility_memory_set((UCHAR *)media_ptr, 0, sizeof(FX_MEDIA));
+#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */
+#ifdef FX_DISABLE_CACHE
+    media_ptr -> fx_media_memory_buffer_sector = (ULONG64)-1;
+#endif /* FX_DISABLE_CACHE */
+
+    /* Save the basic information in the media control block.  */
+    media_ptr -> fx_media_name =                        media_name;
+    media_ptr -> fx_media_driver_entry =                media_driver;
+    media_ptr -> fx_media_memory_buffer =               (UCHAR *)memory_ptr;
+    media_ptr -> fx_media_memory_size =                 memory_size;
+#ifndef FX_DISABLE_FORCE_MEMORY_OPERATION
+    media_ptr -> fx_media_disable_burst_cache =         FX_FALSE;
+    media_ptr -> fx_media_FAT_type =                    0;
+#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */
+
+    /* Save the original memory pointer.  */
+    original_memory_ptr =  (UCHAR *)memory_ptr;
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+    /* Clear the optional media statistics.  */
+    media_ptr -> fx_media_directory_attributes_reads =  0;
+    media_ptr -> fx_media_directory_attributes_sets =  0;
+    media_ptr -> fx_media_directory_creates =  0;
+    media_ptr -> fx_media_directory_default_gets =  0;
+    media_ptr -> fx_media_directory_default_sets =  0;
+    media_ptr -> fx_media_directory_deletes =  0;
+    media_ptr -> fx_media_directory_first_entry_finds =  0;
+    media_ptr -> fx_media_directory_first_full_entry_finds =  0;
+    media_ptr -> fx_media_directory_information_gets =  0;
+    media_ptr -> fx_media_directory_local_path_clears =  0;
+    media_ptr -> fx_media_directory_local_path_gets =  0;
+    media_ptr -> fx_media_directory_local_path_restores =  0;
+    media_ptr -> fx_media_directory_local_path_sets =  0;
+    media_ptr -> fx_media_directory_name_tests =  0;
+    media_ptr -> fx_media_directory_next_entry_finds =  0;
+    media_ptr -> fx_media_directory_next_full_entry_finds =  0;
+    media_ptr -> fx_media_directory_renames =  0;
+    media_ptr -> fx_media_file_allocates =  0;
+    media_ptr -> fx_media_file_attributes_reads =  0;
+    media_ptr -> fx_media_file_attributes_sets =  0;
+    media_ptr -> fx_media_file_best_effort_allocates =  0;
+    media_ptr -> fx_media_file_closes =  0;
+    media_ptr -> fx_media_file_creates =  0;
+    media_ptr -> fx_media_file_deletes =  0;
+    media_ptr -> fx_media_file_opens =  0;
+    media_ptr -> fx_media_file_reads =  0;
+    media_ptr -> fx_media_file_relative_seeks =  0;
+    media_ptr -> fx_media_file_renames =  0;
+    media_ptr -> fx_media_file_seeks =  0;
+    media_ptr -> fx_media_file_truncates =  0;
+    media_ptr -> fx_media_file_truncate_releases =  0;
+    media_ptr -> fx_media_file_writes =  0;
+    media_ptr -> fx_media_aborts =  0;
+    media_ptr -> fx_media_flushes =  0;
+    media_ptr -> fx_media_reads =  0;
+    media_ptr -> fx_media_writes =  0;
+    media_ptr -> fx_media_directory_entry_reads =  0;
+    media_ptr -> fx_media_directory_entry_writes =  0;
+    media_ptr -> fx_media_directory_searches =  0;
+#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
+    media_ptr -> fx_media_directory_search_cache_hits =  0;
+#endif
+    media_ptr -> fx_media_directory_free_searches =  0;
+    media_ptr -> fx_media_fat_entry_reads =  0;
+    media_ptr -> fx_media_fat_entry_writes =  0;
+    media_ptr -> fx_media_fat_entry_cache_read_hits =  0;
+    media_ptr -> fx_media_fat_entry_cache_read_misses =  0;
+    media_ptr -> fx_media_fat_entry_cache_write_hits =  0;
+    media_ptr -> fx_media_fat_entry_cache_write_misses =  0;
+    media_ptr -> fx_media_fat_cache_flushes =  0;
+    media_ptr -> fx_media_fat_sector_reads =  0;
+    media_ptr -> fx_media_fat_sector_writes =  0;
+    media_ptr -> fx_media_logical_sector_reads =  0;
+    media_ptr -> fx_media_logical_sector_writes =  0;
+    media_ptr -> fx_media_logical_sector_cache_read_hits =  0;
+    media_ptr -> fx_media_logical_sector_cache_read_misses =  0;
+    media_ptr -> fx_media_driver_read_requests =  0;
+    media_ptr -> fx_media_driver_write_requests =  0;
+    media_ptr -> fx_media_driver_boot_read_requests =  0;
+    media_ptr -> fx_media_driver_boot_write_requests =  0;
+    media_ptr -> fx_media_driver_release_sectors_requests =  0;
+    media_ptr -> fx_media_driver_flush_requests =  0;
+#endif
+#ifdef FX_ENABLE_FAULT_TOLERANT
+    media_ptr -> fx_media_fault_tolerant_enabled = FX_FALSE;
+    media_ptr -> fx_media_fault_tolerant_state = 0;
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_OPEN, media_ptr, media_driver, memory_ptr, memory_size, FX_TRACE_MEDIA_EVENTS, 0, 0)
+
+    /* Initialize the supplied media I/O driver.  First, build the
+       initialize driver request.  */
+    media_ptr -> fx_media_driver_request =              FX_DRIVER_INIT;
+    media_ptr -> fx_media_driver_status =               FX_IO_ERROR;
+    media_ptr -> fx_media_driver_info =                 driver_info_ptr;
+    media_ptr -> fx_media_driver_write_protect =        FX_FALSE;
+    media_ptr -> fx_media_driver_free_sector_update =   FX_FALSE;
+    media_ptr -> fx_media_driver_data_sector_read =     FX_FALSE;
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_INIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+    /* Call the specified I/O driver with the initialize request.  */
+    (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+    /* Determine if the I/O driver initialized successfully.  */
+    if (media_ptr -> fx_media_driver_status != FX_SUCCESS)
+    {
+
+        /* Return the driver error status.  */
+        return(FX_IO_ERROR);
+    }
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+    /* Increment the number of driver boot read requests.  */
+    media_ptr -> fx_media_driver_boot_read_requests++;
+#endif
+
+    /* Read the boot sector from the device.  Build the read boot sector
+       command.  */
+    media_ptr -> fx_media_driver_request =          FX_DRIVER_BOOT_READ;
+    media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
+    media_ptr -> fx_media_driver_buffer =           memory_ptr;
+    media_ptr -> fx_media_driver_sectors =          1;
+    media_ptr -> fx_media_driver_sector_type =      FX_BOOT_SECTOR;
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_BOOT_READ, media_ptr, memory_ptr, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+    /* Invoke the driver to read the boot sector.  */
+    (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+    /* Determine if the boot sector was read correctly. */
+    if (media_ptr -> fx_media_driver_status != FX_SUCCESS)
+    {
+
+        /* Build the "uninitialize" I/O driver request.  */
+        media_ptr -> fx_media_driver_request =      FX_DRIVER_UNINIT;
+        media_ptr -> fx_media_driver_status =       FX_IO_ERROR;
+
+        /* If trace is enabled, insert this event into the trace buffer.  */
+        FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+        /* Call the specified I/O driver with the uninitialize request.  */
+        (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+        /* Return the boot sector error status.  */
+        return(FX_BOOT_ERROR);
+    }
+
+    /* Extract and validate the media parameters from the boot sector.  */
+    if (_fx_media_boot_info_extract(media_ptr) != FX_SUCCESS)
+    {
+
+        /* Build the "uninitialize" I/O driver request.  */
+        media_ptr -> fx_media_driver_request =      FX_DRIVER_UNINIT;
+        media_ptr -> fx_media_driver_status =       FX_IO_ERROR;
+
+        /* If trace is enabled, insert this event into the trace buffer.  */
+        FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+        /* Call the specified I/O driver with the uninitialize request.  */
+        (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+        /* Return the invalid media error status.  */
+        return(FX_MEDIA_INVALID);
+    }
+
+    /* Pickup the additional info sector number. This will only be used in FAT32 situations.  */
+    additional_info_sector =  _fx_utility_16_unsigned_read(&media_ptr -> fx_media_driver_buffer[48]);
+
+    /* Is there at least one?  */
+    if (memory_size < media_ptr -> fx_media_bytes_per_sector)
+    {
+
+        /* Build the "uninitialize" I/O driver request.  */
+        media_ptr -> fx_media_driver_request =      FX_DRIVER_UNINIT;
+        media_ptr -> fx_media_driver_status =       FX_IO_ERROR;
+
+        /* If trace is enabled, insert this event into the trace buffer.  */
+        FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+        /* Call the specified I/O driver with the uninitialize request.  */
+        (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+        /* Error in the buffer size supplied by user.  */
+        return(FX_BUFFER_ERROR);
+    }
+
+#ifndef FX_DISABLE_CACHE
+    /* Determine how many logical sectors can be cached with user's supplied
+       buffer area - there must be at least enough for one sector!  */
+    media_ptr -> fx_media_sector_cache_size =  memory_size / media_ptr -> fx_media_bytes_per_sector;
+
+    /* If trace is enabled, register this object.  */
+    FX_TRACE_OBJECT_REGISTER(FX_TRACE_OBJECT_TYPE_MEDIA, media_ptr, media_name, FX_MAX_FAT_CACHE, media_ptr -> fx_media_sector_cache_size)
+    
+    /* Adjust the internal cache to fit the fixed number of sector cache control blocks
+       built into the media control block.  */
+    if (media_ptr -> fx_media_sector_cache_size > FX_MAX_SECTOR_CACHE)
+    {
+
+        /* Adjust the number of cache sectors downward.  If this is insufficient,
+           the FX_MAX_SECTOR_CACHE constant in FX_API.H must be changed and the FileX
+           library must be rebuilt.  */
+        media_ptr -> fx_media_sector_cache_size =  FX_MAX_SECTOR_CACHE;
+    }
+
+    /* Otherwise, everything is okay.  Initialize the data structures for managing the
+       logical sector cache.  */
+    i =  (UINT)media_ptr -> fx_media_sector_cache_size;
+    cache_entry_ptr =  media_ptr -> fx_media_sector_cache;
+    while (i--)
+    {
+
+        /* Initialize each of the cache entries.  */
+        cache_entry_ptr -> fx_cached_sector_memory_buffer =  (UCHAR *)memory_ptr;
+        cache_entry_ptr -> fx_cached_sector =                (~(ULONG64)0);
+        cache_entry_ptr -> fx_cached_sector_buffer_dirty =   FX_FALSE;
+        cache_entry_ptr -> fx_cached_sector_valid =          FX_FALSE;
+        cache_entry_ptr -> fx_cached_sector_next_used =      cache_entry_ptr + 1;
+
+        /* Move to the next cache sector entry.  */
+        cache_entry_ptr++;
+
+        /* Update the memory pointer to the next buffer slot.  */
+        memory_ptr =  (VOID *)(((UCHAR *)memory_ptr) + media_ptr -> fx_media_bytes_per_sector);
+    }
+
+    /* Backup to the last cache entry to set its next pointer to NULL.  */
+    cache_entry_ptr--;
+    cache_entry_ptr -> fx_cached_sector_next_used =  FX_NULL;
+
+    /* Remember the last memory address used by the caching logic.  */
+    media_ptr -> fx_media_sector_cache_end =  ((UCHAR *)memory_ptr) - 1;
+
+    /* Setup the head pointer of the list.  */
+    media_ptr -> fx_media_sector_cache_list_ptr =  media_ptr -> fx_media_sector_cache;
+
+    /* Setup the bit map that keeps track of the valid hashed cache logical sectors.  */
+    media_ptr -> fx_media_sector_cache_hashed_sector_valid =  0;
+
+    /* Clear the counter of the number of outstanding dirty sectors.  */
+    media_ptr -> fx_media_sector_cache_dirty_count =  0;
+
+    /* Determine if the logical sector cache should be managed by the hash function
+       instead of the linear search. The cache must be a power of 2 that is between the
+       minimum and maximum cache size.  */
+    if ((media_ptr -> fx_media_sector_cache_size >= FX_SECTOR_CACHE_HASH_ENABLE) &&
+        ((media_ptr -> fx_media_sector_cache_size ^ (media_ptr -> fx_media_sector_cache_size - 1)) ==
+         (media_ptr -> fx_media_sector_cache_size | (media_ptr -> fx_media_sector_cache_size - 1))))
+    {
+
+
+        /* Set the logical sector cache hash flag. When this flag is set, the logical
+           sector cache is accessed with a hash function instead of a linear search.  */
+        media_ptr -> fx_media_sector_cache_hashed =  FX_TRUE;
+        media_ptr -> fx_media_sector_cache_hash_mask =
+            ((media_ptr -> fx_media_sector_cache_size / FX_SECTOR_CACHE_DEPTH) - 1);
+    }
+    else
+    {
+
+        /* Clear the logical sector cache flag.  */
+        media_ptr -> fx_media_sector_cache_hashed =  FX_FALSE;
+    }
+#else
+    media_ptr -> fx_media_memory_buffer = memory_ptr;
+#endif /* FX_DISABLE_CACHE */
+
+#ifndef FX_DISABLE_FORCE_MEMORY_OPERATION
+    /* Initialize the FAT cache entry array.  */
+    for (i = 0; i < FX_MAX_FAT_CACHE; i++)
+    {
+
+        /* Clear entry in the FAT cache.  */
+        media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_cluster =   0;
+        media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_value   =   0;
+        media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty   =   0;
+    }
+
+    /* Initialize the secondary FAT update map.  */
+    for (i = 0; i < FX_FAT_MAP_SIZE; i++)
+    {
+
+        /* Clear bit map entry for secondary FAT update.  */
+        media_ptr -> fx_media_fat_secondary_update_map[i] =  0;
+    }
+#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */
+
+#ifdef FX_ENABLE_EXFAT
+    if (media_ptr -> fx_media_FAT_type != FX_exFAT)
+    {
+#endif /* FX_ENABLE_EXFAT */
+
+        /* Root_sector_start has been computed */
+        media_ptr -> fx_media_root_sector_start =  media_ptr -> fx_media_reserved_sectors +
+            (media_ptr -> fx_media_number_of_FATs *
+             media_ptr -> fx_media_sectors_per_FAT);
+
+        /* Calculate the number of directory sectors.  */
+        media_ptr -> fx_media_root_sectors =
+            ((media_ptr -> fx_media_root_directory_entries * FX_DIR_ENTRY_SIZE) +
+             media_ptr -> fx_media_bytes_per_sector - 1) /
+            media_ptr -> fx_media_bytes_per_sector;
+
+        /* Calculate the starting data sector.  */
+        media_ptr -> fx_media_data_sector_start =  media_ptr -> fx_media_root_sector_start +
+            media_ptr -> fx_media_root_sectors;
+
+        /* Calculate the total number of clusters.  */
+        media_ptr -> fx_media_total_clusters =  (ULONG)((media_ptr -> fx_media_total_sectors - media_ptr -> fx_media_data_sector_start) /
+                                                            media_ptr -> fx_media_sectors_per_cluster);
+
+        /* Determine if a 12-bit FAT is in use.  */
+        if (media_ptr -> fx_media_total_clusters < FX_12_BIT_FAT_SIZE)
+        {
+
+            /* Yes, 12-bit FAT is present.  Set flag accordingly.  */
+            media_ptr -> fx_media_12_bit_FAT = FX_TRUE;
+            media_ptr -> fx_media_32_bit_FAT = FX_FALSE;
+#ifdef FX_ENABLE_EXFAT
+            media_ptr -> fx_media_FAT_type = FX_FAT12;
+#endif /* FX_ENABLE_EXFAT */
+
+            /* No additional information sector in FAT12.  */
+            media_ptr -> fx_media_FAT32_additional_info_sector =  0;
+
+            /* Set FAT last and FAT reserved. */
+            media_ptr -> fx_media_fat_reserved = FX_RESERVED_1;
+            media_ptr -> fx_media_fat_last = FX_LAST_CLUSTER_2;
+        }
+        else if (media_ptr -> fx_media_total_clusters < FX_16_BIT_FAT_SIZE)
+        {
+
+            /* A 16-bit FAT is present.  Set flag accordingly.  */
+            media_ptr -> fx_media_12_bit_FAT =  FX_FALSE;
+            media_ptr -> fx_media_32_bit_FAT =  FX_FALSE;
+#ifdef FX_ENABLE_EXFAT
+            media_ptr -> fx_media_FAT_type = FX_FAT16;
+#endif /* FX_ENABLE_EXFAT */
+
+            /* No additional information sector in FAT16.  */
+            media_ptr -> fx_media_FAT32_additional_info_sector =  0;
+
+            /* Set FAT last and FAT reserved. */
+            media_ptr -> fx_media_fat_reserved = FX_RESERVED_1;
+            media_ptr -> fx_media_fat_last = FX_LAST_CLUSTER_2;
+        }
+        else
+        {
+
+            /* Yes, a 32-bit FAT is present.  */
+            media_ptr -> fx_media_12_bit_FAT =  FX_FALSE;
+            media_ptr -> fx_media_32_bit_FAT =  FX_TRUE;
+#ifdef FX_ENABLE_EXFAT
+            media_ptr -> fx_media_FAT_type = FX_FAT32;
+#endif /* FX_ENABLE_EXFAT */
+
+            /* Save the additional information sector FAT32. This was read from the boot
+               sector earlier in this routine. */
+            media_ptr -> fx_media_FAT32_additional_info_sector =  additional_info_sector;
+
+            /* Set FAT last and FAT reserved. */
+            media_ptr -> fx_media_fat_reserved = FX_RESERVED_1_32;
+            media_ptr -> fx_media_fat_last = FX_LAST_CLUSTER_2_32;
+        }
+#ifdef FX_ENABLE_EXFAT
+    }
+    else
+    {
+
+        /* Set FAT last and FAT reserved. */
+        media_ptr -> fx_media_fat_reserved = FX_RESERVED_1_exFAT;
+        media_ptr -> fx_media_fat_last = FX_LAST_CLUSTER_exFAT;
+    }
+#endif /* FX_ENABLE_EXFAT */
+
+    /* Determine if a 32-bit FAT is present. If so, calculate the size of the root directory (since
+       it is variable in FAT32.  */
+#ifdef FX_ENABLE_EXFAT
+    if (media_ptr -> fx_media_32_bit_FAT == FX_TRUE || 
+        (media_ptr -> fx_media_FAT_type == FX_exFAT))
+#else
+    if (media_ptr -> fx_media_32_bit_FAT == FX_TRUE)
+#endif /* FX_ENABLE_EXFAT */
+    {
+#ifdef FX_ENABLE_EXFAT
+        if (media_ptr -> fx_media_32_bit_FAT == FX_TRUE)
+        {
+#endif /* FX_ENABLE_EXFAT */
+
+            /* Root First cluster starts from at least cluster 2, or higher. */
+            if (media_ptr -> fx_media_root_cluster_32 < FX_FAT_ENTRY_START)
+            {
+                return(FX_MEDIA_INVALID);
+            }
+
+            /* Calculate logical number of root dir sector.  */
+            media_ptr -> fx_media_root_sector_start = media_ptr -> fx_media_data_sector_start +
+                (media_ptr -> fx_media_root_cluster_32 - FX_FAT_ENTRY_START) *
+                media_ptr -> fx_media_sectors_per_cluster;
+#ifdef FX_ENABLE_EXFAT
+        }
+#endif /* FX_ENABLE_EXFAT */
+
+        /* Calculate maximum possible value for fx_media_root_directory_entries */
+        i = 0;
+        for (cluster_number = media_ptr -> fx_media_root_cluster_32;;)
+        {
+
+            status =  _fx_utility_FAT_entry_read(media_ptr, cluster_number, &FAT_entry);
+            i++;
+            /* Determine if the read was successful.  */
+            if (status != FX_SUCCESS)
+            {
+
+                /* Build the "uninitialize" I/O driver request.  */
+                media_ptr -> fx_media_driver_request =      FX_DRIVER_UNINIT;
+                media_ptr -> fx_media_driver_status =       FX_IO_ERROR;
+
+                /* If trace is enabled, insert this event into the trace buffer.  */
+                FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+                /* Call the specified I/O driver with the uninitialize request.  */
+                (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+                return(FX_FAT_READ_ERROR);
+            }
+
+            if ((cluster_number == FAT_entry) || (i > media_ptr -> fx_media_total_clusters))
+            {
+
+                /* Build the "uninitialize" I/O driver request.  */
+                media_ptr -> fx_media_driver_request =      FX_DRIVER_UNINIT;
+                media_ptr -> fx_media_driver_status =       FX_IO_ERROR;
+
+                /* If trace is enabled, insert this event into the trace buffer.  */
+                FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+                /* Call the specified I/O driver with the uninitialize request.  */
+                (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+                return(FX_FAT_READ_ERROR);
+            }
+            if (FAT_entry >= FX_RESERVED_1_32)
+            {
+                break;
+            }
+            cluster_number = FAT_entry;
+        }
+
+        /* Calculate the number of directory entries.  */
+        media_ptr -> fx_media_root_directory_entries =  (i * media_ptr -> fx_media_sectors_per_cluster *
+                                                         media_ptr -> fx_media_bytes_per_sector) / FX_DIR_ENTRY_SIZE;
+    }
+
+#ifndef FX_DISABLE_FORCE_MEMORY_OPERATION
+    /* Calculate the number of available clusters.  */
+    media_ptr -> fx_media_available_clusters =  0;
+
+    /* Set the cluster search start to an invalid value.  */
+    media_ptr -> fx_media_cluster_search_start =  0;
+#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */
+
+    /* Determine if there is 32-bit FAT additional information sector. */
+    if (media_ptr -> fx_media_FAT32_additional_info_sector)
+    {
+
+    UCHAR *buffer_ptr;
+    ULONG  signature;
+
+
+        /* Yes, read the FAT32 additional information sector to get the available cluster count and
+           the hint for the first available cluster.  */
+
+#ifndef FX_DISABLE_CACHE
+        /* Setup a pointer to the first cached entry's buffer.  */
+        buffer_ptr =  (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector_memory_buffer;
+
+        /* Invalidate this cache entry.  */
+        (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector =  (~((ULONG64) 0));
+        (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector_valid =  FX_FALSE;
+#else
+        buffer_ptr =  media_ptr -> fx_media_memory_buffer;
+        media_ptr -> fx_media_memory_buffer_sector = (ULONG64)-1;
+#endif /* FX_DISABLE_CACHE */
+
+        /* Read the FAT32 additional information sector from the device.  */
+        media_ptr -> fx_media_driver_request =          FX_DRIVER_READ;
+        media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
+        media_ptr -> fx_media_driver_buffer =           buffer_ptr;
+        media_ptr -> fx_media_driver_logical_sector =   media_ptr -> fx_media_FAT32_additional_info_sector;
+        media_ptr -> fx_media_driver_sectors =          1;
+        media_ptr -> fx_media_driver_sector_type =      FX_DIRECTORY_SECTOR;
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+        /* Increment the number of driver read sector(s) requests.  */
+        media_ptr -> fx_media_driver_read_requests++;
+#endif
+
+        /* If trace is enabled, insert this event into the trace buffer.  */
+        FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_READ, media_ptr, media_ptr -> fx_media_FAT32_additional_info_sector, 1, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+        /* Invoke the driver to read the FAT32 additional information sector.  */
+        (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+        /* Determine if the FAT32 sector was read correctly. */
+        if (media_ptr -> fx_media_driver_status == FX_SUCCESS)
+        {
+
+            /* Yes, setup a pointer into the FAT32 additional information sector.  */
+            buffer_ptr =  media_ptr -> fx_media_driver_buffer;
+
+            /* Pickup the first signature long word.  */
+            signature =  _fx_utility_32_unsigned_read(&buffer_ptr[0]);
+
+            /* Determine if the signature is correct.  */
+            if (signature == 0x41615252)
+            {
+
+                /* Yes, the first signature is correct, now pickup the next signature.  */
+                signature =  _fx_utility_32_unsigned_read(&buffer_ptr[484]);
+
+                /* Determine if this signature is correct.  */
+                if (signature == 0x61417272)
+                {
+
+                    /* Yes, we have a good FAT32 additional information sector.  */
+
+                    /* Pickup the current available cluster count on the media.  */
+                    media_ptr -> fx_media_available_clusters =  _fx_utility_32_unsigned_read(&buffer_ptr[488]);
+
+                    /* Initialize the last reported available cluster count to the same value.  */
+                    media_ptr -> fx_media_FAT32_additional_info_last_available =  media_ptr -> fx_media_available_clusters;
+
+                    /* Pickup the hint for the starting free cluster search.  */
+                    media_ptr -> fx_media_cluster_search_start =  _fx_utility_32_unsigned_read(&buffer_ptr[492]);
+
+                    /* Perform a quick sanity check on the available cluster count and the starting free
+                       cluster search.  */
+                    if ((media_ptr -> fx_media_available_clusters > media_ptr -> fx_media_total_clusters) ||
+                        (media_ptr -> fx_media_cluster_search_start > media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START) ||
+                        (media_ptr -> fx_media_cluster_search_start < FX_FAT_ENTRY_START))
+                    {
+
+                        /* Something is wrong, clear the available cluster count and search so the regular processing
+                           is used.  */
+                        media_ptr -> fx_media_available_clusters =    0;
+                        media_ptr -> fx_media_cluster_search_start =  0;
+
+                        /* We don't invalidate the additional info sector here because only the data is bad.  */
+                    }
+                }
+                else
+                {
+
+                    /* Signature is bad, invalidate the additional info sector.  */
+                    media_ptr -> fx_media_FAT32_additional_info_sector =  0;
+                }
+            }
+            else
+            {
+
+                /* Signature is bad, invalidate the additional info sector.  */
+                media_ptr -> fx_media_FAT32_additional_info_sector =  0;
+            }
+        }
+        else
+        {
+
+            /* IO error trying to read additional information sector, invalidate the additional info sector.  */
+            media_ptr -> fx_media_FAT32_additional_info_sector =  0;
+        }
+    }
+
+    /* Search the media to find the first available cluster as well as the total
+       available clusters.  */
+
+    /* Determine what type of FAT is present.  */
+    if (media_ptr -> fx_media_12_bit_FAT)
+    {
+
+        /* A 12-bit FAT is present.  Utilize the FAT entry read utility to pickup
+           each FAT entry's contents.  */
+
+        /* Loop to read each cluster entry in the first FAT.  */
+        for (cluster_number =  FX_FAT_ENTRY_START;
+             cluster_number < (media_ptr -> fx_media_total_clusters) + FX_FAT_ENTRY_START;
+             cluster_number++)
+        {
+
+            /* Read a FAT entry.  */
+            status =  _fx_utility_FAT_entry_read(media_ptr, cluster_number, &FAT_entry);
+
+            /* Determine if the read was successful.  */
+            if (status != FX_SUCCESS)
+            {
+
+                /* Build the "uninitialize" I/O driver request.  */
+                media_ptr -> fx_media_driver_request =      FX_DRIVER_UNINIT;
+                media_ptr -> fx_media_driver_status =       FX_IO_ERROR;
+
+                /* If trace is enabled, insert this event into the trace buffer.  */
+                FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+                /* Call the specified I/O driver with the uninitialize request.  */
+                (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+                return(FX_FAT_READ_ERROR);
+            }
+
+            /* Now determine if the FAT entry is available.  */
+            if (FAT_entry == FX_FREE_CLUSTER)
+            {
+
+                /* Increment the number of available clusters.  */
+                media_ptr -> fx_media_available_clusters++;
+
+                /* Determine if the starting free cluster has been found yet.  */
+                if (media_ptr -> fx_media_cluster_search_start == 0)
+                {
+
+                    /* Remember the first free cluster to start further searches from.  */
+                    media_ptr -> fx_media_cluster_search_start =  cluster_number;
+                }
+            }
+        }
+    }
+#ifdef FX_ENABLE_EXFAT
+    else if ((media_ptr -> fx_media_available_clusters == 0)
+             && (media_ptr -> fx_media_FAT_type != FX_exFAT))
+#else
+    else if (media_ptr -> fx_media_available_clusters == 0)
+#endif /* FX_ENABLE_EXFAT */
+    {
+
+        /* A 16 or 32-bit FAT is present. Read directly into the logical sector
+           cache memory to optimize I/O on larger devices. Since we are looking for
+           values of zero, endian issues are not important.  */
+
+        /* Invalidate the current logical sector cache.  */
+        _fx_utility_logical_sector_flush(media_ptr, ((ULONG64) 1), (ULONG64) (media_ptr -> fx_media_total_sectors), FX_TRUE);
+
+        /* Reset the memory pointer.  */
+        media_ptr -> fx_media_memory_buffer =  original_memory_ptr;
+
+        /* Loop through all FAT sectors in the primary FAT.  The first two entries are
+           examined in this loop, but they are always unavailable.  */
+        cluster_number =  0;
+#ifndef FX_DISABLE_CACHE
+        for (i = 0; i < media_ptr -> fx_media_sectors_per_FAT; i = i + media_ptr -> fx_media_sector_cache_size)
+        {
+
+            /* Calculate the starting next FAT sector.  */
+            FAT_sector =  media_ptr -> fx_media_reserved_sectors + i;
+
+            /* Calculate how many sectors to read.  */
+            FAT_read_sectors =  media_ptr -> fx_media_sectors_per_FAT - i;
+
+            /* Determine if there is not enough memory to read the remaining FAT sectors.  */
+            if (FAT_read_sectors > media_ptr -> fx_media_sector_cache_size)
+            {
+                FAT_read_sectors =  media_ptr -> fx_media_sector_cache_size;
+            }
+#else
+        for (i = 0; i < media_ptr -> fx_media_sectors_per_FAT; i++)
+        {
+
+            /* Calculate the starting next FAT sector.  */
+            FAT_sector =  media_ptr -> fx_media_reserved_sectors + i;
+
+            /* Calculate how many sectors to read.  */
+            FAT_read_sectors =  1;
+#endif /* FX_DISABLE_CACHE */
+
+            /* Read the FAT sectors directly from the driver.  */
+            media_ptr -> fx_media_driver_request =          FX_DRIVER_READ;
+            media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
+            media_ptr -> fx_media_driver_buffer =           media_ptr -> fx_media_memory_buffer;
+            media_ptr -> fx_media_driver_logical_sector =   FAT_sector;
+            media_ptr -> fx_media_driver_sectors =          FAT_read_sectors;
+            media_ptr -> fx_media_driver_sector_type =      FX_FAT_SECTOR;
+
+            /* If trace is enabled, insert this event into the trace buffer.  */
+            FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_READ, media_ptr, FAT_sector, FAT_read_sectors, media_ptr -> fx_media_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+            /* Invoke the driver to read the FAT sectors.  */
+            (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+            /* Determine if the read was successful.  */
+            if (media_ptr -> fx_media_driver_status != FX_SUCCESS)
+            {
+
+                /* Build the "uninitialize" I/O driver request.  */
+                media_ptr -> fx_media_driver_request =      FX_DRIVER_UNINIT;
+                media_ptr -> fx_media_driver_status =       FX_IO_ERROR;
+
+                /* If trace is enabled, insert this event into the trace buffer.  */
+                FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+                /* Call the specified I/O driver with the uninitialize request.  */
+                (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+                return(FX_FAT_READ_ERROR);
+            }
+
+            /* Calculate the number of bytes in the buffer.  */
+            bytes_in_buffer =  (media_ptr -> fx_media_bytes_per_sector * FAT_read_sectors);
+
+            /* Walk through the sector cache memory to search for available clusters and the first
+               available if not already found.  */
+            for (j = 0; j < bytes_in_buffer;)
+            {
+
+                /* Check for a 32-bit FAT.  */
+                if (media_ptr -> fx_media_32_bit_FAT)
+                {
+
+                    /* Pickup 32-bit FAT entry.  */
+                    FAT_entry =  *((ULONG *)&(media_ptr -> fx_media_memory_buffer[j]));
+
+                    /* Advance to next FAT entry.  */
+                    j = j + 4;
+                }
+                else
+                {
+
+                    /* Process a 16-bit FAT entry.  */
+                    FAT_entry =  (((ULONG)(media_ptr -> fx_media_memory_buffer[j])) & 0xFF) |
+                        ((((ULONG)(media_ptr -> fx_media_memory_buffer[j + 1])) & 0xFF) << 8);
+
+                    /* Advance to next FAT entry.  */
+                    j =  j + 2;
+                }
+
+                /* Determine if the FAT entry is free.  */
+                if (FAT_entry == FX_FREE_CLUSTER)
+                {
+
+                    /* Entry is free, increment available clusters.  */
+                    media_ptr -> fx_media_available_clusters++;
+
+                    /* Determine if the starting free cluster has been found yet.  */
+                    if (media_ptr -> fx_media_cluster_search_start == 0)
+                    {
+
+                        /* Remember the first free cluster to start further searches from.  */
+                        media_ptr -> fx_media_cluster_search_start =  cluster_number;
+                    }
+                }
+
+                /* Increment the cluster number.  */
+                cluster_number++;
+
+                /* Determine if we have reviewed all FAT entries.  */
+                if (cluster_number >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START))
+                {
+
+                    /* Yes, we have looked at all the FAT entries.  */
+
+                    /* Ensure that the outer loop terminates as well.  */
+                    i = media_ptr -> fx_media_sectors_per_FAT;
+                    break;
+                }
+            }
+        }
+    }
+#ifdef FX_ENABLE_EXFAT
+    else if (media_ptr -> fx_media_FAT_type == FX_exFAT)
+    {
+        status = _fx_utility_exFAT_bitmap_initialize(media_ptr);
+
+        if ((FX_SUCCESS         != status)  &&
+            (FX_NO_MORE_SPACE   != status))
+        {
+            return(status);
+        }
+    }
+#endif /* FX_ENABLE_EXFAT */
+
+    /* If there were no free clusters, just set the search pointer to the
+       first cluster number.  */
+    if (media_ptr -> fx_media_cluster_search_start == 0)
+    {
+        media_ptr -> fx_media_cluster_search_start =  FX_FAT_ENTRY_START;
+    }
+
+    /* Setup the current working directory fields to default to the root
+       directory.  */
+    media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name =
+        media_ptr -> fx_media_default_path.fx_path_name_buffer;
+    media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_short_name[0] =  0;
+    media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name[0] =        0;
+    media_ptr -> fx_media_default_path.fx_path_string[0] =                      (CHAR)0;
+    media_ptr -> fx_media_default_path.fx_path_string[FX_MAXIMUM_PATH - 1] =      (CHAR)0;
+    media_ptr -> fx_media_default_path.fx_path_current_entry =                         0;
+
+#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
+
+    /* Invalidate the previously found directory entry.  */
+    media_ptr -> fx_media_last_found_name[0] =  0;
+#endif
+
+#ifndef FX_DISABLE_FORCE_MEMORY_OPERATION
+    /* Initialize the opened file linked list and associated counter.  */
+    media_ptr -> fx_media_opened_file_list =      FX_NULL;
+    media_ptr -> fx_media_opened_file_count =     0;
+#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */
+
+    /* Create the media protection structure if FX_SINGLE_THREAD is not
+       defined.  */
+#ifndef FX_SINGLE_THREAD
+
+#ifndef FX_DONT_CREATE_MUTEX
+
+    /* Create ThreadX mutex for protection.  */
+    tx_mutex_create(&(media_ptr -> fx_media_protect), "FileX Media Mutex", TX_NO_INHERIT);
+#endif
+
+#endif
+
+#ifdef FX_DONT_CREATE_MUTEX
+
+    /* Load the media ID field in the media control block.  This allows the FX_PROTECT
+       call to succeed.  */
+    media_ptr -> fx_media_id =  (ULONG)FX_MEDIA_ID;
+
+    /* Protect against other threads accessing the media.  */
+    FX_PROTECT
+#endif
+
+    /* Lockout interrupts.  */
+    FX_DISABLE_INTS
+
+    /* At this point, the media has been opened successfully.  Place the
+       media on the linked list of currently opened media.  */
+
+    /* Load the media ID field in the media control block.  */
+    media_ptr -> fx_media_id =  (ULONG)FX_MEDIA_ID;
+
+    /* Place the thread on the list of opened media.  First,
+       check for an empty list.  */
+    if (_fx_system_media_opened_ptr)
+    {
+
+        /* Pickup tail pointer.  */
+        tail_ptr =  _fx_system_media_opened_ptr -> fx_media_opened_previous;
+
+        /* Place the new media in the list.  */
+        _fx_system_media_opened_ptr -> fx_media_opened_previous =  media_ptr;
+        tail_ptr -> fx_media_opened_next =  media_ptr;
+
+        /* Setup this media's opened links.  */
+        media_ptr -> fx_media_opened_previous =  tail_ptr;
+        media_ptr -> fx_media_opened_next =      _fx_system_media_opened_ptr;
+    }
+    else
+    {
+
+        /* The opened media list is empty.  Add the media to empty list.  */
+        _fx_system_media_opened_ptr =           media_ptr;
+        media_ptr -> fx_media_opened_next =     media_ptr;
+        media_ptr -> fx_media_opened_previous = media_ptr;
+    }
+
+    /* Increment the opened media counter.  */
+    _fx_system_media_opened_count++;
+
+    /* Invoke media open callback. */
+    if (media_ptr -> fx_media_open_notify)
+    {
+        media_ptr -> fx_media_open_notify(media_ptr);
+    }
+
+    /* Restore interrupts.  */
+    FX_RESTORE_INTS
+
+#ifdef FX_DONT_CREATE_MUTEX
+
+    /* Release media protection.  */
+    FX_UNPROTECT
+#endif
+
+    /* Return a successful status.  */
+    return(FX_SUCCESS);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_partition_offset_calculate.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_partition_offset_calculate.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_partition_offset_calculate.c	(revision 54)
@@ -0,0 +1,683 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Application Utility                                                 */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_utility.h"
+
+
+/* Define internal data structures.  */
+
+typedef struct FX_MEDIA_PARTITION_STRUCT
+{
+    ULONG fx_media_part_start;
+    ULONG fx_media_part_size;
+} FX_MEDIA_PARTITION;
+
+/* Define internal partition constants. */
+
+#ifndef FX_MAX_PARTITION_COUNT
+#define FX_MAX_PARTITION_COUNT              16
+#endif /* FX_MAX_PARTITION_COUNT */
+
+#define FX_PARTITION_TABLE_OFFSET           446
+#define FX_PARTITION_ENTRY_SIZE             16
+#define FX_PARTITION_TYPE_OFFSET            4
+#define FX_PARTITION_LBA_OFFSET             8
+#define FX_PARTITION_SECTORS_OFFSET         12
+
+#define FX_PARTITION_TYPE_FREE              0x00
+#define FX_PARTITION_TYPE_EXTENDED          0x05
+#define FX_PARTITION_TYPE_EXTENDED_LBA      0x0F
+
+
+/* Define function prototypes for the partition table parsing application
+   utility.  */
+
+UINT    _fx_partition_offset_calculate(void  *partition_sector, UINT partition,
+                                     ULONG *partition_start, ULONG *partition_size);
+UINT    _fx_utility_partition_get(FX_MEDIA_PARTITION *partition_table,
+                                UINT *count, ULONG sector, UCHAR *sector_buffer);
+UINT    _fx_partition_offset_calculate_extended(FX_MEDIA *media_ptr, void  *partition_sector, UINT partition,
+                                     ULONG *partition_start, ULONG *partition_size);
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_partition_offset_calculate                      PORTABLE C      */
+/*                                                           6.1.6        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function calculates the sector offset to the specified         */
+/*    partition.  The buffer containing the partition table is also       */
+/*    supplied to this function.  If the buffer supplied is a boot        */
+/*    record (which could be the case in non-partition systems), this     */
+/*    function returns an offset of zero, the total sectors, and a        */
+/*    successful status indicating that the buffer supplied is the boot   */
+/*    record.  Otherwise, if a partition is found, this function returns  */
+/*    the sector offset to its boot record along with a successful        */
+/*    status. If the specified partition is not found or the buffer is    */
+/*    not a partition table or boot record, this function returns an      */
+/*    error.                                                              */
+/*                                                                        */
+/*    Note: Empty partitions have a FX_SUCCESS return code, however their */
+/*          starting sector is FX_NULL and the size returned is 0.        */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    partition_sector                      Pointer to buffer containing  */
+/*                                            either the partition table  */
+/*                                            or the boot sector          */
+/*    partition                             Desired partition             */
+/*    partition_start                       Return partition start        */
+/*    partition_size                        Return partition size         */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    return status                                                       */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*     _fx_utility_partition_get            Actual partition parsing      */
+/*                                            routine                     */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Driver                                                  */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*  04-02-2021     William E. Lamie         Modified comment(s),          */
+/*                                            ignored signature check for */
+/*                                            no partition situation,     */
+/*                                            resulting in version 6.1.6  */
+/*                                                                        */
+/**************************************************************************/
+UINT  _fx_partition_offset_calculate(void  *partition_sector, UINT partition,
+                                     ULONG *partition_start, ULONG *partition_size)
+{
+
+FX_MEDIA_PARTITION  partition_table[4];
+UINT                count;
+ULONG64             total_sectors;
+UCHAR               *partition_sector_ptr;
+
+
+    /* Setup working pointer and initialize count.  */
+    partition_sector_ptr =  partition_sector;
+    count =  0;
+
+    /* Check for a real boot sector instead of a partition table.  */
+    if ((partition_sector_ptr[0] == 0xe9) || ((partition_sector_ptr[0] == 0xeb) && (partition_sector_ptr[2] == 0x90)))
+    {
+
+        /* Yes, a real boot sector could be present.  */
+
+        /* See if there are good values for sectors per FAT.  */
+        if (partition_sector_ptr[0x16] || partition_sector_ptr[0x17] || partition_sector_ptr[0x24] || partition_sector_ptr[0x25] || partition_sector_ptr[0x26] || partition_sector_ptr[0x27])
+        {
+
+            /* There are values for sectors per FAT.  */
+
+            /* Determine if there is a total sector count.  */
+            total_sectors =  0;
+
+            if (partition_sector_ptr[0x13] || partition_sector_ptr[0x14])
+            {
+
+                /* Calculate the total sectors, FAT12/16.  */
+                total_sectors =  (((ULONG) partition_sector_ptr[0x14]) << 8) | ((ULONG) partition_sector_ptr[0x13]);
+            }
+            else if (partition_sector_ptr[0x20] || partition_sector_ptr[0x21] || partition_sector_ptr[0x22] || partition_sector_ptr[0x23])
+            {
+
+                /* Calculate the total sectors, FAT32.  */
+                total_sectors =  (((ULONG) partition_sector_ptr[0x23]) << 24) |
+                                 (((ULONG) partition_sector_ptr[0x22]) << 16) |
+                                 (((ULONG) partition_sector_ptr[0x21]) << 8)  |
+                                 ((ULONG) partition_sector_ptr[0x20]);
+            }
+
+            /* Determine if there is a total sector count.  */
+            if (total_sectors)
+            {
+
+                if (partition_start != FX_NULL)
+                {
+                    /* Return an offset of 0, size of boot record, and a successful status.  */
+                    *partition_start =  0;
+                }
+
+                /* Determine if the total sectors is required.  */
+                if (partition_size != FX_NULL)
+                {
+
+                    /* Return the total sectors.  */
+                    *partition_size =  (ULONG)(total_sectors & 0xFFFFFFFF);
+                }
+
+                /* Return success!  */
+                return(FX_SUCCESS);
+            }
+        }
+#ifdef FX_ENABLE_EXFAT
+        /* See if there are good values for sectors per exFAT.  */
+        else if (partition_sector_ptr[0x0b] == 0 && partition_sector_ptr[0x0c] == 0)
+        {
+            /* There are values for sectors per exFAT.  */
+
+            /* Calculate the total sectors.  */
+            total_sectors = _fx_utility_64_unsigned_read(&partition_sector_ptr[FX_EF_VOLUME_LENGTH]);
+
+            /* Determine if there is a total sector count.  */
+            if (total_sectors)
+            {
+
+                if (partition_start != FX_NULL)
+                {
+                    /* Return an offset of 0, size of boot record, and a successful status.  */
+                    *partition_start =  0;
+                }
+
+                /* Determine if the total sectors is required.  */
+                if (partition_size != FX_NULL)
+                {
+
+                    if (total_sectors > 0xFFFFFFFF)
+                    {
+
+                        /* Overflow. Just return not found. */
+                        return(FX_NOT_FOUND);
+                    }
+
+                    /* Return the total sectors.  */
+                    *partition_size =  (ULONG)(total_sectors & 0xFFFFFFFF);
+                }
+
+                /* Return success!  */
+                return(FX_SUCCESS);
+            }
+        }
+#endif /* FX_ENABLE_EXFAT */
+    }
+
+    /* Check signature to make sure the buffer is valid.  */
+    if ((partition_sector_ptr[510] != 0x55) || (partition_sector_ptr[511] != 0xAA))
+    {
+
+        /* Invalid, return an error.  */
+        return(FX_NOT_FOUND);
+    }
+
+    /* Not bootable, look for specific partition.  */
+    _fx_utility_partition_get(partition_table, &count, 0, partition_sector_ptr);
+
+    /* Determine if return value is valid.  */
+    if (partition >= count)
+    {
+
+        /* No, return an error.  */
+        return(FX_NOT_FOUND);
+    }
+
+    /* Return the partition starting sector, if non-NULL.  */
+    if (partition_start != FX_NULL)
+    {
+        *partition_start =  partition_table[partition].fx_media_part_start;
+    }
+
+    /* Return the partition size, if non-NULL.  */
+    if (partition_size != FX_NULL)
+    {
+        *partition_size =  partition_table[partition].fx_media_part_size;
+    }
+
+    /* Return successful completion.  */
+    return(FX_SUCCESS);
+}
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_utility_partition_get                           PORTABLE C      */
+/*                                                           6.1.6        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function parses the partition sector and completes the         */
+/*    supplied partition entry structure.                                 */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    partition_table                       Pointer to partition table    */
+/*    count                                 Number of partitions found    */
+/*    sector                                Base sector                   */
+/*    sector_buffer                         Buffer containing partition   */
+/*                                            table                       */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    return status                                                       */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _fx_partition_offset_calculate        Calculate partition offset    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*  04-02-2021     William E. Lamie         Modified comment(s),          */
+/*                                            resulting in version 6.1.6  */
+/*                                                                        */
+/**************************************************************************/
+UINT  _fx_utility_partition_get(FX_MEDIA_PARTITION *partition_table,
+                                UINT *count, ULONG sector, UCHAR *sector_buffer)
+{
+
+UINT    i;
+ULONG   base_sector, value;
+
+    /* This parameter has not been supported yet. */
+    FX_PARAMETER_NOT_USED(sector);
+
+    /* Initialize base sector.  */
+    base_sector =  0;
+
+    for(i = 446; i <= 494; i+=16)
+    {
+        if (sector_buffer[i + 4] == 0) /* no partition entry here */
+        {
+
+            partition_table[*count].fx_media_part_start = 0;
+            partition_table[*count].fx_media_part_size  = 0;
+        }
+        else
+        {
+
+            value =  (ULONG) sector_buffer[i + 8]; /* little endian start value */
+            value =  (((ULONG) sector_buffer[i + 9]) << 8) | value;
+            value =  (((ULONG) sector_buffer[i + 10]) << 16) | value;
+            value =  (((ULONG) sector_buffer[i + 11]) << 24) | value;
+            partition_table[*count].fx_media_part_start = value + base_sector;
+
+            value =  (ULONG) sector_buffer[i + 12]; /* little endian size value */
+            value =  (((ULONG) sector_buffer[i + 13]) << 8) | value;
+            value =  (((ULONG) sector_buffer[i + 14]) << 16) | value;
+            value =  (((ULONG) sector_buffer[i + 15]) << 24) | value;
+            partition_table[*count].fx_media_part_size = value;
+        }
+
+        (*count)++;
+    }
+
+    /* Return success.  */
+    return(FX_SUCCESS);
+}
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_partition_offset_calculate_extended             PORTABLE C      */
+/*                                                           6.2.0        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    Xiuwen Cai, Microsoft Corporation                                   */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function calculates the sector offset to the specified         */
+/*    partition.  The buffer containing the partition table is also       */
+/*    supplied to this function.  If the buffer supplied is a boot        */
+/*    record (which could be the case in non-partition systems), this     */
+/*    function returns an offset of zero, the total sectors, and a        */
+/*    successful status indicating that the buffer supplied is the boot   */
+/*    record.  Otherwise, if a partition is found, this function returns  */
+/*    the sector offset to its boot record along with a successful        */
+/*    status. If the specified partition is not found or the buffer is    */
+/*    not a partition table or boot record, this function returns an      */
+/*    error.                                                              */
+/*                                                                        */
+/*    Note: Empty partitions have a FX_NOT_FOUND return code.             */
+/*      Use partition index 0 to 3 for primary partition and index 4 to   */
+/*      FX_MAX_PARTITION_COUNT for extended partition.                    */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    media_ptr                             Media control block pointer   */
+/*    partition_sector                      Pointer to buffer containing  */
+/*                                            either the partition table  */
+/*                                            or the boot sector          */
+/*    partition                             Desired partition             */
+/*    partition_start                       Return partition start        */
+/*    partition_size                        Return partition size         */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    return status                                                       */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _fx_utility_16_unsigned_read          Read a USHORT from memory     */
+/*    _fx_utility_32_unsigned_read          Read a ULONG from memory      */
+/*    _fx_utility_64_unsigned_read          Read a ULONG64 from memory    */
+/*    Media driver                                                        */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Driver                                                  */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  10-31-2022     Xiuwen Cai               Initial Version 6.2.0        */
+/*                                                                        */
+/**************************************************************************/
+UINT  _fx_partition_offset_calculate_extended(FX_MEDIA *media_ptr, void  *partition_sector, UINT partition,
+                                     ULONG *partition_start, ULONG *partition_size)
+{
+
+ULONG64             total_sectors;
+UCHAR               *partition_sector_ptr;
+UCHAR               partition_type;
+UINT                i;
+ULONG               base_sector;
+ULONG               base_sector_extended;
+
+
+    /* Setup working pointer.  */
+    partition_sector_ptr =  partition_sector;
+
+    /* Check for a real boot sector instead of a partition table.  */
+    if ((partition_sector_ptr[0] == 0xe9) || ((partition_sector_ptr[0] == 0xeb) && (partition_sector_ptr[2] == 0x90)))
+    {
+
+        /* Yes, a real boot sector could be present.  */
+
+        /* See if there are good values for sectors per FAT.  */
+        if (partition_sector_ptr[0x16] || partition_sector_ptr[0x17] || partition_sector_ptr[0x24] || partition_sector_ptr[0x25] || partition_sector_ptr[0x26] || partition_sector_ptr[0x27])
+        {
+
+            /* There are values for sectors per FAT.  */
+
+            /* Get the total sectors, FAT12/16.  */
+            total_sectors =  _fx_utility_16_unsigned_read(&partition_sector_ptr[FX_SECTORS]);
+
+            if (total_sectors == 0)
+            {
+
+                /* Get the total sectors, FAT32.  */
+                total_sectors = _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_HUGE_SECTORS]);
+            }
+
+            /* Determine if there is a total sector count.  */
+            if (total_sectors)
+            {
+
+                if (partition_start != FX_NULL)
+                {
+                    /* Return an offset of 0, size of boot record, and a successful status.  */
+                    *partition_start =  0;
+                }
+
+                /* Determine if the total sectors is required.  */
+                if (partition_size != FX_NULL)
+                {
+
+                    /* Return the total sectors.  */
+                    *partition_size =  (ULONG)(total_sectors & 0xFFFFFFFF);
+                }
+
+                /* Return success!  */
+                return(FX_SUCCESS);
+            }
+        }
+#ifdef FX_ENABLE_EXFAT
+        /* See if there are good values for sectors per exFAT.  */
+        else if (partition_sector_ptr[0x0b] == 0 && partition_sector_ptr[0x0c] == 0)
+        {
+            /* There are values for sectors per exFAT.  */
+
+            /* Calculate the total sectors.  */
+            total_sectors = _fx_utility_64_unsigned_read(&partition_sector_ptr[FX_EF_VOLUME_LENGTH]);
+
+            /* Determine if there is a total sector count.  */
+            if (total_sectors)
+            {
+
+                if (partition_start != FX_NULL)
+                {
+                    /* Return an offset of 0, size of boot record, and a successful status.  */
+                    *partition_start =  0;
+                }
+
+                /* Determine if the total sectors is required.  */
+                if (partition_size != FX_NULL)
+                {
+
+                    if (total_sectors > 0xFFFFFFFF)
+                    {
+
+                        /* Overflow. Just return not found. */
+                        return(FX_NOT_FOUND);
+                    }
+
+                    /* Return the total sectors.  */
+                    *partition_size =  (ULONG)(total_sectors & 0xFFFFFFFF);
+                }
+
+                /* Return success!  */
+                return(FX_SUCCESS);
+            }
+        }
+#endif /* FX_ENABLE_EXFAT */
+    }
+
+    /* Check signature to make sure the buffer is valid.  */
+    if ((partition_sector_ptr[510] != FX_SIG_BYTE_1) || (partition_sector_ptr[511] != FX_SIG_BYTE_2))
+    {
+
+        /* Invalid, return an error.  */
+        return(FX_NOT_FOUND);
+    }
+
+    /* Not bootable, look for specific partition.  */
+
+    /* Check if primary partitions are addressed.  */
+    if (partition < 4)
+    {
+
+        /* Get partition type.  */
+        partition_type =  partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + partition * FX_PARTITION_ENTRY_SIZE + FX_PARTITION_TYPE_OFFSET];
+
+        /* Check if there is a vaild partition.  */
+        if (partition_type != FX_PARTITION_TYPE_FREE)
+        {
+
+            /* Return the partition starting sector, if non-NULL.  */
+            if (partition_start != FX_NULL)
+            {
+                *partition_start = _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + partition * FX_PARTITION_ENTRY_SIZE + FX_PARTITION_LBA_OFFSET]);
+            }
+
+            /* Return the partition size, if non-NULL.  */
+            if (partition_size != FX_NULL)
+            {
+                *partition_size =  _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + partition * FX_PARTITION_ENTRY_SIZE + FX_PARTITION_SECTORS_OFFSET]);
+            }
+
+            /* Return success!  */
+            return(FX_SUCCESS);
+        }
+        else
+        {
+
+            /* Not partition here.  */
+            return(FX_NOT_FOUND);
+        }
+    }
+
+    /* Check for invalid parameter.  */
+    if (partition > FX_MAX_PARTITION_COUNT)
+    {
+
+        /* Return error.  */
+        return(FX_NOT_FOUND);
+    }
+
+    base_sector = 0;
+
+    /* Loop to find the extended partition table.  */
+    for(i = FX_PARTITION_TABLE_OFFSET; i <= FX_PARTITION_TABLE_OFFSET + 3 * FX_PARTITION_ENTRY_SIZE; i += FX_PARTITION_ENTRY_SIZE)
+    {
+
+        /* Get partition type.  */
+        partition_type =  partition_sector_ptr[i + FX_PARTITION_TYPE_OFFSET];
+        if (partition_type == FX_PARTITION_TYPE_EXTENDED || partition_type == FX_PARTITION_TYPE_EXTENDED_LBA)
+        {
+            base_sector =  _fx_utility_32_unsigned_read(&partition_sector_ptr[i + FX_PARTITION_LBA_OFFSET]);
+            break;
+        }
+    }
+
+    if (base_sector == 0)
+    {
+
+        /* No extended partition.  */
+        return(FX_NOT_FOUND);
+    }
+
+    base_sector_extended = base_sector;
+
+    for (i = 4; i <= partition; i++)
+    {
+
+        /* Read the partition sector from the device.  Build the read sector
+            command.  */
+        media_ptr -> fx_media_driver_request =          FX_DRIVER_READ;
+        media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
+        media_ptr -> fx_media_driver_buffer =           partition_sector_ptr;
+        media_ptr -> fx_media_driver_logical_sector =   base_sector;
+        media_ptr -> fx_media_driver_sectors =          1;
+        media_ptr -> fx_media_driver_sector_type =      FX_UNKNOWN_SECTOR;
+        media_ptr -> fx_media_hidden_sectors =          0;
+
+        /* Invoke the driver to read the sector.  */
+        (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+        /* Determine if the sector was read correctly. */
+        if (media_ptr -> fx_media_driver_status != FX_SUCCESS)
+        {
+
+            /* Return error.  */
+            return(FX_IO_ERROR);
+        }
+
+        /* Check signature to make sure the sector is valid.  */
+        if ((partition_sector_ptr[510] != FX_SIG_BYTE_1) || (partition_sector_ptr[511] != FX_SIG_BYTE_2))
+        {
+
+            /* Invalid, return an error.  */
+            return(FX_NOT_FOUND);
+        }
+
+        /* Determine if this is the desired partition.  */
+        if (i == partition)
+        {
+
+            /* Get partition type.  */
+            partition_type =  partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_TYPE_OFFSET];
+            if (partition_type != FX_PARTITION_TYPE_FREE)
+            {
+
+                /* Return the partition starting sector, if non-NULL.  */
+                if (partition_start != FX_NULL)
+                {
+                    *partition_start = _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_LBA_OFFSET]) + base_sector;
+                }
+
+                /* Return the partition size, if non-NULL.  */
+                if (partition_size != FX_NULL)
+                {
+                    *partition_size =  _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_SECTORS_OFFSET]);
+                }
+
+                /* Return success!  */
+                return(FX_SUCCESS);
+            }
+            else
+            {
+                /* Not partition here.  */
+                return(FX_NOT_FOUND);
+            }
+        }
+        else
+        {
+
+            /* Get partition type.  */
+            partition_type =  partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_ENTRY_SIZE + FX_PARTITION_TYPE_OFFSET];
+            if (partition_type == FX_PARTITION_TYPE_EXTENDED || partition_type == FX_PARTITION_TYPE_EXTENDED_LBA)
+            {
+
+                /* Update sector number for next partition table.  */
+                base_sector =  _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_ENTRY_SIZE + FX_PARTITION_LBA_OFFSET]) + base_sector_extended;
+            }
+            else
+            {
+                /* No valid partition, get out of the loop.  */
+                break;
+            }
+        }
+
+    }
+
+    /* Return error.  */
+    return(FX_NOT_FOUND);
+}
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_system_initialize.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_system_initialize.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_system_initialize.c	(revision 54)
@@ -0,0 +1,207 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   System                                                              */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Locate FileX control component data in this file.  */
+
+#define FX_SYSTEM_INIT
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_system_initialize                               PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function initializes the various control data structures for   */
+/*    the FileX System component.                                         */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    tx_timer_create                       Create system timer           */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Initialization                                          */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s), and      */
+/*                                            added conditional to        */
+/*                                            disable build options,      */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _fx_system_initialize(VOID)
+{
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    FX_TRACE_IN_LINE_INSERT(FX_TRACE_SYSTEM_INITIALIZE, 0, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+    /* Initialize the head pointer of the opened media list and the
+       number of opened media.  */
+    _fx_system_media_opened_ptr =       FX_NULL;
+    _fx_system_media_opened_count =     0;
+
+    /* Initialize the time and date fields with their default values.  */
+    _fx_system_date =   FX_INITIAL_DATE;
+    _fx_system_time =   FX_INITIAL_TIME;
+
+    /* Initialize the sector and FAT cache sizes.  */
+    _fx_system_media_max_sector_cache =  FX_MAX_SECTOR_CACHE;
+    _fx_system_media_max_fat_cache =     FX_MAX_FAT_CACHE;
+
+    /* Create the FileX system timer.  This is responsible for updating
+       the specified date and time at the rate specified by
+       FX_UPDATE_RATE_IN_TICKS.  Note that the timer is not necessary for
+       regular FileX operation - it is only needed for accurate system
+       date and time stamps on files.  */
+
+#ifndef FX_NO_TIMER
+    tx_timer_create(&_fx_system_timer, "FileX System Timer", _fx_system_timer_entry, FX_TIMER_ID,
+                    FX_UPDATE_RATE_IN_TICKS, FX_UPDATE_RATE_IN_TICKS, TX_AUTO_ACTIVATE);
+#endif
+
+#ifndef FX_DISABLE_BUILD_OPTIONS
+    /* Setup the build options variables.  */
+
+    /* Setup the first build options variable.  */
+    if (FX_MAX_LONG_NAME_LEN > 0xFF)
+    {
+        _fx_system_build_options_1 =  _fx_system_build_options_1 | (((ULONG)0xFF) << 24);
+    }
+    else
+    {
+        _fx_system_build_options_1 =  _fx_system_build_options_1 | (((ULONG)(FX_MAX_LONG_NAME_LEN & 0xFF)) << 24);
+    }
+    if (FX_MAX_LAST_NAME_LEN > 0xFF)
+    {
+        _fx_system_build_options_1 =  _fx_system_build_options_1 | (((ULONG)0xFF) << 16);
+    }
+    else
+    {
+        _fx_system_build_options_1 =  _fx_system_build_options_1 | (((ULONG)(FX_MAX_LAST_NAME_LEN & 0xFF)) << 24);
+    }
+
+#ifdef FX_NO_TIMER
+    _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 10);
+#endif
+#ifdef FX_SINGLE_THREAD
+    _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 9);
+#endif
+#ifdef FX_DONT_UPDATE_OPEN_FILES
+    _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 8);
+#endif
+#ifdef FX_MEDIA_DISABLE_SEARCH_CACHE
+    _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 7);
+#endif
+#ifdef FX_MEDIA_STATISTICS_DISABLE
+    _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 6);
+#endif
+
+#ifdef FX_SINGLE_OPEN_LEGACY
+    _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 4);
+#endif
+#ifdef FX_RENAME_PATH_INHERIT
+    _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 3);
+#endif
+#ifdef FX_NO_LOCAL_PATH
+    _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 2);
+#endif
+#ifdef FX_FAULT_TOLERANT_DATA
+    _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 1);
+#endif
+#ifdef FX_FAULT_TOLERANT
+    _fx_system_build_options_1 = _fx_system_build_options_1 | ((ULONG)1);
+#endif
+
+    /* Setup the second build options variable.  */
+    if (FX_MAX_SECTOR_CACHE > ((ULONG)0xFFFF))
+    {
+        _fx_system_build_options_2 =  _fx_system_build_options_2 | (((ULONG)0xFFFF) << 16);
+    }
+    else
+    {
+        _fx_system_build_options_2 =  _fx_system_build_options_2 | (((ULONG)FX_MAX_SECTOR_CACHE) << 16);
+    }
+    if (FX_FAT_MAP_SIZE > 0xFF)
+    {
+        _fx_system_build_options_2 =  _fx_system_build_options_2 | (((ULONG)0xFF) << 8);
+    }
+    else
+    {
+        _fx_system_build_options_2 =  _fx_system_build_options_2 | (((ULONG)FX_FAT_MAP_SIZE) << 8);
+    }
+    if (FX_MAX_FAT_CACHE > 0xFF)
+    {
+        _fx_system_build_options_2 =  _fx_system_build_options_2 | ((ULONG)0xFF);
+    }
+    else
+    {
+        _fx_system_build_options_2 =  _fx_system_build_options_2 | ((ULONG)FX_MAX_FAT_CACHE);
+    }
+
+    /* Setup the third build options variable.  */
+    if (FX_UPDATE_RATE_IN_SECONDS > 0xFF)
+    {
+        _fx_system_build_options_3 =  _fx_system_build_options_3 | (((ULONG)0xFF) << 16);
+    }
+    else
+    {
+        _fx_system_build_options_3 =  _fx_system_build_options_3 | (((ULONG)FX_UPDATE_RATE_IN_SECONDS) << 16);
+    }
+    if (FX_UPDATE_RATE_IN_TICKS > ((ULONG)0xFFFF))
+    {
+        _fx_system_build_options_3 =  _fx_system_build_options_3 | ((ULONG)0xFFFF);
+    }
+    else
+    {
+        _fx_system_build_options_3 =  _fx_system_build_options_3 | ((ULONG)FX_UPDATE_RATE_IN_TICKS);
+    }
+#endif /* FX_DISABLE_BUILD_OPTIONS */
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_system_timer_entry.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_system_timer_entry.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_system_timer_entry.c	(revision 54)
@@ -0,0 +1,343 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   System                                                              */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_system_timer_entry                              PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function is FileX system timer function.  It is called at the  */
+/*    rate specified by FX_UPDATE_RATE_IN_SECONDS and is responsible for  */
+/*    maintaining both the system date and time.                          */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    id                                    Not used                      */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Initialization                                          */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID    _fx_system_timer_entry(ULONG id)
+{
+
+UINT second;
+UINT minute;
+UINT hour;
+UINT day;
+UINT month;
+UINT year;
+
+
+    /* Determine if the ID is valid.  */
+    if (id == FX_TIMER_ID)
+    {
+
+        /* Break the current date time into separate fields for easier work!  */
+        second =  (_fx_system_time & FX_SECOND_MASK) * 2;
+        minute =  (_fx_system_time >> FX_MINUTE_SHIFT) & FX_MINUTE_MASK;
+        hour =    (_fx_system_time >> FX_HOUR_SHIFT) & FX_HOUR_MASK;
+        day =     _fx_system_date & FX_DAY_MASK;
+        month =   (_fx_system_date >> FX_MONTH_SHIFT) & FX_MONTH_MASK;
+        year =    ((_fx_system_date >> FX_YEAR_SHIFT) & FX_YEAR_MASK) + FX_BASE_YEAR;
+
+        /* Now apply the "second" update.  */
+        second =  second + FX_UPDATE_RATE_IN_SECONDS;
+
+        /* Determine if we need to adjust the minute field.  */
+        if (second > FX_MAXIMUM_SECOND)
+        {
+
+            /* Yes, we need to adjust the minute field.  */
+            minute =  minute + second / 60;
+            second =  second % 60;
+
+            /* Determine if we need to adjust the hour field.  */
+            if (minute > FX_MAXIMUM_MINUTE)
+            {
+
+                /* Yes, we need to adjust the hour field.  */
+                hour =    hour + minute / 60;
+                minute =  minute % 60;
+
+                /* Determine if we need to adjust the day field.  */
+                if (hour > FX_MAXIMUM_HOUR)
+                {
+
+                    /* Yes, we need to adjust the day field.  */
+                    hour =  0;
+                    day++;
+
+                    /* Determine if we need to adjust the month field.  */
+                    switch (month)
+                    {
+
+                    case 1:                 /* January  */
+                    {
+
+                        /* Check for end of the month.  */
+                        if (day > 31)
+                        {
+
+                            /* Move to next month.  */
+                            day = 1;
+                            month++;
+                        }
+                        break;
+                    }
+
+                    case 2:                 /* February  */
+                    {
+
+                        /* Check for leap year.  We don't need to check for leap
+                           century her (century years divisible by 400) since 2000
+                           is and this FAT format only supports years to 2107. */
+                        if ((year % 4) == 0)
+                        {
+
+                            /* Leap year in February... check for 29 days
+                               instead of 28.  */
+                            if (day > 29)
+                            {
+
+                                /* Adjust the month.  */
+                                day =  1;
+                                month++;
+                            }
+                        }
+                        else
+                        {
+
+                            if (day > 28)
+                            {
+
+                                /* Adjust the month.  */
+                                day = 1;
+                                month++;
+                            }
+                        }
+                        break;
+                    }
+
+                    case 3:                 /* March  */
+                    {
+
+                        /* Check for end of the month.  */
+                        if (day > 31)
+                        {
+
+                            /* Move to next month.  */
+                            day = 1;
+                            month++;
+                        }
+                        break;
+                    }
+
+                    case 4:                 /* April  */
+                    {
+
+                        /* Check for end of the month.  */
+                        if (day > 30)
+                        {
+
+                            /* Move to next month.  */
+                            day = 1;
+                            month++;
+                        }
+                        break;
+                    }
+
+                    case 5:                 /* May  */
+                    {
+
+                        /* Check for end of the month.  */
+                        if (day > 31)
+                        {
+
+                            /* Move to next month.  */
+                            day = 1;
+                            month++;
+                        }
+                        break;
+                    }
+
+                    case 6:                 /* June */
+                    {
+
+                        /* Check for end of the month.  */
+                        if (day > 30)
+                        {
+
+                            /* Move to next month.  */
+                            day = 1;
+                            month++;
+                        }
+                        break;
+                    }
+
+                    case 7:                 /* July */
+                    {
+
+                        /* Check for end of the month.  */
+                        if (day > 31)
+                        {
+
+                            /* Move to next month.  */
+                            day = 1;
+                            month++;
+                        }
+                        break;
+                    }
+
+                    case 8:                 /* August */
+                    {
+
+                        /* Check for end of the month.  */
+                        if (day > 31)
+                        {
+
+                            /* Move to next month.  */
+                            day = 1;
+                            month++;
+                        }
+                        break;
+                    }
+
+                    case 9:                 /* September */
+                    {
+
+                        /* Check for end of the month.  */
+                        if (day > 30)
+                        {
+
+                            /* Move to next month.  */
+                            day = 1;
+                            month++;
+                        }
+                        break;
+                    }
+
+                    case 10:                /* October */
+                    {
+
+                        /* Check for end of the month.  */
+                        if (day > 31)
+                        {
+
+                            /* Move to next month.  */
+                            day = 1;
+                            month++;
+                        }
+                        break;
+                    }
+
+                    case 11:                /* November */
+                    {
+
+                        /* Check for end of the month.  */
+                        if (day > 30)
+                        {
+
+                            /* Move to next month.  */
+                            day = 1;
+                            month++;
+                        }
+                        break;
+                    }
+
+                    case 12:                /* December */
+                    {
+
+                        /* Check for end of the month.  */
+                        if (day > 31)
+                        {
+
+                            /* Move to next month.  */
+                            day = 1;
+                            month = 1;
+
+                            /* Also move to next year.  */
+                            year++;
+
+                            /* Check for a year that exceeds the representation
+                               in this format.  */
+                            if (year > FX_MAXIMUM_YEAR)
+                            {
+                                return;
+                            }
+                        }
+                        break;
+                    }
+
+                    default:                /* Invalid month!  */
+
+                        return;             /* Skip updating date/time!  */
+                    }
+                }
+            }
+        }
+
+        /* Now apply the new setting to the internal representation.  */
+
+        /* Set the system date.  */
+        _fx_system_date =  ((year - FX_BASE_YEAR) << FX_YEAR_SHIFT) |
+                            (month << FX_MONTH_SHIFT) | day;
+
+        /* Set the new system time.  */
+        _fx_system_time  =  (hour << FX_HOUR_SHIFT) |
+                            (minute << FX_MINUTE_SHIFT) | (second / 2);
+    }
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_16_unsigned_read.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_16_unsigned_read.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_16_unsigned_read.c	(revision 54)
@@ -0,0 +1,85 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Utility                                                             */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+#include "fx_utility.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_utility_16_unsigned_read                        PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function reads (with endian awareness) 16-bit unsigned data    */
+/*    from the specified source and returns the value to the caller.      */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    source_ptr                            Source memory pointer         */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    UINT value                                                          */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    FileX System Functions                                              */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _fx_utility_16_unsigned_read(UCHAR *source_ptr)
+{
+
+UINT value;
+
+    /* Pickup the UINT from the destination with endian-awareness.  */
+    value =  ((((UINT)*(source_ptr + 1)) & 0xFF) << 8) |
+              ((UINT)*(source_ptr) & 0xFF);
+
+    /* Return value to caller.  */
+    return(value);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_16_unsigned_write.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_16_unsigned_write.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_16_unsigned_write.c	(revision 54)
@@ -0,0 +1,81 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Utility                                                             */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+#include "fx_utility.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_utility_16_unsigned_write                       PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function writes (with endian awareness) 16-bit unsigned data   */
+/*    to the specified destination.                                       */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    dest_ptr                              Destination memory pointer    */
+/*    value                                 Value to write to memory      */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    FileX System Functions                                              */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _fx_utility_16_unsigned_write(UCHAR *dest_ptr, UINT value)
+{
+
+    /* Store the UINT into the destination with endian-awareness.  */
+    *(dest_ptr) =       (UCHAR)(value & 0xFF);
+    *(dest_ptr + 1) =   (UCHAR)((value >> 8) & 0xFF);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_32_unsigned_read.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_32_unsigned_read.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_32_unsigned_read.c	(revision 54)
@@ -0,0 +1,87 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Utility                                                             */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+#include "fx_utility.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_utility_32_unsigned_read                        PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function reads (with endian awareness) a 32-bit unsigned data  */
+/*    from the specified source and returns the value to the caller.      */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    source_ptr                            Source memory pointer         */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    ULONG value                                                         */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    FileX System Functions                                              */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+ULONG  _fx_utility_32_unsigned_read(UCHAR *source_ptr)
+{
+
+ULONG value;
+
+    /* Pickup the UINT from the destination with endian-awareness.  */
+    value =  ((((ULONG) *(source_ptr+3)) & 0xFF) << 24) |
+             ((((ULONG) *(source_ptr+2)) & 0xFF) << 16) |
+             ((((ULONG) *(source_ptr+1)) & 0xFF) << 8)  |
+              (((ULONG) *(source_ptr)) & 0xFF);
+
+    /* Return value to caller.  */
+    return(value);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_32_unsigned_write.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_32_unsigned_write.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_32_unsigned_write.c	(revision 54)
@@ -0,0 +1,83 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Utility                                                             */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+#include "fx_utility.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_utility_32_unsigned_write                       PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function writes (with endian awareness) a 32-bit unsigned data */
+/*    type to the specified destination.                                  */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    dest_ptr                              Destination memory pointer    */
+/*    value                                 Value to write to memory      */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    FileX System Functions                                              */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _fx_utility_32_unsigned_write(UCHAR *dest_ptr, ULONG value)
+{
+
+    /* Store the UINT into the destination with endian-awareness.  */
+    *(dest_ptr) =       (UCHAR)(value & 0xFF);
+    *(dest_ptr + 1) =   (UCHAR)((value >> 8) & 0xFF);
+    *(dest_ptr + 2) =   (UCHAR)((value >> 16) & 0xFF);
+    *(dest_ptr + 3) =   (UCHAR)((value >> 24) & 0xFF);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_FAT_entry_read.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_FAT_entry_read.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_FAT_entry_read.c	(revision 54)
@@ -0,0 +1,450 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Utility                                                             */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+#include "fx_utility.h"
+#ifdef FX_ENABLE_FAULT_TOLERANT
+#include "fx_fault_tolerant.h"
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_utility_FAT_entry_read                          PORTABLE C      */
+/*                                                           6.2.0        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function reads the supplied FAT entry from the first FAT of    */
+/*    the media.  12-bit, 16-bit, and 32-bit FAT reading is supported.    */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    media_ptr                             Media control block pointer   */
+/*    cluster                               Cluster entry number          */
+/*    entry_ptr                             Pointer to destination for    */
+/*                                            the FAT entry               */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    return status                                                       */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _fx_utility_16_unsigned_read          Read a UINT from FAT buffer   */
+/*    _fx_utility_32_unsigned_read          Read a ULONG form FAT buffer  */
+/*    _fx_utility_FAT_flush                 Flush FAT entry cache         */
+/*    _fx_utility_logical_sector_read       Read FAT sector into memory   */
+/*    _fx_fault_tolerant_read_FAT           Read FAT entry from log file  */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    FileX System Functions                                              */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s), and      */
+/*                                            added conditional to        */
+/*                                            disable fat entry refresh,  */
+/*                                            resulting in version 6.1    */
+/*  10-31-2022     Tiejun Zhou              Modified comment(s), and      */
+/*                                            fixed compiler warning,     */
+/*                                            resulting in version 6.2.0  */
+/*                                                                        */
+/**************************************************************************/
+UINT  _fx_utility_FAT_entry_read(FX_MEDIA *media_ptr, ULONG cluster, ULONG *entry_ptr)
+{
+
+ULONG               FAT_sector;
+ULONG               byte_offset, entry32;
+UCHAR              *FAT_ptr;
+UINT                entry, index;
+UINT                status;
+FX_FAT_CACHE_ENTRY *cache_entry_ptr;
+#ifndef FX_DISABLE_FAT_ENTRY_REFRESH
+FX_FAT_CACHE_ENTRY  temp_cache_entry;
+#endif /* FX_DISABLE_FAT_ENTRY_REFRESH */
+
+
+#ifdef FX_ENABLE_FAULT_TOLERANT
+    if (media_ptr -> fx_media_fault_tolerant_enabled &&
+        (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED))
+    {
+
+        /* Redirect this request to log file. */
+        status = _fx_fault_tolerant_read_FAT(media_ptr, cluster, entry_ptr, FX_FAULT_TOLERANT_FAT_LOG_TYPE);
+
+        /* Return on success. */
+        if (status != FX_READ_CONTINUE)
+        {
+            return(status);
+        }
+    }
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+    /* Increment the number of FAT entry reads and cache hits.  */
+    media_ptr -> fx_media_fat_entry_reads++;
+    media_ptr -> fx_media_fat_entry_cache_read_hits++;
+#endif
+
+    /* Extended port-specific processing macro, which is by default defined to white space.  */
+    FX_UTILITY_FAT_ENTRY_READ_EXTENSION
+
+    /* Calculate the area of the cache for this FAT entry.  */
+    index =  (cluster & FX_FAT_CACHE_HASH_MASK) * FX_FAT_CACHE_DEPTH;
+
+    /* Build a pointer to the cache entry.  */
+    cache_entry_ptr =  &media_ptr -> fx_media_fat_cache[index];
+
+#ifndef FX_DISABLE_FAT_ENTRY_REFRESH
+    /* Determine if the FAT entry is in the cache - assuming the depth of the FAT cache is
+       4 entries.  */
+    if ((cache_entry_ptr -> fx_fat_cache_entry_cluster) == cluster)
+    {
+
+        /* Yes, return the cached value.  */
+        *entry_ptr =  cache_entry_ptr -> fx_fat_cache_entry_value;
+
+        /* Don't move anything since we found the entry.  */
+
+        /* Return a successful status.  */
+        return(FX_SUCCESS);
+    }
+    else if (((cache_entry_ptr + 1) -> fx_fat_cache_entry_cluster) == cluster)
+    {
+
+        /* Yes, return the cached value.  */
+        *entry_ptr =  (cache_entry_ptr + 1) -> fx_fat_cache_entry_value;
+
+        /* Just swap the first and second entry.  */
+        temp_cache_entry =        *(cache_entry_ptr);
+        *(cache_entry_ptr) =      *(cache_entry_ptr + 1);
+        *(cache_entry_ptr + 1) =  temp_cache_entry;
+
+        /* Return a successful status.  */
+        return(FX_SUCCESS);
+    }
+    else if (((cache_entry_ptr + 2) -> fx_fat_cache_entry_cluster) == cluster)
+    {
+
+        /* Yes, return the cached value.  */
+        *entry_ptr =  (cache_entry_ptr + 2) -> fx_fat_cache_entry_value;
+
+        /* Move the third entry to the top and the first two entries down.  */
+        temp_cache_entry =        *(cache_entry_ptr);
+        *(cache_entry_ptr) =      *(cache_entry_ptr + 2);
+        *(cache_entry_ptr + 2) =  *(cache_entry_ptr + 1);
+        *(cache_entry_ptr + 1) =  temp_cache_entry;
+
+        /* Return a successful status.  */
+        return(FX_SUCCESS);
+    }
+    else if (((cache_entry_ptr + 3) -> fx_fat_cache_entry_cluster) == cluster)
+    {
+
+        /* Yes, return the cached value.  */
+        *entry_ptr =  (cache_entry_ptr + 3) -> fx_fat_cache_entry_value;
+
+        /* Move the last entry to the top and the first three entries down.  */
+        temp_cache_entry =        *(cache_entry_ptr);
+        *(cache_entry_ptr) =      *(cache_entry_ptr + 3);
+        *(cache_entry_ptr + 3) =  *(cache_entry_ptr + 2);
+        *(cache_entry_ptr + 2) =  *(cache_entry_ptr + 1);
+        *(cache_entry_ptr + 1) =  temp_cache_entry;
+
+        /* Return a successful status.  */
+        return(FX_SUCCESS);
+    }
+#else
+    for (UINT i = 0; i < 4; i++)
+    {
+        if (((cache_entry_ptr + i) -> fx_fat_cache_entry_cluster) == cluster)
+        {
+            *entry_ptr =  (cache_entry_ptr + i) -> fx_fat_cache_entry_value;
+
+            /* Return a successful status.  */
+            return(FX_SUCCESS);
+        }
+    }
+#endif /* FX_DISABLE_FAT_ENTRY_REFRESH */
+
+    /* Determine if the oldest entry was modified, i.e. whether or not it is
+       dirty.  */
+    if (media_ptr -> fx_media_fat_cache[index + 3].fx_fat_cache_entry_dirty)
+    {
+
+        /* Yes, the entry is dirty and needs to be flushed out.  */
+        status = _fx_utility_FAT_flush(media_ptr);
+
+        /* Check for completion status.  */
+        if (status != FX_SUCCESS)
+        {
+
+            /* Return error status.  */
+            return(status);
+        }
+    }
+
+    /* If we get here, the entry was not found in the FAT entry cache.  We need to
+       actually read the FAT entry.  */
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+    /* Decrement the number of cache hits.  */
+    media_ptr -> fx_media_fat_entry_cache_read_hits--;
+
+    /* Increment the number of cache misses.  */
+    media_ptr -> fx_media_fat_entry_cache_read_misses++;
+#endif
+
+    /* Determine which type of FAT is present.  */
+    if (media_ptr -> fx_media_12_bit_FAT)
+    {
+
+        /* Calculate the byte offset to the cluster entry.  */
+        byte_offset =  (((ULONG)cluster << 1) + cluster) >> 1;
+
+        /* Calculate the FAT sector the requested FAT entry resides in.  */
+        FAT_sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
+            (ULONG)media_ptr -> fx_media_reserved_sectors;
+
+        /* Read the sector in.  */
+        status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector,
+                                                  media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
+
+        /* Determine if an error occurred.  */
+        if (status != FX_SUCCESS)
+        {
+            /* Return the error status.  */
+            return(status);
+        }
+
+        /* Now calculate the byte offset into this FAT sector.  */
+        byte_offset =  byte_offset -
+            ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) *
+             media_ptr -> fx_media_bytes_per_sector);
+
+        /* Setup a pointer into the buffer.  */
+        FAT_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset;
+
+        /* Determine if the cluster entry is odd or even.  */
+        if (cluster & 1)
+        {
+
+            /* Odd cluster number.  */
+
+            /* Pickup the lower nibble of the FAT entry.  */
+            entry =  (((UINT)*FAT_ptr) & 0xF0) >> 4;
+
+            /* Move to the next byte of the FAT entry.  */
+            FAT_ptr++;
+
+            /* Determine if we are now past the end of the FAT buffer in memory.  */
+            if (byte_offset == (ULONG)(media_ptr -> fx_media_bytes_per_sector - 1))
+            {
+
+                /* Yes, we need to read the next sector.  */
+                FAT_sector++;
+                status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector,
+                                                          media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
+
+                /* Determine if an error occurred.  */
+                if (status != FX_SUCCESS)
+                {
+
+                    /* Return the error status.  */
+                    return(status);
+                }
+
+                /* Setup a pointer into the buffer.  */
+                FAT_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer;
+            }
+
+            /* Pickup the upper 8 bits of the FAT entry.  */
+            entry =  entry | (((UINT)*FAT_ptr) << 4);
+        }
+        else
+        {
+
+            /* Even cluster number.  */
+
+            /* Pickup the lower byte of the FAT entry.  */
+            entry =  (UINT)(((UINT)*FAT_ptr) & 0xFF);
+
+            /* Move to the next nibble of the FAT entry.  */
+            FAT_ptr++;
+
+            /* Determine if we are now past the end of the FAT buffer in memory.  */
+            if (byte_offset == (ULONG)(media_ptr -> fx_media_bytes_per_sector - 1))
+            {
+
+                /* Yes, we need to read the next sector.  */
+                FAT_sector++;
+                status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector,
+                                                          media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
+
+                /* Determine if an error occurred.  */
+                if (status != FX_SUCCESS)
+                {
+                    return(status);
+                }
+
+                /* Setup a pointer into the buffer.  */
+                FAT_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer;
+            }
+
+            /* Pickup the upper 4 bits of the FAT entry.  */
+            entry =  entry | ((((UINT)*FAT_ptr) & 0x0F) << 8);
+        }
+
+        /* Determine if we need to do sign extension on the 12-bit eof value.  */
+        if (entry >= FX_MAX_12BIT_CLUST)
+        {
+
+            /* Yes, we need to sign extend.  */
+            entry =  entry | FX_SIGN_EXTEND;
+        }
+
+        *entry_ptr =  entry;
+    }
+
+    /* Check for a 16-bit FAT.  */
+#ifdef FX_ENABLE_EXFAT
+    else if (FX_FAT16  == media_ptr -> fx_media_FAT_type)
+#else
+    else if (!media_ptr -> fx_media_32_bit_FAT)
+#endif /* FX_ENABLE_EXFAT */
+    {
+
+        /* 16-bit FAT is present.  */
+
+        /* Calculate the byte offset to the cluster entry.  */
+        byte_offset =  (((ULONG)cluster) * 2);
+
+        /* Calculate the FAT sector the requested FAT entry resides in.  */
+        FAT_sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
+            (ULONG)media_ptr -> fx_media_reserved_sectors;
+
+        /* Read the FAT sector.  */
+        status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector,
+                                                  media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
+
+        /* Determine if an error occurred.  */
+        if (status != FX_SUCCESS)
+        {
+
+            /* Return the error code.  */
+            return(status);
+        }
+
+        /* Now calculate the byte offset into this FAT sector.  */
+        byte_offset =  byte_offset -
+            ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) *
+             media_ptr -> fx_media_bytes_per_sector);
+
+        /* Setup a pointer into the buffer.  */
+        FAT_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset;
+
+        /* Pickup the FAT entry.  */
+        entry =  _fx_utility_16_unsigned_read(FAT_ptr);
+
+        *entry_ptr =  entry;
+    }
+#ifdef FX_ENABLE_EXFAT
+    else if ((media_ptr -> fx_media_FAT_type == FX_FAT32) ||
+             (media_ptr -> fx_media_FAT_type == FX_exFAT))
+#else
+    else
+#endif /* FX_ENABLE_EXFAT */
+    {
+
+        /* Otherwise, a 32 bit FAT present.  */
+        byte_offset =  (((ULONG)cluster) * 4);
+
+        /* Calculate the FAT sector the requested FAT entry resides in.  */
+        FAT_sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
+            (ULONG)media_ptr -> fx_media_reserved_sectors;
+
+        /* Calculate the byte offset to the FAT entry.  */
+        byte_offset = (byte_offset % media_ptr -> fx_media_bytes_per_sector);
+
+        /* Read the appropriate FAT sector.  */
+        status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector,
+                                                  media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
+
+        /* Determine if an error occurred.  */
+        if (status != FX_SUCCESS)
+        {
+
+            /* Return the error code.  */
+            return(status);
+        }
+
+        /* Setup a pointer into the buffer.  */
+        FAT_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer + (ULONG)byte_offset;
+
+        /* Pickup the FAT entry.  */
+        entry32 =  _fx_utility_32_unsigned_read(FAT_ptr);
+
+#ifdef FX_ENABLE_EXFAT
+        /* FAT32 uses 28 bit cluster addressing but  exFAT uses 32 bit.  */
+        if (media_ptr -> fx_media_FAT_type == FX_FAT32)
+        {
+#endif /* FX_ENABLE_EXFAT */
+
+            /* Clear upper nibble.  */
+            entry32 = entry32 & 0x0FFFFFFF;
+#ifdef FX_ENABLE_EXFAT
+        }
+#endif /* FX_ENABLE_EXFAT */
+
+        *entry_ptr =  entry32;
+    }
+
+    /* Move all the cache entries down so the oldest is at the bottom.  */
+    *(cache_entry_ptr + 3) =  *(cache_entry_ptr + 2);
+    *(cache_entry_ptr + 2) =  *(cache_entry_ptr + 1);
+    *(cache_entry_ptr + 1) =  *(cache_entry_ptr);
+
+    /* Setup the new FAT entry in the cache.  */
+    cache_entry_ptr -> fx_fat_cache_entry_cluster =  cluster;
+    cache_entry_ptr -> fx_fat_cache_entry_value   =  *entry_ptr;
+
+    /* Return success to the caller.  */
+    return(FX_SUCCESS);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_FAT_flush.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_FAT_flush.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_FAT_flush.c	(revision 54)
@@ -0,0 +1,564 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Utility                                                             */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+#include "fx_utility.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_utility_FAT_flush                               PORTABLE C      */
+/*                                                           6.1.2        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function flushes the contents of the FAT cache to the media.   */
+/*    12-bit, 16-bit and 32-bit FAT writing is supported.                 */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    media_ptr                             Media control block pointer   */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    return status                                                       */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _fx_utility_16_unsigned_write         Write a UINT into buffer      */
+/*    _fx_utility_32_unsigned_read          Read a ULONG from buffer      */
+/*    _fx_utility_32_unsigned_write         Write a ULONG into buffer     */
+/*    _fx_utility_logical_sector_read       Read FAT sector into memory   */
+/*    _fx_utility_logical_sector_write      Write FAT sector back to disk */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    FileX System Functions                                              */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*  11-09-2020     William E. Lamie         Modified comment(s),          */
+/*                                            updated logic for           */
+/*                                            FAT secondary update map,   */
+/*                                            resulting in version 6.1.2  */
+/*                                                                        */
+/**************************************************************************/
+UINT  _fx_utility_FAT_flush(FX_MEDIA *media_ptr)
+{
+
+ULONG  FAT_sector;
+ULONG  byte_offset;
+UCHAR *FAT_ptr;
+UINT   temp, i;
+UINT   status, index, ind;
+ULONG  cluster, next_cluster;
+UCHAR  sectors_per_bit;
+INT    multi_sector_entry;
+ULONG  sector;
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+    /* Increment the number of cache flush requests.  */
+    media_ptr -> fx_media_fat_cache_flushes++;
+#endif
+
+    /* Loop through the media's FAT cache and flush out dirty entries.  */
+    for (index = 0; index < FX_MAX_FAT_CACHE; index++)
+    {
+
+        /* Determine if the entry is dirty.  */
+        if ((media_ptr -> fx_media_fat_cache[index].fx_fat_cache_entry_dirty) == 0)
+        {
+
+            /* No, just advance to the next entry.  */
+            continue;
+        }
+
+        /* Otherwise, the entry is indeed dirty and must be flushed out.  Process
+           relative to the type of FAT that is being used.  */
+
+        /* Pickup the contents of the FAT cache entry.  */
+        cluster =       media_ptr -> fx_media_fat_cache[index].fx_fat_cache_entry_cluster;
+
+        /* Determine which type of FAT is present.  */
+#ifdef FX_ENABLE_EXFAT
+        if (media_ptr -> fx_media_FAT_type == FX_FAT12)
+#else
+        if (media_ptr -> fx_media_12_bit_FAT)
+#endif /* FX_ENABLE_EXFAT */
+        {
+
+            /* Calculate the byte offset to the cluster entry.  */
+            byte_offset =  (((ULONG)cluster << 1) + cluster) >> 1;
+
+            /* Calculate the FAT sector the requested FAT entry resides in.  */
+            FAT_sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
+                (ULONG)media_ptr -> fx_media_reserved_sectors;
+
+            /* Initialize as not written.  */
+            multi_sector_entry = -1;
+
+            for (;;)
+            {
+
+                /* Pickup the FAT sector.  */
+                status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector,
+                                                          media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
+
+                /* Determine if an error occurred.  */
+                if (status != FX_SUCCESS)
+                {
+
+                    /* Return the error status.  */
+                    return(status);
+                }
+
+                /* Determine if a mulit-sector FAT update is present.  */
+                if (multi_sector_entry != -1)
+                {
+
+                    /* Yes, store the remaining portion of the new FAT entry in the
+                       next FAT sector.  */
+
+                    /* Setup a pointer into the buffer.  */
+                    FAT_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer;
+
+                    /* Pickup the cluster and next cluster.  */
+                    cluster = (media_ptr -> fx_media_fat_cache[multi_sector_entry].fx_fat_cache_entry_cluster);
+                    next_cluster = media_ptr -> fx_media_fat_cache[multi_sector_entry].fx_fat_cache_entry_value;
+
+                    /* Determine if the cluster entry is odd or even.  */
+                    if (cluster & 1)
+                    {
+
+                        /* Store the upper 8 bits of the FAT entry.  */
+                        *FAT_ptr =  (UCHAR)((next_cluster >> 4) & 0xFF);
+                    }
+                    else
+                    {
+
+                        /* Store the upper 4 bits of the FAT entry.  */
+                        temp =  ((UINT)*FAT_ptr) & 0xF0;
+                        *FAT_ptr =  (UCHAR)(temp | ((next_cluster >> 8) & 0xF));
+                    }
+
+                    /* Clear the multi-sector flag.  */
+                    multi_sector_entry = -1;
+                }
+
+                /* Loop through the remainder of the cache to check for multiple entries
+                   within the same FAT sector being written out.  */
+                for (i = index; i < FX_MAX_FAT_CACHE; i++)
+                {
+
+                    /* Is the cache entry dirty?  */
+                    if ((media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty) == 0)
+                    {
+
+                        /* Not dirty, does not need to be flushed.  */
+                        continue;
+                    }
+
+                    /* Isolate the cluster.  */
+                    cluster = (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_cluster);
+
+                    /* Calculate the byte offset to the cluster entry.  */
+                    byte_offset =  (((ULONG)cluster << 1) + cluster) >> 1;
+
+                    /* Pickup the sector.  */
+                    sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
+                        (ULONG)media_ptr -> fx_media_reserved_sectors;
+
+                    /* Is it the current FAT sector?  */
+                    if (sector != FAT_sector)
+                    {
+
+                        /* Different FAT sector - not in this pass of the loop.  */
+                        continue;
+                    }
+
+                    /* Pickup new value for this FAT entry.  */
+                    next_cluster =  media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_value;
+
+                    /* Now calculate the byte offset into this FAT sector.  */
+                    byte_offset =  byte_offset -
+                        ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) *
+                         media_ptr -> fx_media_bytes_per_sector);
+
+                    /* Determine if we are now past the end of the FAT buffer in memory.  */
+                    if (byte_offset == (ULONG)(media_ptr -> fx_media_bytes_per_sector - 1))
+                    {
+
+                        /* Yes, we need to read the next sector */
+                        multi_sector_entry = (INT)i;
+                    }
+
+                    /* Setup a pointer into the buffer.  */
+                    FAT_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset;
+
+                    /* Clear the dirty flag.  */
+                    media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty = 0;
+
+                    /* Determine if the cluster entry is odd or even.  */
+                    if (cluster & 1)
+                    {
+
+                        /* Odd cluster number.  */
+
+                        /* Pickup the upper nibble of the FAT entry.  */
+
+                        /* First, set the lower nibble of the FAT entry.  */
+                        temp =      (((UINT)*FAT_ptr) & 0x0F);
+                        *FAT_ptr =  (UCHAR)(temp | ((next_cluster << 4) & 0xF0));
+
+                        /* Determine if this is a mulit-sector entry.  */
+                        if ((multi_sector_entry) == (INT)i)
+                        {
+
+                            /* Yes, requires multiple sector - will write rest of the part later.  */
+                            continue;
+                        }
+
+                        /* Move to the next byte of the FAT entry.  */
+                        FAT_ptr++;
+
+                        /* Store the upper 8 bits of the FAT entry.  */
+                        *FAT_ptr =  (UCHAR)((next_cluster >> 4) & 0xFF);
+                    }
+                    else
+                    {
+
+                        /* Even cluster number.  */
+
+                        /* Store the lower byte of the FAT entry.  */
+                        *FAT_ptr =  (UCHAR)(next_cluster & 0xFF);
+
+                        /* Determine if this is a mulit-sector entry.  */
+                        if ((multi_sector_entry) == (INT)i)
+                        {
+
+                            /* Yes, requires multiple sector - will write rest of the part later.  */
+                            continue;
+                        }
+
+                        /* Move to the next nibble of the FAT entry.  */
+                        FAT_ptr++;
+
+                        /* Store the upper 4 bits of the FAT entry.  */
+                        temp =  ((UINT)*FAT_ptr) & 0xF0;
+                        *FAT_ptr =  (UCHAR)(temp | ((next_cluster >> 8) & 0xF));
+                    }
+                }
+
+                /* First, write out the current sector. */
+                status =  _fx_utility_logical_sector_write(media_ptr, (ULONG64) FAT_sector,
+                                                           media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
+                /* Determine if an error occurred.  */
+                if (status != FX_SUCCESS)
+                {
+
+                    /* Return the error status.  */
+                    return(status);
+                }
+
+                /* Mark the FAT sector update bit map to indicate this sector has been written.  */
+                if (media_ptr -> fx_media_sectors_per_FAT % (FX_FAT_MAP_SIZE << 3) == 0)
+                {
+                    sectors_per_bit =  (UCHAR)((UINT)media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3));
+                }
+                else
+                {
+                    sectors_per_bit =  (UCHAR)((UINT)media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3) + 1);
+                }
+
+                /* Check for invalid value.  */
+                if (sectors_per_bit == 0)
+                {
+
+                    /* Invalid media, return error.  */
+                    return(FX_MEDIA_INVALID);
+                }
+
+                ind = ((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) >> 3;
+                media_ptr -> fx_media_fat_secondary_update_map[ind] = 
+                    (UCHAR)((INT)media_ptr -> fx_media_fat_secondary_update_map[ind]
+                    | (1 <<(((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) & 7)));
+
+                /* Determine if the multi-sector flag is set.  */
+                if (multi_sector_entry != -1)
+                {
+
+                    /* Yes, position to the next sector and read it in.  */
+                    FAT_sector++;
+                }
+                else
+                {
+
+                    /* No, we are finished with this loop.   */
+                    break;
+                }
+            }
+        }
+#ifdef FX_ENABLE_EXFAT
+        else if (media_ptr -> fx_media_FAT_type == FX_FAT16)
+#else
+        else if (!media_ptr -> fx_media_32_bit_FAT)
+#endif /* FX_ENABLE_EXFAT */
+        {
+
+            /* 16-bit FAT is present.  */
+
+            /* Calculate the byte offset to the cluster entry.  */
+            byte_offset =  (((ULONG)cluster) << 1);
+
+            /* Calculate the FAT sector the requested FAT entry resides in.  */
+            FAT_sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
+                (ULONG)media_ptr -> fx_media_reserved_sectors;
+
+            /* Read the FAT sector.  */
+            status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector,
+                                                      media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
+
+            /* Determine if an error occurred.  */
+            if (status != FX_SUCCESS)
+            {
+
+                /* Return the error status.  */
+                return(status);
+            }
+
+            /* Loop through the remainder of the cache to check for multiple entries
+               within the same FAT sector being written out.  */
+            for (i = index; i < FX_MAX_FAT_CACHE; i++)
+            {
+
+                /* Determine if the entry is dirty.  */
+                if (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty == 0)
+                {
+
+                    /* Not dirty, does not need to be flushed.  */
+                    continue;
+                }
+
+                /* Isolate the cluster.  */
+                cluster = (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_cluster);
+
+                /* Calculate the byte offset to the cluster entry.  */
+                byte_offset =  (((ULONG)cluster) * 2);
+
+                /* Pickup the sector.  */
+                sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
+                    (ULONG)media_ptr -> fx_media_reserved_sectors;
+
+                /* Is it the current FAT sector?  */
+                if (sector != FAT_sector)
+                {
+
+                    /* Different FAT sector - not in this pass of the loop.  */
+                    continue;
+                }
+
+                /* Now calculate the byte offset into this FAT sector.  */
+                byte_offset =  byte_offset -
+                    ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) *
+                     media_ptr -> fx_media_bytes_per_sector);
+
+                /* Setup a pointer into the buffer.  */
+                FAT_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset;
+
+                /* Pickup new value for this FAT entry.  */
+                next_cluster =  media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_value;
+
+                /* Store the FAT entry.  */
+                _fx_utility_16_unsigned_write(FAT_ptr, (UINT)next_cluster);
+
+                /* Clear the dirty flag.  */
+                media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty = 0;
+            }
+
+            /* Write the last written FAT sector out.  */
+            status =  _fx_utility_logical_sector_write(media_ptr, (ULONG64) FAT_sector,
+                                                       media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
+
+            /* Determine if an error occurred.  */
+            if (status != FX_SUCCESS)
+            {
+                /* Return the error status.  */
+                return(status);
+            }
+
+            /* Mark the FAT sector update bit map to indicate this sector has been
+               written.  */
+            if (media_ptr -> fx_media_sectors_per_FAT % (FX_FAT_MAP_SIZE << 3) == 0)
+            {
+                sectors_per_bit =  (UCHAR)(media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3));
+            }
+            else
+            {
+                sectors_per_bit =  (UCHAR)((media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3)) + 1);
+            }
+            ind = ((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) >> 3;
+            media_ptr -> fx_media_fat_secondary_update_map[ind] = 
+                (UCHAR)((INT)media_ptr -> fx_media_fat_secondary_update_map[ind]
+                | (1 <<(((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) & 7)));
+        }
+        else
+        {
+
+            /* 32-bit FAT or exFAT are present.  */
+
+            /* Calculate the byte offset to the cluster entry.  */
+            byte_offset =  (((ULONG)cluster) * 4);
+
+            /* Calculate the FAT sector the requested FAT entry resides in.  */
+            FAT_sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
+                (ULONG)media_ptr -> fx_media_reserved_sectors;
+
+            /* Read the FAT sector.  */
+            status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector,
+                                                      media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
+
+            /* Determine if an error occurred.  */
+            if (status != FX_SUCCESS)
+            {
+
+                /* Return the error status.  */
+                return(status);
+            }
+
+            /* Loop through the remainder of the cache to check for multiple entries
+               within the same FAT sector being written out.  */
+            for (i = index; i < FX_MAX_FAT_CACHE; i++)
+            {
+
+                /* Determine if the entry is dirty.  */
+                if (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty == 0)
+                {
+
+                    /* Not dirty, does not need to be flushed.  */
+                    continue;
+                }
+
+                /* Isolate the cluster.  */
+                cluster = (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_cluster);
+
+                /* Calculate the byte offset to the cluster entry.  */
+                byte_offset =  (((ULONG)cluster) * 4);
+
+                /* Pickup the sector.  */
+                sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
+                    (ULONG)media_ptr -> fx_media_reserved_sectors;
+
+                /* Is it the current FAT sector?  */
+                if (sector != FAT_sector)
+                {
+
+                    /* Different FAT sector - not in this pass of the loop.  */
+                    continue;
+                }
+
+                /* Now calculate the byte offset into this FAT sector.  */
+                byte_offset =  byte_offset -
+                    ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) *
+                     media_ptr -> fx_media_bytes_per_sector);
+
+                /* Setup a pointer into the buffer.  */
+                FAT_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset;
+
+                /* Pickup new value for this FAT entry.  */
+                next_cluster =  media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_value;
+
+                /* Store the FAT entry.  */
+                _fx_utility_32_unsigned_write(FAT_ptr, next_cluster);
+
+                /* Clear the dirty flag.  */
+                media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty = 0;
+            }
+
+            /* Write the last written FAT sector out.  */
+            status =  _fx_utility_logical_sector_write(media_ptr, (ULONG64) FAT_sector,
+                                                       media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
+
+            /* Determine if an error occurred.  */
+            if (status != FX_SUCCESS)
+            {
+
+                /* Return the error status.  */
+                return(status);
+            }
+
+#ifdef FX_ENABLE_EXFAT
+            /* We are not using fx_media_fat_secondary_update_map for exFAT.  */
+            if (media_ptr -> fx_media_FAT_type == FX_FAT32)
+            {
+#endif /* FX_ENABLE_EXFAT */
+
+                /* Mark the FAT sector update bit map to indicate this sector has been
+                   written.  */
+                if (media_ptr -> fx_media_sectors_per_FAT % (FX_FAT_MAP_SIZE << 3) == 0)
+                {
+                    sectors_per_bit =  (UCHAR)(media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3));
+                }
+                else
+                {
+                    sectors_per_bit =  (UCHAR)((media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3)) + 1);
+                }
+                ind = ((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) >> 3;
+                media_ptr -> fx_media_fat_secondary_update_map[ind] = 
+                    (UCHAR)((INT)media_ptr -> fx_media_fat_secondary_update_map[ind]
+                    | (1 <<(((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) & 7)));
+#ifdef FX_ENABLE_EXFAT
+            }
+#endif /* FX_ENABLE_EXFAT */
+        }
+    }
+
+#ifdef FX_ENABLE_FAULT_TOLERANT
+
+    /* While fault_tolerant is enabled, this function will be called before the return of all APIs. */
+    if (media_ptr -> fx_media_fault_tolerant_enabled)
+    {
+
+        /* Delete the record of FAT sector since all FAT entries are flushed. */
+        media_ptr -> fx_media_fault_tolerant_cached_FAT_sector = 0;
+    }
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+
+    /* Return successful status.  */
+    return(FX_SUCCESS);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_logical_sector_cache_entry_read.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_logical_sector_cache_entry_read.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_logical_sector_cache_entry_read.c	(revision 54)
@@ -0,0 +1,359 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Utility                                                             */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+#include "fx_utility.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_utility_logical_sector_cache_entry_read         PORTABLE C      */
+/*                                                           6.1.10       */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function handles logical sector cache read requests for the    */
+/*    logical sector read function. If the function finds the requested   */
+/*    sector in the cache, it setup the appropriate pointers and          */
+/*    returns a FX_NULL.                                                  */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    media_ptr                             Media control block pointer   */
+/*    logical_sector                        Logical sector number         */
+/*    previous_cache_entry                  Pointer to previous entry in  */
+/*                                            non-hashed cache            */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    FX_CACHED_SECTOR *                    Cache entry to setup          */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _fx_utility_logical_sector_read       Logical sector read function  */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s), and      */
+/*                                            added conditional to        */
+/*                                            disable cache,              */
+/*                                            resulting in version 6.1    */
+/*  01-31-2022     William E. Lamie         Modified comment(s), fixed    */
+/*                                            errors without cache,       */
+/*                                            resulting in version 6.1.10 */
+/*                                                                        */
+/**************************************************************************/
+FX_CACHED_SECTOR  *_fx_utility_logical_sector_cache_entry_read(FX_MEDIA *media_ptr, ULONG64 logical_sector,
+                                                               FX_CACHED_SECTOR **previous_cache_entry)
+{
+
+#ifndef FX_DISABLE_CACHE
+FX_CACHED_SECTOR *cache_entry;
+FX_CACHED_SECTOR  temp_storage;
+ULONG             cache_size;
+ULONG             index;
+
+
+    /* Determine if the logical sector cache access should use the hash function.  */
+    if (media_ptr -> fx_media_sector_cache_hashed)
+    {
+
+        /* Calculate the area of the cache for this logical sector.  */
+
+        /* First compute the hashed value of this index by simply using the lower bits of
+           the sector number.  */
+        index =  (ULONG)(logical_sector & media_ptr -> fx_media_sector_cache_hash_mask);
+
+        /* Set the bit indicating there is one or more valid sectors at this cache index.  */
+        media_ptr -> fx_media_sector_cache_hashed_sector_valid |=  ((ULONG)1) << (index % 32);
+
+        /* Compute the actual array index by multiplying by the cache depth.  */
+        index =  index * FX_SECTOR_CACHE_DEPTH;
+
+        /* Build a pointer to the cache entry.  */
+        cache_entry =  &(media_ptr -> fx_media_sector_cache[index]);
+
+        /* Determine if the logical sector is in the cache - assuming the depth of the
+           sector cache is 4 entries.  */
+        if ((cache_entry -> fx_cached_sector_valid) && (cache_entry -> fx_cached_sector == logical_sector))
+        {
+
+            /* Yes, we found a match.  Simply setup the pointer to this
+               buffer and return.  */
+            media_ptr -> fx_media_memory_buffer =  cache_entry -> fx_cached_sector_memory_buffer;
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+            /* Increment the number of logical sectors cache read hits.  */
+            media_ptr -> fx_media_logical_sector_cache_read_hits++;
+#endif
+            /* Success, return to caller immediately!  */
+            return(FX_NULL);
+        }
+        else if (((cache_entry + 1) -> fx_cached_sector_valid) && ((cache_entry + 1) -> fx_cached_sector == logical_sector))
+        {
+
+            /* Yes, we found a match.  Simply setup the pointer to this
+               buffer and return.  */
+            media_ptr -> fx_media_memory_buffer =  (cache_entry + 1) -> fx_cached_sector_memory_buffer;
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+            /* Increment the number of logical sectors cache read hits.  */
+            media_ptr -> fx_media_logical_sector_cache_read_hits++;
+#endif
+
+            /* Swap the first and second cache entries to keep the most recently used
+               at the top.  */
+            temp_storage.fx_cached_sector_memory_buffer =           (cache_entry) -> fx_cached_sector_memory_buffer;
+            temp_storage.fx_cached_sector =                         (cache_entry) -> fx_cached_sector;
+            temp_storage.fx_cached_sector_buffer_dirty =            (cache_entry) -> fx_cached_sector_buffer_dirty;
+            temp_storage.fx_cached_sector_valid =                   (cache_entry) -> fx_cached_sector_valid;
+            temp_storage.fx_cached_sector_type =                    (cache_entry) -> fx_cached_sector_type;
+
+            (cache_entry) -> fx_cached_sector_memory_buffer =       (cache_entry + 1) -> fx_cached_sector_memory_buffer;
+            (cache_entry) -> fx_cached_sector =                     (cache_entry + 1) -> fx_cached_sector;
+            (cache_entry) -> fx_cached_sector_buffer_dirty =        (cache_entry + 1) -> fx_cached_sector_buffer_dirty;
+            (cache_entry) -> fx_cached_sector_valid =               (cache_entry + 1) -> fx_cached_sector_valid;
+            (cache_entry) -> fx_cached_sector_type =                (cache_entry + 1) -> fx_cached_sector_type;
+
+            (cache_entry + 1) -> fx_cached_sector_memory_buffer =   temp_storage.fx_cached_sector_memory_buffer;
+            (cache_entry + 1) -> fx_cached_sector =                 temp_storage.fx_cached_sector;
+            (cache_entry + 1) -> fx_cached_sector_buffer_dirty =    temp_storage.fx_cached_sector_buffer_dirty;
+            (cache_entry + 1) -> fx_cached_sector_valid =           temp_storage.fx_cached_sector_valid;
+            (cache_entry + 1) -> fx_cached_sector_type =            temp_storage.fx_cached_sector_type;
+
+            /* Success, return to caller immediately!  */
+            return(FX_NULL);
+        }
+        else if (((cache_entry + 2) -> fx_cached_sector_valid) && ((cache_entry + 2) -> fx_cached_sector == logical_sector))
+        {
+
+            /* Yes, we found a match.  Simply setup the pointer to this
+               buffer and return.  */
+            media_ptr -> fx_media_memory_buffer =  (cache_entry + 2) -> fx_cached_sector_memory_buffer;
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+            /* Increment the number of logical sectors cache read hits.  */
+            media_ptr -> fx_media_logical_sector_cache_read_hits++;
+#endif
+
+            /* Move the third entry to the top and the first two entries down.  */
+            temp_storage.fx_cached_sector_memory_buffer =           (cache_entry) -> fx_cached_sector_memory_buffer;
+            temp_storage.fx_cached_sector =                         (cache_entry) -> fx_cached_sector;
+            temp_storage.fx_cached_sector_buffer_dirty =            (cache_entry) -> fx_cached_sector_buffer_dirty;
+            temp_storage.fx_cached_sector_valid =                   (cache_entry) -> fx_cached_sector_valid;
+            temp_storage.fx_cached_sector_type =                    (cache_entry) -> fx_cached_sector_type;
+
+            (cache_entry) -> fx_cached_sector_memory_buffer =       (cache_entry + 2) -> fx_cached_sector_memory_buffer;
+            (cache_entry) -> fx_cached_sector =                     (cache_entry + 2) -> fx_cached_sector;
+            (cache_entry) -> fx_cached_sector_buffer_dirty =        (cache_entry + 2) -> fx_cached_sector_buffer_dirty;
+            (cache_entry) -> fx_cached_sector_valid =               (cache_entry + 2) -> fx_cached_sector_valid;
+            (cache_entry) -> fx_cached_sector_type =                (cache_entry + 2) -> fx_cached_sector_type;
+
+            (cache_entry + 2) -> fx_cached_sector_memory_buffer =   (cache_entry + 1) -> fx_cached_sector_memory_buffer;
+            (cache_entry + 2) -> fx_cached_sector =                 (cache_entry + 1) -> fx_cached_sector;
+            (cache_entry + 2) -> fx_cached_sector_buffer_dirty =    (cache_entry + 1) -> fx_cached_sector_buffer_dirty;
+            (cache_entry + 2) -> fx_cached_sector_valid =           (cache_entry + 1) -> fx_cached_sector_valid;
+            (cache_entry + 2) -> fx_cached_sector_type =            (cache_entry + 1) -> fx_cached_sector_type;
+
+            (cache_entry + 1) -> fx_cached_sector_memory_buffer =   temp_storage.fx_cached_sector_memory_buffer;
+            (cache_entry + 1) -> fx_cached_sector =                 temp_storage.fx_cached_sector;
+            (cache_entry + 1) -> fx_cached_sector_buffer_dirty =    temp_storage.fx_cached_sector_buffer_dirty;
+            (cache_entry + 1) -> fx_cached_sector_valid =           temp_storage.fx_cached_sector_valid;
+            (cache_entry + 1) -> fx_cached_sector_type =            temp_storage.fx_cached_sector_type;
+
+            /* Success, return to caller immediately!  */
+            return(FX_NULL);
+        }
+        else if (((cache_entry + 3) -> fx_cached_sector_valid) && ((cache_entry + 3) -> fx_cached_sector == logical_sector))
+        {
+
+            /* Yes, we found a match.  Simply setup the pointer to this
+               buffer and return.  */
+            media_ptr -> fx_media_memory_buffer =  (cache_entry + 3) -> fx_cached_sector_memory_buffer;
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+            /* Increment the number of logical sectors cache read hits.  */
+            media_ptr -> fx_media_logical_sector_cache_read_hits++;
+#endif
+
+            /* Move the last entry to the top and the first three entries down.  */
+            temp_storage.fx_cached_sector_memory_buffer =           (cache_entry) -> fx_cached_sector_memory_buffer;
+            temp_storage.fx_cached_sector =                         (cache_entry) -> fx_cached_sector;
+            temp_storage.fx_cached_sector_buffer_dirty =            (cache_entry) -> fx_cached_sector_buffer_dirty;
+            temp_storage.fx_cached_sector_valid =                   (cache_entry) -> fx_cached_sector_valid;
+            temp_storage.fx_cached_sector_type =                    (cache_entry) -> fx_cached_sector_type;
+
+            (cache_entry) -> fx_cached_sector_memory_buffer =       (cache_entry + 3) -> fx_cached_sector_memory_buffer;
+            (cache_entry) -> fx_cached_sector =                     (cache_entry + 3) -> fx_cached_sector;
+            (cache_entry) -> fx_cached_sector_buffer_dirty =        (cache_entry + 3) -> fx_cached_sector_buffer_dirty;
+            (cache_entry) -> fx_cached_sector_valid =               (cache_entry + 3) -> fx_cached_sector_valid;
+            (cache_entry) -> fx_cached_sector_type =                (cache_entry + 3) -> fx_cached_sector_type;
+
+            (cache_entry + 3) -> fx_cached_sector_memory_buffer =   (cache_entry + 2) -> fx_cached_sector_memory_buffer;
+            (cache_entry + 3) -> fx_cached_sector =                 (cache_entry + 2) -> fx_cached_sector;
+            (cache_entry + 3) -> fx_cached_sector_buffer_dirty =    (cache_entry + 2) -> fx_cached_sector_buffer_dirty;
+            (cache_entry + 3) -> fx_cached_sector_valid =           (cache_entry + 2) -> fx_cached_sector_valid;
+            (cache_entry + 3) -> fx_cached_sector_type =            (cache_entry + 2) -> fx_cached_sector_type;
+
+            (cache_entry + 2) -> fx_cached_sector_memory_buffer =   (cache_entry + 1) -> fx_cached_sector_memory_buffer;
+            (cache_entry + 2) -> fx_cached_sector =                 (cache_entry + 1) -> fx_cached_sector;
+            (cache_entry + 2) -> fx_cached_sector_buffer_dirty =    (cache_entry + 1) -> fx_cached_sector_buffer_dirty;
+            (cache_entry + 2) -> fx_cached_sector_valid =           (cache_entry + 1) -> fx_cached_sector_valid;
+            (cache_entry + 2) -> fx_cached_sector_type =            (cache_entry + 1) -> fx_cached_sector_type;
+
+            (cache_entry + 1) -> fx_cached_sector_memory_buffer =   temp_storage.fx_cached_sector_memory_buffer;
+            (cache_entry + 1) -> fx_cached_sector =                 temp_storage.fx_cached_sector;
+            (cache_entry + 1) -> fx_cached_sector_buffer_dirty =    temp_storage.fx_cached_sector_buffer_dirty;
+            (cache_entry + 1) -> fx_cached_sector_valid =           temp_storage.fx_cached_sector_valid;
+            (cache_entry + 1) -> fx_cached_sector_type =            temp_storage.fx_cached_sector_type;
+
+            /* Success, return to caller immediately!  */
+            return(FX_NULL);
+        }
+
+        /* At this point we have a cache miss.  We need to move all of the sectors down one slot, swapping
+           the 4th entry with the first.  */
+        temp_storage.fx_cached_sector_memory_buffer =           (cache_entry + 3) -> fx_cached_sector_memory_buffer;
+        temp_storage.fx_cached_sector =                         (cache_entry + 3) -> fx_cached_sector;
+        temp_storage.fx_cached_sector_buffer_dirty =            (cache_entry + 3) -> fx_cached_sector_buffer_dirty;
+        temp_storage.fx_cached_sector_valid =                   (cache_entry + 3) -> fx_cached_sector_valid;
+        temp_storage.fx_cached_sector_type =                    (cache_entry + 3) -> fx_cached_sector_type;
+
+        (cache_entry + 3) -> fx_cached_sector_memory_buffer =   (cache_entry + 2) -> fx_cached_sector_memory_buffer;
+        (cache_entry + 3) -> fx_cached_sector =                 (cache_entry + 2) -> fx_cached_sector;
+        (cache_entry + 3) -> fx_cached_sector_buffer_dirty =    (cache_entry + 2) -> fx_cached_sector_buffer_dirty;
+        (cache_entry + 3) -> fx_cached_sector_valid =           (cache_entry + 2) -> fx_cached_sector_valid;
+        (cache_entry + 3) -> fx_cached_sector_type =            (cache_entry + 2) -> fx_cached_sector_type;
+
+        (cache_entry + 2) -> fx_cached_sector_memory_buffer =   (cache_entry + 1) -> fx_cached_sector_memory_buffer;
+        (cache_entry + 2) -> fx_cached_sector =                 (cache_entry + 1) -> fx_cached_sector;
+        (cache_entry + 2) -> fx_cached_sector_buffer_dirty =    (cache_entry + 1) -> fx_cached_sector_buffer_dirty;
+        (cache_entry + 2) -> fx_cached_sector_valid =           (cache_entry + 1) -> fx_cached_sector_valid;
+        (cache_entry + 2) -> fx_cached_sector_type =            (cache_entry + 1) -> fx_cached_sector_type;
+
+        (cache_entry + 1) -> fx_cached_sector_memory_buffer =   (cache_entry) -> fx_cached_sector_memory_buffer;
+        (cache_entry + 1) -> fx_cached_sector =                 (cache_entry) -> fx_cached_sector;
+        (cache_entry + 1) -> fx_cached_sector_buffer_dirty =    (cache_entry) -> fx_cached_sector_buffer_dirty;
+        (cache_entry + 1) -> fx_cached_sector_valid =           (cache_entry) -> fx_cached_sector_valid;
+        (cache_entry + 1) -> fx_cached_sector_type =            (cache_entry) -> fx_cached_sector_type;
+
+        (cache_entry) -> fx_cached_sector_memory_buffer =       temp_storage.fx_cached_sector_memory_buffer;
+        (cache_entry) -> fx_cached_sector =                     temp_storage.fx_cached_sector;
+        (cache_entry) -> fx_cached_sector_buffer_dirty =        temp_storage.fx_cached_sector_buffer_dirty;
+        (cache_entry) -> fx_cached_sector_valid =               temp_storage.fx_cached_sector_valid;
+        (cache_entry) -> fx_cached_sector_type =                temp_storage.fx_cached_sector_type;
+
+        /* Set the previous pointer to NULL to avoid the linked list update below.  */
+        *previous_cache_entry =  FX_NULL;
+    }
+    else
+    {
+
+        /* Search for an entry in the cache that matches this request.  */
+        cache_size =            media_ptr -> fx_media_sector_cache_size;
+        cache_entry =           media_ptr -> fx_media_sector_cache_list_ptr;
+        *previous_cache_entry =  FX_NULL;
+
+        /* Look at the cache entries until a match is found or the end of
+           the cache is reached.  */
+        while (cache_size--)
+        {
+
+            /* Determine if the requested sector has been found.  */
+            if ((cache_entry -> fx_cached_sector_valid) && (cache_entry -> fx_cached_sector == logical_sector))
+            {
+
+                /* Yes, we found a match.  Simply setup the pointer to this
+                   buffer and return.  */
+                media_ptr -> fx_media_memory_buffer =  cache_entry -> fx_cached_sector_memory_buffer;
+
+                /* Determine if we need to update the last used list.  */
+                if (*previous_cache_entry)
+                {
+
+                    /* Yes, the current entry is not at the front of the list
+                       so we need to change the order.  */
+
+                    /* Link the previous entry to this entry's next pointer.  */
+                    (*previous_cache_entry) -> fx_cached_sector_next_used =
+                        cache_entry -> fx_cached_sector_next_used;
+
+                    /* Place this entry at the head of the list.  */
+                    cache_entry -> fx_cached_sector_next_used =
+                        media_ptr -> fx_media_sector_cache_list_ptr;
+                    media_ptr -> fx_media_sector_cache_list_ptr =  cache_entry;
+                }
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+                /* Increment the number of logical sectors cache read hits.  */
+                media_ptr -> fx_media_logical_sector_cache_read_hits++;
+#endif
+
+                /* Success, return to caller immediately!  */
+                return(FX_NULL);
+            }
+
+            /* Otherwise, we have not found the cached entry yet.  */
+
+            /* If there are more entries, move to the next one.  */
+            if (cache_entry -> fx_cached_sector_next_used)
+            {
+
+                *previous_cache_entry =  cache_entry;
+                cache_entry =           cache_entry -> fx_cached_sector_next_used;
+            }
+        }
+    }
+
+    /* The requested sector is not in cache, return the last cache entry.  */
+    return(cache_entry);
+#else
+    FX_PARAMETER_NOT_USED(media_ptr);
+    FX_PARAMETER_NOT_USED(logical_sector);
+    FX_PARAMETER_NOT_USED(previous_cache_entry);
+    return(FX_NULL);
+#endif /* FX_DISABLE_CACHE */
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_logical_sector_flush.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_logical_sector_flush.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_logical_sector_flush.c	(revision 54)
@@ -0,0 +1,522 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Utility                                                             */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+#include "fx_utility.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_utility_logical_sector_flush                    PORTABLE C      */
+/*                                                           6.1.10       */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function handles logical sector flush requests for all FileX   */
+/*    components. It will process all dirty logical sectors in the        */
+/*    logical sector cache within the range specified. This function      */
+/*    optionally invalidates sectors.                                     */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    media_ptr                             Media control block pointer   */
+/*    starting_sector                       Starting sector number        */
+/*    sectors                               Number of sectors             */
+/*    invalidate                            Invalidate flag               */
+/*                                            (FX_TRUE -> invalidate)     */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    return status                                                       */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    I/O Driver                                                          */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    FileX System Functions                                              */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s), and      */
+/*                                            added conditional to        */
+/*                                            disable cache,              */
+/*                                            resulting in version 6.1    */
+/*  01-31-2022     William E. Lamie         Modified comment(s), fixed    */
+/*                                            errors without cache,       */
+/*                                            resulting in version 6.1.10 */
+/*                                                                        */
+/**************************************************************************/
+UINT  _fx_utility_logical_sector_flush(FX_MEDIA *media_ptr, ULONG64 starting_sector, ULONG64 sectors, UINT invalidate)
+{
+
+#ifndef FX_DISABLE_CACHE
+FX_CACHED_SECTOR *cache_entry;
+UINT              cache_size;
+UINT              i, bit_set, use_starting_sector;
+ULONG             index;
+ULONG             remaining_valid;
+ULONG             remaining_dirty;
+ULONG64           ending_sector;
+ULONG             valid_bit_map;
+
+
+    /* Extended port-specific processing macro, which is by default defined to white space.  */
+    FX_UTILITY_LOGICAL_SECTOR_FLUSH_EXTENSION
+
+    /* Calculate the ending sector.  */
+    ending_sector =  starting_sector + sectors - 1;
+
+    /* Pickup the number of dirty sectors currently in the cache.  */
+    remaining_dirty =  media_ptr -> fx_media_sector_cache_dirty_count;
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_MEDIA_FLUSH, media_ptr, media_ptr -> fx_media_sector_cache_dirty_count, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+    /* Determine what type of cache configuration we have.  */
+    if (media_ptr -> fx_media_sector_cache_hashed == FX_FALSE)
+    {
+
+        /* Linear cache present, simply walk through the search list until
+           an unused cache entry is present.  */
+
+        /* Flush and invalidate the internal logical sector cache.  */
+        cache_size =            media_ptr -> fx_media_sector_cache_size;
+        cache_entry =           media_ptr -> fx_media_sector_cache_list_ptr;
+
+        /* Look at the cache entries that have been written to.  */
+        while ((cache_size--) && (cache_entry -> fx_cached_sector))
+        {
+
+            /* Determine if invalidation is not required and there are no
+               more dirty sectors. */
+            if ((remaining_dirty == 0) && (invalidate == FX_FALSE))
+            {
+
+                /* Yes, nothing left to do.  */
+                break;
+            }
+
+            /* Determine if there are any more sectors to process.  */
+            if (sectors == 0)
+            {
+
+                /* No more sectors required to process.  */
+                break;
+            }
+
+            /* Determine if this cached sector is within the specified range and is valid.  */
+            if ((cache_entry -> fx_cached_sector_valid) &&
+                (cache_entry -> fx_cached_sector >= starting_sector) &&
+                (cache_entry -> fx_cached_sector <= ending_sector))
+            {
+
+                /* Yes, the cache entry is valid and within the specified range. Determine if
+                   the requested sector has been written to.  */
+                if (cache_entry -> fx_cached_sector_buffer_dirty)
+                {
+
+                    /* Yes, write the cached sector out to the media.  */
+
+                    /* Check for write protect at the media level (set by driver).  */
+                    if (media_ptr -> fx_media_driver_write_protect == FX_FALSE)
+                    {
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+                        /* Increment the number of driver write sector(s) requests.  */
+                        media_ptr -> fx_media_driver_write_requests++;
+#endif
+
+                        /* Build write request to the driver.  */
+                        media_ptr -> fx_media_driver_request =          FX_DRIVER_WRITE;
+                        media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
+                        media_ptr -> fx_media_driver_buffer =           cache_entry -> fx_cached_sector_memory_buffer;
+#ifdef FX_DRIVER_USE_64BIT_LBA
+                        media_ptr -> fx_media_driver_logical_sector =   cache_entry -> fx_cached_sector;
+#else
+                        media_ptr -> fx_media_driver_logical_sector =   (ULONG)cache_entry -> fx_cached_sector;
+#endif
+                        media_ptr -> fx_media_driver_sectors =          1;
+                        media_ptr -> fx_media_driver_sector_type =      cache_entry -> fx_cached_sector_type;
+
+                        /* Sectors other than FX_DATA_SECTOR will never be dirty when FX_FAULT_TOLERANT is defined. */
+#ifndef FX_FAULT_TOLERANT
+                        /* Determine if the system write flag needs to be set.  */
+                        if (cache_entry -> fx_cached_sector_type != FX_DATA_SECTOR)
+                        {
+
+                            /* Yes, a system sector write is present so set the flag.  The driver
+                               can use this flag to make extra safeguards in writing the sector
+                               out, yielding more fault tolerance.  */
+                            media_ptr -> fx_media_driver_system_write =  FX_TRUE;
+                        }
+#endif /* FX_FAULT_TOLERANT */
+
+                        /* If trace is enabled, insert this event into the trace buffer.  */
+                        FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, cache_entry -> fx_cached_sector, 1, cache_entry -> fx_cached_sector_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+                        /* Invoke the driver to write the sector.  */
+                        (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+                        /* Clear the system write flag.  */
+                        media_ptr -> fx_media_driver_system_write =  FX_FALSE;
+
+                        /* Check for successful completion.  */
+                        if (media_ptr -> fx_media_driver_status)
+                        {
+
+                            /* Error writing a cached sector out.  Return the
+                               error status.  */
+                            return(media_ptr -> fx_media_driver_status);
+                        }
+
+                        /* Clear the buffer dirty flag since it has been flushed
+                           out.  */
+                        cache_entry -> fx_cached_sector_buffer_dirty =  FX_FALSE;
+
+                        /* Decrement the number of dirty sectors currently in the cache.  */
+                        media_ptr -> fx_media_sector_cache_dirty_count--;
+                        remaining_dirty--;
+                    }
+                }
+
+                /* Determine if the invalidate option is specified.  */
+                if (invalidate)
+                {
+
+                    /* Invalidate the cache entry.  */
+                    cache_entry -> fx_cached_sector_valid =  FX_FALSE;
+
+                    /* Place all ones in the sector number.  */
+                    cache_entry -> fx_cached_sector =  (~(ULONG64)0);
+
+                    /* Determine if this sector is still dirty, this could be the case if
+                       write protection was turned on.  */
+                    if (cache_entry -> fx_cached_sector_buffer_dirty)
+                    {
+
+                        /* Yes, clear the dirty flag.  */
+                        cache_entry -> fx_cached_sector_buffer_dirty =  FX_FALSE;
+
+                        /* Decrement the number of dirty sectors currently in the cache.  */
+                        media_ptr -> fx_media_sector_cache_dirty_count--;
+                        remaining_dirty--;
+                    }
+                }
+
+                /* Decrement the number of sectors in the range that have been processed.  */
+                sectors--;
+            }
+
+            /* Move to the next entry in the sector cache.  */
+            cache_entry =  cache_entry -> fx_cached_sector_next_used;
+        }
+    }
+    else
+    {
+
+        /* Hashed cache is present. Pickup the cache size.  */
+        cache_size =            media_ptr -> fx_media_sector_cache_size;
+
+        /* Initialize the loop control parameters.  */
+        bit_set =  0;
+        valid_bit_map =  media_ptr -> fx_media_sector_cache_hashed_sector_valid;
+
+        /* Determine how to process the hashed cache based on the number of sectors
+           to process. If the sequential sector range is less than the bit map size,
+           simply use the starting sector to derive the index into the cache.  */
+        if (sectors < 32)
+        {
+            use_starting_sector =  FX_TRUE;
+        }
+        else
+        {
+            use_starting_sector =  FX_FALSE;
+        }
+
+        /* Determine if there is anything valid in the cache.  */
+        while (valid_bit_map)
+        {
+
+            /* Determine if invalidation is not required and there are no
+               more dirty sectors. */
+            if ((remaining_dirty == 0) && (invalidate == FX_FALSE))
+            {
+
+                /* Yes, nothing left to do.  */
+                break;
+            }
+
+            /* Determine if there are any more sectors to process.  */
+            if ((sectors == 0) || (starting_sector > ending_sector))
+            {
+
+                /* No more sectors required to process.  */
+                break;
+            }
+
+            /* Determine how to compute the hash index.  */
+            if (use_starting_sector)
+            {
+
+                /* Calculate the hash value of this sector using the lower bits.  */
+                index =  (ULONG)(starting_sector & media_ptr -> fx_media_sector_cache_hash_mask);
+
+                /* Calculate the bit set indicating there is one or more valid sectors at this cache index.  */
+                bit_set =  (index % 32);
+
+                /* Compute the actual array index by multiplying by the cache depth.  */
+                index =  (bit_set * FX_SECTOR_CACHE_DEPTH);
+            }
+            else
+            {
+
+                /* Walk the bit map to find the next valid entry.  */
+
+                /* Find the next set bit.  */
+                while ((valid_bit_map & 1) == 0)
+                {
+
+                    /* Otherwise, shift down the bit in the bit map.  */
+                    valid_bit_map =  valid_bit_map >> 1;
+
+                    /* Increment the set bit marker.  */
+                    bit_set++;
+                }
+
+                /* Compute the first actual index into the hashed cache.  */
+                index =  (bit_set * FX_SECTOR_CACHE_DEPTH);
+            }
+
+            /* At this point, bit_set represents the next group of hashed sectors that could
+               have valid cache entries and index represents the index into the sector cache
+               of that sector group.  */
+
+            /* Clear the remaining valid sectors for this entry in the bit map.  */
+            remaining_valid =  0;
+
+            /* Loop to check the corresponding hash entries.  */
+            do
+            {
+
+                /* Setup pointer to the cache entry.  */
+                cache_entry =  &(media_ptr -> fx_media_sector_cache[index]);
+
+                /* Loop to examine the full depth of the hashed cache.  */
+                for (i = 0; i < 4; i++)
+                {
+
+                    /* Determine if this cached sector is within the specified range and is valid.  */
+                    if ((cache_entry -> fx_cached_sector_valid) &&
+                        (cache_entry -> fx_cached_sector >= starting_sector) &&
+                        (cache_entry -> fx_cached_sector <= ending_sector))
+                    {
+
+                        /* Determine if the requested sector has been written to.  */
+                        if (cache_entry -> fx_cached_sector_buffer_dirty)
+                        {
+
+
+                            /* Yes, write the cached sector out to the media.  */
+
+                            /* Check for write protect at the media level (set by driver).  */
+                            if (media_ptr -> fx_media_driver_write_protect == FX_FALSE)
+                            {
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+                                /* Increment the number of driver write sector(s) requests.  */
+                                media_ptr -> fx_media_driver_write_requests++;
+#endif
+
+                                /* Build Write request to the driver.  */
+                                media_ptr -> fx_media_driver_request =          FX_DRIVER_WRITE;
+                                media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
+                                media_ptr -> fx_media_driver_buffer =           cache_entry -> fx_cached_sector_memory_buffer;
+#ifdef FX_DRIVER_USE_64BIT_LBA
+                                media_ptr -> fx_media_driver_logical_sector =   cache_entry -> fx_cached_sector;
+#else
+                                media_ptr -> fx_media_driver_logical_sector =   (ULONG)cache_entry -> fx_cached_sector;
+#endif
+                                media_ptr -> fx_media_driver_sectors =          1;
+                                media_ptr -> fx_media_driver_sector_type =      cache_entry -> fx_cached_sector_type;
+
+                                /* Sectors other than FX_DATA_SECTOR will never be dirty when FX_FAULT_TOLERANT is defined. */
+#ifndef FX_FAULT_TOLERANT
+                                /* Determine if the system write flag needs to be set.  */
+                                if (cache_entry -> fx_cached_sector_type != FX_DATA_SECTOR)
+                                {
+
+                                    /* Yes, a system sector write is present so set the flag.  The driver
+                                       can use this flag to make extra safeguards in writing the sector
+                                       out, yielding more fault tolerance.  */
+                                    media_ptr -> fx_media_driver_system_write =  FX_TRUE;
+                                }
+#endif /* FX_FAULT_TOLERANT */
+
+                                /* If trace is enabled, insert this event into the trace buffer.  */
+                                FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, cache_entry -> fx_cached_sector, 1, cache_entry -> fx_cached_sector_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+                                /* Invoke the driver to write the sector.  */
+                                (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+                                /* Clear the system write flag.  */
+                                media_ptr -> fx_media_driver_system_write =  FX_FALSE;
+
+                                /* Check for successful completion.  */
+                                if (media_ptr -> fx_media_driver_status)
+                                {
+
+                                    /* Error writing a cached sector out.  Return the
+                                       error status.  */
+                                    return(media_ptr -> fx_media_driver_status);
+                                }
+
+                                /* Clear the buffer dirty flag since it has been flushed
+                                   out.  */
+                                cache_entry -> fx_cached_sector_buffer_dirty =  FX_FALSE;
+
+                                /* Decrement the number of dirty sectors currently in the cache.  */
+                                media_ptr -> fx_media_sector_cache_dirty_count--;
+                                remaining_dirty--;
+                            }
+                        }
+
+                        /* Determine if the invalidate option is specified.  */
+                        if (invalidate)
+                        {
+
+                            /* Invalidate the cache entry.  */
+                            cache_entry -> fx_cached_sector_valid =  FX_FALSE;
+
+                            /* Place all ones in the sector number.  */
+                            cache_entry -> fx_cached_sector =  (~(ULONG64)0);
+
+                            /* Determine if this sector is still dirty, this could be the case if
+                               write protection was turned on.  */
+                            if (cache_entry -> fx_cached_sector_buffer_dirty)
+                            {
+
+                                /* Yes, clear the dirty flag.  */
+                                cache_entry -> fx_cached_sector_buffer_dirty =  FX_FALSE;
+
+                                /* Decrement the number of dirty sectors currently in the cache.  */
+                                media_ptr -> fx_media_sector_cache_dirty_count--;
+                                remaining_dirty--;
+                            }
+                        }
+
+                        /* Decrement the number of sectors in the range that have been processed.  */
+                        sectors--;
+                    }
+                    else
+                    {
+
+                        /* Determine if the sector is valid.  */
+                        if (cache_entry -> fx_cached_sector_valid)
+                        {
+
+                            /* Increment the number of still remaining but out of range sectors.  */
+                            remaining_valid++;
+                        }
+                    }
+
+                    /* Determine if invalidation is not required and there are no
+                       more dirty sectors. */
+                    if ((remaining_dirty == 0) && (invalidate == FX_FALSE))
+                    {
+
+                        /* Yes, nothing left to do.  */
+                        break;
+                    }
+
+                    /* Determine if there are any more sectors to process.  */
+                    if ((sectors == 0) && (invalidate == FX_FALSE))
+                    {
+
+                        /* No more sectors required to process.  */
+                        break;
+                    }
+
+                    /* Move to the next cache entry.  */
+                    cache_entry++;
+                }
+
+                /* Move the index to the next position since the bit map can only represent 32
+                   cache entries.  */
+                index =  index + (32 * FX_SECTOR_CACHE_DEPTH);
+            } while (index < cache_size);
+
+            /* Determine if invalidation was required and there are no more valid sectors
+               associated with this bit position.  */
+            if ((invalidate) && (remaining_valid == 0))
+            {
+
+                /* Clear this bit position.  */
+                media_ptr -> fx_media_sector_cache_hashed_sector_valid &=  ~(((ULONG)1) << bit_set);
+            }
+
+            /* Determine if the starting sector is being used for examination of the hash.  */
+            if (use_starting_sector)
+            {
+
+                /* Move to the next sector.  */
+                starting_sector++;
+            }
+            else
+            {
+
+                /* Move to next bit in the map.  */
+                valid_bit_map =  valid_bit_map >> 1;
+
+                /* Increment the set bit marker.  */
+                bit_set++;
+            }
+        }
+    }
+#else
+    FX_PARAMETER_NOT_USED(media_ptr);
+    FX_PARAMETER_NOT_USED(starting_sector);
+    FX_PARAMETER_NOT_USED(sectors);
+    FX_PARAMETER_NOT_USED(invalidate);
+#endif /* FX_DISABLE_CACHE */
+
+    /* If we get here, return successful status to the caller.  */
+    return(FX_SUCCESS);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_logical_sector_read.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_logical_sector_read.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_logical_sector_read.c	(revision 54)
@@ -0,0 +1,647 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Utility                                                             */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+#include "fx_utility.h"
+#ifdef FX_ENABLE_FAULT_TOLERANT
+#include "fx_fault_tolerant.h"
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_utility_logical_sector_read                     PORTABLE C      */
+/*                                                           6.2.0        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function handles logical sector read requests for all FileX    */
+/*    components.  If the logical sector is currently in the logical      */
+/*    sector cache, the function simply sets the appropriate pointer and  */
+/*    returns a successful status to the caller.  Otherwise, physical I/O */
+/*    is requested through the corresponding I/O driver.                  */
+/*                                                                        */
+/*    Note: Conversion of the logical sector is done inside the driver.   */
+/*          This results in a performance boost for FLASH or RAM media    */
+/*          devices.                                                      */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    media_ptr                             Media control block pointer   */
+/*    logical_sector                        Logical sector number         */
+/*    buffer_ptr                            Pointer of receiving buffer   */
+/*    sectors                               Number of sectors to read     */
+/*    sector_type                           Type of sector(s) to read     */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    return status                                                       */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _fx_utility_logical_sector_cache_entry_read                         */
+/*                                          Read logical sector cache     */
+/*    _fx_utility_logical_sector_flush      Flush and invalidate sectors  */
+/*                                          that overlap with non-cache   */
+/*                                          sector I/O.                   */
+/*    _fx_utility_memory_copy               Copy cache sector             */
+/*    _fx_fault_tolerant_read_directory_sector                            */
+/*                                          Read directory sector         */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    FileX System Functions                                              */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s),          */
+/*                                            verified memcpy usage, and  */
+/*                                            added conditional to        */
+/*                                            disable cache,              */
+/*                                            resulting in version 6.1    */
+/*  04-02-2021     Bhupendra Naphade        Modified comment(s),          */
+/*                                            updated check for logical   */
+/*                                            sector value,               */
+/*                                            resulting in version 6.1.6  */
+/*  10-31-2022     Tiejun Zhou              Modified comment(s),          */
+/*                                            fixed memory buffer when    */
+/*                                            cache is disabled,          */
+/*                                            resulting in version 6.2.0  */
+/*                                                                        */
+/**************************************************************************/
+UINT  _fx_utility_logical_sector_read(FX_MEDIA *media_ptr, ULONG64 logical_sector,
+                                      VOID *buffer_ptr, ULONG sectors, UCHAR sector_type)
+{
+#ifndef FX_DISABLE_CACHE
+FX_CACHED_SECTOR *cache_entry;
+FX_CACHED_SECTOR *previous_cache_entry;
+ULONG64           end_sector;
+#endif /* FX_DISABLE_CACHE */
+
+#ifdef FX_ENABLE_FAULT_TOLERANT
+UINT              status;
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+    /* Determine if the request is for FAT sector.  */
+    if (sector_type == FX_FAT_SECTOR)
+    {
+
+        /* Increment the number of FAT sector reads.  */
+        media_ptr -> fx_media_fat_sector_reads++;
+    }
+
+    /* Increment the number of logical sectors read.  */
+    media_ptr -> fx_media_logical_sector_reads++;
+#endif
+
+    /* Extended port-specific processing macro, which is by default defined to white space.  */
+    FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION
+
+#ifndef FX_DISABLE_CACHE
+    /* Determine if the request is for the internal media buffer area.  */
+    if ((((UCHAR *)buffer_ptr) >= media_ptr -> fx_media_memory_buffer) &&
+        (((UCHAR *)buffer_ptr) <= media_ptr -> fx_media_sector_cache_end))
+    {
+
+        /* Internal cache buffer is requested.  */
+
+        /* Examine the logical sector cache.  */
+        cache_entry = _fx_utility_logical_sector_cache_entry_read(media_ptr, logical_sector, &previous_cache_entry);
+
+        /* Was the sector found?  */
+        if (cache_entry == FX_NULL)
+        {
+
+            /* Yes, the sector was found. Return success!  */
+            return(FX_SUCCESS);
+        }
+
+        /* At this point, we need to read in a sector from the media.  */
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+        /* Increment the number of logical sectors cache read misses.  */
+        media_ptr -> fx_media_logical_sector_cache_read_misses++;
+#endif
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+        /* If trace is enabled, insert this event into the trace buffer.  */
+        FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_LOG_SECTOR_CACHE_MISS, media_ptr, logical_sector, media_ptr -> fx_media_logical_sector_cache_read_misses, media_ptr -> fx_media_sector_cache_size, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+#else
+
+        /* If trace is enabled, insert this event into the trace buffer.  */
+        FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_LOG_SECTOR_CACHE_MISS, media_ptr, logical_sector, 0, media_ptr -> fx_media_sector_cache_size, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+#endif
+
+        /* First, check and see if the last used entry has been
+           modified.  */
+        if ((cache_entry -> fx_cached_sector_valid) &&
+            (cache_entry -> fx_cached_sector_buffer_dirty))
+        {
+
+            /* Yes, we need to flush this buffer out to the physical media
+               before we read in the new buffer.  */
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+            /* Increment the number of driver write sector(s) requests.  */
+            media_ptr -> fx_media_driver_write_requests++;
+#endif
+
+            /* Build write request to the driver.  */
+            media_ptr -> fx_media_driver_request =          FX_DRIVER_WRITE;
+            media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
+            media_ptr -> fx_media_driver_buffer =           cache_entry -> fx_cached_sector_memory_buffer;
+#ifdef FX_DRIVER_USE_64BIT_LBA
+            media_ptr -> fx_media_driver_logical_sector =   cache_entry -> fx_cached_sector;
+#else
+            media_ptr -> fx_media_driver_logical_sector =   (ULONG)cache_entry -> fx_cached_sector;
+#endif
+            media_ptr -> fx_media_driver_sectors =          1;
+            media_ptr -> fx_media_driver_sector_type =      cache_entry -> fx_cached_sector_type;
+
+            /* Determine if the sector is a data sector or a system sector.  */
+            if (cache_entry -> fx_cached_sector_type != FX_DATA_SECTOR)
+            {
+
+                /* System sector is present.  */
+                media_ptr -> fx_media_driver_system_write =  FX_TRUE;
+            }
+
+            /* If trace is enabled, insert this event into the trace buffer.  */
+            FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, cache_entry -> fx_cached_sector, 1, cache_entry -> fx_cached_sector_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+            /* Invoke the driver to write the sector.  */
+            (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+            /* Clear the system write flag.  */
+            media_ptr -> fx_media_driver_system_write =  FX_FALSE;
+
+            /* Check for successful completion.  */
+            if (media_ptr -> fx_media_driver_status)
+            {
+
+                /* Error writing a cached sector out.  Return the
+                   error status.  */
+                return(media_ptr -> fx_media_driver_status);
+            }
+
+            /* Clear the buffer dirty flag since it has been flushed
+               out.  */
+            cache_entry -> fx_cached_sector_buffer_dirty =  FX_FALSE;
+
+            /* Decrement the number of outstanding dirty cache entries.  */
+            media_ptr -> fx_media_sector_cache_dirty_count--;
+        }
+
+        /* At this point, we can go out and setup this cached sector
+           entry.  */
+
+        /* Compare against logical sector to make sure it is valid.  */
+        if (logical_sector >= media_ptr -> fx_media_total_sectors)
+        {
+            return(FX_SECTOR_INVALID);
+        }
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+        /* Increment the number of driver read sector(s) requests.  */
+        media_ptr -> fx_media_driver_read_requests++;
+#endif
+
+        /* Build Read request to the driver.  */
+        media_ptr -> fx_media_driver_request =          FX_DRIVER_READ;
+        media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
+        media_ptr -> fx_media_driver_buffer =           cache_entry -> fx_cached_sector_memory_buffer;
+#ifdef FX_DRIVER_USE_64BIT_LBA
+        media_ptr -> fx_media_driver_logical_sector =   logical_sector;
+#else
+        media_ptr -> fx_media_driver_logical_sector =   (ULONG)logical_sector;
+#endif
+        media_ptr -> fx_media_driver_sectors =          1;
+        media_ptr -> fx_media_driver_sector_type =      sector_type;
+
+        /* Determine if the sector is a data sector or a system sector.  */
+        if (sector_type == FX_DATA_SECTOR)
+        {
+
+            /* Data sector is present.  */
+            media_ptr -> fx_media_driver_data_sector_read =  FX_TRUE;
+        }
+
+        /* If trace is enabled, insert this event into the trace buffer.  */
+        FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_READ, media_ptr, logical_sector, 1, cache_entry -> fx_cached_sector_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+        /* Invoke the driver to read the sector.  */
+        (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+        /* Clear data sector is present flag.  */
+        media_ptr -> fx_media_driver_data_sector_read =  FX_FALSE;
+
+        /* Determine if the read was successful.  */
+        if (media_ptr -> fx_media_driver_status == FX_SUCCESS)
+        {
+
+            /* Remember the sector number.  */
+            cache_entry -> fx_cached_sector =  logical_sector;
+
+            /* Make the cache entry valid.  */
+            cache_entry -> fx_cached_sector_valid =  FX_TRUE;
+
+            /* Remember the sector type.  */
+            cache_entry -> fx_cached_sector_type =  sector_type;
+
+            /* Place this entry that the head of the cached sector
+               list.  */
+
+            /* Determine if we need to update the last used list.  */
+            if (previous_cache_entry)
+            {
+
+                /* Yes, the current entry is not at the front of the list
+                   so we need to change the order.  */
+
+                /* Link the previous entry to this entry's next pointer.  */
+                previous_cache_entry -> fx_cached_sector_next_used =
+                    cache_entry -> fx_cached_sector_next_used;
+
+                /* Place this entry at the head of the list.  */
+                cache_entry -> fx_cached_sector_next_used =
+                    media_ptr -> fx_media_sector_cache_list_ptr;
+                media_ptr -> fx_media_sector_cache_list_ptr =  cache_entry;
+            }
+
+#ifdef FX_ENABLE_FAULT_TOLERANT
+            if (media_ptr -> fx_media_fault_tolerant_enabled &&
+                (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED) &&
+                (sector_type == FX_DIRECTORY_SECTOR))
+            {
+
+                /* Read sector from log file. */
+                status = _fx_fault_tolerant_read_directory_sector(media_ptr, logical_sector, cache_entry -> fx_cached_sector_memory_buffer, 1);
+
+                /* Check for successful completion.  */
+                if (status)
+                {
+
+                    /* Return the error status. */
+                    return(status);
+                }
+            }
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+        }
+        else
+        {
+
+            /* Invalidate the cache entry on read errors.  */
+            cache_entry -> fx_cached_sector_valid =  FX_FALSE;
+
+            /* Put all ones in the sector value.  */
+            cache_entry -> fx_cached_sector =  (~(ULONG64)0);
+        }
+
+        /* Simply setup the pointer to this buffer and return.  */
+        media_ptr -> fx_media_memory_buffer =  cache_entry -> fx_cached_sector_memory_buffer;
+
+        /* Return the driver status.  */
+        return(media_ptr -> fx_media_driver_status);
+    }
+#else
+    if ((logical_sector == media_ptr -> fx_media_memory_buffer_sector) && (sectors == 1) && (buffer_ptr == media_ptr -> fx_media_memory_buffer))
+    {
+#ifdef FX_ENABLE_FAULT_TOLERANT
+        if (media_ptr -> fx_media_fault_tolerant_enabled &&
+            (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED) &&
+            (sector_type == FX_DIRECTORY_SECTOR))
+        {
+
+            /* Read sector from log file. */
+            status = _fx_fault_tolerant_read_directory_sector(media_ptr, logical_sector, buffer_ptr, 1);
+
+            /* Check for successful completion.  */
+            if (status)
+            {
+
+                /* Return the error status. */
+                return(status);
+            }
+        }
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+        return(FX_SUCCESS);
+    }
+#endif
+    else
+    {
+
+        /* Direct I/O to application buffer area.  */
+
+        /* Compare against logical sector to make sure it is valid.  */
+        if ((logical_sector + sectors - 1) > (ULONG)media_ptr -> fx_media_total_sectors)
+        {
+            return(FX_SECTOR_INVALID);
+        }
+
+#ifndef FX_DISABLE_CACHE
+        /* Attempt to fill the beginning of the buffer from cached sectors.  */
+        while (sectors)
+        {
+
+            /* Determine if the sector is in the cache.  */
+            if (_fx_utility_logical_sector_cache_entry_read(media_ptr, logical_sector, &previous_cache_entry))
+            {
+
+                /* Not in the cache - get out of the loop!  */
+                break;
+            }
+
+            /* Yes, sector is in the cache. Copy the data from the cache to the destination buffer.  */
+            _fx_utility_memory_copy(media_ptr -> fx_media_memory_buffer, buffer_ptr, media_ptr -> fx_media_bytes_per_sector); /* Use case of memcpy is verified. */
+
+            /* Advance the destination buffer.  */
+            buffer_ptr =  ((UCHAR *)buffer_ptr) + media_ptr -> fx_media_bytes_per_sector;
+
+            /* Advance the sector and decrement the number of sectors left.  */
+            logical_sector++;
+            sectors--;
+        }
+
+        /* Calculate the end sector.  */
+        end_sector = logical_sector + sectors - 1;
+
+        /* Attempt to fill the end of the buffer from the opposite direction.  */
+        while (sectors)
+        {
+
+            /* Determine if the sector is in the cache.  */
+            if (_fx_utility_logical_sector_cache_entry_read(media_ptr, end_sector, &previous_cache_entry))
+            {
+
+                /* Not in the cache - get out of the loop!  */
+                break;
+            }
+
+            /* Yes, sector is in the cache. Copy the data from the cache to the destination buffer.  */
+            _fx_utility_memory_copy(media_ptr -> fx_media_memory_buffer, /* Use case of memcpy is verified. */
+                                    ((UCHAR *)buffer_ptr) + ((sectors - 1) * media_ptr -> fx_media_bytes_per_sector),
+                                    media_ptr -> fx_media_bytes_per_sector);
+
+            /* Move sector to previous sector and decrement the number of sectors left.  */
+            end_sector--;
+            sectors--;
+        }
+
+        /* Determine if there are still sectors left to read.  */
+        if (sectors == 0)
+        {
+
+            /* No more sectors to read - return success!  */
+            return(FX_SUCCESS);
+        }
+
+        /* Flush and invalidate any entries in the cache that are in this direct I/O read request range.  */
+        _fx_utility_logical_sector_flush(media_ptr, logical_sector, (ULONG64) sectors, FX_TRUE);
+#endif /* FX_DISABLE_CACHE */
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+        /* Increment the number of driver read sector(s) requests.  */
+        media_ptr -> fx_media_driver_read_requests++;
+#endif
+
+        /* Build read request to the driver.  */
+        media_ptr -> fx_media_driver_request =          FX_DRIVER_READ;
+        media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
+        media_ptr -> fx_media_driver_buffer =           buffer_ptr;
+#ifdef FX_DRIVER_USE_64BIT_LBA
+        media_ptr -> fx_media_driver_logical_sector =   logical_sector;
+#else
+        media_ptr -> fx_media_driver_logical_sector =   (ULONG)logical_sector;
+#endif
+        media_ptr -> fx_media_driver_sectors =          sectors;
+        media_ptr -> fx_media_driver_sector_type =      sector_type;
+
+        /* Determine if the sector is a data sector or a system sector.  */
+        if (sector_type == FX_DATA_SECTOR)
+        {
+
+            /* Data sector is present.  */
+            media_ptr -> fx_media_driver_data_sector_read =  FX_TRUE;
+        }
+
+        /* If trace is enabled, insert this event into the trace buffer.  */
+        FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_READ, media_ptr, logical_sector, sectors, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+        /* Invoke the driver to read the sector.  */
+        (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+        /* Clear data sector is present flag.  */
+        media_ptr -> fx_media_driver_data_sector_read =  FX_FALSE;
+
+#ifdef FX_DISABLE_CACHE
+        if ((media_ptr -> fx_media_driver_status == FX_SUCCESS) && (sectors == 1) && (buffer_ptr == media_ptr -> fx_media_memory_buffer))
+        {
+            media_ptr -> fx_media_memory_buffer_sector = logical_sector;
+#ifdef FX_ENABLE_FAULT_TOLERANT
+            if (media_ptr -> fx_media_fault_tolerant_enabled &&
+                (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED) &&
+                (sector_type == FX_DIRECTORY_SECTOR))
+            {
+
+                /* Read sector from log file. */
+                status = _fx_fault_tolerant_read_directory_sector(media_ptr, logical_sector, buffer_ptr, 1);
+
+                /* Check for successful completion.  */
+                if (status)
+                {
+
+                    /* Return the error status. */
+                    return(status);
+                }
+            }
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+            return(FX_SUCCESS);
+        }
+#endif /* FX_DISABLE_CACHE */
+
+#ifndef FX_DISABLE_DIRECT_DATA_READ_CACHE_FILL
+
+        /* Determine if the read was successful and if number of sectors just read will
+           reasonably fit into the cache.  */
+        if ((media_ptr -> fx_media_driver_status == FX_SUCCESS) && (sectors < (media_ptr -> fx_media_sector_cache_size / 4)))
+        {
+
+            /* Yes, read of direct sectors was successful.  */
+
+            /* Copy the sectors directly read into the cache so they are available on
+               subsequent read requests.  */
+            while (sectors)
+            {
+
+                /* Attempt to read the cache entry.  */
+                cache_entry =  _fx_utility_logical_sector_cache_entry_read(media_ptr, logical_sector, &previous_cache_entry);
+
+                /* Extended port-specific processing macro, which is by default defined to white space.  */
+                FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION_1
+
+                /* At this point, a cache entry should always be present since we invalidated
+                   the cache over this sector range previously. In any case, check for the error
+                   condition.  */
+                if (cache_entry == FX_NULL)
+                {
+
+                    /* This case should never happen, however, if it does simply give up on updating the
+                       cache with the sectors from the direct read.  */
+                    return(FX_SUCCESS);
+                }
+
+                /* Determine if the cache entry is dirty and needs to be written out before it is used.  */
+                if ((cache_entry -> fx_cached_sector_valid) &&
+                    (cache_entry -> fx_cached_sector_buffer_dirty))
+                {
+
+                    /* Yes, we need to flush this buffer out to the physical media
+                       before we read in the new buffer.  */
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+                    /* Increment the number of driver write sector(s) requests.  */
+                    media_ptr -> fx_media_driver_write_requests++;
+#endif
+
+                    /* Build write request to the driver.  */
+                    media_ptr -> fx_media_driver_request =          FX_DRIVER_WRITE;
+                    media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
+                    media_ptr -> fx_media_driver_buffer =           cache_entry -> fx_cached_sector_memory_buffer;
+#ifdef FX_DRIVER_USE_64BIT_LBA
+                    media_ptr -> fx_media_driver_logical_sector =   cache_entry -> fx_cached_sector;
+#else
+                    media_ptr -> fx_media_driver_logical_sector =   (ULONG)cache_entry -> fx_cached_sector;
+#endif
+                    media_ptr -> fx_media_driver_sectors =          1;
+                    media_ptr -> fx_media_driver_sector_type =      cache_entry -> fx_cached_sector_type;
+
+                    /* Only data sectors may be dirty when FX_FAULT_TOLERANT is defined */
+#ifndef FX_FAULT_TOLERANT
+                    /* Determine if the sector is a data sector or a system sector.  */
+                    if (cache_entry -> fx_cached_sector_type != FX_DATA_SECTOR)
+                    {
+
+                        /* System sector is present.  */
+                        media_ptr -> fx_media_driver_system_write =  FX_TRUE;
+                    }
+#endif /* FX_FAULT_TOLERANT */
+
+                    /* If trace is enabled, insert this event into the trace buffer.  */
+                    FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, cache_entry -> fx_cached_sector, 1, cache_entry -> fx_cached_sector_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+                    /* Invoke the driver to write the sector.  */
+                    (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+                    /* Clear the system write flag.  */
+                    media_ptr -> fx_media_driver_system_write =  FX_FALSE;
+
+                    /* Check for successful completion.  */
+                    if (media_ptr -> fx_media_driver_status)
+                    {
+
+                        /* Error writing a cached sector out.  Return the
+                           error status.  */
+                        return(media_ptr -> fx_media_driver_status);
+                    }
+
+                    /* Clear the buffer dirty flag since it has been flushed
+                       out.  */
+                    cache_entry -> fx_cached_sector_buffer_dirty =  FX_FALSE;
+
+                    /* Decrement the number of outstanding dirty cache entries.  */
+                    media_ptr -> fx_media_sector_cache_dirty_count--;
+                }
+
+                /* Now setup the cache entry with information from the new sector.  */
+
+                /* Remember the sector number.  */
+                cache_entry -> fx_cached_sector =  logical_sector;
+
+                /* Make the cache entry valid.  */
+                cache_entry -> fx_cached_sector_valid =  FX_TRUE;
+
+                /* Remember the sector type.  */
+                cache_entry -> fx_cached_sector_type =  sector_type;
+
+                /* Place this entry that the head of the cached sector
+                   list.  */
+
+                /* Determine if we need to update the last used list.  */
+                if (previous_cache_entry)
+                {
+
+                    /* Yes, the current entry is not at the front of the list
+                       so we need to change the order.  */
+
+                    /* Link the previous entry to this entry's next pointer.  */
+                    previous_cache_entry -> fx_cached_sector_next_used =
+                        cache_entry -> fx_cached_sector_next_used;
+
+                    /* Place this entry at the head of the list.  */
+                    cache_entry -> fx_cached_sector_next_used =
+                        media_ptr -> fx_media_sector_cache_list_ptr;
+                    media_ptr -> fx_media_sector_cache_list_ptr =  cache_entry;
+                }
+
+                /* Copy the data from the destination buffer to the cache entry.  */
+                _fx_utility_memory_copy(buffer_ptr, /* Use case of memcpy is verified. */
+                                        cache_entry -> fx_cached_sector_memory_buffer,
+                                        media_ptr -> fx_media_bytes_per_sector);
+
+                /* Advance the destination buffer.  */
+                buffer_ptr =  ((UCHAR *)buffer_ptr) + media_ptr -> fx_media_bytes_per_sector;
+
+                /* Advance the source sector and decrement the sector count.  */
+                logical_sector++;
+                sectors--;
+            }
+        }
+#endif
+
+        /* Return the driver status.  */
+        return(media_ptr -> fx_media_driver_status);
+    }
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_logical_sector_write.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_logical_sector_write.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_logical_sector_write.c	(revision 54)
@@ -0,0 +1,444 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Utility                                                             */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+#include "fx_utility.h"
+#ifdef FX_ENABLE_FAULT_TOLERANT
+#include "fx_fault_tolerant.h"
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_utility_logical_sector_write                    PORTABLE C      */
+/*                                                           6.1.6        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function handles logical sector write requests for all FileX   */
+/*    components.  If the logical sector is currently in the media's      */
+/*    buffer supplied by the caller, the function simply marks the buffer */
+/*    as written to.  Otherwise, physical I/O is requested through the    */
+/*    corresponding I/O driver.                                           */
+/*                                                                        */
+/*    Note: Conversion of the logical sector is done inside the driver.   */
+/*          This results in a performance boost for FLASH or RAM media    */
+/*          devices.                                                      */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    media_ptr                             Media control block pointer   */
+/*    logical_sector                        Logical sector number         */
+/*    buffer_ptr                            Pointer of sector buffer      */
+/*    sectors                               Number of sectors to write    */
+/*    sector_type                           Type of sector(s) to write    */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    return status                                                       */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _fx_utility_logical_sector_flush      Flush and invalidate sectors  */
+/*                                          that overlap with non-cache   */
+/*                                          sector I/O.                   */
+/*    I/O Driver                                                          */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    FileX System Functions                                              */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s), and      */
+/*                                            added conditional to        */
+/*                                            disable cache,              */
+/*                                            resulting in version 6.1    */
+/*  04-02-2021     Bhupendra Naphade        Modified comment(s),          */
+/*                                            updated check for logical   */
+/*                                            sector value,               */
+/*                                            resulting in version 6.1.6  */
+/*                                                                        */
+/**************************************************************************/
+UINT  _fx_utility_logical_sector_write(FX_MEDIA *media_ptr, ULONG64 logical_sector,
+                                       VOID *buffer_ptr, ULONG sectors, UCHAR sector_type)
+{
+
+#ifndef FX_DISABLE_CACHE
+FX_CACHED_SECTOR *cache_entry;
+UINT              cache_size;
+UINT              index;
+UINT              i;
+UCHAR             cache_found = FX_FALSE;
+#endif /* FX_DISABLE_CACHE */
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+    /* Determine if the request is for FAT sector.  */
+    if (sector_type == FX_FAT_SECTOR)
+    {
+
+        /* Increment the number of FAT sector writes.  */
+        media_ptr -> fx_media_fat_sector_writes++;
+    }
+
+    /* Increment the number of logical sectors written.  */
+    media_ptr -> fx_media_logical_sector_writes++;
+#endif
+
+    /* Extended port-specific processing macro, which is by default defined to white space.  */
+    FX_UTILITY_LOGICAL_SECTOR_WRITE_EXTENSION
+
+#ifndef FX_DISABLE_CACHE
+    /* Determine if the request is from the internal media buffer area.  */
+    if ((((UCHAR *)buffer_ptr) >= media_ptr -> fx_media_memory_buffer) &&
+        (((UCHAR *)buffer_ptr) <= media_ptr -> fx_media_sector_cache_end))
+    {
+
+        /* Internal cache buffer is requested.  */
+
+        /* Determine if the logical sector cache access should use the hash function.  */
+        if (media_ptr -> fx_media_sector_cache_hashed)
+        {
+
+            /* Calculate the area of the cache for this logical sector.  */
+            index =  (ULONG)(logical_sector & media_ptr -> fx_media_sector_cache_hash_mask) * FX_SECTOR_CACHE_DEPTH;
+
+            /* Build a pointer to the cache entry.  */
+            cache_entry =  &(media_ptr -> fx_media_sector_cache[index]);
+
+            for (i = 0; i < FX_SECTOR_CACHE_DEPTH; i++, cache_entry++)
+            {
+
+
+                /* Determine if the logical sector is in the cache - assuming the depth of the
+                   sector cache is 4 entries.  */
+                if ((cache_entry -> fx_cached_sector_valid) && (cache_entry -> fx_cached_sector == logical_sector))
+                {
+                    cache_found = FX_TRUE;
+                    break;
+                }
+            }
+        }
+        else
+        {
+
+            /* Search for an entry in the cache that matches this request.  */
+            cache_size =            media_ptr -> fx_media_sector_cache_size;
+            cache_entry =           media_ptr -> fx_media_sector_cache_list_ptr;
+
+            /* Look at the cache entries until a match is found or the end of
+               the cache is reached.  */
+            while (cache_size--)
+            {
+
+                /* Determine if the requested sector has been found.  */
+                if ((cache_entry -> fx_cached_sector_valid) && (cache_entry -> fx_cached_sector == logical_sector))
+                {
+                    cache_found = FX_TRUE;
+                    break;
+                }
+
+                /* Otherwise, we have not found the cached entry yet.  */
+
+                /* If there are more entries, move to the next one.  */
+                if (cache_entry -> fx_cached_sector_next_used)
+                {
+
+                    /* Move to the next cache entry.  */
+                    cache_entry =  cache_entry -> fx_cached_sector_next_used;
+                }
+            }
+        }
+
+#ifdef FX_ENABLE_FAULT_TOLERANT
+        if (media_ptr -> fx_media_fault_tolerant_enabled &&
+            (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED) &&
+            (sector_type == FX_DATA_SECTOR) &&
+            !(cache_found && (cache_entry -> fx_cached_sector_memory_buffer == buffer_ptr)))
+        {
+
+            /* Special use case for file write when fault tolerant is enabled. */
+            /* Data are read from one sector but write to another sector. */
+            /* Need to invalidate both of original and new caches. */
+            if (cache_found)
+            {
+
+                /* Invalidate the new cache. */
+                cache_entry -> fx_cached_sector_valid = FX_FALSE;
+                cache_found = FX_FALSE;
+            }
+
+            /* Search for original cache.  */
+            cache_size =            media_ptr -> fx_media_sector_cache_size;
+            cache_entry =           media_ptr -> fx_media_sector_cache_list_ptr;
+
+            /* Look at the cache entries until a match is found or the end of
+               the cache is reached.  */
+            while (cache_size--)
+            {
+
+                /* Determine if the original sector has been found.  */
+                if ((cache_entry -> fx_cached_sector_valid) &&
+                    (cache_entry -> fx_cached_sector_memory_buffer == buffer_ptr))
+                {
+
+                    /* Invalidate the original cache. */
+                    cache_entry -> fx_cached_sector_valid = FX_FALSE;
+                    break;
+                }
+
+                /* Otherwise, we have not found the cached entry yet.  */
+
+                /* If there are more entries, move to the next one.  */
+                if (cache_entry -> fx_cached_sector_next_used)
+                {
+
+                    /* Move to the next cache entry.  */
+                    cache_entry =  cache_entry -> fx_cached_sector_next_used;
+                }
+            }
+        }
+#endif /* FX_ENABLE_FAULT_TOLERANT */
+
+        if (cache_found)
+        {
+
+            /* Yes, we found a match.  */
+
+#ifdef FX_FAULT_TOLERANT
+
+            /* Check for a system sector. Data sector fault tolerance is selected with
+               the FX_FAULT_TOLERANT_DATA option.  */
+            if (sector_type != FX_DATA_SECTOR)
+            {
+
+                /* With the fault tolerant option enabled, system sectors are written immediately to
+                   the media.  */
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+                /* Increment the number of driver write sector(s) requests.  */
+                media_ptr -> fx_media_driver_write_requests++;
+#endif
+
+                /* Build write request to the driver.  */
+                media_ptr -> fx_media_driver_request =          FX_DRIVER_WRITE;
+                media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
+                media_ptr -> fx_media_driver_buffer =           cache_entry -> fx_cached_sector_memory_buffer;
+#ifdef FX_DRIVER_USE_64BIT_LBA
+                media_ptr -> fx_media_driver_logical_sector =   logical_sector;
+#else
+                media_ptr -> fx_media_driver_logical_sector =   (ULONG)logical_sector;
+#endif
+                media_ptr -> fx_media_driver_sectors =          1;
+                media_ptr -> fx_media_driver_sector_type =      sector_type;
+
+                /* Yes, a system sector write is present so set the flag.  The driver
+                   can use this flag to make extra safeguards in writing the sector
+                   out, yielding more fault tolerance.  */
+                media_ptr -> fx_media_driver_system_write =  FX_TRUE;
+
+                /* If trace is enabled, insert this event into the trace buffer.  */
+                FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, logical_sector, 1, cache_entry -> fx_cached_sector_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+                /* Invoke the driver to write the sector(s).  */
+                (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+                /* Clear the system write flag.  */
+                media_ptr -> fx_media_driver_system_write =  FX_FALSE;
+
+                /* Return success.  */
+                return(media_ptr -> fx_media_driver_status);
+            }
+#endif
+
+            /* Determine if this is the first write of this logical sector.  */
+            if (cache_entry -> fx_cached_sector_buffer_dirty == FX_FALSE)
+            {
+
+                /* Yes, increment the number of outstanding dirty sectors.  */
+                media_ptr -> fx_media_sector_cache_dirty_count++;
+
+                /* Simply mark this entry as dirty.  */
+                cache_entry -> fx_cached_sector_buffer_dirty =  FX_TRUE;
+            }
+
+            /* Don't bother updating the cache linked list since writes are
+               preceded by reads anyway.  */
+
+            /* Success, return to caller immediately!  */
+            return(FX_SUCCESS);
+        }
+
+
+        /* Okay, so if we are here the request must be for the additional FAT writes, since this is the
+           only time a write request is made without a preceding read request.  */
+
+        /* Is the logical sector valid?  */
+        if ((logical_sector == 0) || (logical_sector == ((ULONG)0xFFFFFFFF)))
+        {
+            return(FX_SECTOR_INVALID);
+        }
+
+        /* Compare logical sector against total sectors to make sure it is valid.  */
+        if ((logical_sector + sectors - 1) >= media_ptr -> fx_media_total_sectors)
+        {
+            return(FX_SECTOR_INVALID);
+        }
+
+        /* Just write the buffer to the media.  */
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+        /* Increment the number of driver write sector(s) requests.  */
+        media_ptr -> fx_media_driver_write_requests++;
+#endif
+
+        /* Build write request to the driver.  */
+        media_ptr -> fx_media_driver_request =          FX_DRIVER_WRITE;
+        media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
+        media_ptr -> fx_media_driver_buffer =           buffer_ptr;
+#ifdef FX_DRIVER_USE_64BIT_LBA
+        media_ptr -> fx_media_driver_logical_sector =   logical_sector;
+#else
+        media_ptr -> fx_media_driver_logical_sector =   (ULONG)logical_sector;
+#endif
+        media_ptr -> fx_media_driver_sectors =          sectors;
+        media_ptr -> fx_media_driver_sector_type =      sector_type;
+
+        /* Determine if the system write flag needs to be set.  */
+        if (sector_type != FX_DATA_SECTOR)
+        {
+
+            /* Yes, a system sector write is present so set the flag.  The driver
+               can use this flag to make extra safeguards in writing the sector
+               out, yielding more fault tolerance.  */
+            media_ptr -> fx_media_driver_system_write =  FX_TRUE;
+        }
+
+        /* If trace is enabled, insert this event into the trace buffer.  */
+        FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, logical_sector, sectors, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+        /* Invoke the driver to write the sector(s).  */
+        (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+        /* Clear the system write flag.  */
+        media_ptr -> fx_media_driver_system_write =  FX_FALSE;
+
+        /* Check for successful completion.  */
+        if (media_ptr -> fx_media_driver_status)
+        {
+
+            /* Error writing a internal sector out.  Return the
+               error status.  */
+            return(media_ptr -> fx_media_driver_status);
+        }
+
+        /* At this point, we have a successful write.  */
+        return(FX_SUCCESS);
+    }
+    else
+#endif /* FX_DISABLE_CACHE */
+    {
+
+        /* Otherwise, the write request is being made directly from an application
+           buffer. Determine if the logical sector is valid.  */
+
+        /* Is the logical sector valid? */
+        if ((logical_sector == 0) || (logical_sector == ((ULONG)0xFFFFFFFF)))
+        {
+            return(FX_SECTOR_INVALID);
+        }
+
+        /* Compare logical sector against total sectors to make sure it is valid.  */
+        if ((logical_sector + sectors - 1) >= media_ptr -> fx_media_total_sectors)
+        {
+            return(FX_SECTOR_INVALID);
+        }
+
+        /* Flush and invalidate for any entries in the cache that are in this direct I/O read request range.  */
+        _fx_utility_logical_sector_flush(media_ptr, logical_sector, (ULONG64) sectors, FX_TRUE);
+
+#ifdef FX_DISABLE_CACHE
+        if ((logical_sector <= media_ptr -> fx_media_memory_buffer_sector) && (logical_sector + sectors >= media_ptr -> fx_media_memory_buffer_sector))
+        {
+            media_ptr -> fx_media_memory_buffer_sector = (ULONG64)-1;
+        }
+#endif /* FX_DISABLE_CACHE */
+
+#ifndef FX_MEDIA_STATISTICS_DISABLE
+
+        /* Increment the number of driver write sector(s) requests.  */
+        media_ptr -> fx_media_driver_write_requests++;
+#endif
+
+        /* Build request to the driver.  */
+        media_ptr -> fx_media_driver_request =          FX_DRIVER_WRITE;
+        media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
+        media_ptr -> fx_media_driver_buffer =           buffer_ptr;
+#ifdef FX_DRIVER_USE_64BIT_LBA
+        media_ptr -> fx_media_driver_logical_sector =   logical_sector;
+#else
+        media_ptr -> fx_media_driver_logical_sector =   (ULONG)logical_sector;
+#endif
+        media_ptr -> fx_media_driver_sectors =          sectors;
+        media_ptr -> fx_media_driver_sector_type =      sector_type;
+
+        /* Determine if the system write flag needs to be set.  */
+        if (sector_type != FX_DATA_SECTOR)
+        {
+
+            /* Yes, a system sector write is present so set the flag.  The driver
+               can use this flag to make extra safeguards in writing the sector
+               out, yielding more fault tolerance.  */
+            media_ptr -> fx_media_driver_system_write =  FX_TRUE;
+        }
+
+        /* If trace is enabled, insert this event into the trace buffer.  */
+        FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, logical_sector, sectors, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0)
+
+        /* Invoke the driver to write the sector(s).  */
+        (media_ptr -> fx_media_driver_entry) (media_ptr);
+
+        /* Clear the system write flag.  */
+        media_ptr -> fx_media_driver_system_write =  FX_FALSE;
+
+        /* Return driver status.  */
+        return(media_ptr -> fx_media_driver_status);
+    }
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_memory_copy.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_memory_copy.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fx_utility_memory_copy.c	(revision 54)
@@ -0,0 +1,89 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Utility                                                             */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_system.h"
+#include "fx_utility.h"
+#include "string.h"
+
+
+/* Remove any previous remapping for memory copy when compiling this
+   module.  */
+
+#undef _fx_utility_memory_copy
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fx_utility_memory_copy                             PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function copies the specified number of bytes from the source  */
+/*    to the destination.                                                 */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    source_ptr                            Source memory pointer         */
+/*    dest_ptr                              Destination memory pointer    */
+/*    size                                  number of bytes to copy       */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    FileX System Functions                                              */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s), verified */
+/*                                            memcpy usage,               */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _fx_utility_memory_copy(UCHAR *source_ptr, UCHAR *dest_ptr, ULONG size)
+{
+
+    /* Copy the memory.  */
+    memcpy(dest_ptr, source_ptr, size); /* Use case of memcpy is verified. */
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fxe_media_open.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fxe_media_open.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/common/src/fxe_media_open.c	(revision 54)
@@ -0,0 +1,189 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Media                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define FX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "fx_api.h"
+#include "fx_media.h"
+#include "fx_system.h"
+
+
+FX_CALLER_CHECKING_EXTERNS
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _fxe_media_open                                     PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function checks for errors in the media open call.             */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    media_ptr                             Media control block pointer   */
+/*    media_name                            Pointer to media name string  */
+/*    media_driver                          Media driver entry function   */
+/*    driver_info_ptr                       Optional information pointer  */
+/*                                            supplied to media driver    */
+/*    memory_ptr                            Pointer to memory used by the */
+/*                                            FileX for this media.       */
+/*    memory_size                           Size of media memory - must   */
+/*                                            at least 512 bytes and      */
+/*                                            one sector size.            */
+/*    media_control_block_size              Size of FX_MEDIA structure    */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    FX_PTR_ERROR                          One or more input parameters  */
+/*                                            are NULL                    */
+/*    status                                Actual completion status      */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    tx_thread_identify                    Get current thread            */
+/*    tx_thread_preemption_change           Disable/restore preemption    */
+/*    _fx_media_open                        Actual media open service     */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     William E. Lamie         Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _fxe_media_open(FX_MEDIA *media_ptr, CHAR *media_name,
+                      VOID (*media_driver)(FX_MEDIA *), VOID *driver_info_ptr,
+                      VOID *memory_ptr, ULONG memory_size, UINT media_control_block_size)
+{
+
+UINT       status;
+ULONG      temp;
+FX_MEDIA  *current_media;
+ULONG      open_count;
+
+#ifndef FX_SINGLE_THREAD
+TX_THREAD *current_thread;
+UINT       old_threshold;
+#endif
+
+
+    /* Check for invalid input pointers.  */
+    if ((media_ptr == FX_NULL) || (media_driver == FX_NULL) || (memory_ptr == FX_NULL) || (media_control_block_size != sizeof(FX_MEDIA)))
+    {
+        return(FX_PTR_ERROR);
+    }
+
+    /* Check for a valid caller.  */
+    FX_CALLER_CHECKING_CODE
+
+    /* Check for proper size of the logical sector cache.  */
+    temp =  _fx_system_media_max_sector_cache;
+
+    /* Isolate the lowest set bit.  */
+    temp =  (temp & ((~temp) + ((ULONG) 1)));
+
+    /* If FX_MAX_SECTOR_CACHE is a power of 2, the value of temp should be unchanged.  */
+    if ((temp == 1) || (temp != _fx_system_media_max_sector_cache))
+    {
+
+        /* Not a power of 2, return an error.  */
+        return(FX_MEDIA_INVALID);
+    }
+
+    /* Check for proper size of the FAT cache.  */
+    temp =  _fx_system_media_max_fat_cache;
+
+    /* Isolate the lowest set bit.  */
+    temp =  (temp & ((~temp) + ((ULONG) 1)));
+
+    /* If FX_MAX_FAT_CACHE is a power of 2, the value of temp should be unchanged.  */
+    if ((temp == 1) || (temp != _fx_system_media_max_fat_cache))
+    {
+
+        /* Not a power of 2, return an error.  */
+        return(FX_MEDIA_INVALID);
+    }
+
+#ifndef FX_SINGLE_THREAD
+
+    /* Pickup current thread pointer. At this point we know the current thread pointer is non-null since 
+       it was checked by code in FX_CALLER_CHECKING_CODE macro.  */
+    current_thread =  tx_thread_identify();
+
+    /* Disable preemption temporarily.  */
+    tx_thread_preemption_change(current_thread, 0, &old_threshold);
+#endif
+
+    /* Loop to check for the media already opened.  */
+    current_media =  _fx_system_media_opened_ptr;
+    open_count =     _fx_system_media_opened_count;
+    while (open_count--)
+    {
+
+        /* Is the new media pointer already open?  */
+        if (media_ptr == current_media)
+        {
+
+#ifndef FX_SINGLE_THREAD
+
+            /* Restore preemption.  */
+            tx_thread_preemption_change(current_thread, old_threshold, &old_threshold);
+#endif
+
+            /* Duplicate media open, return an error!  */
+            return(FX_PTR_ERROR);
+        }
+
+        /* Move to next entry.  */
+        current_media =  current_media -> fx_media_opened_next;
+    }
+
+#ifndef FX_SINGLE_THREAD
+
+    /* Restore preemption.  */
+    tx_thread_preemption_change(current_thread, old_threshold, &old_threshold);
+#endif
+
+    /* Call actual media open service.  */
+    status =  _fx_media_open(media_ptr, media_name, media_driver, driver_info_ptr,
+                             memory_ptr, memory_size);
+
+    /* Return status.  */
+    return(status);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/ports/generic/inc/fx_port.h
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/ports/generic/inc/fx_port.h	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/filex/ports/generic/inc/fx_port.h	(revision 54)
@@ -0,0 +1,227 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */ 
+/** FileX Component                                                       */
+/**                                                                       */
+/**   Port Specific                                                       */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+
+/**************************************************************************/ 
+/*                                                                        */ 
+/*  PORT SPECIFIC C INFORMATION                            RELEASE        */ 
+/*                                                                        */ 
+/*    fx_port.h                                            Generic        */ 
+/*                                                           6.3.0        */
+/*                                                                        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */ 
+/*                                                                        */ 
+/*    This file contains data type definitions that make the FileX FAT    */ 
+/*    compatible file system function identically on a variety of         */ 
+/*    different processor architectures.  For example, the byte offset of */ 
+/*    various entries in the boot record, and directory entries are       */ 
+/*    defined in this file.                                               */ 
+/*                                                                        */ 
+/*  RELEASE HISTORY                                                       */ 
+/*                                                                        */ 
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  11-09-2020     William E. Lamie         Initial Version 6.1.2         */
+/*  03-02-2021     William E. Lamie         Modified comment(s), and      */
+/*                                            added standalone support,   */
+/*                                            resulting in version 6.1.5  */
+/*  10-31-2023     Xiuwen Cai               Modified comment(s),          */
+/*                                            added basic types guards,   */
+/*                                            resulting in version 6.3.0  */
+/*                                                                        */
+/**************************************************************************/
+
+#ifndef FX_PORT_H
+#define FX_PORT_H
+
+
+/* Determine if the optional FileX user define file should be used.  */
+
+#ifdef FX_INCLUDE_USER_DEFINE_FILE
+
+
+/* Yes, include the user defines in fx_user.h. The defines in this file may 
+   alternately be defined on the command line.  */
+
+#include "fx_user.h"
+#endif
+
+
+/* Include the ThreadX api file.  */
+
+#ifndef FX_STANDALONE_ENABLE
+#include "tx_api.h"
+
+
+/* Define ULONG64 typedef, if not already defined.  */
+
+#ifndef ULONG64_DEFINED
+#define ULONG64_DEFINED
+typedef unsigned long long  ULONG64;
+#endif
+
+#else
+
+/* Define compiler library include files.  */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifndef VOID
+#define VOID                                    void
+typedef char                                    CHAR;
+typedef char                                    BOOL;
+typedef unsigned char                           UCHAR;
+typedef int                                     INT;
+typedef unsigned int                            UINT;
+typedef long                                    LONG;
+typedef unsigned long                           ULONG;
+typedef short                                   SHORT;
+typedef unsigned short                          USHORT;
+#endif
+
+#ifndef ULONG64_DEFINED
+#define ULONG64_DEFINED
+typedef unsigned long long                      ULONG64;
+#endif
+
+/* Define basic alignment type used in block and byte pool operations. This data type must
+   be at least 32-bits in size and also be large enough to hold a pointer type.  */
+
+#ifndef ALIGN_TYPE_DEFINED
+#define ALIGN_TYPE_DEFINED
+#define ALIGN_TYPE                              ULONG
+#endif
+
+#endif
+
+/* Define FileX internal protection macros.  If FX_SINGLE_THREAD is defined,
+   these protection macros are effectively disabled.  However, for multi-thread
+   uses, the macros are setup to utilize a ThreadX mutex for multiple thread 
+   access control into an open media.  */
+
+#if defined(FX_SINGLE_THREAD) || defined(FX_STANDALONE_ENABLE)
+#define FX_PROTECT                   
+#define FX_UNPROTECT
+#else
+#define FX_PROTECT                      if (media_ptr -> fx_media_id != FX_MEDIA_ID) return(FX_MEDIA_NOT_OPEN); \
+                                        else if (tx_mutex_get(&(media_ptr -> fx_media_protect), TX_WAIT_FOREVER) != TX_SUCCESS) return(FX_MEDIA_NOT_OPEN);
+#define FX_UNPROTECT                    tx_mutex_put(&(media_ptr -> fx_media_protect));
+#endif
+
+
+/* Define interrupt lockout constructs to protect the system date/time from being updated
+   while they are being read.  */
+#ifndef FX_STANDALONE_ENABLE
+#ifndef FX_INT_SAVE_AREA
+#define FX_INT_SAVE_AREA                unsigned int  old_interrupt_posture;
+#endif
+
+#ifndef FX_DISABLE_INTS
+#define FX_DISABLE_INTS                 old_interrupt_posture =  tx_interrupt_control(TX_INT_DISABLE);
+#endif
+
+#ifndef FX_RESTORE_INTS
+#define FX_RESTORE_INTS                 tx_interrupt_control(old_interrupt_posture);
+#endif
+#else
+/* Disable use of ThreadX protection in standalone mode for FileX */
+#ifndef FX_LEGACY_INTERRUPT_PROTECTION
+#define FX_LEGACY_INTERRUPT_PROTECTION
+#endif
+#define FX_INT_SAVE_AREA
+#define FX_DISABLE_INTS
+#define FX_RESTORE_INTS
+#endif
+
+/* Define the error checking logic to determine if there is a caller error in the FileX API.  
+   The default definitions assume ThreadX is being used.  This code can be completely turned 
+   off by just defining these macros to white space.  */
+
+#ifndef FX_STANDALONE_ENABLE
+#ifndef TX_TIMER_PROCESS_IN_ISR
+
+#define FX_CALLER_CHECKING_EXTERNS      extern  TX_THREAD      *_tx_thread_current_ptr; \
+                                        extern  TX_THREAD       _tx_timer_thread; \
+                                        extern  volatile ULONG  _tx_thread_system_state;
+
+#define FX_CALLER_CHECKING_CODE         if ((TX_THREAD_GET_SYSTEM_STATE()) || \
+                                            (_tx_thread_current_ptr == TX_NULL) || \
+                                            (_tx_thread_current_ptr == &_tx_timer_thread)) \
+                                            return(FX_CALLER_ERROR);
+
+#else
+#define FX_CALLER_CHECKING_EXTERNS      extern  TX_THREAD      *_tx_thread_current_ptr; \
+                                        extern  volatile ULONG  _tx_thread_system_state;
+
+#define FX_CALLER_CHECKING_CODE         if ((TX_THREAD_GET_SYSTEM_STATE()) || \
+                                            (_tx_thread_current_ptr == TX_NULL)) \
+                                            return(FX_CALLER_ERROR);
+#endif
+#else
+#define FX_CALLER_CHECKING_EXTERNS
+#define FX_CALLER_CHECKING_CODE
+#endif
+
+
+/* Define the update rate of the system timer.  These values may also be defined at the command
+   line when compiling the fx_system_initialize.c module in the FileX library build.  Alternatively, they can
+   be modified in this file or fx_user.h. Note: the update rate must be an even number of seconds greater
+   than or equal to 2, which is the minimal update rate for FAT time. */
+
+/* Define the number of seconds the timer parameters are updated in FileX.  The default
+   value is 10 seconds.  This value can be overwritten externally. */
+
+#ifndef FX_UPDATE_RATE_IN_SECONDS
+#define FX_UPDATE_RATE_IN_SECONDS 10
+#endif
+
+
+/* Defines the number of ThreadX timer ticks required to achieve the update rate specified by 
+   FX_UPDATE_RATE_IN_SECONDS defined previously. By default, the ThreadX timer tick is 10ms, 
+   so the default value for this constant is 1000.  If TX_TIMER_TICKS_PER_SECOND is defined,
+   this value is derived from TX_TIMER_TICKS_PER_SECOND.  */
+ 
+#ifndef FX_UPDATE_RATE_IN_TICKS
+#if (defined(TX_TIMER_TICKS_PER_SECOND) && (!defined(FX_STANDALONE_ENABLE)))
+#define FX_UPDATE_RATE_IN_TICKS         (TX_TIMER_TICKS_PER_SECOND * FX_UPDATE_RATE_IN_SECONDS)
+#else
+#define FX_UPDATE_RATE_IN_TICKS         1000 
+#endif
+#endif
+
+
+/* Define the version ID of FileX.  This may be utilized by the application.  */
+
+#ifdef FX_SYSTEM_INIT
+CHAR                            _fx_version_id[] = 
+                                    "Copyright (c) Microsoft Corporation. All rights reserved.  *  FileX Generic Version 6.4.0 *";
+#else
+extern  CHAR                    _fx_version_id[];
+#endif
+
+#endif
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/inc/tx_initialize.h
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/inc/tx_initialize.h	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/inc/tx_initialize.h	(revision 54)
@@ -0,0 +1,113 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Initialize                                                          */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  COMPONENT DEFINITION                                   RELEASE        */
+/*                                                                        */
+/*    tx_initialize.h                                     PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This file defines the ThreadX initialization component, including   */
+/*    data types and external references.  It is assumed that tx_api.h    */
+/*    and tx_port.h have already been included.                           */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+
+#ifndef TX_INITIALIZE_H
+#define TX_INITIALIZE_H
+
+
+/* Define constants that indicate initialization is in progress.  */
+
+#define TX_INITIALIZE_IN_PROGRESS               ((ULONG) 0xF0F0F0F0UL)
+#define TX_INITIALIZE_ALMOST_DONE               ((ULONG) 0xF0F0F0F1UL)
+#define TX_INITIALIZE_IS_FINISHED               ((ULONG) 0x00000000UL)
+
+
+/* Define internal initialization function prototypes.  */
+
+VOID        _tx_initialize_high_level(VOID);
+VOID        _tx_initialize_kernel_setup(VOID);
+VOID        _tx_initialize_low_level(VOID);
+
+
+/* Define the macro for adding additional port-specific global data. This macro is defined
+   as white space, unless defined by tx_port.h.  */
+
+#ifndef TX_PORT_SPECIFIC_DATA
+#define TX_PORT_SPECIFIC_DATA
+#endif
+
+
+/* Define the macro for adding additional port-specific pre and post initialization processing.
+   These macros is defined as white space, unless defined by tx_port.h.  */
+
+#ifndef TX_PORT_SPECIFIC_PRE_INITIALIZATION
+#define TX_PORT_SPECIFIC_PRE_INITIALIZATION
+#endif
+
+#ifndef TX_PORT_SPECIFIC_POST_INITIALIZATION
+#define TX_PORT_SPECIFIC_POST_INITIALIZATION
+#endif
+
+#ifndef TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION
+#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION
+#endif
+
+
+/* Initialization component data declarations follow.  */
+
+/* Determine if the initialization function of this component is including
+   this file.  If so, make the data definitions really happen.  Otherwise,
+   make them extern so other functions in the component can access them.  */
+
+#ifdef TX_INITIALIZE_INIT
+#define INITIALIZE_DECLARE
+#else
+#define INITIALIZE_DECLARE extern
+#endif
+
+
+/* Define the unused memory pointer.  The value of the first available
+   memory address is placed in this variable in the low-level
+   initialization function.  The content of this variable is passed
+   to the application's system definition function.  */
+
+INITIALIZE_DECLARE VOID     *_tx_initialize_unused_memory;
+
+
+#endif
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_byte_allocate.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_byte_allocate.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_byte_allocate.c	(revision 54)
@@ -0,0 +1,411 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Byte Memory                                                         */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#ifdef TX_ENABLE_EVENT_TRACE
+#include "tx_trace.h"
+#endif
+#include "tx_thread.h"
+#include "tx_byte_pool.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_byte_allocate                                   PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function allocates bytes from the specified memory byte        */
+/*    pool.                                                               */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    pool_ptr                          Pointer to pool control block     */
+/*    memory_ptr                        Pointer to place allocated bytes  */
+/*                                        pointer                         */
+/*    memory_size                       Number of bytes to allocate       */
+/*    wait_option                       Suspension option                 */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    status                            Completion status                 */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_suspend         Suspend thread service            */
+/*    _tx_thread_system_ni_suspend      Non-interruptable suspend thread  */
+/*    _tx_byte_pool_search              Search byte pool for memory       */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _tx_byte_allocate(TX_BYTE_POOL *pool_ptr, VOID **memory_ptr, ULONG memory_size,  ULONG wait_option)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+UINT                        status;
+TX_THREAD                   *thread_ptr;
+UCHAR                       *work_ptr;
+UINT                        suspended_count;
+TX_THREAD                   *next_thread;
+TX_THREAD                   *previous_thread;
+UINT                        finished;
+#ifdef TX_ENABLE_EVENT_TRACE
+TX_TRACE_BUFFER_ENTRY       *entry_ptr;
+ULONG                       time_stamp =  ((ULONG) 0);
+#endif
+#ifdef TX_ENABLE_EVENT_LOGGING
+UCHAR                       *log_entry_ptr;
+ULONG                       upper_tbu;
+ULONG                       lower_tbu;
+#endif
+
+
+    /* Round the memory size up to the next size that is evenly divisible by
+       an ALIGN_TYPE (this is typically a 32-bit ULONG).  This guarantees proper alignment.  */
+    memory_size = (((memory_size + (sizeof(ALIGN_TYPE)))-((ALIGN_TYPE) 1))/(sizeof(ALIGN_TYPE))) * (sizeof(ALIGN_TYPE));
+
+    /* Disable interrupts.  */
+    TX_DISABLE
+
+    /* Pickup thread pointer.  */
+    TX_THREAD_GET_CURRENT(thread_ptr)
+
+#ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
+
+    /* Increment the total allocations counter.  */
+    _tx_byte_pool_performance_allocate_count++;
+
+    /* Increment the number of allocations on this pool.  */
+    pool_ptr -> tx_byte_pool_performance_allocate_count++;
+#endif
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+    /* If trace is enabled, save the current event pointer.  */
+    entry_ptr =  _tx_trace_buffer_current_ptr;
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_BYTE_ALLOCATE, pool_ptr, 0, memory_size, wait_option, TX_TRACE_BYTE_POOL_EVENTS)
+
+    /* Save the time stamp for later comparison to verify that
+       the event hasn't been overwritten by the time the allocate
+       call succeeds.  */
+    if (entry_ptr != TX_NULL)
+    {
+
+        time_stamp =  entry_ptr -> tx_trace_buffer_entry_time_stamp;
+    }
+#endif
+
+#ifdef TX_ENABLE_EVENT_LOGGING
+    log_entry_ptr =  *(UCHAR **) _tx_el_current_event;
+
+    /* Log this kernel call.  */
+    TX_EL_BYTE_ALLOCATE_INSERT
+
+    /* Store -1 in the fourth event slot.  */
+    *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =  (ULONG) -1;
+
+    /* Save the time stamp for later comparison to verify that
+       the event hasn't been overwritten by the time the allocate
+       call succeeds.  */
+    lower_tbu =  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET));
+    upper_tbu =  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET));
+#endif
+
+    /* Set the search finished flag to false.  */
+    finished =  TX_FALSE;
+
+    /* Loop to handle cases where the owner of the pool changed.  */
+    do
+    {
+
+        /* Indicate that this thread is the current owner.  */
+        pool_ptr -> tx_byte_pool_owner =  thread_ptr;
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* At this point, the executing thread owns the pool and can perform a search
+           for free memory.  */
+        work_ptr =  _tx_byte_pool_search(pool_ptr, memory_size);
+
+        /* Optional processing extension.  */
+        TX_BYTE_ALLOCATE_EXTENSION
+
+        /* Lockout interrupts.  */
+        TX_DISABLE
+
+        /* Determine if we are finished.  */
+        if (work_ptr != TX_NULL)
+        {
+
+            /* Yes, we have found a block the search is finished.  */
+            finished =  TX_TRUE;
+        }
+        else
+        {
+
+            /* No block was found, does this thread still own the pool?  */
+            if (pool_ptr -> tx_byte_pool_owner == thread_ptr)
+            {
+
+                /* Yes, then we have looked through the entire pool and haven't found the memory.  */
+                finished =  TX_TRUE;
+            }
+        }
+
+    } while (finished == TX_FALSE);
+
+    /* Copy the pointer into the return destination.  */
+    *memory_ptr =  (VOID *) work_ptr;
+
+    /* Determine if memory was found.  */
+    if (work_ptr != TX_NULL)
+    {
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+        /* Check that the event time stamp is unchanged.  A different
+           timestamp means that a later event wrote over the byte
+           allocate event.  In that case, do nothing here.  */
+        if (entry_ptr != TX_NULL)
+        {
+
+            /* Is the timestamp the same?  */
+            if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
+            {
+
+                /* Timestamp is the same, update the entry with the address.  */
+#ifdef TX_MISRA_ENABLE
+                entry_ptr -> tx_trace_buffer_entry_info_2 =  TX_POINTER_TO_ULONG_CONVERT(*memory_ptr);
+#else
+                entry_ptr -> tx_trace_buffer_entry_information_field_2 =  TX_POINTER_TO_ULONG_CONVERT(*memory_ptr);
+#endif
+            }
+        }
+#endif
+
+#ifdef TX_ENABLE_EVENT_LOGGING
+        /* Check that the event time stamp is unchanged.  A different
+           timestamp means that a later event wrote over the byte
+           allocate event.  In that case, do nothing here.  */
+        if (lower_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) &&
+            upper_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)))
+        {
+            /* Store the address of the allocated fragment.  */
+            *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =  (ULONG) *memory_ptr;
+        }
+#endif
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Set the status to success.  */
+        status =  TX_SUCCESS;
+    }
+    else
+    {
+
+        /* No memory of sufficient size was found...  */
+
+        /* Determine if the request specifies suspension.  */
+        if (wait_option != TX_NO_WAIT)
+        {
+
+            /* Determine if the preempt disable flag is non-zero.  */
+            if (_tx_thread_preempt_disable != ((UINT) 0))
+            {
+
+                /* Suspension is not allowed if the preempt disable flag is non-zero at this point - return error completion.  */
+                status =  TX_NO_MEMORY;
+
+                /* Restore interrupts.  */
+                TX_RESTORE
+            }
+            else
+            {
+
+                /* Prepare for suspension of this thread.  */
+
+#ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
+
+                /* Increment the total suspensions counter.  */
+                _tx_byte_pool_performance_suspension_count++;
+
+                /* Increment the number of suspensions on this pool.  */
+                pool_ptr -> tx_byte_pool_performance_suspension_count++;
+#endif
+
+                /* Setup cleanup routine pointer.  */
+                thread_ptr -> tx_thread_suspend_cleanup =  &(_tx_byte_pool_cleanup);
+
+                /* Setup cleanup information, i.e. this pool control
+                   block.  */
+                thread_ptr -> tx_thread_suspend_control_block =  (VOID *) pool_ptr;
+
+                /* Save the return memory pointer address as well.  */
+                thread_ptr -> tx_thread_additional_suspend_info =  (VOID *) memory_ptr;
+
+                /* Save the byte size requested.  */
+                thread_ptr -> tx_thread_suspend_info =  memory_size;
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+                /* Increment the suspension sequence number, which is used to identify
+                   this suspension event.  */
+                thread_ptr -> tx_thread_suspension_sequence++;
+#endif
+
+                /* Pickup the number of suspended threads.  */
+                suspended_count =  pool_ptr -> tx_byte_pool_suspended_count;
+
+                /* Increment the suspension count.  */
+                (pool_ptr -> tx_byte_pool_suspended_count)++;
+
+                /* Setup suspension list.  */
+                if (suspended_count == TX_NO_SUSPENSIONS)
+                {
+
+                    /* No other threads are suspended.  Setup the head pointer and
+                       just setup this threads pointers to itself.  */
+                    pool_ptr -> tx_byte_pool_suspension_list =      thread_ptr;
+                    thread_ptr -> tx_thread_suspended_next =        thread_ptr;
+                    thread_ptr -> tx_thread_suspended_previous =    thread_ptr;
+                }
+                else
+                {
+
+                    /* This list is not NULL, add current thread to the end. */
+                    next_thread =                                   pool_ptr -> tx_byte_pool_suspension_list;
+                    thread_ptr -> tx_thread_suspended_next =        next_thread;
+                    previous_thread =                               next_thread -> tx_thread_suspended_previous;
+                    thread_ptr -> tx_thread_suspended_previous =    previous_thread;
+                    previous_thread -> tx_thread_suspended_next =   thread_ptr;
+                    next_thread -> tx_thread_suspended_previous =   thread_ptr;
+                }
+
+                /* Set the state to suspended.  */
+                thread_ptr -> tx_thread_state =       TX_BYTE_MEMORY;
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+                /* Call actual non-interruptable thread suspension routine.  */
+                _tx_thread_system_ni_suspend(thread_ptr, wait_option);
+
+                /* Restore interrupts.  */
+                TX_RESTORE
+#else
+
+                /* Set the suspending flag.  */
+                thread_ptr -> tx_thread_suspending =  TX_TRUE;
+
+                /* Setup the timeout period.  */
+                thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option;
+
+                /* Temporarily disable preemption.  */
+                _tx_thread_preempt_disable++;
+
+                /* Restore interrupts.  */
+                TX_RESTORE
+
+                /* Call actual thread suspension routine.  */
+                _tx_thread_system_suspend(thread_ptr);
+#endif
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+                /* Check that the event time stamp is unchanged.  A different
+                   timestamp means that a later event wrote over the byte
+                   allocate event.  In that case, do nothing here.  */
+                if (entry_ptr != TX_NULL)
+                {
+
+                    /* Is the timestamp the same?  */
+                    if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
+                    {
+
+                        /* Timestamp is the same, update the entry with the address.  */
+#ifdef TX_MISRA_ENABLE
+                        entry_ptr -> tx_trace_buffer_entry_info_2 =  TX_POINTER_TO_ULONG_CONVERT(*memory_ptr);
+#else
+                       entry_ptr -> tx_trace_buffer_entry_information_field_2 =  TX_POINTER_TO_ULONG_CONVERT(*memory_ptr);
+#endif
+                    }
+                }
+#endif
+
+#ifdef TX_ENABLE_EVENT_LOGGING
+                /* Check that the event time stamp is unchanged.  A different
+                   timestamp means that a later event wrote over the byte
+                   allocate event.  In that case, do nothing here.  */
+                if (lower_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) &&
+                    upper_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)))
+                {
+
+                    /* Store the address of the allocated fragment.  */
+                    *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =  (ULONG) *memory_ptr;
+                }
+#endif
+
+                /* Return the completion status.  */
+                status =  thread_ptr -> tx_thread_suspend_status;
+            }
+        }
+        else
+        {
+
+            /* Restore interrupts.  */
+            TX_RESTORE
+
+            /* Immediate return, return error completion.  */
+            status =  TX_NO_MEMORY;
+        }
+    }
+
+    /* Return completion status.  */
+    return(status);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_byte_pool_cleanup.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_byte_pool_cleanup.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_byte_pool_cleanup.c	(revision 54)
@@ -0,0 +1,214 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Byte Memory                                                         */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_byte_pool.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_byte_pool_cleanup                               PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function processes byte allocate timeout and thread terminate  */
+/*    actions that require the byte pool data structures to be cleaned    */
+/*    up.                                                                 */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    thread_ptr                        Pointer to suspended thread's     */
+/*                                        control block                   */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_resume          Resume thread service             */
+/*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_thread_timeout                Thread timeout processing         */
+/*    _tx_thread_terminate              Thread terminate processing       */
+/*    _tx_thread_wait_abort             Thread wait abort processing      */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_byte_pool_cleanup(TX_THREAD *thread_ptr, ULONG suspension_sequence)
+{
+
+#ifndef TX_NOT_INTERRUPTABLE
+TX_INTERRUPT_SAVE_AREA
+#endif
+
+TX_BYTE_POOL        *pool_ptr;
+UINT                suspended_count;
+TX_THREAD           *next_thread;
+TX_THREAD           *previous_thread;
+
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+    /* Disable interrupts to remove the suspended thread from the byte pool.  */
+    TX_DISABLE
+
+    /* Determine if the cleanup is still required.  */
+    if (thread_ptr -> tx_thread_suspend_cleanup == &(_tx_byte_pool_cleanup))
+    {
+
+        /* Check for valid suspension sequence.  */
+        if (suspension_sequence == thread_ptr -> tx_thread_suspension_sequence)
+        {
+
+            /* Setup pointer to byte pool control block.  */
+            pool_ptr =  TX_VOID_TO_BYTE_POOL_POINTER_CONVERT(thread_ptr -> tx_thread_suspend_control_block);
+
+            /* Check for a NULL byte pool pointer.  */
+            if (pool_ptr != TX_NULL)
+            {
+
+                /* Check for valid pool ID.  */
+                if (pool_ptr -> tx_byte_pool_id == TX_BYTE_POOL_ID)
+                {
+
+                    /* Determine if there are any thread suspensions.  */
+                    if (pool_ptr -> tx_byte_pool_suspended_count != TX_NO_SUSPENSIONS)
+                    {
+#else
+
+                        /* Setup pointer to byte pool control block.  */
+                        pool_ptr =  TX_VOID_TO_BYTE_POOL_POINTER_CONVERT(thread_ptr -> tx_thread_suspend_control_block);
+#endif
+
+                        /* Thread suspended for memory... Clear the suspension cleanup flag.  */
+                        thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
+
+                        /* Decrement the suspension count.  */
+                        pool_ptr -> tx_byte_pool_suspended_count--;
+
+                        /* Pickup the suspended count.  */
+                        suspended_count =  pool_ptr -> tx_byte_pool_suspended_count;
+
+                        /* Remove the suspended thread from the list.  */
+
+                        /* See if this is the only suspended thread on the list.  */
+                        if (suspended_count == TX_NO_SUSPENSIONS)
+                        {
+
+                            /* Yes, the only suspended thread.  */
+
+                            /* Update the head pointer.  */
+                            pool_ptr -> tx_byte_pool_suspension_list =  TX_NULL;
+                        }
+                        else
+                        {
+
+                            /* At least one more thread is on the same suspension list.  */
+
+                            /* Update the links of the adjacent threads.  */
+                            next_thread =                                   thread_ptr -> tx_thread_suspended_next;
+                            previous_thread =                               thread_ptr -> tx_thread_suspended_previous;
+                            next_thread -> tx_thread_suspended_previous =   previous_thread;
+                            previous_thread -> tx_thread_suspended_next =   next_thread;
+
+                            /* Determine if we need to update the head pointer.  */
+                            if (pool_ptr -> tx_byte_pool_suspension_list == thread_ptr)
+                            {
+
+                                /* Update the list head pointer.  */
+                                pool_ptr -> tx_byte_pool_suspension_list =      next_thread;
+                            }
+                        }
+
+                        /* Now we need to determine if this cleanup is from a terminate, timeout,
+                           or from a wait abort.  */
+                        if (thread_ptr -> tx_thread_state == TX_BYTE_MEMORY)
+                        {
+
+                            /* Timeout condition and the thread still suspended on the byte pool.
+                               Setup return error status and resume the thread.  */
+
+#ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
+
+                            /* Increment the total timeouts counter.  */
+                            _tx_byte_pool_performance_timeout_count++;
+
+                            /* Increment the number of timeouts on this byte pool.  */
+                            pool_ptr -> tx_byte_pool_performance_timeout_count++;
+#endif
+
+                            /* Setup return status.  */
+                            thread_ptr -> tx_thread_suspend_status =  TX_NO_MEMORY;
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+                            /* Resume the thread!  */
+                            _tx_thread_system_ni_resume(thread_ptr);
+#else
+
+                            /* Temporarily disable preemption.  */
+                            _tx_thread_preempt_disable++;
+
+                            /* Restore interrupts.  */
+                            TX_RESTORE
+
+                            /* Resume the thread!  */
+                            _tx_thread_system_resume(thread_ptr);
+
+                            /* Disable interrupts.  */
+                            TX_DISABLE
+#endif
+                        }
+#ifndef TX_NOT_INTERRUPTABLE
+                    }
+                }
+            }
+        }
+    }
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+#endif
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_byte_pool_create.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_byte_pool_create.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_byte_pool_create.c	(revision 54)
@@ -0,0 +1,199 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Byte Pool                                                           */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_trace.h"
+#include "tx_byte_pool.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_byte_pool_create                                PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function creates a pool of memory bytes in the specified       */
+/*    memory area.                                                        */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    pool_ptr                          Pointer to pool control block     */
+/*    name_ptr                          Pointer to byte pool name         */
+/*    pool_start                        Address of beginning of pool area */
+/*    pool_size                         Number of bytes in the byte pool  */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_SUCCESS                        Successful completion status      */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _tx_byte_pool_create(TX_BYTE_POOL *pool_ptr, CHAR *name_ptr, VOID *pool_start, ULONG pool_size)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+UCHAR               *block_ptr;
+UCHAR               **block_indirect_ptr;
+UCHAR               *temp_ptr;
+TX_BYTE_POOL        *next_pool;
+TX_BYTE_POOL        *previous_pool;
+ALIGN_TYPE          *free_ptr;
+
+
+    /* Initialize the byte pool control block to all zeros.  */
+    TX_MEMSET(pool_ptr, 0, (sizeof(TX_BYTE_POOL)));
+
+    /* Round the pool size down to something that is evenly divisible by
+       an ULONG.  */
+    pool_size =   (pool_size/(sizeof(ALIGN_TYPE))) * (sizeof(ALIGN_TYPE));
+
+    /* Setup the basic byte pool fields.  */
+    pool_ptr -> tx_byte_pool_name =              name_ptr;
+
+    /* Save the start and size of the pool.  */
+    pool_ptr -> tx_byte_pool_start =   TX_VOID_TO_UCHAR_POINTER_CONVERT(pool_start);
+    pool_ptr -> tx_byte_pool_size =    pool_size;
+
+    /* Setup memory list to the beginning as well as the search pointer.  */
+    pool_ptr -> tx_byte_pool_list =    TX_VOID_TO_UCHAR_POINTER_CONVERT(pool_start);
+    pool_ptr -> tx_byte_pool_search =  TX_VOID_TO_UCHAR_POINTER_CONVERT(pool_start);
+
+    /* Initially, the pool will have two blocks.  One large block at the
+       beginning that is available and a small allocated block at the end
+       of the pool that is there just for the algorithm.  Be sure to count
+       the available block's header in the available bytes count.  */
+    pool_ptr -> tx_byte_pool_available =   pool_size - ((sizeof(VOID *)) + (sizeof(ALIGN_TYPE)));
+    pool_ptr -> tx_byte_pool_fragments =   ((UINT) 2);
+
+    /* Each block contains a "next" pointer that points to the next block in the pool followed by a ALIGN_TYPE
+       field that contains either the constant TX_BYTE_BLOCK_FREE (if the block is free) or a pointer to the
+       owning pool (if the block is allocated).  */
+
+    /* Calculate the end of the pool's memory area.  */
+    block_ptr =  TX_VOID_TO_UCHAR_POINTER_CONVERT(pool_start);
+    block_ptr =  TX_UCHAR_POINTER_ADD(block_ptr, pool_size);
+
+    /* Backup the end of the pool pointer and build the pre-allocated block.  */
+    block_ptr =  TX_UCHAR_POINTER_SUB(block_ptr, (sizeof(ALIGN_TYPE)));
+
+    /* Cast the pool pointer into a ULONG.  */
+    temp_ptr =             TX_BYTE_POOL_TO_UCHAR_POINTER_CONVERT(pool_ptr);
+    block_indirect_ptr =   TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(block_ptr);
+    *block_indirect_ptr =  temp_ptr;
+
+    block_ptr =            TX_UCHAR_POINTER_SUB(block_ptr, (sizeof(UCHAR *)));
+    block_indirect_ptr =   TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(block_ptr);
+    *block_indirect_ptr =  TX_VOID_TO_UCHAR_POINTER_CONVERT(pool_start);
+
+    /* Now setup the large available block in the pool.  */
+    temp_ptr =             TX_VOID_TO_UCHAR_POINTER_CONVERT(pool_start);
+    block_indirect_ptr =   TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(temp_ptr);
+    *block_indirect_ptr =  block_ptr;
+    block_ptr =            TX_VOID_TO_UCHAR_POINTER_CONVERT(pool_start);
+    block_ptr =            TX_UCHAR_POINTER_ADD(block_ptr, (sizeof(UCHAR *)));
+    free_ptr =             TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(block_ptr);
+    *free_ptr =            TX_BYTE_BLOCK_FREE;
+
+    /* Clear the owner id.  */
+    pool_ptr -> tx_byte_pool_owner =  TX_NULL;
+
+    /* Disable interrupts to place the byte pool on the created list.  */
+    TX_DISABLE
+
+    /* Setup the byte pool ID to make it valid.  */
+    pool_ptr -> tx_byte_pool_id =  TX_BYTE_POOL_ID;
+
+    /* Place the byte pool on the list of created byte pools.  First,
+       check for an empty list.  */
+    if (_tx_byte_pool_created_count == TX_EMPTY)
+    {
+
+        /* The created byte pool list is empty.  Add byte pool to empty list.  */
+        _tx_byte_pool_created_ptr =                  pool_ptr;
+        pool_ptr -> tx_byte_pool_created_next =      pool_ptr;
+        pool_ptr -> tx_byte_pool_created_previous =  pool_ptr;
+    }
+    else
+    {
+
+        /* This list is not NULL, add to the end of the list.  */
+        next_pool =      _tx_byte_pool_created_ptr;
+        previous_pool =  next_pool -> tx_byte_pool_created_previous;
+
+        /* Place the new byte pool in the list.  */
+        next_pool -> tx_byte_pool_created_previous =  pool_ptr;
+        previous_pool -> tx_byte_pool_created_next =  pool_ptr;
+
+        /* Setup this byte pool's created links.  */
+        pool_ptr -> tx_byte_pool_created_previous =  previous_pool;
+        pool_ptr -> tx_byte_pool_created_next =      next_pool;
+    }
+
+    /* Increment the number of created byte pools.  */
+    _tx_byte_pool_created_count++;
+
+    /* Optional byte pool create extended processing.  */
+    TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr)
+
+    /* If trace is enabled, register this object.  */
+    TX_TRACE_OBJECT_REGISTER(TX_TRACE_OBJECT_TYPE_BYTE_POOL, pool_ptr, name_ptr, pool_size, 0)
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_BYTE_POOL_CREATE, pool_ptr, TX_POINTER_TO_ULONG_CONVERT(pool_start), pool_size, TX_POINTER_TO_ULONG_CONVERT(&block_ptr), TX_TRACE_BYTE_POOL_EVENTS)
+
+    /* Log this kernel call.  */
+    TX_EL_BYTE_POOL_CREATE_INSERT
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Return TX_SUCCESS.  */
+    return(TX_SUCCESS);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_byte_pool_search.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_byte_pool_search.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_byte_pool_search.c	(revision 54)
@@ -0,0 +1,354 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Byte Pool                                                           */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_byte_pool.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_byte_pool_search                                PORTABLE C      */
+/*                                                           6.1.7        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function searches a byte pool for a memory block to satisfy    */
+/*    the requested number of bytes.  Merging of adjacent free blocks     */
+/*    takes place during the search and a split of the block that         */
+/*    satisfies the request may occur before this function returns.       */
+/*                                                                        */
+/*    It is assumed that this function is called with interrupts enabled  */
+/*    and with the tx_pool_owner field set to the thread performing the   */
+/*    search.  Also note that the search can occur during allocation and  */
+/*    release of a memory block.                                          */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    pool_ptr                          Pointer to pool control block     */
+/*    memory_size                       Number of bytes required          */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    UCHAR *                           Pointer to the allocated memory,  */
+/*                                        if successful.  Otherwise, a    */
+/*                                        NULL is returned                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_byte_allocate                 Allocate bytes of memory          */
+/*    _tx_byte_release                  Release bytes of memory           */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020      William E. Lamie        Initial Version 6.0           */
+/*  09-30-2020      Yuxin Zhou              Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*  06-02-2021      Scott Larson            Improve possible free bytes   */
+/*                                            calculation,                */
+/*                                            resulting in version 6.1.7  */
+/*                                                                        */
+/**************************************************************************/
+UCHAR  *_tx_byte_pool_search(TX_BYTE_POOL *pool_ptr, ULONG memory_size)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+UCHAR           *current_ptr;
+UCHAR           *next_ptr;
+UCHAR           **this_block_link_ptr;
+UCHAR           **next_block_link_ptr;
+ULONG           available_bytes;
+UINT            examine_blocks;
+UINT            first_free_block_found =  TX_FALSE;
+TX_THREAD       *thread_ptr;
+ALIGN_TYPE      *free_ptr;
+UCHAR           *work_ptr;
+ULONG           total_theoretical_available;
+
+
+    /* Disable interrupts.  */
+    TX_DISABLE
+
+    /* First, determine if there are enough bytes in the pool.  */
+    /* Theoretical bytes available = free bytes + ((fragments-2) * overhead of each block) */
+    total_theoretical_available = pool_ptr -> tx_byte_pool_available + ((pool_ptr -> tx_byte_pool_fragments - 2) * ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE))));
+    if (memory_size >= total_theoretical_available)
+    {
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Not enough memory, return a NULL pointer.  */
+        current_ptr =  TX_NULL;
+    }
+    else
+    {
+
+        /* Pickup thread pointer.  */
+        TX_THREAD_GET_CURRENT(thread_ptr)
+
+        /* Setup ownership of the byte pool.  */
+        pool_ptr -> tx_byte_pool_owner =  thread_ptr;
+
+        /* Walk through the memory pool in search for a large enough block.  */
+        current_ptr =      pool_ptr -> tx_byte_pool_search;
+        examine_blocks =   pool_ptr -> tx_byte_pool_fragments + ((UINT) 1);
+        available_bytes =  ((ULONG) 0);
+        do
+        {
+
+
+#ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
+
+            /* Increment the total fragment search counter.  */
+            _tx_byte_pool_performance_search_count++;
+
+            /* Increment the number of fragments searched on this pool.  */
+            pool_ptr -> tx_byte_pool_performance_search_count++;
+#endif
+
+            /* Check to see if this block is free.  */
+            work_ptr =  TX_UCHAR_POINTER_ADD(current_ptr, (sizeof(UCHAR *)));
+            free_ptr =  TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr);
+            if ((*free_ptr) == TX_BYTE_BLOCK_FREE)
+            {
+
+                /* Determine if this is the first free block.  */
+                if (first_free_block_found == TX_FALSE)
+                {
+                    /* This is the first free block.  */
+                    pool_ptr->tx_byte_pool_search =  current_ptr;
+
+                    /* Set the flag to indicate we have found the first free
+                       block.  */
+                    first_free_block_found =  TX_TRUE;
+                }
+
+                /* Block is free, see if it is large enough.  */
+
+                /* Pickup the next block's pointer.  */
+                this_block_link_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
+                next_ptr =             *this_block_link_ptr;
+
+                /* Calculate the number of bytes available in this block.  */
+                available_bytes =   TX_UCHAR_POINTER_DIF(next_ptr, current_ptr);
+                available_bytes =   available_bytes - ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)));
+
+                /* If this is large enough, we are done because our first-fit algorithm
+                   has been satisfied!  */
+                if (available_bytes >= memory_size)
+                {
+                    /* Get out of the search loop!  */
+                    break;
+                }
+                else
+                {
+
+                    /* Clear the available bytes variable.  */
+                    available_bytes =  ((ULONG) 0);
+
+                    /* Not enough memory, check to see if the neighbor is
+                       free and can be merged.  */
+                    work_ptr =  TX_UCHAR_POINTER_ADD(next_ptr, (sizeof(UCHAR *)));
+                    free_ptr =  TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr);
+                    if ((*free_ptr) == TX_BYTE_BLOCK_FREE)
+                    {
+
+                        /* Yes, neighbor block can be merged!  This is quickly accomplished
+                           by updating the current block with the next blocks pointer.  */
+                        next_block_link_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr);
+                        *this_block_link_ptr =  *next_block_link_ptr;
+
+                        /* Reduce the fragment total.  We don't need to increase the bytes
+                           available because all free headers are also included in the available
+                           count.  */
+                        pool_ptr -> tx_byte_pool_fragments--;
+
+#ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
+
+                        /* Increment the total merge counter.  */
+                        _tx_byte_pool_performance_merge_count++;
+
+                        /* Increment the number of blocks merged on this pool.  */
+                        pool_ptr -> tx_byte_pool_performance_merge_count++;
+#endif
+
+                        /* See if the search pointer is affected.  */
+                        if (pool_ptr -> tx_byte_pool_search ==  next_ptr)
+                        {
+                            /* Yes, update the search pointer.   */
+                            pool_ptr -> tx_byte_pool_search =  current_ptr;
+                        }
+                    }
+                    else
+                    {
+                        /* Neighbor is not free so we can skip over it!  */
+                        next_block_link_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr);
+                        current_ptr =  *next_block_link_ptr;
+
+                        /* Decrement the examined block count to account for this one.  */
+                        if (examine_blocks != ((UINT) 0))
+                        {
+                            examine_blocks--;
+
+#ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
+
+                            /* Increment the total fragment search counter.  */
+                            _tx_byte_pool_performance_search_count++;
+
+                            /* Increment the number of fragments searched on this pool.  */
+                            pool_ptr -> tx_byte_pool_performance_search_count++;
+#endif
+                        }
+                    }
+                }
+            }
+            else
+            {
+
+                /* Block is not free, move to next block.  */
+                this_block_link_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
+                current_ptr =  *this_block_link_ptr;
+            }
+
+            /* Another block has been searched... decrement counter.  */
+            if (examine_blocks != ((UINT) 0))
+            {
+
+                examine_blocks--;
+            }
+
+            /* Restore interrupts temporarily.  */
+            TX_RESTORE
+
+            /* Disable interrupts.  */
+            TX_DISABLE
+
+            /* Determine if anything has changed in terms of pool ownership.  */
+            if (pool_ptr -> tx_byte_pool_owner != thread_ptr)
+            {
+
+                /* Pool changed ownership in the brief period interrupts were
+                   enabled.  Reset the search.  */
+                current_ptr =      pool_ptr -> tx_byte_pool_search;
+                examine_blocks =   pool_ptr -> tx_byte_pool_fragments + ((UINT) 1);
+
+                /* Setup our ownership again.  */
+                pool_ptr -> tx_byte_pool_owner =  thread_ptr;
+            }
+        } while(examine_blocks != ((UINT) 0));
+
+        /* Determine if a block was found.  If so, determine if it needs to be
+           split.  */
+        if (available_bytes != ((ULONG) 0))
+        {
+
+            /* Determine if we need to split this block.  */
+            if ((available_bytes - memory_size) >= ((ULONG) TX_BYTE_BLOCK_MIN))
+            {
+
+                /* Split the block.  */
+                next_ptr =  TX_UCHAR_POINTER_ADD(current_ptr, (memory_size + ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)))));
+
+                /* Setup the new free block.  */
+                next_block_link_ptr =   TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr);
+                this_block_link_ptr =   TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
+                *next_block_link_ptr =  *this_block_link_ptr;
+                work_ptr =              TX_UCHAR_POINTER_ADD(next_ptr, (sizeof(UCHAR *)));
+                free_ptr =              TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr);
+                *free_ptr =             TX_BYTE_BLOCK_FREE;
+
+                /* Increase the total fragment counter.  */
+                pool_ptr -> tx_byte_pool_fragments++;
+
+                /* Update the current pointer to point at the newly created block.  */
+                *this_block_link_ptr =  next_ptr;
+
+                /* Set available equal to memory size for subsequent calculation.  */
+                available_bytes =  memory_size;
+
+#ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
+
+                /* Increment the total split counter.  */
+                _tx_byte_pool_performance_split_count++;
+
+                /* Increment the number of blocks split on this pool.  */
+                pool_ptr -> tx_byte_pool_performance_split_count++;
+#endif
+            }
+
+            /* In any case, mark the current block as allocated.  */
+            work_ptr =              TX_UCHAR_POINTER_ADD(current_ptr, (sizeof(UCHAR *)));
+            this_block_link_ptr =   TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr);
+            *this_block_link_ptr =  TX_BYTE_POOL_TO_UCHAR_POINTER_CONVERT(pool_ptr);
+
+            /* Reduce the number of available bytes in the pool.  */
+            pool_ptr -> tx_byte_pool_available =  (pool_ptr -> tx_byte_pool_available - available_bytes) - ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)));
+
+            /* Determine if the search pointer needs to be updated. This is only done
+               if the search pointer matches the block to be returned.  */
+            if (current_ptr == pool_ptr -> tx_byte_pool_search)
+            {
+
+                /* Yes, update the search pointer to the next block.  */
+                this_block_link_ptr =   TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
+                pool_ptr -> tx_byte_pool_search =  *this_block_link_ptr;
+            }
+
+            /* Restore interrupts.  */
+            TX_RESTORE
+
+            /* Adjust the pointer for the application.  */
+            current_ptr =  TX_UCHAR_POINTER_ADD(current_ptr, (((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)))));
+        }
+        else
+        {
+
+            /* Restore interrupts.  */
+            TX_RESTORE
+
+            /* Set current pointer to NULL to indicate nothing was found.  */
+            current_ptr =  TX_NULL;
+        }
+    }
+
+    /* Return the search pointer.  */
+    return(current_ptr);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_initialize_high_level.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_initialize_high_level.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_initialize_high_level.c	(revision 54)
@@ -0,0 +1,152 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Initialize                                                          */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_trace.h"
+
+/* Determine if in-line initialization is required.  */
+#ifdef TX_INLINE_INITIALIZATION
+#define TX_INVOKE_INLINE_INITIALIZATION
+#endif
+
+#include "tx_initialize.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+#include "tx_semaphore.h"
+#include "tx_queue.h"
+#include "tx_event_flags.h"
+#include "tx_mutex.h"
+#include "tx_block_pool.h"
+#include "tx_byte_pool.h"
+
+
+/* Define the unused memory pointer.  The value of the first available
+   memory address is placed in this variable in the low-level
+   initialization function.  The content of this variable is passed
+   to the application's system definition function.  */
+
+VOID     *_tx_initialize_unused_memory;
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_initialize_high_level                           PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function is responsible for initializing all of the other      */
+/*    components in the ThreadX real-time kernel.                         */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_initialize             Initialize the thread control     */
+/*                                        component                       */
+/*    _tx_timer_initialize              Initialize the timer control      */
+/*                                        component                       */
+/*    _tx_semaphore_initialize          Initialize the semaphore control  */
+/*                                        component                       */
+/*    _tx_queue_initialize              Initialize the queue control      */
+/*                                        component                       */
+/*    _tx_event_flags_initialize        Initialize the event flags control*/
+/*                                        component                       */
+/*    _tx_block_pool_initialize         Initialize the block pool control */
+/*                                        component                       */
+/*    _tx_byte_pool_initialize          Initialize the byte pool control  */
+/*                                        component                       */
+/*    _tx_mutex_initialize              Initialize the mutex control      */
+/*                                        component                       */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_initialize_kernel_enter       Kernel entry function             */
+/*    _tx_initialize_kernel_setup       Early kernel setup function that  */
+/*                                        is optionally called by         */
+/*                                        compiler's startup code.        */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID    _tx_initialize_high_level(VOID)
+{
+
+    /* Initialize event tracing, if enabled.  */
+    TX_TRACE_INITIALIZE
+
+    /* Initialize the event log, if enabled.  */
+    TX_EL_INITIALIZE
+
+    /* Call the thread control initialization function.  */
+    _tx_thread_initialize();
+
+#ifndef TX_NO_TIMER
+
+    /* Call the timer control initialization function.  */
+    _tx_timer_initialize();
+#endif
+
+#ifndef TX_DISABLE_REDUNDANT_CLEARING
+
+    /* Call the semaphore initialization function.  */
+    _tx_semaphore_initialize();
+
+    /* Call the queue initialization function.  */
+    _tx_queue_initialize();
+
+    /* Call the event flag initialization function.  */
+    _tx_event_flags_initialize();
+
+    /* Call the block pool initialization function.  */
+    _tx_block_pool_initialize();
+
+    /* Call the byte pool initialization function.  */
+    _tx_byte_pool_initialize();
+
+    /* Call the mutex initialization function.  */
+    _tx_mutex_initialize();
+#endif
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_initialize_kernel_enter.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_initialize_kernel_enter.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_initialize_kernel_enter.c	(revision 54)
@@ -0,0 +1,168 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Initialize                                                          */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_initialize.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+
+#if defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)
+extern VOID _tx_execution_initialize(VOID);
+#endif
+
+/* Define any port-specific scheduling data structures.  */
+
+TX_PORT_SPECIFIC_DATA
+
+
+#ifdef TX_SAFETY_CRITICAL
+TX_SAFETY_CRITICAL_EXCEPTION_HANDLER
+#endif
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_initialize_kernel_enter                         PORTABLE C      */
+/*                                                           6.3.0        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function is the first ThreadX function called during           */
+/*    initialization.  It is called from the application's "main()"       */
+/*    function.  It is important to note that this routine never          */
+/*    returns.  The processing of this function is relatively simple:     */
+/*    it calls several ThreadX initialization functions (if needed),      */
+/*    calls the application define function, and then invokes the         */
+/*    scheduler.                                                          */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_initialize_low_level          Low-level initialization          */
+/*    _tx_initialize_high_level         High-level initialization         */
+/*    tx_application_define             Application define function       */
+/*    _tx_thread_scheduler              ThreadX scheduling loop           */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    main                              Application main program          */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020      William E. Lamie        Initial Version 6.0           */
+/*  09-30-2020      Yuxin Zhou              Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*  04-25-2022      Scott Larson            Modified comment(s),          */
+/*                                            added EPK initialization,   */
+/*                                            resulting in version 6.1.11 */
+/*  10-31-2023      Xiuwen Cai              Modified comment(s),          */
+/*                                            added random generator      */
+/*                                            initialization,             */
+/*                                            resulting in version 6.3.0  */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_initialize_kernel_enter(VOID)
+{
+
+    /* Determine if the compiler has pre-initialized ThreadX.  */
+    if (_tx_thread_system_state != TX_INITIALIZE_ALMOST_DONE)
+    {
+
+        /* No, the initialization still needs to take place.  */
+
+        /* Ensure that the system state variable is set to indicate
+           initialization is in progress.  Note that this variable is
+           later used to represent interrupt nesting.  */
+        _tx_thread_system_state =  TX_INITIALIZE_IN_PROGRESS;
+
+        /* Call any port specific preprocessing.  */
+        TX_PORT_SPECIFIC_PRE_INITIALIZATION
+
+        /* Invoke the low-level initialization to handle all processor specific
+           initialization issues.  */
+        _tx_initialize_low_level();
+
+        /* Invoke the high-level initialization to exercise all of the
+           ThreadX components and the application's initialization
+           function.  */
+        _tx_initialize_high_level();
+
+        /* Call any port specific post-processing.  */
+        TX_PORT_SPECIFIC_POST_INITIALIZATION
+    }
+
+    /* Optional processing extension.  */
+    TX_INITIALIZE_KERNEL_ENTER_EXTENSION
+
+    /* Ensure that the system state variable is set to indicate
+       initialization is in progress.  Note that this variable is
+       later used to represent interrupt nesting.  */
+    _tx_thread_system_state =  TX_INITIALIZE_IN_PROGRESS;
+
+    /* Optional random number generator initialization.  */
+    TX_INITIALIZE_RANDOM_GENERATOR_INITIALIZATION
+
+    /* Call the application provided initialization function.  Pass the
+       first available memory address to it.  */
+    tx_application_define(_tx_initialize_unused_memory);
+
+    /* Set the system state in preparation for entering the thread
+       scheduler.  */
+    _tx_thread_system_state =  TX_INITIALIZE_IS_FINISHED;
+
+    /* Call any port specific pre-scheduler processing.  */
+    TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION
+
+#if defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)
+    /* Initialize Execution Profile Kit.  */
+    _tx_execution_initialize();
+#endif
+
+    /* Enter the scheduling loop to start executing threads!  */
+    _tx_thread_schedule();
+
+#ifdef TX_SAFETY_CRITICAL
+
+    /* If we ever get here, raise safety critical exception.  */
+    TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, 0);
+#endif
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_cleanup.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_cleanup.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_cleanup.c	(revision 54)
@@ -0,0 +1,317 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Mutex                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_mutex.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_mutex_cleanup                                   PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function processes mutex timeout and thread terminate          */
+/*    actions that require the mutex data structures to be cleaned        */
+/*    up.                                                                 */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    thread_ptr                        Pointer to suspended thread's     */
+/*                                        control block                   */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_resume          Resume thread service             */
+/*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_thread_timeout                Thread timeout processing         */
+/*    _tx_thread_terminate              Thread terminate processing       */
+/*    _tx_thread_wait_abort             Thread wait abort processing      */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_mutex_cleanup(TX_THREAD  *thread_ptr, ULONG suspension_sequence)
+{
+
+#ifndef TX_NOT_INTERRUPTABLE
+TX_INTERRUPT_SAVE_AREA
+#endif
+
+TX_MUTEX            *mutex_ptr;
+UINT                suspended_count;
+TX_THREAD           *next_thread;
+TX_THREAD           *previous_thread;
+
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+    /* Disable interrupts to remove the suspended thread from the mutex.  */
+    TX_DISABLE
+
+    /* Determine if the cleanup is still required.  */
+    if (thread_ptr -> tx_thread_suspend_cleanup == &(_tx_mutex_cleanup))
+    {
+
+        /* Check for valid suspension sequence.  */
+        if (suspension_sequence == thread_ptr -> tx_thread_suspension_sequence)
+        {
+
+            /* Setup pointer to mutex control block.  */
+            mutex_ptr =  TX_VOID_TO_MUTEX_POINTER_CONVERT(thread_ptr -> tx_thread_suspend_control_block);
+
+            /* Check for NULL mutex pointer.  */
+            if (mutex_ptr != TX_NULL)
+            {
+
+                /* Determine if the mutex ID is valid.  */
+                if (mutex_ptr -> tx_mutex_id == TX_MUTEX_ID)
+                {
+
+                    /* Determine if there are any thread suspensions.  */
+                    if (mutex_ptr -> tx_mutex_suspended_count != TX_NO_SUSPENSIONS)
+                    {
+#else
+
+                        /* Setup pointer to mutex control block.  */
+                        mutex_ptr =  TX_VOID_TO_MUTEX_POINTER_CONVERT(thread_ptr -> tx_thread_suspend_control_block);
+#endif
+
+                        /* Yes, we still have thread suspension!  */
+
+                        /* Clear the suspension cleanup flag.  */
+                        thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
+
+                        /* Decrement the suspension count.  */
+                        mutex_ptr -> tx_mutex_suspended_count--;
+
+                        /* Pickup the suspended count.  */
+                        suspended_count =  mutex_ptr -> tx_mutex_suspended_count;
+
+                        /* Remove the suspended thread from the list.  */
+
+                        /* See if this is the only suspended thread on the list.  */
+                        if (suspended_count == TX_NO_SUSPENSIONS)
+                        {
+
+                            /* Yes, the only suspended thread.  */
+
+                            /* Update the head pointer.  */
+                            mutex_ptr -> tx_mutex_suspension_list =  TX_NULL;
+                        }
+                        else
+                        {
+
+                            /* At least one more thread is on the same suspension list.  */
+
+                            /* Update the links of the adjacent threads.  */
+                            next_thread =                                   thread_ptr -> tx_thread_suspended_next;
+                            previous_thread =                               thread_ptr -> tx_thread_suspended_previous;
+                            next_thread -> tx_thread_suspended_previous =   previous_thread;
+                            previous_thread -> tx_thread_suspended_next =   next_thread;
+
+                            /* Determine if we need to update the head pointer.  */
+                            if (mutex_ptr -> tx_mutex_suspension_list == thread_ptr)
+                            {
+
+                                /* Update the list head pointer.  */
+                                mutex_ptr -> tx_mutex_suspension_list =         next_thread;
+                            }
+                        }
+
+                        /* Now we need to determine if this cleanup is from a terminate, timeout,
+                           or from a wait abort.  */
+                        if (thread_ptr -> tx_thread_state == TX_MUTEX_SUSP)
+                        {
+
+                            /* Timeout condition and the thread still suspended on the mutex.
+                               Setup return error status and resume the thread.  */
+
+#ifdef TX_MUTEX_ENABLE_PERFORMANCE_INFO
+
+                            /* Increment the total timeouts counter.  */
+                            _tx_mutex_performance_timeout_count++;
+
+                            /* Increment the number of timeouts on this semaphore.  */
+                            mutex_ptr -> tx_mutex_performance_timeout_count++;
+#endif
+
+                            /* Setup return status.  */
+                            thread_ptr -> tx_thread_suspend_status =  TX_NOT_AVAILABLE;
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+                            /* Resume the thread!  */
+                            _tx_thread_system_ni_resume(thread_ptr);
+#else
+
+                            /* Temporarily disable preemption.  */
+                            _tx_thread_preempt_disable++;
+
+                            /* Restore interrupts.  */
+                            TX_RESTORE
+
+                            /* Resume the thread!  */
+                            _tx_thread_system_resume(thread_ptr);
+
+                            /* Disable interrupts.  */
+                            TX_DISABLE
+#endif
+                        }
+#ifndef TX_NOT_INTERRUPTABLE
+                    }
+                }
+            }
+        }
+    }
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+#endif
+}
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_mutex_thread_release                            PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function releases all mutexes owned by the thread. This        */
+/*    function is called when the thread completes or is terminated.      */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    thread_ptr                        Pointer to thread's control block */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_mutex_put                     Release the mutex                 */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_thread_shell_entry            Thread completion processing      */
+/*    _tx_thread_terminate              Thread terminate processing       */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_mutex_thread_release(TX_THREAD  *thread_ptr)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+TX_MUTEX    *mutex_ptr;
+#ifdef TX_MISRA_ENABLE
+UINT        status;
+#endif
+
+
+    /* Disable interrupts.  */
+    TX_DISABLE
+
+    /* Temporarily disable preemption.  */
+    _tx_thread_preempt_disable++;
+
+    /* Loop to look at all the mutexes.  */
+    do
+    {
+
+        /* Pickup the mutex head pointer.  */
+        mutex_ptr =  thread_ptr -> tx_thread_owned_mutex_list;
+
+        /* Determine if there is a mutex.  */
+        if (mutex_ptr != TX_NULL)
+        {
+
+            /* Yes, set the ownership count to 1.  */
+            mutex_ptr -> tx_mutex_ownership_count =  ((UINT) 1);
+
+            /* Restore interrupts.   */
+            TX_RESTORE
+
+#ifdef TX_MISRA_ENABLE
+            /* Release the mutex.  */
+            do
+            {
+                status =  _tx_mutex_put(mutex_ptr);
+            } while (status != TX_SUCCESS);
+#else
+            _tx_mutex_put(mutex_ptr);
+#endif
+
+            /* Disable interrupts.  */
+            TX_DISABLE
+
+            /* Move to the next mutex.  */
+            mutex_ptr =  thread_ptr -> tx_thread_owned_mutex_list;
+        }
+    } while (mutex_ptr != TX_NULL);
+
+    /* Restore preemption.  */
+    _tx_thread_preempt_disable--;
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_create.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_create.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_create.c	(revision 54)
@@ -0,0 +1,148 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Mutex                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_trace.h"
+#include "tx_mutex.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_mutex_create                                    PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function creates a mutex with optional priority inheritance as */
+/*    specified in this call.                                             */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    mutex_ptr                             Pointer to mutex control block*/
+/*    name_ptr                              Pointer to mutex name         */
+/*    inherit                               Priority inheritance option   */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_SUCCESS                        Successful completion status      */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _tx_mutex_create(TX_MUTEX *mutex_ptr, CHAR *name_ptr, UINT inherit)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+TX_MUTEX        *next_mutex;
+TX_MUTEX        *previous_mutex;
+
+
+    /* Initialize mutex control block to all zeros.  */
+    TX_MEMSET(mutex_ptr, 0, (sizeof(TX_MUTEX)));
+
+    /* Setup the basic mutex fields.  */
+    mutex_ptr -> tx_mutex_name =             name_ptr;
+    mutex_ptr -> tx_mutex_inherit =          inherit;
+
+    /* Disable interrupts to place the mutex on the created list.  */
+    TX_DISABLE
+
+    /* Setup the mutex ID to make it valid.  */
+    mutex_ptr -> tx_mutex_id =  TX_MUTEX_ID;
+
+    /* Setup the thread mutex release function pointer.  */
+    _tx_thread_mutex_release =  &(_tx_mutex_thread_release);
+
+    /* Place the mutex on the list of created mutexes.  First,
+       check for an empty list.  */
+    if (_tx_mutex_created_count == TX_EMPTY)
+    {
+
+        /* The created mutex list is empty.  Add mutex to empty list.  */
+        _tx_mutex_created_ptr =                   mutex_ptr;
+        mutex_ptr -> tx_mutex_created_next =      mutex_ptr;
+        mutex_ptr -> tx_mutex_created_previous =  mutex_ptr;
+    }
+    else
+    {
+
+        /* This list is not NULL, add to the end of the list.  */
+        next_mutex =      _tx_mutex_created_ptr;
+        previous_mutex =  next_mutex -> tx_mutex_created_previous;
+
+        /* Place the new mutex in the list.  */
+        next_mutex -> tx_mutex_created_previous =  mutex_ptr;
+        previous_mutex -> tx_mutex_created_next =  mutex_ptr;
+
+        /* Setup this mutex's next and previous created links.  */
+        mutex_ptr -> tx_mutex_created_previous =  previous_mutex;
+        mutex_ptr -> tx_mutex_created_next =      next_mutex;
+    }
+
+    /* Increment the ownership count.  */
+    _tx_mutex_created_count++;
+
+    /* Optional mutex create extended processing.  */
+    TX_MUTEX_CREATE_EXTENSION(mutex_ptr)
+
+    /* If trace is enabled, register this object.  */
+    TX_TRACE_OBJECT_REGISTER(TX_TRACE_OBJECT_TYPE_MUTEX, mutex_ptr, name_ptr, inherit, 0)
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_MUTEX_CREATE, mutex_ptr, inherit, TX_POINTER_TO_ULONG_CONVERT(&next_mutex), 0, TX_TRACE_MUTEX_EVENTS)
+
+    /* Log this kernel call.  */
+    TX_EL_MUTEX_CREATE_INSERT
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Return TX_SUCCESS.  */
+    return(TX_SUCCESS);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_prioritize.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_prioritize.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_prioritize.c	(revision 54)
@@ -0,0 +1,267 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Mutex                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_trace.h"
+#include "tx_thread.h"
+#include "tx_mutex.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_mutex_prioritize                                PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function places the highest priority suspended thread at the   */
+/*    front of the suspension list.  All other threads remain in the same */
+/*    FIFO suspension order.                                              */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    mutex_ptr                         Pointer to mutex control block    */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    status                            Completion status                 */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_preempt_check   Check for preemption              */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _tx_mutex_prioritize(TX_MUTEX *mutex_ptr)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+TX_THREAD       *thread_ptr;
+TX_THREAD       *priority_thread_ptr;
+TX_THREAD       *head_ptr;
+UINT            suspended_count;
+TX_THREAD       *next_thread;
+TX_THREAD       *previous_thread;
+UINT            list_changed;
+#ifdef TX_MISRA_ENABLE
+UINT            status;
+#endif
+
+
+    /* Disable interrupts.  */
+    TX_DISABLE
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_MUTEX_PRIORITIZE, mutex_ptr, mutex_ptr -> tx_mutex_suspended_count, TX_POINTER_TO_ULONG_CONVERT(&suspended_count), 0, TX_TRACE_MUTEX_EVENTS)
+
+    /* Log this kernel call.  */
+    TX_EL_MUTEX_PRIORITIZE_INSERT
+
+    /* Pickup the suspended count.  */
+    suspended_count =  mutex_ptr -> tx_mutex_suspended_count;
+
+    /* Determine if there are fewer than 2 suspended threads.  */
+    if (suspended_count < ((UINT) 2))
+    {
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+    }
+
+    /* Determine if there how many threads are suspended on this mutex.  */
+    else if (suspended_count == ((UINT) 2))
+    {
+
+        /* Pickup the head pointer and the next pointer.  */
+        head_ptr =  mutex_ptr -> tx_mutex_suspension_list;
+        next_thread =  head_ptr -> tx_thread_suspended_next;
+
+        /* Determine if the next suspended thread has a higher priority.  */
+        if ((next_thread -> tx_thread_priority) < (head_ptr -> tx_thread_priority))
+        {
+
+            /* Yes, move the list head to the next thread.  */
+            mutex_ptr -> tx_mutex_suspension_list =  next_thread;
+        }
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+    }
+    else
+    {
+
+        /* Remember the suspension count and head pointer.  */
+        head_ptr =   mutex_ptr -> tx_mutex_suspension_list;
+
+        /* Default the highest priority thread to the thread at the front of the list.  */
+        priority_thread_ptr =  head_ptr;
+
+        /* Setup search pointer.  */
+        thread_ptr =  priority_thread_ptr -> tx_thread_suspended_next;
+
+        /* Disable preemption.  */
+        _tx_thread_preempt_disable++;
+
+        /* Set the list changed flag to false.  */
+        list_changed =  TX_FALSE;
+
+        /* Search through the list to find the highest priority thread.  */
+        do
+        {
+
+            /* Is the current thread higher priority?  */
+            if (thread_ptr -> tx_thread_priority < priority_thread_ptr -> tx_thread_priority)
+            {
+
+                /* Yes, remember that this thread is the highest priority.  */
+                priority_thread_ptr =  thread_ptr;
+            }
+
+            /* Restore interrupts temporarily.  */
+            TX_RESTORE
+
+            /* Disable interrupts again.  */
+            TX_DISABLE
+
+            /* Determine if any changes to the list have occurred while
+               interrupts were enabled.  */
+
+            /* Is the list head the same?  */
+            if (head_ptr != mutex_ptr -> tx_mutex_suspension_list)
+            {
+
+                /* The list head has changed, set the list changed flag.  */
+                list_changed =  TX_TRUE;
+            }
+            else
+            {
+
+                /* Is the suspended count the same?  */
+                if (suspended_count != mutex_ptr -> tx_mutex_suspended_count)
+                {
+
+                    /* The list head has changed, set the list changed flag.  */
+                    list_changed =  TX_TRUE;
+                }
+            }
+
+            /* Determine if the list has changed.  */
+            if (list_changed == TX_FALSE)
+            {
+
+                /* Move the thread pointer to the next thread.  */
+                thread_ptr =  thread_ptr -> tx_thread_suspended_next;
+            }
+            else
+            {
+
+                /* Remember the suspension count and head pointer.  */
+                head_ptr =   mutex_ptr -> tx_mutex_suspension_list;
+                suspended_count =  mutex_ptr -> tx_mutex_suspended_count;
+
+                /* Default the highest priority thread to the thread at the front of the list.  */
+                priority_thread_ptr =  head_ptr;
+
+                /* Setup search pointer.  */
+                thread_ptr =  priority_thread_ptr -> tx_thread_suspended_next;
+
+                /* Reset the list changed flag.  */
+                list_changed =  TX_FALSE;
+            }
+
+        } while (thread_ptr != head_ptr);
+
+        /* Release preemption.  */
+        _tx_thread_preempt_disable--;
+
+        /* Now determine if the highest priority thread is at the front
+           of the list.  */
+        if (priority_thread_ptr != head_ptr)
+        {
+
+            /* No, we need to move the highest priority suspended thread to the
+               front of the list.  */
+
+            /* First, remove the highest priority thread by updating the
+               adjacent suspended threads.  */
+            next_thread =                                  priority_thread_ptr -> tx_thread_suspended_next;
+            previous_thread =                              priority_thread_ptr -> tx_thread_suspended_previous;
+            next_thread -> tx_thread_suspended_previous =  previous_thread;
+            previous_thread -> tx_thread_suspended_next =  next_thread;
+
+            /* Now, link the highest priority thread at the front of the list.  */
+            previous_thread =                                      head_ptr -> tx_thread_suspended_previous;
+            priority_thread_ptr -> tx_thread_suspended_next =      head_ptr;
+            priority_thread_ptr -> tx_thread_suspended_previous =  previous_thread;
+            previous_thread -> tx_thread_suspended_next =          priority_thread_ptr;
+            head_ptr -> tx_thread_suspended_previous =             priority_thread_ptr;
+
+            /* Move the list head pointer to the highest priority suspended thread.  */
+            mutex_ptr -> tx_mutex_suspension_list =  priority_thread_ptr;
+        }
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Check for preemption.  */
+        _tx_thread_system_preempt_check();
+    }
+
+#ifdef TX_MISRA_ENABLE
+
+    /* Initialize status to success.  */
+    status =  TX_SUCCESS;
+
+    /* Define extended processing option.  */
+    status =  TX_MUTEX_PRIORITIZE_MISRA_EXTENSION(status);
+
+    /* Return completion status.  */
+    return(status);
+#else
+
+    /* Return successful completion.  */
+    return(TX_SUCCESS);
+#endif
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_priority_change.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_priority_change.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_priority_change.c	(revision 54)
@@ -0,0 +1,339 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Mutex                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_mutex.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_mutex_priority_change                           PORTABLE C      */
+/*                                                           6.1.6        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function changes the priority of the specified thread for the  */
+/*    priority inheritance option of the mutex service.                   */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    thread_ptr                            Pointer to thread to suspend  */
+/*    new_priority                          New thread priority           */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_resume          Resume thread                     */
+/*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
+/*    _tx_thread_system_suspend         Suspend thread                    */
+/*    _tx_thread_system_ni_suspend      Non-interruptable suspend thread  */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_mutex_get                     Inherit priority                  */
+/*    _tx_mutex_put                     Restore previous priority         */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020      William E. Lamie        Initial Version 6.0           */
+/*  09-30-2020      William E. Lamie        Modified comment(s), and      */
+/*                                            change thread state from    */
+/*                                            TX_SUSPENDED to             */
+/*                                            TX_PRIORITY_CHANGE before   */
+/*                                            calling                     */
+/*                                            _tx_thread_system_suspend,  */
+/*                                            resulting in version 6.1    */
+/*  04-02-2021      Scott Larson            Modified comments, fixed      */
+/*                                            mapping current thread's    */
+/*                                            priority rather than next,  */
+/*                                            resulting in version 6.1.6  */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_mutex_priority_change(TX_THREAD *thread_ptr, UINT new_priority)
+{
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+TX_INTERRUPT_SAVE_AREA
+#endif
+
+TX_THREAD       *execute_ptr;
+TX_THREAD       *next_execute_ptr;
+UINT            original_priority;
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+ULONG           priority_bit;
+#if TX_MAX_PRIORITIES > 32
+UINT            map_index;
+#endif
+#endif
+
+
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+    /* Lockout interrupts while the thread is being suspended.  */
+    TX_DISABLE
+#endif
+
+    /* Determine if this thread is currently ready.  */
+    if (thread_ptr -> tx_thread_state != TX_READY)
+    {
+
+        /* Change thread priority to the new mutex priority-inheritance priority.  */
+        thread_ptr -> tx_thread_priority =  new_priority;
+
+        /* Determine how to setup the thread's preemption-threshold.  */
+        if (thread_ptr -> tx_thread_user_preempt_threshold < new_priority)
+        {
+
+            /* Change thread preemption-threshold to the user's preemption-threshold.  */
+            thread_ptr -> tx_thread_preempt_threshold =  thread_ptr -> tx_thread_user_preempt_threshold;
+        }
+        else
+        {
+
+            /* Change the thread preemption-threshold to the new threshold.  */
+            thread_ptr -> tx_thread_preempt_threshold =  new_priority;
+        }
+
+#ifndef TX_NOT_INTERRUPTABLE
+        /* Restore interrupts.  */
+        TX_RESTORE
+#endif
+    }
+    else
+    {
+
+        /* Pickup the next thread to execute.  */
+        execute_ptr =  _tx_thread_execute_ptr;
+
+        /* Save the original priority.  */
+        original_priority =  thread_ptr -> tx_thread_priority;
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+        /* Increment the preempt disable flag.  */
+        _tx_thread_preempt_disable++;
+
+        /* Set the state to priority change.  */
+        thread_ptr -> tx_thread_state =    TX_PRIORITY_CHANGE;
+
+        /* Call actual non-interruptable thread suspension routine.  */
+        _tx_thread_system_ni_suspend(thread_ptr, ((ULONG) 0));
+
+        /* At this point, the preempt disable flag is still set, so we still have
+           protection against all preemption.  */
+
+        /* Change thread priority to the new mutex priority-inheritance priority.  */
+        thread_ptr -> tx_thread_priority =  new_priority;
+
+        /* Determine how to setup the thread's preemption-threshold.  */
+        if (thread_ptr -> tx_thread_user_preempt_threshold < new_priority)
+        {
+
+            /* Change thread preemption-threshold to the user's preemption-threshold.  */
+            thread_ptr -> tx_thread_preempt_threshold =  thread_ptr -> tx_thread_user_preempt_threshold;
+        }
+        else
+        {
+
+            /* Change the thread preemption-threshold to the new threshold.  */
+            thread_ptr -> tx_thread_preempt_threshold =  new_priority;
+        }
+
+        /* Resume the thread with the new priority.  */
+        _tx_thread_system_ni_resume(thread_ptr);
+
+        /* Decrement the preempt disable flag.  */
+        _tx_thread_preempt_disable--;
+#else
+
+        /* Increment the preempt disable flag.  */
+        _tx_thread_preempt_disable =  _tx_thread_preempt_disable + ((UINT) 2);
+
+        /* Set the state to priority change.  */
+        thread_ptr -> tx_thread_state =    TX_PRIORITY_CHANGE;
+
+        /* Set the suspending flag. */
+        thread_ptr -> tx_thread_suspending =  TX_TRUE;
+
+        /* Setup the timeout period.  */
+        thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  ((ULONG) 0);
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* The thread is ready and must first be removed from the list.  Call the
+           system suspend function to accomplish this.  */
+        _tx_thread_system_suspend(thread_ptr);
+
+        /* Disable interrupts.  */
+        TX_DISABLE
+
+        /* At this point, the preempt disable flag is still set, so we still have
+           protection against all preemption.  */
+
+        /* Change thread priority to the new mutex priority-inheritance priority.  */
+        thread_ptr -> tx_thread_priority =  new_priority;
+
+        /* Determine how to setup the thread's preemption-threshold.  */
+        if (thread_ptr -> tx_thread_user_preempt_threshold < new_priority)
+        {
+
+            /* Change thread preemption-threshold to the user's preemption-threshold.  */
+            thread_ptr -> tx_thread_preempt_threshold =  thread_ptr -> tx_thread_user_preempt_threshold;
+        }
+        else
+        {
+
+            /* Change the thread preemption-threshold to the new threshold.  */
+            thread_ptr -> tx_thread_preempt_threshold =  new_priority;
+        }
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Resume the thread with the new priority.  */
+        _tx_thread_system_resume(thread_ptr);
+#endif
+
+        /* Optional processing extension.  */
+        TX_MUTEX_PRIORITY_CHANGE_EXTENSION
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+        /* Disable interrupts.  */
+        TX_DISABLE
+#endif
+
+        /* Pickup the next thread to execute.  */
+        next_execute_ptr =  _tx_thread_execute_ptr;
+
+        /* Determine if this thread is not the next thread to execute.  */
+        if (thread_ptr != next_execute_ptr)
+        {
+
+            /* Make sure the thread is still ready.  */
+            if (thread_ptr -> tx_thread_state == TX_READY)
+            {
+
+                /* Now check and see if this thread has an equal or higher priority.  */
+                if (thread_ptr -> tx_thread_priority <= next_execute_ptr -> tx_thread_priority)
+                {
+
+                    /* Now determine if this thread was the previously executing thread.  */
+                    if (thread_ptr == execute_ptr)
+                    {
+
+                        /* Yes, this thread was previously executing before we temporarily suspended and resumed
+                           it in order to change the priority. A lower or same priority thread cannot be the next thread
+                           to execute in this case since this thread really didn't suspend.  Simply reset the execute
+                           pointer to this thread.  */
+                        _tx_thread_execute_ptr =  thread_ptr;
+
+                        /* Determine if we moved to a lower priority. If so, move the thread to the front of its priority list.  */
+                        if (original_priority < new_priority)
+                        {
+
+                            /* Ensure that this thread is placed at the front of the priority list.  */
+                            _tx_thread_priority_list[thread_ptr -> tx_thread_priority] =  thread_ptr;
+                        }
+                    }
+                }
+                else
+                {
+
+                    /* Now determine if this thread's preemption-threshold needs to be enforced.  */
+                    if (thread_ptr -> tx_thread_preempt_threshold < thread_ptr -> tx_thread_priority)
+                    {
+
+                        /* Yes, preemption-threshold is in force for this thread. */
+
+                        /* Compare the next thread to execute thread's priority against the thread's preemption-threshold.  */
+                        if (thread_ptr -> tx_thread_preempt_threshold <= next_execute_ptr -> tx_thread_priority)
+                        {
+
+                            /* We must swap execute pointers to enforce the preemption-threshold of a thread coming out of
+                               priority inheritance.  */
+                            _tx_thread_execute_ptr =  thread_ptr;
+
+                            /* Determine if we moved to a lower priority. If so, move the thread to the front of its priority list.  */
+                            if (original_priority < new_priority)
+                            {
+
+                                /* Ensure that this thread is placed at the front of the priority list.  */
+                                _tx_thread_priority_list[thread_ptr -> tx_thread_priority] =  thread_ptr;
+                            }
+                        }
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+
+                        else
+                        {
+
+                            /* In this case, we need to mark the preempted map to indicate a thread executed above the
+                               preemption-threshold.  */
+
+#if TX_MAX_PRIORITIES > 32
+
+                            /* Calculate the index into the bit map array.  */
+                            map_index =  (thread_ptr -> tx_thread_priority)/ ((UINT) 32);
+
+                            /* Set the active bit to remember that the preempt map has something set.  */
+                            TX_DIV32_BIT_SET(thread_ptr -> tx_thread_priority, priority_bit)
+                            _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active | priority_bit;
+#endif
+
+                            /* Remember that this thread was preempted by a thread above the thread's threshold.  */
+                            TX_MOD32_BIT_SET(thread_ptr -> tx_thread_priority, priority_bit)
+                            _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] | priority_bit;
+                        }
+#endif
+                    }
+                }
+            }
+        }
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+#endif
+    }
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_put.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_put.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_mutex_put.c	(revision 54)
@@ -0,0 +1,656 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Mutex                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_trace.h"
+#include "tx_thread.h"
+#include "tx_mutex.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_mutex_put                                       PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function puts back an instance of the specified mutex.         */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    mutex_ptr                         Pointer to mutex control block    */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_SUCCESS                        Success completion status         */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_preempt_check   Check for preemption              */
+/*    _tx_thread_system_resume          Resume thread service             */
+/*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
+/*    _tx_mutex_priority_change         Restore previous thread priority  */
+/*    _tx_mutex_prioritize              Prioritize the mutex suspension   */
+/*    _tx_mutex_thread_release          Release all thread's mutexes      */
+/*    _tx_mutex_delete                  Release ownership upon mutex      */
+/*                                        deletion                        */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _tx_mutex_put(TX_MUTEX *mutex_ptr)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+TX_THREAD       *thread_ptr;
+TX_THREAD       *old_owner;
+UINT            old_priority;
+UINT            status;
+TX_MUTEX        *next_mutex;
+TX_MUTEX        *previous_mutex;
+UINT            owned_count;
+UINT            suspended_count;
+TX_THREAD       *current_thread;
+TX_THREAD       *next_thread;
+TX_THREAD       *previous_thread;
+TX_THREAD       *suspended_thread;
+UINT            inheritance_priority;
+
+
+    /* Setup status to indicate the processing is not complete.  */
+    status =  TX_NOT_DONE;
+
+    /* Disable interrupts to put an instance back to the mutex.  */
+    TX_DISABLE
+
+#ifdef TX_MUTEX_ENABLE_PERFORMANCE_INFO
+
+    /* Increment the total mutex put counter.  */
+    _tx_mutex_performance_put_count++;
+
+    /* Increment the number of attempts to put this mutex.  */
+    mutex_ptr -> tx_mutex_performance_put_count++;
+#endif
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_MUTEX_PUT, mutex_ptr, TX_POINTER_TO_ULONG_CONVERT(mutex_ptr -> tx_mutex_owner), mutex_ptr -> tx_mutex_ownership_count, TX_POINTER_TO_ULONG_CONVERT(&old_priority), TX_TRACE_MUTEX_EVENTS)
+
+    /* Log this kernel call.  */
+    TX_EL_MUTEX_PUT_INSERT
+
+    /* Determine if this mutex is owned.  */
+    if (mutex_ptr -> tx_mutex_ownership_count != ((UINT) 0))
+    {
+
+        /* Pickup the owning thread pointer.  */
+        thread_ptr =  mutex_ptr -> tx_mutex_owner;
+
+        /* Pickup thread pointer.  */
+        TX_THREAD_GET_CURRENT(current_thread)
+
+        /* Check to see if the mutex is owned by the calling thread.  */
+        if (mutex_ptr -> tx_mutex_owner != current_thread)
+        {
+
+            /* Determine if the preempt disable flag is set, indicating that
+               the caller is not the application but from ThreadX. In such
+               cases, the thread mutex owner does not need to match.  */
+            if (_tx_thread_preempt_disable == ((UINT) 0))
+            {
+
+                /* Invalid mutex release.  */
+
+                /* Restore interrupts.  */
+                TX_RESTORE
+
+                /* Caller does not own the mutex.  */
+                status =  TX_NOT_OWNED;
+            }
+        }
+
+        /* Determine if we should continue.  */
+        if (status == TX_NOT_DONE)
+        {
+
+            /* Decrement the mutex ownership count.  */
+            mutex_ptr -> tx_mutex_ownership_count--;
+
+            /* Determine if the mutex is still owned by the current thread.  */
+            if (mutex_ptr -> tx_mutex_ownership_count != ((UINT) 0))
+            {
+
+                /* Restore interrupts.  */
+                TX_RESTORE
+
+                /* Mutex is still owned, just return successful status.  */
+                status =  TX_SUCCESS;
+            }
+            else
+            {
+
+                /* Check for a NULL thread pointer, which can only happen during initialization.   */
+                if (thread_ptr == TX_NULL)
+                {
+
+                    /* Restore interrupts.  */
+                    TX_RESTORE
+
+                    /* Mutex is now available, return successful status.  */
+                    status =  TX_SUCCESS;
+                }
+                else
+                {
+
+                    /* The mutex is now available.   */
+
+                    /* Remove this mutex from the owned mutex list.  */
+
+                    /* Decrement the ownership count.  */
+                    thread_ptr -> tx_thread_owned_mutex_count--;
+
+                    /* Determine if this mutex was the only one on the list.  */
+                    if (thread_ptr -> tx_thread_owned_mutex_count == ((UINT) 0))
+                    {
+
+                        /* Yes, the list is empty.  Simply set the head pointer to NULL.  */
+                        thread_ptr -> tx_thread_owned_mutex_list =  TX_NULL;
+                    }
+                    else
+                    {
+
+                        /* No, there are more mutexes on the list.  */
+
+                        /* Link-up the neighbors.  */
+                        next_mutex =                             mutex_ptr -> tx_mutex_owned_next;
+                        previous_mutex =                         mutex_ptr -> tx_mutex_owned_previous;
+                        next_mutex -> tx_mutex_owned_previous =  previous_mutex;
+                        previous_mutex -> tx_mutex_owned_next =  next_mutex;
+
+                        /* See if we have to update the created list head pointer.  */
+                        if (thread_ptr -> tx_thread_owned_mutex_list == mutex_ptr)
+                        {
+
+                            /* Yes, move the head pointer to the next link. */
+                            thread_ptr -> tx_thread_owned_mutex_list =  next_mutex;
+                        }
+                    }
+
+                    /* Determine if the simple, non-suspension, non-priority inheritance case is present.  */
+                    if (mutex_ptr -> tx_mutex_suspension_list == TX_NULL)
+                    {
+
+                        /* Is this a priority inheritance mutex?  */
+                        if (mutex_ptr -> tx_mutex_inherit == TX_FALSE)
+                        {
+
+                            /* Yes, we are done - set the mutex owner to NULL.   */
+                            mutex_ptr -> tx_mutex_owner =  TX_NULL;
+
+                            /* Restore interrupts.  */
+                            TX_RESTORE
+
+                            /* Mutex is now available, return successful status.  */
+                            status =  TX_SUCCESS;
+                        }
+                    }
+
+                    /* Determine if the processing is complete.  */
+                    if (status == TX_NOT_DONE)
+                    {
+
+                        /* Initialize original owner and thread priority.  */
+                        old_owner =      TX_NULL;
+                        old_priority =   thread_ptr -> tx_thread_user_priority;
+
+                        /* Does this mutex support priority inheritance?  */
+                        if (mutex_ptr -> tx_mutex_inherit == TX_TRUE)
+                        {
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+                            /* Temporarily disable preemption.  */
+                            _tx_thread_preempt_disable++;
+
+                            /* Restore interrupts.  */
+                            TX_RESTORE
+#endif
+
+                            /* Default the inheritance priority to disabled.  */
+                            inheritance_priority =  ((UINT) TX_MAX_PRIORITIES);
+
+                            /* Search the owned mutexes for this thread to determine the highest priority for this
+                               former mutex owner to return to.  */
+                            next_mutex =  thread_ptr -> tx_thread_owned_mutex_list;
+                            while (next_mutex != TX_NULL)
+                            {
+
+                                /* Does this mutex support priority inheritance?  */
+                                if (next_mutex -> tx_mutex_inherit == TX_TRUE)
+                                {
+
+                                    /* Determine if highest priority field of the mutex is higher than the priority to
+                                       restore.  */
+                                    if (next_mutex -> tx_mutex_highest_priority_waiting < inheritance_priority)
+                                    {
+
+                                        /* Use this priority to return releasing thread to.  */
+                                        inheritance_priority =   next_mutex -> tx_mutex_highest_priority_waiting;
+                                    }
+                                }
+
+                                /* Move mutex pointer to the next mutex in the list.  */
+                                next_mutex =  next_mutex -> tx_mutex_owned_next;
+
+                                /* Are we at the end of the list?  */
+                                if (next_mutex == thread_ptr -> tx_thread_owned_mutex_list)
+                                {
+
+                                    /* Yes, set the next mutex to NULL.  */
+                                    next_mutex =  TX_NULL;
+                                }
+                            }
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+                            /* Disable interrupts.  */
+                            TX_DISABLE
+
+                            /* Undo the temporarily preemption disable.  */
+                            _tx_thread_preempt_disable--;
+#endif
+
+                            /* Set the inherit priority to that of the highest priority thread waiting on the mutex.  */
+                            thread_ptr -> tx_thread_inherit_priority =  inheritance_priority;
+
+                            /* Determine if the inheritance priority is less than the default old priority.  */
+                            if (inheritance_priority < old_priority)
+                            {
+
+                                /* Yes, update the old priority.  */
+                                old_priority =  inheritance_priority;
+                            }
+                        }
+
+                        /* Determine if priority inheritance is in effect and there are one or more
+                           threads suspended on the mutex.  */
+                        if (mutex_ptr -> tx_mutex_suspended_count > ((UINT) 1))
+                        {
+
+                            /* Is priority inheritance in effect?  */
+                            if (mutex_ptr -> tx_mutex_inherit == TX_TRUE)
+                            {
+
+                                /* Yes, this code is simply to ensure the highest priority thread is positioned
+                                   at the front of the suspension list.  */
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+                                /* Temporarily disable preemption.  */
+                                _tx_thread_preempt_disable++;
+
+                                /* Restore interrupts.  */
+                                TX_RESTORE
+#endif
+
+                                /* Call the mutex prioritize processing to ensure the
+                                   highest priority thread is resumed.  */
+#ifdef TX_MISRA_ENABLE
+                                do
+                                {
+                                    status =  _tx_mutex_prioritize(mutex_ptr);
+                                } while (status != TX_SUCCESS);
+#else
+                                _tx_mutex_prioritize(mutex_ptr);
+#endif
+
+                                /* At this point, the highest priority thread is at the
+                                   front of the suspension list.  */
+
+                                /* Optional processing extension.  */
+                                TX_MUTEX_PUT_EXTENSION_1
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+                                /* Disable interrupts.  */
+                                TX_DISABLE
+
+                                /* Back off the preemption disable.  */
+                                _tx_thread_preempt_disable--;
+#endif
+                            }
+                        }
+
+                        /* Now determine if there are any threads still waiting on the mutex.  */
+                        if (mutex_ptr -> tx_mutex_suspension_list == TX_NULL)
+                        {
+
+                            /* No, there are no longer any threads waiting on the mutex.  */
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+                            /* Temporarily disable preemption.  */
+                            _tx_thread_preempt_disable++;
+
+                            /* Restore interrupts.  */
+                            TX_RESTORE
+#endif
+
+                            /* Mutex is not owned, but it is possible that a thread that
+                               caused a priority inheritance to occur is no longer waiting
+                               on the mutex.  */
+
+                            /* Setup the highest priority waiting thread.  */
+                            mutex_ptr -> tx_mutex_highest_priority_waiting =  (UINT) TX_MAX_PRIORITIES;
+
+                            /* Determine if we need to restore priority.  */
+                            if ((mutex_ptr -> tx_mutex_owner) -> tx_thread_priority != old_priority)
+                            {
+
+                                /* Yes, restore the priority of thread.  */
+                                _tx_mutex_priority_change(mutex_ptr -> tx_mutex_owner, old_priority);
+                            }
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+                            /* Disable interrupts again.  */
+                            TX_DISABLE
+
+                            /* Back off the preemption disable.  */
+                            _tx_thread_preempt_disable--;
+#endif
+
+                            /* Set the mutex owner to NULL.  */
+                            mutex_ptr -> tx_mutex_owner =  TX_NULL;
+
+                            /* Restore interrupts.  */
+                            TX_RESTORE
+
+                            /* Check for preemption.  */
+                            _tx_thread_system_preempt_check();
+
+                            /* Set status to success.  */
+                            status =  TX_SUCCESS;
+                        }
+                        else
+                        {
+
+                            /* Pickup the thread at the front of the suspension list.  */
+                            thread_ptr =  mutex_ptr -> tx_mutex_suspension_list;
+
+                            /* Save the previous ownership information, if inheritance is
+                               in effect.  */
+                            if (mutex_ptr -> tx_mutex_inherit == TX_TRUE)
+                            {
+
+                                /* Remember the old mutex owner.  */
+                                old_owner =  mutex_ptr -> tx_mutex_owner;
+
+                                /* Setup owner thread priority information.  */
+                                mutex_ptr -> tx_mutex_original_priority =   thread_ptr -> tx_thread_priority;
+
+                                /* Setup the highest priority waiting thread.  */
+                                mutex_ptr -> tx_mutex_highest_priority_waiting =  (UINT) TX_MAX_PRIORITIES;
+                            }
+
+                            /* Determine how many mutexes are owned by this thread.  */
+                            owned_count =  thread_ptr -> tx_thread_owned_mutex_count;
+
+                            /* Determine if this thread owns any other mutexes that have priority inheritance.  */
+                            if (owned_count == ((UINT) 0))
+                            {
+
+                                /* The owned mutex list is empty.  Add mutex to empty list.  */
+                                thread_ptr -> tx_thread_owned_mutex_list =     mutex_ptr;
+                                mutex_ptr -> tx_mutex_owned_next =             mutex_ptr;
+                                mutex_ptr -> tx_mutex_owned_previous =         mutex_ptr;
+                            }
+                            else
+                            {
+
+                                /* Non-empty list. Link up the mutex.  */
+
+                                /* Pickup tail pointer.  */
+                                next_mutex =                            thread_ptr -> tx_thread_owned_mutex_list;
+                                previous_mutex =                        next_mutex -> tx_mutex_owned_previous;
+
+                                /* Place the owned mutex in the list.  */
+                                next_mutex -> tx_mutex_owned_previous =  mutex_ptr;
+                                previous_mutex -> tx_mutex_owned_next =  mutex_ptr;
+
+                                /* Setup this mutex's next and previous created links.  */
+                                mutex_ptr -> tx_mutex_owned_previous =   previous_mutex;
+                                mutex_ptr -> tx_mutex_owned_next =       next_mutex;
+                            }
+
+                            /* Increment the number of mutexes owned counter.  */
+                            thread_ptr -> tx_thread_owned_mutex_count =  owned_count + ((UINT) 1);
+
+                            /* Mark the Mutex as owned and fill in the corresponding information.  */
+                            mutex_ptr -> tx_mutex_ownership_count =  (UINT) 1;
+                            mutex_ptr -> tx_mutex_owner =            thread_ptr;
+
+                            /* Remove the suspended thread from the list.  */
+
+                            /* Decrement the suspension count.  */
+                            mutex_ptr -> tx_mutex_suspended_count--;
+
+                            /* Pickup the suspended count.  */
+                            suspended_count =  mutex_ptr -> tx_mutex_suspended_count;
+
+                            /* See if this is the only suspended thread on the list.  */
+                            if (suspended_count == TX_NO_SUSPENSIONS)
+                            {
+
+                                /* Yes, the only suspended thread.  */
+
+                                /* Update the head pointer.  */
+                                mutex_ptr -> tx_mutex_suspension_list =  TX_NULL;
+                            }
+                            else
+                            {
+
+                                /* At least one more thread is on the same expiration list.  */
+
+                                /* Update the list head pointer.  */
+                                next_thread =                                  thread_ptr -> tx_thread_suspended_next;
+                                mutex_ptr -> tx_mutex_suspension_list =        next_thread;
+
+                                /* Update the links of the adjacent threads.  */
+                                previous_thread =                              thread_ptr -> tx_thread_suspended_previous;
+                                next_thread -> tx_thread_suspended_previous =  previous_thread;
+                                previous_thread -> tx_thread_suspended_next =  next_thread;
+                            }
+
+                            /* Prepare for resumption of the first thread.  */
+
+                            /* Clear cleanup routine to avoid timeout.  */
+                            thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
+
+                            /* Put return status into the thread control block.  */
+                            thread_ptr -> tx_thread_suspend_status =  TX_SUCCESS;
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+                            /* Determine if priority inheritance is enabled for this mutex.  */
+                            if (mutex_ptr -> tx_mutex_inherit == TX_TRUE)
+                            {
+
+                                /* Yes, priority inheritance is requested.  */
+
+                                /* Determine if there are any more threads still suspended on the mutex.  */
+                                if (mutex_ptr -> tx_mutex_suspended_count != ((ULONG) 0))
+                                {
+
+                                    /* Determine if there are more than one thread suspended on the mutex.  */
+                                    if (mutex_ptr -> tx_mutex_suspended_count > ((ULONG) 1))
+                                    {
+
+                                        /* If so, prioritize the list so the highest priority thread is placed at the
+                                           front of the suspension list.  */
+#ifdef TX_MISRA_ENABLE
+                                        do
+                                        {
+                                            status =  _tx_mutex_prioritize(mutex_ptr);
+                                        } while (status != TX_SUCCESS);
+#else
+                                        _tx_mutex_prioritize(mutex_ptr);
+#endif
+                                    }
+
+                                    /* Now, pickup the list head and set the priority.  */
+
+                                    /* Determine if there still are threads suspended for this mutex.  */
+                                    suspended_thread =  mutex_ptr -> tx_mutex_suspension_list;
+                                    if (suspended_thread != TX_NULL)
+                                    {
+
+                                        /* Setup the highest priority thread waiting on this mutex.  */
+                                        mutex_ptr -> tx_mutex_highest_priority_waiting =  suspended_thread -> tx_thread_priority;
+                                    }
+                                }
+
+                                /* Restore previous priority needs to be restored after priority
+                                   inheritance.  */
+
+                                /* Determine if we need to restore priority.  */
+                                if (old_owner -> tx_thread_priority != old_priority)
+                                {
+
+                                    /* Restore priority of thread.  */
+                                    _tx_mutex_priority_change(old_owner, old_priority);
+                                }
+                            }
+
+                            /* Resume the thread!  */
+                            _tx_thread_system_ni_resume(thread_ptr);
+
+                            /* Restore interrupts.  */
+                            TX_RESTORE
+#else
+
+                            /* Temporarily disable preemption.  */
+                            _tx_thread_preempt_disable++;
+
+                            /* Restore interrupts.  */
+                            TX_RESTORE
+
+                            /* Determine if priority inheritance is enabled for this mutex.  */
+                            if (mutex_ptr -> tx_mutex_inherit == TX_TRUE)
+                            {
+
+                                /* Yes, priority inheritance is requested.  */
+
+                                /* Determine if there are any more threads still suspended on the mutex.  */
+                                if (mutex_ptr -> tx_mutex_suspended_count != TX_NO_SUSPENSIONS)
+                                {
+
+                                    /* Prioritize the list so the highest priority thread is placed at the
+                                       front of the suspension list.  */
+#ifdef TX_MISRA_ENABLE
+                                    do
+                                    {
+                                        status =  _tx_mutex_prioritize(mutex_ptr);
+                                    } while (status != TX_SUCCESS);
+#else
+                                    _tx_mutex_prioritize(mutex_ptr);
+#endif
+
+                                    /* Now, pickup the list head and set the priority.  */
+
+                                    /* Optional processing extension.  */
+                                    TX_MUTEX_PUT_EXTENSION_2
+
+                                    /* Disable interrupts.  */
+                                    TX_DISABLE
+
+                                    /* Determine if there still are threads suspended for this mutex.  */
+                                    suspended_thread =  mutex_ptr -> tx_mutex_suspension_list;
+                                    if (suspended_thread != TX_NULL)
+                                    {
+
+                                        /* Setup the highest priority thread waiting on this mutex.  */
+                                        mutex_ptr -> tx_mutex_highest_priority_waiting =  suspended_thread -> tx_thread_priority;
+                                    }
+
+                                    /* Restore interrupts.  */
+                                    TX_RESTORE
+                                }
+
+                                /* Restore previous priority needs to be restored after priority
+                                   inheritance.  */
+
+                                /* Is the priority different?  */
+                                if (old_owner -> tx_thread_priority != old_priority)
+                                {
+
+                                    /* Restore the priority of thread.  */
+                                    _tx_mutex_priority_change(old_owner, old_priority);
+                                }
+                            }
+
+                            /* Resume thread.  */
+                            _tx_thread_system_resume(thread_ptr);
+#endif
+
+                            /* Return a successful status.  */
+                            status =  TX_SUCCESS;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    else
+    {
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Caller does not own the mutex.  */
+        status =  TX_NOT_OWNED;
+    }
+
+    /* Return the completion status.  */
+    return(status);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_cleanup.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_cleanup.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_cleanup.c	(revision 54)
@@ -0,0 +1,217 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Semaphore                                                           */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_semaphore.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_semaphore_cleanup                               PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function processes semaphore timeout and thread terminate      */
+/*    actions that require the semaphore data structures to be cleaned    */
+/*    up.                                                                 */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    thread_ptr                        Pointer to suspended thread's     */
+/*                                        control block                   */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_resume          Resume thread service             */
+/*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_thread_timeout                Thread timeout processing         */
+/*    _tx_thread_terminate              Thread terminate processing       */
+/*    _tx_thread_wait_abort             Thread wait abort processing      */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_semaphore_cleanup(TX_THREAD *thread_ptr, ULONG suspension_sequence)
+{
+
+#ifndef TX_NOT_INTERRUPTABLE
+TX_INTERRUPT_SAVE_AREA
+#endif
+
+TX_SEMAPHORE        *semaphore_ptr;
+UINT                suspended_count;
+TX_THREAD           *next_thread;
+TX_THREAD           *previous_thread;
+
+
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+    /* Disable interrupts to remove the suspended thread from the semaphore.  */
+    TX_DISABLE
+
+    /* Determine if the cleanup is still required.  */
+    if (thread_ptr -> tx_thread_suspend_cleanup == &(_tx_semaphore_cleanup))
+    {
+
+        /* Check for valid suspension sequence.  */
+        if (suspension_sequence == thread_ptr -> tx_thread_suspension_sequence)
+        {
+
+            /* Setup pointer to semaphore control block.  */
+            semaphore_ptr =  TX_VOID_TO_SEMAPHORE_POINTER_CONVERT(thread_ptr -> tx_thread_suspend_control_block);
+
+            /* Check for a NULL semaphore pointer.  */
+            if (semaphore_ptr != TX_NULL)
+            {
+
+                /* Check for a valid semaphore ID.  */
+                if (semaphore_ptr -> tx_semaphore_id == TX_SEMAPHORE_ID)
+                {
+
+                    /* Determine if there are any thread suspensions.  */
+                    if (semaphore_ptr -> tx_semaphore_suspended_count != TX_NO_SUSPENSIONS)
+                    {
+#else
+
+                        /* Setup pointer to semaphore control block.  */
+                        semaphore_ptr =  TX_VOID_TO_SEMAPHORE_POINTER_CONVERT(thread_ptr -> tx_thread_suspend_control_block);
+#endif
+
+                        /* Yes, we still have thread suspension!  */
+
+                        /* Clear the suspension cleanup flag.  */
+                        thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
+
+                        /* Decrement the suspended count.  */
+                        semaphore_ptr -> tx_semaphore_suspended_count--;
+
+                        /* Pickup the suspended count.  */
+                        suspended_count =  semaphore_ptr -> tx_semaphore_suspended_count;
+
+                        /* Remove the suspended thread from the list.  */
+
+                        /* See if this is the only suspended thread on the list.  */
+                        if (suspended_count == TX_NO_SUSPENSIONS)
+                        {
+
+                            /* Yes, the only suspended thread.  */
+
+                            /* Update the head pointer.  */
+                            semaphore_ptr -> tx_semaphore_suspension_list =  TX_NULL;
+                        }
+                        else
+                        {
+
+                            /* At least one more thread is on the same suspension list.  */
+
+                            /* Update the links of the adjacent threads.  */
+                            next_thread =                                   thread_ptr -> tx_thread_suspended_next;
+                            previous_thread =                               thread_ptr -> tx_thread_suspended_previous;
+                            next_thread -> tx_thread_suspended_previous =   previous_thread;
+                            previous_thread -> tx_thread_suspended_next =   next_thread;
+
+                            /* Determine if we need to update the head pointer.  */
+                            if (semaphore_ptr -> tx_semaphore_suspension_list == thread_ptr)
+                            {
+
+                                /* Update the list head pointer.  */
+                                semaphore_ptr -> tx_semaphore_suspension_list =   next_thread;
+                            }
+                        }
+
+                        /* Now we need to determine if this cleanup is from a terminate, timeout,
+                           or from a wait abort.  */
+                        if (thread_ptr -> tx_thread_state == TX_SEMAPHORE_SUSP)
+                        {
+
+                            /* Timeout condition and the thread is still suspended on the semaphore.
+                               Setup return error status and resume the thread.  */
+
+#ifdef TX_SEMAPHORE_ENABLE_PERFORMANCE_INFO
+
+                            /* Increment the total timeouts counter.  */
+                            _tx_semaphore_performance_timeout_count++;
+
+                            /* Increment the number of timeouts on this semaphore.  */
+                            semaphore_ptr -> tx_semaphore_performance_timeout_count++;
+#endif
+
+                            /* Setup return status.  */
+                            thread_ptr -> tx_thread_suspend_status =  TX_NO_INSTANCE;
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+                            /* Resume the thread!  */
+                            _tx_thread_system_ni_resume(thread_ptr);
+#else
+
+                            /* Temporarily disable preemption.  */
+                            _tx_thread_preempt_disable++;
+
+                            /* Restore interrupts.  */
+                            TX_RESTORE
+
+                            /* Resume the thread!  */
+                            _tx_thread_system_resume(thread_ptr);
+
+                            /* Disable interrupts.  */
+                            TX_DISABLE
+#endif
+                      }
+#ifndef TX_NOT_INTERRUPTABLE
+                    }
+                }
+            }
+        }
+    }
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+#endif
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_create.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_create.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_create.c	(revision 54)
@@ -0,0 +1,144 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Semaphore                                                           */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_trace.h"
+#include "tx_semaphore.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_semaphore_create                                PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function creates a counting semaphore with the initial count   */
+/*    specified in this call.                                             */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    semaphore_ptr                     Pointer to semaphore control block*/
+/*    name_ptr                          Pointer to semaphore name         */
+/*    initial_count                     Initial semaphore count           */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_SUCCESS                        Successful completion status      */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _tx_semaphore_create(TX_SEMAPHORE *semaphore_ptr, CHAR *name_ptr, ULONG initial_count)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+TX_SEMAPHORE    *next_semaphore;
+TX_SEMAPHORE    *previous_semaphore;
+
+
+    /* Initialize semaphore control block to all zeros.  */
+    TX_MEMSET(semaphore_ptr, 0, (sizeof(TX_SEMAPHORE)));
+
+    /* Setup the basic semaphore fields.  */
+    semaphore_ptr -> tx_semaphore_name =             name_ptr;
+    semaphore_ptr -> tx_semaphore_count =            initial_count;
+
+    /* Disable interrupts to place the semaphore on the created list.  */
+    TX_DISABLE
+
+    /* Setup the semaphore ID to make it valid.  */
+    semaphore_ptr -> tx_semaphore_id =  TX_SEMAPHORE_ID;
+
+    /* Place the semaphore on the list of created semaphores.  First,
+       check for an empty list.  */
+    if (_tx_semaphore_created_count == TX_EMPTY)
+    {
+
+        /* The created semaphore list is empty.  Add semaphore to empty list.  */
+        _tx_semaphore_created_ptr =                       semaphore_ptr;
+        semaphore_ptr -> tx_semaphore_created_next =      semaphore_ptr;
+        semaphore_ptr -> tx_semaphore_created_previous =  semaphore_ptr;
+    }
+    else
+    {
+
+        /* This list is not NULL, add to the end of the list.  */
+        next_semaphore =      _tx_semaphore_created_ptr;
+        previous_semaphore =  next_semaphore -> tx_semaphore_created_previous;
+
+        /* Place the new semaphore in the list.  */
+        next_semaphore -> tx_semaphore_created_previous =  semaphore_ptr;
+        previous_semaphore -> tx_semaphore_created_next =  semaphore_ptr;
+
+        /* Setup this semaphore's next and previous created links.  */
+        semaphore_ptr -> tx_semaphore_created_previous =  previous_semaphore;
+        semaphore_ptr -> tx_semaphore_created_next =      next_semaphore;
+    }
+
+    /* Increment the created count.  */
+    _tx_semaphore_created_count++;
+
+    /* Optional semaphore create extended processing.  */
+    TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr)
+
+    /* If trace is enabled, register this object.  */
+    TX_TRACE_OBJECT_REGISTER(TX_TRACE_OBJECT_TYPE_SEMAPHORE, semaphore_ptr, name_ptr, initial_count, 0)
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_SEMAPHORE_CREATE, semaphore_ptr, initial_count, TX_POINTER_TO_ULONG_CONVERT(&next_semaphore), 0, TX_TRACE_SEMAPHORE_EVENTS)
+
+    /* Log this kernel call.  */
+    TX_EL_SEMAPHORE_CREATE_INSERT
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Return TX_SUCCESS.  */
+    return(TX_SUCCESS);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_delete.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_delete.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_delete.c	(revision 54)
@@ -0,0 +1,209 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Semaphore                                                           */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_trace.h"
+#include "tx_thread.h"
+#include "tx_semaphore.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_semaphore_delete                                PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function deletes the specified semaphore.  All threads         */
+/*    suspended on the semaphore are resumed with the TX_DELETED status   */
+/*    code.                                                               */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    semaphore_ptr                     Pointer to semaphore control block*/
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_SUCCESS                        Successful completion status      */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_preempt_check   Check for preemption              */
+/*    _tx_thread_system_resume          Resume thread service             */
+/*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _tx_semaphore_delete(TX_SEMAPHORE *semaphore_ptr)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+TX_THREAD       *thread_ptr;
+TX_THREAD       *next_thread;
+UINT            suspended_count;
+TX_SEMAPHORE    *next_semaphore;
+TX_SEMAPHORE    *previous_semaphore;
+
+
+    /* Disable interrupts to remove the semaphore from the created list.  */
+    TX_DISABLE
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_SEMAPHORE_DELETE, semaphore_ptr, TX_POINTER_TO_ULONG_CONVERT(&thread_ptr), 0, 0, TX_TRACE_SEMAPHORE_EVENTS)
+
+    /* Optional semaphore delete extended processing.  */
+    TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr)
+
+    /* If trace is enabled, unregister this object.  */
+    TX_TRACE_OBJECT_UNREGISTER(semaphore_ptr)
+
+    /* Log this kernel call.  */
+    TX_EL_SEMAPHORE_DELETE_INSERT
+
+    /* Clear the semaphore ID to make it invalid.  */
+    semaphore_ptr -> tx_semaphore_id =  TX_CLEAR_ID;
+
+    /* Decrement the number of semaphores.  */
+    _tx_semaphore_created_count--;
+
+    /* See if the semaphore is the only one on the list.  */
+    if (_tx_semaphore_created_count == TX_EMPTY)
+    {
+
+        /* Only created semaphore, just set the created list to NULL.  */
+        _tx_semaphore_created_ptr =  TX_NULL;
+    }
+    else
+    {
+
+        /* Link-up the neighbors.  */
+        next_semaphore =                                   semaphore_ptr -> tx_semaphore_created_next;
+        previous_semaphore =                               semaphore_ptr -> tx_semaphore_created_previous;
+        next_semaphore -> tx_semaphore_created_previous =  previous_semaphore;
+        previous_semaphore -> tx_semaphore_created_next =  next_semaphore;
+
+        /* See if we have to update the created list head pointer.  */
+        if (_tx_semaphore_created_ptr == semaphore_ptr)
+        {
+
+            /* Yes, move the head pointer to the next link. */
+            _tx_semaphore_created_ptr =  next_semaphore;
+        }
+    }
+
+    /* Temporarily disable preemption.  */
+    _tx_thread_preempt_disable++;
+
+    /* Pickup the suspension information.  */
+    thread_ptr =                                     semaphore_ptr -> tx_semaphore_suspension_list;
+    semaphore_ptr -> tx_semaphore_suspension_list =  TX_NULL;
+    suspended_count =                                semaphore_ptr -> tx_semaphore_suspended_count;
+    semaphore_ptr -> tx_semaphore_suspended_count =  TX_NO_SUSPENSIONS;
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Walk through the semaphore list to resume any and all threads suspended
+       on this semaphore.  */
+    while (suspended_count != TX_NO_SUSPENSIONS)
+    {
+
+        /* Decrement the suspension count.  */
+        suspended_count--;
+
+        /* Lockout interrupts.  */
+        TX_DISABLE
+
+        /* Clear the cleanup pointer, this prevents the timeout from doing
+           anything.  */
+        thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
+
+        /* Set the return status in the thread to TX_DELETED.  */
+        thread_ptr -> tx_thread_suspend_status =  TX_DELETED;
+
+        /* Move the thread pointer ahead.  */
+        next_thread =  thread_ptr -> tx_thread_suspended_next;
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+        /* Resume the thread!  */
+        _tx_thread_system_ni_resume(thread_ptr);
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+#else
+
+        /* Temporarily disable preemption again.  */
+        _tx_thread_preempt_disable++;
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Resume the thread.  */
+        _tx_thread_system_resume(thread_ptr);
+#endif
+
+        /* Move to next thread.  */
+        thread_ptr =  next_thread;
+    }
+
+    /* Execute Port-Specific completion processing. If needed, it is typically defined in tx_port.h.  */
+    TX_SEMAPHORE_DELETE_PORT_COMPLETION(semaphore_ptr)
+
+    /* Disable interrupts.  */
+    TX_DISABLE
+
+    /* Release previous preempt disable.  */
+    _tx_thread_preempt_disable--;
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Check for preemption.  */
+    _tx_thread_system_preempt_check();
+
+    /* Return TX_SUCCESS.  */
+    return(TX_SUCCESS);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_get.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_get.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_get.c	(revision 54)
@@ -0,0 +1,234 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Semaphore                                                           */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_trace.h"
+#include "tx_thread.h"
+#include "tx_semaphore.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_semaphore_get                                   PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function gets an instance from the specified counting          */
+/*    semaphore.                                                          */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    semaphore_ptr                     Pointer to semaphore control block*/
+/*    wait_option                       Suspension option                 */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    status                            Completion status                 */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_suspend         Suspend thread service            */
+/*    _tx_thread_system_ni_suspend      Non-interruptable suspend thread  */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _tx_semaphore_get(TX_SEMAPHORE *semaphore_ptr, ULONG wait_option)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+TX_THREAD       *thread_ptr;
+TX_THREAD       *next_thread;
+TX_THREAD       *previous_thread;
+UINT            status;
+
+
+    /* Default the status to TX_SUCCESS.  */
+    status =  TX_SUCCESS;
+
+    /* Disable interrupts to get an instance from the semaphore.  */
+    TX_DISABLE
+
+#ifdef TX_SEMAPHORE_ENABLE_PERFORMANCE_INFO
+
+    /* Increment the total semaphore get counter.  */
+    _tx_semaphore_performance_get_count++;
+
+    /* Increment the number of attempts to get this semaphore.  */
+    semaphore_ptr -> tx_semaphore_performance_get_count++;
+#endif
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_SEMAPHORE_GET, semaphore_ptr, wait_option, semaphore_ptr -> tx_semaphore_count, TX_POINTER_TO_ULONG_CONVERT(&thread_ptr), TX_TRACE_SEMAPHORE_EVENTS)
+
+    /* Log this kernel call.  */
+    TX_EL_SEMAPHORE_GET_INSERT
+
+    /* Determine if there is an instance of the semaphore.  */
+    if (semaphore_ptr -> tx_semaphore_count != ((ULONG) 0))
+    {
+
+        /* Decrement the semaphore count.  */
+        semaphore_ptr -> tx_semaphore_count--;
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+    }
+
+    /* Determine if the request specifies suspension.  */
+    else if (wait_option != TX_NO_WAIT)
+    {
+
+        /* Determine if the preempt disable flag is non-zero.  */
+        if (_tx_thread_preempt_disable != ((UINT) 0))
+        {
+
+            /* Restore interrupts.  */
+            TX_RESTORE
+
+            /* Suspension is not allowed if the preempt disable flag is non-zero at this point - return error completion.  */
+            status =  TX_NO_INSTANCE;
+        }
+        else
+        {
+
+            /* Prepare for suspension of this thread.  */
+
+#ifdef TX_SEMAPHORE_ENABLE_PERFORMANCE_INFO
+
+            /* Increment the total semaphore suspensions counter.  */
+            _tx_semaphore_performance_suspension_count++;
+
+            /* Increment the number of suspensions on this semaphore.  */
+            semaphore_ptr -> tx_semaphore_performance_suspension_count++;
+#endif
+
+            /* Pickup thread pointer.  */
+            TX_THREAD_GET_CURRENT(thread_ptr)
+
+            /* Setup cleanup routine pointer.  */
+            thread_ptr -> tx_thread_suspend_cleanup =  &(_tx_semaphore_cleanup);
+
+            /* Setup cleanup information, i.e. this semaphore control
+               block.  */
+            thread_ptr -> tx_thread_suspend_control_block =  (VOID *) semaphore_ptr;
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+            /* Increment the suspension sequence number, which is used to identify
+               this suspension event.  */
+            thread_ptr -> tx_thread_suspension_sequence++;
+#endif
+
+            /* Setup suspension list.  */
+            if (semaphore_ptr -> tx_semaphore_suspended_count == TX_NO_SUSPENSIONS)
+            {
+
+                /* No other threads are suspended.  Setup the head pointer and
+                   just setup this threads pointers to itself.  */
+                semaphore_ptr -> tx_semaphore_suspension_list =         thread_ptr;
+                thread_ptr -> tx_thread_suspended_next =                thread_ptr;
+                thread_ptr -> tx_thread_suspended_previous =            thread_ptr;
+            }
+            else
+            {
+
+                /* This list is not NULL, add current thread to the end. */
+                next_thread =                                   semaphore_ptr -> tx_semaphore_suspension_list;
+                thread_ptr -> tx_thread_suspended_next =        next_thread;
+                previous_thread =                               next_thread -> tx_thread_suspended_previous;
+                thread_ptr -> tx_thread_suspended_previous =    previous_thread;
+                previous_thread -> tx_thread_suspended_next =   thread_ptr;
+                next_thread -> tx_thread_suspended_previous =   thread_ptr;
+            }
+
+            /* Increment the number of suspensions.  */
+            semaphore_ptr -> tx_semaphore_suspended_count++;
+
+            /* Set the state to suspended.  */
+            thread_ptr -> tx_thread_state =    TX_SEMAPHORE_SUSP;
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+            /* Call actual non-interruptable thread suspension routine.  */
+            _tx_thread_system_ni_suspend(thread_ptr, wait_option);
+
+            /* Restore interrupts.  */
+            TX_RESTORE
+#else
+
+            /* Set the suspending flag.  */
+            thread_ptr -> tx_thread_suspending =  TX_TRUE;
+
+            /* Setup the timeout period.  */
+            thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option;
+
+            /* Temporarily disable preemption.  */
+            _tx_thread_preempt_disable++;
+
+            /* Restore interrupts.  */
+            TX_RESTORE
+
+            /* Call actual thread suspension routine.  */
+            _tx_thread_system_suspend(thread_ptr);
+#endif
+
+            /* Return the completion status.  */
+            status =  thread_ptr -> tx_thread_suspend_status;
+        }
+    }
+    else
+    {
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Immediate return, return error completion.  */
+        status =  TX_NO_INSTANCE;
+    }
+
+    /* Return completion status.  */
+    return(status);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_put.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_put.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_semaphore_put.c	(revision 54)
@@ -0,0 +1,224 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Semaphore                                                           */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_trace.h"
+#include "tx_thread.h"
+#include "tx_semaphore.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_semaphore_put                                   PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function puts an instance into the specified counting          */
+/*    semaphore.                                                          */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    semaphore_ptr                     Pointer to semaphore control block*/
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_SUCCESS                        Success completion status         */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_resume          Resume thread service             */
+/*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _tx_semaphore_put(TX_SEMAPHORE *semaphore_ptr)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+#ifndef TX_DISABLE_NOTIFY_CALLBACKS
+VOID            (*semaphore_put_notify)(struct TX_SEMAPHORE_STRUCT *notify_semaphore_ptr);
+#endif
+
+TX_THREAD       *thread_ptr;
+UINT            suspended_count;
+TX_THREAD       *next_thread;
+TX_THREAD       *previous_thread;
+
+
+    /* Disable interrupts to put an instance back to the semaphore.  */
+    TX_DISABLE
+
+#ifdef TX_SEMAPHORE_ENABLE_PERFORMANCE_INFO
+
+    /* Increment the total semaphore put counter.  */
+    _tx_semaphore_performance_put_count++;
+
+    /* Increment the number of puts on this semaphore.  */
+    semaphore_ptr -> tx_semaphore_performance_put_count++;
+#endif
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_SEMAPHORE_PUT, semaphore_ptr, semaphore_ptr -> tx_semaphore_count, semaphore_ptr -> tx_semaphore_suspended_count, TX_POINTER_TO_ULONG_CONVERT(&thread_ptr), TX_TRACE_SEMAPHORE_EVENTS)
+
+    /* Log this kernel call.  */
+    TX_EL_SEMAPHORE_PUT_INSERT
+
+    /* Pickup the number of suspended threads.  */
+    suspended_count =  semaphore_ptr -> tx_semaphore_suspended_count;
+
+    /* Determine if there are any threads suspended on the semaphore.  */
+    if (suspended_count == TX_NO_SUSPENSIONS)
+    {
+
+        /* Increment the semaphore count.  */
+        semaphore_ptr -> tx_semaphore_count++;
+
+#ifndef TX_DISABLE_NOTIFY_CALLBACKS
+
+        /* Pickup the application notify function.  */
+        semaphore_put_notify =  semaphore_ptr -> tx_semaphore_put_notify;
+#endif
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+#ifndef TX_DISABLE_NOTIFY_CALLBACKS
+
+        /* Determine if notification is required.  */
+        if (semaphore_put_notify != TX_NULL)
+        {
+
+            /* Yes, call the appropriate notify callback function.  */
+            (semaphore_put_notify)(semaphore_ptr);
+        }
+#endif
+    }
+    else
+    {
+
+        /* A thread is suspended on this semaphore.  */
+
+        /* Pickup the pointer to the first suspended thread.  */
+        thread_ptr =  semaphore_ptr -> tx_semaphore_suspension_list;
+
+        /* Remove the suspended thread from the list.  */
+
+        /* See if this is the only suspended thread on the list.  */
+        suspended_count--;
+        if (suspended_count == TX_NO_SUSPENSIONS)
+        {
+
+            /* Yes, the only suspended thread.  */
+
+            /* Update the head pointer.  */
+            semaphore_ptr -> tx_semaphore_suspension_list =  TX_NULL;
+        }
+        else
+        {
+
+            /* At least one more thread is on the same expiration list.  */
+
+            /* Update the list head pointer.  */
+            next_thread =                                     thread_ptr -> tx_thread_suspended_next;
+            semaphore_ptr -> tx_semaphore_suspension_list =   next_thread;
+
+            /* Update the links of the adjacent threads.  */
+            previous_thread =                               thread_ptr -> tx_thread_suspended_previous;
+            next_thread -> tx_thread_suspended_previous =   previous_thread;
+            previous_thread -> tx_thread_suspended_next =   next_thread;
+        }
+
+        /* Decrement the suspension count.  */
+        semaphore_ptr -> tx_semaphore_suspended_count =  suspended_count;
+
+        /* Prepare for resumption of the first thread.  */
+
+        /* Clear cleanup routine to avoid timeout.  */
+        thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
+
+#ifndef TX_DISABLE_NOTIFY_CALLBACKS
+
+        /* Pickup the application notify function.  */
+        semaphore_put_notify =  semaphore_ptr -> tx_semaphore_put_notify;
+#endif
+
+        /* Put return status into the thread control block.  */
+        thread_ptr -> tx_thread_suspend_status =  TX_SUCCESS;
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+        /* Resume the thread!  */
+        _tx_thread_system_ni_resume(thread_ptr);
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+#else
+
+        /* Temporarily disable preemption.  */
+        _tx_thread_preempt_disable++;
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Resume thread.  */
+        _tx_thread_system_resume(thread_ptr);
+#endif
+
+#ifndef TX_DISABLE_NOTIFY_CALLBACKS
+
+        /* Determine if notification is required.  */
+        if (semaphore_put_notify != TX_NULL)
+        {
+
+            /* Yes, call the appropriate notify callback function.  */
+            (semaphore_put_notify)(semaphore_ptr);
+        }
+#endif
+    }
+
+    /* Return successful completion.  */
+    return(TX_SUCCESS);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_create.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_create.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_create.c	(revision 54)
@@ -0,0 +1,391 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Thread                                                              */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_trace.h"
+#include "tx_thread.h"
+#include "tx_initialize.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_thread_create                                   PORTABLE C      */
+/*                                                           6.3.0        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function creates a thread and places it on the list of created */
+/*    threads.                                                            */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    thread_ptr                            Thread control block pointer  */
+/*    name                                  Pointer to thread name string */
+/*    entry_function                        Entry function of the thread  */
+/*    entry_input                           32-bit input value to thread  */
+/*    stack_start                           Pointer to start of stack     */
+/*    stack_size                            Stack size in bytes           */
+/*    priority                              Priority of thread            */
+/*                                            (default 0-31)              */
+/*    preempt_threshold                     Preemption threshold          */
+/*    time_slice                            Thread time-slice value       */
+/*    auto_start                            Automatic start selection     */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    return status                         Thread create return status   */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_stack_build                Build initial thread stack    */
+/*    _tx_thread_system_resume              Resume automatic start thread */
+/*    _tx_thread_system_ni_resume           Noninterruptable resume thread*/
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*    _tx_timer_initialize                  Create system timer thread    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020      William E. Lamie        Initial Version 6.0           */
+/*  09-30-2020      William E. Lamie        Modified comment(s), and      */
+/*                                            changed stack calculations  */
+/*                                            to use ALIGN_TYPE integers, */
+/*                                            resulting in version 6.1    */
+/*  06-02-2021      William E. Lamie        Modified comment(s), and      */
+/*                                            supported TX_MISRA_ENABLE,  */
+/*  08-02-2021      Scott Larson            Removed unneeded cast,        */
+/*                                            resulting in version 6.1.8  */
+/*  10-31-2023      Xiuwen Cai              Modified comment(s),          */
+/*                                            added option for random     */
+/*                                            number stack filling,       */
+/*                                            resulting in version 6.3.0  */
+/*                                                                        */
+/**************************************************************************/
+UINT  _tx_thread_create(TX_THREAD *thread_ptr, CHAR *name_ptr, VOID (*entry_function)(ULONG id), ULONG entry_input,
+                            VOID *stack_start, ULONG stack_size, UINT priority, UINT preempt_threshold,
+                            ULONG time_slice, UINT auto_start)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+TX_THREAD               *next_thread;
+TX_THREAD               *previous_thread;
+TX_THREAD               *saved_thread_ptr;
+UINT                    saved_threshold =  ((UINT) 0);
+UCHAR                   *temp_ptr;
+
+#ifdef TX_ENABLE_STACK_CHECKING
+ALIGN_TYPE              new_stack_start;
+ALIGN_TYPE              updated_stack_start;
+#endif
+
+#ifndef TX_DISABLE_STACK_FILLING
+#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING)
+
+    /* Initialize the stack fill value to a 8-bit random value.  */
+    thread_ptr -> tx_thread_stack_fill_value = ((ULONG) TX_RAND()) & 0xFFUL;
+
+    /* Duplicate the random value in each of the 4 bytes of the stack fill value.  */
+    thread_ptr -> tx_thread_stack_fill_value = thread_ptr -> tx_thread_stack_fill_value |
+                    (thread_ptr -> tx_thread_stack_fill_value << 8) |
+                    (thread_ptr -> tx_thread_stack_fill_value << 16) |
+                    (thread_ptr -> tx_thread_stack_fill_value << 24);
+#endif
+
+    /* Set the thread stack to a pattern prior to creating the initial
+       stack frame.  This pattern is used by the stack checking routines
+       to see how much has been used.  */
+    TX_MEMSET(stack_start, ((UCHAR) TX_STACK_FILL), stack_size);
+#endif
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+    /* Ensure that there are two ULONG of 0xEF patterns at the top and
+       bottom of the thread's stack. This will be used to check for stack
+       overflow conditions during run-time.  */
+    stack_size =  ((stack_size/(sizeof(ULONG))) * (sizeof(ULONG))) - (sizeof(ULONG));
+
+    /* Ensure the starting stack address is evenly aligned.  */
+#ifdef TX_MISRA_ENABLE
+    new_stack_start = TX_POINTER_TO_ULONG_CONVERT(stack_start);
+#else
+    new_stack_start =  TX_POINTER_TO_ALIGN_TYPE_CONVERT(stack_start);
+#endif /* TX_MISRA_ENABLE */
+    updated_stack_start =  (((new_stack_start) + ((sizeof(ULONG)) - ((ULONG) 1)) ) & (~((sizeof(ULONG)) - ((ULONG) 1))));
+
+    /* Determine if the starting stack address is different.  */
+    if (new_stack_start != updated_stack_start)
+    {
+
+        /* Yes, subtract another ULONG from the size to avoid going past the stack area.  */
+        stack_size =  stack_size - (sizeof(ULONG));
+    }
+
+    /* Update the starting stack pointer.  */
+#ifdef TX_MISRA_ENABLE
+    stack_start = TX_ULONG_TO_POINTER_CONVERT(updated_stack_start);
+#else
+    stack_start =  TX_ALIGN_TYPE_TO_POINTER_CONVERT(updated_stack_start);
+#endif /* TX_MISRA_ENABLE */
+#endif
+
+    /* Prepare the thread control block prior to placing it on the created
+       list.  */
+
+    /* Initialize thread control block to all zeros.  */
+    TX_MEMSET(thread_ptr, 0, (sizeof(TX_THREAD)));
+
+    /* Place the supplied parameters into the thread's control block.  */
+    thread_ptr -> tx_thread_name =              name_ptr;
+    thread_ptr -> tx_thread_entry =             entry_function;
+    thread_ptr -> tx_thread_entry_parameter =   entry_input;
+    thread_ptr -> tx_thread_stack_start =       stack_start;
+    thread_ptr -> tx_thread_stack_size =        stack_size;
+    thread_ptr -> tx_thread_priority =          priority;
+    thread_ptr -> tx_thread_user_priority =     priority;
+    thread_ptr -> tx_thread_time_slice =        time_slice;
+    thread_ptr -> tx_thread_new_time_slice =    time_slice;
+    thread_ptr -> tx_thread_inherit_priority =  ((UINT) TX_MAX_PRIORITIES);
+
+    /* Calculate the end of the thread's stack area.  */
+    temp_ptr =  TX_VOID_TO_UCHAR_POINTER_CONVERT(stack_start);
+    temp_ptr =  (TX_UCHAR_POINTER_ADD(temp_ptr, (stack_size - ((ULONG) 1))));
+    thread_ptr -> tx_thread_stack_end =         TX_UCHAR_TO_VOID_POINTER_CONVERT(temp_ptr);
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+
+    /* Preemption-threshold is enabled, setup accordingly.  */
+    thread_ptr -> tx_thread_preempt_threshold =       preempt_threshold;
+    thread_ptr -> tx_thread_user_preempt_threshold =  preempt_threshold;
+#else
+
+    /* Preemption-threshold is disabled, determine if preemption-threshold was required.  */
+    if (priority != preempt_threshold)
+    {
+
+        /* Preemption-threshold specified. Since specific preemption-threshold is not supported,
+           disable all preemption.  */
+        thread_ptr -> tx_thread_preempt_threshold =       ((UINT) 0);
+        thread_ptr -> tx_thread_user_preempt_threshold =  ((UINT) 0);
+    }
+    else
+    {
+
+        /* Preemption-threshold is not specified, just setup with the priority.  */
+        thread_ptr -> tx_thread_preempt_threshold =       priority;
+        thread_ptr -> tx_thread_user_preempt_threshold =  priority;
+    }
+#endif
+
+    /* Now fill in the values that are required for thread initialization.  */
+    thread_ptr -> tx_thread_state =  TX_SUSPENDED;
+
+    /* Setup the necessary fields in the thread timer block.  */
+    TX_THREAD_CREATE_TIMEOUT_SETUP(thread_ptr)
+
+    /* Perform any additional thread setup activities for tool or user purpose.  */
+    TX_THREAD_CREATE_INTERNAL_EXTENSION(thread_ptr)
+
+    /* Call the target specific stack frame building routine to build the
+       thread's initial stack and to setup the actual stack pointer in the
+       control block.  */
+    _tx_thread_stack_build(thread_ptr, _tx_thread_shell_entry);
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+    /* Setup the highest usage stack pointer.  */
+    thread_ptr -> tx_thread_stack_highest_ptr =  thread_ptr -> tx_thread_stack_ptr;
+#endif
+
+    /* Prepare to make this thread a member of the created thread list.  */
+    TX_DISABLE
+
+    /* Load the thread ID field in the thread control block.  */
+    thread_ptr -> tx_thread_id =  TX_THREAD_ID;
+
+    /* Place the thread on the list of created threads.  First,
+       check for an empty list.  */
+    if (_tx_thread_created_count == TX_EMPTY)
+    {
+
+        /* The created thread list is empty.  Add thread to empty list.  */
+        _tx_thread_created_ptr =                    thread_ptr;
+        thread_ptr -> tx_thread_created_next =      thread_ptr;
+        thread_ptr -> tx_thread_created_previous =  thread_ptr;
+    }
+    else
+    {
+
+        /* This list is not NULL, add to the end of the list.  */
+        next_thread =  _tx_thread_created_ptr;
+        previous_thread =  next_thread -> tx_thread_created_previous;
+
+        /* Place the new thread in the list.  */
+        next_thread -> tx_thread_created_previous =  thread_ptr;
+        previous_thread -> tx_thread_created_next =  thread_ptr;
+
+        /* Setup this thread's created links.  */
+        thread_ptr -> tx_thread_created_previous =  previous_thread;
+        thread_ptr -> tx_thread_created_next =      next_thread;
+    }
+
+    /* Increment the thread created count.  */
+    _tx_thread_created_count++;
+
+    /* If trace is enabled, register this object.  */
+    TX_TRACE_OBJECT_REGISTER(TX_TRACE_OBJECT_TYPE_THREAD, thread_ptr, name_ptr, TX_POINTER_TO_ULONG_CONVERT(stack_start), stack_size)
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_THREAD_CREATE, thread_ptr, priority, TX_POINTER_TO_ULONG_CONVERT(stack_start), stack_size, TX_TRACE_THREAD_EVENTS)
+
+    /* Register thread in the thread array structure.  */
+    TX_EL_THREAD_REGISTER(thread_ptr)
+
+    /* Log this kernel call.  */
+    TX_EL_THREAD_CREATE_INSERT
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+    /* Temporarily disable preemption.  */
+    _tx_thread_preempt_disable++;
+#endif
+
+    /* Determine if an automatic start was requested.  If so, call the resume
+       thread function and then check for a preemption condition.  */
+    if (auto_start == TX_AUTO_START)
+    {
+
+        /* Determine if the create call is being called from initialization.  */
+        if (TX_THREAD_GET_SYSTEM_STATE() >= TX_INITIALIZE_IN_PROGRESS)
+        {
+
+            /* Yes, this create call was made from initialization.  */
+
+            /* Pickup the current thread execute pointer, which corresponds to the
+               highest priority thread ready to execute.  Interrupt lockout is
+               not required, since interrupts are assumed to be disabled during
+               initialization.  */
+            saved_thread_ptr =  _tx_thread_execute_ptr;
+
+            /* Determine if there is thread ready for execution.  */
+            if (saved_thread_ptr != TX_NULL)
+            {
+
+                /* Yes, a thread is ready for execution when initialization completes.  */
+
+                /* Save the current preemption-threshold.  */
+                saved_threshold =  saved_thread_ptr -> tx_thread_preempt_threshold;
+
+                /* For initialization, temporarily set the preemption-threshold to the
+                   priority level to make sure the highest-priority thread runs once
+                   initialization is complete.  */
+                saved_thread_ptr -> tx_thread_preempt_threshold =  saved_thread_ptr -> tx_thread_priority;
+            }
+        }
+        else
+        {
+
+            /* Simply set the saved thread pointer to NULL.  */
+            saved_thread_ptr =  TX_NULL;
+        }
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+        /* Perform any additional activities for tool or user purpose.  */
+        TX_THREAD_CREATE_EXTENSION(thread_ptr)
+
+        /* Resume the thread!  */
+        _tx_thread_system_ni_resume(thread_ptr);
+
+        /* Restore previous interrupt posture.  */
+        TX_RESTORE
+#else
+
+        /* Restore previous interrupt posture.  */
+        TX_RESTORE
+
+        /* Perform any additional activities for tool or user purpose.  */
+        TX_THREAD_CREATE_EXTENSION(thread_ptr)
+
+        /* Call the resume thread function to make this thread ready.  */
+        _tx_thread_system_resume(thread_ptr);
+#endif
+
+        /* Determine if the thread's preemption-threshold needs to be restored.  */
+        if (saved_thread_ptr != TX_NULL)
+        {
+
+            /* Yes, restore the previous highest-priority thread's preemption-threshold. This
+               can only happen if this routine is called from initialization.  */
+            saved_thread_ptr -> tx_thread_preempt_threshold =  saved_threshold;
+        }
+    }
+    else
+    {
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+        /* Perform any additional activities for tool or user purpose.  */
+        TX_THREAD_CREATE_EXTENSION(thread_ptr)
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+#else
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Perform any additional activities for tool or user purpose.  */
+        TX_THREAD_CREATE_EXTENSION(thread_ptr)
+
+        /* Disable interrupts.  */
+        TX_DISABLE
+
+        /* Re-enable preemption.  */
+        _tx_thread_preempt_disable--;
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Check for preemption.  */
+        _tx_thread_system_preempt_check();
+#endif
+    }
+
+    /* Always return a success.  */
+    return(TX_SUCCESS);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_identify.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_identify.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_identify.c	(revision 54)
@@ -0,0 +1,105 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Thread                                                              */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#ifdef TX_ENABLE_EVENT_TRACE
+#include "tx_trace.h"
+#endif
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_thread_identify                                 PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function returns the control block pointer of the currently    */
+/*    executing thread.  If the return value is NULL, no thread is        */
+/*    executing.                                                          */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_THREAD *                           Pointer to control block of   */
+/*                                            currently executing thread  */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+TX_THREAD  *_tx_thread_identify(VOID)
+{
+
+TX_THREAD       *thread_ptr;
+
+TX_INTERRUPT_SAVE_AREA
+
+
+    /* Disable interrupts to put the timer on the created list.  */
+    TX_DISABLE
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_THREAD_IDENTIFY, 0, 0, 0, 0, TX_TRACE_THREAD_EVENTS)
+#endif
+
+   /* Log this kernel call.  */
+    TX_EL_THREAD_IDENTIFY_INSERT
+
+    /* Pickup thread pointer.  */
+    TX_THREAD_GET_CURRENT(thread_ptr)
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Return the current thread pointer.  */
+    return(thread_ptr);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_initialize.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_initialize.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_initialize.c	(revision 54)
@@ -0,0 +1,456 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Thread                                                              */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+#ifndef TX_MISRA_ENABLE
+#define TX_THREAD_INIT
+#endif
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_initialize.h"
+#include "tx_thread.h"
+
+
+/* Define the pointer that contains the system stack pointer.  This is
+   utilized when control returns from a thread to the system to reset the
+   current stack.  This is setup in the low-level initialization function. */
+
+VOID *          _tx_thread_system_stack_ptr;
+
+
+/* Define the current thread pointer.  This variable points to the currently
+   executing thread.  If this variable is NULL, no thread is executing.  */
+
+TX_THREAD *     _tx_thread_current_ptr;
+
+
+/* Define the variable that holds the next thread to execute.  It is important
+   to remember that this is not necessarily equal to the current thread 
+   pointer.  */
+
+TX_THREAD *     _tx_thread_execute_ptr;
+
+
+/* Define the head pointer of the created thread list.  */
+
+TX_THREAD *     _tx_thread_created_ptr;
+
+
+/* Define the variable that holds the number of created threads. */
+
+ULONG           _tx_thread_created_count;
+
+
+/* Define the current state variable.  When this value is 0, a thread
+   is executing or the system is idle.  Other values indicate that 
+   interrupt or initialization processing is active.  This variable is
+   initialized to TX_INITIALIZE_IN_PROGRESS to indicate initialization is
+   active.  */
+
+volatile ULONG  _tx_thread_system_state =  TX_INITIALIZE_IN_PROGRESS;
+
+
+/* Define the 32-bit priority bit-maps. There is one priority bit map for each
+   32 priority levels supported. If only 32 priorities are supported there is 
+   only one bit map. Each bit within a priority bit map represents that one 
+   or more threads at the associated thread priority are ready.  */
+
+ULONG           _tx_thread_priority_maps[TX_MAX_PRIORITIES/32];
+
+
+/* Define the priority map active bit map that specifies which of the previously 
+   defined priority maps have something set. This is only necessary if more than 
+   32 priorities are supported.  */
+
+#if TX_MAX_PRIORITIES > 32
+ULONG           _tx_thread_priority_map_active;
+#endif
+
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+
+/* Define the 32-bit preempt priority bit maps.  There is one preempt bit map 
+   for each 32 priority levels supported. If only 32 priorities are supported 
+   there is only one bit map. Each set set bit corresponds to a preempted priority 
+   level that had preemption-threshold active to protect against preemption of a 
+   range of relatively higher priority threads.  */
+
+ULONG           _tx_thread_preempted_maps[TX_MAX_PRIORITIES/32];
+
+
+/* Define the preempt map active bit map that specifies which of the previously 
+   defined preempt maps have something set. This is only necessary if more than 
+   32 priorities are supported.  */
+
+#if TX_MAX_PRIORITIES > 32
+ULONG           _tx_thread_preempted_map_active;
+#endif
+#endif
+
+/* Define the variable that holds the highest priority group ready for 
+   execution.  It is important to note that this is not necessarily the same
+   as the priority of the thread pointed to by _tx_execute_thread.  */
+
+UINT            _tx_thread_highest_priority;
+
+
+/* Define the array of thread pointers.  Each entry represents the threads that
+   are ready at that priority group.  For example, index 10 in this array
+   represents the first thread ready at priority 10.  If this entry is NULL,
+   no threads are ready at that priority.  */
+
+TX_THREAD *     _tx_thread_priority_list[TX_MAX_PRIORITIES];
+
+
+/* Define the global preempt disable variable.  If this is non-zero, preemption is
+   disabled.  It is used internally by ThreadX to prevent preemption of a thread in 
+   the middle of a service that is resuming or suspending another thread.  */
+
+volatile UINT   _tx_thread_preempt_disable;
+
+
+/* Define the global function pointer for mutex cleanup on thread completion or 
+   termination. This pointer is setup during mutex initialization.  */
+
+VOID            (*_tx_thread_mutex_release)(TX_THREAD *thread_ptr);
+
+
+/* Define the global build options variable.  This contains a bit map representing
+   how the ThreadX library was built. The following are the bit field definitions:
+
+                    Bit(s)                   Meaning
+
+                    31                  TX_NOT_INTERRUPTABLE defined
+                    30                  TX_INLINE_THREAD_RESUME_SUSPEND define
+                    29-24               Priority groups 1  -> 32 priorities
+                                                        2  -> 64 priorities
+                                                        3  -> 96 priorities
+
+                                                        ...
+
+                                                        32 -> 1024 priorities
+                    23                  TX_TIMER_PROCESS_IN_ISR defined
+                    22                  TX_REACTIVATE_INLINE defined
+                    21                  TX_DISABLE_STACK_FILLING defined
+                    20                  TX_ENABLE_STACK_CHECKING defined
+                    19                  TX_DISABLE_PREEMPTION_THRESHOLD defined
+                    18                  TX_DISABLE_REDUNDANT_CLEARING defined
+                    17                  TX_DISABLE_NOTIFY_CALLBACKS defined
+                    16                  TX_BLOCK_POOL_ENABLE_PERFORMANCE_INFO defined
+                    15                  TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO defined
+                    14                  TX_EVENT_FLAGS_ENABLE_PERFORMANCE_INFO defined
+                    13                  TX_MUTEX_ENABLE_PERFORMANCE_INFO defined
+                    12                  TX_QUEUE_ENABLE_PERFORMANCE_INFO defined
+                    11                  TX_SEMAPHORE_ENABLE_PERFORMANCE_INFO defined
+                    10                  TX_THREAD_ENABLE_PERFORMANCE_INFO defined
+                    9                   TX_TIMER_ENABLE_PERFORMANCE_INFO defined
+                    8                   TX_ENABLE_EVENT_TRACE defined
+                    7                   TX_ENABLE_EXECUTION_CHANGE_NOTIFY defined
+                    6-0                 Port Specific   */
+
+ULONG           _tx_build_options;
+
+
+#if defined(TX_ENABLE_STACK_CHECKING) || defined(TX_PORT_THREAD_STACK_ERROR_HANDLING)
+
+/* Define the global function pointer for stack error handling. If a stack error is 
+   detected and the application has registered a stack error handler, it will be 
+   called via this function pointer.  */
+
+VOID            (*_tx_thread_application_stack_error_handler)(TX_THREAD *thread_ptr);
+
+#endif
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+/* Define the total number of thread resumptions. Each time a thread enters the
+   ready state this variable is incremented.  */
+
+ULONG           _tx_thread_performance_resume_count;
+
+
+/* Define the total number of thread suspensions. Each time a thread enters a 
+   suspended state this variable is incremented.  */
+
+ULONG           _tx_thread_performance_suspend_count;
+
+
+/* Define the total number of solicited thread preemptions. Each time a thread is 
+   preempted by directly calling a ThreadX service, this variable is incremented.  */
+
+ULONG           _tx_thread_performance_solicited_preemption_count;
+
+
+/* Define the total number of interrupt thread preemptions. Each time a thread is 
+   preempted as a result of an ISR calling a ThreadX service, this variable is 
+   incremented.  */
+
+ULONG           _tx_thread_performance_interrupt_preemption_count;
+
+
+/* Define the total number of priority inversions. Each time a thread is blocked by
+   a mutex owned by a lower-priority thread, this variable is incremented.  */
+
+ULONG           _tx_thread_performance_priority_inversion_count;
+
+
+/* Define the total number of time-slices.  Each time a time-slice operation is 
+   actually performed (another thread is setup for running) this variable is 
+   incremented.  */
+
+ULONG           _tx_thread_performance_time_slice_count;
+
+
+/* Define the total number of thread relinquish operations.  Each time a thread 
+   relinquish operation is actually performed (another thread is setup for running)
+   this variable is incremented.  */
+
+ULONG           _tx_thread_performance_relinquish_count;
+
+
+/* Define the total number of thread timeouts. Each time a thread has a 
+   timeout this variable is incremented.  */
+
+ULONG           _tx_thread_performance_timeout_count;
+
+
+/* Define the total number of thread wait aborts. Each time a thread's suspension 
+   is lifted by the tx_thread_wait_abort call this variable is incremented.  */
+
+ULONG           _tx_thread_performance_wait_abort_count;
+
+
+/* Define the total number of idle system thread returns. Each time a thread returns to 
+   an idle system (no other thread is ready to run) this variable is incremented.  */
+
+ULONG           _tx_thread_performance_idle_return_count;
+
+
+/* Define the total number of non-idle system thread returns. Each time a thread returns to 
+   a non-idle system (another thread is ready to run) this variable is incremented.  */
+
+ULONG           _tx_thread_performance_non_idle_return_count;
+
+
+/* Define the last TX_THREAD_EXECUTE_LOG_SIZE threads scheduled in ThreadX. This 
+   is a circular list, where the index points to the oldest entry.  */
+
+ULONG           _tx_thread_performance__execute_log_index;
+TX_THREAD *     _tx_thread_performance_execute_log[TX_THREAD_EXECUTE_LOG_SIZE];
+#endif
+
+
+/* Define special string.  */
+
+#ifndef TX_MISRA_ENABLE
+const CHAR _tx_thread_special_string[] = 
+  "G-ML-EL-ML-BL-DL-BL-GB-GL-M-D-DL-GZ-KH-EL-CM-NH-HA-GF-DD-JC-YZ-CT-AT-DW-USA-CA-SD-SDSU";
+#endif
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_thread_initialize                               PORTABLE C      */
+/*                                                           6.1.9        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function initializes the various control data structures for   */
+/*    the thread control component.                                       */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_initialize_high_level         High level initialization         */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*  06-02-2021     Yuxin Zhou               Modified comment(s), added    */
+/*                                            Execution Profile support,  */
+/*                                            resulting in version 6.1.7  */
+/*  10-15-2021     Yuxin Zhou               Modified comment(s), improved */
+/*                                            stack check error handling, */
+/*                                            resulting in version 6.1.9  */   
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_thread_initialize(VOID)
+{
+
+    /* Note: the system stack pointer and the system state variables are 
+       initialized by the low and high-level initialization functions,
+       respectively.  */
+
+#ifndef TX_DISABLE_REDUNDANT_CLEARING
+
+    /* Set current thread pointer to NULL.  */
+    TX_THREAD_SET_CURRENT(TX_NULL)
+
+    /* Initialize the execute thread pointer to NULL.  */
+    _tx_thread_execute_ptr =  TX_NULL;
+
+    /* Initialize the priority information.  */
+    TX_MEMSET(&_tx_thread_priority_maps[0], 0, (sizeof(_tx_thread_priority_maps)));
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+    TX_MEMSET(&_tx_thread_preempted_maps[0], 0, (sizeof(_tx_thread_preempted_maps)));
+#endif
+#endif
+
+    /* Setup the highest priority variable to the max, indicating no thread is currently
+       ready.  */
+    _tx_thread_highest_priority =  ((UINT) TX_MAX_PRIORITIES);
+
+
+#ifndef TX_DISABLE_REDUNDANT_CLEARING
+
+    /* Initialize the array of priority head pointers.  */
+    TX_MEMSET(&_tx_thread_priority_list[0], 0, (sizeof(_tx_thread_priority_list)));
+
+    /* Initialize the head pointer of the created threads list and the
+       number of threads created.  */
+    _tx_thread_created_ptr =        TX_NULL;
+    _tx_thread_created_count =      TX_EMPTY;
+
+    /* Clear the global preempt disable variable.  */
+    _tx_thread_preempt_disable =    ((UINT) 0);
+
+    /* Initialize the thread mutex release function pointer.  */
+    _tx_thread_mutex_release =      TX_NULL;
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+    /* Clear application registered stack error handler.  */
+    _tx_thread_application_stack_error_handler =  TX_NULL;
+#endif
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+    /* Clear performance counters.  */
+    _tx_thread_performance_resume_count =                ((ULONG) 0);
+    _tx_thread_performance_suspend_count =               ((ULONG) 0);
+    _tx_thread_performance_solicited_preemption_count =  ((ULONG) 0);
+    _tx_thread_performance_interrupt_preemption_count =  ((ULONG) 0);
+    _tx_thread_performance_priority_inversion_count =    ((ULONG) 0);
+    _tx_thread_performance_time_slice_count =            ((ULONG) 0);
+    _tx_thread_performance_relinquish_count =            ((ULONG) 0);
+    _tx_thread_performance_timeout_count =               ((ULONG) 0);
+    _tx_thread_performance_wait_abort_count =            ((ULONG) 0);
+    _tx_thread_performance_idle_return_count =           ((ULONG) 0);
+    _tx_thread_performance_non_idle_return_count =       ((ULONG) 0);
+
+    /* Initialize the execute thread log.  */
+    TX_MEMSET(&_tx_thread_performance_execute_log[0], 0, (sizeof(_tx_thread_performance_execute_log)));
+#endif
+#endif
+
+    /* Setup the build options flag. This is used to identify how the ThreadX library was constructed.  */
+    _tx_build_options =  _tx_build_options 
+                            | (((ULONG) (TX_MAX_PRIORITIES/32)) << 24) 
+#ifdef TX_NOT_INTERRUPTABLE
+                            | (((ULONG) 1) << 31)
+#endif
+#ifdef TX_INLINE_THREAD_RESUME_SUSPEND
+                            | (((ULONG) 1) << 30)
+#endif
+#ifdef TX_TIMER_PROCESS_IN_ISR
+                            | (((ULONG) 1) << 23)
+#endif
+#ifdef TX_REACTIVATE_INLINE
+                            | (((ULONG) 1) << 22)
+#endif
+#ifdef TX_DISABLE_STACK_FILLING
+                            | (((ULONG) 1) << 21)
+#endif
+#ifdef TX_ENABLE_STACK_CHECKING
+                            | (((ULONG) 1) << 20)
+#endif
+#ifdef TX_DISABLE_PREEMPTION_THRESHOLD
+                            | (((ULONG) 1) << 19)
+#endif
+#ifdef TX_DISABLE_REDUNDANT_CLEARING
+                            | (((ULONG) 1) << 18)
+#endif
+#ifdef TX_DISABLE_NOTIFY_CALLBACKS
+                            | (((ULONG) 1) << 17)
+#endif
+#ifdef TX_BLOCK_POOL_ENABLE_PERFORMANCE_INFO
+                            | (((ULONG) 1) << 16)
+#endif
+#ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
+                            | (((ULONG) 1) << 15)
+#endif
+#ifdef TX_EVENT_FLAGS_ENABLE_PERFORMANCE_INFO
+                            | (((ULONG) 1) << 14)
+#endif
+#ifdef TX_MUTEX_ENABLE_PERFORMANCE_INFO
+                            | (((ULONG) 1) << 13)
+#endif
+#ifdef TX_QUEUE_ENABLE_PERFORMANCE_INFO
+                            | (((ULONG) 1) << 12)
+#endif
+#ifdef TX_SEMAPHORE_ENABLE_PERFORMANCE_INFO
+                            | (((ULONG) 1) << 11)
+#endif
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+                            | (((ULONG) 1) << 10)
+#endif
+#ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
+                            | (((ULONG) 1) << 9)
+#endif
+#ifdef TX_ENABLE_EVENT_TRACE
+                            | (((ULONG) 1) << 8)
+#endif
+#if defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)
+                            | (((ULONG) 1) << 7)
+#endif
+#if TX_PORT_SPECIFIC_BUILD_OPTIONS != 0
+                            | TX_PORT_SPECIFIC_BUILD_OPTIONS
+#endif
+                            ;
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_preemption_change.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_preemption_change.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_preemption_change.c	(revision 54)
@@ -0,0 +1,282 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Thread                                                              */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_trace.h"
+#include "tx_thread.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_thread_preemption_change                        PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function processes preemption-threshold change requests.  The  */
+/*    previous preemption is returned to the caller.  If the new request  */
+/*    allows a higher priority thread to execute, preemption takes place  */
+/*    inside of this function.                                            */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    thread_ptr                            Pointer to thread             */
+/*    new_threshold                         New preemption threshold      */
+/*    old_threshold                         Old preemption threshold      */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    status                                Service return status         */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_preempt_check       Check for preemption          */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _tx_thread_preemption_change(TX_THREAD *thread_ptr, UINT new_threshold, UINT *old_threshold)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+ULONG       priority_bit;
+#if TX_MAX_PRIORITIES > 32
+UINT        map_index;
+#endif
+#endif
+UINT        status;
+
+
+    /* Default status to success.  */
+    status =  TX_SUCCESS;
+
+#ifdef TX_DISABLE_PREEMPTION_THRESHOLD
+
+    /* Only allow 0 (disable all preemption) and returning preemption-threshold to the
+       current thread priority if preemption-threshold is disabled. All other threshold
+       values are converted to 0.  */
+    if (thread_ptr -> tx_thread_user_priority != new_threshold)
+    {
+
+        /* Is the new threshold zero?  */
+        if (new_threshold != ((UINT) 0))
+        {
+
+            /* Convert the new threshold to disable all preemption, since preemption-threshold is
+               not supported.  */
+            new_threshold =  ((UINT) 0);
+        }
+    }
+#endif
+
+    /* Lockout interrupts while the thread is being resumed.  */
+    TX_DISABLE
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_THREAD_PREEMPTION_CHANGE, thread_ptr, new_threshold, thread_ptr -> tx_thread_preempt_threshold, thread_ptr -> tx_thread_state, TX_TRACE_THREAD_EVENTS)
+
+    /* Log this kernel call.  */
+    TX_EL_THREAD_PREEMPTION_CHANGE_INSERT
+
+    /* Determine if the new threshold is greater than the current user priority.  */
+    if (new_threshold > thread_ptr -> tx_thread_user_priority)
+    {
+
+        /* Return error.  */
+        status =  TX_THRESH_ERROR;
+    }
+    else
+    {
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+
+        /* Determine if the new threshold is the same as the priority.  */
+        if (thread_ptr -> tx_thread_user_priority == new_threshold)
+        {
+
+            /* Determine if this thread is at the head of the list.  */
+            if (_tx_thread_priority_list[thread_ptr -> tx_thread_priority] == thread_ptr)
+            {
+
+#if TX_MAX_PRIORITIES > 32
+
+                /* Calculate the index into the bit map array.  */
+                map_index =  (thread_ptr -> tx_thread_priority)/((UINT) 32);
+#endif
+
+                /* Yes, this thread is at the front of the list.  Make sure
+                   the preempted bit is cleared for this thread.  */
+                TX_MOD32_BIT_SET(thread_ptr -> tx_thread_priority, priority_bit)
+                _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
+
+#if TX_MAX_PRIORITIES > 32
+
+                /* Determine if there are any other bits set in this preempt map.  */
+                if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
+                {
+
+                    /* No, clear the active bit to signify this preempt map has nothing set.  */
+                    TX_DIV32_BIT_SET(thread_ptr -> tx_thread_priority, priority_bit)
+                    _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
+                }
+#endif
+            }
+        }
+#endif
+
+        /* Return the user's preemption-threshold.   */
+        *old_threshold =  thread_ptr -> tx_thread_user_preempt_threshold;
+
+        /* Setup the new threshold.  */
+        thread_ptr -> tx_thread_user_preempt_threshold =  new_threshold;
+
+        /* Determine if the new threshold represents a higher priority than the priority inheritance threshold.  */
+        if (new_threshold < thread_ptr -> tx_thread_inherit_priority)
+        {
+
+            /* Update the actual preemption-threshold with the new threshold.  */
+            thread_ptr -> tx_thread_preempt_threshold =  new_threshold;
+        }
+        else
+        {
+
+            /* Update the actual preemption-threshold with the priority inheritance.  */
+            thread_ptr -> tx_thread_preempt_threshold =  thread_ptr -> tx_thread_inherit_priority;
+        }
+
+        /* Is the thread priority less than the current highest priority?  If not, no preemption is required.  */
+        if (_tx_thread_highest_priority < thread_ptr -> tx_thread_priority)
+        {
+
+            /* Is the new thread preemption-threshold less than the current highest priority?  If not, no preemption is required.  */
+            if (_tx_thread_highest_priority < new_threshold)
+            {
+
+                /* If the current execute pointer is the same at this thread, preemption needs to take place.  */
+                if (_tx_thread_execute_ptr == thread_ptr)
+                {
+
+                    /* Preemption needs to take place.  */
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+
+                    /* Determine if this thread has preemption threshold set.  */
+                    if (thread_ptr -> tx_thread_preempt_threshold != thread_ptr -> tx_thread_priority)
+                    {
+
+#if TX_MAX_PRIORITIES > 32
+
+                        /* Calculate the index into the bit map array.  */
+                        map_index =  (thread_ptr -> tx_thread_priority)/((UINT) 32);
+
+                        /* Set the active bit to remember that the preempt map has something set.  */
+                        TX_DIV32_BIT_SET(thread_ptr -> tx_thread_priority, priority_bit)
+                        _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active | priority_bit;
+#endif
+
+                        /* Remember that this thread was preempted by a thread above the thread's threshold.  */
+                        TX_MOD32_BIT_SET(thread_ptr -> tx_thread_priority, priority_bit)
+                        _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] | priority_bit;
+                    }
+#endif
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                    /* Determine if the caller is an interrupt or from a thread.  */
+                    if (TX_THREAD_GET_SYSTEM_STATE() == ((ULONG) 0))
+                    {
+
+                        /* Caller is a thread, so this is a solicited preemption.  */
+                        _tx_thread_performance_solicited_preemption_count++;
+
+                        /* Increment the thread's solicited preemption counter.  */
+                        thread_ptr -> tx_thread_performance_solicited_preemption_count++;
+                    }
+
+                    /* Remember the thread that preempted this thread.  */
+                    thread_ptr -> tx_thread_performance_last_preempting_thread =  _tx_thread_priority_list[_tx_thread_highest_priority];
+
+                    /* Is the execute pointer different?  */
+                    if (_tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] != _tx_thread_execute_ptr)
+                    {
+
+                        /* Move to next entry.  */
+                        _tx_thread_performance__execute_log_index++;
+
+                        /* Check for wrap condition.  */
+                        if (_tx_thread_performance__execute_log_index >= TX_THREAD_EXECUTE_LOG_SIZE)
+                        {
+
+                            /* Set the index to the beginning.  */
+                            _tx_thread_performance__execute_log_index =  ((UINT) 0);
+                        }
+
+                        /* Log the new execute pointer.  */
+                        _tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] =  _tx_thread_execute_ptr;
+                    }
+#endif
+
+                    /* Setup the highest priority thread to execute.  */
+                    _tx_thread_execute_ptr =  _tx_thread_priority_list[_tx_thread_highest_priority];
+
+                    /* Restore interrupts.  */
+                    TX_RESTORE
+
+                    /* Check for preemption.  */
+                    _tx_thread_system_preempt_check();
+
+                    /* Disable interrupts.  */
+                    TX_DISABLE
+                }
+            }
+        }
+    }
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Return completion status.  */
+    return(status);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_shell_entry.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_shell_entry.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_shell_entry.c	(revision 54)
@@ -0,0 +1,203 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Thread                                                              */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_thread_shell_entry                              PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function calls the specified entry function of the thread.  It */
+/*    also provides a place for the thread's entry function to return.    */
+/*    If the thread returns, this function places the thread in a         */
+/*    "COMPLETED" state.                                                  */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    thread_entry                      Thread's entry function           */
+/*    _tx_thread_system_suspend         Thread suspension routine         */
+/*    _tx_thread_system_ni_suspend      Non-interruptable suspend thread  */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Initial thread stack frame                                          */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_thread_shell_entry(VOID)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+TX_THREAD       *thread_ptr;
+#ifndef TX_DISABLE_NOTIFY_CALLBACKS
+VOID            (*entry_exit_notify)(TX_THREAD *notify_thread_ptr, UINT type);
+#endif
+
+
+    /* Pickup thread pointer.  */
+    TX_THREAD_GET_CURRENT(thread_ptr)
+
+    /* Perform any additional activities for tool or user purpose.  */
+    TX_THREAD_STARTED_EXTENSION(thread_ptr)
+
+#ifndef TX_DISABLE_NOTIFY_CALLBACKS
+
+    /* Disable interrupts.  */
+    TX_DISABLE
+
+    /* Pickup the entry/exit application callback routine.  */
+    entry_exit_notify =  thread_ptr -> tx_thread_entry_exit_notify;
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Determine if an application callback routine is specified.  */
+    if (entry_exit_notify != TX_NULL)
+    {
+
+        /* Yes, notify application that this thread has been entered!  */
+        (entry_exit_notify)(thread_ptr, TX_THREAD_ENTRY);
+    }
+#endif
+
+    /* Call current thread's entry function.  */
+    (thread_ptr -> tx_thread_entry) (thread_ptr -> tx_thread_entry_parameter);
+
+    /* Suspend thread with a "completed" state.  */
+
+    /* Determine if the application is using mutexes.  */
+    if (_tx_thread_mutex_release != TX_NULL)
+    {
+
+        /* Yes, call the mutex release function via a function pointer that
+           is setup during mutex initialization.  */
+        (_tx_thread_mutex_release)(thread_ptr);
+    }
+
+    /* Lockout interrupts while the thread state is setup.  */
+    TX_DISABLE
+
+#ifndef TX_DISABLE_NOTIFY_CALLBACKS
+
+    /* Pickup the entry/exit application callback routine again.  */
+    entry_exit_notify =  thread_ptr -> tx_thread_entry_exit_notify;
+#endif
+
+    /* Set the status to suspending, in order to indicate the suspension
+       is in progress.  */
+    thread_ptr -> tx_thread_state =  TX_COMPLETED;
+
+    /* Thread state change.  */
+    TX_THREAD_STATE_CHANGE(thread_ptr, TX_COMPLETED)
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+#ifndef TX_DISABLE_NOTIFY_CALLBACKS
+
+    /* Determine if an application callback routine is specified.  */
+    if (entry_exit_notify != TX_NULL)
+    {
+
+        /* Yes, notify application that this thread has exited!  */
+        (entry_exit_notify)(thread_ptr, TX_THREAD_EXIT);
+    }
+#endif
+
+    /* Perform any additional activities for tool or user purpose.  */
+    TX_THREAD_COMPLETED_EXTENSION(thread_ptr)
+
+    /* Call actual non-interruptable thread suspension routine.  */
+    _tx_thread_system_ni_suspend(thread_ptr, ((ULONG) 0));
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+#else
+
+    /* Set the suspending flag. */
+    thread_ptr -> tx_thread_suspending =  TX_TRUE;
+
+    /* Setup for no timeout period.  */
+    thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  ((ULONG) 0);
+
+    /* Temporarily disable preemption.  */
+    _tx_thread_preempt_disable++;
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Perform any additional activities for tool or user purpose.  */
+    TX_THREAD_COMPLETED_EXTENSION(thread_ptr)
+
+#ifndef TX_DISABLE_NOTIFY_CALLBACKS
+
+    /* Determine if an application callback routine is specified.  */
+    if (entry_exit_notify != TX_NULL)
+    {
+
+        /* Yes, notify application that this thread has exited!  */
+        (entry_exit_notify)(thread_ptr, TX_THREAD_EXIT);
+    }
+#endif
+
+    /* Call actual thread suspension routine.  */
+    _tx_thread_system_suspend(thread_ptr);
+#endif
+
+
+#ifdef TX_SAFETY_CRITICAL
+
+    /* If we ever get here, raise safety critical exception.  */
+    TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, 0);
+#endif
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_system_preempt_check.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_system_preempt_check.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_system_preempt_check.c	(revision 54)
@@ -0,0 +1,129 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Thread                                                              */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_thread_system_preempt_check                     PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function checks for preemption that could have occurred as a   */
+/*    result scheduling activities occurring while the preempt disable    */
+/*    flag was set.                                                       */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_return              Return to the system          */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Other ThreadX Components                                            */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_thread_system_preempt_check(VOID)
+{
+
+ULONG           combined_flags;
+TX_THREAD       *current_thread;
+TX_THREAD       *thread_ptr;
+
+
+    /* Combine the system state and preempt disable flags into one for comparison.  */
+    TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
+
+    /* Determine if we are in a system state (ISR or Initialization) or internal preemption is disabled.  */
+    if (combined_flags == ((ULONG) 0))
+    {
+
+        /* No, at thread execution level so continue checking for preemption.  */
+
+        /* Pickup thread pointer.  */
+        TX_THREAD_GET_CURRENT(current_thread)
+
+        /* Pickup the next execute pointer.  */
+        thread_ptr =  _tx_thread_execute_ptr;
+
+        /* Determine if preemption should take place.  */
+        if (current_thread != thread_ptr)
+        {
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+            /* Check this thread's stack.  */
+            TX_THREAD_STACK_CHECK(thread_ptr)
+#endif
+
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+            /* Determine if an idle system return is present.  */
+            if (thread_ptr == TX_NULL)
+            {
+
+                /* Yes, increment the return to idle return count.  */
+                _tx_thread_performance_idle_return_count++;
+            }
+            else
+            {
+
+                /* No, there is another thread ready to run and will be scheduled upon return.  */
+                _tx_thread_performance_non_idle_return_count++;
+            }
+#endif
+
+            /* Return to the system so the higher priority thread can be scheduled.  */
+            _tx_thread_system_return();
+        }
+    }
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_system_resume.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_system_resume.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_system_resume.c	(revision 54)
@@ -0,0 +1,1004 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Thread                                                              */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+/* Include necessary system files.  */
+#include "tx_api.h"
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+#include "tx_initialize.h"
+#endif
+#include "tx_trace.h"
+#include "tx_timer.h"
+#include "tx_thread.h"
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_thread_system_resume                            PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function places the specified thread on the list of ready      */
+/*    threads at the thread's specific priority.                          */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    thread_ptr                            Pointer to thread to resume   */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_return              Return to the system          */
+/*    _tx_thread_system_ni_resume           Noninterruptable thread resume*/
+/*    _tx_timer_system_deactivate           Timer deactivate              */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_thread_create                     Thread create function        */
+/*    _tx_thread_priority_change            Thread priority change        */
+/*    _tx_thread_resume                     Application resume service    */
+/*    _tx_thread_timeout                    Thread timeout                */
+/*    _tx_thread_wait_abort                 Thread wait abort             */
+/*    Other ThreadX Components                                            */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_thread_system_resume(TX_THREAD *thread_ptr)
+#ifndef TX_NOT_INTERRUPTABLE
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+UINT            priority;
+ULONG           priority_bit;
+TX_THREAD       *head_ptr;
+TX_THREAD       *tail_ptr;
+TX_THREAD       *execute_ptr;
+TX_THREAD       *current_thread;
+ULONG           combined_flags;
+
+#ifdef TX_ENABLE_EVENT_TRACE
+TX_TRACE_BUFFER_ENTRY       *entry_ptr;
+ULONG                       time_stamp =  ((ULONG) 0);
+#endif
+
+#if TX_MAX_PRIORITIES > 32
+UINT            map_index;
+#endif
+
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+    /* Check this thread's stack.  */
+    TX_THREAD_STACK_CHECK(thread_ptr)
+#endif
+
+    /* Lockout interrupts while the thread is being resumed.  */
+    TX_DISABLE
+
+#ifndef TX_NO_TIMER
+
+    /* Deactivate the timeout timer if necessary.  */
+    if (thread_ptr -> tx_thread_timer.tx_timer_internal_list_head != TX_NULL)
+    {
+
+        /* Deactivate the thread's timeout timer.  */
+        _tx_timer_system_deactivate(&(thread_ptr -> tx_thread_timer));
+    }
+    else
+    {
+
+        /* Clear the remaining time to ensure timer doesn't get activated.  */
+        thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  ((ULONG) 0);
+    }
+#endif
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+    /* If trace is enabled, save the current event pointer.  */
+    entry_ptr =  _tx_trace_buffer_current_ptr;
+#endif
+
+    /* Log the thread status change.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_THREAD_RESUME, thread_ptr, thread_ptr -> tx_thread_state, TX_POINTER_TO_ULONG_CONVERT(&execute_ptr), TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr), TX_TRACE_INTERNAL_EVENTS)
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+    /* Save the time stamp for later comparison to verify that
+       the event hasn't been overwritten by the time we have
+       computed the next thread to execute.  */
+    if (entry_ptr != TX_NULL)
+    {
+
+        /* Save time stamp.  */
+        time_stamp =  entry_ptr -> tx_trace_buffer_entry_time_stamp;
+    }
+#endif
+
+    /* Decrease the preempt disabled count.  */
+    _tx_thread_preempt_disable--;
+
+    /* Determine if the thread is in the process of suspending.  If so, the thread
+       control block is already on the linked list so nothing needs to be done.  */
+    if (thread_ptr -> tx_thread_suspending == TX_FALSE)
+    {
+
+        /* Thread is not in the process of suspending. Now check to make sure the thread
+           has not already been resumed.  */
+        if (thread_ptr -> tx_thread_state != TX_READY)
+        {
+
+            /* No, now check to see if the delayed suspension flag is set.  */
+            if (thread_ptr -> tx_thread_delayed_suspend == TX_FALSE)
+            {
+
+                /* Resume the thread!  */
+
+                /* Make this thread ready.  */
+
+                /* Change the state to ready.  */
+                thread_ptr -> tx_thread_state =  TX_READY;
+
+                /* Pickup priority of thread.  */
+                priority =  thread_ptr -> tx_thread_priority;
+
+                /* Thread state change.  */
+                TX_THREAD_STATE_CHANGE(thread_ptr, TX_READY)
+
+                /* Log the thread status change.  */
+                TX_EL_THREAD_STATUS_CHANGE_INSERT(thread_ptr, TX_READY)
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                /* Increment the total number of thread resumptions.  */
+                _tx_thread_performance_resume_count++;
+
+                /* Increment this thread's resume count.  */
+                thread_ptr -> tx_thread_performance_resume_count++;
+#endif
+
+                /* Determine if there are other threads at this priority that are
+                   ready.  */
+                head_ptr =  _tx_thread_priority_list[priority];
+                if (head_ptr == TX_NULL)
+                {
+
+                    /* First thread at this priority ready.  Add to the front of the list.  */
+                    _tx_thread_priority_list[priority] =       thread_ptr;
+                    thread_ptr -> tx_thread_ready_next =       thread_ptr;
+                    thread_ptr -> tx_thread_ready_previous =   thread_ptr;
+
+#if TX_MAX_PRIORITIES > 32
+
+                    /* Calculate the index into the bit map array.  */
+                    map_index =  priority/((UINT) 32);
+
+                    /* Set the active bit to remember that the priority map has something set.  */
+                    TX_DIV32_BIT_SET(priority, priority_bit)
+                    _tx_thread_priority_map_active =  _tx_thread_priority_map_active | priority_bit;
+#endif
+
+                    /* Or in the thread's priority bit.  */
+                    TX_MOD32_BIT_SET(priority, priority_bit)
+                    _tx_thread_priority_maps[MAP_INDEX] =  _tx_thread_priority_maps[MAP_INDEX] | priority_bit;
+
+                    /* Determine if this newly ready thread is the highest priority.  */
+                    if (priority < _tx_thread_highest_priority)
+                    {
+
+                        /* A new highest priority thread is present. */
+
+                        /* Update the highest priority variable.  */
+                        _tx_thread_highest_priority =  priority;
+
+                        /* Pickup the execute pointer. Since it is going to be referenced multiple
+                           times, it is placed in a local variable.  */
+                        execute_ptr =  _tx_thread_execute_ptr;
+
+                        /* Determine if no thread is currently executing.  */
+                        if (execute_ptr == TX_NULL)
+                        {
+
+                            /* Simply setup the execute pointer.  */
+                            _tx_thread_execute_ptr =  thread_ptr;
+                        }
+                        else
+                        {
+
+                            /* Another thread has been scheduled for execution.  */
+
+                            /* Check to see if this is a higher priority thread and determine if preemption is allowed.  */
+                            if (priority < execute_ptr -> tx_thread_preempt_threshold)
+                            {
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+
+                                /* Determine if the preempted thread had preemption-threshold set.  */
+                                if (execute_ptr -> tx_thread_preempt_threshold != execute_ptr -> tx_thread_priority)
+                                {
+
+#if TX_MAX_PRIORITIES > 32
+
+                                    /* Calculate the index into the bit map array.  */
+                                    map_index =  (execute_ptr -> tx_thread_priority)/((UINT) 32);
+
+                                    /* Set the active bit to remember that the preempt map has something set.  */
+                                    TX_DIV32_BIT_SET(execute_ptr -> tx_thread_priority, priority_bit)
+                                    _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active | priority_bit;
+#endif
+
+                                    /* Remember that this thread was preempted by a thread above the thread's threshold.  */
+                                    TX_MOD32_BIT_SET(execute_ptr -> tx_thread_priority, priority_bit)
+                                    _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] | priority_bit;
+                                }
+#endif
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                                /* Determine if the caller is an interrupt or from a thread.  */
+                                if (TX_THREAD_GET_SYSTEM_STATE() == ((ULONG) 0))
+                                {
+
+                                    /* Caller is a thread, so this is a solicited preemption.  */
+                                    _tx_thread_performance_solicited_preemption_count++;
+
+                                    /* Increment the thread's solicited preemption counter.  */
+                                    execute_ptr -> tx_thread_performance_solicited_preemption_count++;
+                                }
+                                else
+                                {
+
+                                    if (TX_THREAD_GET_SYSTEM_STATE() < TX_INITIALIZE_IN_PROGRESS)
+                                    {
+
+                                        /* Caller is an interrupt, so this is an interrupt preemption.  */
+                                        _tx_thread_performance_interrupt_preemption_count++;
+
+                                        /* Increment the thread's interrupt preemption counter.  */
+                                        execute_ptr -> tx_thread_performance_interrupt_preemption_count++;
+                                    }
+                                }
+
+                                /* Remember the thread that preempted this thread.  */
+                                execute_ptr -> tx_thread_performance_last_preempting_thread =  thread_ptr;
+
+#endif
+
+                                /* Yes, modify the execute thread pointer.  */
+                                _tx_thread_execute_ptr =  thread_ptr;
+
+#ifndef TX_MISRA_ENABLE
+
+                                /* If MISRA is not-enabled, insert a preemption and return in-line for performance.  */
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                                /* Is the execute pointer different?  */
+                                if (_tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] != _tx_thread_execute_ptr)
+                                {
+
+                                    /* Move to next entry.  */
+                                    _tx_thread_performance__execute_log_index++;
+
+                                    /* Check for wrap condition.  */
+                                    if (_tx_thread_performance__execute_log_index >= TX_THREAD_EXECUTE_LOG_SIZE)
+                                    {
+
+                                        /* Set the index to the beginning.  */
+                                        _tx_thread_performance__execute_log_index =  ((UINT) 0);
+                                    }
+
+                                    /* Log the new execute pointer.  */
+                                    _tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] =  _tx_thread_execute_ptr;
+                                }
+#endif
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+                                /* Check that the event time stamp is unchanged.  A different
+                                   timestamp means that a later event wrote over the thread
+                                   resume event. In that case, do nothing here.  */
+                                if (entry_ptr != TX_NULL)
+                                {
+
+                                    /* Is the timestamp the same?  */
+                                    if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
+                                    {
+
+                                        /* Timestamp is the same, set the "next thread pointer" to NULL. This can
+                                           be used by the trace analysis tool to show idle system conditions.  */
+                                        entry_ptr -> tx_trace_buffer_entry_information_field_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
+                                    }
+                                }
+#endif
+
+                                /* Restore interrupts.  */
+                                TX_RESTORE
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+                                /* Pickup the next execute pointer.  */
+                                thread_ptr =  _tx_thread_execute_ptr;
+
+                                /* Check this thread's stack.  */
+                                TX_THREAD_STACK_CHECK(thread_ptr)
+#endif
+
+                                /* Now determine if preemption should take place. This is only possible if the current thread pointer is
+                                   not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
+                                TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
+                                if (combined_flags == ((ULONG) 0))
+                                {
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                                    /* There is another thread ready to run and will be scheduled upon return.  */
+                                    _tx_thread_performance_non_idle_return_count++;
+#endif
+
+                                    /* Preemption is needed - return to the system!  */
+                                    _tx_thread_system_return();
+                                }
+
+                                /* Return in-line when MISRA is not enabled.  */
+                                return;
+#endif
+                            }
+                        }
+                    }
+                }
+                else
+                {
+
+                    /* No, there are other threads at this priority already ready.  */
+
+                    /* Just add this thread to the priority list.  */
+                    tail_ptr =                                 head_ptr -> tx_thread_ready_previous;
+                    tail_ptr -> tx_thread_ready_next =         thread_ptr;
+                    head_ptr -> tx_thread_ready_previous =     thread_ptr;
+                    thread_ptr -> tx_thread_ready_previous =   tail_ptr;
+                    thread_ptr -> tx_thread_ready_next =       head_ptr;
+                }
+            }
+
+            /* Else, delayed suspend flag was set.  */
+            else
+            {
+
+                /* Clear the delayed suspend flag and change the state.  */
+                thread_ptr -> tx_thread_delayed_suspend =  TX_FALSE;
+                thread_ptr -> tx_thread_state =            TX_SUSPENDED;
+            }
+        }
+    }
+    else
+    {
+
+        /* A resumption occurred in the middle of a previous thread suspension.  */
+
+        /* Make sure the type of suspension under way is not a terminate or
+           thread completion.  In either of these cases, do not void the
+           interrupted suspension processing.  */
+        if (thread_ptr -> tx_thread_state != TX_COMPLETED)
+        {
+
+            /* Make sure the thread isn't terminated.  */
+            if (thread_ptr -> tx_thread_state != TX_TERMINATED)
+            {
+
+                /* No, now check to see if the delayed suspension flag is set.  */
+                if (thread_ptr -> tx_thread_delayed_suspend == TX_FALSE)
+                {
+
+                    /* Clear the suspending flag.  */
+                    thread_ptr -> tx_thread_suspending =   TX_FALSE;
+
+                    /* Restore the state to ready.  */
+                    thread_ptr -> tx_thread_state =        TX_READY;
+
+                    /* Thread state change.  */
+                    TX_THREAD_STATE_CHANGE(thread_ptr, TX_READY)
+
+                    /* Log the thread status change.  */
+                    TX_EL_THREAD_STATUS_CHANGE_INSERT(thread_ptr, TX_READY)
+                }
+                else
+                {
+
+                    /* Clear the delayed suspend flag and change the state.  */
+                    thread_ptr -> tx_thread_delayed_suspend =  TX_FALSE;
+                    thread_ptr -> tx_thread_state =            TX_SUSPENDED;
+                }
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                /* Increment the total number of thread resumptions.  */
+                _tx_thread_performance_resume_count++;
+
+                /* Increment this thread's resume count.  */
+                thread_ptr -> tx_thread_performance_resume_count++;
+#endif
+            }
+        }
+    }
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+    /* Is the execute pointer different?  */
+    if (_tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] != _tx_thread_execute_ptr)
+    {
+
+        /* Move to next entry.  */
+        _tx_thread_performance__execute_log_index++;
+
+        /* Check for wrap condition.  */
+        if (_tx_thread_performance__execute_log_index >= TX_THREAD_EXECUTE_LOG_SIZE)
+        {
+
+            /* Set the index to the beginning.  */
+            _tx_thread_performance__execute_log_index =  ((UINT) 0);
+        }
+
+        /* Log the new execute pointer.  */
+        _tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] =  _tx_thread_execute_ptr;
+    }
+#endif
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+    /* Check that the event time stamp is unchanged.  A different
+       timestamp means that a later event wrote over the thread
+       resume event. In that case, do nothing here.  */
+    if (entry_ptr != TX_NULL)
+    {
+
+        /* Is the timestamp the same?  */
+        if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
+        {
+
+            /* Timestamp is the same, set the "next thread pointer" to NULL. This can
+               be used by the trace analysis tool to show idle system conditions.  */
+#ifdef TX_MISRA_ENABLE
+            entry_ptr -> tx_trace_buffer_entry_info_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
+#else
+            entry_ptr -> tx_trace_buffer_entry_information_field_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
+#endif
+        }
+    }
+#endif
+
+    /* Pickup thread pointer.  */
+    TX_THREAD_GET_CURRENT(current_thread)
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Determine if a preemption condition is present.  */
+    if (current_thread != _tx_thread_execute_ptr)
+    {
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+        /* Pickup the next execute pointer.  */
+        thread_ptr =  _tx_thread_execute_ptr;
+
+        /* Check this thread's stack.  */
+        TX_THREAD_STACK_CHECK(thread_ptr)
+#endif
+
+        /* Now determine if preemption should take place. This is only possible if the current thread pointer is
+           not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
+        TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
+        if (combined_flags == ((ULONG) 0))
+        {
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+            /* There is another thread ready to run and will be scheduled upon return.  */
+            _tx_thread_performance_non_idle_return_count++;
+#endif
+
+            /* Preemption is needed - return to the system!  */
+            _tx_thread_system_return();
+        }
+    }
+}
+#else
+{
+
+TX_INTERRUPT_SAVE_AREA
+#ifdef TX_ENABLE_EVENT_TRACE
+UINT            temp_state;
+#endif
+UINT            state;
+
+
+    /* Lockout interrupts while the thread is being resumed.  */
+    TX_DISABLE
+
+    /* Decrease the preempt disabled count.  */
+    _tx_thread_preempt_disable--;
+
+    /* Determine if the thread is in the process of suspending.  If so, the thread
+       control block is already on the linked list so nothing needs to be done.  */
+    if (thread_ptr -> tx_thread_suspending == TX_FALSE)
+    {
+
+        /* Call the non-interruptable thread system resume function.  */
+        _tx_thread_system_ni_resume(thread_ptr);
+    }
+    else
+    {
+
+        /* A resumption occurred in the middle of a previous thread suspension.  */
+
+        /* Pickup the current thread state.  */
+        state =  thread_ptr -> tx_thread_state;
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+        /* Move the state into a different variable for MISRA compliance.  */
+        temp_state =  state;
+#endif
+
+        /* Log the thread status change.  */
+        TX_TRACE_IN_LINE_INSERT(TX_TRACE_THREAD_RESUME, thread_ptr, ((ULONG) state), TX_POINTER_TO_ULONG_CONVERT(&temp_state), TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr), TX_TRACE_INTERNAL_EVENTS)
+
+        /* Make sure the type of suspension under way is not a terminate or
+           thread completion.  In either of these cases, do not void the
+           interrupted suspension processing.  */
+        if (state != TX_COMPLETED)
+        {
+
+            /* Check for terminated thread.  */
+            if (state != TX_TERMINATED)
+            {
+
+                /* Clear the suspending flag.  */
+                thread_ptr -> tx_thread_suspending =   TX_FALSE;
+
+                /* Restore the state to ready.  */
+                thread_ptr -> tx_thread_state =        TX_READY;
+
+                /* Thread state change.  */
+                TX_THREAD_STATE_CHANGE(thread_ptr, TX_READY)
+
+                /* Log the thread status change.  */
+                TX_EL_THREAD_STATUS_CHANGE_INSERT(thread_ptr, TX_READY)
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                /* Increment the total number of thread resumptions.  */
+                _tx_thread_performance_resume_count++;
+
+                /* Increment this thread's resume count.  */
+                thread_ptr -> tx_thread_performance_resume_count++;
+#endif
+            }
+        }
+    }
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+}
+
+/* Define the non-interruptable version of thread resume. It is assumed at this point that
+   all interrupts are disabled and will remain so during this function.  */
+
+VOID  _tx_thread_system_ni_resume(TX_THREAD *thread_ptr)
+{
+
+UINT            priority;
+ULONG           priority_bit;
+TX_THREAD       *head_ptr;
+TX_THREAD       *tail_ptr;
+TX_THREAD       *execute_ptr;
+TX_THREAD       *current_thread;
+ULONG           combined_flags;
+
+#ifdef TX_ENABLE_EVENT_TRACE
+TX_TRACE_BUFFER_ENTRY       *entry_ptr;
+ULONG                       time_stamp =  ((ULONG) 0);
+#endif
+
+#if TX_MAX_PRIORITIES > 32
+UINT            map_index;
+#endif
+
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+    /* If trace is enabled, save the current event pointer.  */
+    entry_ptr =  _tx_trace_buffer_current_ptr;
+#endif
+
+    /* Log the thread status change.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_THREAD_RESUME, thread_ptr, ((ULONG) thread_ptr -> tx_thread_state), TX_POINTER_TO_ULONG_CONVERT(&execute_ptr), TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr), TX_TRACE_INTERNAL_EVENTS)
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+    /* Save the time stamp for later comparison to verify that
+       the event hasn't been overwritten by the time we have
+       computed the next thread to execute.  */
+    if (entry_ptr != TX_NULL)
+    {
+
+        /* Save time stamp.  */
+        time_stamp =  entry_ptr -> tx_trace_buffer_entry_time_stamp;
+    }
+#endif
+
+
+#ifndef TX_NO_TIMER
+
+    /* Deactivate the timeout timer if necessary.  */
+    if (thread_ptr -> tx_thread_timer.tx_timer_internal_list_head != TX_NULL)
+    {
+
+        /* Deactivate the thread's timeout timer.  */
+        _tx_timer_system_deactivate(&(thread_ptr -> tx_thread_timer));
+    }
+#endif
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+    /* Check this thread's stack.  */
+    TX_THREAD_STACK_CHECK(thread_ptr)
+#endif
+
+    /* Thread is not in the process of suspending. Now check to make sure the thread
+       has not already been resumed.  */
+    if (thread_ptr -> tx_thread_state != TX_READY)
+    {
+
+        /* No, now check to see if the delayed suspension flag is set.  */
+        if (thread_ptr -> tx_thread_delayed_suspend == TX_FALSE)
+        {
+
+            /* Resume the thread!  */
+
+            /* Make this thread ready.  */
+
+            /* Change the state to ready.  */
+            thread_ptr -> tx_thread_state =  TX_READY;
+
+            /* Thread state change.  */
+            TX_THREAD_STATE_CHANGE(thread_ptr, TX_READY)
+
+            /* Log the thread status change.  */
+            TX_EL_THREAD_STATUS_CHANGE_INSERT(thread_ptr, TX_READY)
+
+            /* Pickup priority of thread.  */
+            priority =  thread_ptr -> tx_thread_priority;
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+            /* Increment the total number of thread resumptions.  */
+            _tx_thread_performance_resume_count++;
+
+            /* Increment this thread's resume count.  */
+            thread_ptr -> tx_thread_performance_resume_count++;
+#endif
+
+            /* Determine if there are other threads at this priority that are
+               ready.  */
+            head_ptr =  _tx_thread_priority_list[priority];
+            if (head_ptr == TX_NULL)
+            {
+
+                /* First thread at this priority ready.  Add to the front of the list.  */
+                _tx_thread_priority_list[priority] =       thread_ptr;
+                thread_ptr -> tx_thread_ready_next =       thread_ptr;
+                thread_ptr -> tx_thread_ready_previous =   thread_ptr;
+
+#if TX_MAX_PRIORITIES > 32
+
+                /* Calculate the index into the bit map array.  */
+                map_index =  priority/((UINT) 32);
+
+                /* Set the active bit to remember that the priority map has something set.  */
+                TX_DIV32_BIT_SET(priority, priority_bit)
+                _tx_thread_priority_map_active =  _tx_thread_priority_map_active | priority_bit;
+#endif
+
+                /* Or in the thread's priority bit.  */
+                TX_MOD32_BIT_SET(priority, priority_bit)
+                _tx_thread_priority_maps[MAP_INDEX] =  _tx_thread_priority_maps[MAP_INDEX] | priority_bit;
+
+                /* Determine if this newly ready thread is the highest priority.  */
+                if (priority < _tx_thread_highest_priority)
+                {
+
+                    /* A new highest priority thread is present. */
+
+                    /* Update the highest priority variable.  */
+                    _tx_thread_highest_priority =  priority;
+
+                    /* Pickup the execute pointer. Since it is going to be referenced multiple
+                       times, it is placed in a local variable.  */
+                    execute_ptr =  _tx_thread_execute_ptr;
+
+                    /* Determine if no thread is currently executing.  */
+                    if (execute_ptr == TX_NULL)
+                    {
+
+                        /* Simply setup the execute pointer.  */
+                        _tx_thread_execute_ptr =  thread_ptr;
+                    }
+                    else
+                    {
+
+                        /* Check to see if this is a higher priority thread and determine if preemption is allowed.  */
+                        if (priority < execute_ptr -> tx_thread_preempt_threshold)
+                        {
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+
+                            /* Determine if the preempted thread had preemption-threshold set.  */
+                            if (execute_ptr -> tx_thread_preempt_threshold != execute_ptr -> tx_thread_priority)
+                            {
+
+#if TX_MAX_PRIORITIES > 32
+
+                                /* Calculate the index into the bit map array.  */
+                                map_index =  (execute_ptr -> tx_thread_priority)/((UINT) 32);
+
+                                /* Set the active bit to remember that the preempt map has something set.  */
+                                TX_DIV32_BIT_SET(execute_ptr -> tx_thread_priority, priority_bit)
+                                _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active | priority_bit;
+#endif
+
+                                /* Remember that this thread was preempted by a thread above the thread's threshold.  */
+                                TX_MOD32_BIT_SET(execute_ptr -> tx_thread_priority, priority_bit)
+                                _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] | priority_bit;
+                            }
+#endif
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                            /* Determine if the caller is an interrupt or from a thread.  */
+                            if (TX_THREAD_GET_SYSTEM_STATE() == ((ULONG) 0))
+                            {
+
+                                /* Caller is a thread, so this is a solicited preemption.  */
+                                _tx_thread_performance_solicited_preemption_count++;
+
+                                /* Increment the thread's solicited preemption counter.  */
+                                execute_ptr -> tx_thread_performance_solicited_preemption_count++;
+                            }
+                            else
+                            {
+
+                                if (TX_THREAD_GET_SYSTEM_STATE() < TX_INITIALIZE_IN_PROGRESS)
+                                {
+
+                                    /* Caller is an interrupt, so this is an interrupt preemption.  */
+                                    _tx_thread_performance_interrupt_preemption_count++;
+
+                                    /* Increment the thread's interrupt preemption counter.  */
+                                    execute_ptr -> tx_thread_performance_interrupt_preemption_count++;
+                                }
+                            }
+
+                            /* Remember the thread that preempted this thread.  */
+                            execute_ptr -> tx_thread_performance_last_preempting_thread =  thread_ptr;
+#endif
+
+                            /* Yes, modify the execute thread pointer.  */
+                            _tx_thread_execute_ptr =  thread_ptr;
+
+#ifndef TX_MISRA_ENABLE
+
+                            /* If MISRA is not-enabled, insert a preemption and return in-line for performance.  */
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                            /* Is the execute pointer different?  */
+                            if (_tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] != _tx_thread_execute_ptr)
+                            {
+
+                                /* Move to next entry.  */
+                                _tx_thread_performance__execute_log_index++;
+
+                                /* Check for wrap condition.  */
+                                if (_tx_thread_performance__execute_log_index >= TX_THREAD_EXECUTE_LOG_SIZE)
+                                {
+
+                                    /* Set the index to the beginning.  */
+                                    _tx_thread_performance__execute_log_index =  ((UINT) 0);
+                                }
+
+                                /* Log the new execute pointer.  */
+                                _tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] =  _tx_thread_execute_ptr;
+                            }
+#endif
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+                            /* Check that the event time stamp is unchanged.  A different
+                               timestamp means that a later event wrote over the thread
+                               resume event. In that case, do nothing here.  */
+                            if (entry_ptr != TX_NULL)
+                            {
+
+                                /* Is the timestamp the same?  */
+                                if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
+                                {
+
+                                    /* Timestamp is the same, set the "next thread pointer" to NULL. This can
+                                       be used by the trace analysis tool to show idle system conditions.  */
+                                    entry_ptr -> tx_trace_buffer_entry_information_field_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
+                                }
+                            }
+#endif
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+                            /* Pickup the next execute pointer.  */
+                            thread_ptr =  _tx_thread_execute_ptr;
+
+                            /* Check this thread's stack.  */
+                            TX_THREAD_STACK_CHECK(thread_ptr)
+#endif
+
+                            /* Now determine if preemption should take place. This is only possible if the current thread pointer is
+                               not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
+                            TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
+                            if (combined_flags == ((ULONG) 0))
+                            {
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                                /* There is another thread ready to run and will be scheduled upon return.  */
+                                _tx_thread_performance_non_idle_return_count++;
+#endif
+
+                                /* Preemption is needed - return to the system!  */
+                                _tx_thread_system_return();
+                            }
+
+                            /* Return in-line when MISRA is not enabled.  */
+                            return;
+#endif
+                        }
+                    }
+                }
+            }
+            else
+            {
+
+                /* No, there are other threads at this priority already ready.  */
+
+                /* Just add this thread to the priority list.  */
+                tail_ptr =                                 head_ptr -> tx_thread_ready_previous;
+                tail_ptr -> tx_thread_ready_next =         thread_ptr;
+                head_ptr -> tx_thread_ready_previous =     thread_ptr;
+                thread_ptr -> tx_thread_ready_previous =   tail_ptr;
+                thread_ptr -> tx_thread_ready_next =       head_ptr;
+            }
+        }
+
+        /* Else, delayed suspend flag was set.  */
+        else
+        {
+
+            /* Clear the delayed suspend flag and change the state.  */
+            thread_ptr -> tx_thread_delayed_suspend =  TX_FALSE;
+            thread_ptr -> tx_thread_state =            TX_SUSPENDED;
+        }
+    }
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+    /* Is the execute pointer different?  */
+    if (_tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] != _tx_thread_execute_ptr)
+    {
+
+        /* Move to next entry.  */
+        _tx_thread_performance__execute_log_index++;
+
+        /* Check for wrap condition.  */
+        if (_tx_thread_performance__execute_log_index >= TX_THREAD_EXECUTE_LOG_SIZE)
+        {
+
+            /* Set the index to the beginning.  */
+            _tx_thread_performance__execute_log_index =  ((UINT) 0);
+        }
+
+        /* Log the new execute pointer.  */
+        _tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] =  _tx_thread_execute_ptr;
+    }
+#endif
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+    /* Check that the event time stamp is unchanged.  A different
+       timestamp means that a later event wrote over the thread
+       resume event. In that case, do nothing here.  */
+    if (entry_ptr != TX_NULL)
+    {
+
+        /* Does the timestamp match?  */
+        if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
+        {
+
+            /* Timestamp is the same, set the "next thread pointer" to NULL. This can
+               be used by the trace analysis tool to show idle system conditions.  */
+#ifdef TX_MISRA_ENABLE
+            entry_ptr -> tx_trace_buffer_entry_info_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
+#else
+            entry_ptr -> tx_trace_buffer_entry_information_field_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
+#endif
+        }
+    }
+#endif
+
+    /* Pickup thread pointer.  */
+    TX_THREAD_GET_CURRENT(current_thread)
+
+    /* Determine if a preemption condition is present.  */
+    if (current_thread != _tx_thread_execute_ptr)
+    {
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+        /* Pickup the next execute pointer.  */
+        thread_ptr =  _tx_thread_execute_ptr;
+
+        /* Check this thread's stack.  */
+        TX_THREAD_STACK_CHECK(thread_ptr)
+#endif
+
+        /* Now determine if preemption should take place. This is only possible if the current thread pointer is
+           not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
+        TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
+        if (combined_flags == ((ULONG) 0))
+        {
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+            /* There is another thread ready to run and will be scheduled upon return.  */
+            _tx_thread_performance_non_idle_return_count++;
+#endif
+
+            /* Preemption is needed - return to the system!  */
+            _tx_thread_system_return();
+        }
+    }
+}
+#endif
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_system_suspend.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_system_suspend.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_system_suspend.c	(revision 54)
@@ -0,0 +1,1220 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Thread                                                              */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_trace.h"
+#include "tx_timer.h"
+#include "tx_thread.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_thread_system_suspend                           PORTABLE C      */
+/*                                                           6.1          */
+/*                                                                        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function suspends the specified thread and changes the thread  */
+/*    state to the value specified.  Note: delayed suspension processing  */
+/*    is handled outside of this routine.                                 */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    thread_ptr                            Pointer to thread to suspend  */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_return              Return to system              */
+/*    _tx_thread_system_preempt_check       System preemption check       */
+/*    _tx_timer_system_activate             Activate timer for timeout    */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_thread_priority_change            Thread priority change        */
+/*    _tx_thread_shell_entry                Thread shell function         */
+/*    _tx_thread_sleep                      Thread sleep                  */
+/*    _tx_thread_suspend                    Application thread suspend    */
+/*    _tx_thread_terminate                  Thread terminate              */
+/*    Other ThreadX Components                                            */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_thread_system_suspend(TX_THREAD *thread_ptr)
+#ifndef TX_NOT_INTERRUPTABLE
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+UINT            priority;
+UINT            base_priority;
+ULONG           priority_map;
+ULONG           priority_bit;
+ULONG           combined_flags;
+TX_THREAD       *ready_next;
+TX_THREAD       *ready_previous;
+TX_THREAD       *current_thread;
+
+#if TX_MAX_PRIORITIES > 32
+UINT            map_index;
+#endif
+
+#ifndef TX_NO_TIMER
+ULONG           timeout;
+#endif
+
+#ifdef TX_ENABLE_EVENT_TRACE
+TX_TRACE_BUFFER_ENTRY       *entry_ptr;
+ULONG                       time_stamp =  ((ULONG) 0);
+#endif
+
+    /* Pickup thread pointer.  */
+    TX_THREAD_GET_CURRENT(current_thread)
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+    /* Check this thread's stack.  */
+    TX_THREAD_STACK_CHECK(thread_ptr)
+#endif
+
+    /* Lockout interrupts while the thread is being suspended.  */
+    TX_DISABLE
+
+#ifndef TX_NO_TIMER
+
+    /* Is the current thread suspending?  */
+    if (thread_ptr == current_thread)
+    {
+
+        /* Pickup the wait option.  */
+        timeout =  thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks;
+
+        /* Determine if an activation is needed.  */
+        if (timeout != TX_NO_WAIT)
+        {
+
+            /* Make sure the suspension is not a wait-forever.  */
+            if (timeout != TX_WAIT_FOREVER)
+            {
+
+                /* Activate the thread timer with the timeout value setup in the caller.  */
+                _tx_timer_system_activate(&(thread_ptr -> tx_thread_timer));
+            }
+        }
+
+        /* Yes, reset time slice for current thread.  */
+        _tx_timer_time_slice =  thread_ptr -> tx_thread_new_time_slice;
+    }
+#endif
+
+    /* Decrease the preempt disabled count.  */
+    _tx_thread_preempt_disable--;
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+    /* Increment the thread's suspend count.  */
+    thread_ptr -> tx_thread_performance_suspend_count++;
+
+    /* Increment the total number of thread suspensions.  */
+    _tx_thread_performance_suspend_count++;
+#endif
+
+    /* Check to make sure the thread suspending flag is still set.  If not, it
+       has already been resumed.  */
+    if (thread_ptr -> tx_thread_suspending == TX_TRUE)
+    {
+
+        /* Thread state change.  */
+        TX_THREAD_STATE_CHANGE(thread_ptr, thread_ptr -> tx_thread_state)
+
+        /* Log the thread status change.  */
+        TX_EL_THREAD_STATUS_CHANGE_INSERT(thread_ptr, thread_ptr -> tx_thread_state)
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+        /* If trace is enabled, save the current event pointer.  */
+        entry_ptr =  _tx_trace_buffer_current_ptr;
+#endif
+
+        /* Log the thread status change.  */
+        TX_TRACE_IN_LINE_INSERT(TX_TRACE_THREAD_SUSPEND, thread_ptr, thread_ptr -> tx_thread_state, TX_POINTER_TO_ULONG_CONVERT(&priority), TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr), TX_TRACE_INTERNAL_EVENTS)
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+        /* Save the time stamp for later comparison to verify that
+           the event hasn't been overwritten by the time we have
+           computed the next thread to execute.  */
+        if (entry_ptr != TX_NULL)
+        {
+
+            /* Save time stamp.  */
+            time_stamp =  entry_ptr -> tx_trace_buffer_entry_time_stamp;
+        }
+#endif
+
+        /* Actually suspend this thread.  But first, clear the suspending flag.  */
+        thread_ptr -> tx_thread_suspending =  TX_FALSE;
+
+        /* Pickup priority of thread.  */
+        priority =  thread_ptr -> tx_thread_priority;
+
+        /* Pickup the next ready thread pointer.  */
+        ready_next =      thread_ptr -> tx_thread_ready_next;
+
+        /* Determine if there are other threads at this priority that are
+           ready.  */
+        if (ready_next != thread_ptr)
+        {
+
+            /* Yes, there are other threads at this priority ready.  */
+
+            /* Pickup the previous ready thread pointer.  */
+            ready_previous =  thread_ptr -> tx_thread_ready_previous;
+
+            /* Just remove this thread from the priority list.  */
+            ready_next -> tx_thread_ready_previous =    ready_previous;
+            ready_previous -> tx_thread_ready_next =    ready_next;
+
+            /* Determine if this is the head of the priority list.  */
+            if (_tx_thread_priority_list[priority] == thread_ptr)
+            {
+
+                /* Update the head pointer of this priority list.  */
+                _tx_thread_priority_list[priority] =  ready_next;
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+
+#if TX_MAX_PRIORITIES > 32
+
+                /* Calculate the index into the bit map array.  */
+                map_index =  priority/((UINT) 32);
+#endif
+
+                /* Check for a thread preempted that had preemption threshold set.  */
+                if (_tx_thread_preempted_maps[MAP_INDEX] != ((ULONG) 0))
+                {
+
+                    /* Ensure that this thread's priority is clear in the preempt map.  */
+                    TX_MOD32_BIT_SET(priority, priority_bit)
+                    _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
+
+#if TX_MAX_PRIORITIES > 32
+
+                    /* Determine if there are any other bits set in this preempt map.  */
+                    if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
+                    {
+
+                        /* No, clear the active bit to signify this preempt map has nothing set.  */
+                        TX_DIV32_BIT_SET(priority, priority_bit)
+                        _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
+                    }
+#endif
+                }
+#endif
+            }
+        }
+        else
+        {
+
+            /* This is the only thread at this priority ready to run.  Set the head
+               pointer to NULL.  */
+            _tx_thread_priority_list[priority] =    TX_NULL;
+
+#if TX_MAX_PRIORITIES > 32
+
+            /* Calculate the index into the bit map array.  */
+            map_index =  priority/((UINT) 32);
+#endif
+
+            /* Clear this priority bit in the ready priority bit map.  */
+            TX_MOD32_BIT_SET(priority, priority_bit)
+            _tx_thread_priority_maps[MAP_INDEX] =  _tx_thread_priority_maps[MAP_INDEX] & (~(priority_bit));
+
+#if TX_MAX_PRIORITIES > 32
+
+            /* Determine if there are any other bits set in this priority map.  */
+            if (_tx_thread_priority_maps[MAP_INDEX] == ((ULONG) 0))
+            {
+
+                /* No, clear the active bit to signify this priority map has nothing set.  */
+                TX_DIV32_BIT_SET(priority, priority_bit)
+                _tx_thread_priority_map_active =  _tx_thread_priority_map_active & (~(priority_bit));
+            }
+#endif
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+
+            /* Check for a thread preempted that had preemption-threshold set.  */
+            if (_tx_thread_preempted_maps[MAP_INDEX] != ((ULONG) 0))
+            {
+
+                /* Ensure that this thread's priority is clear in the preempt map.  */
+                TX_MOD32_BIT_SET(priority, priority_bit)
+                _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
+
+#if TX_MAX_PRIORITIES > 32
+
+                /* Determine if there are any other bits set in this preempt map.  */
+                if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
+                {
+
+                    /* No, clear the active bit to signify this preempted map has nothing set.  */
+                    TX_DIV32_BIT_SET(priority, priority_bit)
+                    _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
+                }
+#endif
+            }
+#endif
+
+#if TX_MAX_PRIORITIES > 32
+
+            /* Calculate the index to find the next highest priority thread ready for execution.  */
+            priority_map =    _tx_thread_priority_map_active;
+
+            /* Determine if there is anything.   */
+            if (priority_map != ((ULONG) 0))
+            {
+
+                /* Calculate the lowest bit set in the priority map. */
+                TX_LOWEST_SET_BIT_CALCULATE(priority_map, map_index)
+            }
+
+            /* Calculate the base priority as well.  */
+            base_priority =  map_index * ((UINT) 32);
+#else
+
+            /* Setup the base priority to zero.  */
+            base_priority =   ((UINT) 0);
+#endif
+
+            /* Setup working variable for the priority map.  */
+            priority_map =    _tx_thread_priority_maps[MAP_INDEX];
+
+            /* Make a quick check for no other threads ready for execution.  */
+            if (priority_map == ((ULONG) 0))
+            {
+
+                /* Nothing else is ready.  Set highest priority and execute thread
+                   accordingly.  */
+                _tx_thread_highest_priority =  ((UINT) TX_MAX_PRIORITIES);
+                _tx_thread_execute_ptr =       TX_NULL;
+
+#ifndef TX_MISRA_ENABLE
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+                /* Check that the event time stamp is unchanged.  A different
+                   timestamp means that a later event wrote over the thread
+                   suspend event. In that case, do nothing here.  */
+                if (entry_ptr != TX_NULL)
+                {
+
+                    /* Is the timestamp the same?  */
+                    if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
+                    {
+
+                        /* Timestamp is the same, set the "next thread pointer" to the new value of the
+                           next thread to execute. This can be used by the trace analysis tool to keep
+                           track of next thread execution.  */
+                        entry_ptr -> tx_trace_buffer_entry_information_field_4 =  0;
+                    }
+                }
+#endif
+
+                /* Restore interrupts.  */
+                TX_RESTORE
+
+                /* Determine if preemption should take place. This is only possible if the current thread pointer is
+                   not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
+                TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
+                if (combined_flags == ((ULONG) 0))
+                {
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                    /* Yes, increment the return to idle return count.  */
+                    _tx_thread_performance_idle_return_count++;
+#endif
+
+                    /* Preemption is needed - return to the system!  */
+                    _tx_thread_system_return();
+                }
+
+                /* Return to caller.  */
+                return;
+#endif
+            }
+            else
+            {
+
+                /* Other threads at different priority levels are ready to run.  */
+
+                /* Calculate the lowest bit set in the priority map. */
+                TX_LOWEST_SET_BIT_CALCULATE(priority_map, priority_bit)
+
+                /* Setup the next highest priority variable.  */
+                _tx_thread_highest_priority =  base_priority + ((UINT) priority_bit);
+            }
+        }
+
+        /* Determine if the suspending thread is the thread designated to execute.  */
+        if (thread_ptr == _tx_thread_execute_ptr)
+        {
+
+            /* Pickup the highest priority thread to execute.  */
+            _tx_thread_execute_ptr =  _tx_thread_priority_list[_tx_thread_highest_priority];
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+
+            /* Determine if a previous thread with preemption-threshold was preempted.  */
+#if TX_MAX_PRIORITIES > 32
+            if (_tx_thread_preempted_map_active != ((ULONG) 0))
+#else
+            if (_tx_thread_preempted_maps[MAP_INDEX] != ((ULONG) 0))
+#endif
+            {
+
+                /* Yes, there was a thread preempted when it was using preemption-threshold.  */
+
+                /* Disable preemption.  */
+                _tx_thread_preempt_disable++;
+
+                /* Restore interrupts.  */
+                TX_RESTORE
+
+                /* Interrupts are enabled briefly here to keep the interrupt
+                   lockout time deterministic.  */
+
+                /* Disable interrupts again.  */
+                TX_DISABLE
+
+                /* Decrement the preemption disable variable.  */
+                _tx_thread_preempt_disable--;
+
+                /* Calculate the thread with preemption threshold set that
+                   was interrupted by a thread above the preemption level.  */
+
+#if TX_MAX_PRIORITIES > 32
+
+                /* Calculate the index to find the next highest priority thread ready for execution.  */
+                priority_map =    _tx_thread_preempted_map_active;
+
+                /* Calculate the lowest bit set in the priority map. */
+                TX_LOWEST_SET_BIT_CALCULATE(priority_map, map_index)
+
+                /* Calculate the base priority as well.  */
+                base_priority =  map_index * ((UINT) 32);
+#else
+
+                /* Setup the base priority to zero.  */
+                base_priority =   ((UINT) 0);
+#endif
+
+                /* Setup temporary preempted map.  */
+                priority_map =  _tx_thread_preempted_maps[MAP_INDEX];
+
+                /* Calculate the lowest bit set in the priority map. */
+                TX_LOWEST_SET_BIT_CALCULATE(priority_map, priority_bit)
+
+                /* Setup the highest priority preempted thread.  */
+                priority =  base_priority + ((UINT) priority_bit);
+
+                /* Determine if the next highest priority thread is above the highest priority threshold value.  */
+                if (_tx_thread_highest_priority >= (_tx_thread_priority_list[priority] -> tx_thread_preempt_threshold))
+                {
+
+                    /* Thread not allowed to execute until earlier preempted thread finishes or lowers its
+                       preemption-threshold.  */
+                    _tx_thread_execute_ptr =  _tx_thread_priority_list[priority];
+
+                    /* Clear the corresponding bit in the preempted map, since the preemption has been restored.  */
+                    TX_MOD32_BIT_SET(priority, priority_bit)
+                    _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
+
+#if TX_MAX_PRIORITIES > 32
+
+                    /* Determine if there are any other bits set in this preempt map.  */
+                    if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
+                    {
+
+                        /* No, clear the active bit to signify this preempt map has nothing set.  */
+                        TX_DIV32_BIT_SET(priority, priority_bit)
+                        _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
+                    }
+#endif
+                }
+            }
+#endif
+
+#ifndef TX_MISRA_ENABLE
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+            /* Is the execute pointer different?  */
+            if (_tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] != _tx_thread_execute_ptr)
+            {
+
+                /* Move to next entry.  */
+                _tx_thread_performance__execute_log_index++;
+
+                /* Check for wrap condition.  */
+                if (_tx_thread_performance__execute_log_index >= TX_THREAD_EXECUTE_LOG_SIZE)
+                {
+
+                    /* Set the index to the beginning.  */
+                    _tx_thread_performance__execute_log_index =  ((UINT) 0);
+                }
+
+                /* Log the new execute pointer.  */
+                _tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] =  _tx_thread_execute_ptr;
+            }
+#endif
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+            /* Check that the event time stamp is unchanged.  A different
+               timestamp means that a later event wrote over the thread
+               suspend event. In that case, do nothing here.  */
+            if (entry_ptr != TX_NULL)
+            {
+
+                /* Is the timestamp the same?  */
+                if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
+                {
+
+                    /* Timestamp is the same, set the "next thread pointer" to the new value of the
+                       next thread to execute. This can be used by the trace analysis tool to keep
+                       track of next thread execution.  */
+                    entry_ptr -> tx_trace_buffer_entry_information_field_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
+                }
+            }
+#endif
+
+            /* Restore interrupts.  */
+            TX_RESTORE
+
+            /* Determine if preemption should take place. This is only possible if the current thread pointer is
+               not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
+            TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
+            if (combined_flags == ((ULONG) 0))
+            {
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                /* No, there is another thread ready to run and will be scheduled upon return.  */
+                _tx_thread_performance_non_idle_return_count++;
+#endif
+
+                /* Preemption is needed - return to the system!  */
+                _tx_thread_system_return();
+            }
+
+            /* Return to caller.  */
+            return;
+#endif
+        }
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+        /* Is the execute pointer different?  */
+        if (_tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] != _tx_thread_execute_ptr)
+        {
+
+            /* Move to next entry.  */
+            _tx_thread_performance__execute_log_index++;
+
+            /* Check for wrap condition.  */
+            if (_tx_thread_performance__execute_log_index >= TX_THREAD_EXECUTE_LOG_SIZE)
+            {
+
+                /* Set the index to the beginning.  */
+                _tx_thread_performance__execute_log_index =  ((UINT) 0);
+            }
+
+            /* Log the new execute pointer.  */
+            _tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] =  _tx_thread_execute_ptr;
+        }
+#endif
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+         /* Check that the event time stamp is unchanged.  A different
+            timestamp means that a later event wrote over the thread
+            suspend event. In that case, do nothing here.  */
+         if (entry_ptr != TX_NULL)
+         {
+
+            /* Is the timestamp the same?  */
+            if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
+            {
+
+                /* Timestamp is the same, set the "next thread pointer" to the new value of the
+                   next thread to execute. This can be used by the trace analysis tool to keep
+                   track of next thread execution.  */
+#ifdef TX_MISRA_ENABLE
+                entry_ptr -> tx_trace_buffer_entry_info_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
+#else
+                entry_ptr -> tx_trace_buffer_entry_information_field_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
+#endif
+            }
+        }
+#endif
+    }
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Determine if a preemption condition is present.  */
+    if (current_thread != _tx_thread_execute_ptr)
+    {
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+        /* Pickup the next execute pointer.  */
+        thread_ptr =  _tx_thread_execute_ptr;
+
+        /* Check this thread's stack.  */
+        TX_THREAD_STACK_CHECK(thread_ptr)
+#endif
+
+        /* Determine if preemption should take place. This is only possible if the current thread pointer is
+           not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
+        TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
+        if (combined_flags == ((ULONG) 0))
+        {
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+            /* Determine if an idle system return is present.  */
+            if (_tx_thread_execute_ptr == TX_NULL)
+            {
+
+                /* Yes, increment the return to idle return count.  */
+                _tx_thread_performance_idle_return_count++;
+            }
+            else
+            {
+
+                /* No, there is another thread ready to run and will be scheduled upon return.  */
+                _tx_thread_performance_non_idle_return_count++;
+            }
+#endif
+
+            /* Preemption is needed - return to the system!  */
+            _tx_thread_system_return();
+        }
+    }
+
+    /* Return to caller.  */
+    return;
+}
+#else
+/* Define the entry function for modules assuming the interruptable version of system suspend.  */
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+ULONG       wait_option;
+
+    /* Disable interrupts.  */
+    TX_DISABLE
+
+    /* Determine if the thread is still suspending.  */
+    if (thread_ptr -> tx_thread_suspending == TX_TRUE)
+    {
+
+        /* Yes, prepare to call the non-interruptable system suspend function.  */
+
+        /* Clear the thread suspending flag.  */
+        thread_ptr -> tx_thread_suspending =  TX_FALSE;
+
+        /* Pickup the wait option.  */
+        wait_option =  thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks;
+
+        /* Decrement the preempt disable count.  */
+        _tx_thread_preempt_disable--;
+
+        /* Call actual non-interruptable thread suspension routine.  */
+        _tx_thread_system_ni_suspend(thread_ptr, wait_option);
+    }
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Check for preemption.  */
+    _tx_thread_system_preempt_check();
+}
+
+/* Define the system suspend function that is not interruptable, i.e., it is assumed that
+   interrupts are disabled upon calling this function.  */
+
+VOID  _tx_thread_system_ni_suspend(TX_THREAD *thread_ptr, ULONG wait_option)
+{
+
+UINT            priority;
+UINT            base_priority;
+ULONG           priority_map;
+ULONG           priority_bit;
+ULONG           combined_flags;
+TX_THREAD       *ready_next;
+TX_THREAD       *ready_previous;
+TX_THREAD       *current_thread;
+
+#if TX_MAX_PRIORITIES > 32
+UINT            map_index;
+#endif
+
+#ifdef TX_ENABLE_EVENT_TRACE
+TX_TRACE_BUFFER_ENTRY       *entry_ptr;
+ULONG                       time_stamp =  ((ULONG) 0);
+#endif
+
+
+    /* Pickup thread pointer.  */
+    TX_THREAD_GET_CURRENT(current_thread)
+
+#ifndef TX_NO_TIMER
+
+
+    /* Determine if a timeout needs to be activated.  */
+    if (thread_ptr == current_thread)
+    {
+
+        /* Is there a wait option?  */
+        if (wait_option != TX_NO_WAIT)
+        {
+
+            /* Make sure it is not a wait-forever option.  */
+            if (wait_option != TX_WAIT_FOREVER)
+            {
+
+                /* Setup the wait option.  */
+                thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option;
+
+                /* Activate the thread timer with the timeout value setup in the caller.  */
+                _tx_timer_system_activate(&(thread_ptr -> tx_thread_timer));
+            }
+        }
+
+        /* Reset time slice for current thread.  */
+        _tx_timer_time_slice =  thread_ptr -> tx_thread_new_time_slice;
+    }
+#endif
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+    /* Check this thread's stack.  */
+    TX_THREAD_STACK_CHECK(thread_ptr)
+#endif
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+    /* Increment the thread's suspend count.  */
+    thread_ptr -> tx_thread_performance_suspend_count++;
+
+    /* Increment the total number of thread suspensions.  */
+    _tx_thread_performance_suspend_count++;
+#endif
+
+    /* Thread state change.  */
+    TX_THREAD_STATE_CHANGE(thread_ptr, thread_ptr -> tx_thread_state)
+
+    /* Log the thread status change.  */
+    TX_EL_THREAD_STATUS_CHANGE_INSERT(thread_ptr, thread_ptr -> tx_thread_state)
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+    /* If trace is enabled, save the current event pointer.  */
+    entry_ptr =  _tx_trace_buffer_current_ptr;
+#endif
+
+    /* Log the thread status change.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_THREAD_SUSPEND, thread_ptr, thread_ptr -> tx_thread_state, TX_POINTER_TO_ULONG_CONVERT(&priority), TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr), TX_TRACE_INTERNAL_EVENTS)
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+    /* Save the time stamp for later comparison to verify that
+       the event hasn't been overwritten by the time we have
+       computed the next thread to execute.  */
+    if (entry_ptr != TX_NULL)
+    {
+
+        /* Save time stamp.  */
+        time_stamp =  entry_ptr -> tx_trace_buffer_entry_time_stamp;
+    }
+#endif
+
+    /* Pickup priority of thread.  */
+    priority =  thread_ptr -> tx_thread_priority;
+
+    /* Pickup the next ready thread pointer.  */
+    ready_next =      thread_ptr -> tx_thread_ready_next;
+
+    /* Determine if there are other threads at this priority that are
+       ready.  */
+    if (ready_next != thread_ptr)
+    {
+
+        /* Yes, there are other threads at this priority ready.  */
+
+        /* Pickup the previous ready thread pointer.  */
+        ready_previous =  thread_ptr -> tx_thread_ready_previous;
+
+        /* Just remove this thread from the priority list.  */
+        ready_next -> tx_thread_ready_previous =    ready_previous;
+        ready_previous -> tx_thread_ready_next =    ready_next;
+
+        /* Determine if this is the head of the priority list.  */
+        if (_tx_thread_priority_list[priority] == thread_ptr)
+        {
+
+            /* Update the head pointer of this priority list.  */
+            _tx_thread_priority_list[priority] =  ready_next;
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+
+#if TX_MAX_PRIORITIES > 32
+
+            /* Calculate the index into the bit map array.  */
+            map_index =  priority/((UINT) 32);
+#endif
+
+            /* Check for a thread preempted that had preemption threshold set.  */
+            if (_tx_thread_preempted_maps[MAP_INDEX] != ((ULONG) 0))
+            {
+
+                /* Ensure that this thread's priority is clear in the preempt map.  */
+                TX_MOD32_BIT_SET(priority, priority_bit)
+                _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
+
+#if TX_MAX_PRIORITIES > 32
+
+                /* Determine if there are any other bits set in this preempt map.  */
+                if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
+                {
+
+                    /* No, clear the active bit to signify this preempt map has nothing set.  */
+                    TX_DIV32_BIT_SET(priority, priority_bit)
+                    _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
+                }
+#endif
+            }
+#endif
+        }
+    }
+    else
+    {
+
+        /* This is the only thread at this priority ready to run.  Set the head
+           pointer to NULL.  */
+        _tx_thread_priority_list[priority] =    TX_NULL;
+
+#if TX_MAX_PRIORITIES > 32
+
+        /* Calculate the index into the bit map array.  */
+        map_index =  priority/((UINT) 32);
+#endif
+
+        /* Clear this priority bit in the ready priority bit map.  */
+        TX_MOD32_BIT_SET(priority, priority_bit)
+        _tx_thread_priority_maps[MAP_INDEX] =  _tx_thread_priority_maps[MAP_INDEX] & (~(priority_bit));
+
+#if TX_MAX_PRIORITIES > 32
+
+        /* Determine if there are any other bits set in this priority map.  */
+        if (_tx_thread_priority_maps[MAP_INDEX] == ((ULONG) 0))
+        {
+
+            /* No, clear the active bit to signify this priority map has nothing set.  */
+            TX_DIV32_BIT_SET(priority, priority_bit)
+            _tx_thread_priority_map_active =  _tx_thread_priority_map_active & (~(priority_bit));
+        }
+#endif
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+
+        /* Check for a thread preempted that had preemption-threshold set.  */
+        if (_tx_thread_preempted_maps[MAP_INDEX] != ((ULONG) 0))
+        {
+
+            /* Ensure that this thread's priority is clear in the preempt map.  */
+            TX_MOD32_BIT_SET(priority, priority_bit)
+            _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
+
+#if TX_MAX_PRIORITIES > 32
+
+            /* Determine if there are any other bits set in this preempt map.  */
+            if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
+            {
+
+                /* No, clear the active bit to signify this preempted map has nothing set.  */
+                TX_DIV32_BIT_SET(priority, priority_bit)
+                _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
+            }
+#endif
+        }
+#endif
+
+#if TX_MAX_PRIORITIES > 32
+
+        /* Calculate the index to find the next highest priority thread ready for execution.  */
+        priority_map =    _tx_thread_priority_map_active;
+
+        /* Determine if there is anything.   */
+        if (priority_map != ((ULONG) 0))
+        {
+
+            /* Calculate the lowest bit set in the priority map. */
+            TX_LOWEST_SET_BIT_CALCULATE(priority_map, map_index)
+        }
+
+        /* Calculate the base priority as well.  */
+        base_priority =  map_index * ((UINT) 32);
+#else
+
+        /* Setup the base priority to zero.  */
+        base_priority =   ((UINT) 0);
+#endif
+
+        /* Setup working variable for the priority map.  */
+        priority_map =    _tx_thread_priority_maps[MAP_INDEX];
+
+        /* Make a quick check for no other threads ready for execution.  */
+        if (priority_map == ((ULONG) 0))
+        {
+
+            /* Nothing else is ready.  Set highest priority and execute thread
+               accordingly.  */
+            _tx_thread_highest_priority =  ((UINT) TX_MAX_PRIORITIES);
+            _tx_thread_execute_ptr =       TX_NULL;
+
+#ifndef TX_MISRA_ENABLE
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+            /* Check that the event time stamp is unchanged.  A different
+               timestamp means that a later event wrote over the thread
+               suspend event. In that case, do nothing here.  */
+            if (entry_ptr != TX_NULL)
+            {
+
+                /* Is the timestamp the same?  */
+                if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
+                {
+
+                    /* Timestamp is the same, set the "next thread pointer" to the new value of the
+                       next thread to execute. This can be used by the trace analysis tool to keep
+                       track of next thread execution.  */
+                    entry_ptr -> tx_trace_buffer_entry_information_field_4 =  0;
+                }
+            }
+#endif
+
+            /* Determine if preemption should take place. This is only possible if the current thread pointer is
+               not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
+            TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
+            if (combined_flags == ((ULONG) 0))
+            {
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                /* Yes, increment the return to idle return count.  */
+                _tx_thread_performance_idle_return_count++;
+#endif
+
+                /* Preemption is needed - return to the system!  */
+                _tx_thread_system_return();
+            }
+
+            /* Return to caller.  */
+            return;
+#endif
+        }
+        else
+        {
+
+            /* Calculate the lowest bit set in the priority map. */
+            TX_LOWEST_SET_BIT_CALCULATE(priority_map, priority_bit)
+
+            /* Setup the next highest priority variable.  */
+            _tx_thread_highest_priority =  base_priority + ((UINT) priority_bit);
+        }
+    }
+
+    /* Determine if the suspending thread is the thread designated to execute.  */
+    if (thread_ptr == _tx_thread_execute_ptr)
+    {
+
+        /* Pickup the highest priority thread to execute.  */
+        _tx_thread_execute_ptr =  _tx_thread_priority_list[_tx_thread_highest_priority];
+
+#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
+
+        /* Determine if a previous thread with preemption-threshold was preempted.  */
+#if TX_MAX_PRIORITIES > 32
+        if (_tx_thread_preempted_map_active != ((ULONG) 0))
+#else
+        if (_tx_thread_preempted_maps[MAP_INDEX] != ((ULONG) 0))
+#endif
+        {
+
+            /* Yes, there was a thread preempted when it was using preemption-threshold.  */
+
+            /* Disable preemption.  */
+            _tx_thread_preempt_disable++;
+
+            /* Decrement the preemption disable variable.  */
+            _tx_thread_preempt_disable--;
+
+            /* Calculate the thread with preemption threshold set that
+               was interrupted by a thread above the preemption level.  */
+
+#if TX_MAX_PRIORITIES > 32
+
+            /* Calculate the index to find the next highest priority thread ready for execution.  */
+            priority_map =    _tx_thread_preempted_map_active;
+
+            /* Calculate the lowest bit set in the priority map. */
+            TX_LOWEST_SET_BIT_CALCULATE(priority_map, map_index)
+
+            /* Calculate the base priority as well.  */
+            base_priority =  map_index * ((UINT) 32);
+#else
+
+            /* Setup the base priority to zero.  */
+            base_priority =   ((UINT) 0);
+#endif
+
+            /* Setup temporary preempted map.  */
+            priority_map =  _tx_thread_preempted_maps[MAP_INDEX];
+
+            /* Calculate the lowest bit set in the priority map. */
+            TX_LOWEST_SET_BIT_CALCULATE(priority_map, priority_bit)
+
+            /* Setup the highest priority preempted thread.  */
+            priority =  base_priority + ((UINT) priority_bit);
+
+            /* Determine if the next highest priority thread is above the highest priority threshold value.  */
+            if (_tx_thread_highest_priority >= (_tx_thread_priority_list[priority] -> tx_thread_preempt_threshold))
+            {
+
+                /* Thread not allowed to execute until earlier preempted thread finishes or lowers its
+                   preemption-threshold.  */
+                _tx_thread_execute_ptr =  _tx_thread_priority_list[priority];
+
+                /* Clear the corresponding bit in the preempted map, since the preemption has been restored.  */
+                TX_MOD32_BIT_SET(priority, priority_bit)
+                _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
+
+#if TX_MAX_PRIORITIES > 32
+
+                /* Determine if there are any other bits set in this preempt map.  */
+                if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
+                {
+
+                    /* No, clear the active bit to signify this preempt map has nothing set.  */
+                    TX_DIV32_BIT_SET(priority, priority_bit)
+                    _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
+                }
+#endif
+            }
+        }
+#endif
+
+#ifndef TX_MISRA_ENABLE
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+        /* Is the execute pointer different?  */
+        if (_tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] != _tx_thread_execute_ptr)
+        {
+
+            /* Move to next entry.  */
+            _tx_thread_performance__execute_log_index++;
+
+            /* Check for wrap condition.  */
+            if (_tx_thread_performance__execute_log_index >= TX_THREAD_EXECUTE_LOG_SIZE)
+            {
+
+                /* Set the index to the beginning.  */
+                _tx_thread_performance__execute_log_index =  ((UINT) 0);
+            }
+
+            /* Log the new execute pointer.  */
+            _tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] =  _tx_thread_execute_ptr;
+        }
+#endif
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+        /* Check that the event time stamp is unchanged.  A different
+           timestamp means that a later event wrote over the thread
+           suspend event. In that case, do nothing here.  */
+        if (entry_ptr != TX_NULL)
+        {
+
+            /* Is the timestamp the same?  */
+            if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
+            {
+
+                /* Timestamp is the same, set the "next thread pointer" to the new value of the
+                   next thread to execute. This can be used by the trace analysis tool to keep
+                   track of next thread execution.  */
+                entry_ptr -> tx_trace_buffer_entry_information_field_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
+            }
+        }
+#endif
+
+        /* Determine if preemption should take place. This is only possible if the current thread pointer is
+           not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
+        TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
+        if (combined_flags == ((ULONG) 0))
+        {
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+            /* No, there is another thread ready to run and will be scheduled upon return.  */
+            _tx_thread_performance_non_idle_return_count++;
+#endif
+
+            /* Preemption is needed - return to the system!  */
+            _tx_thread_system_return();
+        }
+
+        /* Return to caller.  */
+        return;
+#endif
+    }
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+    /* Is the execute pointer different?  */
+    if (_tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] != _tx_thread_execute_ptr)
+    {
+
+        /* Move to next entry.  */
+        _tx_thread_performance__execute_log_index++;
+
+        /* Check for wrap condition.  */
+        if (_tx_thread_performance__execute_log_index >= TX_THREAD_EXECUTE_LOG_SIZE)
+        {
+
+            /* Set the index to the beginning.  */
+            _tx_thread_performance__execute_log_index =  ((UINT) 0);
+        }
+
+        /* Log the new execute pointer.  */
+        _tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] =  _tx_thread_execute_ptr;
+    }
+#endif
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+    /* Check that the event time stamp is unchanged.  A different
+       timestamp means that a later event wrote over the thread
+       suspend event. In that case, do nothing here.  */
+    if (entry_ptr != TX_NULL)
+    {
+
+        /* Is the timestamp the same?  */
+        if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
+        {
+
+            /* Timestamp is the same, set the "next thread pointer" to the new value of the
+               next thread to execute. This can be used by the trace analysis tool to keep
+               track of next thread execution.  */
+#ifdef TX_MISRA_ENABLE
+            entry_ptr -> tx_trace_buffer_entry_info_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
+#else
+            entry_ptr -> tx_trace_buffer_entry_information_field_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
+#endif
+        }
+    }
+#endif
+
+    /* Determine if a preemption condition is present.  */
+    if (current_thread != _tx_thread_execute_ptr)
+    {
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+        /* Pickup the next execute pointer.  */
+        thread_ptr =  _tx_thread_execute_ptr;
+
+        /* Check this thread's stack.  */
+        TX_THREAD_STACK_CHECK(thread_ptr)
+#endif
+
+        /* Determine if preemption should take place. This is only possible if the current thread pointer is
+           not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
+        TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
+        if (combined_flags == ((ULONG) 0))
+        {
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+            /* Determine if an idle system return is present.  */
+            if (_tx_thread_execute_ptr == TX_NULL)
+            {
+
+                /* Yes, increment the return to idle return count.  */
+                _tx_thread_performance_idle_return_count++;
+            }
+            else
+            {
+
+                /* No, there is another thread ready to run and will be scheduled upon return.  */
+                _tx_thread_performance_non_idle_return_count++;
+            }
+#endif
+
+            /* Preemption is needed - return to the system!  */
+            _tx_thread_system_return();
+        }
+    }
+
+    /* Return to caller.  */
+    return;
+}
+#endif
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_time_slice.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_time_slice.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_time_slice.c	(revision 54)
@@ -0,0 +1,190 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Thread                                                              */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+#ifndef TX_NO_TIMER
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_timer.h"
+#include "tx_thread.h"
+#include "tx_trace.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_thread_time_slice                               PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function moves the currently executing thread to the end of    */
+/*    the threads ready at the same priority level as a result of a       */
+/*    time-slice interrupt.  If no other thread of the same priority is   */
+/*    ready, this function simply returns.                                */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_timer_interrupt                   Timer interrupt handling      */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Scott Larson             Modified comment(s), and      */
+/*                                            opt out of function when    */
+/*                                            TX_NO_TIMER is defined,     */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_thread_time_slice(VOID)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+TX_THREAD       *thread_ptr;
+#ifdef TX_ENABLE_STACK_CHECKING
+TX_THREAD       *next_thread_ptr;
+#endif
+#ifdef TX_ENABLE_EVENT_TRACE
+ULONG           system_state;
+UINT            preempt_disable;
+#endif
+
+    /* Pickup thread pointer.  */
+    TX_THREAD_GET_CURRENT(thread_ptr)
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+    /* Check this thread's stack.  */
+    TX_THREAD_STACK_CHECK(thread_ptr)
+
+    /* Set the next thread pointer to NULL.  */
+    next_thread_ptr =  TX_NULL;
+#endif
+
+    /* Lockout interrupts while the time-slice is evaluated.  */
+    TX_DISABLE
+
+    /* Clear the expired time-slice flag.  */
+    _tx_timer_expired_time_slice =  TX_FALSE;
+
+    /* Make sure the thread pointer is valid.  */
+    if (thread_ptr != TX_NULL)
+    {
+
+        /* Make sure the thread is still active, i.e. not suspended.  */
+        if (thread_ptr -> tx_thread_state == TX_READY)
+        {
+
+            /* Setup a fresh time-slice for the thread.  */
+            thread_ptr -> tx_thread_time_slice =  thread_ptr -> tx_thread_new_time_slice;
+
+            /* Reset the actual time-slice variable.  */
+            _tx_timer_time_slice =  thread_ptr -> tx_thread_time_slice;
+
+            /* Determine if there is another thread at the same priority and preemption-threshold
+               is not set.  Preemption-threshold overrides time-slicing.  */
+            if (thread_ptr -> tx_thread_ready_next != thread_ptr)
+            {
+
+                /* Check to see if preemption-threshold is not being used.  */
+                if (thread_ptr -> tx_thread_priority == thread_ptr -> tx_thread_preempt_threshold)
+                {
+
+                    /* Preemption-threshold is not being used by this thread.  */
+
+                    /* There is another thread at this priority, make it the highest at
+                       this priority level.  */
+                    _tx_thread_priority_list[thread_ptr -> tx_thread_priority] =  thread_ptr -> tx_thread_ready_next;
+
+                    /* Designate the highest priority thread as the one to execute.  Don't use this
+                       thread's priority as an index just in case a higher priority thread is now
+                       ready!  */
+                    _tx_thread_execute_ptr =  _tx_thread_priority_list[_tx_thread_highest_priority];
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+                    /* Increment the thread's time-slice counter.  */
+                    thread_ptr -> tx_thread_performance_time_slice_count++;
+
+                    /* Increment the total number of thread time-slice operations.  */
+                    _tx_thread_performance_time_slice_count++;
+#endif
+
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+                    /* Pickup the next execute pointer.  */
+                    next_thread_ptr =  _tx_thread_execute_ptr;
+#endif
+                }
+            }
+        }
+    }
+
+#ifdef TX_ENABLE_EVENT_TRACE
+
+    /* Pickup the volatile information.  */
+    system_state =  TX_THREAD_GET_SYSTEM_STATE();
+    preempt_disable =  _tx_thread_preempt_disable;
+
+    /* Insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_TIME_SLICE, _tx_thread_execute_ptr, system_state, preempt_disable, TX_POINTER_TO_ULONG_CONVERT(&thread_ptr), TX_TRACE_INTERNAL_EVENTS)
+#endif
+
+    /* Restore previous interrupt posture.  */
+    TX_RESTORE
+
+#ifdef TX_ENABLE_STACK_CHECKING
+
+    /* Determine if there is a next thread pointer to perform stack checking on.  */
+    if (next_thread_ptr != TX_NULL)
+    {
+
+        /* Yes, check this thread's stack.  */
+        TX_THREAD_STACK_CHECK(next_thread_ptr)
+    }
+#endif
+}
+
+#endif
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_timeout.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_timeout.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_thread_timeout.c	(revision 54)
@@ -0,0 +1,167 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Thread                                                              */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_thread_timeout                                  PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function handles thread timeout processing.  Timeouts occur in */
+/*    two flavors, namely the thread sleep timeout and all other service  */
+/*    call timeouts.  Thread sleep timeouts are processed locally, while  */
+/*    the others are processed by the appropriate suspension clean-up     */
+/*    service.                                                            */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    timeout_input                         Contains the thread pointer   */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    Suspension Cleanup Functions                                        */
+/*    _tx_thread_system_resume          Resume thread                     */
+/*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_timer_expiration_process          Timer expiration function     */
+/*    _tx_timer_thread_entry                Timer thread function         */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_thread_timeout(ULONG timeout_input)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+TX_THREAD       *thread_ptr;
+VOID            (*suspend_cleanup)(struct TX_THREAD_STRUCT *suspend_thread_ptr, ULONG suspension_sequence);
+ULONG           suspension_sequence;
+
+
+    /* Pickup the thread pointer.  */
+    TX_THREAD_TIMEOUT_POINTER_SETUP(thread_ptr)
+
+    /* Disable interrupts.  */
+    TX_DISABLE
+
+    /* Determine how the thread is currently suspended.  */
+    if (thread_ptr -> tx_thread_state == TX_SLEEP)
+    {
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+        /* Resume the thread!  */
+        _tx_thread_system_ni_resume(thread_ptr);
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+#else
+
+        /* Increment the disable preemption flag.  */
+        _tx_thread_preempt_disable++;
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Lift the suspension on the sleeping thread.  */
+        _tx_thread_system_resume(thread_ptr);
+#endif
+    }
+    else
+    {
+
+        /* Process all other suspension timeouts.  */
+
+#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
+
+        /* Increment the total number of thread timeouts.  */
+        _tx_thread_performance_timeout_count++;
+
+        /* Increment the number of timeouts for this thread.  */
+        thread_ptr -> tx_thread_performance_timeout_count++;
+#endif
+
+        /* Pickup the cleanup routine address.  */
+        suspend_cleanup =  thread_ptr -> tx_thread_suspend_cleanup;
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+        /* Pickup the suspension sequence number that is used later to verify that the
+           cleanup is still necessary.  */
+        suspension_sequence =  thread_ptr -> tx_thread_suspension_sequence;
+#else
+
+        /* When not interruptable is selected, the suspension sequence is not used - just set to 0.  */
+        suspension_sequence =  ((ULONG) 0);
+#endif
+
+#ifndef TX_NOT_INTERRUPTABLE
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+#endif
+
+        /* Call any cleanup routines.  */
+        if (suspend_cleanup != TX_NULL)
+        {
+
+            /* Yes, there is a function to call.  */
+            (suspend_cleanup)(thread_ptr, suspension_sequence);
+        }
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+#endif
+    }
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_time_get.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_time_get.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_time_get.c	(revision 54)
@@ -0,0 +1,104 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Timer                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_trace.h"
+#include "tx_timer.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_time_get                                        PORTABLE C      */
+/*                                                           6.1.3        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function retrieves the internal, free-running, system clock    */
+/*    and returns it to the caller.                                       */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    _tx_timer_system_clock            Returns the system clock value    */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*  12-31-2020     Andres Mlinar            Modified comment(s),          */
+/*                                            resulting in version 6.1.3  */
+/*                                                                        */
+/**************************************************************************/
+ULONG  _tx_time_get(VOID)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+#ifdef TX_ENABLE_EVENT_TRACE
+ULONG   another_temp_time =  ((ULONG) 0);
+#endif
+ULONG   temp_time;
+
+
+    /* Disable interrupts.  */
+    TX_DISABLE
+
+    /* Pickup the system clock time.  */
+    temp_time =  _tx_timer_system_clock;
+
+    /* If trace is enabled, insert this event into the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_TIME_GET, TX_ULONG_TO_POINTER_CONVERT(temp_time), TX_POINTER_TO_ULONG_CONVERT(&another_temp_time), 0, 0, TX_TRACE_TIME_EVENTS)
+
+    /* Log this kernel call.  */
+    TX_EL_TIME_GET_INSERT
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Return the time.  */
+    return(temp_time);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_create.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_create.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_create.c	(revision 54)
@@ -0,0 +1,169 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Timer                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_trace.h"
+#include "tx_timer.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_timer_create                                    PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function creates an application timer from the specified       */
+/*    input.                                                              */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    timer_ptr                         Pointer to timer control block    */
+/*    name_ptr                          Pointer to timer name             */
+/*    expiration_function               Application expiration function   */
+/*    initial_ticks                     Initial expiration ticks          */
+/*    reschedule_ticks                  Reschedule ticks                  */
+/*    auto_activate                     Automatic activation flag         */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_SUCCESS                        Successful completion status      */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_timer_system_activate         Timer activation function         */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _tx_timer_create(TX_TIMER *timer_ptr, CHAR *name_ptr,
+            VOID (*expiration_function)(ULONG id), ULONG expiration_input,
+            ULONG initial_ticks, ULONG reschedule_ticks, UINT auto_activate)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+TX_TIMER        *next_timer;
+TX_TIMER        *previous_timer;
+
+
+    /* Initialize timer control block to all zeros.  */
+    TX_MEMSET(timer_ptr, 0, (sizeof(TX_TIMER)));
+
+    /* Setup the basic timer fields.  */
+    timer_ptr -> tx_timer_name =                                            name_ptr;
+    timer_ptr -> tx_timer_internal.tx_timer_internal_remaining_ticks =      initial_ticks;
+    timer_ptr -> tx_timer_internal.tx_timer_internal_re_initialize_ticks =  reschedule_ticks;
+    timer_ptr -> tx_timer_internal.tx_timer_internal_timeout_function =     expiration_function;
+    timer_ptr -> tx_timer_internal.tx_timer_internal_timeout_param =        expiration_input;
+
+    /* Disable interrupts to put the timer on the created list.  */
+    TX_DISABLE
+
+    /* Setup the timer ID to make it valid.  */
+    timer_ptr -> tx_timer_id =  TX_TIMER_ID;
+
+    /* Place the timer on the list of created application timers.  First,
+       check for an empty list.  */
+    if (_tx_timer_created_count == TX_EMPTY)
+    {
+
+        /* The created timer list is empty.  Add timer to empty list.  */
+        _tx_timer_created_ptr =                   timer_ptr;
+        timer_ptr -> tx_timer_created_next =      timer_ptr;
+        timer_ptr -> tx_timer_created_previous =  timer_ptr;
+    }
+    else
+    {
+
+        /* This list is not NULL, add to the end of the list.  */
+        next_timer =  _tx_timer_created_ptr;
+        previous_timer =  next_timer -> tx_timer_created_previous;
+
+        /* Place the new timer in the list.  */
+        next_timer -> tx_timer_created_previous =  timer_ptr;
+        previous_timer -> tx_timer_created_next =    timer_ptr;
+
+        /* Setup this timer's created links.  */
+        timer_ptr -> tx_timer_created_previous =  previous_timer;
+        timer_ptr -> tx_timer_created_next =      next_timer;
+    }
+
+    /* Increment the number of created timers.  */
+    _tx_timer_created_count++;
+
+    /* Optional timer create extended processing.  */
+    TX_TIMER_CREATE_EXTENSION(timer_ptr)
+
+    /* If trace is enabled, register this object.  */
+    TX_TRACE_OBJECT_REGISTER(TX_TRACE_OBJECT_TYPE_TIMER, timer_ptr, name_ptr, initial_ticks, reschedule_ticks)
+
+    /* If trace is enabled, insert this call in the trace buffer.  */
+    TX_TRACE_IN_LINE_INSERT(TX_TRACE_TIMER_CREATE, timer_ptr, initial_ticks, reschedule_ticks, auto_activate, TX_TRACE_TIMER_EVENTS)
+
+    /* Log this kernel call.  */
+    TX_EL_TIMER_CREATE_INSERT
+
+    /* Determine if this timer needs to be activated.  */
+    if (auto_activate == TX_AUTO_ACTIVATE)
+    {
+
+#ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
+
+        /* Increment the total activations counter.  */
+        _tx_timer_performance_activate_count++;
+
+        /* Increment the number of activations on this timer.  */
+        timer_ptr -> tx_timer_performance_activate_count++;
+#endif
+
+        /* Call actual activation function.  */
+        _tx_timer_system_activate(&(timer_ptr -> tx_timer_internal));
+    }
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Return TX_SUCCESS.  */
+    return(TX_SUCCESS);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_expiration_process.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_expiration_process.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_expiration_process.c	(revision 54)
@@ -0,0 +1,483 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Timer                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+#ifndef TX_NO_TIMER
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_timer.h"
+#include "tx_thread.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_timer_expiration_process                        PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function processes thread and application timer expirations.   */
+/*    It is called from the _tx_timer_interrupt handler and either        */
+/*    processes the timer expiration in the ISR or defers to the system   */
+/*    timer thread. The actual processing is determined during            */
+/*    compilation.                                                        */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_system_resume          Thread resume processing          */
+/*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
+/*    _tx_timer_system_activate         Timer reactivate processing       */
+/*    Timer Expiration Function                                           */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_timer_interrupt               Timer interrupt handler           */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Scott Larson             Modified comment(s), and      */
+/*                                            opt out of function when    */
+/*                                            TX_NO_TIMER is defined,     */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_timer_expiration_process(VOID)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+#ifdef TX_TIMER_PROCESS_IN_ISR
+
+TX_TIMER_INTERNAL           *expired_timers;
+TX_TIMER_INTERNAL           *reactivate_timer;
+TX_TIMER_INTERNAL           *next_timer;
+TX_TIMER_INTERNAL           *previous_timer;
+#ifdef TX_REACTIVATE_INLINE
+TX_TIMER_INTERNAL           **timer_list;               /* Timer list pointer           */
+UINT                        expiration_time;            /* Value used for pointer offset*/
+ULONG                       delta;
+#endif
+TX_TIMER_INTERNAL           *current_timer;
+VOID                        (*timeout_function)(ULONG id);
+ULONG                       timeout_param =  ((ULONG) 0);
+#ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
+TX_TIMER                    *timer_ptr;
+#endif
+#endif
+
+#ifndef TX_TIMER_PROCESS_IN_ISR
+
+    /* Don't process in the ISR, wakeup the system timer thread to process the
+       timer expiration.  */
+
+    /* Disable interrupts.  */
+    TX_DISABLE
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+    /* Resume the thread!  */
+    _tx_thread_system_ni_resume(&_tx_timer_thread);
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+#else
+
+    /* Increment the preempt disable flag.  */
+    _tx_thread_preempt_disable++;
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+
+    /* Call the system resume function to activate the timer thread.  */
+    _tx_thread_system_resume(&_tx_timer_thread);
+#endif
+
+#else
+
+    /* Process the timer expiration directly in the ISR. This increases the interrupt
+       processing, however, it eliminates the need for a system timer thread and associated
+       resources.  */
+
+    /* Disable interrupts.  */
+    TX_DISABLE
+
+    /* Determine if the timer processing is already active.  This needs to be checked outside
+       of the processing loop because it remains set throughout nested timer interrupt conditions.  */
+    if (_tx_timer_processing_active == TX_FALSE)
+    {
+
+        /* Timer processing is not nested.  */
+
+        /* Determine if the timer expiration has already been cleared.  */
+        if (_tx_timer_expired != ((UINT) 0))
+        {
+
+            /* Proceed with timer processing.  */
+
+            /* Set the timer interrupt processing active flag.  */
+            _tx_timer_processing_active =  TX_TRUE;
+
+            /* Now go into an infinite loop to process timer expirations.  */
+            do
+            {
+
+                /* First, move the current list pointer and clear the timer
+                   expired value.  This allows the interrupt handling portion
+                   to continue looking for timer expirations.  */
+
+                /* Save the current timer expiration list pointer.  */
+                expired_timers =  *_tx_timer_current_ptr;
+
+                /* Modify the head pointer in the first timer in the list, if there
+                   is one!  */
+                if (expired_timers != TX_NULL)
+                {
+
+                    expired_timers -> tx_timer_internal_list_head =  &expired_timers;
+                }
+
+                /* Set the current list pointer to NULL.  */
+                *_tx_timer_current_ptr =  TX_NULL;
+
+                /* Move the current pointer up one timer entry wrap if we get to
+                   the end of the list.  */
+                _tx_timer_current_ptr =  TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, 1);
+                if (_tx_timer_current_ptr == _tx_timer_list_end)
+                {
+
+                    _tx_timer_current_ptr =  _tx_timer_list_start;
+                }
+
+                /* Clear the expired flag.  */
+                _tx_timer_expired =  TX_FALSE;
+
+                /* Restore interrupts temporarily.  */
+                TX_RESTORE
+
+                /* Disable interrupts again.  */
+                TX_DISABLE
+
+                /* Next, process the expiration of the associated timers at this
+                   time slot.  */
+                while (expired_timers != TX_NULL)
+                {
+
+                    /* Something is on the list.  Remove it and process the expiration.  */
+                    current_timer =  expired_timers;
+
+                    /* Pickup the next timer.  */
+                    next_timer =  expired_timers -> tx_timer_internal_active_next;
+
+                    /* Set the reactivate timer to NULL.  */
+                    reactivate_timer =  TX_NULL;
+
+                    /* Determine if this is the only timer.  */
+                    if (current_timer == next_timer)
+                    {
+
+                        /* Yes, this is the only timer in the list.  */
+
+                        /* Set the head pointer to NULL.  */
+                        expired_timers =  TX_NULL;
+                    }
+                    else
+                    {
+
+                        /* No, not the only expired timer.  */
+
+                        /* Remove this timer from the expired list.  */
+                        previous_timer =                                   current_timer -> tx_timer_internal_active_previous;
+                        next_timer -> tx_timer_internal_active_previous =  previous_timer;
+                        previous_timer -> tx_timer_internal_active_next =  next_timer;
+
+                        /* Modify the next timer's list head to point at the current list head.  */
+                        next_timer -> tx_timer_internal_list_head =  &expired_timers;
+
+                        /* Set the list head pointer.  */
+                        expired_timers =  next_timer;
+                    }
+
+                    /* In any case, the timer is now off of the expired list.  */
+
+                    /* Determine if the timer has expired or if it is just a really
+                       big timer that needs to be placed in the list again.  */
+                    if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES)
+                    {
+
+                        /* Timer is bigger than the timer entries and must be
+                           rescheduled.  */
+
+#ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
+
+                        /* Increment the total expiration adjustments counter.  */
+                        _tx_timer_performance__expiration_adjust_count++;
+
+                        /* Determine if this is an application timer.  */
+                        if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
+                        {
+
+                            /* Derive the application timer pointer.  */
+
+                            /* Pickup the application timer pointer.  */
+                            TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
+
+                            /* Increment the number of expiration adjustments on this timer.  */
+                            if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
+                            {
+
+                                timer_ptr -> tx_timer_performance__expiration_adjust_count++;
+                            }
+                        }
+#endif
+
+                        /* Decrement the remaining ticks of the timer.  */
+                        current_timer -> tx_timer_internal_remaining_ticks =
+                                current_timer -> tx_timer_internal_remaining_ticks - TX_TIMER_ENTRIES;
+
+                        /* Set the timeout function to NULL in order to bypass the
+                           expiration.  */
+                        timeout_function =  TX_NULL;
+
+                        /* Make the timer appear that it is still active while interrupts
+                           are enabled.  This will permit proper processing of a timer
+                           deactivate from an ISR.  */
+                        current_timer -> tx_timer_internal_list_head =    &reactivate_timer;
+                        current_timer -> tx_timer_internal_active_next =  current_timer;
+
+                        /* Setup the temporary timer list head pointer.  */
+                        reactivate_timer =  current_timer;
+                    }
+                    else
+                    {
+
+                        /* Timer did expire.  */
+
+#ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
+
+                        /* Increment the total expirations counter.  */
+                        _tx_timer_performance_expiration_count++;
+
+                        /* Determine if this is an application timer.  */
+                        if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
+                        {
+
+                            /* Derive the application timer pointer.  */
+
+                            /* Pickup the application timer pointer.  */
+                            TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
+
+                            /* Increment the number of expirations on this timer.  */
+                            if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
+                            {
+
+                                timer_ptr -> tx_timer_performance_expiration_count++;
+                            }
+                        }
+#endif
+
+                        /* Copy the calling function and ID into local variables before interrupts
+                           are re-enabled.  */
+                        timeout_function =  current_timer -> tx_timer_internal_timeout_function;
+                        timeout_param =     current_timer -> tx_timer_internal_timeout_param;
+
+                        /* Copy the reinitialize ticks into the remaining ticks.  */
+                        current_timer -> tx_timer_internal_remaining_ticks =  current_timer -> tx_timer_internal_re_initialize_ticks;
+
+                        /* Determine if the timer should be reactivated.  */
+                        if (current_timer -> tx_timer_internal_remaining_ticks != ((ULONG) 0))
+                        {
+
+                            /* Make the timer appear that it is still active while processing
+                               the expiration routine and with interrupts enabled.  This will
+                               permit proper processing of a timer deactivate from both the
+                               expiration routine and an ISR.  */
+                            current_timer -> tx_timer_internal_list_head =    &reactivate_timer;
+                            current_timer -> tx_timer_internal_active_next =  current_timer;
+
+                            /* Setup the temporary timer list head pointer.  */
+                            reactivate_timer =  current_timer;
+                        }
+                        else
+                        {
+
+                            /* Set the list pointer of this timer to NULL.  This is used to indicate
+                               the timer is no longer active.  */
+                            current_timer -> tx_timer_internal_list_head =  TX_NULL;
+                        }
+                    }
+
+                    /* Set pointer to indicate the expired timer that is currently being processed.  */
+                    _tx_timer_expired_timer_ptr =  current_timer;
+
+                    /* Restore interrupts for timer expiration call.  */
+                    TX_RESTORE
+
+                    /* Call the timer-expiration function, if non-NULL.  */
+                    if (timeout_function != TX_NULL)
+                    {
+
+                        (timeout_function) (timeout_param);
+                    }
+
+                    /* Lockout interrupts again.  */
+                    TX_DISABLE
+
+                    /* Clear expired timer pointer.  */
+                    _tx_timer_expired_timer_ptr =  TX_NULL;
+
+                    /* Determine if the timer needs to be reactivated.  */
+                    if (reactivate_timer == current_timer)
+                    {
+
+                        /* Reactivate the timer.  */
+
+#ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
+
+                        /* Determine if this timer expired.  */
+                        if (timeout_function != TX_NULL)
+                        {
+
+                            /* Increment the total reactivations counter.  */
+                            _tx_timer_performance_reactivate_count++;
+
+                            /* Determine if this is an application timer.  */
+                            if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
+                            {
+
+                                /* Derive the application timer pointer.  */
+
+                                /* Pickup the application timer pointer.  */
+                                TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
+
+                                /* Increment the number of expirations on this timer.  */
+                                if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
+                                {
+
+                                    timer_ptr -> tx_timer_performance_reactivate_count++;
+                                }
+                            }
+                        }
+#endif
+
+
+#ifdef TX_REACTIVATE_INLINE
+
+                        /* Calculate the amount of time remaining for the timer.  */
+                        if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES)
+                        {
+
+                            /* Set expiration time to the maximum number of entries.  */
+                            expiration_time =  TX_TIMER_ENTRIES - ((UINT) 1);
+                        }
+                        else
+                        {
+
+                            /* Timer value fits in the timer entries.  */
+
+                            /* Set the expiration time.  */
+                            expiration_time =  ((UINT) current_timer -> tx_timer_internal_remaining_ticks) - ((UINT) 1);
+                        }
+
+                        /* At this point, we are ready to put the timer back on one of
+                           the timer lists.  */
+
+                        /* Calculate the proper place for the timer.  */
+                        timer_list =  TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, expiration_time);
+                        if (TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(timer_list) >= TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(_tx_timer_list_end))
+                        {
+
+                            /* Wrap from the beginning of the list.  */
+                            delta =  TX_TIMER_POINTER_DIF(timer_list, _tx_timer_list_end);
+                            timer_list =  TX_TIMER_POINTER_ADD(_tx_timer_list_start, delta);
+                        }
+
+                        /* Now put the timer on this list.  */
+                        if ((*timer_list) == TX_NULL)
+                        {
+
+                            /* This list is NULL, just put the new timer on it.  */
+
+                            /* Setup the links in this timer.  */
+                            current_timer -> tx_timer_internal_active_next =      current_timer;
+                            current_timer -> tx_timer_internal_active_previous =  current_timer;
+
+                            /* Setup the list head pointer.  */
+                            *timer_list =  current_timer;
+                        }
+                        else
+                        {
+
+                            /* This list is not NULL, add current timer to the end. */
+                            next_timer =                                          *timer_list;
+                            previous_timer =                                      next_timer -> tx_timer_internal_active_previous;
+                            previous_timer -> tx_timer_internal_active_next =     current_timer;
+                            next_timer -> tx_timer_internal_active_previous =     current_timer;
+                            current_timer -> tx_timer_internal_active_next =      next_timer;
+                            current_timer -> tx_timer_internal_active_previous =  previous_timer;
+                        }
+
+                        /* Setup list head pointer.  */
+                        current_timer -> tx_timer_internal_list_head =  timer_list;
+#else
+
+                        /* Reactivate through the timer activate function.  */
+
+                        /* Clear the list head for the timer activate call.  */
+                        current_timer -> tx_timer_internal_list_head = TX_NULL;
+
+                        /* Activate the current timer.  */
+                        _tx_timer_system_activate(current_timer);
+#endif
+                    }
+                }
+            } while (_tx_timer_expired != TX_FALSE);
+
+            /* Clear the timer interrupt processing active flag.  */
+            _tx_timer_processing_active =  TX_FALSE;
+        }
+    }
+
+    /* Restore interrupts.  */
+    TX_RESTORE
+#endif
+}
+
+#endif
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_initialize.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_initialize.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_initialize.c	(revision 54)
@@ -0,0 +1,306 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Timer                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+
+
+/* Check for the TX_NO_TIMER option. When defined, do not define all of the
+   timer component global variables.  */
+
+#ifndef TX_NO_TIMER
+
+
+/* Define the system clock value that is continually incremented by the
+   periodic timer interrupt processing.  */
+
+volatile ULONG      _tx_timer_system_clock;
+
+
+/* Define the time-slice expiration flag.  This is used to indicate that a time-slice
+   has happened.  */
+
+UINT                _tx_timer_expired_time_slice;
+
+
+/* Define the thread and application timer entry list.  This list provides a direct access
+   method for insertion of times less than TX_TIMER_ENTRIES.  */
+
+TX_TIMER_INTERNAL   *_tx_timer_list[TX_TIMER_ENTRIES];
+
+
+/* Define the boundary pointers to the list.  These are setup to easily manage
+   wrapping the list.  */
+
+TX_TIMER_INTERNAL   **_tx_timer_list_start;
+TX_TIMER_INTERNAL   **_tx_timer_list_end;
+
+
+/* Define the current timer pointer in the list.  This pointer is moved sequentially
+   through the timer list by the timer interrupt handler.  */
+
+TX_TIMER_INTERNAL   **_tx_timer_current_ptr;
+
+
+/* Define the timer expiration flag.  This is used to indicate that a timer
+   has expired.  */
+
+UINT                _tx_timer_expired;
+
+
+/* Define the created timer list head pointer.  */
+
+TX_TIMER            *_tx_timer_created_ptr;
+
+
+/* Define the created timer count.  */
+
+ULONG               _tx_timer_created_count;
+
+
+/* Define the pointer to the timer that has expired and is being processed.  */
+
+TX_TIMER_INTERNAL  *_tx_timer_expired_timer_ptr;
+
+
+#ifndef TX_TIMER_PROCESS_IN_ISR
+
+/* Define the timer thread's control block.  */
+
+TX_THREAD           _tx_timer_thread;
+
+
+/* Define the variable that holds the timer thread's starting stack address.  */
+
+VOID                *_tx_timer_stack_start;
+
+
+/* Define the variable that holds the timer thread's stack size.  */
+
+ULONG               _tx_timer_stack_size;
+
+
+/* Define the variable that holds the timer thread's priority.  */
+
+UINT                _tx_timer_priority;
+
+/* Define the system timer thread's stack.   The default size is defined
+   in tx_port.h.  */
+
+ULONG               _tx_timer_thread_stack_area[(((UINT) TX_TIMER_THREAD_STACK_SIZE)+((sizeof(ULONG))- ((UINT) 1)))/(sizeof(ULONG))];
+
+#else
+
+
+/* Define the busy flag that will prevent nested timer ISR processing.  */
+
+UINT                _tx_timer_processing_active;
+
+#endif
+
+#ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
+
+/* Define the total number of timer activations.  */
+
+ULONG               _tx_timer_performance_activate_count;
+
+
+/* Define the total number of timer reactivations.  */
+
+ULONG               _tx_timer_performance_reactivate_count;
+
+
+/* Define the total number of timer deactivations.  */
+
+ULONG               _tx_timer_performance_deactivate_count;
+
+
+/* Define the total number of timer expirations.  */
+
+ULONG               _tx_timer_performance_expiration_count;
+
+
+/* Define the total number of timer expiration adjustments. These are required
+   if the expiration time is greater than the size of the timer list. In such
+   cases, the timer is placed at the end of the list and then reactivated
+   as many times as necessary to finally achieve the resulting timeout. */
+
+ULONG               _tx_timer_performance__expiration_adjust_count;
+
+#endif
+#endif
+
+
+/* Define the current time slice value.  If non-zero, a time-slice is active.
+   Otherwise, the time_slice is not active.  */
+
+ULONG               _tx_timer_time_slice;
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_timer_initialize                                PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function initializes the various control data structures for   */
+/*    the clock control component.                                        */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_create                 Create the system timer thread    */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_initialize_high_level         High level initialization         */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_timer_initialize(VOID)
+{
+#ifndef TX_NO_TIMER
+#ifndef TX_TIMER_PROCESS_IN_ISR
+UINT    status;
+#endif
+
+#ifndef TX_DISABLE_REDUNDANT_CLEARING
+
+    /* Initialize the system clock to 0.  */
+    _tx_timer_system_clock =  ((ULONG) 0);
+
+    /* Initialize the time-slice value to 0 to make sure it is disabled.  */
+    _tx_timer_time_slice =  ((ULONG) 0);
+
+    /* Clear the expired flags.  */
+    _tx_timer_expired_time_slice =  TX_FALSE;
+    _tx_timer_expired =             TX_FALSE;
+
+    /* Set the currently expired timer being processed pointer to NULL.  */
+    _tx_timer_expired_timer_ptr =  TX_NULL;
+
+    /* Initialize the thread and application timer management control structures.  */
+
+    /* First, initialize the timer list.  */
+    TX_MEMSET(&_tx_timer_list[0], 0, (sizeof(_tx_timer_list)));
+#endif
+
+    /* Initialize all of the list pointers.  */
+    _tx_timer_list_start =   &_tx_timer_list[0];
+    _tx_timer_current_ptr =  &_tx_timer_list[0];
+
+    /* Set the timer list end pointer to one past the actual timer list.  This is done
+       to make the timer interrupt handling in assembly language a little easier.  */
+    _tx_timer_list_end =     &_tx_timer_list[TX_TIMER_ENTRIES-((ULONG) 1)];
+    _tx_timer_list_end =     TX_TIMER_POINTER_ADD(_tx_timer_list_end, ((ULONG) 1));
+
+#ifndef TX_TIMER_PROCESS_IN_ISR
+
+    /* Setup the variables associated with the system timer thread's stack and
+       priority.  */
+    _tx_timer_stack_start =  (VOID *) &_tx_timer_thread_stack_area[0];
+    _tx_timer_stack_size =   ((ULONG) TX_TIMER_THREAD_STACK_SIZE);
+    _tx_timer_priority =     ((UINT) TX_TIMER_THREAD_PRIORITY);
+
+    /* Create the system timer thread.  This thread processes all of the timer
+       expirations and reschedules.  Its stack and priority are defined in the
+       low-level initialization component.  */
+    do
+    {
+
+        /* Create the system timer thread.  */
+        status =  _tx_thread_create(&_tx_timer_thread,
+                                    TX_CONST_CHAR_TO_CHAR_POINTER_CONVERT("System Timer Thread"),
+                                    _tx_timer_thread_entry,
+                                    ((ULONG) TX_TIMER_ID),
+                                    _tx_timer_stack_start, _tx_timer_stack_size,
+                                    _tx_timer_priority, _tx_timer_priority, TX_NO_TIME_SLICE, TX_DONT_START);
+
+#ifdef TX_SAFETY_CRITICAL
+
+        /* Check return from thread create - if an error is detected throw an exception.  */
+        if (status != TX_SUCCESS)
+        {
+
+            /* Raise safety critical exception.  */
+            TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, status);
+        }
+#endif
+
+        /* Define timer initialize extension.  */
+        TX_TIMER_INITIALIZE_EXTENSION(status)
+
+    } while (status != TX_SUCCESS);
+
+#else
+
+    /* Clear the timer interrupt processing active flag.  */
+    _tx_timer_processing_active =  TX_FALSE;
+#endif
+
+#ifndef TX_DISABLE_REDUNDANT_CLEARING
+
+    /* Initialize the head pointer of the created application timer list.  */
+    _tx_timer_created_ptr =  TX_NULL;
+
+    /* Set the created count to zero.  */
+    _tx_timer_created_count =  TX_EMPTY;
+
+#ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
+
+    /* Initialize timer performance counters.  */
+    _tx_timer_performance_activate_count =           ((ULONG) 0);
+    _tx_timer_performance_reactivate_count =         ((ULONG) 0);
+    _tx_timer_performance_deactivate_count =         ((ULONG) 0);
+    _tx_timer_performance_expiration_count =         ((ULONG) 0);
+    _tx_timer_performance__expiration_adjust_count =  ((ULONG) 0);
+#endif
+#endif
+#endif
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_system_activate.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_system_activate.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_system_activate.c	(revision 54)
@@ -0,0 +1,170 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Timer                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+#ifndef TX_NO_TIMER
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_timer.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_timer_system_activate                           PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function places the specified internal timer in the proper     */
+/*    place in the timer expiration list.  If the timer is already active */
+/*    this function does nothing.                                         */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    timer_ptr                         Pointer to timer control block    */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_SUCCESS                        Always returns success            */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_thread_system_suspend         Thread suspend function           */
+/*    _tx_thread_system_ni_suspend      Non-interruptable suspend thread  */
+/*    _tx_timer_thread_entry            Timer thread processing           */
+/*    _tx_timer_activate                Application timer activate        */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Scott Larson             Modified comment(s), and      */
+/*                                            opt out of function when    */
+/*                                            TX_NO_TIMER is defined,     */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_timer_system_activate(TX_TIMER_INTERNAL *timer_ptr)
+{
+
+TX_TIMER_INTERNAL           **timer_list;
+TX_TIMER_INTERNAL           *next_timer;
+TX_TIMER_INTERNAL           *previous_timer;
+ULONG                       delta;
+ULONG                       remaining_ticks;
+ULONG                       expiration_time;
+
+
+    /* Pickup the remaining ticks.  */
+    remaining_ticks =  timer_ptr -> tx_timer_internal_remaining_ticks;
+
+    /* Determine if there is a timer to activate.  */
+    if (remaining_ticks != ((ULONG) 0))
+    {
+
+        /* Determine if the timer is set to wait forever.  */
+        if (remaining_ticks != TX_WAIT_FOREVER)
+        {
+
+            /* Valid timer activate request.  */
+
+            /* Determine if the timer still needs activation.  */
+            if (timer_ptr -> tx_timer_internal_list_head == TX_NULL)
+            {
+
+                /* Activate the timer.  */
+
+                /* Calculate the amount of time remaining for the timer.  */
+                if (remaining_ticks > TX_TIMER_ENTRIES)
+                {
+
+                    /* Set expiration time to the maximum number of entries.  */
+                    expiration_time =  TX_TIMER_ENTRIES - ((ULONG) 1);
+                }
+                else
+                {
+
+                    /* Timer value fits in the timer entries.  */
+
+                    /* Set the expiration time.  */
+                    expiration_time =  (remaining_ticks - ((ULONG) 1));
+                }
+
+                /* At this point, we are ready to put the timer on one of
+                   the timer lists.  */
+
+                /* Calculate the proper place for the timer.  */
+                timer_list =  TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, expiration_time);
+                if (TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(timer_list) >= TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(_tx_timer_list_end))
+                {
+
+                    /* Wrap from the beginning of the list.  */
+                    delta =  TX_TIMER_POINTER_DIF(timer_list, _tx_timer_list_end);
+                    timer_list =  TX_TIMER_POINTER_ADD(_tx_timer_list_start, delta);
+                }
+
+                /* Now put the timer on this list.  */
+                if ((*timer_list) == TX_NULL)
+                {
+
+                    /* This list is NULL, just put the new timer on it.  */
+
+                    /* Setup the links in this timer.  */
+                    timer_ptr -> tx_timer_internal_active_next =      timer_ptr;
+                    timer_ptr -> tx_timer_internal_active_previous =  timer_ptr;
+
+                    /* Setup the list head pointer.  */
+                    *timer_list =  timer_ptr;
+                }
+                else
+                {
+
+                    /* This list is not NULL, add current timer to the end. */
+                    next_timer =                                        *timer_list;
+                    previous_timer =                                    next_timer -> tx_timer_internal_active_previous;
+                    previous_timer -> tx_timer_internal_active_next =   timer_ptr;
+                    next_timer -> tx_timer_internal_active_previous =   timer_ptr;
+                    timer_ptr -> tx_timer_internal_active_next =        next_timer;
+                    timer_ptr -> tx_timer_internal_active_previous =    previous_timer;
+                }
+
+                /* Setup list head pointer.  */
+                timer_ptr -> tx_timer_internal_list_head =  timer_list;
+            }
+        }
+    }
+}
+
+#endif
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_system_deactivate.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_system_deactivate.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_system_deactivate.c	(revision 54)
@@ -0,0 +1,134 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Timer                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_timer.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_timer_system_deactivate                         PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function deactivates, or removes the timer from the active     */
+/*    timer expiration list.  If the timer is already deactivated, this   */
+/*    function just returns.                                              */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    timer_ptr                         Pointer to timer control block    */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_SUCCESS                        Always returns success            */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    _tx_thread_system_resume          Thread resume function            */
+/*    _tx_timer_thread_entry            Timer thread processing           */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+VOID  _tx_timer_system_deactivate(TX_TIMER_INTERNAL *timer_ptr)
+{
+
+TX_TIMER_INTERNAL   **list_head;
+TX_TIMER_INTERNAL   *next_timer;
+TX_TIMER_INTERNAL   *previous_timer;
+
+
+    /* Pickup the list head pointer.  */
+    list_head =  timer_ptr -> tx_timer_internal_list_head;
+
+    /* Determine if the timer still needs deactivation.  */
+    if (list_head != TX_NULL)
+    {
+
+        /* Deactivate the timer.  */
+
+        /* Pickup the next active timer.  */
+        next_timer =  timer_ptr -> tx_timer_internal_active_next;
+
+        /* See if this is the only timer in the list.  */
+        if (timer_ptr == next_timer)
+        {
+
+            /* Yes, the only timer on the list.  */
+
+            /* Determine if the head pointer needs to be updated.  */
+            if (*(list_head) == timer_ptr)
+            {
+
+                /* Update the head pointer.  */
+                *(list_head) =  TX_NULL;
+            }
+        }
+        else
+        {
+
+            /* At least one more timer is on the same expiration list.  */
+
+            /* Update the links of the adjacent timers.  */
+            previous_timer =                                   timer_ptr -> tx_timer_internal_active_previous;
+            next_timer -> tx_timer_internal_active_previous =  previous_timer;
+            previous_timer -> tx_timer_internal_active_next =  next_timer;
+
+            /* Determine if the head pointer needs to be updated.  */
+            if (*(list_head) == timer_ptr)
+            {
+
+                /* Update the next timer in the list with the list head pointer.  */
+                next_timer -> tx_timer_internal_list_head =  list_head;
+
+                /* Update the head pointer.  */
+                *(list_head) =  next_timer;
+            }
+        }
+
+        /* Clear the timer's list head pointer.  */
+        timer_ptr -> tx_timer_internal_list_head =  TX_NULL;
+    }
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_thread_entry.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_thread_entry.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/tx_timer_thread_entry.c	(revision 54)
@@ -0,0 +1,482 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Timer                                                               */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_timer.h"
+#include "tx_thread.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _tx_timer_thread_entry                              PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function manages thread and application timer expirations.     */
+/*    Actually, from this thread's point of view, there is no difference. */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    timer_thread_input                Used just for verification        */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    None                                                                */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    Timer Expiration Function                                           */
+/*    _tx_thread_system_suspend         Thread suspension                 */
+/*    _tx_thread_system_ni_suspend      Non-interruptable suspend thread  */
+/*    _tx_timer_system_activate         Timer reactivate processing       */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    ThreadX Scheduler                                                   */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+#ifndef TX_TIMER_PROCESS_IN_ISR
+VOID  _tx_timer_thread_entry(ULONG timer_thread_input)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+TX_TIMER_INTERNAL           *expired_timers;
+TX_TIMER_INTERNAL           *reactivate_timer;
+TX_TIMER_INTERNAL           *next_timer;
+TX_TIMER_INTERNAL           *previous_timer;
+TX_TIMER_INTERNAL           *current_timer;
+VOID                        (*timeout_function)(ULONG id);
+ULONG                       timeout_param =  ((ULONG) 0);
+TX_THREAD                   *thread_ptr;
+#ifdef TX_REACTIVATE_INLINE
+TX_TIMER_INTERNAL           **timer_list;               /* Timer list pointer           */
+UINT                        expiration_time;            /* Value used for pointer offset*/
+ULONG                       delta;
+#endif
+#ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
+TX_TIMER                    *timer_ptr;
+#endif
+
+
+    /* Make sure the timer input is correct.  This also gets rid of the
+       silly compiler warnings.  */
+    if (timer_thread_input == TX_TIMER_ID)
+    {
+
+        /* Yes, valid thread entry, proceed...  */
+
+        /* Now go into an infinite loop to process timer expirations.  */
+        while (TX_LOOP_FOREVER)
+        {
+
+            /* First, move the current list pointer and clear the timer
+               expired value.  This allows the interrupt handling portion
+               to continue looking for timer expirations.  */
+            TX_DISABLE
+
+            /* Save the current timer expiration list pointer.  */
+            expired_timers =  *_tx_timer_current_ptr;
+
+            /* Modify the head pointer in the first timer in the list, if there
+               is one!  */
+            if (expired_timers != TX_NULL)
+            {
+
+                expired_timers -> tx_timer_internal_list_head =  &expired_timers;
+            }
+
+            /* Set the current list pointer to NULL.  */
+            *_tx_timer_current_ptr =  TX_NULL;
+
+            /* Move the current pointer up one timer entry wrap if we get to
+               the end of the list.  */
+            _tx_timer_current_ptr =  TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, 1);
+            if (_tx_timer_current_ptr == _tx_timer_list_end)
+            {
+
+                _tx_timer_current_ptr =  _tx_timer_list_start;
+            }
+
+            /* Clear the expired flag.  */
+            _tx_timer_expired =  TX_FALSE;
+
+            /* Restore interrupts temporarily.  */
+            TX_RESTORE
+
+            /* Disable interrupts again.  */
+            TX_DISABLE
+
+            /* Next, process the expiration of the associated timers at this
+               time slot.  */
+            while (expired_timers != TX_NULL)
+            {
+
+                /* Something is on the list.  Remove it and process the expiration.  */
+                current_timer =  expired_timers;
+
+                /* Pickup the next timer.  */
+                next_timer =  expired_timers -> tx_timer_internal_active_next;
+
+                /* Set the reactivate_timer to NULL.  */
+                reactivate_timer =  TX_NULL;
+
+                /* Determine if this is the only timer.  */
+                if (current_timer == next_timer)
+                {
+
+                    /* Yes, this is the only timer in the list.  */
+
+                    /* Set the head pointer to NULL.  */
+                    expired_timers =  TX_NULL;
+                }
+                else
+                {
+
+                    /* No, not the only expired timer.  */
+
+                    /* Remove this timer from the expired list.  */
+                    previous_timer =                                   current_timer -> tx_timer_internal_active_previous;
+                    next_timer -> tx_timer_internal_active_previous =  previous_timer;
+                    previous_timer -> tx_timer_internal_active_next =  next_timer;
+
+                    /* Modify the next timer's list head to point at the current list head.  */
+                    next_timer -> tx_timer_internal_list_head =  &expired_timers;
+
+                    /* Set the list head pointer.  */
+                    expired_timers =  next_timer;
+                }
+
+                /* In any case, the timer is now off of the expired list.  */
+
+                /* Determine if the timer has expired or if it is just a really
+                   big timer that needs to be placed in the list again.  */
+                if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES)
+                {
+
+                    /* Timer is bigger than the timer entries and must be
+                       rescheduled.  */
+
+#ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
+
+                    /* Increment the total expiration adjustments counter.  */
+                    _tx_timer_performance__expiration_adjust_count++;
+
+                    /* Determine if this is an application timer.  */
+                    if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
+                    {
+
+                        /* Derive the application timer pointer.  */
+
+                        /* Pickup the application timer pointer.  */
+                        TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
+
+                        /* Increment the number of expiration adjustments on this timer.  */
+                        if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
+                        {
+
+                            timer_ptr -> tx_timer_performance__expiration_adjust_count++;
+                        }
+                    }
+#endif
+
+                    /* Decrement the remaining ticks of the timer.  */
+                    current_timer -> tx_timer_internal_remaining_ticks =
+                            current_timer -> tx_timer_internal_remaining_ticks - TX_TIMER_ENTRIES;
+
+                    /* Set the timeout function to NULL in order to bypass the
+                       expiration.  */
+                    timeout_function =  TX_NULL;
+
+                    /* Make the timer appear that it is still active while interrupts
+                       are enabled.  This will permit proper processing of a timer
+                       deactivate from an ISR.  */
+                    current_timer -> tx_timer_internal_list_head =    &reactivate_timer;
+                    current_timer -> tx_timer_internal_active_next =  current_timer;
+
+                    /* Setup the temporary timer list head pointer.  */
+                    reactivate_timer =  current_timer;
+                }
+                else
+                {
+
+                    /* Timer did expire.  */
+
+#ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
+
+                    /* Increment the total expirations counter.  */
+                    _tx_timer_performance_expiration_count++;
+
+                    /* Determine if this is an application timer.  */
+                    if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
+                    {
+
+                        /* Derive the application timer pointer.  */
+
+                        /* Pickup the application timer pointer.  */
+                        TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
+
+                        /* Increment the number of expirations on this timer.  */
+                        if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
+                        {
+
+                            timer_ptr -> tx_timer_performance_expiration_count++;
+                        }
+                    }
+#endif
+
+                    /* Copy the calling function and ID into local variables before interrupts
+                       are re-enabled.  */
+                    timeout_function =  current_timer -> tx_timer_internal_timeout_function;
+                    timeout_param =     current_timer -> tx_timer_internal_timeout_param;
+
+                    /* Copy the reinitialize ticks into the remaining ticks.  */
+                    current_timer -> tx_timer_internal_remaining_ticks =  current_timer -> tx_timer_internal_re_initialize_ticks;
+
+                    /* Determine if the timer should be reactivated.  */
+                    if (current_timer -> tx_timer_internal_remaining_ticks != ((ULONG) 0))
+                    {
+
+                        /* Make the timer appear that it is still active while processing
+                           the expiration routine and with interrupts enabled.  This will
+                           permit proper processing of a timer deactivate from both the
+                           expiration routine and an ISR.  */
+                        current_timer -> tx_timer_internal_list_head =    &reactivate_timer;
+                        current_timer -> tx_timer_internal_active_next =  current_timer;
+
+                        /* Setup the temporary timer list head pointer.  */
+                        reactivate_timer =  current_timer;
+                    }
+                    else
+                    {
+
+                        /* Set the list pointer of this timer to NULL.  This is used to indicate
+                           the timer is no longer active.  */
+                        current_timer -> tx_timer_internal_list_head =  TX_NULL;
+                    }
+                }
+
+                /* Set pointer to indicate the expired timer that is currently being processed.  */
+                _tx_timer_expired_timer_ptr =  current_timer;
+
+                /* Restore interrupts for timer expiration call.  */
+                TX_RESTORE
+
+                /* Call the timer-expiration function, if non-NULL.  */
+                if (timeout_function != TX_NULL)
+                {
+
+                    (timeout_function) (timeout_param);
+                }
+
+                /* Lockout interrupts again.  */
+                TX_DISABLE
+
+                /* Clear expired timer pointer.  */
+                _tx_timer_expired_timer_ptr =  TX_NULL;
+
+                /* Determine if the timer needs to be reactivated.  */
+                if (reactivate_timer == current_timer)
+                {
+
+                    /* Reactivate the timer.  */
+
+#ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
+
+                    /* Determine if this timer expired.  */
+                    if (timeout_function != TX_NULL)
+                    {
+
+                        /* Increment the total reactivations counter.  */
+                        _tx_timer_performance_reactivate_count++;
+
+                        /* Determine if this is an application timer.  */
+                        if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
+                        {
+
+                            /* Derive the application timer pointer.  */
+
+                            /* Pickup the application timer pointer.  */
+                            TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
+
+                            /* Increment the number of expirations on this timer.  */
+                            if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
+                            {
+
+                                timer_ptr -> tx_timer_performance_reactivate_count++;
+                            }
+                        }
+                    }
+#endif
+
+#ifdef TX_REACTIVATE_INLINE
+
+                    /* Calculate the amount of time remaining for the timer.  */
+                    if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES)
+                    {
+
+                        /* Set expiration time to the maximum number of entries.  */
+                        expiration_time =  TX_TIMER_ENTRIES - ((UINT) 1);
+                    }
+                    else
+                    {
+
+                        /* Timer value fits in the timer entries.  */
+
+                        /* Set the expiration time.  */
+                        expiration_time =  ((UINT) current_timer -> tx_timer_internal_remaining_ticks) - ((UINT) 1);
+                    }
+
+                    /* At this point, we are ready to put the timer back on one of
+                       the timer lists.  */
+
+                    /* Calculate the proper place for the timer.  */
+                    timer_list =  TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, expiration_time);
+                    if (TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(timer_list) >= TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(_tx_timer_list_end))
+                    {
+
+                        /* Wrap from the beginning of the list.  */
+                        delta =  TX_TIMER_POINTER_DIF(timer_list, _tx_timer_list_end);
+                        timer_list =  TX_TIMER_POINTER_ADD(_tx_timer_list_start, delta);
+                    }
+
+                    /* Now put the timer on this list.  */
+                    if ((*timer_list) == TX_NULL)
+                    {
+
+                        /* This list is NULL, just put the new timer on it.  */
+
+                        /* Setup the links in this timer.  */
+                        current_timer -> tx_timer_internal_active_next =      current_timer;
+                        current_timer -> tx_timer_internal_active_previous =  current_timer;
+
+                        /* Setup the list head pointer.  */
+                        *timer_list =  current_timer;
+                    }
+                    else
+                    {
+
+                        /* This list is not NULL, add current timer to the end. */
+                        next_timer =                                          *timer_list;
+                        previous_timer =                                      next_timer -> tx_timer_internal_active_previous;
+                        previous_timer -> tx_timer_internal_active_next =     current_timer;
+                        next_timer -> tx_timer_internal_active_previous =     current_timer;
+                        current_timer -> tx_timer_internal_active_next =      next_timer;
+                        current_timer -> tx_timer_internal_active_previous =  previous_timer;
+                    }
+
+                    /* Setup list head pointer.  */
+                    current_timer -> tx_timer_internal_list_head =  timer_list;
+#else
+
+                    /* Reactivate through the timer activate function.  */
+
+                    /* Clear the list head for the timer activate call.  */
+                    current_timer -> tx_timer_internal_list_head = TX_NULL;
+
+                    /* Activate the current timer.  */
+                    _tx_timer_system_activate(current_timer);
+#endif
+                }
+
+                /* Restore interrupts.  */
+                TX_RESTORE
+
+                /* Lockout interrupts again.  */
+                TX_DISABLE
+            }
+
+            /* Finally, suspend this thread and wait for the next expiration.  */
+
+            /* Determine if another expiration took place while we were in this
+               thread.  If so, process another expiration.  */
+            if (_tx_timer_expired == TX_FALSE)
+            {
+
+                /* Otherwise, no timer expiration, so suspend the thread.  */
+
+                /* Build pointer to the timer thread.  */
+                thread_ptr =  &_tx_timer_thread;
+
+                /* Set the status to suspending, in order to indicate the
+                   suspension is in progress.  */
+                thread_ptr -> tx_thread_state =  TX_SUSPENDED;
+
+#ifdef TX_NOT_INTERRUPTABLE
+
+                /* Call actual non-interruptable thread suspension routine.  */
+                _tx_thread_system_ni_suspend(thread_ptr, ((ULONG) 0));
+
+                /* Restore interrupts.  */
+                TX_RESTORE
+#else
+
+                /* Set the suspending flag. */
+                thread_ptr -> tx_thread_suspending =  TX_TRUE;
+
+                /* Increment the preempt disable count prior to suspending.  */
+                _tx_thread_preempt_disable++;
+
+                /* Restore interrupts.  */
+                TX_RESTORE
+
+                /* Call actual thread suspension routine.  */
+                _tx_thread_system_suspend(thread_ptr);
+#endif
+            }
+            else
+            {
+
+                /* Restore interrupts.  */
+                TX_RESTORE
+            }
+        }
+    }
+
+#ifdef TX_SAFETY_CRITICAL
+
+    /* If we ever get here, raise safety critical exception.  */
+    TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, 0);
+#endif
+
+}
+#endif
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_byte_allocate.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_byte_allocate.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_byte_allocate.c	(revision 54)
@@ -0,0 +1,201 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Byte Memory                                                         */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_initialize.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+#include "tx_byte_pool.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _txe_byte_allocate                                  PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function checks for errors in allocate bytes function call.    */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    pool_ptr                          Pointer to pool control block     */
+/*    memory_ptr                        Pointer to place allocated bytes  */
+/*                                        pointer                         */
+/*    memory_size                       Number of bytes to allocate       */
+/*    wait_option                       Suspension option                 */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_POOL_ERROR                     Invalid memory pool pointer       */
+/*    TX_PTR_ERROR                      Invalid destination pointer       */
+/*    TX_WAIT_ERROR                     Invalid wait option               */
+/*    TX_CALLER_ERROR                   Invalid caller of this function   */
+/*    TX_SIZE_ERROR                     Invalid size of memory request    */
+/*    status                            Actual completion status          */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_byte_allocate                 Actual byte allocate function     */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _txe_byte_allocate(TX_BYTE_POOL *pool_ptr, VOID **memory_ptr,
+                                    ULONG memory_size,  ULONG wait_option)
+{
+
+UINT            status;
+#ifndef TX_TIMER_PROCESS_IN_ISR
+TX_THREAD       *thread_ptr;
+#endif
+
+
+    /* Default status to success.  */
+    status =  TX_SUCCESS;
+
+    /* Check for an invalid byte pool pointer.  */
+    if (pool_ptr == TX_NULL)
+    {
+
+        /* Byte pool pointer is invalid, return appropriate error code.  */
+        status =  TX_POOL_ERROR;
+    }
+
+    /* Now check for invalid pool ID.  */
+    else if  (pool_ptr -> tx_byte_pool_id != TX_BYTE_POOL_ID)
+    {
+
+        /* Byte pool pointer is invalid, return appropriate error code.  */
+        status =  TX_POOL_ERROR;
+    }
+
+    /* Check for an invalid destination for return pointer.  */
+    else if (memory_ptr == TX_NULL)
+    {
+
+        /* Null destination pointer, return appropriate error.  */
+        status =  TX_PTR_ERROR;
+    }
+
+    /* Check for an invalid memory size.  */
+    else if (memory_size == ((ULONG) 0))
+    {
+
+        /* Error in size, return appropriate error.  */
+        status =  TX_SIZE_ERROR;
+    }
+
+    /* Determine if the size is greater than the pool size.  */
+    else if (memory_size > pool_ptr -> tx_byte_pool_size)
+    {
+
+        /* Error in size, return appropriate error.  */
+        status =  TX_SIZE_ERROR;
+    }
+
+    else
+    {
+
+        /* Check for a wait option error.  Only threads are allowed any form of
+           suspension.  */
+        if (wait_option != TX_NO_WAIT)
+        {
+
+            /* Is call from ISR or Initialization?  */
+            if (TX_THREAD_GET_SYSTEM_STATE() != ((ULONG) 0))
+            {
+
+                /* A non-thread is trying to suspend, return appropriate error code.  */
+                status =  TX_WAIT_ERROR;
+            }
+        }
+    }
+#ifndef TX_TIMER_PROCESS_IN_ISR
+
+    /* Check for timer execution.  */
+    if (status == TX_SUCCESS)
+    {
+
+        /* Pickup thread pointer.  */
+        TX_THREAD_GET_CURRENT(thread_ptr)
+
+        /* Check for invalid caller of this function.  First check for a calling thread.  */
+        if (thread_ptr == &_tx_timer_thread)
+        {
+
+            /* Invalid caller of this function, return appropriate error code.  */
+            status =  TX_CALLER_ERROR;
+        }
+    }
+#endif
+
+    /* Is everything still okay?  */
+    if (status == TX_SUCCESS)
+    {
+
+        /* Check for interrupt call.  */
+        if (TX_THREAD_GET_SYSTEM_STATE() != ((ULONG) 0))
+        {
+
+            /* Now, make sure the call is from an interrupt and not initialization.  */
+            if (TX_THREAD_GET_SYSTEM_STATE() < TX_INITIALIZE_IN_PROGRESS)
+            {
+
+                /* Invalid caller of this function, return appropriate error code.  */
+                status =  TX_CALLER_ERROR;
+            }
+        }
+    }
+
+    /* Determine if everything is okay.  */
+    if (status == TX_SUCCESS)
+    {
+
+        /* Call actual byte memory allocate function.  */
+        status =  _tx_byte_allocate(pool_ptr, memory_ptr, memory_size,  wait_option);
+    }
+
+    /* Return completion status.  */
+    return(status);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_byte_pool_create.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_byte_pool_create.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_byte_pool_create.c	(revision 54)
@@ -0,0 +1,224 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Byte Pool                                                           */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_initialize.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+#include "tx_byte_pool.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _txe_byte_pool_create                               PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function checks for errors in the create byte pool memory      */
+/*    function.                                                           */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    pool_ptr                          Pointer to pool control block     */
+/*    name_ptr                          Pointer to byte pool name         */
+/*    pool_start                        Address of beginning of pool area */
+/*    pool_size                         Number of bytes in the byte pool  */
+/*    pool_control_block_size           Size of byte pool control block   */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_POOL_ERROR                     Invalid byte pool pointer         */
+/*    TX_PTR_ERROR                      Invalid pool starting address     */
+/*    TX_SIZE_ERROR                     Invalid pool size                 */
+/*    TX_CALLER_ERROR                   Invalid caller of this function   */
+/*    status                            Actual completion status          */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_byte_pool_create              Actual byte pool create function  */
+/*    _tx_thread_system_preempt_check   Check for preemption              */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _txe_byte_pool_create(TX_BYTE_POOL *pool_ptr, CHAR *name_ptr, VOID *pool_start, ULONG pool_size, UINT pool_control_block_size)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+UINT            status;
+ULONG           i;
+TX_BYTE_POOL    *next_pool;
+#ifndef TX_TIMER_PROCESS_IN_ISR
+TX_THREAD       *thread_ptr;
+#endif
+
+
+    /* Default status to success.  */
+    status =  TX_SUCCESS;
+
+    /* Check for an invalid byte pool pointer.  */
+    if (pool_ptr == TX_NULL)
+    {
+
+        /* Byte pool pointer is invalid, return appropriate error code.  */
+        status =  TX_POOL_ERROR;
+    }
+
+    /* Now see if the pool control block size is valid.  */
+    else if (pool_control_block_size != (sizeof(TX_BYTE_POOL)))
+    {
+
+        /* Byte pool pointer is invalid, return appropriate error code.  */
+        status =  TX_POOL_ERROR;
+    }
+    else
+    {
+
+        /* Disable interrupts.  */
+        TX_DISABLE
+
+        /* Increment the preempt disable flag.  */
+        _tx_thread_preempt_disable++;
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Next see if it is already in the created list.  */
+        next_pool =   _tx_byte_pool_created_ptr;
+        for (i = ((ULONG) 0); i < _tx_byte_pool_created_count; i++)
+        {
+
+            /* Determine if this byte pool matches the pool in the list.  */
+            if (pool_ptr == next_pool)
+            {
+
+                break;
+            }
+            else
+            {
+
+                /* Move to the next pool.  */
+                next_pool =  next_pool -> tx_byte_pool_created_next;
+            }
+        }
+
+        /* Disable interrupts.  */
+        TX_DISABLE
+
+        /* Decrement the preempt disable flag.  */
+        _tx_thread_preempt_disable--;
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Check for preemption.  */
+        _tx_thread_system_preempt_check();
+
+        /* At this point, check to see if there is a duplicate pool.  */
+        if (pool_ptr == next_pool)
+        {
+
+            /* Pool is already created, return appropriate error code.  */
+            status =  TX_POOL_ERROR;
+        }
+
+        /* Check for an invalid starting address.  */
+        else if (pool_start == TX_NULL)
+        {
+
+            /* Null starting address pointer, return appropriate error.  */
+            status =  TX_PTR_ERROR;
+        }
+
+        /* Check for invalid pool size.  */
+        else if (pool_size < TX_BYTE_POOL_MIN)
+        {
+
+            /* Pool not big enough, return appropriate error.  */
+            status =  TX_SIZE_ERROR;
+        }
+        else
+        {
+
+#ifndef TX_TIMER_PROCESS_IN_ISR
+
+            /* Pickup thread pointer.  */
+            TX_THREAD_GET_CURRENT(thread_ptr)
+
+            /* Check for invalid caller of this function.  First check for a calling thread.  */
+            if (thread_ptr == &_tx_timer_thread)
+            {
+
+                /* Invalid caller of this function, return appropriate error code.  */
+                status =  TX_CALLER_ERROR;
+            }
+#endif
+
+            /* Check for interrupt call.  */
+            if (TX_THREAD_GET_SYSTEM_STATE() != ((ULONG) 0))
+            {
+
+                /* Now, make sure the call is from an interrupt and not initialization.  */
+                if (TX_THREAD_GET_SYSTEM_STATE() < TX_INITIALIZE_IN_PROGRESS)
+                {
+
+                    /* Invalid caller of this function, return appropriate error code.  */
+                    status =  TX_CALLER_ERROR;
+                }
+            }
+        }
+    }
+
+    /* Determine if everything is okay.  */
+    if (status == TX_SUCCESS)
+    {
+
+        /* Call actual byte pool create function.  */
+        status =  _tx_byte_pool_create(pool_ptr, name_ptr, pool_start, pool_size);
+    }
+
+    /* Return completion status.  */
+    return(status);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_semaphore_create.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_semaphore_create.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_semaphore_create.c	(revision 54)
@@ -0,0 +1,210 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Semaphore                                                           */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_initialize.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+#include "tx_semaphore.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _txe_semaphore_create                               PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function checks for errors in the create semaphore function    */
+/*    call.                                                               */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    semaphore_ptr                     Pointer to semaphore control block*/
+/*    name_ptr                          Pointer to semaphore name         */
+/*    initial_count                     Initial semaphore count           */
+/*    semaphore_control_block_size      Size of semaphore control block   */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_SEMAPHORE_ERROR                Invalid semaphore pointer         */
+/*    TX_CALLER_ERROR                   Invalid caller of this function   */
+/*    status                            Actual completion status          */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_semaphore_create              Actual create semaphore function  */
+/*    _tx_thread_system_preempt_check   Check for preemption              */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _txe_semaphore_create(TX_SEMAPHORE *semaphore_ptr, CHAR *name_ptr, ULONG initial_count, UINT semaphore_control_block_size)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+UINT                status;
+ULONG               i;
+TX_SEMAPHORE        *next_semaphore;
+#ifndef TX_TIMER_PROCESS_IN_ISR
+TX_THREAD           *thread_ptr;
+#endif
+
+
+    /* Default status to success.  */
+    status =  TX_SUCCESS;
+
+    /* Check for an invalid semaphore pointer.  */
+    if (semaphore_ptr == TX_NULL)
+    {
+
+        /* Semaphore pointer is invalid, return appropriate error code.  */
+        status =  TX_SEMAPHORE_ERROR;
+    }
+
+    /* Now check for a valid semaphore ID.  */
+    else if (semaphore_control_block_size != (sizeof(TX_SEMAPHORE)))
+    {
+
+        /* Semaphore pointer is invalid, return appropriate error code.  */
+        status =  TX_SEMAPHORE_ERROR;
+    }
+    else
+    {
+
+        /* Disable interrupts.  */
+        TX_DISABLE
+
+        /* Increment the preempt disable flag.  */
+        _tx_thread_preempt_disable++;
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Next see if it is already in the created list.  */
+        next_semaphore =  _tx_semaphore_created_ptr;
+        for (i = ((ULONG) 0); i < _tx_semaphore_created_count; i++)
+        {
+
+            /* Determine if this semaphore matches the current semaphore in the list.  */
+            if (semaphore_ptr == next_semaphore)
+            {
+
+                break;
+            }
+            else
+            {
+
+                /* Move to next semaphore.  */
+                next_semaphore =  next_semaphore -> tx_semaphore_created_next;
+            }
+        }
+
+        /* Disable interrupts.  */
+        TX_DISABLE
+
+        /* Decrement the preempt disable flag.  */
+        _tx_thread_preempt_disable--;
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Check for preemption.  */
+        _tx_thread_system_preempt_check();
+
+        /* At this point, check to see if there is a duplicate semaphore.  */
+        if (semaphore_ptr == next_semaphore)
+        {
+
+            /* Semaphore is already created, return appropriate error code.  */
+            status =  TX_SEMAPHORE_ERROR;
+        }
+
+#ifndef TX_TIMER_PROCESS_IN_ISR
+        else
+        {
+
+            /* Pickup thread pointer.  */
+            TX_THREAD_GET_CURRENT(thread_ptr)
+
+            /* Check for invalid caller of this function.  First check for a calling thread.  */
+            if (thread_ptr == &_tx_timer_thread)
+            {
+
+                /* Invalid caller of this function, return appropriate error code.  */
+                status =  TX_CALLER_ERROR;
+            }
+        }
+#endif
+    }
+
+    /* Determine if everything is okay.  */
+    if (status == TX_SUCCESS)
+    {
+
+        /* Check for interrupt call.  */
+        if (TX_THREAD_GET_SYSTEM_STATE() != ((ULONG) 0))
+        {
+
+            /* Now, make sure the call is from an interrupt and not initialization.  */
+            if (TX_THREAD_GET_SYSTEM_STATE() < TX_INITIALIZE_IN_PROGRESS)
+            {
+
+                /* Invalid caller of this function, return appropriate error code.  */
+                status =  TX_CALLER_ERROR;
+            }
+        }
+    }
+
+    /* Determine if everything is okay.  */
+    if (status == TX_SUCCESS)
+    {
+
+        /* Call actual semaphore create function.  */
+        status =  _tx_semaphore_create(semaphore_ptr, name_ptr, initial_count);
+    }
+
+    /* Return completion status.  */
+    return(status);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_semaphore_delete.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_semaphore_delete.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_semaphore_delete.c	(revision 54)
@@ -0,0 +1,145 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Semaphore                                                           */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+#include "tx_semaphore.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _txe_semaphore_delete                               PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function checks for errors in the semaphore delete function    */
+/*    call.                                                               */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    semaphore_ptr                     Pointer to semaphore control block*/
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_SEMAPHORE_ERROR                Invalid semaphore pointer         */
+/*    TX_CALLER_ERROR                   Invalid caller of this function   */
+/*    status                            Actual completion status          */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_semaphore_delete              Actual delete semaphore function  */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _txe_semaphore_delete(TX_SEMAPHORE *semaphore_ptr)
+{
+
+UINT            status;
+#ifndef TX_TIMER_PROCESS_IN_ISR
+TX_THREAD       *thread_ptr;
+#endif
+
+
+    /* Default status to success.  */
+    status =  TX_SUCCESS;
+
+    /* Check for an invalid semaphore pointer.  */
+    if (semaphore_ptr == TX_NULL)
+    {
+
+        /* Semaphore pointer is invalid, return appropriate error code.  */
+        status =  TX_SEMAPHORE_ERROR;
+    }
+
+    /* Now check for invalid semaphore ID.  */
+    else if (semaphore_ptr -> tx_semaphore_id != TX_SEMAPHORE_ID)
+    {
+
+        /* Semaphore pointer is invalid, return appropriate error code.  */
+        status =  TX_SEMAPHORE_ERROR;
+    }
+    else
+    {
+
+        /* Check for invalid caller of this function.  */
+
+        /* Is the caller an ISR or Initialization?  */
+        if (TX_THREAD_GET_SYSTEM_STATE() != ((ULONG) 0))
+        {
+
+            /* Invalid caller of this function, return appropriate error code.  */
+            status =  TX_CALLER_ERROR;
+        }
+
+#ifndef TX_TIMER_PROCESS_IN_ISR
+        else
+        {
+
+            /* Pickup thread pointer.  */
+            TX_THREAD_GET_CURRENT(thread_ptr)
+
+            /* Is the caller the system timer thread?  */
+            if (thread_ptr == &_tx_timer_thread)
+            {
+
+                /* Invalid caller of this function, return appropriate error code.  */
+                status =  TX_CALLER_ERROR;
+            }
+        }
+#endif
+    }
+
+    /* Determine if everything is okay.  */
+    if (status == TX_SUCCESS)
+    {
+
+        /* Call actual semaphore delete function.  */
+        status =  _tx_semaphore_delete(semaphore_ptr);
+    }
+
+    /* Return completion status.  */
+    return(status);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_semaphore_get.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_semaphore_get.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_semaphore_get.c	(revision 54)
@@ -0,0 +1,150 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Semaphore                                                           */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+#include "tx_semaphore.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _txe_semaphore_get                                  PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function checks for errors in the semaphore get function call. */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    semaphore_ptr                     Pointer to semaphore control block*/
+/*    wait_option                       Suspension option                 */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_SEMAPHORE_ERROR                Invalid semaphore pointer         */
+/*    TX_WAIT_ERROR                     Invalid wait option               */
+/*    status                            Actual completion status          */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_semaphore_get                 Actual get semaphore function     */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _txe_semaphore_get(TX_SEMAPHORE *semaphore_ptr, ULONG wait_option)
+{
+
+UINT        status;
+
+#ifndef TX_TIMER_PROCESS_IN_ISR
+TX_THREAD   *current_thread;
+#endif
+
+
+    /* Default status to success.  */
+    status =  TX_SUCCESS;
+
+    /* Check for an invalid semaphore pointer.  */
+    if (semaphore_ptr == TX_NULL)
+    {
+
+        /* Semaphore pointer is invalid, return appropriate error code.  */
+        status =  TX_SEMAPHORE_ERROR;
+    }
+
+    /* Now check for invalid semaphore ID.  */
+    else if (semaphore_ptr -> tx_semaphore_id != TX_SEMAPHORE_ID)
+    {
+
+        /* Semaphore pointer is invalid, return appropriate error code.  */
+        status =  TX_SEMAPHORE_ERROR;
+    }
+    else
+    {
+
+        /* Check for a wait option error.  Only threads are allowed any form of
+           suspension.  */
+        if (wait_option != TX_NO_WAIT)
+        {
+
+            /* Is the call from an ISR or Initialization?  */
+            if (TX_THREAD_GET_SYSTEM_STATE() != ((ULONG) 0))
+            {
+
+                /* A non-thread is trying to suspend, return appropriate error code.  */
+                status =  TX_WAIT_ERROR;
+            }
+
+#ifndef TX_TIMER_PROCESS_IN_ISR
+            else
+            {
+
+                /* Pickup thread pointer.  */
+                TX_THREAD_GET_CURRENT(current_thread)
+
+                /* Is the current thread the timer thread?  */
+                if (current_thread == &_tx_timer_thread)
+                {
+
+                    /* A non-thread is trying to suspend, return appropriate error code.  */
+                    status =  TX_WAIT_ERROR;
+                }
+            }
+#endif
+        }
+    }
+
+    /* Determine if everything is okay.  */
+    if (status == TX_SUCCESS)
+    {
+
+        /* Call actual get semaphore function.  */
+        status =  _tx_semaphore_get(semaphore_ptr, wait_option);
+    }
+
+    /* Return completion status.  */
+    return(status);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_semaphore_put.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_semaphore_put.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_semaphore_put.c	(revision 54)
@@ -0,0 +1,103 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Semaphore                                                           */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_semaphore.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _txe_semaphore_put                                  PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function checks for errors in the semaphore put function call. */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    semaphore_ptr                     Pointer to semaphore control block*/
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_SEMAPHORE_ERROR                Invalid semaphore pointer         */
+/*    status                            Actual completion status          */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_semaphore_put                 Actual put semaphore function     */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT  _txe_semaphore_put(TX_SEMAPHORE *semaphore_ptr)
+{
+
+UINT        status;
+
+
+    /* Check for an invalid semaphore pointer.  */
+    if (semaphore_ptr == TX_NULL)
+    {
+
+        /* Semaphore pointer is invalid, return appropriate error code.  */
+        status =  TX_SEMAPHORE_ERROR;
+    }
+
+    /* Now check for invalid semaphore ID.  */
+    else if (semaphore_ptr -> tx_semaphore_id != TX_SEMAPHORE_ID)
+    {
+
+        /* Semaphore pointer is invalid, return appropriate error code.  */
+        status =  TX_SEMAPHORE_ERROR;
+    }
+    else
+    {
+
+        /* Call actual put semaphore function.  */
+        status =  _tx_semaphore_put(semaphore_ptr);
+    }
+
+    /* Return completion status.  */
+    return(status);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_thread_create.c
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_thread_create.c	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/common/src/txe_thread_create.c	(revision 54)
@@ -0,0 +1,313 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Thread                                                              */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files.  */
+
+#include "tx_api.h"
+#include "tx_initialize.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  FUNCTION                                               RELEASE        */
+/*                                                                        */
+/*    _txe_thread_create                                  PORTABLE C      */
+/*                                                           6.1          */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    William E. Lamie, Microsoft Corporation                             */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This function checks for errors in the thread create function call. */
+/*                                                                        */
+/*  INPUT                                                                 */
+/*                                                                        */
+/*    thread_ptr                            Thread control block pointer  */
+/*    name                                  Pointer to thread name string */
+/*    entry_function                        Entry function of the thread  */
+/*    entry_input                           32-bit input value to thread  */
+/*    stack_start                           Pointer to start of stack     */
+/*    stack_size                            Stack size in bytes           */
+/*    priority                              Priority of thread (0-31)     */
+/*    preempt_threshold                     Preemption threshold          */
+/*    time_slice                            Thread time-slice value       */
+/*    auto_start                            Automatic start selection     */
+/*    thread_control_block_size             Size of thread control block  */
+/*                                                                        */
+/*  OUTPUT                                                                */
+/*                                                                        */
+/*    TX_THREAD_ERROR                       Invalid thread pointer        */
+/*    TX_PTR_ERROR                          Invalid entry point or stack  */
+/*                                            address                     */
+/*    TX_SIZE_ERROR                         Invalid stack size -too small */
+/*    TX_PRIORITY_ERROR                     Invalid thread priority       */
+/*    TX_THRESH_ERROR                       Invalid preemption threshold  */
+/*    status                                Actual completion status      */
+/*                                                                        */
+/*  CALLS                                                                 */
+/*                                                                        */
+/*    _tx_thread_create                     Actual thread create function */
+/*    _tx_thread_system_preempt_check       Check for preemption          */
+/*                                                                        */
+/*  CALLED BY                                                             */
+/*                                                                        */
+/*    Application Code                                                    */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
+/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
+/*                                            resulting in version 6.1    */
+/*                                                                        */
+/**************************************************************************/
+UINT    _txe_thread_create(TX_THREAD *thread_ptr, CHAR *name_ptr,
+                VOID (*entry_function)(ULONG id), ULONG entry_input,
+                VOID *stack_start, ULONG stack_size,
+                UINT priority, UINT preempt_threshold,
+                ULONG time_slice, UINT auto_start, UINT thread_control_block_size)
+{
+
+TX_INTERRUPT_SAVE_AREA
+
+UINT            status;
+UINT            break_flag;
+ULONG           i;
+TX_THREAD       *next_thread;
+VOID            *stack_end;
+UCHAR           *work_ptr;
+#ifndef TX_TIMER_PROCESS_IN_ISR
+TX_THREAD       *current_thread;
+#endif
+
+
+    /* Default status to success.  */
+    status =  TX_SUCCESS;
+
+    /* Check for an invalid thread pointer.  */
+    if (thread_ptr == TX_NULL)
+    {
+
+        /* Thread pointer is invalid, return appropriate error code.  */
+        status =  TX_THREAD_ERROR;
+    }
+
+    /* Now check for invalid thread control block size.  */
+    else if (thread_control_block_size != (sizeof(TX_THREAD)))
+    {
+
+        /* Thread pointer is invalid, return appropriate error code.  */
+        status =  TX_THREAD_ERROR;
+    }
+    else
+    {
+
+        /* Disable interrupts.  */
+        TX_DISABLE
+
+        /* Increment the preempt disable flag.  */
+        _tx_thread_preempt_disable++;
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Next see if it is already in the created list.  */
+        break_flag =   TX_FALSE;
+        next_thread =  _tx_thread_created_ptr;
+        work_ptr =     TX_VOID_TO_UCHAR_POINTER_CONVERT(stack_start);
+        work_ptr =     TX_UCHAR_POINTER_ADD(work_ptr, (stack_size - ((ULONG) 1)));
+        stack_end =    TX_UCHAR_TO_VOID_POINTER_CONVERT(work_ptr);
+        for (i = ((ULONG) 0); i < _tx_thread_created_count; i++)
+        {
+
+            /* Determine if this thread matches the thread in the list.  */
+            if (thread_ptr == next_thread)
+            {
+
+                /* Set the break flag.  */
+                break_flag =  TX_TRUE;
+            }
+
+            /* Determine if we need to break the loop.  */
+            if (break_flag == TX_TRUE)
+            {
+
+                /* Yes, break out of the loop.  */
+                break;
+            }
+
+            /* Check the stack pointer to see if it overlaps with this thread's stack.  */
+            if (stack_start >= next_thread -> tx_thread_stack_start)
+            {
+
+                if (stack_start < next_thread -> tx_thread_stack_end)
+                {
+
+                    /* This stack overlaps with an existing thread, clear the stack pointer to
+                       force a stack error below.  */
+                    stack_start =  TX_NULL;
+
+                    /* Set the break flag.  */
+                    break_flag =  TX_TRUE;
+                }
+            }
+
+            /* Check the end of the stack to see if it is inside this thread's stack area as well.  */
+            if (stack_end >= next_thread -> tx_thread_stack_start)
+            {
+
+                if (stack_end < next_thread -> tx_thread_stack_end)
+                {
+
+                    /* This stack overlaps with an existing thread, clear the stack pointer to
+                       force a stack error below.  */
+                    stack_start =  TX_NULL;
+
+                    /* Set the break flag.  */
+                    break_flag =  TX_TRUE;
+                }
+            }
+
+            /* Move to the next thread.  */
+            next_thread =  next_thread -> tx_thread_created_next;
+        }
+
+        /* Disable interrupts.  */
+        TX_DISABLE
+
+        /* Decrement the preempt disable flag.  */
+        _tx_thread_preempt_disable--;
+
+        /* Restore interrupts.  */
+        TX_RESTORE
+
+        /* Check for preemption.  */
+        _tx_thread_system_preempt_check();
+
+        /* At this point, check to see if there is a duplicate thread.  */
+        if (thread_ptr == next_thread)
+        {
+
+            /* Thread is already created, return appropriate error code.  */
+            status =  TX_THREAD_ERROR;
+        }
+
+        /* Check for invalid starting address of stack.  */
+        else if (stack_start == TX_NULL)
+        {
+
+            /* Invalid stack or entry point, return appropriate error code.  */
+            status =  TX_PTR_ERROR;
+        }
+
+        /* Check for invalid thread entry point.  */
+        else if (entry_function == TX_NULL)
+        {
+
+            /* Invalid stack or entry point, return appropriate error code.  */
+            status =  TX_PTR_ERROR;
+        }
+
+        /* Check the stack size.  */
+        else if (stack_size < ((ULONG) TX_MINIMUM_STACK))
+        {
+
+            /* Stack is not big enough, return appropriate error code.  */
+            status =  TX_SIZE_ERROR;
+        }
+
+        /* Check the priority specified.  */
+        else if (priority >= ((UINT) TX_MAX_PRIORITIES))
+        {
+
+            /* Invalid priority selected, return appropriate error code.  */
+            status =  TX_PRIORITY_ERROR;
+        }
+
+        /* Check preemption threshold. */
+        else if (preempt_threshold > priority)
+        {
+
+            /* Invalid preempt threshold, return appropriate error code.  */
+            status =  TX_THRESH_ERROR;
+        }
+
+        /* Check the start selection.  */
+        else if (auto_start > TX_AUTO_START)
+        {
+
+            /* Invalid auto start selection, return appropriate error code.  */
+            status =  TX_START_ERROR;
+        }
+        else
+        {
+
+#ifndef TX_TIMER_PROCESS_IN_ISR
+
+            /* Pickup thread pointer.  */
+            TX_THREAD_GET_CURRENT(current_thread)
+
+            /* Check for invalid caller of this function.  First check for a calling thread.  */
+            if (current_thread == &_tx_timer_thread)
+            {
+
+                /* Invalid caller of this function, return appropriate error code.  */
+                status =  TX_CALLER_ERROR;
+            }
+#endif
+
+            /* Check for interrupt call.  */
+            if (TX_THREAD_GET_SYSTEM_STATE() != ((ULONG) 0))
+            {
+
+                /* Now, make sure the call is from an interrupt and not initialization.  */
+                if (TX_THREAD_GET_SYSTEM_STATE() < TX_INITIALIZE_IN_PROGRESS)
+                {
+
+                    /* Invalid caller of this function, return appropriate error code.  */
+                    status =  TX_CALLER_ERROR;
+                }
+            }
+        }
+    }
+
+    /* Determine if everything is okay.  */
+    if (status == TX_SUCCESS)
+    {
+
+        /* Call actual thread create function.  */
+        status =  _tx_thread_create(thread_ptr, name_ptr, entry_function, entry_input,
+                        stack_start, stack_size, priority, preempt_threshold,
+                        time_slice, auto_start);
+    }
+
+    /* Return completion status.  */
+    return(status);
+}
+
Index: ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/ports/cortex_m7/gnu/inc/tx_port.h
===================================================================
--- ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/ports/cortex_m7/gnu/inc/tx_port.h	(revision 54)
+++ ctrl/firmware/Main/CubeMX/Middlewares/ST/threadx/ports/cortex_m7/gnu/inc/tx_port.h	(revision 54)
@@ -0,0 +1,729 @@
+/**************************************************************************/
+/*                                                                        */
+/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
+/*                                                                        */
+/*       This software is licensed under the Microsoft Software License   */
+/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
+/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
+/*       and in the root directory of this software.                      */
+/*                                                                        */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/**                                                                       */
+/** ThreadX Component                                                     */
+/**                                                                       */
+/**   Port Specific                                                       */
+/**                                                                       */
+/**************************************************************************/
+/**************************************************************************/
+
+
+/**************************************************************************/
+/*                                                                        */
+/*  PORT SPECIFIC C INFORMATION                            RELEASE        */
+/*                                                                        */
+/*    tx_port.h                                         Cortex-M7/GNU     */
+/*                                                           6.1.12       */
+/*                                                                        */
+/*  AUTHOR                                                                */
+/*                                                                        */
+/*    Scott Larson, Microsoft Corporation                                 */
+/*                                                                        */
+/*  DESCRIPTION                                                           */
+/*                                                                        */
+/*    This file contains data type definitions that make the ThreadX      */
+/*    real-time kernel function identically on a variety of different     */
+/*    processor architectures.  For example, the size or number of bits   */
+/*    in an "int" data type vary between microprocessor architectures and */
+/*    even C compilers for the same microprocessor.  ThreadX does not     */
+/*    directly use native C data types.  Instead, ThreadX creates its     */
+/*    own special types that can be mapped to actual data types by this   */
+/*    file to guarantee consistency in the interface and functionality.   */
+/*                                                                        */
+/*    This file replaces the previous Cortex-M3/M4/M7 files. It unifies   */
+/*    the ARMv7-M architecture and compilers into one common file.        */
+/*                                                                        */
+/*  RELEASE HISTORY                                                       */
+/*                                                                        */
+/*    DATE              NAME                      DESCRIPTION             */
+/*                                                                        */
+/*  06-02-2021      Scott Larson            Initial Version 6.1.7         */
+/*  01-31-2022      Scott Larson            Modified comments, updated    */
+/*                                            typedef to fix misra        */
+/*                                            violation,                  */
+/*                                            fixed predefined macro,     */
+/*                                            resulting in version 6.1.10 */
+/*  04-25-2022      Scott Larson            Modified comments and added   */
+/*                                            volatile to registers,      */
+/*                                            resulting in version 6.1.11 */
+/*  07-29-2022      Scott Larson            Modified comments and         */
+/*                                            described BASEPRI usage,    */
+/*                                            resulting in version 6.1.12 */
+/*                                                                        */
+/**************************************************************************/
+
+#ifndef TX_PORT_H
+#define TX_PORT_H
+
+
+/* Determine if the optional ThreadX user define file should be used.  */
+
+#ifdef TX_INCLUDE_USER_DEFINE_FILE
+
+/* Yes, include the user defines in tx_user.h. The defines in this file may
+   alternately be defined on the command line.  */
+
+#include "tx_user.h"
+#endif
+
+
+/* Define compiler library include files.  */
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __ICCARM__
+#include <intrinsics.h>                     /* IAR Intrinsics */
+#define __asm__ __asm                       /* Define to make all inline asm look similar */
+#ifdef  TX_ENABLE_IAR_LIBRARY_SUPPORT
+#include <yvals.h>
+#endif
+#endif /* __ICCARM__ */
+
+#ifdef __ghs__
+#include <arm_ghs.h>
+#include "tx_ghs.h"
+#endif  /* __ghs__ */
+
+
+#if !defined(__GNUC__) && !defined(__CC_ARM)
+#define __get_control_value __get_CONTROL
+#define __set_control_value __set_CONTROL
+#endif
+
+#ifndef __GNUC__
+#define __get_ipsr_value __get_IPSR
+#endif
+
+/* Define ThreadX basic types for this port.  */
+
+#define VOID                                    void
+typedef char                                    CHAR;
+typedef unsigned char                           UCHAR;
+typedef int                                     INT;
+typedef unsigned int                            UINT;
+typedef long                                    LONG;
+typedef unsigned long                           ULONG;
+typedef unsigned long long                      ULONG64;
+typedef short                                   SHORT;
+typedef unsigned short                          USHORT;
+#define ULONG64_DEFINED
+
+/* Define the priority levels for ThreadX.  Legal values range
+   from 32 to 1024 and MUST be evenly divisible by 32.  */
+
+#ifndef TX_MAX_PRIORITIES
+#define TX_MAX_PRIORITIES                       32
+#endif
+
+
+/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during
+   thread creation is less than this value, the thread create call will return an error.  */
+
+#ifndef TX_MINIMUM_STACK
+#define TX_MINIMUM_STACK                        200         /* Minimum stack size for this port  */
+#endif
+
+
+/* Define the system timer thread's default stack size and priority.  These are only applicable
+   if TX_TIMER_PROCESS_IN_ISR is not defined.  */
+
+#ifndef TX_TIMER_THREAD_STACK_SIZE
+#define TX_TIMER_THREAD_STACK_SIZE              1024        /* Default timer thread stack size  */
+#endif
+
+#ifndef TX_TIMER_THREAD_PRIORITY
+#define TX_TIMER_THREAD_PRIORITY                0           /* Default timer thread priority    */
+#endif
+
+/* By default, ThreadX for Cortex-M uses the PRIMASK register to enable/disable interrupts.
+If using BASEPRI is desired, define the following two symbols for both c and assembly files:
+TX_PORT_USE_BASEPRI - This tells ThreadX to use BASEPRI instead of PRIMASK.
+TX_PORT_BASEPRI = (priority_mask << (8 - number_priority_bits)) - this defines the maximum priority level to mask.
+Any interrupt with a higher priority than priority_mask will not be masked, thus the interrupt will run.
+*/
+
+/* Define various constants for the ThreadX Cortex-M port.  */
+
+#define TX_INT_DISABLE                          1           /* Disable interrupts               */
+#define TX_INT_ENABLE                           0           /* Enable interrupts                */
+
+
+/* Define the clock source for trace event entry time stamp. The following two item are port specific.
+   For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock
+   source constants would be:
+
+#define TX_TRACE_TIME_SOURCE                    *((volatile ULONG *) 0x0a800024)
+#define TX_TRACE_TIME_MASK                      0x0000FFFFUL
+
+*/
+
+#ifndef TX_MISRA_ENABLE
+#ifndef TX_TRACE_TIME_SOURCE
+#define TX_TRACE_TIME_SOURCE                    *((volatile ULONG *) 0xE0001004)
+#endif
+#else
+ULONG   _tx_misra_time_stamp_get(VOID);
+#define TX_TRACE_TIME_SOURCE                    _tx_misra_time_stamp_get()
+#endif
+
+#ifndef TX_TRACE_TIME_MASK
+#define TX_TRACE_TIME_MASK                      0xFFFFFFFFUL
+#endif
+
+#ifdef __ghs__
+/* Define constants for Green Hills EventAnalyzer.  */
+
+/* Define the number of ticks per second. This informs the EventAnalyzer what the timestamps
+   represent.  By default, this is set to 1,000,000 i.e., one tick every microsecond. */
+
+#define TX_EL_TICKS_PER_SECOND                  1000000
+
+/* Define the method of how to get the upper and lower 32-bits of the time stamp. By default, simply
+   simulate the time-stamp source with a counter.  */
+
+#define read_tbu()                              _tx_el_time_base_upper
+#define read_tbl()                              ++_tx_el_time_base_lower
+#endif  /* __ghs__ */
+
+/* Define the port specific options for the _tx_build_options variable. This variable indicates
+   how the ThreadX library was built.  */
+
+#define TX_PORT_SPECIFIC_BUILD_OPTIONS          (0)
+
+
+/* Define the in-line initialization constant so that modules with in-line
+   initialization capabilities can prevent their initialization from being
+   a function call.  */
+
+#ifdef TX_MISRA_ENABLE
+#define TX_DISABLE_INLINE
+#else
+#define TX_INLINE_INITIALIZATION
+#endif
+
+
+/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is
+   disabled. When the following is defined, ThreadX thread stack checking is enabled.  If stack
+   checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING
+   define is negated, thereby forcing the stack fill which is necessary for the stack checking
+   logic.  */
+
+#ifndef TX_MISRA_ENABLE
+#ifdef TX_ENABLE_STACK_CHECKING
+#undef TX_DISABLE_STACK_FILLING
+#endif
+#endif
+
+
+/* Define the TX_THREAD control block extensions for this port. The main reason
+   for the multiple macros is so that backward compatibility can be maintained with
+   existing ThreadX kernel awareness modules.  */
+
+#define TX_THREAD_EXTENSION_0
+#define TX_THREAD_EXTENSION_1
+#ifdef  TX_ENABLE_IAR_LIBRARY_SUPPORT
+#define TX_THREAD_EXTENSION_2           VOID    *tx_thread_iar_tls_pointer;
+#elif defined(__ghs__)
+#define TX_THREAD_EXTENSION_2           VOID *  tx_thread_eh_globals;                           \
+                                        int     Errno;             /* errno.  */                \
+                                        char *  strtok_saved_pos;  /* strtok() position.  */
+#else
+#define TX_THREAD_EXTENSION_2
+#endif
+
+
+#define TX_THREAD_EXTENSION_3
+
+
+
+/* Define the port extensions of the remaining ThreadX objects.  */
+
+#define TX_BLOCK_POOL_EXTENSION
+#define TX_BYTE_POOL_EXTENSION
+#define TX_EVENT_FLAGS_GROUP_EXTENSION
+#define TX_MUTEX_EXTENSION
+#define TX_QUEUE_EXTENSION
+#define TX_SEMAPHORE_EXTENSION
+#define TX_TIMER_EXTENSION
+
+
+/* Define the user extension field of the thread control block.  Nothing
+   additional is needed for this port so it is defined as white space.  */
+
+#ifndef TX_THREAD_USER_EXTENSION
+#define TX_THREAD_USER_EXTENSION
+#endif
+
+
+/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete,
+   tx_thread_shell_entry, and tx_thread_terminate.  */
+
+
+#ifdef  TX_ENABLE_IAR_LIBRARY_SUPPORT
+#if (__VER__ < 8000000)
+#define TX_THREAD_CREATE_EXTENSION(thread_ptr)                      thread_ptr -> tx_thread_iar_tls_pointer =  __iar_dlib_perthread_allocate();
+#define TX_THREAD_DELETE_EXTENSION(thread_ptr)                      __iar_dlib_perthread_deallocate(thread_ptr -> tx_thread_iar_tls_pointer); \
+                                                                    thread_ptr -> tx_thread_iar_tls_pointer =  TX_NULL;
+#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION               __iar_dlib_perthread_access(0);
+#else
+void    *_tx_iar_create_per_thread_tls_area(void);
+void    _tx_iar_destroy_per_thread_tls_area(void *tls_ptr);
+void    __iar_Initlocks(void);
+
+#define TX_THREAD_CREATE_EXTENSION(thread_ptr)                      thread_ptr -> tx_thread_iar_tls_pointer =  _tx_iar_create_per_thread_tls_area();
+#define TX_THREAD_DELETE_EXTENSION(thread_ptr)                      do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \
+                                                                        thread_ptr -> tx_thread_iar_tls_pointer =  TX_NULL; } while(0);
+#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION               do {__iar_Initlocks();} while(0);
+#endif
+#else
+#define TX_THREAD_CREATE_EXTENSION(thread_ptr)
+#define TX_THREAD_DELETE_EXTENSION(thread_ptr)
+#endif
+
+#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__)
+
+#ifdef TX_MISRA_ENABLE
+
+ULONG  _tx_misra_control_get(void);
+void   _tx_misra_control_set(ULONG value);
+ULONG  _tx_misra_fpccr_get(void);
+void   _tx_misra_vfp_touch(void);
+
+#else   /* TX_MISRA_ENABLE not defined */
+
+/* Define some helper functions (these are intrinsics in some compilers). */
+#ifdef __GNUC__ /* GCC and ARM Compiler 6 */
+
+__attribute__( ( always_inline ) ) static inline ULONG __get_control_value(void)
+{
+ULONG  control_value;
+
+    __asm__ volatile (" MRS  %0,CONTROL ": "=r" (control_value) );
+    return(control_value);
+}
+
+__attribute__( ( always_inline ) ) static inline void __set_control_value(ULONG control_value)
+{
+    __asm__ volatile (" MSR  CONTROL,%0": : "r" (control_value): "memory" );
+}
+
+#define TX_VFP_TOUCH()  __asm__ volatile ("VMOV.F32 s0, s0");
+
+#elif defined(__CC_ARM) /* ARM Compiler 5 */
+
+__attribute__( ( always_inline ) ) ULONG __get_control_value(void)
+{
+ULONG  control_value;
+
+    __asm volatile ("MRS control_value,CONTROL");
+    return(control_value);
+}
+
+__attribute__( ( always_inline ) ) void __set_control_value(ULONG control_value)
+{
+    __asm__ volatile ("MSR CONTROL,control_value");
+}
+/* Can't access VFP registers with inline asm, so define this in tx_thread_schedule.  */
+void _tx_vfp_access(void);
+#define TX_VFP_TOUCH()  _tx_vfp_access();
+
+#elif defined(__ICCARM__)  /* IAR */
+#define TX_VFP_TOUCH()  __asm__ volatile ("VMOV.F32 s0, s0");
+#endif  /* Helper functions for different compilers */
+
+#endif  /* TX_MISRA_ENABLE */
+
+
+/* A completed thread falls into _thread_shell_entry and we can simply deactivate the FPU via CONTROL.FPCA
+   in order to ensure no lazy stacking will occur. */
+
+#ifndef TX_MISRA_ENABLE
+
+#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr)   {                                                       \
+                                                    ULONG  _tx_vfp_state;                                   \
+                                                        _tx_vfp_state =  __get_control_value();             \
+                                                        _tx_vfp_state =  _tx_vfp_state & ~((ULONG) 0x4);    \
+                                                        __set_control_value(_tx_vfp_state);                 \
+                                                    }
+#else
+
+#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr)   {                                                       \
+                                                    ULONG  _tx_vfp_state;                                   \
+                                                        _tx_vfp_state =  _tx_misra_control_get();           \
+                                                        _tx_vfp_state =  _tx_vfp_state & ~((ULONG) 0x4);    \
+                                                        _tx_misra_control_set(_tx_vfp_state);               \
+                                                    }
+
+#endif
+
+/* A thread can be terminated by another thread, so we first check if it's self-terminating and not in an ISR.
+   If so, deactivate the FPU via CONTROL.FPCA. Otherwise we are in an interrupt or another thread is terminating
+   this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush 
+   the lazy FPU save, then restore the CONTROL.FPCA state. */
+
+#ifndef TX_MISRA_ENABLE
+
+#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr)  {                                                                                       \
+                                                    ULONG  _tx_system_state;                                                                \
+                                                        _tx_system_state =  TX_THREAD_GET_SYSTEM_STATE();                                   \
+                                                        if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr))  \
+                                                        {                                                                                   \
+                                                        ULONG  _tx_vfp_state;                                                               \
+                                                            _tx_vfp_state =  __get_control_value();                                         \
+                                                            _tx_vfp_state =  _tx_vfp_state & ~((ULONG) 0x4);                                \
+                                                            __set_control_value(_tx_vfp_state);                                             \
+                                                        }                                                                                   \
+                                                        else                                                                                \
+                                                        {                                                                                   \
+                                                        ULONG  _tx_fpccr;                                                                   \
+                                                            _tx_fpccr =  *((volatile ULONG *) 0xE000EF34);                                  \
+                                                            _tx_fpccr =  _tx_fpccr & ((ULONG) 0x01);                                        \
+                                                            if (_tx_fpccr == ((ULONG) 0x01))                                                \
+                                                            {                                                                               \
+                                                            ULONG _tx_vfp_state;                                                            \
+                                                                _tx_vfp_state = __get_control_value();                                      \
+                                                                _tx_vfp_state =  _tx_vfp_state & ((ULONG) 0x4);                             \
+                                                                TX_VFP_TOUCH();                                                             \
+                                                                if (_tx_vfp_state == ((ULONG) 0))                                           \
+                                                                {                                                                           \
+                                                                    _tx_vfp_state =  __get_control_value();                                 \
+                                                                    _tx_vfp_state =  _tx_vfp_state & ~((ULONG) 0x4);                        \
+                                                                    __set_control_value(_tx_vfp_state);                                     \
+                                                                }                                                                           \
+                                                            }                                                                               \
+                                                        }                                                                                   \
+                                                    }
+#else
+
+#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr)  {                                                                                       \
+                                                    ULONG  _tx_system_state;                                                                \
+                                                        _tx_system_state =  TX_THREAD_GET_SYSTEM_STATE();                                   \
+                                                        if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr))  \
+                                                        {                                                                                   \
+                                                        ULONG  _tx_vfp_state;                                                               \
+                                                            _tx_vfp_state =  _tx_misra_control_get();                                       \
+                                                            _tx_vfp_state =  _tx_vfp_state & ~((ULONG) 0x4);                                \
+                                                            _tx_misra_control_set(_tx_vfp_state);                                           \
+                                                        }                                                                                   \
+                                                        else                                                                                \
+                                                        {                                                                                   \
+                                                        ULONG  _tx_fpccr;                                                                   \
+                                                            _tx_fpccr =  _tx_misra_fpccr_get();                                             \
+                                                            _tx_fpccr =  _tx_fpccr & ((ULONG) 0x01);                                        \
+                                                            if (_tx_fpccr == ((ULONG) 0x01))                                                \
+                                                            {                                                                               \
+                                                            ULONG _tx_vfp_state;                                                            \
+                                                                _tx_vfp_state = _tx_misra_control_get();                                    \
+                                                                _tx_vfp_state =  _tx_vfp_state & ((ULONG) 0x4);                             \
+                                                                _tx_misra_vfp_touch();                                                      \
+                                                                if (_tx_vfp_state == ((ULONG) 0))                                           \
+                                                                {                                                                           \
+                                                                    _tx_vfp_state =  _tx_misra_control_get();                               \
+                                                                    _tx_vfp_state =  _tx_vfp_state & ~((ULONG) 0x4);                        \
+                                                                    _tx_misra_control_set(_tx_vfp_state);                                   \
+                                                                }                                                                           \
+                                                            }                                                                               \
+                                                        }                                                                                   \
+                                                    }
+#endif
+
+#else   /* No VFP in use */
+
+#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr)
+#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr)
+
+#endif  /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */
+
+
+/* Define the ThreadX object creation extensions for the remaining objects.  */
+
+#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr)
+#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr)
+#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr)
+#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr)
+#define TX_QUEUE_CREATE_EXTENSION(queue_ptr)
+#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr)
+#define TX_TIMER_CREATE_EXTENSION(timer_ptr)
+
+
+/* Define the ThreadX object deletion extensions for the remaining objects.  */
+
+#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr)
+#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr)
+#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr)
+#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr)
+#define TX_QUEUE_DELETE_EXTENSION(queue_ptr)
+#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr)
+#define TX_TIMER_DELETE_EXTENSION(timer_ptr)
+
+
+/* Define the get system state macro.  */
+
+#ifndef TX_THREAD_GET_SYSTEM_STATE
+#ifndef TX_MISRA_ENABLE
+
+#ifdef __CC_ARM /* ARM Compiler 5 */
+
+register unsigned int _ipsr __asm("ipsr");
+#define TX_THREAD_GET_SYSTEM_STATE()        (_tx_thread_system_state | _ipsr)
+
+#elif defined(__GNUC__) /* GCC and ARM Compiler 6 */
+
+__attribute__( ( always_inline ) ) static inline unsigned int __get_ipsr_value(void)
+{
+unsigned int  ipsr_value;
+    __asm__ volatile (" MRS  %0,IPSR ": "=r" (ipsr_value) );
+    return(ipsr_value);
+}
+
+#define TX_THREAD_GET_SYSTEM_STATE()        (_tx_thread_system_state | __get_ipsr_value())
+
+#elif defined(__ICCARM__)   /* IAR */
+
+#define TX_THREAD_GET_SYSTEM_STATE()        (_tx_thread_system_state | __get_IPSR())
+
+#endif  /* TX_THREAD_GET_SYSTEM_STATE for different compilers */
+
+#else   /* TX_MISRA_ENABLE is defined, use MISRA function. */
+ULONG   _tx_misra_ipsr_get(VOID);
+#define TX_THREAD_GET_SYSTEM_STATE()        (_tx_thread_system_state | _tx_misra_ipsr_get())
+#endif  /* TX_MISRA_ENABLE */
+#endif  /* TX_THREAD_GET_SYSTEM_STATE */
+
+
+/* Define the check for whether or not to call the _tx_thread_system_return function.  A non-zero value
+   indicates that _tx_thread_system_return should not be called. This overrides the definition in tx_thread.h
+   for Cortex-M since so we don't waste time checking the _tx_thread_system_state variable that is always
+   zero after initialization for Cortex-M ports. */
+
+#ifndef TX_THREAD_SYSTEM_RETURN_CHECK
+#define TX_THREAD_SYSTEM_RETURN_CHECK(c)    (c) = ((ULONG) _tx_thread_preempt_disable);
+#endif
+
+/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to
+   prevent early scheduling on Cortex-M parts.  */
+
+#define TX_PORT_SPECIFIC_POST_INITIALIZATION    _tx_thread_preempt_disable++;
+
+
+
+
+#ifndef TX_DISABLE_INLINE
+
+/* Define the TX_LOWEST_SET_BIT_CALCULATE macro for each compiler. */
+#ifdef __ICCARM__       /* IAR Compiler */
+#define TX_LOWEST_SET_BIT_CALCULATE(m, b)       (b) = (UINT) __CLZ(__RBIT((m)));
+#elif defined(__CC_ARM) /* AC5 Compiler */
+#define TX_LOWEST_SET_BIT_CALCULATE(m, b)       (b) = (UINT) __clz(__rbit((m)));
+#elif defined(__GNUC__) /* GCC and AC6 Compiler */
+#define TX_LOWEST_SET_BIT_CALCULATE(m, b)       __asm__ volatile (" RBIT %0,%1 ": "=r" (m) : "r" (m) ); \
+                                                __asm__ volatile (" CLZ  %0,%1 ": "=r" (b) : "r" (m) ); 
+#endif
+
+
+
+/* Define the interrupt disable/restore macros for each compiler. */
+
+#if defined(__GNUC__) || defined(__ICCARM__)
+
+/*** GCC/AC6 and IAR ***/
+
+__attribute__( ( always_inline ) ) static inline unsigned int __get_interrupt_posture(void)
+{
+unsigned int posture;
+#ifdef TX_PORT_USE_BASEPRI
+    __asm__ volatile ("MRS  %0, BASEPRI ": "=r" (posture));
+#else
+    __asm__ volatile ("MRS  %0, PRIMASK ": "=r" (posture));
+#endif
+    return(posture);
+}
+
+#ifdef TX_PORT_USE_BASEPRI
+__attribute__( ( always_inline ) ) static inline void __set_basepri_value(unsigned int basepri_value)
+{
+    __asm__ volatile ("MSR  BASEPRI,%0 ": : "r" (basepri_value));
+}
+#else
+__attribute__( ( always_inline ) ) static inline void __enable_interrupts(void)
+{
+    __asm__ volatile ("CPSIE  i": : : "memory");
+}
+#endif
+
+__attribute__( ( always_inline ) ) static inline void __restore_interrupt(unsigned int int_posture)
+{
+#ifdef TX_PORT_USE_BASEPRI
+    __set_basepri_value(int_posture);
+    //__asm__ volatile ("MSR  BASEPRI,%0": : "r" (int_posture): "memory");
+#else
+    __asm__ volatile ("MSR  PRIMASK,%0": : "r" (int_posture): "memory");
+#endif
+}
+
+__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void)
+{
+unsigned int int_posture;
+
+    int_posture = __get_interrupt_posture();
+
+#ifdef TX_PORT_USE_BASEPRI
+    __set_basepri_value(TX_PORT_BASEPRI);
+#else
+    __asm__ volatile ("CPSID i" : : : "memory");
+#endif
+    return(int_posture);
+}
+
+__attribute__( ( always_inline ) ) static inline void _tx_thread_system_return_inline(void)
+{
+unsigned int interrupt_save;
+
+    /* Set PendSV to invoke ThreadX scheduler.  */
+    *((volatile ULONG *) 0xE000ED04) = ((ULONG) 0x10000000);
+    if (__get_ipsr_value() == 0)
+    {
+        interrupt_save = __get_interrupt_posture();
+#ifdef TX_PORT_USE_BASEPRI
+        __set_basepri_value(0);
+#else
+        __enable_interrupts();
+#endif
+        __restore_interrupt(interrupt_save);
+    }
+}
+
+#define TX_INTERRUPT_SAVE_AREA                  UINT interrupt_save;
+#define TX_DISABLE                              interrupt_save =  __disable_interrupts();
+#define TX_RESTORE                              __restore_interrupt(interrupt_save);
+
+/*** End GCC/AC6 and IAR ***/
+
+#elif defined(__CC_ARM)
+
+/*** AC5 ***/
+
+static __inline unsigned int __get_interrupt_posture(void)
+{
+unsigned int posture;
+#ifdef TX_PORT_USE_BASEPRI
+    __asm__ volatile ("MRS  #posture, BASEPRI");
+#else
+    __asm__ volatile ("MRS  #posture, PRIMASK");
+#endif
+    return(posture);
+}
+
+#ifdef TX_PORT_USE_BASEPRI
+static __inline void __set_basepri_value(unsigned int basepri_value)
+{
+    __asm__ volatile ("MSR  BASEPRI, #basepri_value");
+}
+#endif
+
+static __inline unsigned int __disable_interrupts(void)
+{
+unsigned int int_posture;
+
+    int_posture = __get_interrupt_posture();
+
+#ifdef TX_PORT_USE_BASEPRI
+    __set_basepri_value(TX_PORT_BASEPRI);
+#else
+    __asm__ volatile ("CPSID i");
+#endif
+    return(int_posture);
+}
+
+static __inline void __restore_interrupt(unsigned int int_posture)
+{
+#ifdef TX_PORT_USE_BASEPRI
+    __set_basepri_value(int_posture);
+#else
+    __asm__ volatile ("MSR  PRIMASK, #int_posture");
+#endif
+}
+
+static void _tx_thread_system_return_inline(void)
+{
+unsigned int interrupt_save;
+
+    /* Set PendSV to invoke ThreadX scheduler.  */
+    *((volatile ULONG *) 0xE000ED04) = ((ULONG) 0x10000000);
+    if (_ipsr == 0)
+    {
+#ifdef TX_PORT_USE_BASEPRI
+        interrupt_save = __get_interrupt_posture();
+        __set_basepri_value(0);
+        __set_basepri_value(interrupt_save);
+#else
+        interrupt_save = __disable_irq();
+        __enable_irq();
+        if (interrupt_save != 0)
+            __disable_irq();
+#endif
+    }
+}
+
+
+#define TX_INTERRUPT_SAVE_AREA                  UINT interrupt_save;
+#define TX_DISABLE                              interrupt_save = __disable_interrupts();
+#define TX_RESTORE                              __restore_interrupt(interrupt_save);
+
+/*** End AC5 ***/
+
+#endif  /* Interrupt disable/restore macros for each compiler. */
+
+/* Redefine _tx_thread_system_return for improved performance.  */
+
+#define _tx_thread_system_return                _tx_thread_system_return_inline
+
+
+#else   /* TX_DISABLE_INLINE is defined */
+
+UINT                                            _tx_thread_interrupt_disable(VOID);
+VOID                                            _tx_thread_interrupt_restore(UINT previous_posture);
+
+#define TX_INTERRUPT_SAVE_AREA                  register UINT interrupt_save;
+
+#define TX_DISABLE                              interrupt_save = _tx_thread_interrupt_disable();
+#define TX_RESTORE                              _tx_thread_interrupt_restore(interrupt_save);
+#endif  /* TX_DISABLE_INLINE */
+
+
+/* Define FPU extension for the Cortex-M. Each is assumed to be called in the context of the executing
+   thread. These are no longer needed, but are preserved for backward compatibility only.  */
+
+void    tx_thread_fpu_enable(void);
+void    tx_thread_fpu_disable(void);
+
+
+/* Define the version ID of ThreadX.  This may be utilized by the application.  */
+
+#ifdef TX_THREAD_INIT
+CHAR                            _tx_version_id[] =
+                                    "Copyright (c) Microsoft Corporation. All rights reserved.  *  ThreadX Cortex-M7/GNU Version 6.4.0 *";
+#else
+#ifdef TX_MISRA_ENABLE
+extern  CHAR                    _tx_version_id[100];
+#else
+extern  CHAR                    _tx_version_id[];
+#endif
+#endif
+
+
+#endif
Index: ctrl/firmware/Main/CubeMX/charger.ioc
===================================================================
--- ctrl/firmware/Main/CubeMX/charger.ioc	(revision 53)
+++ ctrl/firmware/Main/CubeMX/charger.ioc	(revision 54)
@@ -51,21 +51,25 @@
 Mcu.Pin10=PC8
 Mcu.Pin11=PC9
-Mcu.Pin12=PA13(JTMS/SWDIO)
-Mcu.Pin13=PA14(JTCK/SWCLK)
-Mcu.Pin14=PC10
-Mcu.Pin15=PC11
-Mcu.Pin16=PC12
-Mcu.Pin17=PD2
-Mcu.Pin18=PD7
-Mcu.Pin19=PG9
+Mcu.Pin12=PA8
+Mcu.Pin13=PA13(JTMS/SWDIO)
+Mcu.Pin14=PA14(JTCK/SWCLK)
+Mcu.Pin15=PC10
+Mcu.Pin16=PC11
+Mcu.Pin17=PC12
+Mcu.Pin18=PD2
+Mcu.Pin19=PD7
 Mcu.Pin2=PC15-OSC32_OUT
-Mcu.Pin20=PG10
-Mcu.Pin21=PG11
-Mcu.Pin22=PG12
-Mcu.Pin23=PG13
-Mcu.Pin24=VP_RTC_VS_RTC_Activate
-Mcu.Pin25=VP_SYS_VS_Systick
-Mcu.Pin26=VP_MEMORYMAP_VS_MEMORYMAP
+Mcu.Pin20=PG9
+Mcu.Pin21=PG10
+Mcu.Pin22=PG11
+Mcu.Pin23=PG12
+Mcu.Pin24=PG13
+Mcu.Pin25=VP_RTC_VS_RTC_Activate
+Mcu.Pin26=VP_SYS_VS_tim7
+Mcu.Pin27=VP_MEMORYMAP_VS_MEMORYMAP
+Mcu.Pin28=VP_STMicroelectronics.X-CUBE-AZRTOS-H7_VS_RTOSJjThreadX_6.4.0_3.3.0
+Mcu.Pin29=VP_STMicroelectronics.X-CUBE-AZRTOS-H7_VS_FileOoSystemJjFileX_6.4.0_3.3.0
 Mcu.Pin3=PH0-OSC_IN
+Mcu.Pin30=VP_STMicroelectronics.X-CUBE-AZRTOS-H7_VS_FileOoSystemJjInterfaces_3.3.0_3.3.0
 Mcu.Pin4=PH1-OSC_OUT
 Mcu.Pin5=PE11
@@ -74,28 +78,40 @@
 Mcu.Pin8=PE14
 Mcu.Pin9=PE15
-Mcu.PinsNb=27
-Mcu.ThirdPartyNb=0
+Mcu.PinsNb=31
+Mcu.ThirdParty0=STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0
+Mcu.ThirdPartyNb=1
 Mcu.UserConstants=
 Mcu.UserName=STM32H723ZETx
 MxCube.Version=6.13.0
 MxDb.Version=DB.6.0.130
-NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
-NVIC.DMA1_Stream0_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true
-NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
+NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
+NVIC.DMA1_Stream0_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:true\:true
+NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
 NVIC.ForceEnableDMAVector=true
-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.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
+NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
+NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
+NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:false\:false
 NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4
-NVIC.SDMMC1_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true
-NVIC.SPI4_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true
-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.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
+NVIC.SDMMC1_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true\:true
+NVIC.SPI4_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:true\:true
+NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:false\:false
+NVIC.SavedPendsvIrqHandlerGenerated=true
+NVIC.SavedSvcallIrqHandlerGenerated=true
+NVIC.SavedSystickIrqHandlerGenerated=true
+NVIC.SysTick_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:true\:false
+NVIC.TIM7_IRQn=true\:15\:0\:false\:false\:true\:false\:false\:true\:true
+NVIC.TimeBase=TIM7_IRQn
+NVIC.TimeBaseIP=TIM7
+NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
 PA13(JTMS/SWDIO).Mode=Serial_Wire
 PA13(JTMS/SWDIO).Signal=DEBUG_JTMS-SWDIO
 PA14(JTCK/SWCLK).Mode=Serial_Wire
 PA14(JTCK/SWCLK).Signal=DEBUG_JTCK-SWCLK
+PA8.GPIOParameters=GPIO_PuPd,GPIO_Label
+PA8.GPIO_Label=SD_DETECT
+PA8.GPIO_PuPd=GPIO_PULLDOWN
+PA8.Locked=true
+PA8.Signal=GPIO_Input
 PC10.Mode=SD_4_bits_Wide_bus
 PC10.Signal=SDMMC1_D2
@@ -187,8 +203,8 @@
 ProjectManager.KeepUserCode=true
 ProjectManager.LastFirmware=true
-ProjectManager.LibraryCopy=0
+ProjectManager.LibraryCopy=1
 ProjectManager.MainLocation=Core/Src
 ProjectManager.NoMain=false
-ProjectManager.PreviousToolchain=
+ProjectManager.PreviousToolchain=STM32CubeIDE
 ProjectManager.ProjectBuild=false
 ProjectManager.ProjectFileName=charger.ioc
@@ -197,10 +213,10 @@
 ProjectManager.RegisterCallBack=
 ProjectManager.StackSize=0x400
-ProjectManager.TargetToolchain=EWARM V8.50
+ProjectManager.TargetToolchain=STM32CubeIDE
 ProjectManager.ToolChainLocation=
 ProjectManager.UAScriptAfterPath=
 ProjectManager.UAScriptBeforePath=
-ProjectManager.UnderRoot=false
-ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_RTC_Init-RTC-false-HAL-true,5-MX_SPI4_Init-SPI4-false-HAL-true,0-MX_CORTEX_M7_Init-CORTEX_M7-false-HAL-true
+ProjectManager.UnderRoot=true
+ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_RTC_Init-RTC-false-HAL-true,5-MX_SPI4_Init-SPI4-false-HAL-true,6-MX_SDMMC1_SD_Init-SDMMC1-false-HAL-true,0-MX_CORTEX_M7_Init-CORTEX_M7-false-HAL-true
 RCC.ADCFreq_Value=166666666.66666666
 RCC.AHB12Freq_Value=100000000
@@ -293,9 +309,37 @@
 SPI4.VirtualNSS=VM_NSSHARD
 SPI4.VirtualType=VM_MASTER
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.FX_APP_MEM_POOL_SIZE=8192
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.FX_DRIVER_SD_INIT=0
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.FX_ENABLE_EXFAT=1
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.FX_ENABLE_FAULT_TOLERANT=1
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.FX_FAULT_TOLERANT=1
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.FX_FAULT_TOLERANT_DATA=1
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.FileOoSystemJjFileX_Checked=true
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.FileOoSystemJjInterfaces_Checked=true
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.FileXCcFileOoSystemJjFileXJjCore=true
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.IPParameters=TX_APP_MEM_POOL_SIZE,FX_APP_MEM_POOL_SIZE,TX_APP_GENERATE_INIT_CODE,TX_APP_CREATION,TX_ENABLE_STACK_CHECKING,TX_NO_FILEX_POINTER,TX_LOW_POWER,FX_ENABLE_EXFAT,FX_ENABLE_FAULT_TOLERANT,FX_FAULT_TOLERANT,FX_FAULT_TOLERANT_DATA,FX_DRIVER_SD_INIT,ThreadXCcRTOSJjThreadXJjCore,ThreadXCcRTOSJjThreadXJjLowOoPowerOosupport,FileXCcFileOoSystemJjFileXJjCore,InterfacesCcFileOoSystemJjFileXOoSDOointerface
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.InterfacesCcFileOoSystemJjFileXOoSDOointerface=true
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.RTOSJjThreadX_Checked=true
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.TX_APP_CREATION=1
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.TX_APP_GENERATE_INIT_CODE=false
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.TX_APP_MEM_POOL_SIZE=8192
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.TX_ENABLE_STACK_CHECKING=1
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.TX_LOW_POWER=1
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.TX_NO_FILEX_POINTER=1
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.ThreadXCcRTOSJjThreadXJjCore=true
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0.ThreadXCcRTOSJjThreadXJjLowOoPowerOosupport=true
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0_IsAnAzureRtosMw=true
+STMicroelectronics.X-CUBE-AZRTOS-H7.3.3.0_SwParameter=InterfacesCcFileOoSystemJjFileXOoSDOointerface\:true;ThreadXCcRTOSJjThreadXJjLowOoPowerOosupport\:true;FileXCcFileOoSystemJjFileXJjCore\:true;ThreadXCcRTOSJjThreadXJjCore\:true;
 VP_MEMORYMAP_VS_MEMORYMAP.Mode=CurAppReg
 VP_MEMORYMAP_VS_MEMORYMAP.Signal=MEMORYMAP_VS_MEMORYMAP
 VP_RTC_VS_RTC_Activate.Mode=RTC_Enabled
 VP_RTC_VS_RTC_Activate.Signal=RTC_VS_RTC_Activate
-VP_SYS_VS_Systick.Mode=SysTick
-VP_SYS_VS_Systick.Signal=SYS_VS_Systick
+VP_STMicroelectronics.X-CUBE-AZRTOS-H7_VS_FileOoSystemJjFileX_6.4.0_3.3.0.Mode=FileOoSystemJjFileX
+VP_STMicroelectronics.X-CUBE-AZRTOS-H7_VS_FileOoSystemJjFileX_6.4.0_3.3.0.Signal=STMicroelectronics.X-CUBE-AZRTOS-H7_VS_FileOoSystemJjFileX_6.4.0_3.3.0
+VP_STMicroelectronics.X-CUBE-AZRTOS-H7_VS_FileOoSystemJjInterfaces_3.3.0_3.3.0.Mode=FileOoSystemJjInterfaces
+VP_STMicroelectronics.X-CUBE-AZRTOS-H7_VS_FileOoSystemJjInterfaces_3.3.0_3.3.0.Signal=STMicroelectronics.X-CUBE-AZRTOS-H7_VS_FileOoSystemJjInterfaces_3.3.0_3.3.0
+VP_STMicroelectronics.X-CUBE-AZRTOS-H7_VS_RTOSJjThreadX_6.4.0_3.3.0.Mode=RTOSJjThreadX
+VP_STMicroelectronics.X-CUBE-AZRTOS-H7_VS_RTOSJjThreadX_6.4.0_3.3.0.Signal=STMicroelectronics.X-CUBE-AZRTOS-H7_VS_RTOSJjThreadX_6.4.0_3.3.0
+VP_SYS_VS_tim7.Mode=TIM7
+VP_SYS_VS_tim7.Signal=SYS_VS_tim7
 board=custom
