/*
 * LF.c
 *
 *  Created on: Dec 11, 2016
 *      Author: B50961
 */

#include "device.h"
#include "util.h"
#include "LF.h"
#include "TPM.h"


#define OUT1_PCR          PORTB_PCR0
#define OUT2_PCR          PORTB_PCR1

#define DEPORTED_LF_ALT_FUNCTION  	    3  // valid for PTB0 and PTB1
#define PRESCALER_DIV_16				4
#define PRESCALER_DIV_64				6
#define LF_TPM1_MODULO_VALUE_125kHz     10 // Adjusted to have exactly 125kHz clock; this is for TPM1
#define TPM_PWM_50_CV_VALUE             6 // With 6 we have exactly a 50% PWM

#define LF_TPM0_MODULO_VALUE_BR3906     42 // For baud rate = 3906; this is for TPM0

#define MSB_MASK        (1 << 5)
#define MSA_MASK        (1 << 4)
#define ELSB_MASK       (1 << 3)
#define ELSA_MASK       (1 << 2)

#define CARRIER_ON	Enable_TPM1();
#define CARRIER_OFF	Disable_TPM1();


/*
 * Private functions
 */
void Init_Tx_State_Machine(void);
void Update_Output_Pins(void);
void Write_Data(uint8_t Current_Half_Byte_Transmitted);
void Write_One (void);
void Write_Zero (void);

/*
 * Enum
 */
enum State_Machine_Tx {preamble_Tx , pattern_Tx , data_Tx , eof_Tx , interframe_Tx};

/*
 * Private global variables
 */
enum State_Machine_Tx current_Tx_state;
uint16_t Nb_Bytes_Transmitted;
uint8_t Current_Half_Byte_Transmitted;
uint8_t Current_Bit_Transmitted;
bool_t go_to_next_half_byte;
uint8_t current_half_byte_nb;
uint8_t preamble_piece_nb;
uint8_t pattern_piece_nb;
uint8_t eof_quarter_nb;
uint8_t Current_half_bit;
uint8_t last_bits_to_transmit;

/*
 * Public global variables
 */
uint8_t Tx_Buffer[50];
uint16_t Nb_Bytes_To_Transmit;
volatile uint8_t LF_Tx_Ended;




/*
 * Public function
 */
void LF_Init_General (void)
{

 /* Below will initialize TPM1 */

  // Enable clock
  Init_TPM1_General();

  // Configure prescaler and modulo
  Init_Counter_TPM1(PRESCALER_DIV_16, LF_TPM1_MODULO_VALUE_125kHz);

  // Configure PWM for channels 0 and 1
  TPM1_SC &= ~(1 << 5); // Disable Center-Aligned PWM
  TPM1_C0SC = 0x00; // Disable channel 0 to do the init below
  TPM1_C1SC = 0x00; // Disable channel 1 to do the init below
  // Channels 0 and 1 must be PWM edged-aligned complemented =>
  // Channel 0 : MSB=1, MSA=0, ELSB=1 and ELSA=0
  // Channel 1 : MSB=1, MSA=0, ELSB=0 and ELSA=1
  TPM1_C0SC |= (MSB_MASK | ELSB_MASK);
  TPM1_C1SC |= (MSB_MASK | ELSA_MASK);
  TPM1_C0V = TPM_PWM_50_CV_VALUE; // Duty cycle
  TPM1_C1V = TPM_PWM_50_CV_VALUE; // Duty cycle

  /* Initialize PTB0 and PTB1 to be connected to TPM1 */
  OUT1_PCR &= ~PORT_PCR_MUX_MASK;
  OUT1_PCR |= PORT_PCR_MUX(DEPORTED_LF_ALT_FUNCTION);
  OUT2_PCR &= ~PORT_PCR_MUX_MASK;
  OUT2_PCR |= PORT_PCR_MUX(DEPORTED_LF_ALT_FUNCTION);

  return;
}

/*
 * Public function
 */
void Init_Tx_LF_PREMO (void)
{

  Init_Counter_TPM0(PRESCALER_DIV_64, LF_TPM0_MODULO_VALUE_BR3906);
  Enable_TOIE_TPM0();
  Init_Tx_State_Machine();

  return;
}

/*
 * Public function
 */
void LF_WaitEndTx (void)
{
	while (LF_Tx_Ended == 0);
	Disable_TOIE_TPM0();
	Disable_TPM0();
}

/*
 * Public function
 */
void Start_Tx(void)
{
  current_Tx_state = preamble_Tx;
  Enable_TPM0();
}


/*
 * Below are private functions
 */
void Init_Tx_State_Machine(void)
{
  Current_Bit_Transmitted = 0x00;
  Current_half_bit = 0x00;
  go_to_next_half_byte = FALSE;
  current_half_byte_nb = 0x00;
  Nb_Bytes_Transmitted = 0;
  Current_Half_Byte_Transmitted = (uint8_t)(Tx_Buffer[Nb_Bytes_Transmitted] >> 4);
  preamble_piece_nb = 0x00;
  pattern_piece_nb = 0x00;
  eof_quarter_nb = 0x00;
  current_Tx_state = interframe_Tx;
  last_bits_to_transmit = 0x00;

  LF_Tx_Ended = 0x00;

  CARRIER_OFF;

}


/*
 * Below is used for LF only
 */
void TPM0_IRQHandler ()
{
	// Clear the flag
	TPM0_SC |= TPM_TOF_MASK;


	  switch (current_Tx_state) {

	  case interframe_Tx:
	    // Do nothing
	    break;

	  case preamble_Tx:
	    if (preamble_piece_nb == 31)
	    {
	      // Low state
	      CARRIER_OFF;

	      preamble_piece_nb = 0x00;
	      current_Tx_state = pattern_Tx;

	    } else {

	      if ((preamble_piece_nb & 0x01) == 0x00)
	      {
	        // High state
	        CARRIER_ON;
	      }
	      else
	      {
	        // Low state
	        CARRIER_OFF;
	      }
	      preamble_piece_nb ++;
	    }

	    break;

	  case pattern_Tx: // Implements 9T pattern

	    if (pattern_piece_nb < 0x03)
	    {
	      // High state
	      CARRIER_ON;
	      pattern_piece_nb ++;

	    } else if (pattern_piece_nb < 0x06) {

	      // Low state
	      CARRIER_OFF;
	      pattern_piece_nb ++;

	    } else if (pattern_piece_nb < 0x07) {

	      // High state
	      CARRIER_ON;
	      pattern_piece_nb ++;

	    } else if (pattern_piece_nb < 0x08) {

	      // Low state
	      CARRIER_OFF;
	      pattern_piece_nb ++;

	    } else if (pattern_piece_nb < 0x0A) {

	      // High state
	      CARRIER_ON;
	      pattern_piece_nb ++;

	    } else if (pattern_piece_nb < 0x0C) {

	      // Low state
	      CARRIER_OFF;
	      pattern_piece_nb ++;

	    } else if (pattern_piece_nb < 0x0E) {

	      // High state
	      CARRIER_ON;
	      pattern_piece_nb ++;

	    } else if (pattern_piece_nb < 0x10) {

	      // Low state
	      CARRIER_OFF;
	      pattern_piece_nb ++;

	    } else if (pattern_piece_nb < 0x11) {

	      // High state
	      CARRIER_ON;
	      pattern_piece_nb ++;

	    } else if (pattern_piece_nb < 0x12) {

	      // Low state
	      CARRIER_OFF;
	      pattern_piece_nb = 0x00;
	      current_Tx_state = data_Tx;
	    }

	    break;

	  case data_Tx:

	    if (Nb_Bytes_Transmitted == (Nb_Bytes_To_Transmit - 1))
	    {
	      if (last_bits_to_transmit == 15)
	      {
	        current_Tx_state = eof_Tx;
	        // High state
	        CARRIER_ON;

	        Nb_Bytes_Transmitted = 0;
	      } else {
	        last_bits_to_transmit ++;
	        Update_Output_Pins();
	      }
	    } else {
	      Update_Output_Pins();
	    }

	    break;

	  case eof_Tx:
	    if (eof_quarter_nb == 3)
	    {
	      Init_Tx_State_Machine();
	      eof_quarter_nb = 0x00;
	      LF_Tx_Ended = 1; // Set after calling Init_Tx_State_Machine();
	      // Low state
	      CARRIER_OFF;

	    } else {
	      // High state
	      CARRIER_ON;
	      eof_quarter_nb ++;
	    }
	    break;
	  }
}


void Write_Zero (void)
{
  if (Current_half_bit == 0x00)
  {
    // High state
    CARRIER_ON;

    Current_half_bit ++;
  } else {
    // Low state
    CARRIER_OFF;

    Current_half_bit = 0x00;

    if (Current_Bit_Transmitted == 0x03)
    {
      Current_Bit_Transmitted = 0x00;
      go_to_next_half_byte = TRUE;
    } else {
      Current_Bit_Transmitted ++;
    }
  }
}

void Write_One (void)
{
  if (Current_half_bit == 0x00)
  {
    // Low state
    CARRIER_OFF;

    Current_half_bit ++;
  } else {
    // High state
    CARRIER_ON;

    Current_half_bit = 0x00;

     if (Current_Bit_Transmitted == 0x03)
    {
      Current_Bit_Transmitted = 0x00;
      go_to_next_half_byte = TRUE;
    } else {
      Current_Bit_Transmitted ++;
    }
  }
}



void Update_Output_Pins(void)
{

  if (go_to_next_half_byte == TRUE) {
    if (current_half_byte_nb == 0x00) {
      Current_Half_Byte_Transmitted = (uint8_t)(Tx_Buffer[Nb_Bytes_Transmitted] & 0x0F);
      current_half_byte_nb ++;
    } else {
      Nb_Bytes_Transmitted ++;
      Current_Half_Byte_Transmitted = (uint8_t)(Tx_Buffer[Nb_Bytes_Transmitted] >> 4);
      current_half_byte_nb = 0x00;
    }
      go_to_next_half_byte = FALSE;

  } else {

    // Do nothing

  }

   Write_Data(Current_Half_Byte_Transmitted);

}

void Write_Data(uint8_t Current_Half_Byte_Transmitted)
{
  unsigned char hex_word[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

    switch (hex_word[Current_Half_Byte_Transmitted])
    {
      case '0':
        switch (Current_Bit_Transmitted) {
          case 0x00: Write_Zero();
          break;
          case 0x01: Write_Zero();
          break;
          case 0x02: Write_Zero();
          break;
          case 0x03: Write_Zero();
          break;
        }

      break;
      case '1':
        switch (Current_Bit_Transmitted) {
          case 0x00: Write_Zero();
          break;
          case 0x01: Write_Zero();
          break;
          case 0x02: Write_Zero();
          break;
          case 0x03: Write_One();
          break;
        }
      break;
      case '2':
        switch (Current_Bit_Transmitted) {
          case 0x00: Write_Zero();
          break;
          case 0x01: Write_Zero();
          break;
          case 0x02: Write_One();
          break;
          case 0x03: Write_Zero();
          break;
        }
      break;
      case '3':
        switch (Current_Bit_Transmitted) {
          case 0x00: Write_Zero();
          break;
          case 0x01: Write_Zero();
          break;
          case 0x02: Write_One();
          break;
          case 0x03: Write_One();
          break;
        }
      break;
      case '4':
        switch (Current_Bit_Transmitted) {
          case 0x00: Write_Zero();
          break;
          case 0x01: Write_One();
          break;
          case 0x02: Write_Zero();
          break;
          case 0x03: Write_Zero();
          break;
        }
      break;
      case '5':
        switch (Current_Bit_Transmitted) {
          case 0x00: Write_Zero();
          break;
          case 0x01: Write_One();
          break;
          case 0x02: Write_Zero();
          break;
          case 0x03: Write_One();
          break;
        }
      break;
      case '6':
       switch (Current_Bit_Transmitted) {
          case 0x00: Write_Zero();
          break;
          case 0x01: Write_One();
          break;
          case 0x02: Write_One();
          break;
          case 0x03: Write_Zero();
          break;
        }
      break;
      case '7':
        switch (Current_Bit_Transmitted) {
          case 0x00: Write_Zero();
          break;
          case 0x01: Write_One();
          break;
          case 0x02: Write_One();
          break;
          case 0x03: Write_One();
          break;
        }
      break;
      case '8':
       switch (Current_Bit_Transmitted) {
          case 0x00: Write_One();
          break;
          case 0x01: Write_Zero();
          break;
          case 0x02: Write_Zero();
          break;
          case 0x03: Write_Zero();
          break;
        }
      break;
      case '9':
        switch (Current_Bit_Transmitted) {
          case 0x00: Write_One();
          break;
          case 0x01: Write_Zero();
          break;
          case 0x02: Write_Zero();
          break;
          case 0x03: Write_One();
          break;
        }
      break;
      case 'A':
        switch (Current_Bit_Transmitted) {
          case 0x00: Write_One();
          break;
          case 0x01: Write_Zero();
          break;
          case 0x02: Write_One();
          break;
          case 0x03: Write_Zero();
          break;
        }
      break;
      case 'B':
        switch (Current_Bit_Transmitted) {
          case 0x00: Write_One();
          break;
          case 0x01: Write_Zero();
          break;
          case 0x02: Write_One();
          break;
          case 0x03: Write_One();
          break;
        }
      break;
      case 'C':
       switch (Current_Bit_Transmitted) {
          case 0x00: Write_One();
          break;
          case 0x01: Write_One();
          break;
          case 0x02: Write_Zero();
          break;
          case 0x03: Write_Zero();
          break;
        }
      break;
      case 'D':
        switch (Current_Bit_Transmitted) {
          case 0x00: Write_One();
          break;
          case 0x01: Write_One();
          break;
          case 0x02: Write_Zero();
          break;
          case 0x03: Write_One();
          break;
        }
      break;
      case 'E':
        switch (Current_Bit_Transmitted) {
          case 0x00: Write_One();
          break;
          case 0x01: Write_One();
          break;
          case 0x02: Write_One();
          break;
          case 0x03: Write_Zero();
         break;
        }
      break;
      case 'F':
        switch (Current_Bit_Transmitted) {
          case 0x00: Write_One();
          break;
          case 0x01: Write_One();
          break;
          case 0x02: Write_One();
          break;
          case 0x03: Write_One();
          break;
        }
      break;
    }

}

