#include "Timer.h"
#include "stm32f1xx_ll_bus.h" // Pour l'activation des horloges
#include "stm32f1xx_ll_tim.h" // Pour les timers


/****************************************************************************
	*																	INTERRUPTIONS
	***************************************************************************/


// variable pointeur de fonction permettant de m�moriser le callback � appeler depuis
// le handler d'IT
void (*it_callback_TIM1)(void); 
void (*it_callback_TIM2)(void); 
void (*it_callback_TIM3)(void); 
void (*it_callback_TIM4)(void); 


void TIM1_UP_IRQHandler(void)
{
	// rabaisser le flag d'IT
	LL_TIM_ClearFlag_UPDATE(TIM1);
	(*it_callback_TIM1)();
}	

void TIM2_IRQHandler(void)
{
	// rabaisser le flag d'IT
	LL_TIM_ClearFlag_UPDATE(TIM2);
	(*it_callback_TIM2)();
}	

void TIM3_IRQHandler(void)
{
	// rabaisser le flag d'IT
	LL_TIM_ClearFlag_UPDATE(TIM3);
	(*it_callback_TIM3)();
}	

void TIM4_IRQHandler(void)
{
	// rabaisser le flag d'IT
	LL_TIM_ClearFlag_UPDATE(TIM4);
	(*it_callback_TIM4)();
}	


/**
	* @brief  Autorise les interruptions
	* @note   
	* @param  TIM_TypeDef Timer : indique le timer � utiliser par le chronom�tre, TIM1, TIM2, TIM3 ou TIM4
	* @retval None
	*/
void Timer_IT_enable(TIM_TypeDef * timer)
{
	LL_TIM_EnableIT_UPDATE(timer);
}


/**
	* @brief  Interdit les interruptions
	* @note   
	* @param  TIM_TypeDef Timer : indique le timer � utiliser par le chronom�tre, TIM1, TIM2, TIM3 ou TIM4
	* @retval None
	*/
void Timer_IT_disable(TIM_TypeDef * timer)
{
	LL_TIM_DisableIT_UPDATE(timer); 
}

/**
	* @brief  Configure le Timer consid�r� en interruption sur d�bordement.
	* @note   A ce stade, les interruptions ne sont pas valid�s (voir  MyTimer_IT_Enable )
	* @param  TIM_TypeDef Timer : indique le timer � utiliser par le chronom�tre, TIM1, TIM2, TIM3 ou TIM4
	* 				void (*IT_function) (void) : nom (adresse) de la fonction � lancer sur interruption
	*         int Prio : priorit� associ�e � l'interruption
	* @retval None
	*/
void Timer_IT_conf(TIM_TypeDef * timer, void (*it_callback) (void), int prio)
{
	// affectation de la fonction
	if (timer == TIM1) it_callback_TIM1 = it_callback;
	else if (timer == TIM2)	it_callback_TIM2 = it_callback;
	else if (timer == TIM3)	it_callback_TIM3 = it_callback;
	else  it_callback_TIM4 = it_callback;

	
	// Blocage IT (il faudra la d�bloquer voir fct suivante)
	LL_TIM_DisableIT_UPDATE(timer);
	
	// validation du canal NVIC
	IRQn_Type TIM_irq;
	
	if (timer == TIM1) TIM_irq = TIM1_UP_IRQn;
	else if (timer == TIM2)	TIM_irq = TIM2_IRQn;
	else if (timer == TIM3)	TIM_irq = TIM3_IRQn;
	else 	TIM_irq = TIM4_IRQn;
	
	NVIC_SetPriority(TIM_irq, prio);
	NVIC_EnableIRQ(TIM_irq);

	
}

/****************************************************************************
	*																	TIMER
	***************************************************************************/

/**
	* @brief  D�marre le timer consid�r� et active les interruptions
	* @note   
	* @param  TIM_TypeDef Timer : indique le timer � utiliser par le chronom�tre, TIM1, TIM2, TIM3 ou TIM4
	* @retval None
	*/
void Timer_start(TIM_TypeDef * timer)
{
	LL_TIM_EnableCounter(timer);
}

/**
	* @brief  Arr�t le timer consid�r�  et d�sactive les interruptions
	* @note   
	* @param  TIM_TypeDef Timer : indique le timer � utiliser par le chronom�tre, TIM1, TIM2, TIM3 ou TIM4
	* @retval None
	*/
void Timer_stop(TIM_TypeDef * timer)
{
	LL_TIM_DisableCounter(timer);
}

/**
	* @brief  Active l'horloge et r�gle l'ARR et le PSC du timer vis�.
	* @note   Fonction � lancer avant toute autre. Le timer n'est pas encore lanc� (voir Timer_start)
	* @param  TIM_TypeDef Timer : indique le timer � utiliser par le chronom�tre, TIM1, TIM2, TIM3 ou TIM4
	* 				int Arr   : valeur � placer dans ARR
	*					int Psc   : valeur � placer dans PSC
	* @retval None
	*/
void Timer_conf(TIM_TypeDef * timer, int arr, int psc)
{
	LL_TIM_InitTypeDef init_struct;
	
	// Validation horloge locale
	if (timer == TIM1) LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
	else if (timer == TIM2) LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
	else if (timer == TIM3) LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3);
	else  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM4);
	
	// chargement structure Arr, Psc, Up Count
	init_struct.Autoreload = arr;
	init_struct.Prescaler = psc;
	init_struct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
	init_struct.CounterMode = LL_TIM_COUNTERMODE_UP;
	init_struct.RepetitionCounter = 0;
	
	LL_TIM_Init(timer,&init_struct);

	// Blocage interruptions
	Timer_IT_disable(timer);
	
	// Blocage Timer
	Timer_stop(timer);
}

/****************************************************************************
	*																	PWM INPUT
	***************************************************************************/
void PWMi_conf(TIM_TypeDef * TIMx, int channel){
	
	//Periode à recuperer dans TIMx_CCR1, duty cycle dans TIMx_CCR2. Seul les 2 premiers channels peuvent être utilisés (cf 315).
	
	// Validation horloge locale
	if (TIMx == TIM1) LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
	else if (TIMx == TIM2) LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
	else if (TIMx == TIM3) LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3);
	else  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM4);
	
	
	if (channel == 1) {
		//
		TIMx -> CCMR1 |= TIM_CCMR1_CC1S_0;
		TIMx -> CCMR1 |= TIM_CCMR1_CC2S_1;

		//TIM_CCMR1_IC1F_0; Potentiellement utile, à voir plus tard

		//On met le channel principal en rising edge, le secondaire en falling edge
		TIMx -> CCER &= ~TIM_CCER_CC1P; 	
		TIMx -> CCER |= TIM_CCER_CC2P;
		
		TIMx -> SMCR |= TIM_SMCR_TS_0 | TIM_SMCR_TS_2; //101
		TIMx -> SMCR |= TIM_SMCR_SMS_2; //100
	}
	
	else {
		TIMx -> CCMR1 |= TIM_CCMR1_CC1S_1;
		TIMx -> CCMR1 |= TIM_CCMR1_CC2S_0;
		
		TIMx -> CCER |= TIM_CCER_CC1P; 	
		TIMx -> CCER &= ~TIM_CCER_CC2P;

		TIMx -> SMCR |= TIM_SMCR_TS_1 | TIM_SMCR_TS_2; //110
		TIMx -> SMCR |= TIM_SMCR_SMS_2; //100
	}
	
	//On met les prescalers à 0, on veut compter chaque transition
	TIMx -> CCMR1 &= ~TIM_CCMR1_IC1PSC;
	TIMx -> CCMR1 &= ~TIM_CCMR1_IC2PSC;
	
	TIMx -> CCER |= TIM_CCER_CC1E | TIM_CCER_CC2E;
	
	//TIMx -> DIER |= TIM_DIER_CC1IE; gestion des interrupts, probablement pas utile
  //TIM_DIER_CC1DE_Pos; Probablement pas utile
}

int PWMi_getPeriod(TIM_TypeDef * TIMx) {
	
	return TIMx -> CCR1;
}

int PWMi_getDutyCycle(TIM_TypeDef * TIMx) {
	
	return TIMx -> CCR2;
}

/****************************************************************************
	*																	PWM OUTPUT
	***************************************************************************/

int getArrFromFreq(float freq)
{
	return (72000 / freq) - 1;
}

void Timer_pwmo_conf(TIM_TypeDef * timer, int channel, float freq, float dutyCycle)
{
	const int arr = getArrFromFreq(freq);
	Timer_conf(timer, arr, 1000);
	LL_TIM_OC_InitTypeDef init_struct;
	LL_TIM_OC_StructInit(&init_struct);
	
	init_struct.OCMode = LL_TIM_OCMODE_PWM1;
	init_struct.OCState = LL_TIM_OCSTATE_ENABLE;
	init_struct.CompareValue= dutyCycle * arr;
	
	LL_TIM_OC_Init(timer, channel, &init_struct);
}

void Timer_pwmo_setFreq(TIM_TypeDef * timer, float freq)
{
	const int arr = getArrFromFreq(freq);
	LL_TIM_SetAutoReload(timer, arr);
}

void Timer_pwmo_setDutyCycle(TIM_TypeDef * timer, int channel, float dutyCycle)
{
	const int arr = LL_TIM_GetAutoReload(timer);
	int compare = dutyCycle * arr;
	if (channel == LL_TIM_CHANNEL_CH1) LL_TIM_OC_SetCompareCH1(timer, compare);
	else if (channel == LL_TIM_CHANNEL_CH2) LL_TIM_OC_SetCompareCH2(timer, compare);
	else if (channel == LL_TIM_CHANNEL_CH3) LL_TIM_OC_SetCompareCH3(timer, compare);
	else  LL_TIM_OC_SetCompareCH4(timer, compare);
}

float Timer_pwmo_getDutyCycle(TIM_TypeDef * timer, int channel)
{
	int compare = 0;
	const int arr = LL_TIM_GetAutoReload(timer);
	if (channel == LL_TIM_CHANNEL_CH1) compare = LL_TIM_OC_GetCompareCH1(timer);
	else if (channel == LL_TIM_CHANNEL_CH2) compare = LL_TIM_OC_GetCompareCH2(timer);
	else if (channel == LL_TIM_CHANNEL_CH3) compare = LL_TIM_OC_GetCompareCH3(timer);
	else  compare = LL_TIM_OC_GetCompareCH4(timer);
	return ((float) compare) / ((float) arr);
}

/****************************************************************************
	*																ENCODER
	***************************************************************************/

void Timer_encoder_conf(TIM_TypeDef * timer)
{
	Timer_conf(timer, 359, 0);
	LL_TIM_ENCODER_InitTypeDef init_struct;
	LL_TIM_ENCODER_StructInit(&init_struct);
	
	init_struct.EncoderMode = LL_TIM_ENCODERMODE_X2_TI1;
	
	LL_TIM_ENCODER_Init(timer, &init_struct);
}

int Timer_encoder_getAngle(TIM_TypeDef * timer)
{
	return LL_TIM_GetCounter(timer);
}

enum CounterDirection Timer_encoder_getDirection(TIM_TypeDef * timer)
{
	const int dir = LL_TIM_GetDirection(timer);
	if (dir == LL_TIM_COUNTERDIRECTION_UP)
		return CLOCKWISE;
	else
		return COUNTER_CLOCKWISE;
}
