/***********************************************************************************************
* Company: Microsemi Corporation
*
* File: fan_control.c
* File history:
*      Revision: 1.0 Date: July 29, 2011
*
* Description: This code does the following functionalities.
*              1. Configure the PWM in the fabric.
*              2. Accepts the Fan Configuration from GUI
*              3. It configures the timer1. upon the occurrence of the timer1 interrupt it selects
*                 different parameters
*              4. Does the closed/open loop control
*              Please see the Software flow chart in Application note for more details
*
* Author: Pavan Marisetti
*         pavan.marisetti@microsemi.com
*         Corporate Applications Engineering
*
************************************************************************************************/

/***********************************************************************************************/
 /* Including Directories */
/***********************************************************************************************/
#include "../CMSIS/a2fxxxm3.h"
#include "../drivers/mss_uart/mss_uart.h"
#include "../drivers/mss_gpio/mss_gpio.h"
#include "../drivers/mss_timer/mss_timer.h"
#include "fan_control.h"

/***********************************************************************************************/
 /* Global variables */
/***********************************************************************************************/
uint32_t g_no_of_poles;
uint8_t  g_tach_recev;
uint32_t g_tach_no = 0x00000000;
uint32_t g_max_rpm;
uint32_t g_pwm_freq = 2;
uint32_t g_min_pwm_duty_cy;
uint32_t g_pwm_neg_edge_cutoff;
uint8_t  g_read_result = 0;
uint32_t g_tach_reading = 0;
uint32_t g_measured_rpm1 = 0x00000000;
uint32_t g_measured_rpm2 = 0x00000000;
uint32_t g_measured_rpm3 = 0x00000000;
uint32_t g_measured_rpm4 = 0x00000000;
uint32_t g_pwm_neg_edge = NEG_EDGE;
uint8_t  g_fan_type;
uint8_t rx_size = 0;
uint8_t rx_char[10];
uint32_t user_rpm;
uint8_t *rx_char_ptr;
extern uint32_t g_FrequencyFPGA;


/* Fan States */
typedef enum fan_states
{
	fan_stop,
	fan_stopped,
	fan_full_speed_start,
	fan_full_speed_started,
	fan_open_loop_start,
	fan_open_loop_started,
	fan_closed_loop_start,
	fan_closed_loop_started,
	fan_display_staus
} fan_states_t;

/* Fan Status */

typedef struct fan_state_status
{
	fan_states_t fan_status;
}fan_state_status_t;

static fan_state_status_t g_fan_state_status;
size_t
UART_Polled_Rx
(
    mss_uart_instance_t * this_uart,
    uint8_t * rx_buff,
    size_t buff_size
)
{
    size_t rx_size = 0U;
    while( rx_size < buff_size )
    {
       while ( this_uart->hw_reg_bit->LSR_DR != 0U  )
       {
           rx_buff[rx_size] = this_uart->hw_reg->RBR;
           ++rx_size;
       }
    }

    return rx_size;
}
void timer1_config(void)
{
  //MSS_TIM1_load_immediate(0x0430A720);
	MSS_TIM1_load_immediate(0x0030A720);
  MSS_TIM1_start();
  MSS_TIM1_clear_irq();
  MSS_TIM1_enable_irq();
}

void PWM_init_fan()
{

  /*PWM Configurations */
  if((g_FrequencyFPGA/g_pwm_freq)>=  VAL_255_32_bit)
  {
     /* Period value of PWM */
     (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0x04)))= VAL_255_32_bit; /* period - 5000 - 2KHz PWM Freq*/
     /* Prescale value of PWM */
     (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0x00)))= ((g_FrequencyFPGA/(g_pwm_freq * (VAL_255_32_bit + 1))) - 1);
  }
  else
  {
	  /* Period value of PWM */
	  (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0x04)))= ((g_FrequencyFPGA/g_pwm_freq) - 1); /* period - 5000 - 2KHz PWM Freq*/
	  /* Prescale value of PWM */
	  (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0x00)))= VAL_0_32_bit;
  }
  /* Positive edge PWM1 */
  (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0x10)))= VAL_0_32_bit;

  /* Negative edge PWM1 */
  (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0x14)))=VAL_255_32_bit;
  /* Sync update PWM */
  (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0xE4)))= VAL_1_32_bit;
  g_pwm_neg_edge_cutoff = (g_min_pwm_duty_cy * (VAL_255_32_bit))/100;
}

void PWM_enable_fan()
{
  /* Enable PWM */
  (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0x08)))= VAL_1_32_bit;
}
void PWM_disable_fan()
{
  /* Disable PWM */
  (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0x08)))= VAL_0_32_bit;
}
/* Function to check any key press */
void read_user_input (uint8_t *rx_char_ptr)
{
    uint8_t recv_char,size, index = 0;
    for(index = 0; index < 10; index++)
    {
    	rx_char[index] = '0';
    }
    index = 0;
    while(recv_char != '\n')
    {
      size = MSS_UART_get_rx(&g_mss_uart0, &recv_char,1);
	  if(size > 0)
	  {
		  rx_char_ptr[index] = recv_char;
		  index++;
	  }
	}

}
#if 0
void delay ( volatile uint32_t n)
{
    while(n!=0)
    {
        n--;
    }
}
#endif
__attribute__((__interrupt__)) void Timer1_IRQHandler(void)
{
    uint32_t temp;
    if(g_read_result == 0)
    {
      if((g_tach_no - 1) == 0)
      {
  	    g_measured_rpm1 = 0;
      }
      else if((g_tach_no - 1) == 1)
	  {
	    g_measured_rpm2 = 0;
      }
      else if((g_tach_no - 1) == 2)
	  {
	  	g_measured_rpm3 = 0;
      }
      else if((g_tach_no - 1) == 3)
	 {
	  	g_measured_rpm4 = 0;
      }

    }

    if(g_tach_no == (0x00000004))
    {
  	  g_tach_no = (0x00000000);
    }

  if(g_fan_type == '3')
  {
    while(1)
    {
      /* Enabling Tacho register map*/
      (*((uint32_t volatile *)(FABRIC_TACH_BASE_ADDR + 0xF0)))= (g_tach_no << 1);
      temp = (*((uint32_t volatile *)(FABRIC_TACH_BASE_ADDR + 0xF4))) & 0x03FFFFFF; /* Reading the TACH reading */
      temp  = temp >> 24;
      if(temp == g_tach_no)
      {
    	  (*((uint32_t volatile *)(FABRIC_TACH_BASE_ADDR + 0xF0)))= ((0x00000009) | (g_tach_no << 1));
    	  break;
      }
    }
  }
  else if(g_fan_type == '4')
  {
	    while(1)
	    {
	      /* Enabling Tacho register map*/
	      (*((uint32_t volatile *)(FABRIC_TACH_BASE_ADDR + 0xF0)))= (g_tach_no << 1);
	      temp = (*((uint32_t volatile *)(FABRIC_TACH_BASE_ADDR + 0xF4))) & 0x03FFFFFF; /* Reading the TACH reading */
	      temp  = temp >> 24;
	      if(temp == g_tach_no)
	      {
	    	  (*((uint32_t volatile *)(FABRIC_TACH_BASE_ADDR + 0xF0)))= ((0x00000001) | (g_tach_no << 1));
	    	  break;
	      }
	    }
  }
  else
  {
      /* Enabling Tacho register map*/
      (*((uint32_t volatile *)(FABRIC_TACH_BASE_ADDR + 0xF0)))= (0x00000000);
   }

  g_tach_no = g_tach_no + 1;
  g_read_result = 0;
  /* Clear TIM1 interrupt */
  MSS_TIM1_clear_irq();
  MSS_TIM1_stop();
  timer1_config();
}

void fan_closed_loop_control(void)
{
  int16_t adjust_rpm;
  g_tach_recev = g_tach_reading >> 24;

  if((((g_tach_reading & 0x00FFFFFF)/TACHO_PULSE_COUNT)*(g_no_of_poles/2)) != 0)
  {
    if(g_tach_recev == 0)
    {
	  g_measured_rpm1 = (PCLK_FREQ * 60)/((((g_tach_reading & 0x00FFFFFF)/TACHO_PULSE_COUNT)*(g_no_of_poles/2)) * NO_OF_POLES);
    }
    if(g_tach_recev == 1)
    {
      g_measured_rpm2 = (PCLK_FREQ * 60)/((((g_tach_reading & 0x00FFFFFF)/TACHO_PULSE_COUNT)*(g_no_of_poles/2)) * NO_OF_POLES);
    }
    if(g_tach_recev == 2)
    {
      g_measured_rpm3 = (PCLK_FREQ * 60)/((((g_tach_reading & 0x00FFFFFF)/TACHO_PULSE_COUNT)*(g_no_of_poles/2)) * NO_OF_POLES);
    }
    if(g_tach_recev == 3)
    {
      g_measured_rpm4 = (PCLK_FREQ * 60)/((((g_tach_reading & 0x00FFFFFF)/TACHO_PULSE_COUNT)*(g_no_of_poles/2)) * NO_OF_POLES);
    }
  }

  if((g_tach_reading >> 24) == 0)
  {
	adjust_rpm = g_measured_rpm1 - user_rpm;
	if (adjust_rpm > 0 )
	{
	   if((adjust_rpm >= FIFTY_PERSENT_OF_MAX_RMP)&& (g_pwm_neg_edge > 0x0000000F))
       {
		 g_pwm_neg_edge = g_pwm_neg_edge - 0x0000000F;
	   }
	   else if((adjust_rpm >= TWENTYFIVE_PERSENT_OF_MAX_RMP)&& (g_pwm_neg_edge > 0x00000007))
	   {
		 g_pwm_neg_edge = g_pwm_neg_edge - 0x00000007;
	   }
       else if((adjust_rpm >= TEN_PERSENT_OF_MAX_RMP)&& (g_pwm_neg_edge > 0x00000003))
       {
		 g_pwm_neg_edge = g_pwm_neg_edge -  0x00000003;
	   }
       else if((adjust_rpm >= FIVE_PERSENT_OF_MAX_RMP)&& (g_pwm_neg_edge > 0x00000002))
	   {
		 g_pwm_neg_edge = g_pwm_neg_edge - 0x00000002;
	   }
	   else if((adjust_rpm >= ONE_PERSENT_OF_MAX_RMP)&& (g_pwm_neg_edge > 0x00000001))
	   {
   	   	 g_pwm_neg_edge = g_pwm_neg_edge - 0x00000001;
	   }
       /* Negative edge PWM1 */
       (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0x14)))= g_pwm_neg_edge;
       /* Sync update PWM */
       (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0xE4)))= 0x00000001;
	}
	else if (adjust_rpm < 0 )
    {
       adjust_rpm = adjust_rpm * (-1);
       if((adjust_rpm >= FIFTY_PERSENT_OF_MAX_RMP)&& (g_pwm_neg_edge < 0x000000F1))
       {
          g_pwm_neg_edge = g_pwm_neg_edge + 0x0000000F;
	   }
       else if((adjust_rpm >= TWENTYFIVE_PERSENT_OF_MAX_RMP)&& (g_pwm_neg_edge < 0x000000F8))
       {
          g_pwm_neg_edge = g_pwm_neg_edge + 0x00000007;
	   }
       else if((adjust_rpm >= TEN_PERSENT_OF_MAX_RMP) && (g_pwm_neg_edge < 0x000000FC))
       {
          g_pwm_neg_edge = g_pwm_neg_edge +  0x00000003;
       }
       else if((adjust_rpm >= FIVE_PERSENT_OF_MAX_RMP)&& (g_pwm_neg_edge < 0x000000FD))
       {
          g_pwm_neg_edge = g_pwm_neg_edge + 0x00000002;
       }
       else if((adjust_rpm >= ONE_PERSENT_OF_MAX_RMP)&& (g_pwm_neg_edge < 0x000000FE))
       {
          g_pwm_neg_edge = g_pwm_neg_edge + 0x00000001;
	   }
       /* Negative edge PWM1 */
       (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0x14)))= g_pwm_neg_edge;
       /* Sync update PWM */
       (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0xE4)))= 0x00000001;
	}
  }
}

void fan_open_loop_control(void)
{
  int32_t pwm_neg_edge;
  pwm_neg_edge = (VAL_255_32_bit * user_rpm)/g_max_rpm;
  /* Negative edge PWM1 */
  (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0x14)))= pwm_neg_edge;
  /* Sync update PWM */
  (*((uint32_t volatile *)(FABRIC_PWM_BASE_ADDR + 0xE4)))= VAL_1_32_bit;
}
uint8_t fan_menu_n_action(uint8_t menu_print)
{
	uint8_t key     = 0;
	uint8_t rx_size =0;
	uint8_t rpm[7];
	uint8_t ack[] = "A\r\n";

    rx_size = MSS_UART_get_rx(&g_mss_uart0, &key, 2);
    if(key == '*')
    {
    	/* Ack to configuration */
    	MSS_UART_polled_tx(&g_mss_uart0,ack,3);

	    /* Reception of Type of FAN */
	    rx_char_ptr = rx_char;
	    read_user_input(rx_char_ptr);
	    g_fan_type = rx_char[0];
	    /* Ack */
	    MSS_UART_polled_tx(&g_mss_uart0,ack,3);

    	/* Reception of Maximum RPM */
    	rx_char_ptr = rx_char;
    	read_user_input(rx_char_ptr);
    	char_to_int( &rx_char_ptr, &g_max_rpm );
	    /* Ack */
	    MSS_UART_polled_tx(&g_mss_uart0,ack,3);

	    /* Reception of minimum duty cycle to turn on the FAN */
		    rx_char_ptr = rx_char;
		    read_user_input(rx_char_ptr);
		    char_to_int( &rx_char_ptr, &g_min_pwm_duty_cy );
		    /* Ack */
		    MSS_UART_polled_tx(&g_mss_uart0,ack,3);

	    /* Reception of number of poles */
	    rx_char_ptr = rx_char;
	    read_user_input(rx_char_ptr);
	    char_to_int( &rx_char_ptr, &g_no_of_poles );
	    /* Ack */
	    MSS_UART_polled_tx(&g_mss_uart0,ack,3);




	    /* Reception of PWM frequency */
	    rx_char_ptr = rx_char;
	    read_user_input(rx_char_ptr);
	    char_to_int( &rx_char_ptr, &g_pwm_freq );
	    /* Ack */
	    MSS_UART_polled_tx(&g_mss_uart0,ack,3);


		init_fan_controller();
    }
    else if(key == 'R')
    {
    	MSS_UART_polled_tx(&g_mss_uart0,ack,3);
    	do {
             rx_size = MSS_UART_get_rx(&g_mss_uart0, &key,1);
           } while(rx_size == 0);

    	if(key == 'F')
        {
    	  MSS_UART_polled_tx(&g_mss_uart0,ack,3);
  	      rx_char_ptr = rx_char;
  	      read_user_input(rx_char_ptr);
  	      MSS_UART_polled_tx(&g_mss_uart0,ack,3);
    	  g_measured_rpm1 = g_max_rpm;
    	  g_fan_state_status.fan_status = fan_full_speed_start;
        }
        else if(key == 'O')
        {
           g_fan_state_status.fan_status = fan_open_loop_start;
        }
        else if((key == 'C') && (g_fan_type == '4' || g_fan_type == '3'))
        {
          g_fan_state_status.fan_status = fan_closed_loop_start;
        }
    }
   else if(key == 'S')
   {
      g_fan_state_status.fan_status = fan_stop;
   }
    else if(key == 'D')
    {
       g_fan_state_status.fan_status = fan_display_staus;
    }
    else if(key == 'W')
    {
    	int_to_char(g_measured_rpm1,rpm);
    	MSS_UART_polled_tx(&g_mss_uart0,rpm,7);
       return 1;
    }
    else if(key == 'X')
    {
    	int_to_char(g_measured_rpm2,rpm);
    	MSS_UART_polled_tx(&g_mss_uart0,rpm,7);
       return 1;
    }
    else if(key == 'Y')
    {
    	int_to_char(g_measured_rpm3,rpm);
    	MSS_UART_polled_tx(&g_mss_uart0,rpm,7);
       return 1;
    }
    else if(key == 'Z')
    {
    	int_to_char(g_measured_rpm4,rpm);
    	MSS_UART_polled_tx(&g_mss_uart0,rpm,7);
       return 1;
    }

    switch (g_fan_state_status.fan_status)
    {
        case fan_full_speed_start:

        	 MSS_TIM1_stop();
        	 PWM_init_fan();
    	     PWM_enable_fan();
    	     MSS_UART_polled_tx(&g_mss_uart0,ack,3);
    	     g_fan_state_status.fan_status = fan_full_speed_started;
    	     return 1;

        case fan_stop:
        	 MSS_TIM1_stop();
    	     PWM_disable_fan();
    	     g_fan_state_status.fan_status = fan_stopped;
    	     return 1;

        case fan_open_loop_start:

        	 MSS_UART_polled_tx(&g_mss_uart0,ack,3);
        	 PWM_enable_fan();
    	     rx_char_ptr = rx_char;
    	     read_user_input(rx_char_ptr);
    	     char_to_int( &rx_char_ptr, &user_rpm );
    	     MSS_UART_polled_tx(&g_mss_uart0,ack,3);
    	     g_fan_state_status.fan_status = fan_open_loop_started;
    	     MSS_TIM1_stop();
    	     fan_open_loop_control();
    	     return 1;

        case fan_open_loop_started:
             return 0;

        case fan_closed_loop_start:

             PWM_enable_fan();
             timer1_config();
             MSS_UART_polled_tx(&g_mss_uart0,ack,3);
             rx_char_ptr = rx_char;
             read_user_input(rx_char_ptr);
             char_to_int( &rx_char_ptr, &user_rpm );
    	     MSS_UART_polled_tx(&g_mss_uart0,ack,3);
    	     g_fan_state_status.fan_status = fan_closed_loop_started;
    	     return 1;
        case fan_closed_loop_started:
        	/*if(g_read_result == 1)
        	{
    	      fan_closed_loop_control();
    	      g_read_result = 0;
        	}*/
		    return 0;
        default:
        	return 0;
    }

}


void init_fan_controller(void)
{
	NVIC_EnableIRQ(Fabric_IRQn);
    PWM_init_fan();
	/* Start timer */
	MSS_TIM1_init(MSS_TIMER_PERIODIC_MODE);

}


void Fabric_IRQHandler( void )
{
	uint32_t loc_read;
	loc_read = (*((uint32_t volatile *)(FABRIC_TACH_BASE_ADDR + 0xF4))) & 0x03FFFFFF; /* Reading the TACH reading */;
	loc_read = loc_read >> 24;
	loc_read = loc_read << 1;
	/*  Tacho disable with register map*/
	(*((uint32_t volatile *)(FABRIC_TACH_BASE_ADDR + 0xF0)))= (0x00000006 & loc_read); /* disabling TACHO for  taking TACHO reading*/
	g_tach_reading = (*((uint32_t volatile *)(FABRIC_TACH_BASE_ADDR + 0xF4))) & 0x03FFFFFF; /* Reading the TACH reading */

	NVIC_ClearPendingIRQ( Fabric_IRQn ); /* Clear Pending Fabric Interrupt */
	g_read_result = 1;
	timer1_config();
	fan_closed_loop_control();
}
