/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * This notice applies to any and all portions of this file
  * that are not between comment pairs USER CODE BEGIN and
  * USER CODE END. Other portions of this file, whether 
  * inserted by the user or by software development tools
  * are owned by their respective copyright owners.
  *
  * Copyright (c) 2019 STMicroelectronics International N.V. 
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without 
  * modification, are permitted, provided that the following conditions are met:
  *
  * 1. Redistribution of source code must retain the above copyright notice, 
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
  * 3. Neither the name of STMicroelectronics nor the names of other 
  *    contributors to this software may be used to endorse or promote products 
  *    derived from this software without specific written permission.
  * 4. This software, including modifications and/or derivative works of this 
  *    software, must execute solely and exclusively on microcontroller or
  *    microprocessor devices manufactured by or for STMicroelectronics.
  * 5. Redistribution and use of this software other than as permitted under 
  *    this license is void and will automatically terminate your rights under 
  *    this license. 
  *
  * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" 
  * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT 
  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
  * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
  * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT 
  * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#define MAIN_C
#include "core/main.h"
#include "usb_device.h"
#include "core/gpio.h"
//#include "drivers/spi/spi.h"
#include "drivers/flash/base/extmem_flash.h"
#include "app/usbapp/usb_application_flash.h"
#include "app/usbapp/usb_application_enumspy.h"
#include "app/thermo/tsensor.h"
#include "app/thermo/NTCtsensor.h"
#include "app/control_table/control_table.h"
#include "app/automat/automat.h"
#include "app/led/led.h"
#include "app/vbat/vbat.h"
#include "app/nfm/nfm_base.h"
#include "stm32l1xx_hal_i2c.h"
#include "drivers/keycontrol/keycontrol.h"
#include "drivers/adc/adc.h"
#include "drivers/power_management/power_management.h"
#include "app/nfm/nfm_base.h"
#include "core/sleep_and_exti.h"
#include "core_cm3.h" // SCB, SCB_SHCSR_USGFAULTENA
#include "stm32l1xx_hal_conf.h" // TICK_INT_PRIORITY()
#include "stm32l1xx_hal_cortex.h" // HAL_NVIC_SetPriority()
#include "core/config.h"
#include "core/config_pins.h"
#include "core/debugpins.h"
#include "core/csect.h"
#include "my_assert.h"
#include "static_assert.h"
#if CONFIG_NOIRQ_SNPRINTF
#include <stdarg.h> // va_list
#endif

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#if CONFIG_STACKOVERFLOW_CONTROLSECTION
// CSTACK overflow protection/notification section
// Place specially prepared signature into the area before CSTACK block in RAM
// to provide easy control of stack overflow.
extern void size_cstack_protect; // Size of area: see .icf
#pragma location=".cstacktail"   // Protection section name

// Fill the area with signature:
static __root volatile uint32_t cstacktail[8] = { 
   0xEFBEADDE, 0xEFBEADDE, 0xEFBEADDE, 0xEFBEADDE, 0xEFBEADDE, 0xEFBEADDE, 0xEFBEADDE, 0xEFBEADDE 
};
#endif
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#if DEBUG_TRACE
static volatile int debugcnt = 0;
static int getdebugcnt()
{
   int r = 0;
   __DI__ r = debugcnt; debugcnt++; __EI__
   return r;
}
#define MAX_TRACEPOINTS 16
static volatile int g_debug_tracepoints[ MAX_TRACEPOINTS ] = {0};
void debug_trace( int idx )
{
   if( idx < MAX_TRACEPOINTS )
     g_debug_tracepoints[idx] =  getdebugcnt();
}
#endif
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

static volatile bool xUSBPlugged = false;
static volatile bool xUSBActive = false;
static volatile bool xAutomatActive = false;

//static volatile bool g_bHarmUpEnable = false;
//static volatile uint32_t g_nHarmUpTimestamp = 0ul;
#if CONFIG_REBOOT_FEATURE
static volatile bool g_RebootRequest = false;
#endif
#if CONFIG_VBATMON
static volatile uint32_t xVBatVoltage = 0;
#endif

#if CONFIG_USB
static eNFMUSBInterface_t usbDeviceInterface = 
  #if CONFIG_USB_OVERRIDE_IFACE_USBTMC
  eNFM_IfaceUSBTMC;
  #else
  eNFM_IfaceUSBVendor;
  #endif
#endif

void SystemClock_Config(void);
void SystemClock_Config_Low(void);

#if CONFIG_EXTREME_LOWPOWTEST
void GPIO_Init_LowPowerTest(void)
{
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE(); 
  
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /*Configure all GPIO pins of port C*/
  GPIO_InitStruct.Pin = GPIO_PIN_All;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure all GPIO pins of port A ,except PA13 and PA14 (SWD)*/
  GPIO_InitStruct.Pin = GPIO_PIN_All & (~(GPIO_PIN_13|GPIO_PIN_14));
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure all GPIO pins of port B*/
  GPIO_InitStruct.Pin = GPIO_PIN_All;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

   /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_DISABLE();
  __HAL_RCC_GPIOH_CLK_DISABLE();
  __HAL_RCC_GPIOA_CLK_DISABLE();
  __HAL_RCC_GPIOB_CLK_DISABLE();
  

}
#endif

#if CONFIG_EXTREME_LOWPOWTEST
static void main_extreme_low_power()
{
  __HAL_RCC_COMP_CLK_ENABLE();
  __HAL_RCC_SYSCFG_CLK_ENABLE();
  __HAL_RCC_PWR_CLK_ENABLE();

  GPIO_Init_LowPowerTest();

  SystemClock_Config_Low();

  SleepManagerHandle.Init();
  
  while(1)
    SleepManagerHandle.Sleep();
}
#endif

/**
  * @brief  The application entry point.
  * @retval int
  */ 

#if CONFIG_USB_DYNAMIC_CLOCK_ADOPT && CONFIG_USB
static void SysConfig_USBConnected()
{
   __DI__
   // need to disable interrupts to setup SystemTick correctly

   SystemClock_Config();

   #if CONFIG_SYSTICK == CONFIG_SYSTICK_NORMAL
       #if CONFIG_SYSTICK_BOOST_MULTIPLIER
          HAL_SYSTICK_Config( SystemCoreClock / 1000 / (CONFIG_SYSTICK_BOOST_MULTIPLIER) ); // CONFIG_SYSTICK_BOOST_MULTIPLIER times per 1ms   
          HAL_NVIC_SetPriority( SysTick_IRQn, TICK_INT_PRIORITY, 0 );
       #else
          HAL_SYSTICK_Config( SystemCoreClock / 1000 ); // 1 times per 1ms
          HAL_NVIC_SetPriority( SysTick_IRQn, TICK_INT_PRIORITY, 0 );
       #endif
   #else
     HAL_SYSTICK_Config( SystemCoreClock / 1000 ); // 1 times per 1ms
     HAL_NVIC_SetPriority( SysTick_IRQn, TICK_INT_PRIORITY, 0 );
   #endif

   __EI__

   #if CONFIG_EXTMEM
   /* Reinitialize the external memory */
   ExtMemHandle.Init();
   #endif

   #if CONFIG_NFMBASECLASS
   #if CONFIG_USB_OVERRIDE_IFACE_USBTMC
   usbDeviceInterface = eNFM_IfaceUSBTMC;
   #else
   #if CONFIG_USB_OVERRIDE_IFACE_VENDOR
   usbDeviceInterface = eNFM_IfaceUSBVendor;
   #else
   if( ! NFMClass->methods.usbInterface.getInterface( &usbDeviceInterface ) )
   {
       usbDeviceInterface = eNFM_IfaceUSBVendor;
   }
   #endif
   #endif
   #else
   #if CONFIG_USB_OVERRIDE_IFACE_USBTMC
   usbDeviceInterface = eNFM_IfaceUSBTMC;
   #else
   usbDeviceInterface = eNFM_IfaceUSBVendor;
   #endif
   #endif

   USB_Device_Init( (eUSBDeviceInterface_t)usbDeviceInterface );
}
#endif

#if CONFIG_USB_DYNAMIC_CLOCK_ADOPT && CONFIG_USB
static void SysConfig_USBDisconnected()
{
   USB_Device_DeInit();

   #if CONFIG_EXTMEM
   #if CONFIG_AUTOMAT_MODE == 0
   /* Deinitialize the external memory */
   ExtMemHandle.DeInit( true );
   #else
   // later...
   #endif
   #endif

   __DI__
   // need to disable interrupts to setup SystemTick correctly

   SystemClock_Config_Low();

   #if CONFIG_SYSTICK == CONFIG_SYSTICK_NORMAL
       #if CONFIG_SYSTICK_BOOST_MULTIPLIER
          HAL_SYSTICK_Config( SystemCoreClock / 1000 / (CONFIG_SYSTICK_BOOST_MULTIPLIER) ); // CONFIG_SYSTICK_BOOST_MULTIPLIER times per 1ms   
          HAL_NVIC_SetPriority( SysTick_IRQn, TICK_INT_PRIORITY, 0 );
       #else
          HAL_SYSTICK_Config( SystemCoreClock / 1000 ); // 1 times per 1ms
          HAL_NVIC_SetPriority( SysTick_IRQn, TICK_INT_PRIORITY, 0 );
       #endif
   #else
   HAL_SYSTICK_Config( SystemCoreClock / 1000 ); // 1 times per 1ms
   HAL_NVIC_SetPriority( SysTick_IRQn, TICK_INT_PRIORITY, 0 );
   #endif

   __EI__
}
#endif

#if CONFIG_USBIRQ_TEST
void USBIRQ_Test()
{
   #warning Тест ошибки чтения памяти через USB при длительном запрещении прерываний USB
   HAL_NVIC_DisableIRQ( USB_LP_IRQn );
   for( size_t i = 0; i < 1024; ++i ) __NOP();
   HAL_NVIC_EnableIRQ( USB_LP_IRQn );
}
#endif

//#if CONFIG_HARMUP_INTERVAL > 0
//void harmup_init()
//{
//  g_bHarmUpEnable = true;
//  g_nHarmUpTimestamp = HAL_GetTick();
//
//  LEDHandle.SetHarmupStatus( !g_bHarmUpEnable ); 
//}
//
//void harmup_serve()
//{
//  if( g_bHarmUpEnable )
//  {
//      if( (HAL_GetTick() - g_nHarmUpTimestamp)/1000 > (CONFIG_HARMUP_INTERVAL) )
//      {
//          g_bHarmUpEnable = false;
//
//          LEDHandle.SetHarmupStatus( !g_bHarmUpEnable ); 
//      }
//  }
//}
//#endif

#if CONFIG_REBOOT_FEATURE
void RebootRequest()
{
  __DI__
  g_RebootRequest = true;  // set global flag
  __EI__
}
#endif

#if CONFIG_NOIRQ_SNPRINTF
// @_snprintf
// Limits the depth of stack usage during @snprintf by
// disabling interrupts.
int _snprintf( char * buffer, size_t szBuffer, const char * format, ... )
{
  int rc;
  __DI__
  va_list args;
  va_start (args, format);
  rc = vsnprintf(buffer, szBuffer, format, args);  
  va_end (args);
  __EI__
  return rc;
}
#endif

int main(void)
{
  // Validate the CSTACK protection area size
  my_assert( sizeof(cstacktail) == (size_t)(&size_cstack_protect) );
  
  /* MCU Configuration--------------------------------------------------------*/
  #if CONFIG_USB && CONFIG_AUTOMAT_MODE && CONFIG_AUTOMAT_MODE_USB_POWERBANK
  bool bEnumerateAwaiting = false;
  bool bUsbPowerBankConnected = false;
  #endif
  
  // Enable MemFault, BusFault and UsgFault handlers
  SCB->SHCSR |= SCB_SHCSR_USGFAULTENA;
  SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA;
  SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA;

  #if CONFIG_EXTREME_LOWPOWTEST
  main_extreme_low_power();
  #endif

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  // Initialize GPIO
  // Need init before SysClock and USB to determine the clock freq
  MX_GPIO_Init();
  
  #if CONFIG_ADC
  ADC1Handle.Init(eADCRes_12bit);
  #endif
  
  #if CONFIG_PM
  // Initialization and set of all pins except FPGA power control pin. It will be set separately after all init routine.
  PMHandle.Init();
  HAL_Wait_100us(500); // Задержка 0.5 с
  #endif
  
  #if CONFIG_TABLE
  TableHandle.Init();
  #endif
  
  #if CONFIG_PM
  PMHandle.INPTRG_EN(false);
  #endif
  
  #if CONFIG_LEDS
  LEDHandle.Init();
  setLedColor(eLed_color_grn);
  #endif
  
  //DebugPins_Init();

  #if CONFIG_SLEEPMANAGER
  // Initialize Sleep/Wakeup manager
  // Need init before SysClock and USB to determine the clock freq
  SleepManagerHandle.Init();
  #endif

  // Initialize system clock
  #if CONFIG_USB_DYNAMIC_CLOCK_ADOPT && CONFIG_USB
      // First USB presense detecting...

      #if CONFIG_SLEEPMANAGER
      xUSBPlugged = SleepManagerHandle.GetUSBPlugged();
      #else
      xUSBPlugged = true;
      #endif

      __DI__
      // need to disable interrupts to setup SystemTick correctly

      if( xUSBPlugged )
          SystemClock_Config();
      else
          SystemClock_Config_Low();

      #if CONFIG_SYSTICK == CONFIG_SYSTICK_NORMAL
       #if CONFIG_SYSTICK_BOOST_MULTIPLIER
          HAL_SYSTICK_Config( SystemCoreClock / 1000 / (CONFIG_SYSTICK_BOOST_MULTIPLIER) ); // CONFIG_SYSTICK_BOOST_MULTIPLIER times per 1ms   
          HAL_NVIC_SetPriority( SysTick_IRQn, TICK_INT_PRIORITY, 0 );
       #endif
     #endif

     __EI__

  #else
      __DI__
      // need to disable interrupts to setup SystemTick correctly

      /* Configure the system clock */
      #if (CONFIG_SYSCLOCK  == CONFIG_SYSCLOCK_NORMAL) || (CONFIG_SYSCLOCK  == CONFIG_SYSCLOCK_HIGH)
      SystemClock_Config();
      #endif
      #if CONFIG_SYSCLOCK  == CONFIG_SYSCLOCK_LOW
      #if CONFIG_USB
      #warning Invalid clock while USB is used!
      #endif
      SystemClock_Config_Low();
      #endif
    //-----------------------
      #if CONFIG_USB       //
      xUSBPlugged = true;  //
      #endif               //
    //-----------------------
     #if CONFIG_SYSTICK == CONFIG_SYSTICK_NORMAL
       #if CONFIG_SYSTICK_BOOST_MULTIPLIER
          HAL_SYSTICK_Config( SystemCoreClock / 1000 / (CONFIG_SYSTICK_BOOST_MULTIPLIER) ); // CONFIG_SYSTICK_BOOST_MULTIPLIER times per 1ms   
          HAL_NVIC_SetPriority( SysTick_IRQn, TICK_INT_PRIORITY, 0 );
       #endif
     #endif

     __EI__
  #endif

  // Configure the system tick
  #if CONFIG_SYSTICK == CONFIG_SYSTICK_NORMAL  
  #endif
 
  #if CONFIG_EXTMEM
  /* Initialize External Non-Volatile memory */
  ExtMemHandle.Init();
  #endif

  #if CONFIG_NFMBASECLASS
  nfmbase_init();
  #endif
  
  #if CONFIG_NFMBASECLASS && CONFIG_KEYSW
  #error The modes "CONFIG_NFMBASECLASS" and "CONFIG_KEYSW" are incompatible
  #endif

  #if CONFIG_SYSTICK  == CONFIG_SYSTICK_LOW    
  // power consumption debug
  HAL_SYSTICK_Config( 5000000 );
  HAL_NVIC_SetPriority( SysTick_IRQn, TICK_INT_PRIORITY, 0 );
  #endif

  #if CONFIG_USB_OVERRIDE_IFACE_USBTMC && CONFIG_USB_OVERRIDE_IFACE_VENDOR
  #error Invalid configuration: choose only one overriding interface.
  #endif
  #if !CONFIG_USB_USBTMC_ENABLE && CONFIG_USB_OVERRIDE_IFACE_USBTMC
  #error Invalid configuration: USBTMC is disabled and can not be overriding interface.
  #endif

  #if CONFIG_USB
    // Install USB Hook
    #if CONFIG_SLEEPMANAGER
    SleepManagerHandle.SetUSBWakeup( true );
    #endif

    #if CONFIG_USB_DYNAMIC_CLOCK_ADOPT        
        if( xUSBPlugged )
        {
            #if CONFIG_NFMBASECLASS
            #if CONFIG_USB_OVERRIDE_IFACE_USBTMC
            usbDeviceInterface = eNFM_IfaceUSBTMC;
            #else
            #if CONFIG_USB_OVERRIDE_IFACE_VENDOR
            usbDeviceInterface = eNFM_IfaceVendor;
            #else
            if( ! NFMClass->methods.usbInterface.getInterface( &usbDeviceInterface ) )
            {
                usbDeviceInterface = eNFM_IfaceUSBVendor;
            }
            #endif
            #endif
            #else
            #if CONFIG_USB_OVERRIDE_IFACE_USBTMC
            usbDeviceInterface = eNFM_IfaceUSBTMC;
            #else
            usbDeviceInterface = eNFM_IfaceUSBVendor;
            #endif
            #endif

            // Initialize USB
            USB_Device_Init( (eUSBDeviceInterface_t)usbDeviceInterface );
        }
    #else
        #if CONFIG_NFMBASECLASS
        #if CONFIG_USB_OVERRIDE_IFACE_USBTMC
        usbDeviceInterface = eNFM_IfaceUSBTMC;
        NFMClass->methods.usbInterface.setInterface( usbDeviceInterface );
        #else
        #if CONFIG_USB_OVERRIDE_IFACE_VENDOR
        usbDeviceInterface = eNFM_IfaceUSBVendor;
        NFMClass->methods.usbInterface.setInterface( usbDeviceInterface );
        #else
        if( ! NFMClass->methods.usbInterface.getInterface( &usbDeviceInterface ) )
        {
            usbDeviceInterface = eNFM_IfaceUSBVendor;
        }
        #endif
        #endif
        #else
        #if CONFIG_USB_OVERRIDE_IFACE_USBTMC
        usbDeviceInterface = eNFM_IfaceUSBTMC;
        #else
        usbDeviceInterface = eNFM_IfaceUSBVendor;
        #endif
        #endif
        // Initialize USB once and forever
        USB_Device_Init( (eUSBDeviceInterface_t)usbDeviceInterface );
    #endif

  #endif

  #if CONFIG_AUTOMAT_MODE
  #if CONFIG_USB_DYNAMIC_CLOCK_ADOPT && CONFIG_USB
      if( !xUSBPlugged ) 
      {
  #endif
          #if CONFIG_SYSTICK_BOOST_MULTIPLIER
          xAutomatActive = NFMAutomatHandle.Init( CONFIG_SYSTICK_BOOST_MULTIPLIER );
          #else
          xAutomatActive = NFMAutomatHandle.Init( 1 );
          #endif
          #if CONFIG_LEDS
          LEDHandle.SetAutomatAlarm( !xAutomatActive );
          #endif
  #if CONFIG_USB_DYNAMIC_CLOCK_ADOPT && CONFIG_USB
      }
  #endif
  #endif

  #if CONFIG_AUTOMAT_MODE && CONFIG_AUTOMAT_MODE_USB_POWERBANK
  if( xUSBPlugged )
  {
      #warning Feature: Запрошена функция работы от USB-Power-Bank, т.е. питание на разъем USB подано, и событие Plugged сработало, но требуется работать в режиме Automat если нет энумерации в течение некоторого времени.
      bEnumerateAwaiting = true;  // mode: awaiting for enumerating
      UsbApplicationEnumSpyHandle.startEnumerateTimeout();
  }
  #endif

  #if CONFIG_LEDS
  LEDHandle.SetUSBConnectivity( xUSBPlugged );
  #endif

  #if CONFIG_NFMBASECLASS && CONFIG_NFMBASECLASS_TEST
  nfmbase_test();
  #endif

  #if CONFIG_SCPI_TEST
  extern void scpi_parser_test();
  extern void scpi_command_test();
  //scpi_parser_test();
  scpi_command_test();
  #endif

  #if CONFIG_HARMUP_INTERVAL > 0
  // initialize HarmUp procedure
  LEDHandle.harmup_init();
  #else
  LEDHandle.SetHarmupStatus( true );
  #endif

  #if CONFIG_REBOOT_FEATURE
  uint32_t l_RebootRequestStamp = 0;
  bool l_RebootRequest = false;
  #endif
  
//  
  
  while (1)
  {
     #if CONFIG_USB
     #if CONFIG_EXTMEM
     // serve USB application
     if( xUSBPlugged )
     {
         usb_application_flash_serve();
     }
     #endif
     #endif
     
     #if CONFIG_ADC && CONFIG_PM
     //PMHandle.PM_Serve();
     #endif
     
     #if CONFIG_SLEEPMANAGER && CONFIG_SLEEPMANAGER_EXTRASLEEP
     SleepManagerHandle.Sleep();
     #endif
     
     #if CONFIG_USB_DYNAMIC_CLOCK_ADOPT && CONFIG_USB
     #if CONFIG_SLEEPMANAGER
     SleepManagerHandle.Sleep();
     #endif
     
     #if CONFIG_USBIRQ_TEST
     USBIRQ_Test();
     #endif

     #if CONFIG_HARMUP_INTERVAL > 0
     // serve for HarmUp procedure
     LEDHandle.harmup_serve();
     #endif

     {  
         #if CONFIG_SLEEPMANAGER
         bool temp__xUSBPlugged = SleepManagerHandle.GetUSBPlugged();
         xUSBActive = SleepManagerHandle.GetUSBActive();
         #else
         bool temp__xUSBPlugged = true;
         xUSBActive = true;
         #endif
         
         #if CONFIG_AUTOMAT_MODE && CONFIG_AUTOMAT_MODE_USB_POWERBANK
         if( !temp__xUSBPlugged && bUsbPowerBankConnected )
         {
             // When USB-unplug event detected -> reset @bUsbPowerBankConnected
             bUsbPowerBankConnected = false;
         }

         if( bEnumerateAwaiting && temp__xUSBPlugged )
         {
             // USB is plugged and the device waits for enumeration
             if( UsbApplicationEnumSpyHandle.checkEnumerateTimeout() )
             {
                 bEnumerateAwaiting = false;

                 // The host had not enumerate the device for a long time                 
                 bUsbPowerBankConnected = true;
             }
         }
         #endif

         #if CONFIG_AUTOMAT_MODE && CONFIG_AUTOMAT_MODE_USB_POWERBANK
         // Emulate USB unplugged when USB-Power-Bank mode is active 
         if( bUsbPowerBankConnected )
         {
             // Emulate USB unplugged
             temp__xUSBPlugged = false;
         }
         #endif

         #if CONFIG_REBOOT_FEATURE
         {
            __DI__
            if( g_RebootRequest )
            {
                l_RebootRequest = true;   // set local flag
                #if CONFIG_REBOOT_FEATURE
                l_RebootRequestStamp = HAL_GetTick();
                #endif
                g_RebootRequest = false;  // reset global flag
            }
            __EI__
         
            if( l_RebootRequest )
            {
                #if CONFIG_REBOOT_FEATURE
                if( HAL_GetTick() - l_RebootRequestStamp > CONFIG_REBOOT_FEATURE_DELAY )
                {
                #endif
                if( xUSBPlugged && xUSBActive )
                {
                    temp__xUSBPlugged = false; // force reset PLUG indicator
                }
                #if CONFIG_REBOOT_FEATURE
                }
                #endif
            }
         }
         #endif

         if( xUSBPlugged != temp__xUSBPlugged )
         {
             if( temp__xUSBPlugged )
             {   
                 #if CONFIG_AUTOMAT_MODE
                 NFMAutomatHandle.DeInit();
                 xAutomatActive = false;
                 #endif

                 #if CONFIG_LEDS
                 LEDHandle.SetAutomatAlarm( false );
                 #endif

                 SysConfig_USBConnected();
                 
                 #if CONFIG_USB && CONFIG_AUTOMAT_MODE && CONFIG_AUTOMAT_MODE_USB_POWERBANK
                 #warning Feature: Запрошена функция работы от USB-Power-Bank, т.е. питание на разъем USB подано, и событие Plugged сработало, но требуется работать в режиме Automat если нет энумерации в течение некоторого времени.
                 bEnumerateAwaiting = true;  // mode: awaiting for enumerating
                 UsbApplicationEnumSpyHandle.startEnumerateTimeout();
                 #endif
             }
             else
             {
                 SysConfig_USBDisconnected();
                 
                 #if CONFIG_AUTOMAT_MODE && CONFIG_AUTOMAT_MODE_USB_POWERBANK
                 UsbApplicationEnumSpyHandle.stopEnumerateTimeout();
                 bEnumerateAwaiting = false;
                 #endif

                 #if CONFIG_AUTOMAT_MODE
                 #if CONFIG_SYSTICK_BOOST_MULTIPLIER
                 xAutomatActive = NFMAutomatHandle.Init( CONFIG_SYSTICK_BOOST_MULTIPLIER );
                 #else
                 xAutomatActive = NFMAutomatHandle.Init( 1 );
                 #endif
                 #if CONFIG_LEDS
                 LEDHandle.SetAutomatAlarm( !xAutomatActive );
                 #endif
                 #endif

                 #if CONFIG_EXTMEM
                 #if CONFIG_AUTOMAT_MODE
                 /* Deinitialize the external memory */
                 ExtMemHandle.DeInit( true );                 
                 #endif
                 #endif
             }

             #if CONFIG_SYSTICK  == CONFIG_SYSTICK_LOW  
             // power consumption debug
             HAL_SYSTICK_Config( 5000000 );
             HAL_NVIC_SetPriority( SysTick_IRQn, TICK_INT_PRIORITY, 0 );
             #endif
             
             xUSBPlugged = temp__xUSBPlugged;

             #if CONFIG_LEDS
             LEDHandle.SetUSBConnectivity( xUSBPlugged );
             #endif
         }
         #if CONFIG_REBOOT_FEATURE
         if( l_RebootRequest )
         {
              #if CONFIG_REBOOT_FEATURE
              if( HAL_GetTick() - l_RebootRequestStamp > CONFIG_REBOOT_FEATURE_DELAY )
              {
              #endif

              __DI__

              #if CONFIG_EXTMEM
              /* Deinitialize External Non-Volatile memory */
              ExtMemHandle.DeInit( true );
              #endif

              // Deinitialize USB
              USB_Device_DeInit();

              HAL_NVIC_SystemReset();

              __EI__

              while(1);
              #if CONFIG_REBOOT_FEATURE
              }
              #endif
         }
         #endif
     }
     #else
       #if CONFIG_SLEEPMANAGER
       xUSBPlugged = SleepManagerHandle.GetUSBPlugged();
       xUSBActive = SleepManagerHandle.GetUSBActive();
       SleepManagerHandle.Sleep();
       #else
       // ACM2543: USB is always connected
       /*xUSBPlugged = false;*/
       /*xUSBActive = false;*/
       #endif

       #if CONFIG_HARMUP_INTERVAL > 0
       // serve for HarmUp procedure
       LEDHandle.harmup_serve();
       #endif

       #if CONFIG_REBOOT_FEATURE
       {
          __DI__
          if( g_RebootRequest )
          {
              l_RebootRequest = true;   // set local flag
              #if CONFIG_REBOOT_FEATURE
              l_RebootRequestStamp = HAL_GetTick();
              #endif
              g_RebootRequest = false;  // reset global flag
          }
          __EI__

          if( l_RebootRequest )
          {
              #if CONFIG_REBOOT_FEATURE
              if( HAL_GetTick() - l_RebootRequestStamp > CONFIG_REBOOT_FEATURE_DELAY )
              {
              #endif

              __DI__

              #if CONFIG_EXTMEM
              /* Deinitialize External Non-Volatile memory */
              ExtMemHandle.DeInit( true );
              #endif

              // Deinitialize USB
              USB_Device_DeInit();

              HAL_NVIC_SystemReset();

              __EI__

              while(1);
              #if CONFIG_REBOOT_FEATURE
              }
              #endif
          }
       }
       #endif
     #endif

     #if CONFIG_AUTOMAT_MODE
     if( !xUSBPlugged )
     {
         #if CONFIG_TSENSOR
         int8_t automat_temperature = THERMO_SENSOR_AVGTEMP_TO_INT( ThermoSensor.GetTempAVG() );
         #else
         int8_t automat_temperature = THERMO_SENSOR_AVGTEMP_TO_INT( THERMO_SENSOR_INVALID_TEMP_VALUE );
         #endif
         NFMAutomatHandle.TemperatureUpdate( automat_temperature, ThermoSensor.GetReady() );
     }
     #endif

  }  
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
#if (CONFIG_SYSCLOCK == CONFIG_SYSCLOCK_NORMAL) || (CONFIG_SYSCLOCK == CONFIG_SYSCLOCK_HIGH) || CONFIG_USB_DYNAMIC_CLOCK_ADOPT
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  
  /**Configure the main internal regulator output voltage 
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /**Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  #if CONFIG_CRYSTAL == CONFIG_CRYSTAL_12MHZ
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL8;
  #elif CONFIG_CRYSTAL == CONFIG_CRYSTAL_16MHZ
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;
  #elif CONFIG_CRYSTAL == CONFIG_CRYSTAL_8MHZ
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
  #elif CONFIG_CRYSTAL == CONFIG_CRYSTAL_4MHZ
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL24;
  #else
  #error Invalid Crystal Ocsillator Frequency specified
  #endif
  RCC_OscInitStruct.PLL.PLLDIV = RCC_PLL_DIV3;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /**Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

  #if CONFIG_SYSCLOCK == CONFIG_SYSCLOCK_HIGH  
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;   // two times greater than RCC_SYSCLKSOURCE_HSE (see PLL settings)
  #else
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
  #endif

  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // System Clock Divider
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  #if (PREFETCH_ENABLE == 0)
  __HAL_FLASH_PREFETCH_BUFFER_DISABLE();
  #endif /* PREFETCH_ENABLE */

  #if CONFIG_FLASH_64BIT_MANAGE
  // NOTE: Enabling 64-bit access must be performed before
  // changing the FLASH LATENCY and changing the CPU clock
  // Refer: RM0038, STM32L151CB User Manual, ch. 3.3.1. DocID15965
  __HAL_FLASH_ACC64_ENABLE(); // enable 64-bit flash access to enhance performance
  #endif

  #if CONFIG_SYSCLOCK == CONFIG_SYSCLOCK_HIGH
  // On High frequency it is required to add one cycle to flash access latency.
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
  #else
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
  #endif
  
  #if (PREFETCH_ENABLE != 0)
  __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
  #endif /* PREFETCH_ENABLE */

  //HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_NOCLOCK, RCC_MCODIV_1); 

  /*
  { // Configure GPIO pin for MCO function
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = CONFIG_PIN__MCO;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = CONFIG_AF__MCO;
  HAL_GPIO_Init(CONFIG_PORT__MCO, &GPIO_InitStruct);
  }
  */
}
#endif

#if CONFIG_VBATMON
bool SystemClock_HSI( bool state )
{  
  bool bRet = false;
  {
      RCC_OscInitTypeDef RCC_OscInitStruct = {0};
      
      RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
      RCC_OscInitStruct.HSIState = ((state)?RCC_HSI_ON:RCC_HSI_OFF);
      RCC_OscInitStruct.HSICalibrationValue = 0;      
      RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
      if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
      {
        Error_Handler();
      }
      else
      {
        bRet = true;
      }
  }  

  return true;
}
#else
bool SystemClock_HSI( bool state ){ (void)state; return false; }
#endif

#if (CONFIG_SYSCLOCK == CONFIG_SYSCLOCK_LOW) || CONFIG_USB_DYNAMIC_CLOCK_ADOPT || CONFIG_EXTREME_LOWPOWTEST
void SystemClock_Config_Low(void)
{  
  {
      RCC_OscInitTypeDef RCC_OscInitStruct = {0};

      /**Configure the main internal regulator output voltage 
      */
      __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
      /**Initializes the CPU, AHB and APB busses clocks 
      */
      RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
      RCC_OscInitStruct.MSIState = RCC_MSI_ON;
      RCC_OscInitStruct.MSICalibrationValue = 0;
      #if   CONFIG_MSI_FREQ == CONFIG_MSI_FREQ_4MHZ
      RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;
      #elif CONFIG_MSI_FREQ == CONFIG_MSI_FREQ_2MHZ
      RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_5;
      #elif CONFIG_MSI_FREQ == CONFIG_MSI_FREQ_1MHZ
      RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_4;
      #else
      #warning MSI Frequency set to the default
      RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_5; // 2MHz
      #endif
      RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
      if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
      {
        Error_Handler();
      }
  }
  
  __HAL_FLASH_PREFETCH_BUFFER_DISABLE();

  {
      RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
      /**Initializes the CPU, AHB and APB busses clocks 
      */
      RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                  |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
      RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
      RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // System Clock Divider
      RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; //RCC_HCLK_DIV1;
      RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; //RCC_HCLK_DIV1;

      if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) //FLASH_LATENCY_1?
      {
        Error_Handler();
      }      
  }
  {
      RCC_OscInitTypeDef RCC_OscInitStruct = {0};

      RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
      RCC_OscInitStruct.HSEState = RCC_HSE_OFF;
      RCC_OscInitStruct.PLL.PLLState = RCC_PLL_OFF;      
      if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
      {
        Error_Handler();
      }
  }

  #if CONFIG_FLASH_64BIT_MANAGE
  // Disable 64-bit FLASH access, reduce power consumption
  // NOTE: Disable 64-bit access must be performed after
  // changing the FLASH LATENCY and changing the CPU clock
  // Refer: RM0038, STM32L151CB User Manual, ch. 3.3.1. DocID15965
  __HAL_FLASH_ACC64_DISABLE(); // disable 64-bit flash access to enhance performance
  #endif
  
  //HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_NOCLOCK, RCC_MCODIV_1);

  // { // DeConfigure GPIO pin (MCO function)
  // GPIO_InitTypeDef GPIO_InitStruct = {0};
  // GPIO_InitStruct.Pin = CONFIG_PIN__MCO;
  // GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  // GPIO_InitStruct.Pull = GPIO_NOPULL;
  // GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;  
  // HAL_GPIO_Init(CONFIG_PORT__MCO, &GPIO_InitStruct);
  // }
}
#endif

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

void HAL_Wait_us( size_t n )
{
#warning  need to debug. Sometimes the delay is wrong! especially for 1us
   HAL_Wait_1us( 10*n ); // need to debug. Sometimes the delay is wrong! especially for 1us
}

void HAL_Wait_10us( size_t n )
{
   HAL_Wait_1us( 10*n );
}

void HAL_Wait_100us( size_t n )
{
   HAL_Wait_1us( 100*n );
}

 #pragma optimize=speed
 static void HAL_Wait_1us( size_t us )
 {    
    int cycles = (1 + (HAL_RCC_GetHCLKFreq() >> 20));

    if( cycles <= 4 ) // up to 4MHZ clock
    {
        void sys_delayus_up4MHZ( int us, int cycles );
        sys_delayus_up4MHZ( us, cycles );
    }
    else
    if( cycles <= 8 ) // up to 8MHZ clock
    {
        void sys_delayus_up8MHZ( int us, int cycles );
        sys_delayus_up8MHZ( us, cycles );
    }
    else
    if( cycles <= 16 ) // up to 16MHZ clock
    {
        void sys_delayus_up16MHZ( int us, int cycles );
        sys_delayus_up16MHZ( us, cycles );
    }
    else
    {
        void sys_delayus_up96MHZ( int us, int cycles );
        sys_delayus_up96MHZ( us, cycles );
    }
 }

 #pragma optimize=none
 static void sys_delayus_up4MHZ( int us, int cycles )
 {
     int m = cycles * us;
     for( int i = cycles + cycles; i < m; ++i )
       asm("nop");
 }

 #pragma optimize=none
 static void sys_delayus_up8MHZ( int us, int cycles )  // 4...8MHz
 {            
     for( int i = (cycles*us)>>2; i > (us>>1); --i );       
 }

 #pragma optimize=none
 static void sys_delayus_up16MHZ( int us, int cycles )  // 8...16MHz
 {            
     for( int i = (cycles*us)>>2; i > (us>>1); --i );
 }

 #pragma optimize=none
 static void sys_delayus_up96MHZ( int us, int cycles )
 {
     for( int i = (cycles*us)>>2; i > (us>>1); --i );       
 }

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{   
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  while(1)
  {
     HAL_NVIC_SystemReset();
  }
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
