/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 *
 *          Must update the version and date information in "flash.h"         *
 *          Must update the version and date information in "flash.h"         *
 *          Must update the version and date information in "flash.h"         *
 *
 ******************************************************************************
 * edit            :    HwangSungWoo
 * date            :    3/19 종혁님과 작업
 * FW              :    STM32F446_v0.9.1_ProtocolForSPOEX25_250319_PT6BSample
 * Original FW     :    STM32F446_v0.9.1_250317Snapshot_ProtocolForSPOEX25_250319
 * PCB HW          :    v3
 * Machine         :    PT6BSample  *****(3/19일자 앱개발용 샘플 제공용 FW)
 * Goal            :   앱과의 통신 프로토콜 업데이트 (SPOEX를 위한 긴급 기능 한정)
 *     1.
 *     2.
 * Key change      :
 *     1. F_xx들과 I_xx들을 실제 단위 kg, A에 맞게 바꿔야 하는 걸 임시로 했는데 완전히 처리된 지 모름
 *     2.
 * etc.            :
 *     1. 테스트 편의성을 위한 AutoWeight 기능 비활성화 in "WeightManager()"
 *     2.
 * <Open Issues>
 *    1.  가끔씩 상전류 튀면서 귀뚜라미 소리 가 발생함 : 완전 동일한 FW인데 상황에 따라 발생하기도, 안 하기도 함.
 *        - 대체로는 발생 안 함
 *        - 부동소수점 연산 등 줄이면 제거되는 줄 알았는데, 잊을만 하면 종종 발생
 ******************************************************************************
 * @file           : main.c
 * @brief          : Main program body
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2023 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "dac.h"
#include "dma.h"
#include "i2c.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include "usb_otg.h"
#include "gpio.h"
#include "flash.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "WESPION.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define TIMCLCOCK   180000000
#define PRESCALAR   180
#define PERIOD      100   //us

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
//#define __DEBUG__

#ifdef  __DEBUG__
#define DEBUG_printf                UART3_printf
#else
#define DEBUG_printf                Dummy_printf
#endif
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void PeriphCommonClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
float MaxDa1 = 360.0f, MaxDa2 = 360.0f;
uint32_t Dac1, Dac2;
//stm32f4xx_hal.c
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  /* Configure the SysTick to have interrupt in 1ms time basis*/
  if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
  {
    return HAL_ERROR;
  }

  /* Configure the SysTick IRQ priority */
  if (TickPriority < (1UL << __NVIC_PRIO_BITS))
  {
    HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
    uwTickPrio = TickPriority;
  }
  else
  {
    return HAL_ERROR;
  }

  /* Return function status */
  return HAL_OK;
}

int check_While, check_Dac;
//  디버깅 할거면 1 -> WeightManager 수행 안 함 / 안 할거면 0
int Debug_Status = 0;
//  Debug_DriveMotor는 Debug_Status에 따라 자동으로 0, 3 세팅되게 변경 (25.03.04)
int Debug_DriveMotor = 0;
int Debug_Calib=3;
int Debug_LED_Display_Level = 0;
uint8_t LED_Weight_R, LED_Weight_G, LED_Weight_B;
uint8_t LED_Back_R, LED_Back_G, LED_Back_B;
uint8_t SW1_new, SW1_old, SW1_State;
int First_Init_ADC_IDC = 0;
/* USER CODE END 0 */

/**
 * @brief  The application entry point.
 * @retval int
 */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  switch(Debug_Status) {
    case 0:
      Debug_DriveMotor = 3;
      break;
    case 1:
      Debug_DriveMotor = 0;
      break;
  }

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* Configure the peripherals common clocks */
  PeriphCommonClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_DAC_Init();
  MX_I2C1_Init();
  MX_SPI1_Init();
  MX_SPI4_Init();
  MX_TIM1_Init();
  MX_TIM4_Init();
  MX_TIM8_Init();
  MX_TIM9_Init();
  MX_USART2_UART_Init();
  MX_USART3_UART_Init();
  MX_ADC3_Init();
  MX_ADC1_Init();
  MX_ADC2_Init();
  MX_TIM2_Init();
  MX_USB_OTG_FS_USB_Init();
  MX_TIM5_Init();
  /* USER CODE BEGIN 2 */
    Regen_Resistor_Init();                ///somebp 2024.02.02
  //System_Wakeup();                      //for Wake-up from Sleep mode     //not necessary

  DAC1_Start();
  DAC2_Start();

  ADC_Start_DMA();    // 덜 중요
  //  ADC_SWStart();                      //Start Trigger

  ADC1_InjectedStart_IT();    // 중요 -> 전류 제어 관련 (전류 센싱, DC전압,위치), 3상 전류 하이 로우 6개 + 위치 2*2개 + DC전압 + 여분1개
  ADC2_InjectedStart_IT();    // 주석 처리하는 게 맞아 보임!!!
  ADC3_InjectedStart_IT();    // 1,2와 달리 3은 인젝티트 모드가 아니라 폴링으로 도는 (덜 중요한) 채널이 같이 있어서, DMA로 도는... (이해 어려움)

//  Init_ADC_IDC();

  //  HAL_TIM_Encoder_Start_IT(&htim4, TIM_CHANNEL_ALL);
  //  HAL_TIM_Encoder_Start_IT(&htim2, TIM_CHANNEL_ALL);
  HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);
  HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL);

  //  TIM1_PWM_Start_IT(TIM_CHANNEL_1);         //include TIM1_Start()
  //  TIM1_PWM_Start_IT(TIM_CHANNEL_2);         //
  //  TIM1_PWM_Start_IT(TIM_CHANNEL_3);         //
  //  TIM1_PWMN_Start_IT(TIM_CHANNEL_1);
  //  TIM1_PWMN_Start_IT(TIM_CHANNEL_2);
  //  TIM1_PWMN_Start_IT(TIM_CHANNEL_3);

  TIM1_PWM_Start_IT(TIM_CHANNEL_4);
  TIM1_PWMN_Start_IT(TIM_CHANNEL_4);
  //  TIM1_Enable_IT(TIM_IT_UPDATE);
  TIM1_Stop();

  //  TIM8_PWM_Start_IT(TIM_CHANNEL_1);         //include TIM8_Start()
  //  TIM8_PWM_Start_IT(TIM_CHANNEL_2);         //
  //  TIM8_PWM_Start_IT(TIM_CHANNEL_3);         //
  //  TIM8_PWMN_Start_IT(TIM_CHANNEL_1);
  //  TIM8_PWMN_Start_IT(TIM_CHANNEL_2);
  //  TIM8_PWMN_Start_IT(TIM_CHANNEL_3);
  //  TIM8_Enable_IT(TIM_IT_UPDATE);
  TIM8_Stop();

//  Regen_Resistor_Init();    // 반드시 위에서 실행돼야 함!!!! (2/27)

  HAL_TIM_PWM_Start(&htim9, TIM_CHANNEL_1);

  HAL_TIM_PWM_Start(&htim5,TIM_CHANNEL_1);
  HAL_NVIC_DisableIRQ(DMA2_Stream0_IRQn);

  TIM1_Start();     // 완전 동시에 맞춘 상태는 아니고 거의 동시
  TIM8_Start();

  Motor1Ctrl_PwmStart();
  Motor2Ctrl_PwmStart();

  Flash_Init();                               //Backup or Restore
  //  BLE_Rename(BLE_NAME);                       //MUST run only ONE time.
  LED_Send_All(60, 0,0,0, 0,0,0);             //Addressable LED turn OFF
  Delay_msec(10);                             //somebp 2024.02.08

  Initialize_WESPION();
  UART3_Puts("\r\n---------- ---------- ----------\r\nRoomFit On!\r\n");
  UART3_printf("FW v%d.%d.%d (%04d.%02d.%02d)\r\n",WespionVer.VerMajer,WespionVer.VerMiner,WespionVer.VerSub,WespionVer.VerYear,WespionVer.VerMonth,WespionVer.VerDate);///somebp 2024.02.08
  //          무게 관련 상태 업데이트
  Return_4x1byte(WEIGHTMINUS, WP_Gym.WeightSet[L]*4, WP_Gym.WeightMode[L], WP_Gym.WeightSet[R]*4, WP_Gym.WeightMode[R]);
  //          전압 정보 업데이트
  int Vdc100 = Inv.Ctrl.Vdc * 100;
  Return_2byte(GET_VOLTAGE, Vdc100);
  //          무게 On/Off 여부 업데이트
  Return_1byte(WEIGHTONOFF, WP_Weight.Ctrl.OnOffStatus[L]);
  //  Welcome Light (HSW 24.02.02)
  for(int i=1; i<=LED_NUMBER; i++) {
    LED_Send_Now(i,3,3,51);
    Delay_msec(6);
  }
  for(int i=LED_NUMBER; i>0; i--) {
    LED_Send_Now(i,16,16,102);
    Delay_msec(4);
  }
  for(int i=1; i<=LED_NUMBER; i++) {
    LED_Send_Now(i,29,29,153);
    Delay_msec(5);
  }
  for(int i=LED_NUMBER; i>0; i--) {
    LED_Send_Now(i,42,42,204);
    Delay_msec(4);
  }
  for(int i=1; i<=LED_NUMBER; i++) {
    LED_Send_Now(i,55,55,255);
    Delay_msec(4);
  }
  for(int i=LED_NUMBER; i>0; i--) {
    LED_Send_Now(i,3,3,3);
    Delay_msec(4);
  }

  //  Task_Start_1ms(Task1ms);
  Task_Start_50ms(Task50ms);
  Task_Start_100ms(Task100ms);  //  LED_Dispaly_Level
  Task_Start_60s(Report_Vdc);   //  Voltage Report - Periodic

  int Time;
  int32_t LED_Period = 1000;    //  [msec]
  int8_t NumSequence = 5;

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */

  while(1) {

    //  Dac를 통해 수행 주기 체크 (Dac 세팅에 소요 시간을 최소화하기 위해 레지스터 직접 설정 방식 적용 by ChatGPT)
//    static uint32_t dac_value = 0;
//    dac_value = (dac_value == 0) ? 2048 : 0;
//    // DAC1 채널 1 (PA4) 값 설정 - 레지스터 직접 조작
//    DAC->DHR12R1 = dac_value;
//    DAC->SWTRIGR |= DAC_SWTRIGR_SWTRIG1;  // 소프트웨어 트리거 (출력 반영)


//    Regen_Resistor_Control();           ///somebp 2024.02.02
    Regen_Resistor_Control_HSW();     // 수행시간 부족 유발로 수행 위치 이동 테스트 중 -> 스케쥴러 시도 (2/27)

    Protocol_Receive_Task_Uart3();    // BLE Protocol is more important than being in this location.... (24.02.09)

    Time = Get_Time();

//    if(First_Init_ADC_IDC == 0 && Time > 500) {
//      Init_ADC_IDC();
//      First_Init_ADC_IDC++;
//    }

    if(Time % LED_Period == 0) {
      digitalWrite(56, 1);
      digitalWrite(57, 1);
      digitalWrite(87, 1);
    } else if (Time % LED_Period == LED_Period/NumSequence*1) {
      digitalWrite(56, 0);
    } else if (Time % LED_Period == LED_Period/NumSequence*2) {
      digitalWrite(57, 0);
    } else if (Time % LED_Period == LED_Period/NumSequence*3) {
      digitalWrite(87, 0);
    }
    check_While++;

    //other main tasks
    //    MaxDa1 = 2047.0f/ 3300.0f;  // 1650.0f;
    //    MaxDa2 = 2047.0f/ 3300.0f;  // 1650.0f;
    //    Dac1 = (int32_t) WP_Gym.RegionCurr[L] * 2047.0f / 16.5f + 2047;
//    Dac1 = (int32_t) (4095.0f - WP_Gym.Position[R] * 4095.0f / 3300.0f);
//    Dac2 = (int32_t) (4095.0f - Motor2.Cmd.Icmd.q * 4095.0f / 330.0f);
    //    Dac1 = (int32_t) WP_Gym.Speed[L] * 2047.0f / 330.0f + 2047;
    //    Dac2 = (int32_t) WP_Gym.Speed[R] * 2047.0f / 330.0f + 2047;
    //    Dac2 = (int32_t) debug_WeightControl_v2_2 * 4095.0f / 33.0f;
    //    Dac2 = (int32_t)(Motor1_Ang.RpmFil * MaxDa1) + 2047;
    //    Dac1 = (int32_t)(Motor1.PiCurQ.Ff * 2047.0f / 33.0f) + 2047;
    //    Dac2 = (int32_t)(Motor1.Fb.V3.U * 2047.0f / 66.0f) + 2047;
    //    Dac2 = (int32_t) Motor1.PiCurQ.Ff * 2047.0f / 33.0f + 2047;
//    DAC1_SetValue(Dac1);
//    DAC2_SetValue(Dac2);
    //    check_Dac++;

    //other Tasks
    Inverter_Comm();
    //    WESPION_MotorAddon();   // 모터제어 코드에 들어갔음

    //////////////////////////////////////////////////////////////////////
    if(0)
    {                   // NTC TEST
      static uint32_t old_time = 0;
      uint32_t new_time=Get_Time();
      if(new_time - old_time > 1000) {
        old_time = new_time;
        UART3_printf("[%d] %d, NTC:%s'C\t",   0, ADC_Buffer[2], Float2String(Get_Temperature(0), 3));
        UART3_printf("[%d] %d, NTC:%s'C\r\n", 1, ADC_Buffer[3], Float2String(Get_Temperature(1), 3));
      }
    }
    //////////////////////////////////////////////////////////////////////

    // 디버깅용 - 영점 캘리브레이션
    if(Debug_Calib != 0) {
      switch (Debug_Calib) {
        case 1:
          PositionCalibrate(1);
          Debug_Calib=0;
          break;
        case 2:
          PositionCalibrate(2);
          Debug_Calib=0;
          break;
        case 3:
          PositionCalibrate(3);
          Debug_Calib=0;
          break;
      }
    }
  }
  /* 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};

  /** Configure the main internal regulator output voltage
   */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
   * in the RCC_OscInitTypeDef structure.
   */
  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;
  RCC_OscInitStruct.PLL.PLLM = 6;
  RCC_OscInitStruct.PLL.PLLN = 180;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  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 buses 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_5) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
 * @brief Peripherals Common Clock Configuration
 * @retval None
 */
void PeriphCommonClock_Config(void)
{
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  /** Initializes the peripherals clock
   */
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_TIM;
  PeriphClkInitStruct.TIMPresSelection = RCC_TIMPRES_ACTIVATED;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
 * @brief  This function is executed in case of error occurrence.
 * @retval None
 */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
 * @brief  Reports the name of the source file and the source line number
 *         where the assert_param error has occurred.
 * @param  file: pointer to the source file name
 * @param  line: assert_param error line source number
 * @retval None
 */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
