text: /* USER CODE BEGIN Header */
  /**
    ******************************************************************************
    * @file           : main.c
    * @brief          : Main program body
    ******************************************************************************
    * @attention
    *
    * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
    * All rights reserved.</center></h2>
    *
    * This software component is licensed by ST under BSD 3-Clause license,
    * the "License"; You may not use this file except in compliance with the
    * License. You may obtain a copy of the License at:
    *                        opensource.org/licenses/BSD-3-Clause
    *
    ******************************************************************************
    */
  /* USER CODE END Header */
  /* Includes ------------------------------------------------------------------*/
  #include "main.h"
  
  /* Private includes ----------------------------------------------------------*/
  /* USER CODE BEGIN Includes */
  #include "stm32h7xx_hal.h"
  #include "gw_buttons.h"
  #include "gw_flash.h"
  #include "gw_lcd.h"
  #include "gw_linker.h"
  #include "githash.h"
  #include "flashapp.h"
  
  #include "odroid_colors.h"
  #include "odroid_system.h"
  #include "odroid_overlay.h"
  #include "bq24072.h"
  
  #include <string.h>
  #include <assert.h>
  #include <stdio.h>
  /* USER CODE END Includes */
  
  /* Private typedef -----------------------------------------------------------*/
  /* USER CODE BEGIN PTD */
  
  /* USER CODE END PTD */
  
  /* Private define ------------------------------------------------------------*/
  /* USER CODE BEGIN PD */
  /* USER CODE END PD */
  
  /* Private macro -------------------------------------------------------------*/
  /* USER CODE BEGIN PM */
  
  /* USER CODE END PM */
  
  /* Private variables ---------------------------------------------------------*/
  ADC_HandleTypeDef hadc1;
  
  DAC_HandleTypeDef hdac1;
  DAC_HandleTypeDef hdac2;
  
  LTDC_HandleTypeDef hltdc;
  
  OSPI_HandleTypeDef hospi1;
  
  RTC_HandleTypeDef hrtc;
  
  SAI_HandleTypeDef hsai_BlockA1;
  DMA_HandleTypeDef hdma_sai1_a;
  
  SPI_HandleTypeDef hspi2;
  
  TIM_HandleTypeDef htim1;
  
  WWDG_HandleTypeDef hwwdg1;
  
  /* USER CODE BEGIN PV */
  
  #define BOOT_MODE_APP      0
  #define BOOT_MODE_FLASHAPP 1
  
  char logbuf[1024 * 4] PERSISTENT __attribute__((aligned(4)));
  uint32_t log_idx PERSISTENT;
  PERSISTENT volatile uint32_t boot_magic;
  
  uint32_t boot_buttons;
  
  uint32_t uptime_s;
  
  static bool wdog_enabled;
  
  /* USER CODE END PV */
  
  /* Private function prototypes -----------------------------------------------*/
  void SystemClock_Config(void);
  static void MPU_Config(void);
  static void MX_GPIO_Init(void);
  static void MX_DMA_Init(void);
  static void MX_LTDC_Init(void);
  static void MX_SPI2_Init(void);
  static void MX_OCTOSPI1_Init(void);
  static void MX_SAI1_Init(void);
  static void MX_RTC_Init(void);
  static void MX_DAC1_Init(void);
  static void MX_DAC2_Init(void);
  static void MX_WWDG1_Init(void);
  static void MX_ADC1_Init(void);
  static void MX_TIM1_Init(void);
  static void MX_NVIC_Init(void);
  /* USER CODE BEGIN PFP */
  
  void app_main(void);
  
  /* USER CODE END PFP */
  
  /* Private user code ---------------------------------------------------------*/
  /* USER CODE BEGIN 0 */
  
  
  const char *fault_list[] = {
    [BSOD_ABORT] = "Assert",
    [BSOD_HARDFAULT] = "Hardfault",
    [BSOD_MEMFAULT] = "Memfault",
    [BSOD_BUSFAULT] = "Busfault",
    [BSOD_USAGEFAULT] = "Usagefault",
    [BSOD_WATCHDOG] = "Watchdog",
    [BSOD_OTHER] = "Other",
  };
  
  __attribute__((optimize("-O0"))) void BSOD(BSOD_t fault, void *pc, void *lr)
  {
    char msg[256];
    size_t i = 0;
    char *start;
    char *end;
    char *line;
    int y = 2*8;
  
    __disable_irq();
  
    snprintf(msg, sizeof(msg), "FATAL EXCEPTION: %s %s\nPC=%p LR=%p\n", fault_list[fault], GIT_HASH, pc, lr);
  
    lcd_sync();
    lcd_reset_active_buffer();
    odroid_display_set_backlight(ODROID_BACKLIGHT_LEVEL3);
  
    odroid_overlay_draw_text(0, 0, GW_LCD_WIDTH, msg, C_RED, C_BLUE);
  
    // Print each line from the log in reverse
    end = &logbuf[strnlen(logbuf, sizeof(logbuf)) - 1];
    while (y < GW_LCD_HEIGHT) {
      // Max 28 lines
      if (i++ >= 28) {
        break;
      }
  
      // Find the last line start not beyond end (inefficient but simple solution)
      start = logbuf;
      while (start < end) {
        line = start;
        start = strnstr(start, "\n", end - start);
        if (start == NULL) {
          break;
        } else {
          // Move past \n
          start += 1;
        }
      }
  
      // Terminate the previous line
      end[0] = '\x00';
  
      end = line;
  
      y += odroid_overlay_draw_text(0, y, GW_LCD_WIDTH, line, C_WHITE, C_BLUE);
  
      if (line == logbuf) {
        // No more lines to print
        break;
      }
    }
  
    // Wait for a button press (allows a user to hold and release a button when the BSOD occurs)
    uint32_t old_buttons = buttons_get();
    while ((buttons_get() == 0 || (buttons_get() == old_buttons))) {
      wdog_refresh();
    }
  
    // Encode the fault type in the boot magic
    boot_magic = BOOT_MAGIC_BSOD | (fault & 0xffff);
  
    HAL_NVIC_SystemReset();
  
    // Does not return
    while (1) {
      __NOP();
    }
  }
  
  // Used by assert()
  void abort(void)
  {
    BSOD(BSOD_ABORT, 0, 0);
  }
  
  #if 1
  int _write(int file, char *ptr, int len)
  {
    if (log_idx + len + 1 > sizeof(logbuf)) {
      log_idx = 0;
    }
  
    memcpy(&logbuf[log_idx], ptr, len);
    log_idx += len;
    logbuf[log_idx + 1] = '\0';
  
    return len;
  }
  #endif
  
  void store_erase(const uint8_t *flash_ptr, uint32_t size)
  {
    // Only allow pointers in the SAVEFLASH allocated area
    assert(
      ((flash_ptr >= &__SAVEFLASH_START__)   && ((flash_ptr + size) <= &__SAVEFLASH_END__)) ||
      ((flash_ptr >= &__configflash_start__) && ((flash_ptr + size) <= &__configflash_end__))
    );
  
    // Convert mem mapped pointer to flash address
    uint32_t save_address = flash_ptr - &__EXTFLASH_START__;
  
    // Only allow 4kB aligned pointers
    assert((save_address & (4*1024 - 1)) == 0);
  
    // Round size up to nearest 4K
    if ((size & 0xfff) != 0) {
      size += 0x1000 - (size & 0xfff);
    }
  
    OSPI_DisableMemoryMappedMode();
    OSPI_EraseSync(save_address, size);
    OSPI_EnableMemoryMappedMode();
  }
  
  void store_save(const uint8_t *flash_ptr, const uint8_t *data, size_t size)
  {
    // Temporary solution to make things work with flash with 256K erase pages
  #ifdef DISABLE_STORE
    return;
  #endif
  
    // Convert mem mapped pointer to flash address
    uint32_t save_address = flash_ptr - &__EXTFLASH_START__;
  
    // Only allow 4kB aligned pointers
    assert((save_address & (4*1024 - 1)) == 0);
  
    int diff = memcmp((void*)flash_ptr, data, size);
    if (diff == 0) {
      return;
    }
  
    store_erase(flash_ptr, size);
  
    OSPI_DisableMemoryMappedMode();
    OSPI_Program(save_address, data, size);
    OSPI_EnableMemoryMappedMode();
  }
  
  void boot_magic_set(uint32_t magic)
  {
    boot_magic = magic;
  }
  
  void uptime_inc(void)
  {
    uptime_s++;
  }
  
  uint32_t uptime_get(void)
  {
    return uptime_s;
  }
  
  void GW_EnterDeepSleep(void)
  {
    // Stop SAI DMA (audio)
    HAL_SAI_DMAStop(&hsai_BlockA1);
  
    // Enable wakup by PIN1, the power button
    HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1_LOW);
  
    lcd_backlight_off();
  
    // Deinit the LCD, save power.
    lcd_deinit(&hspi2);
  
    // Leave a trace in RAM that we entered standby mode
    boot_magic = BOOT_MAGIC_STANDBY;
  
    // Delay 500ms to give us a chance to attach a debugger in case
    // we end up in a suspend-loop.
    for (int i = 0; i < 10; i++) {
        wdog_refresh();
        HAL_Delay(50);
    }
  
    HAL_PWR_EnterSTANDBYMode();
  
    // Execution stops here, this function will not return
    while(1) {
      // If we for some reason survive until here, let's just reboot
      HAL_NVIC_SystemReset();
    }
  
  }
  
  // Returns buttons that were pressed at boot
  uint32_t GW_GetBootButtons(void)
  {
    return boot_buttons;
  }
  
  // Workaround for being able to run with -D_FORTIFY_SOURCE=1
  static void memcpy_no_check(uint32_t *dst, uint32_t *src, size_t len)
  {
    assert((len & 0b11) == 0);
  
    uint32_t *end = dst + len / 4;
    while (dst != end) {
      *(dst++) = *(src++);
    }
  }
  
  void wdog_enable()
  {
    MX_WWDG1_Init();
    wdog_enabled = true;
  }
  
  void wdog_refresh()
  {
    if (wdog_enabled) {
      HAL_WWDG_Refresh(&hwwdg1);
    }
  }
  
  /* USER CODE END 0 */
  
  /**
    * @brief  The application entry point.
    * @retval int
    */
  int main(void)
  {
    /* USER CODE BEGIN 1 */
    uint8_t trigger_wdt_bsod = 0;
    uint8_t boot_mode = BOOT_MODE_APP;
  
    for(int i = 0; i < 1000000; i++) {
      __NOP();
    }
  
    // Nullpointer redzone
  #pragma GCC diagnostic push
  #pragma GCC diagnostic ignored "-Wnonnull"
    memset(0x0, '\x41', (size_t)&__NULLPTR_LENGTH__);
  #pragma GCC diagnostic pop
  
    // Don't reset the logbuf when rebooting from a watchdog reset
    if (boot_magic != BOOT_MAGIC_WATCHDOG) {
      log_idx = 0;
      logbuf[0] = '\0';
    }
  
    switch (boot_magic) {
    case BOOT_MAGIC_STANDBY:
      printf("Boot from standby.\nboot_magic=0x%08lx\n", boot_magic);
      break;
    case BOOT_MAGIC_RESET:
      printf("Boot from warm reset.\nboot_magic=0x%08lx\n", boot_magic);
      break;
    case BOOT_MAGIC_WATCHDOG:
      printf("Boot from watchdog reset!\nboot_magic=0x%08lx\n", boot_magic);
      trigger_wdt_bsod = 1;
      break;
    case BOOT_MAGIC_FLASHAPP:
      boot_mode = BOOT_MODE_FLASHAPP;
      break;
    default:
      if ((boot_magic & BOOT_MAGIC_BSOD_MASK) == BOOT_MAGIC_BSOD) {
        uint16_t fault_idx = boot_magic & 0xffff;
        const char *fault = (fault_idx < BSOD_COUNT) ? fault_list[fault_idx] : "UNKOWN";
        printf("Boot from BSOD:\nboot_magic=0x%08lx %s\n", boot_magic, fault);
      } else {
        printf("Boot from brownout?\nboot_magic=0x%08lx\n", boot_magic);
      }
      break;
    }
  
    // Leave a trace that indicates a warm reset
    boot_magic = BOOT_MAGIC_RESET;
  
    // Reset the log write pointer
    log_idx = 0;
  
    /* USER CODE END 1 */
  
    /* MPU Configuration--------------------------------------------------------*/
    MPU_Config();
  
    /* MCU Configuration--------------------------------------------------------*/
  
    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();
  
    /* USER CODE BEGIN Init */
  
    // Power pin as Input
    HAL_PWR_DisableWakeUpPin(PWR_WAKEUP_PIN1_LOW);
  
    /* USER CODE END Init */
  
    /* Configure the system clock */
    SystemClock_Config();
  
    /* USER CODE BEGIN SysInit */
  
    /* USER CODE END SysInit */
  
    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_LTDC_Init();
    MX_SPI2_Init();
    MX_OCTOSPI1_Init();
    MX_SAI1_Init();
    MX_RTC_Init();
    MX_DAC1_Init();
    MX_DAC2_Init();
    MX_ADC1_Init();
    MX_TIM1_Init();
  
    /* Initialize interrupts */
    MX_NVIC_Init();
    /* USER CODE BEGIN 2 */
  
    // Save the button states as early as possible
    boot_buttons = buttons_get();
  
    // Keep this
    for (int i = 0; i < 10; i++) {
        wdog_refresh();
        HAL_Delay(50);
    }
  
    lcd_init(&hspi2, &hltdc);
  
    if (trigger_wdt_bsod) {
      BSOD(BSOD_WATCHDOG, 0, 0);
    }
  
    /* USER CODE END 2 */
  
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
  
    SCB_EnableICache();
    SCB_EnableDCache();
  
    // Initialize the external flash
  
    OSPI_Init(&hospi1);
  
    // Copy instructions and data from extflash to axiram
    void *copy_areas[3];
  
    copy_areas[0] = &_siramdata;  // 0x90000000
    copy_areas[1] = &__ram_exec_start__;  // 0x24000000
    copy_areas[2] = &__ram_exec_end__;  // 0x24000000 + length
    memcpy_no_check(copy_areas[1], copy_areas[0], copy_areas[2] - copy_areas[1]);
  
    // Copy ITCRAM HOT section
    static uint32_t copy_areas2[4] __attribute__((used));
    copy_areas2[0] = (uint32_t) &_sitcram_hot;
    copy_areas2[1] = (uint32_t) &__itcram_hot_start__;
    copy_areas2[2] = (uint32_t) &__itcram_hot_end__;
    copy_areas2[3] = copy_areas2[2] - copy_areas2[1];
    memcpy_no_check((uint32_t *) copy_areas2[1], (uint32_t *) copy_areas2[0], copy_areas2[3]);
  
    bq24072_init();
  
    switch (boot_mode) {
    case BOOT_MODE_APP:
      wdog_enable();
      // Launch the emulator
      app_main();
      break;
    case BOOT_MODE_FLASHAPP:
      flashapp_main();
      break;
    default:
      break;
    }
  
    while (1)
    {
      /* USER CODE END WHILE */
  
      /* USER CODE BEGIN 3 */
  
    }
    /* USER CODE END 3 */
  }
  
  /**
    * @brief System Clock Configuration
    * @retval None
    */
  void SystemClock_Config(void)
  {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
    RCC_CRSInitTypeDef RCC_CRSInitStruct = {0};
  
    /** Supply configuration update enable
    */
    HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
    /** Configure the main internal regulator output voltage
    */
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);
  
    while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
  
    /** Configure LSE Drive Capability
    */
    HAL_PWR_EnableBkUpAccess();
    __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_HIGH);
  
    /** Macro to configure the PLL clock source
    */
    __HAL_RCC_PLL_PLLSOURCE_CONFIG(RCC_PLLSOURCE_HSI);
    /** Initializes the RCC Oscillators according to the specified parameters
    * in the RCC_OscInitTypeDef structure.
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_LSE;
    RCC_OscInitStruct.LSEState = RCC_LSE_ON;
    RCC_OscInitStruct.HSIState = RCC_HSI_DIV1;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.LSIState = RCC_LSI_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
    RCC_OscInitStruct.PLL.PLLM = 16;
    RCC_OscInitStruct.PLL.PLLN = 140;
    RCC_OscInitStruct.PLL.PLLP = 2;
    RCC_OscInitStruct.PLL.PLLQ = 2;
    RCC_OscInitStruct.PLL.PLLR = 2;
    RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
    RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    RCC_OscInitStruct.PLL.PLLFRACN = 0;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
      Error_Handler();
    }
    /** Initializes the CPU, AHB and APB buses clocks
    */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                                |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
    RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
  
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK)
    {
      Error_Handler();
    }
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_LTDC
                                |RCC_PERIPHCLK_SPI2|RCC_PERIPHCLK_SAI1
                                |RCC_PERIPHCLK_ADC|RCC_PERIPHCLK_OSPI
                                |RCC_PERIPHCLK_CKPER;
    PeriphClkInitStruct.PLL2.PLL2M = 25;
    PeriphClkInitStruct.PLL2.PLL2N = 192;
    PeriphClkInitStruct.PLL2.PLL2P = 5;
    PeriphClkInitStruct.PLL2.PLL2Q = 2;
    PeriphClkInitStruct.PLL2.PLL2R = 5;
    PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_1;
    PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
    PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
    PeriphClkInitStruct.PLL3.PLL3M = 4;
    PeriphClkInitStruct.PLL3.PLL3N = 9;
    PeriphClkInitStruct.PLL3.PLL3P = 2;
    PeriphClkInitStruct.PLL3.PLL3Q = 2;
    PeriphClkInitStruct.PLL3.PLL3R = 24;
    PeriphClkInitStruct.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_3;
    PeriphClkInitStruct.PLL3.PLL3VCOSEL = RCC_PLL3VCOWIDE;
    PeriphClkInitStruct.PLL3.PLL3FRACN = 0;
    PeriphClkInitStruct.OspiClockSelection = RCC_OSPICLKSOURCE_CLKP;
    PeriphClkInitStruct.CkperClockSelection = RCC_CLKPSOURCE_HSI;
    PeriphClkInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLL2;
    PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_CLKP;
    PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;
    PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
    PeriphClkInitStruct.TIMPresSelection = RCC_TIMPRES_ACTIVATED;
  
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    {
      Error_Handler();
    }
    /** Enable the SYSCFG APB clock
    */
    __HAL_RCC_CRS_CLK_ENABLE();
    /** Configures CRS
    */
    RCC_CRSInitStruct.Prescaler = RCC_CRS_SYNC_DIV1;
    RCC_CRSInitStruct.Source = RCC_CRS_SYNC_SOURCE_LSE;
    RCC_CRSInitStruct.Polarity = RCC_CRS_SYNC_POLARITY_RISING;
    RCC_CRSInitStruct.ReloadValue = __HAL_RCC_CRS_RELOADVALUE_CALCULATE(48000000,32768);
    RCC_CRSInitStruct.ErrorLimitValue = 34;
    RCC_CRSInitStruct.HSI48CalibrationValue = 32;
  
    HAL_RCCEx_CRSConfig(&RCC_CRSInitStruct);
  }
  
  /**
    * @brief NVIC Configuration.
    * @retval None
    */
  static void MX_NVIC_Init(void)
  {
    /* OCTOSPI1_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(OCTOSPI1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(OCTOSPI1_IRQn);
  }
  
  /**
    * @brief ADC1 Initialization Function
    * @param None
    * @retval None
    */
  static void MX_ADC1_Init(void)
  {
  
    /* USER CODE BEGIN ADC1_Init 0 */
  
    /* USER CODE END ADC1_Init 0 */
  
    ADC_MultiModeTypeDef multimode = {0};
    ADC_ChannelConfTypeDef sConfig = {0};
  
    /* USER CODE BEGIN ADC1_Init 1 */
  
    /* USER CODE END ADC1_Init 1 */
    /** Common config
    */
    hadc1.Instance = ADC1;
    hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
    hadc1.Init.Resolution = ADC_RESOLUTION_16B;
    hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
    hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
    hadc1.Init.LowPowerAutoWait = DISABLE;
    hadc1.Init.ContinuousConvMode = DISABLE;
    hadc1.Init.NbrOfConversion = 1;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
    hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
    hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
    hadc1.Init.OversamplingMode = DISABLE;
    if (HAL_ADC_Init(&hadc1) != HAL_OK)
    {
      Error_Handler();
    }
    /** Configure the ADC multi-mode
    */
    multimode.Mode = ADC_MODE_INDEPENDENT;
    if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
    {
      Error_Handler();
    }
    /** Configure Regular Channel
    */
    sConfig.Channel = ADC_CHANNEL_4;
    sConfig.Rank = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
    sConfig.SingleDiff = ADC_SINGLE_ENDED;
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
    sConfig.OffsetSignedSaturation = DISABLE;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
      Error_Handler();
    }
    /* USER CODE BEGIN ADC1_Init 2 */
  
    /* USER CODE END ADC1_Init 2 */
  
  }
  
  /**
    * @brief DAC1 Initialization Function
    * @param None
    * @retval None
    */
  static void MX_DAC1_Init(void)
  {
  
    /* USER CODE BEGIN DAC1_Init 0 */
  
    /* USER CODE END DAC1_Init 0 */
  
    DAC_ChannelConfTypeDef sConfig = {0};
  
    /* USER CODE BEGIN DAC1_Init 1 */
  
    /* USER CODE END DAC1_Init 1 */
    /** DAC Initialization
    */
    hdac1.Instance = DAC1;
    if (HAL_DAC_Init(&hdac1) != HAL_OK)
    {
      Error_Handler();
    }
    /** DAC channel OUT1 config
    */
    sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
    sConfig.DAC_Trigger = DAC_TRIGGER_NONE;
    sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
    sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;
    sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
    if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)
    {
      Error_Handler();
    }
    /** DAC channel OUT2 config
    */
    sConfig.DAC_ConnectOnChipPeripheral = DAC_SAMPLEANDHOLD_DISABLE;
    if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_2) != HAL_OK)
    {
      Error_Handler();
    }
    /* USER CODE BEGIN DAC1_Init 2 */
  
    /* USER CODE END DAC1_Init 2 */
  
  }
  
  /**
    * @brief DAC2 Initialization Function
    * @param None
    * @retval None
    */
  static void MX_DAC2_Init(void)
  {
  
    /* USER CODE BEGIN DAC2_Init 0 */
  
    /* USER CODE END DAC2_Init 0 */
  
    DAC_ChannelConfTypeDef sConfig = {0};
  
    /* USER CODE BEGIN DAC2_Init 1 */
  
    /* USER CODE END DAC2_Init 1 */
    /** DAC Initialization
    */
    hdac2.Instance = DAC2;
    if (HAL_DAC_Init(&hdac2) != HAL_OK)
    {
      Error_Handler();
    }
    /** DAC channel OUT1 config
    */
    sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
    sConfig.DAC_Trigger = DAC_TRIGGER_NONE;
    sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
    sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;
    sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
    if (HAL_DAC_ConfigChannel(&hdac2, &sConfig, DAC_CHANNEL_1) != HAL_OK)
    {
      Error_Handler();
    }
    /* USER CODE BEGIN DAC2_Init 2 */
  
    /* USER CODE END DAC2_Init 2 */
  
  }
  
  /**
    * @brief LTDC Initialization Function
    * @param None
    * @retval None
    */
  static void MX_LTDC_Init(void)
  {
  
    /* USER CODE BEGIN LTDC_Init 0 */
  
    /* USER CODE END LTDC_Init 0 */
  
    LTDC_LayerCfgTypeDef pLayerCfg = {0};
  
    /* USER CODE BEGIN LTDC_Init 1 */
  
    /* USER CODE END LTDC_Init 1 */
    hltdc.Instance = LTDC;
    hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
    hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
    hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
    hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IIPC;
    hltdc.Init.HorizontalSync = 9;
    hltdc.Init.VerticalSync = 1;
    hltdc.Init.AccumulatedHBP = 60;
    hltdc.Init.AccumulatedVBP = 7;
    hltdc.Init.AccumulatedActiveW = 380;
    hltdc.Init.AccumulatedActiveH = 247;
    hltdc.Init.TotalWidth = 392;
    hltdc.Init.TotalHeigh = 255;
    hltdc.Init.Backcolor.Blue = 0;
    hltdc.Init.Backcolor.Green = 0;
    hltdc.Init.Backcolor.Red = 0;
    if (HAL_LTDC_Init(&hltdc) != HAL_OK)
    {
      Error_Handler();
    }
    pLayerCfg.WindowX0 = 0;
    pLayerCfg.WindowX1 = 320;
    pLayerCfg.WindowY0 = 0;
    pLayerCfg.WindowY1 = 240;
  #ifdef GW_LCD_MODE_LUT8
    pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_L8;
  #else
    pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
  #endif
    pLayerCfg.Alpha = 255;
    pLayerCfg.Alpha0 = 255;
    pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
    pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
    pLayerCfg.FBStartAdress = 0x24000000;
    pLayerCfg.ImageWidth = 320;
    pLayerCfg.ImageHeight = 240;
    pLayerCfg.Backcolor.Blue = 0;
    pLayerCfg.Backcolor.Green = 255;
    pLayerCfg.Backcolor.Red = 0;
    if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK)
    {
      Error_Handler();
    }
    /* USER CODE BEGIN LTDC_Init 2 */
  
    /* USER CODE END LTDC_Init 2 */
  
  }
  
  /**
    * @brief OCTOSPI1 Initialization Function
    * @param None
    * @retval None
    */
  static void MX_OCTOSPI1_Init(void)
  {
  
    /* USER CODE BEGIN OCTOSPI1_Init 0 */
  
    /* USER CODE END OCTOSPI1_Init 0 */
  
    OSPIM_CfgTypeDef sOspiManagerCfg = {0};
  
    /* USER CODE BEGIN OCTOSPI1_Init 1 */
  
    /* USER CODE END OCTOSPI1_Init 1 */
    /* OCTOSPI1 parameter configuration*/
    hospi1.Instance = OCTOSPI1;
    hospi1.Init.FifoThreshold = 4;
    hospi1.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
    hospi1.Init.MemoryType = HAL_OSPI_MEMTYPE_MACRONIX;
    hospi1.Init.DeviceSize = 28;
    hospi1.Init.ChipSelectHighTime = 2;
    hospi1.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE;
    hospi1.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0;
    hospi1.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED;
    hospi1.Init.ClockPrescaler = 1;
    hospi1.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE;
    hospi1.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_DISABLE;
    hospi1.Init.ChipSelectBoundary = 0;
    hospi1.Init.ClkChipSelectHighTime = 0;
    hospi1.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED;
    hospi1.Init.MaxTran = 0;
    hospi1.Init.Refresh = 0;
    if (HAL_OSPI_Init(&hospi1) != HAL_OK)
    {
      Error_Handler();
    }
    sOspiManagerCfg.ClkPort = 1;
    sOspiManagerCfg.NCSPort = 1;
    sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_1_LOW;
    if (HAL_OSPIM_Config(&hospi1, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
    {
      Error_Handler();
    }
    /* USER CODE BEGIN OCTOSPI1_Init 2 */
  
    /* USER CODE END OCTOSPI1_Init 2 */
  
  }
  
  /**
    * @brief RTC Initialization Function
    * @param None
    * @retval None
    */
  static void MX_RTC_Init(void)
  {
  
    /* USER CODE BEGIN RTC_Init 0 */
  
    /* USER CODE END RTC_Init 0 */
  
    RTC_TimeTypeDef sTime = {0};
    RTC_DateTypeDef sDate = {0};
  
    /* USER CODE BEGIN RTC_Init 1 */
  
    /* USER CODE END RTC_Init 1 */
    /** Initialize RTC Only
    */
    hrtc.Instance = RTC;
    hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
    hrtc.Init.AsynchPrediv = 127; // Recommended value from application note for LSE, the higher the value the better the accuracy and power consumption
    hrtc.Init.SynchPrediv = 255; // Recommended value from application note for LSE
    hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
    hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
    hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
    hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
    if (HAL_RTC_Init(&hrtc) != HAL_OK)
    {
      Error_Handler();
    }
  
    /* USER CODE BEGIN Check_RTC_BKUP */
    return; // Retain RTC values on boot
    /* USER CODE END Check_RTC_BKUP */
  
    /** Initialize RTC and set the Time and Date
    */
    sTime.Hours = 0x0;
    sTime.Minutes = 0x0;
    sTime.Seconds = 0x0;
    sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
    sTime.StoreOperation = RTC_STOREOPERATION_RESET;
    if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
    {
      Error_Handler();
    }
    sDate.WeekDay = RTC_WEEKDAY_MONDAY;
    sDate.Month = RTC_MONTH_JANUARY;
    sDate.Date = 0x1;
    sDate.Year = 0x0;
  
    if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
    {
      Error_Handler();
    }
    /* USER CODE BEGIN RTC_Init 2 */
  
    /* USER CODE END RTC_Init 2 */
  
  }
  
  /**
    * @brief SAI1 Initialization Function
    * @param None
    * @retval None
    */
  static void MX_SAI1_Init(void)
  {
  
    /* USER CODE BEGIN SAI1_Init 0 */
  
    /* USER CODE END SAI1_Init 0 */
  
    /* USER CODE BEGIN SAI1_Init 1 */
  
    /* USER CODE END SAI1_Init 1 */
    hsai_BlockA1.Instance = SAI1_Block_A;
    hsai_BlockA1.Init.AudioMode = SAI_MODEMASTER_TX;
    hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;
    hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
    hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
    hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_FULL;
    hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_48K;
    hsai_BlockA1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
    hsai_BlockA1.Init.MonoStereoMode = SAI_MONOMODE;
    hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;
    hsai_BlockA1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
    if (HAL_SAI_InitProtocol(&hsai_BlockA1, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_16BIT, 2) != HAL_OK)
    {
      Error_Handler();
    }
    /* USER CODE BEGIN SAI1_Init 2 */
  
    /* USER CODE END SAI1_Init 2 */
  
  }
  
  /**
    * @brief SPI2 Initialization Function
    * @param None
    * @retval None
    */
  static void MX_SPI2_Init(void)
  {
  
    /* USER CODE BEGIN SPI2_Init 0 */
  
    /* USER CODE END SPI2_Init 0 */
  
    /* USER CODE BEGIN SPI2_Init 1 */
  
    /* USER CODE END SPI2_Init 1 */
    /* SPI2 parameter configuration*/
    hspi2.Instance = SPI2;
    hspi2.Init.Mode = SPI_MODE_MASTER;
    hspi2.Init.Direction = SPI_DIRECTION_2LINES_TXONLY;
    hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
    hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
    hspi2.Init.NSS = SPI_NSS_SOFT;
    hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
    hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi2.Init.CRCPolynomial = 0x0;
    hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
    hspi2.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
    hspi2.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
    hspi2.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
    hspi2.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
    hspi2.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
    hspi2.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
    hspi2.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
    hspi2.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
    hspi2.Init.IOSwap = SPI_IO_SWAP_DISABLE;
    if (HAL_SPI_Init(&hspi2) != HAL_OK)
    {
      Error_Handler();
    }
    /* USER CODE BEGIN SPI2_Init 2 */
  
    /* USER CODE END SPI2_Init 2 */
  
  }
  
  /**
    * @brief TIM1 Initialization Function
    * @param None
    * @retval None
    */
  static void MX_TIM1_Init(void)
  {
  
    /* USER CODE BEGIN TIM1_Init 0 */
  
    /* USER CODE END TIM1_Init 0 */
  
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};
  
    /* USER CODE BEGIN TIM1_Init 1 */
  
    /* USER CODE END TIM1_Init 1 */
    htim1.Instance = TIM1;
    htim1.Init.Prescaler = 14000;
    htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim1.Init.Period = 20000;
    htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim1.Init.RepetitionCounter = 0;
    htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
    {
      Error_Handler();
    }
    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
    {
      Error_Handler();
    }
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
    {
      Error_Handler();
    }
    /* USER CODE BEGIN TIM1_Init 2 */
  
    /* USER CODE END TIM1_Init 2 */
  
  }
  
  /**
    * @brief WWDG1 Initialization Function
    * @param None
    * @retval None
    */
  static void MX_WWDG1_Init(void)
  {
  
    /* USER CODE BEGIN WWDG1_Init 0 */
  
    /* USER CODE END WWDG1_Init 0 */
  
    /* USER CODE BEGIN WWDG1_Init 1 */
  
    /* USER CODE END WWDG1_Init 1 */
    hwwdg1.Instance = WWDG1;
    hwwdg1.Init.Prescaler = WWDG_PRESCALER_128;
    hwwdg1.Init.Window = 127;
    hwwdg1.Init.Counter = 127;
    hwwdg1.Init.EWIMode = WWDG_EWI_ENABLE;
    if (HAL_WWDG_Init(&hwwdg1) != HAL_OK)
    {
      Error_Handler();
    }
    /* USER CODE BEGIN WWDG1_Init 2 */
  
    /* USER CODE END WWDG1_Init 2 */
  
  }
  
  /**
    * Enable DMA controller clock
    */
  static void MX_DMA_Init(void)
  {
  
    /* DMA controller clock enable */
    __HAL_RCC_DMA1_CLK_ENABLE();
  
    /* DMA interrupt init */
    /* DMA1_Stream0_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
  
  }
  
  /**
    * @brief GPIO Initialization Function
    * @param None
    * @retval None
    */
  static void MX_GPIO_Init(void)
  {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
  
    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOE_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
  
    /*Configure GPIO pin Output Level */
    HAL_GPIO_WritePin(GPIO_Speaker_enable_GPIO_Port, GPIO_Speaker_enable_Pin, GPIO_PIN_SET);
  
    /*Configure GPIO pin Output Level */
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8, GPIO_PIN_RESET);
  
    /*Configure GPIO pin Output Level */
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
  
    /*Configure GPIO pin Output Level */
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8, GPIO_PIN_SET);
  
    /*Configure GPIO pin Output Level */
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_1|GPIO_PIN_4, GPIO_PIN_RESET);
  
    /*Configure GPIO pins : GPIO_Speaker_enable_Pin PE8 */
    GPIO_InitStruct.Pin = GPIO_Speaker_enable_Pin|GPIO_PIN_8;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
  
    /*Configure GPIO pins : BTN_PAUSE_Pin BTN_GAME_Pin BTN_TIME_Pin */
    GPIO_InitStruct.Pin = BTN_PAUSE_Pin|BTN_GAME_Pin|BTN_TIME_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  
    /*Configure GPIO pin : BTN_PWR_Pin */
    GPIO_InitStruct.Pin = BTN_PWR_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(BTN_PWR_GPIO_Port, &GPIO_InitStruct);
  
    /*Configure GPIO pin : PA2 */
    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  
    /*Configure GPIO pin : PE7 */
    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
  
    /*Configure GPIO pin : PB12 */
    GPIO_InitStruct.Pin = GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  
    /*Configure GPIO pins : PD8 PD1 PD4 */
    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_1|GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
  
    /*Configure GPIO pins : BTN_A_Pin BTN_Left_Pin BTN_Down_Pin BTN_Right_Pin
                             BTN_Up_Pin BTN_B_Pin */
    GPIO_InitStruct.Pin = BTN_A_Pin|BTN_Left_Pin|BTN_Down_Pin|BTN_Right_Pin
                            |BTN_Up_Pin|BTN_B_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
  
    /* EXTI interrupt init*/
    HAL_NVIC_SetPriority(EXTI2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(EXTI2_IRQn);
  
    HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
  
  }
  
  /* USER CODE BEGIN 4 */
  
  int __builtin_popcount (unsigned int x);
  
  /* USER CODE END 4 */
  
  /* MPU Configuration */
  
  void MPU_Config(void)
  {
    MPU_Region_InitTypeDef MPU_InitStruct = {0};
  
    /* Disables the MPU */
    HAL_MPU_Disable();
    /** Initializes and configures the Region and the memory to be protected
    */
    MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    MPU_InitStruct.Number = MPU_REGION_NUMBER0;
    MPU_InitStruct.BaseAddress = 0x30000000;
    MPU_InitStruct.Size = MPU_REGION_SIZE_128KB;
    MPU_InitStruct.SubRegionDisable = 0x0;
    MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
    MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  
    HAL_MPU_ConfigRegion(&MPU_InitStruct);
  
    if (__builtin_popcount((size_t)&__NULLPTR_LENGTH__) == 1) {
      /* Only continue if a single bit set in __NULLPTR_LENGTH__.
       * The MPU can only handle memory sizes which are a power of 2 */
      MPU_InitStruct.Enable = MPU_REGION_ENABLE;
      MPU_InitStruct.Number = MPU_REGION_NUMBER1;
      MPU_InitStruct.BaseAddress = 0x00000000;
      /* 128B --> 0x06, 256B --> 0x07, 512B --> 0x08, ... */
      MPU_InitStruct.Size = ffs((size_t)&__NULLPTR_LENGTH__) - 2;
      MPU_InitStruct.SubRegionDisable = 0x0;
      MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
      MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
      MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
      MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
      MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
      MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  
      HAL_MPU_ConfigRegion(&MPU_InitStruct);
    }
    
    // Stack redzone
    if (__builtin_popcount((size_t)&_Stack_Redzone_Size) == 1) {
      /* Only continue if a single bit set in _Stack_Redzone_Size.
       * The MPU can only handle memory sizes which are a power of 2 */
      MPU_InitStruct.Enable = MPU_REGION_ENABLE;
      MPU_InitStruct.Number = MPU_REGION_NUMBER2;
      MPU_InitStruct.BaseAddress = (uint32_t) &_stack_redzone;
      /* 128B --> 0x06, 256B --> 0x07, 512B --> 0x08, ... */
      MPU_InitStruct.Size = ffs((size_t)&_Stack_Redzone_Size) - 2;
      MPU_InitStruct.SubRegionDisable = 0x0;
      MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
      MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
      MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
      MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
      MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
      MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  
      HAL_MPU_ConfigRegion(&MPU_InitStruct);
    }
  
    /* Uncached areas lead to unalignment issues. Only protect the first 256+32+8+4 kB */
    MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    MPU_InitStruct.Number = MPU_REGION_NUMBER3;
    MPU_InitStruct.BaseAddress = 0x24000000;
    MPU_InitStruct.Size = MPU_REGION_SIZE_256KB;
    MPU_InitStruct.SubRegionDisable = 0x0;
    MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
    MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  
    // 0x24000000 -> +256kB
    HAL_MPU_ConfigRegion(&MPU_InitStruct);
  
    // 0x24000000 + 256kB -> +32kB
    MPU_InitStruct.Number = MPU_REGION_NUMBER4;
    MPU_InitStruct.BaseAddress = 0x24000000 + 256 * 1024;
    MPU_InitStruct.Size = MPU_REGION_SIZE_32KB;
    HAL_MPU_ConfigRegion(&MPU_InitStruct);
  
    // 0x24000000 + 256kB + 32kB -> +8kB
    MPU_InitStruct.Number = MPU_REGION_NUMBER5;
    MPU_InitStruct.BaseAddress = 0x24000000 + (256 + 32) * 1024;
    MPU_InitStruct.Size = MPU_REGION_SIZE_8KB;
    HAL_MPU_ConfigRegion(&MPU_InitStruct);
  
    // 0x24000000 + 256kB + 32kB + 8kB -> +4kB
    MPU_InitStruct.Number = MPU_REGION_NUMBER6;
    MPU_InitStruct.BaseAddress = 0x24000000 + (256 + 32 + 8) * 1024;
    MPU_InitStruct.Size = MPU_REGION_SIZE_4KB;
    HAL_MPU_ConfigRegion(&MPU_InitStruct);
  
    /* Enables the MPU */
    HAL_MPU_Enable(MPU_HFNMI_PRIVDEF);
  
  }
  
  /**
    * @brief  This function is executed in case of error occurrence.
    * @retval None
    */
  __attribute__((optimize("-O0"))) void Error_Handler(void)
  {
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
  
    // Hacky way to get the return address
    uint32_t stack;
    uint32_t *pStack = &stack;
  
    BSOD(BSOD_OTHER, (void *) pStack[3], 0);
  
    /* USER CODE END Error_Handler_Debug */
  }
  
  #ifdef  USE_FULL_ASSERT
  /**
    * @brief  Reports the name of the source file and the source line number
    *         where the assert_param error has occurred.
    * @param  file: pointer to the source file name
    * @param  line: assert_param error line source number
    * @retval None
    */
  void assert_failed(uint8_t *file, uint32_t line)
  {
    /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number,
       tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    /* USER CODE END 6 */
  }
  #endif /* USE_FULL_ASSERT */
  
  /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
  