/*
 * TPM.c
 *
 *  Created on: Nov 24, 2016
 *      Author: B50961
 */

#include "device.h"
#include "TPM.h"
#include "Interrupt.h"
#include "GPIO.h"

#define TPMSRC_SHIFT		24
#define TPMSRC_FIELD		3
#define TPMSRC_MCGFLLCLK	1
#define TPM_CMOD_SHIFT		3
#define TPM_CMOD_FIELD		3
#define TPM_CMOD_INCREMENT	1
#define TPM_TOIE_SHIFT		6
#define TPM_PS_SHIFT		0
#define TPM_PS_FIELD		7
#define TPM_PS_DIVIDE_1		0
#define TPM_PS_DIVIDE_64	6
#define TPM_PS_DIVIDE_128	7
#define TPM_TOF_SHIFT		7
//#define TPM_TOF_MASK		128 // in.h for LF


/*
 * Timer Module Interrupt Service Routine
 *
 * TPM0 Handler is in LF.c as for now only LF uses the TPM0 interrupt
 * For now TPM1 interrupt is not used
 */
/*
void TPM0_IRQHandler ()
{

}
*/

/*
 * Functions of TPM.c
 */

void Init_TPM0_General (void)
{
	// Enable clock gate to TPM0,1
	SIM_SCGC6 |= ((uint32_t)SIM_SCGC6_TPM0_MASK) | ((uint32_t)SIM_SCGC6_TPM1_MASK);

	// Select TPM counter clock to be MCGFLLCLK (we are in FEE mode with MCGFLLCLK=20MHz)
	SIM_SOPT2 &= ~(TPMSRC_FIELD<<TPMSRC_SHIFT);
	SIM_SOPT2 |= (TPMSRC_MCGFLLCLK<<TPMSRC_SHIFT);

	// Enable NVIC IRQ
	NVIC_EnableIRQ(TPM0_IRQn);

	// Set interrupt priority
	NVIC_SetPriority(TPM0_IRQn, TPM0_INTERRUPT_PRIORITY);
}

void Init_TPM1_General (void)
{
	// Enable clock gate to TPM0,1
	SIM_SCGC6 |= ((uint32_t)SIM_SCGC6_TPM0_MASK) | ((uint32_t)SIM_SCGC6_TPM1_MASK);

	// Select TPM counter clock to be MCGFLLCLK (we are in FEE mode with MCGFLLCLK=20MHz)
	SIM_SOPT2 &= ~(TPMSRC_FIELD<<TPMSRC_SHIFT);
	SIM_SOPT2 |= (TPMSRC_MCGFLLCLK<<TPMSRC_SHIFT);

	// Enable NVIC IRQ
	NVIC_EnableIRQ(TPM1_IRQn);

	// Set interrupt priority
	NVIC_SetPriority(TPM1_IRQn, TPM1_INTERRUPT_PRIORITY);
}



// Starts the timer
void Enable_TPM0(void)
{
  TPM0_SC &= ~(TPM_CMOD_FIELD<<TPM_CMOD_SHIFT);
  TPM0_SC |= (TPM_CMOD_INCREMENT<<TPM_CMOD_SHIFT); // CMOD = 01
}
void Enable_TPM1(void)
{
  TPM1_SC &= ~(TPM_CMOD_FIELD<<TPM_CMOD_SHIFT);
  TPM1_SC |= (TPM_CMOD_INCREMENT<<TPM_CMOD_SHIFT); // CMOD = 01
}

void Disable_TPM0(void)
{
  TPM0_SC &= ~(TPM_CMOD_FIELD<<TPM_CMOD_SHIFT);
}
void Disable_TPM1(void)
{
  TPM1_SC &= ~(TPM_CMOD_FIELD<<TPM_CMOD_SHIFT);
}

void Clear_TPM0(void)
{
  // Writing any value to COUNT clears the counter
  TPM0_CNT = 0x0000;
}
void Clear_TPM1(void)
{
  // Writing any value to COUNT clears the counter
  TPM1_CNT = 0x0000;
}

// Enable interrupt when timer overflow
void Enable_TOIE_TPM0(void)
{
  TPM0_SC |= (1<<TPM_TOIE_SHIFT);
}
void Enable_TOIE_TPM1(void)
{
  TPM1_SC |= (1<<TPM_TOIE_SHIFT);
}

// Enable interrupt when timer overflow
void Disable_TOIE_TPM0(void)
{
  TPM0_SC &= ~(1<<TPM_TOIE_SHIFT);
}
void Disable_TOIE_TPM1 (void)
{
  TPM1_SC &= ~(1<<TPM_TOIE_SHIFT);
}


void Init_Counter_TPM0(uint8_t prescaler , uint16_t modulo_value)
{
  TPM0_SC &= ~(TPM_CMOD_FIELD<<TPM_CMOD_SHIFT); // Disable counter in order to init the registers
  TPM0_SC &= ~(1<<TPM_TOIE_SHIFT); // Disable TOIE

  TPM0_CNT = 0; // Init counter

  // Prescaler
  TPM0_SC &= ~(TPM_PS_FIELD<<TPM_PS_SHIFT);
  TPM0_SC |= (prescaler<<TPM_PS_SHIFT);

  // Clear the overflow flag
  TPM0_SC |= TPM_TOF_MASK;

  TPM0_MOD &= ~(0xFFFF); // Only 16bits accessible
  TPM0_MOD |= (uint16_t)(modulo_value);
}

void Init_Counter_TPM1(uint8_t prescaler, uint16_t modulo_value)
{
  TPM1_SC &= ~(TPM_CMOD_FIELD<<TPM_CMOD_SHIFT); // Disable counter in order to init the registers
  TPM1_SC &= ~(1<<TPM_TOIE_SHIFT); // Disable TOIE

  TPM1_CNT = 0; // Init counter

  // Prescaler
  TPM1_SC &= ~(TPM_PS_FIELD<<TPM_PS_SHIFT);
  TPM1_SC |= (prescaler << TPM_PS_SHIFT);

  // Clear the overflow flag
  TPM1_SC |= TPM_TOF_MASK;

  TPM1_MOD &= ~(0xFFFF); // Only 16bits accessible
  TPM1_MOD |= (uint16_t)(modulo_value);
}


void Delay_TPM0_ms (uint8_t nb_ms)
{
	uint16_t modulo_value;

	TPM0_SC &= ~(TPM_CMOD_FIELD<<TPM_CMOD_SHIFT); // Disable counter in order to init the registers

    TPM0_CNT = 0; // Reset counter

    // Prescaler: divide by 128
    TPM0_SC &= ~(TPM_PS_FIELD<<TPM_PS_SHIFT);
    TPM0_SC |= (TPM_PS_DIVIDE_128<<TPM_PS_SHIFT);

    // Clear the overflow flag
    TPM0_SC |= TPM_TOF_MASK;
    // Make sure interrupt is disabled
    TPM0_SC &= ~(1<<TPM_TOIE_SHIFT);

    // Calculate modulo: clock source is 20MHz, PS is 128
    // => need to multiply by 156 to have a 1ms base time
    modulo_value = (uint16_t)(156*nb_ms);

    TPM0_MOD &= ~(0xFFFFFFFF);
    TPM0_MOD |= (uint16_t)(modulo_value);

    // Enable timer
    TPM0_SC &= ~(TPM_CMOD_FIELD<<TPM_CMOD_SHIFT);
    TPM0_SC |= (TPM_CMOD_INCREMENT<<TPM_CMOD_SHIFT);

    // Wait for overflow
    while (!(TPM0_SC&TPM_TOF_MASK));

    // Disable timer
    TPM0_SC &= ~(TPM_CMOD_FIELD<<TPM_CMOD_SHIFT);

    // Clear the overflow
    TPM0_SC |= TPM_TOF_MASK;

}

/*
 * To be noted for function below: initialization time is around 12µs
 * So giving 1µs as input will result in an execution time of around 13µs,
 * there will be a 12µs offset for all delays.
 */
void Delay_TPM0_us (uint8_t nb_us)
{
	uint16_t modulo_value;

	TPM0_SC &= ~(TPM_CMOD_FIELD<<TPM_CMOD_SHIFT); // Disable counter in order to init the registers

    TPM0_CNT = 0; // Reset counter

    // Prescaler: divide by 128
    TPM0_SC &= ~(TPM_PS_FIELD<<TPM_PS_SHIFT);
    TPM0_SC |= (TPM_PS_DIVIDE_1<<TPM_PS_SHIFT);

    // Clear the overflow flag
    TPM0_SC |= TPM_TOF_MASK;
    // Make sure interrupt is disabled
    TPM0_SC &= ~(1<<TPM_TOIE_SHIFT);

    // Calculate modulo: clock source is 20MHz, PS is 1
    // => need to multiply by 20 to have a 1us base time
    modulo_value = (uint16_t)(20*nb_us);

    TPM0_MOD &= ~(0xFFFFFFFF);
    TPM0_MOD |= (uint16_t)(modulo_value);

    // Enable timer
    TPM0_SC &= ~(TPM_CMOD_FIELD<<TPM_CMOD_SHIFT);
    TPM0_SC |= (TPM_CMOD_INCREMENT<<TPM_CMOD_SHIFT);

    // Wait for overflow
    while (!(TPM0_SC&TPM_TOF_MASK));

    // Disable timer
    TPM0_SC &= ~(TPM_CMOD_FIELD<<TPM_CMOD_SHIFT);

    // Clear the overflow
    TPM0_SC |= TPM_TOF_MASK;

}


