#include "main.h"
#include "input_capture.h"

extern TIM_HandleTypeDef htim3;

extern DMA_HandleTypeDef hdma_tim3_ch1;
extern DMA_HandleTypeDef hdma_tim3_ch2;

DShotStruct DShot;
PWMInputStruct PWMInput;

void DMA1_Channel1_IRQ (DMA_HandleTypeDef *hdma) {
	uint32_t flag_it = DMA1->ISR;
	uint32_t source_it = hdma->Instance->CCR;
	uint16_t value;
	uint16_t crc_d;
	uint16_t crc_p;
  	uint8_t i;
  	uint16_t dif_min = 0xFFFF;
  	uint8_t D;

	if ((0U != (flag_it & (DMA_FLAG_TC1 << (hdma->ChannelIndex & 0x1CU)))) && (0U != (source_it & DMA_IT_TC)))
	  {
	      if ((hdma->Instance->CCR & DMA_CCR_CIRC) == 0U)
	      {
	        // Disable the transfer complete and error interrupt
	        __HAL_DMA_DISABLE_IT(hdma, DMA_IT_TE | DMA_IT_TC);

	        // Change the DMA state
	        hdma->State = HAL_DMA_STATE_READY;
	      }
	      // Clear the transfer complete flag
	      __HAL_DMA_CLEAR_FLAG(hdma, (DMA_FLAG_TC1 << (hdma->ChannelIndex & 0x1CU)));

	      // Process Unlocked
	      __HAL_UNLOCK(hdma);



	  	TIM3->CNT = 0;

	  	// Check Packet
		for (i=0; i<16; i++) {
			DShot.Dif[i] = DShot.T1[i]-DShot.T2[i];
			if ((DShot.Type == DShot_Auto)) {
				if (DShot.Dif[i] < dif_min) {
					dif_min = DShot.Dif[i];
				}
			}
			if (DShot.Dif[i] > 64000) {
				DShot.Restart_Counter++;
				if (DShot.Restart_Counter < 100) {
					Dshot_DeInit();
					Dshot_Init(DShot.Type);
				} else {
					Dshot_DeInit();
				}
				return;
			}
		}

		// If DShot_Auto then autodetect 'TDif'
		if (DShot.Type == DShot_Auto) {
			DShot.TDif = dif_min + (dif_min / 2);
		}


	  	value = 0;
	  	crc_p = 0;
	  	for (i=0; i<16; i++) {
	  		D = (DShot.Dif[i] > DShot.TDif);

	  		// Calculate value
	  		if (i <= 11) {
	  			value = (value << 1) | D;
	  		} else {
	  			if (i > 11) { // Get CRC from packet
	  				crc_p = (crc_p << 1) | D;
	  			}
	  		}

	  	}

	  	// Calculate CRC
	  	crc_d = (value ^ (value >> 4) ^ (value >> 8)) & 0xF;
  		// Check CRC
  		if (crc_d == crc_p ) {
  			DShot.Value = (value >> 1) & 0x7FF;
  		}

	  }
}

void Dshot_Init(uint8_t type) {

	switch ( type )
	{
	    case DShot_150:
	    	DShot.TDif = DSHOT150_TIM_TRIG;
	    	DShot.Type = DShot_150;
	        break;
	    case DShot_300:
	    	DShot.TDif = DSHOT300_TIM_TRIG;
	    	DShot.Type = DShot_300;
	        break;
	    case DShot_600:
	    	DShot.TDif = DSHOT600_TIM_TRIG;
	    	DShot.Type = DShot_600;
	        break;
	    case DShot_Auto:
	    	DShot.TDif = 0;
	    	DShot.Type = DShot_Auto;
	        break;
	    default:
	    	DShot.TDif = 0;
	    	DShot.Type = DShot_Auto;
	    	break;

	}

	htim3.Init.Prescaler = 0;
	htim3.Init.Period = 20000;
	if (HAL_TIM_Base_Init(&htim3) != HAL_OK) {
		Error_Handler();
	}
	HAL_TIM_IC_Start_DMA(&htim3, TIM_CHANNEL_1, DShot.T1, 16);
	HAL_TIM_IC_Start_DMA(&htim3, TIM_CHANNEL_2, DShot.T2, 16);
}

void Dshot_DeInit(void) {
	HAL_TIM_IC_Stop_DMA(&htim3, TIM_CHANNEL_1);
	HAL_TIM_IC_Stop_DMA(&htim3, TIM_CHANNEL_2);
}

uint32_t DShot_Get(void) {
	return DShot.Value;
}


void PWMInput_Init(void) {
	PWMInput.Value = 0;

	htim3.Init.Prescaler = 64;
	htim3.Init.Period = 64000;
	if (HAL_TIM_Base_Init(&htim3) != HAL_OK) {
		Error_Handler();
	}

	HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2);
	HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
	__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_UPDATE);
}

void PWMInput_DeInit(void) {
	HAL_TIM_IC_Stop(&htim3, TIM_CHANNEL_2);
	HAL_TIM_IC_Stop(&htim3, TIM_CHANNEL_1);
}

uint32_t PWMInput_Get(void) {
	return PWMInput.Value;
}

void TIM3_IRQ(void) {
	if (__HAL_TIM_GET_IT_SOURCE(&htim3, TIM_IT_CC1) != RESET)
	{
	  if (__HAL_TIM_GET_FLAG(&htim3, TIM_FLAG_CC1) != RESET)
	  {
		  __HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_CC1);
	  }
	  __HAL_TIM_CLEAR_IT(&htim3, TIM_IT_CC1);
	}

	if (__HAL_TIM_GET_IT_SOURCE(&htim3, TIM_IT_CC2) != RESET)
	{
		if (__HAL_TIM_GET_FLAG(&htim3, TIM_FLAG_CC2) != RESET)
		{
			__HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_CC2);

			PWMInput.Value = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_1);

			TIM3->CNT = 0;
	        PWMInput.OverCaptureCounter = 0;
		}
		__HAL_TIM_CLEAR_IT(&htim3, TIM_IT_CC2);
	 }

	if (__HAL_TIM_GET_IT_SOURCE(&htim3, TIM_IT_UPDATE) != RESET)
	{
		__HAL_TIM_CLEAR_IT(&htim3, TIM_IT_UPDATE);

		if (PWMInput.OverCaptureCounter > 3) {
			PWMInput.Value = 0;
		} else {
			PWMInput.OverCaptureCounter++;
		}

	}
}
