#include "hal/hal.h"
#include "hal/pwm.h"
#include "stm32f3xx_ll_tim.h"
#include "stm32f3xx_ll_gpio.h"

#define TIMxCCER_MASK_CH123        ((uint16_t)  (LL_TIM_CHANNEL_CH1|LL_TIM_CHANNEL_CH1N|\
                                                 LL_TIM_CHANNEL_CH2|LL_TIM_CHANNEL_CH2N|\
                                                 LL_TIM_CHANNEL_CH3|LL_TIM_CHANNEL_CH3N))


static TIM_HandleTypeDef htim1;
static void HAL_TIM1_Pin_Init(void);

void HAL_PWM_Init(int fs) {
	TIM_SlaveConfigTypeDef sSlaveConfig = {0};
	TIM_MasterConfigTypeDef sMasterConfig = {0};
	TIM_OC_InitTypeDef sConfigOC = {0};
	TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
	
	htim1.Instance = TIM1;
	htim1.Init.Prescaler = 0;
	htim1.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1;
	htim1.Init.Period = (SystemCoreClock / fs);
	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();
	}
	if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
	{
	  Error_Handler();
	}
	sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
	sSlaveConfig.InputTrigger = TIM_TS_ITR1;
	if (HAL_TIM_SlaveConfigSynchronization(&htim1, &sSlaveConfig) != HAL_OK)
	{
	  Error_Handler();
	}
	sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC4REF;
	sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
	sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
	if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
	{
	  Error_Handler();
	}
	sConfigOC.OCMode = TIM_OCMODE_PWM1;
	sConfigOC.Pulse = (SystemCoreClock / fs) / 2;
	sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
	sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
	sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
	sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
	sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
	if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
	{
	  Error_Handler();
	}
	if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
	{
	  Error_Handler();
	}
	if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
	{
	  Error_Handler();
	}
  	sConfigOC.OCMode = TIM_OCMODE_PWM2;
  	sConfigOC.Pulse = (SystemCoreClock / fs) - 5;
  	if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  	{
    	Error_Handler();
  	}	
	sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_ENABLE;
	sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_ENABLE;
	sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_1;
	sBreakDeadTimeConfig.DeadTime = 0;
	sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
	sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
	sBreakDeadTimeConfig.BreakFilter = 0;
	sBreakDeadTimeConfig.Break2State = TIM_BREAK2_ENABLE;
	sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_LOW;
	sBreakDeadTimeConfig.Break2Filter = 0xF;
	sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
	if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
	{
	  Error_Handler();
	}

	HAL_TIM1_Pin_Init();
}

void PWM_TimerInit(void){
	TIM_TypeDef * TIMx = htim1.Instance;
	uint32_t Brk2Timeout = 1000;
	
	LL_TIM_SetTriggerOutput(TIMx, LL_TIM_TRGO_RESET);
	
	/* Enables the TIMx Preload on CC1 Register */
	LL_TIM_OC_EnablePreload( TIMx, LL_TIM_CHANNEL_CH1 );
	/* Enables the TIMx Preload on CC2 Register */
	LL_TIM_OC_EnablePreload( TIMx, LL_TIM_CHANNEL_CH2 );
	/* Enables the TIMx Preload on CC3 Register */
	LL_TIM_OC_EnablePreload( TIMx, LL_TIM_CHANNEL_CH3 );
	/* Prepare timer for synchronization */
	LL_TIM_GenerateEvent_UPDATE( TIMx );

	LL_TIM_SetCounter( TIMx, 0);

	LL_TIM_ClearFlag_BRK( TIMx );

  	while ((LL_TIM_IsActiveFlag_BRK2 (TIMx) == 1u) && (Brk2Timeout != 0u) )
  	{
		LL_TIM_ClearFlag_BRK2( TIMx );
		Brk2Timeout--;
  	}
	LL_TIM_EnableIT_BRK( TIMx );

	/* Enable PWM channel */
	LL_TIM_CC_EnableChannel( TIMx, TIMxCCER_MASK_CH123 );

  	/* TIM1_BRK_IRQn interrupt configuration */
  	HAL_NVIC_SetPriority(TIM1_BRK_TIM15_IRQn, 4, 1);
  	HAL_NVIC_EnableIRQ(TIM1_BRK_TIM15_IRQn);

  	HAL_NVIC_SetPriority(TIM1_UP_TIM16_IRQn, 0, 0);
  	HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn);

	LL_TIM_EnableCounter(TIMx);
}


void PWM_UpdateDuty(u32 duty1, u32 duty2, u32 duty3, u32 sample_point ) {
	TIM_TypeDef * TIMx = htim1.Instance;
	LL_TIM_DisableUpdateEvent(TIMx);
	LL_TIM_OC_SetCompareCH1(TIMx, duty1);
	LL_TIM_OC_SetCompareCH2(TIMx, duty2);
	LL_TIM_OC_SetCompareCH3(TIMx, duty3);
	LL_TIM_OC_SetCompareCH4(TIMx, sample_point);
	/* wait for a new PWM period */
	LL_TIM_ClearFlag_UPDATE( TIMx );
	LL_TIM_EnableUpdateEvent(TIMx);
	#if 0
	while ( LL_TIM_IsActiveFlag_UPDATE( TIMx ) == 0u )
	{}
	/* Clear Update Flag */
	LL_TIM_ClearFlag_UPDATE( TIMx );
	#endif
}


void PWM_Start(void) {
	TIM_TypeDef * TIMx = htim1.Instance;
	/* Main PWM Output Enable */
	TIMx->BDTR |= LL_TIM_OSSI_ENABLE;
	LL_TIM_EnableAllOutputs ( TIMx );
	if ( ( TIMx->CCER & TIMxCCER_MASK_CH123 ) != 0u )
	{
		LL_GPIO_SetOutputPin( PWM_EN_U_GPIO_Port, PWM_EN_U_Pin );
		LL_GPIO_SetOutputPin( PWM_EN_V_GPIO_Port, PWM_EN_V_Pin );
		LL_GPIO_SetOutputPin( PWM_EN_W_GPIO_Port, PWM_EN_W_Pin );
	}
	else
	{
		/* It is executed during calibration phase the EN signal shall stay off */
		LL_GPIO_ResetOutputPin( PWM_EN_U_GPIO_Port, PWM_EN_U_Pin );
		LL_GPIO_ResetOutputPin( PWM_EN_V_GPIO_Port, PWM_EN_V_Pin );
		LL_GPIO_ResetOutputPin( PWM_EN_W_GPIO_Port, PWM_EN_W_Pin );
	}
	/* Clear Update Flag */
	LL_TIM_ClearFlag_UPDATE( TIMx );
	/* Enable Update IRQ */
	LL_TIM_EnableIT_UPDATE( TIMx );

}

void PWM_Stop(void) {
	TIM_TypeDef * TIMx = htim1.Instance;
	
	/* Disable UPDATE ISR */
	LL_TIM_DisableIT_UPDATE( TIMx );
		
	/* Main PWM Output Disable */
	LL_TIM_DisableAllOutputs( TIMx );
	LL_GPIO_ResetOutputPin( PWM_EN_U_GPIO_Port, PWM_EN_U_Pin );
	LL_GPIO_ResetOutputPin( PWM_EN_V_GPIO_Port, PWM_EN_V_Pin );
	LL_GPIO_ResetOutputPin( PWM_EN_W_GPIO_Port, PWM_EN_W_Pin );
	/* wait for a new PWM period to flush last HF task */
	LL_TIM_ClearFlag_UPDATE( TIMx );
	while ( LL_TIM_IsActiveFlag_UPDATE( TIMx ) == 0u )
	{}
	/* Clear Update Flag */
	LL_TIM_ClearFlag_UPDATE( TIMx );

}

void PWM_TurnOnLowSides(void)
{
  TIM_TypeDef * TIMx = htim1.Instance;

  /* Clear Update Flag */
  LL_TIM_ClearFlag_UPDATE( TIMx );

  /*Turn on the three low side switches */
  LL_TIM_OC_SetCompareCH1( TIMx, 0u );
  LL_TIM_OC_SetCompareCH2( TIMx, 0u );
  LL_TIM_OC_SetCompareCH3( TIMx, 0u );

  /* Wait until next update */
  while ( LL_TIM_IsActiveFlag_UPDATE( TIMx ) == 0u )
  {}

  /* Main PWM Output Enable */
  LL_TIM_EnableAllOutputs( TIMx );

  LL_GPIO_SetOutputPin( PWM_EN_U_GPIO_Port, PWM_EN_U_Pin );
  LL_GPIO_SetOutputPin( PWM_EN_V_GPIO_Port, PWM_EN_V_Pin );
  LL_GPIO_SetOutputPin( PWM_EN_W_GPIO_Port, PWM_EN_W_Pin );

  return;
}


static void HAL_TIM1_Pin_Init(void)
{
  	GPIO_InitTypeDef GPIO_InitStruct = {0};
  	
	GPIO_InitStruct.Pin = PWM_UH_Pin;
  	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  	GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  	GPIO_InitStruct.Alternate = GPIO_AF6_TIM1;
  	HAL_GPIO_Init(PWM_UH_GPIO_Port, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = PWM_VH_Pin;
	HAL_GPIO_Init(PWM_VH_GPIO_Port, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = PWM_WH_Pin;
	HAL_GPIO_Init(PWM_WH_GPIO_Port, &GPIO_InitStruct);
}

