/****************************************************************************/
/* Copyright 2007 MBARI.                                                    */
/* Monterey Bay Aquarium Research Institute Proprietary Information.        */
/* All rights reserved.       																						  */
/*																																					*/
/* loadControl_main.c - main loop program															  		*/
/*																																					*/
/* Rev History:																															*/
/* Initial Creation 11/2007		B.Kieft																		    */
/*																																					*/
/*																																					*/
/****************************************************************************/
#include <16f876A.h>
#device ADC=10 ICD=true *=16

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#use delay(clock=4000000, restart_wdt)

#include "loadControl_main.h"
#include "RS485.c"
#include "spi.c"
#include "Command_Handler.c"

#if DEBUG
#fuses XT,WDT,NOPROTECT,PUT,NOBROWNOUT,NOLVP
#else
#fuses XT,WDT,NOPROTECT,PUT,NOLVP
#endif

#define  RS485_USE_EXT_INT    FALSE


// Purpose: ISR for timer 1. 
#INT_TIMER1                        
void timer_1_isr() {                                              
   set_timer1(0xFD95);  // sets timer to interrupt in 10 ms (100 Hz)
   if(rtos_pin == HI)
     rtos_pin = LO;
   else
   	 rtos_pin = HI;
   output_bit(PIN_C1, rtos_pin); // *** DEBUG
}

// Purpose: Does A/D calculation of current from MAX4081. Stores value in global
void do_adc_current_calcs(float vref, float rsense, float currDiv) {
 adc_value_glob = read_adc();  
 adc_value_glob = (adc_value_glob / 1023.000) * vref; // scale for Vref
 
 adc_value_glob = (adc_value_glob - (vref/2.00)); // subtract Vref/2 since ref1B is grounded
 adc_value_glob = adc_value_glob / currDiv;    // Divide by 20V/V apmplification
 adc_value_glob = adc_value_glob / rsense;     // Divide by Rsense value of .2 Ohms
}

// Purpose: Does A/D calculation of voltage through divider and scales to vscale.
// Stores value in global
void do_adc_voltage_calcs(float vscale) {
 adc_value_glob = read_adc();  
 adc_value_glob = (adc_value_glob / 1023.000) * vscale; // scale for 0-vscale Volts	
}

// Purpose: Holds software in infinite loop to allow watchdog to reset
#SEPARATE
void software_reset() {
  for(;;) {
    null;
  }
}

// Purpose: Removes and re-applies power to the islolated controller
#SEPARATE
void slave_reset() {
  output_bit(PIN_B4, LO); // Turn off PIC  
  delay_ms(1000);
  output_bit(PIN_B4, HI); // Turn on PIC  
}

// Purpose: Initialization for elements common to both PICs
#SEPARATE
void initializeLC() {
  // Set up the system clocks and watchdog
  setup_wdt(WDT_288MS); // See fuses WDT also
  
  // Using timer 1 for "real time" ground fault detection interrupts
  setup_timer_1(T1_INTERNAL|T1_DIV_BY_8); 
}

// Purpose: Initialization for the Master (non-iso) PIC
#SEPARATE
void initializeMaster() {

  // Initialize Outputs/Inputs
  set_tris_a(0x0F); // Pins A3-A0 inputs. Pins A5-A4 outputs
  set_tris_b(0xC1); // Pins B7, B6, B0 inputs. Pins B5, B4, B3, B2, B1 outputs
  set_tris_c(0x93); // Pins C7, C4, C1, C0 inputs. Pins C6, C5, C3, C2 outputs

  // Set outputs low
  output_bit(PIN_A4, LO);
  output_bit(PIN_A5, LO);
  output_bit(PIN_B1, LO);
  output_bit(PIN_B2, LO);
  output_bit(PIN_B3, LO);
  output_bit(PIN_B4, LO);
  output_bit(PIN_B5, LO);
	output_bit(PIN_C2, LO);
	output_bit(PIN_C3, LO);
  output_bit(PIN_C5, LO);
	output_bit(PIN_C6, LO);  

  // Initialize Serial and enable interrupts
  rs485_init();
  
  // Initialize SPI bus
  SETUP_SPI(SPI_MASTER | SPI_L_TO_H); 

  // Set up analog input for AN0 and AN1
  setup_adc(ADC_CLOCK_INTERNAL);
  setup_adc_ports(AN0_AN1_VREF_VREF); // A0 A1 VRef+=A3 VRef-=A2

  // Check for WDT reboot
  if(wdt_reset) {
  	//*** DEBUG - to be removed. WDT reset error will be displayed per interface spec
    sprintf(response_message, "? WDT\r\n ");
    send_response(response_message);  	
    delay_ms(500);
    // Load real error here
    // WDT_Error = True;
    //*** DEBUG
  }  
  else {
  //*** DEBUG - to be removed. Should interface spec include "alphabet soup" on power up?
  sprintf(response_message, "MBARI Load Controller v1.0\r\n");
  send_response(response_message);  
  sprintf(response_message, "Enter Command\r\n");
  send_response(response_message);
  //*** DEBUG
  }	
	
}

// Purpose: Initialization for the Slave (iso) PIC
#SEPARATE
void initializeSlave() {

  // Initialize SPI bus. SS_DISABLED since A5 is potentially a high input.
  // See Document: DS39582B-page 71 for more info
  SETUP_SPI(SPI_SS_DISABLED | SPI_SLAVE | SPI_L_TO_H); 

  // Initialize Outputs/Inputs
  set_tris_a(0x2F); // Pins A5, A3, A2, A1, A0 inputs. Pin A4 output
  set_tris_b(0xDE); // Pins B7, B6, B4, B3, B2, B1 inputs. Pins B5, B0 outputs.
  set_tris_c(0x19); // Pins C4, C3, C0 inputs. Pins C7, C6, C5, C2, C1 outputs.

  // Set outputs low
  output_bit(PIN_A4, LO);
  output_bit(PIN_B0, LO);
  output_bit(PIN_B5, LO);
	output_bit(PIN_C1, LO);
	output_bit(PIN_C2, LO);
	output_bit(PIN_C5, LO);	
  output_bit(PIN_C6, LO);
  output_bit(PIN_C7, LO);
    
  // Set up analog input for AN0, AN1, AN5
  setup_adc(ADC_CLOCK_INTERNAL);
  setup_adc_ports(AN0_AN1_AN4_VREF_VREF); // A0 A1 A5 VRef+=A3 VRef-=A2  

  // Set interrupt masking
  enable_interrupts(GLOBAL);
  disable_interrupts(INT_AD);    //disables A/D interrupt
  enable_interrupts(INT_TIMER1); // fires up the timer interrupt
}


// Purpose: Main control loop for Master (non-iso) PIC
#SEPARATE
void mainMaster() {

  // Initialize the PIC
  initializeMaster();

  /* Begin Loop */        
  for(;;)
	{ 
	  // strobe the dog
	  restart_wdt(); 
	
	  // Check for new RS485 command
 		if(rs485_get_new_message(false)) 
	 	{
	    cmd_received = true;
            
	 	  // Check to see which command was received...
	 	  // And call associated handler
      if(!strcmp(commandStr,load_power)) {
        handle_load_power();
      }
      else if(!strcmp(commandStr,comm_relay)) {
     	  handle_comm_relay();
      }  
      else if(!strcmp(commandStr,write_flash)) {
     	  handle_write_flash();
      }
      else if(!strcmp(commandStr,read_bus_voltage)) {
     	  handle_read_bus_voltage();
      }  
      else if(!strcmp(commandStr,read_load_voltage)) {
     	  handle_read_load_voltage();
      }
      else if(!strcmp(commandStr,read_bus_current)) {
     	  handle_read_bus_current();
      }      
      else if(!strcmp(commandStr,read_load_current)) {
     	  handle_read_load_current();
      }  
      else if(!strcmp(commandStr,read_gf_current)) {
     	  handle_read_gf_current();
      }
      else if(!strcmp(commandStr,read_startup_oc_threshold)) {
     	  handle_read_startup_oc_threshold();
      }  
      else if(!strcmp(commandStr,read_q_oc_threshold)) {
     	  handle_read_q_oc_threshold();
      }
      else if(!strcmp(commandStr,read_startup_oc_time)) {
     	  handle_read_startup_oc_time();
      }  
      else if(!strcmp(commandStr,read_comm_realy_status)) {
     	  handle_read_comm_realy_status();
      }
      else if(!strcmp(commandStr,read_device_serial_number)) {
     	  handle_read_device_serial_number();
      }  
      else if(!strcmp(commandStr,read_errors)) {
     	  handle_read_errors();
      }
      else if(!strcmp(commandStr,oc_threshold_startup)) {
     	  handle_oc_threshold_startup();
      }  
      else if(!strcmp(commandStr,oc_threshold_q)) {
     	  handle_oc_threshold_q();
      }
      else if(!strcmp(commandStr,oc_time_startup)) {
     	  handle_oc_time_startup();
      }  
      else if(!strcmp(commandStr,set_address)) {
     	  handle_set_address();
      }                                         
      //...Or if the command is invalid
      else { 
      	//*** DEBUG - to be replaced with correct error handling per spec
        sprintf(response_message, "\r\nCOMMAND ERROR\r\n");
        send_response(response_message);
        //*** DEBUG
        cmd_received = false;
      }
		}	// Check for new command
		
	  //*** DEBUG - The rest of the functionality (e.g. BIT) goes here

  } // Close of main control loop
	
}


// Purpose: Main control loop for Slave (iso) PIC
#SEPARATE
void mainSlave() {
  int8 spi_cmd_buff[1] = {28};
  char sent_spi_data_slave[MAX_SPI_CHARS];
  
  // Initialize the PIC
  initializeSlave();
  
  // Begin the main loop. 
  // This loop waits for a SPI bus command from the Master
  for(;;) {
  	
    // strobe the dog
	  restart_wdt(); 

    // Check for new SPI command
    if( spi_data_is_in() ) {
    	spi_cmd_buff[0] = spi_read(); 

      // Handle Read GF Currents
      // Chan 0
      if( spi_cmd_buff[0] == 1 ) {
    		strcpy(sent_spi_data_slave, "0.000gf");
        SPI_strwrite(sent_spi_data_slave);       		
    	} 
      // Chan 1    	
      else if( spi_cmd_buff[0] == 2 ) {
    		strcpy(sent_spi_data_slave, "1.000gf");
        SPI_strwrite(sent_spi_data_slave);       		
    	}     	
    	// Chan 2
    	else if( spi_cmd_buff[0] == 3 ) {
    		strcpy(sent_spi_data_slave, "2.000gf");
        SPI_strwrite(sent_spi_data_slave);       		
    	}
    	// Chan 3
    	else if( spi_cmd_buff[0] == 4 ) {
    		strcpy(sent_spi_data_slave, "3.000gf");
        SPI_strwrite(sent_spi_data_slave);   		
    	}
    	
      // Handle Load voltage
      else if( spi_cmd_buff[0] == 50 ) {
        set_adc_channel(0);	
        delay_ms(3);
        do_adc_voltage_calcs(12.000);
 	      sprintf(sent_spi_data_slave, "%2.2f",adc_value_glob); 
        SPI_strwrite(sent_spi_data_slave);   //Write data to buffer    	
    	}    	

    	// Handle Load current
    	else if( spi_cmd_buff[0] == 51 ) {
        set_adc_channel(4);	                                     // Set A/D channel
        delay_ms(3);    	
        do_adc_current_calcs(VREF, RSENSE, CURRDIVISOR);         // Calculate current
        sprintf(sent_spi_data_slave, "i:%f\r\n",adc_value_glob); // Send back the results
        SPI_strwrite(sent_spi_data_slave);  		
    	}
    	
    }	// End spi_data_is_in

  } // End main "slave" loop
}



//
// Main execution loop.
//
#SEPARATE
main()
{
  // Check for WDT reset and output error if necessary
  switch ( restart_cause() )
  {
  	case WDT_FROM_SLEEP:
    case WDT_TIMEOUT:
    {
      wdt_reset = true;
      break;
    }
    case NORMAL_POWER_UP:
    {
    	wdt_reset = false;
      break;
    }
  }     

  // Run initialization common to both PICs
  initializeLC();

  // Determine if controller is master or slave
  if(input(PIN_C0) ) { 
  	master = false;
    mainSlave();
  }
  else {
  	mainMaster();
  }

  //
  // If we execute here, there has been a critical failure. Log error and reset the board
  //
  
  // log error here
  
  software_reset();  

}





