/**
******************************************************************************
* @file pwm_thyristors.c
* @brief Модуль для управления тиристорами
******************************************************************************
* @details
******************************************************************************/
#include "pwm_thyristors.h"
#include "angle_control.h"
#include "tim.h"

static HAL_StatusTypeDef __PWM_SetOutputState(PWM_Channel_t *hCh, uint32_t state);

/** 
  * @brief    Инициализация ШИМ тиристоров.
  * @param    hpwm  Указатель на хендл ШИМ тиристоров
  * @return   HAL Status.
  * @details  Инициализируется 6 структур на каждый канал ШИМ для тиристора. 
  * И одна dummy структура для безопасной инициализации неизвестной фазы.
  * В конце запускаются все каналы ШИМ (с запрещенным выходом).
  */
HAL_StatusTypeDef PWM_Init(PWM_Handle_t *hpwm)
{
  if(hpwm == NULL)
    return HAL_ERROR;
  
  // Фаза A
  hpwm->AllPhases[PHASE_A_POS].State  = PWM_THYR_DISABLED;
  hpwm->AllPhases[PHASE_A_POS].ChMask = PWM_CHANNEL_1;
  hpwm->AllPhases[PHASE_A_POS].htim   = &hpwm1;
  hpwm->AllPhases[PHASE_A_NEG].State  = PWM_THYR_DISABLED;
  hpwm->AllPhases[PHASE_A_NEG].ChMask = PWM_CHANNEL_2;
  hpwm->AllPhases[PHASE_A_NEG].htim   = &hpwm1;
  
  // Фаза B
  hpwm->AllPhases[PHASE_B_POS].State  = PWM_THYR_DISABLED;
  hpwm->AllPhases[PHASE_B_POS].ChMask = PWM_CHANNEL_3;
  hpwm->AllPhases[PHASE_B_POS].htim   = &hpwm1;  
  hpwm->AllPhases[PHASE_B_NEG].State  = PWM_THYR_DISABLED;
  hpwm->AllPhases[PHASE_B_NEG].ChMask = PWM_CHANNEL_4;
  hpwm->AllPhases[PHASE_B_NEG].htim   = &hpwm1;
  
  // Фаза C
  hpwm->AllPhases[PHASE_C_POS].State  = PWM_THYR_DISABLED;
  hpwm->AllPhases[PHASE_C_POS].ChMask = PWM_CHANNEL_5;
  hpwm->AllPhases[PHASE_C_POS].htim   = &hpwm2;  
  hpwm->AllPhases[PHASE_C_NEG].State  = PWM_THYR_DISABLED;
  hpwm->AllPhases[PHASE_C_NEG].ChMask = PWM_CHANNEL_6;
  hpwm->AllPhases[PHASE_C_NEG].htim   = &hpwm2;
  
  // Фаза неизвестная (чтобы не был указатель в никуда, а был на эту структуру)
  hpwm->AllPhases[PHASE_UNKNOWN].State  = PWM_THYR_DISABLED;
  hpwm->AllPhases[PHASE_UNKNOWN].ChMask = 0;
  hpwm->AllPhases[PHASE_UNKNOWN].htim   = NULL;
  hpwm->f.Initialized = 1;
   
  // Инициализируем ничем. Потом когда словим сеть - поставим конкретные тиристоры
  PWM_SetHalfWave(hpwm, UPP_PHASE_A, UPP_WAVE_UNKNOWED);
  PWM_SetHalfWave(hpwm, UPP_PHASE_B, UPP_WAVE_UNKNOWED);
  PWM_SetHalfWave(hpwm, UPP_PHASE_C, UPP_WAVE_UNKNOWED);
  
  PWM_SetConfig(hpwm, MB_INTERNAL.param.pwm.PhaseMask, MB_INTERNAL.param.pwm.Frequency, MB_INTERNAL.param.pwm.PulseNumber);
  
  HAL_TIM_PWM_Start(&hpwm1, PWM_CHANNEL_1);
  HAL_TIM_PWM_Start(&hpwm1, PWM_CHANNEL_2);
  HAL_TIM_PWM_Start(&hpwm1, PWM_CHANNEL_3);
  HAL_TIM_PWM_Start(&hpwm1, PWM_CHANNEL_4);
  HAL_TIM_PWM_Start(&hpwm2, PWM_CHANNEL_5);
  HAL_TIM_PWM_Start(&hpwm2, PWM_CHANNEL_6);
  HAL_TIM_Base_Start_IT(&hpwm1);

  
  return HAL_OK;
}

/** 
  * @brief    Запуск ШИМ.
  * @param    hpwm  Указатель на хендл ШИМ тиристоров
  * @param    Phase На какой фазе надо запустить ШИМ
  * @return   HAL Status.
  * @details  Переводит автомат канала ШИМ в состояние запуска ШИМ.
  */
HAL_StatusTypeDef PWM_Start(PWM_Handle_t *hpwm, UPP_Phase_t Phase)
{
  if(assert_upp(hpwm))
    return HAL_ERROR;
  // Если канал дурацкий - возвращаем ошибку
  if(Phase >= 3)
  {
    return HAL_ERROR;
  }
  if (hpwm->Phase[Phase] == NULL || hpwm->Phase[Phase] == &hpwm->AllPhases[PHASE_UNKNOWN])
    return HAL_ERROR;
  
  
  switch(hpwm->Phase[Phase]->State)
  {
    // Если мы НЕ в режиме ожидание - ошибка
    case PWM_THYR_DISABLED:
    case PWM_THYR_TIM_START:
    case PWM_THYR_TIM_ACTIVE:    
    case PWM_THYR_TIM_DONE:
      return HAL_BUSY;

    // Запуск только если таймер в режиме ожидания
    case PWM_THYR_TIM_WAIT:
      hpwm->Phase[Phase]->State = PWM_THYR_TIM_START;
      return HAL_OK; 
    
    default:
      return HAL_ERROR;
  }
  
  return HAL_ERROR;
}

/** 
  * @brief    Остановить ШИМ.
  * @param    hpwm  Указатель на хендл ШИМ тиристоров
  * @param    Phase На какой фазе надо остановить ШИМ
  * @param    force_stop_all Принудительно остановить ВЕСЬ ШИМ
  * @return   HAL Status.
  * @details  Переводит автомат канала ШИМ в состояние отключенного ШИМ и 
  * включает канал в режим форсированного неактивного выхода.
  * При передаче 1 в force_stop_all, отключаются все каналы ШИМ и выдается дискрет на запрет ШИМ
  */
HAL_StatusTypeDef PWM_Stop(PWM_Handle_t *hpwm, UPP_Phase_t Phase, uint8_t force_stop_all)
{
  if(assert_upp(hpwm))
    return HAL_ERROR;
  // Если канал дурацкий - возвращаем ошибку
  if(Phase >= 3)
  {
    return HAL_ERROR;
  }  
  if (hpwm->Phase[Phase] == NULL || hpwm->Phase[Phase] == &hpwm->AllPhases[PHASE_UNKNOWN])
    return HAL_ERROR;
  
  hpwm->Phase[Phase]->State = PWM_THYR_DISABLED;

  // Если не force_stop_all - сбрасываем только текущий канал
  if (!force_stop_all)
  {
    __PWM_SetOutputState(hpwm->Phase[Phase], PWM_DISABLE);
  }
  // Если force_stop_all - сбрасываем ВСЕ КАНАЛЫ
  else {
    for(int ch = 0; ch < 6; ch++)
    {
      __PWM_SetOutputState(&hpwm->AllPhases[ch], PWM_DISABLE);
    }
  }
  
  return HAL_OK;
}
/** 
  * @brief    Хендл ШИМ тиристоров.
  * @param    hpwm  Указатель на хендл ШИМ тиристоров
  * @return   HAL Status.
  * @details  Автомат состояний, который определяет поведение каналов ШИМ
  */
HAL_StatusTypeDef PWM_Handle(PWM_Handle_t *hpwm)
{
  if(assert_upp(hpwm))
    return HAL_ERROR;

  uint8_t phase = 0;
  PWM_Channel_t *hPhase = NULL;

  for(int ch = 0; ch < 6; ch++)
  {
    hPhase = &hpwm->AllPhases[ch];
    if (hPhase->htim == NULL)
      continue;

    switch (hPhase->State)
    {
      case PWM_THYR_DISABLED: // канал отключен
        __PWM_SetOutputState(hPhase, PWM_DISABLE);
        break;

      case PWM_THYR_TIM_WAIT: // канал ожидает команды
        __PWM_SetOutputState(hPhase, PWM_DISABLE);
        break;

      case PWM_THYR_TIM_START: // начать ШИМ (пачка импульсов)
        __PWM_SetOutputState(hPhase, PWM_ENABLE);
        hPhase->PulseCnt = hpwm->Config.PulseNumber;
        hPhase->State = PWM_THYR_TIM_ACTIVE;
        hpwm->f.Running++;
        break;

      case PWM_THYR_TIM_ACTIVE: // управление пачкой импульсов ШИМ
        hPhase->PulseCnt--;
        if (hPhase->PulseCnt <= 0)
        {
          hPhase->PulseCnt = 0;
          hPhase->State = PWM_THYR_TIM_DONE;
        }
        break;

      case PWM_THYR_TIM_DONE: // пачка импульсов отправлена - отключение
        hPhase->State = PWM_THYR_TIM_WAIT;
        if(hpwm->f.Running)
          hpwm->f.Running--;
        break;

      default: // чзх
        __PWM_SetOutputState(hPhase, PWM_DISABLE);
        break;
    }
  }
  return HAL_OK;
}

/** 
  * @brief    Установка параметров ШИМ.
  * @param    hpwm      Указатель на хендл ШИМ тиристоров
  * @param    Frequency Частота в ГЦ
  * @return   HAL Status.
  */
HAL_StatusTypeDef PWM_SetConfig(PWM_Handle_t *hpwm, uint8_t PhaseMask, uint16_t Frequency, uint8_t PulseNumber)
{
  if(assert_upp(hpwm))
    return HAL_ERROR;
  if(hpwm->f.Running) // Если есть активные каналы - ниче не меняем
    return HAL_BUSY;
  
  // Остановка таймера
  HAL_TIM_Base_Stop_IT(&hpwm1);
  
  hpwm->Config.PhaseMask.all = PhaseMask;
  hpwm->Config.PulseNumber = PulseNumber;
  hpwm->Config.Frequency = Frequency;
  // Высставление периодов
  __HAL_TIM_SET_AUTORELOAD(&hpwm1, TIM_FreqToTick(Frequency, PWM_TIM1_FREQ_MHZ));
  __HAL_TIM_SET_AUTORELOAD(&hpwm2, TIM_FreqToTick(Frequency, PWM_TIM3_FREQ_MHZ));
  // Скважности 50/50
  __HAL_TIM_SET_COMPARE(&hpwm1, PWM_CHANNEL_1, __HAL_TIM_GET_AUTORELOAD(&hpwm1)/2);
  __HAL_TIM_SET_COMPARE(&hpwm1, PWM_CHANNEL_2, __HAL_TIM_GET_AUTORELOAD(&hpwm1)/2);
  __HAL_TIM_SET_COMPARE(&hpwm1, PWM_CHANNEL_3, __HAL_TIM_GET_AUTORELOAD(&hpwm1)/2);
  __HAL_TIM_SET_COMPARE(&hpwm1, PWM_CHANNEL_4, __HAL_TIM_GET_AUTORELOAD(&hpwm1)/2);
  __HAL_TIM_SET_COMPARE(&hpwm2, PWM_CHANNEL_5, __HAL_TIM_GET_AUTORELOAD(&hpwm2)/2);
  __HAL_TIM_SET_COMPARE(&hpwm2, PWM_CHANNEL_6, __HAL_TIM_GET_AUTORELOAD(&hpwm2)/2);
  
  
  // Сброс счетчиков таймера и запуск заного
  __HAL_TIM_SET_COUNTER(&hpwm1, 0);
  __HAL_TIM_SET_COUNTER(&hpwm2, 0);  
  return HAL_TIM_Base_Start_IT(&hpwm1);
}


/** 
  * @brief    Установка полуволны для слежения.
  * @param    hpwm      Указатель на хендл ШИМ тиристоров
  * @param    Phase     Для какой фазы надо установить полуволну
  * @param    halfwave  Какую полуволну установить
  * @return   HAL Status.
  * @details  Меняет указатель канала фазы на канал соответствующей полуволны
  */
HAL_StatusTypeDef PWM_SetHalfWave(PWM_Handle_t *hpwm, UPP_Phase_t Phase, UPP_HalfWave_t halfwave)
{
  if(assert_upp(hpwm))
    return HAL_ERROR;
  
  // Сбрасываем текущий канал
  PWM_Stop(hpwm, Phase, 0);

  // Если канал дурацкий - выставляем заглушку
  if(Phase >= 3)
  {
    hpwm->Phase[Phase] = &hpwm->AllPhases[PHASE_UNKNOWN];
    return HAL_ERROR;
  }
  // Выставляем канал
  switch(halfwave)
  {
    case UPP_WAVE_POSITIVE:
      hpwm->Phase[Phase] = &hpwm->AllPhases[Phase];
      hpwm->Phase[Phase]->State = PWM_THYR_TIM_WAIT;
      return HAL_OK;
    
    case UPP_WAVE_NEGATIVE:
      hpwm->Phase[Phase] = &hpwm->AllPhases[Phase+3];
      hpwm->Phase[Phase]->State = PWM_THYR_TIM_WAIT;
      return HAL_OK;
    
    default:
      hpwm->Phase[Phase] = &hpwm->AllPhases[PHASE_UNKNOWN];
      return HAL_ERROR;
  }
}

/** 
  * @brief    Установка режима для канала ШИМ.
  * @param    hpwm  Указатель на хендл ШИМ тиристоров
  * @param    Phase Для какой фазы надо установить состояние
  * @param    state Какое состояние установить
  * @return   HAL Status.
  */
static HAL_StatusTypeDef __PWM_SetOutputState(PWM_Channel_t *hCh, uint32_t state)
{
  if(hCh == NULL)
    return HAL_ERROR;
  if (hCh->htim == NULL)
    return HAL_ERROR;


  uint32_t ch_mode = state;
  
  switch(hCh->ChMask)
  {
    case TIM_CHANNEL_1:
      hCh->htim->Instance->CCMR1 &= ~TIM_OCMODE_PWM2;
      hCh->htim->Instance->CCMR1 |= ch_mode;
      break;
    case TIM_CHANNEL_2:
      hCh->htim->Instance->CCMR1 &= ~(TIM_OCMODE_PWM2 << 8);
      hCh->htim->Instance->CCMR1 |= (ch_mode << 8);
      break;
    case TIM_CHANNEL_3:
      hCh->htim->Instance->CCMR2 &= ~TIM_OCMODE_PWM2;
      hCh->htim->Instance->CCMR2 |= ch_mode;
      break;
    case TIM_CHANNEL_4:
      hCh->htim->Instance->CCMR2 &= ~(TIM_OCMODE_PWM2 << 8);
      hCh->htim->Instance->CCMR2 |= (ch_mode << 8);
      break;
    default:
      break;
  }
  return HAL_OK;
}
