text: #include "setup.h"
  #include "stm32f7xx_hal.h"
  #include "fatfs.h"
  #include "watchdog.h"
  #include "ads1262.h"
  #include "measure.h"
  
  volatile uint64_t measure_reference_timer_ticks = 0;
  
  TIM_HandleTypeDef htim2;
  SD_HandleTypeDef hsd1;
  DMA_HandleTypeDef hdma_sdmmc1_rx;
  DMA_HandleTypeDef hdma_sdmmc1_tx;
  
  SDRAM_HandleTypeDef sdramHandle;
  FMC_SDRAM_TimingTypeDef Timing;
  FMC_SDRAM_CommandTypeDef Command;
  
  static void disable_cache_for_sram();
  static void SystemClock_Config();
  static void GPIO_Init();
  static void DMA_Init();
  static void SDMMC1_SD_Init();
  static void SDRAM_Init();
  static void SDRAM_Initialization_sequence(uint32_t RefreshCount);
  
  /**
   * Does global setup of the hardware: Clocks, Flash, Cache, and all needed interfaces.
   */
  void setup() {
  	// Enable instruction- and datacache
  	SCB_EnableICache();
  	SCB_EnableDCache();
  	disable_cache_for_sram();
  	// https://community.st.com/s/question/0D50X00009XkbGoSAJ/stm32f7-dma-only-relieable-in-first-64k-of-ram
  
  	// Reset of all peripherals, Initializes the Flash interface and the Systick.
  	HAL_Init();
  
  	// Configure the system clock
  	SystemClock_Config();
  
  	// Initialize all needed peripherals
  	GPIO_Init();
  	measurement_watchdog_init();
  	DMA_Init();
  	SDRAM_Init();
  	SDMMC1_SD_Init();
  }
  
  /**
   * Disables the cache for the main SRAM. This gave errors with DMA in the past...
   */
  static void disable_cache_for_sram() {
  	MPU_Region_InitTypeDef MPU_InitStruct;
  	// Disable the MPU
  	HAL_MPU_Disable();
  	// Configure the MPU attributes as WT for SRAM1 and SRAM2
  	MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  	MPU_InitStruct.BaseAddress = 0x20010000;
  	MPU_InitStruct.Size = MPU_REGION_SIZE_256KB;
  	MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  	MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  	MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  	MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; // Not sharable (forum)
  	MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  	MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
  	MPU_InitStruct.SubRegionDisable = 0x00;
  	MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
  	HAL_MPU_ConfigRegion(&MPU_InitStruct);
  	// Enable the MPU
  	HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
  }
  
  /**
   * System Clock Configuration. Generated by CubeMX. Touching this is not good..
   */
  static void SystemClock_Config() {
  	RCC_OscInitTypeDef RCC_OscInitStruct;
  	RCC_ClkInitTypeDef RCC_ClkInitStruct;
  	RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
  
  	__HAL_RCC_PWR_CLK_ENABLE();
  
  	__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  
  	// Initializes the CPU, AHB and APB busses clocks
  	RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
  	RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  	RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  	RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  	RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  	RCC_OscInitStruct.PLL.PLLM = 25;
  	RCC_OscInitStruct.PLL.PLLN = 400;
  	RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  	RCC_OscInitStruct.PLL.PLLQ = 9;
  	if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  	{
  		Error_Handler();
  	}
  
  	// Activate the Over-Drive mode
  	if (HAL_PWREx_EnableOverDrive() != 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;
  	RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  	RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  	RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  	RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
  
  	if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_6) != HAL_OK)
  	{
  		Error_Handler();
  	}
  
  	PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPDIFRX|RCC_PERIPHCLK_LTDC
  			|RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USART1
  			|RCC_PERIPHCLK_USART6|RCC_PERIPHCLK_SAI2
  			|RCC_PERIPHCLK_I2C1|RCC_PERIPHCLK_I2C3
  			|RCC_PERIPHCLK_SDMMC1|RCC_PERIPHCLK_CLK48;
  	PeriphClkInitStruct.PLLI2S.PLLI2SN = 100;
  	PeriphClkInitStruct.PLLI2S.PLLI2SP = RCC_PLLP_DIV2;
  	PeriphClkInitStruct.PLLI2S.PLLI2SR = 2;
  	PeriphClkInitStruct.PLLI2S.PLLI2SQ = 2;
  	PeriphClkInitStruct.PLLSAI.PLLSAIN = 384;
  	PeriphClkInitStruct.PLLSAI.PLLSAIR = 5;
  	PeriphClkInitStruct.PLLSAI.PLLSAIQ = 2;
  	PeriphClkInitStruct.PLLSAI.PLLSAIP = RCC_PLLSAIP_DIV8;
  	PeriphClkInitStruct.PLLI2SDivQ = 1;
  	PeriphClkInitStruct.PLLSAIDivQ = 1;
  	PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_8;
  	PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
  	PeriphClkInitStruct.Sai2ClockSelection = RCC_SAI2CLKSOURCE_PLLSAI;
  	PeriphClkInitStruct.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
  	PeriphClkInitStruct.Usart6ClockSelection = RCC_USART6CLKSOURCE_PCLK2;
  	PeriphClkInitStruct.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
  	PeriphClkInitStruct.I2c3ClockSelection = RCC_I2C3CLKSOURCE_PCLK1;
  	PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48SOURCE_PLLSAIP;
  	PeriphClkInitStruct.Sdmmc1ClockSelection = RCC_SDMMC1CLKSOURCE_CLK48;
  	if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  	{
  		Error_Handler();
  	}
  
  	// Configure the Systick interrupt time
  	HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
  
  	// Configure the Systick
  	HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
  
  	// SysTick_IRQn interrupt configuration
  	HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);
  }
  
  /**
   * Initialize the 100kHz timer.
   */
  void TIM2_Init() {
  	__HAL_RCC_TIM2_CLK_ENABLE();
  
  	htim2.Instance = TIM2;
  	htim2.Init.Prescaler = 0;
  	htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  	htim2.Init.Period = 1000-1; // 100mhz / 1000 -> 100khz
  	htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  	htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  	if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  	{
  		Error_Handler();
  	}
  
  	TIM_ClockConfigTypeDef sClockSourceConfig;
  	sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  	if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  	{
  		Error_Handler();
  	}
  
  	HAL_NVIC_SetPriority(TIM2_IRQn, 5, 0);
  	HAL_NVIC_EnableIRQ(TIM2_IRQn);
  
  	measure_reference_timer_ticks = 0;
  	HAL_TIM_Base_Start_IT(&htim2);
  }
  
  static void GPIO_Init() {
  
  	GPIO_InitTypeDef GPIO_InitStruct;
  
  	// GPIO Ports Clock Enable
  	__HAL_RCC_GPIOE_CLK_ENABLE();
  	__HAL_RCC_GPIOG_CLK_ENABLE();
  	__HAL_RCC_GPIOB_CLK_ENABLE();
  	__HAL_RCC_GPIOD_CLK_ENABLE();
  	__HAL_RCC_GPIOC_CLK_ENABLE();
  	__HAL_RCC_GPIOA_CLK_ENABLE();
  	__HAL_RCC_GPIOJ_CLK_ENABLE();
  	__HAL_RCC_GPIOI_CLK_ENABLE();
  	__HAL_RCC_GPIOK_CLK_ENABLE();
  	__HAL_RCC_GPIOF_CLK_ENABLE();
  	__HAL_RCC_GPIOH_CLK_ENABLE();
  
  	// uSD_Detect_Pin
  	GPIO_InitStruct.Pin = uSD_Detect_Pin;
  	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  	GPIO_InitStruct.Pull = GPIO_NOPULL;
  	HAL_GPIO_Init(uSD_Detect_GPIO_Port, &GPIO_InitStruct);
  
  	// Backlight control pin. Can be removed, if no LCD is connected..
  	GPIO_InitStruct.Pin = GPIO_PIN_3;
  	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  	GPIO_InitStruct.Pull = GPIO_NOPULL;
  	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  	HAL_GPIO_Init(GPIOK, &GPIO_InitStruct);
  	HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_RESET);
  
  	// Configure GPIO pin : RMII_RXER_Pin
  	GPIO_InitStruct.Pin = RMII_RXER_Pin;
  	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  	GPIO_InitStruct.Pull = GPIO_NOPULL;
  	HAL_GPIO_Init(RMII_RXER_GPIO_Port, &GPIO_InitStruct);
  
  	// Misc output pin (PI3, D7)
  	GPIO_InitStruct.Pin = GPIO_PIN_3;
  	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  	GPIO_InitStruct.Pull = GPIO_NOPULL;
  	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  	HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);
  	HAL_GPIO_WritePin(GPIOI, GPIO_PIN_3, GPIO_PIN_RESET);
  }
  
  /**
   * Enable DMA controller clock. This is needed for the SD card.
   */
  static void DMA_Init() {
  	// DMA controller clock enable
  	__HAL_RCC_DMA2_CLK_ENABLE();
  
  	// DMA interrupt init
  	// DMA2_Stream3_IRQn interrupt configuration
  	HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 8, 0);
  	HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
  	// DMA2_Stream6_IRQn interrupt configuration
  	HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 8, 0);
  	HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);
  }
  
  /**
   * Init the sdram. Generated by CubeMX.
   * The parameter are for the build in MT48LC4M32B2B5-6A (MICRON)
   */
  static void SDRAM_Init() {
  	// Use 0x60000000 for the SDram. Enables caching.
  	HAL_EnableFMCMemorySwapping();
  
  	// SDRAM device configuration
  	sdramHandle.Instance = FMC_SDRAM_DEVICE;
  
  	// Timing configuration for 100Mhz as SD clock frequency (System clock is up to 200Mhz)
  	Timing.LoadToActiveDelay    = 2;
  	Timing.ExitSelfRefreshDelay = 7;
  	Timing.SelfRefreshTime      = 4;
  	Timing.RowCycleDelay        = 7;
  	Timing.WriteRecoveryTime    = 2;
  	Timing.RPDelay              = 2;
  	Timing.RCDDelay             = 2;
  
  	sdramHandle.Init.SDBank             = FMC_SDRAM_BANK1;
  	sdramHandle.Init.ColumnBitsNumber   = FMC_SDRAM_COLUMN_BITS_NUM_8;
  	sdramHandle.Init.RowBitsNumber      = FMC_SDRAM_ROW_BITS_NUM_12;
  	sdramHandle.Init.MemoryDataWidth    = SDRAM_MEMORY_WIDTH;
  	sdramHandle.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
  	sdramHandle.Init.CASLatency         = FMC_SDRAM_CAS_LATENCY_2;
  	sdramHandle.Init.WriteProtection    = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
  	sdramHandle.Init.SDClockPeriod      = SDCLOCK_PERIOD;
  	sdramHandle.Init.ReadBurst          = FMC_SDRAM_RBURST_ENABLE;
  	sdramHandle.Init.ReadPipeDelay      = FMC_SDRAM_RPIPE_DELAY_0;
  
  	if(HAL_SDRAM_Init(&sdramHandle, &Timing) != HAL_OK) {
  		Error_Handler();
  	}
  
  	SDRAM_Initialization_sequence(REFRESH_COUNT);
  }
  
  /**
   * @brief  Programs the SDRAM device.
   * @param  RefreshCount: SDRAM refresh counter value
   * @retval None
   */
  static void SDRAM_Initialization_sequence(uint32_t refresh_count) {
  	__IO uint32_t tmpmrd = 0;
  
  	/* Step 1: Configure a clock configuration enable command */
  	Command.CommandMode            = FMC_SDRAM_CMD_CLK_ENABLE;
  	Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK1;
  	Command.AutoRefreshNumber      = 1;
  	Command.ModeRegisterDefinition = 0;
  
  	// Send the command
  	HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
  
  	// Step 2: Insert 100 us minimum delay
  	// Inserted delay is equal to 1 ms due to systick time base unit (ms)
  	HAL_Delay(1);
  
  	// Step 3: Configure a PALL (precharge all) command
  	Command.CommandMode            = FMC_SDRAM_CMD_PALL;
  	Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK1;
  	Command.AutoRefreshNumber      = 1;
  	Command.ModeRegisterDefinition = 0;
  
  	// Send the command
  	HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
  
  	// Step 4: Configure an Auto Refresh command
  	Command.CommandMode            = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
  	Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK1;
  	Command.AutoRefreshNumber      = 8;
  	Command.ModeRegisterDefinition = 0;
  
  	// Send the command
  	HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
  
  	// Step 5: Program the external memory mode register
  	tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1          |\
  			SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |\
  			SDRAM_MODEREG_CAS_LATENCY_2           |\
  			SDRAM_MODEREG_OPERATING_MODE_STANDARD |\
  			SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
  
  	Command.CommandMode            = FMC_SDRAM_CMD_LOAD_MODE;
  	Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK1;
  	Command.AutoRefreshNumber      = 1;
  	Command.ModeRegisterDefinition = tmpmrd;
  
  	// Send the command
  	HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
  
  	// Step 6: Set the refresh rate counter
  	// Set the device refresh rate
  	HAL_SDRAM_ProgramRefreshRate(&sdramHandle, refresh_count);
  }
  
  /**
   *  SDMMC1 init
   */
  static void SDMMC1_SD_Init()
  {
  	hsd1.Instance = SDMMC1;
  	hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
  	hsd1.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE;
  	hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
  	hsd1.Init.BusWide = SDMMC_BUS_WIDE_1B;
  	hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
  	hsd1.Init.ClockDiv = 0;
  }
  
  /**
   * Period elapsed callback. htim is the timer handle.
   * Used handles:
   * TIM6: 500Hz SysTick
   * TIM2: 100kHz reference timer
   */
  #ifdef SIMULATE_ADC
  uint8_t __c = 0;
  #endif
  void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  {
  	if (htim->Instance == TIM6) {
  		HAL_IncTick();
  	} else if (htim->Instance == TIM2) {
  		measure_reference_timer_ticks++;
  		HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_3);
  #ifdef SIMULATE_ADC
  		__c++;
  		if (__c==SIMULATE_ADC_SCALING) {
  			DRDY_Interrupt((measure_reference_timer_ticks&0xF000) >> 12);
  			__c=0;
  		}
  #endif
  	} else if (htim->Instance == TIM5) {
  		measurement_watchdog_tick();
  	}
  }
  
  /**
   * The DRDY interrupt
   */
  void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
  #ifndef SIMULATE_ADC
  	if (GPIO_Pin == ADS1262_DRDY_PIN) {
  		DRDY_Interrupt();
  	}
  #endif
  }
  