///////////////////////////////////////////////////////////////////////////
////                         rs232_OEM_Universal.C                     ////
////                                                                   ////
////  Version for MBARI with special functions                         ////
////                                                                   ////
////                          					                       ////
///////////////////////////////////////////////////////////////////////////

#include <18f26k20.H>

#include <STRING.H>
#include <STDLIB.H>
#include <STDINT.H>


#fuses EC
#fuses NOMCLR  
#fuses PUT
#fuses NOPROTECT
#fuses NOBROWNOUT
#fuses NOLVP
#fuses NOWDT

#use fast_io(A)
#use fast_io(B)
#use fast_io(C)

#use delay(clock=16000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7)

#define MBARI_PRE_SCANS    2 

#define ReadoutsLow 	 0
#define ReadoutsHigh 	 1
#define IntPeriodL  	 2
#define IntPeriodM  	 3
#define IntPEriodH  	 4
#define SetEnables  	 5
#define SetIO	 		 6
#define SetControlStrobe 7
#define SetStoreEnable	 8
#define DataByte	 0
#define TemperatureVals	 3
#define StatusByte	 1
#define HardTrig	 1
#define SoftTrig	 2
#define PixelSelect	 7
#define PixelValue	 9
#define DMAEnable    15
#define PCTrigger    14
#define LampStatus 	 1
#define ResetCircuit 13
#define DigitalRES   10
#define ChanellSelect 12
#define LoadDetectorWidth 12
#define SelectTempChannel 11
#define ConvertTemperature 12
#define Selector 12
#define SelectMonoColour 12

#define comma 0x2c
#define cr 13
#define lf 10
#define eot 4

#define R 0x52
//#define S 0x53
#define G 0x47
#define F 0x46
#define T 0x54
//#define E 0x45
//#define P 0x50
#define D 0x44
#define H 0x48
#define A 0x41
#define K 0x4b
#define W 0x57
#define Z 0x5a
//#define B 0x42


//#byte PORTA = 5
//#byte PORTB = 6

#byte PORTA=0xf80
#byte PORTB=0xf81
#byte PORTC=0xf82

#define AddrStb PIN_A0
#define DataStb PIN_A1
#define EPPWr   PIN_A2
#define StatusIn  PIN_A3
//#define Wait    PIN_A4
#define LED_RED   PIN_A4
#define LED_GRN   PIN_A5

#define ResetOut PIN_C3
#define CTS   PIN_C4
#define RTS   PIN_C5
#define TXD	  PIN_C6

#define ALL_IN 0xff
#define ALL_OUT 0

#define BUFFER_SIZE 32
byte buffer[BUFFER_SIZE];
byte next_in = 0;
byte next_out = 0;

const char Date[15] = __DATE__;
const char Time[15] = __TIME__;
const char Revision[10] = "1.1";

int resolution, clk, Polling;
int LampstatusRequired;
long Readouts, Pixels;  
int index, Gain;
int32 Period;
char Command[10];
char SubString[10];
int measuring;
int iStart;
int Enabled;
int high,low;
long TempValue;
int EnableRangeChange;
int error;


void EppAddrOut(char Addr)
{
 set_tris_b(ALL_OUT);        //data port output
 PORTB = Addr;               // put addr on data bus
 Output_Bit(EPPWr,0);        //EPP Write low
 Output_Bit(AddrStb,0);      //Addr strobe low
 Output_Bit(AddrStb,1);      //Addr strobe high
 Output_Bit(EPPWr ,1);       //EPP Write high 
}


void EppDataOut(char Data)
{
 set_tris_b(ALL_OUT);        //data port output
 PORTB = Data;               // put data on data bus
 Output_Bit(EPPWr,0);        //EPP Write low 
 Output_Bit(DataStb,0);      //Data strobe low
 Output_Bit(DataStb,1);      //Data Strobe high 
 Output_Bit(EPPWr,1);        //EPP Write high 
}


int8 EppDataIn()
{
 int8 data;
 set_tris_b(ALL_IN);
 Output_Bit(EPPWr,1);        //EPP Write high 
 Output_Bit(DataStb,0);      //Data strobe low
 data = PORTB;
 Output_Bit(DataStb,1);      //Data Strobe high 
 return Data;
}


#include "S3904256Params.c"


void resetHardware()
{
 Output_Bit(ResetOut,0);  //send a hardware reset to 8452 (FLEX 6000A FPGA)
 Output_Bit(ResetOut,1);
}


void CameraWriteEnable()
{
 EppAddrOut(SetStoreEnable);
// delay_ms(1);
 EppDataOut(0);
}


void CameraPCTrigger()
{
EppAddrOut(PCtrigger);
EppDataOut(0);
}


void SetReadouts()
{
 Readouts = 1;	//force readouts to always be one as a hack around the MBARIScan()
 int8 High, Low;
 High = Readouts >> 8; 
 Low = Readouts;
 EPPAddrOut(ReadoutsLow);
 EPPDataOut(Low);
 EPPAddrOut(ReadoutsHigh);
 EPPDataOut(High);
}


void SetTimer(int32 Period)
{
int16 temp16;
int  High,Middle,Low;

if (enabled != 0x10)  
{
 Period =(Period * ClkPeriodMultiplier) + PixelsToAdd;          //8 microsecs clk
} 
else 
{
 period = period;     //if ext bunch trigger mode, 0x10, then count laser pulses not clk pulses
}
temp16 = period/256;                  
high = temp16 / 256;                   
middle = temp16;
low = period;
EPPAddrOut(IntPeriodL);
EPPDataOut(Low);
EPPAddrOut(IntPeriodM);
EPPDataOut(Middle);
EPPAddrOut(IntPeriodH);
EPPDataOut(High);
} 


void Type()
{
 int temp;
 if (LampStatusRequired == 1)
 {
   EppAddrOut(LampStatus);
   Temp = Eppdatain() & 0x08;
   if (Temp == 0x08) resolution = 1; else resolution = 0; // resolution = 1 lamp = ON else Lamp = off
   LampStatusRequired = 0;
 }
 else if (polling == 0)  // normal output of camera properties
 {
  Resolution=InitResolution;       // resolution = 12 or 16
  Clk=InitClk;              // clk = 1, 2, 4, 8 or 16
 }
 else  // report readout status via resolution value 1 = not finished 0 = finished
 {
  if (INPUT(StatusIn) == 1) 
  {
   resolution = 1;   //finished high not complete
  }
  else 
  {
   resolution = 0;     // finished low readout done
   polling = 0; // 
  } 
  // printf("%Lu %U %U \r",Pixels,resolution,clk); 
 }
 printf("%Lu %U %U \r",Pixels,resolution,clk);
}

void Version()
{
   printf("MBARI-ISUS %s, %s, %s\n\r", Revision, Date, Time);
}

void GetSubString()
{
 int x;
 x = iStart + 1;                //After first char in command comes the value
 strcpy(SubString,&Command[x]);	
}

void printdone()
{
 printf("Done\r"); 
}



void DoneMeasurement()
{
 if ( INPUT(StatusIn) == 1 ) printf("NDONE\r"); else  printf("DONE\r");   
}

void Measure()
{
 CameraWriteEnable(); 
 While ( INPUT(StatusIn) == 0 ); // finished signal is low before going high during scan
}


void TriggerReadouts1()
{
 CameraWriteEnable(); 
 While ( INPUT(StatusIn) == 0 ); // finished signal is low before going high during scan
 While ( INPUT(StatusIn) == 1 );  // wait for finished to go low again
 printdone();
}

void TriggerReadouts2()
{
 CameraPCTrigger();                    //trigger one readout
 While ( INPUT(StatusIn) == 1 ); // finished signal is low before going high during scan
 While ( INPUT(StatusIn) == 0 ); // wait for finished to go low again
}


void TriggerReadouts3()   // used enable trigger mode 0x80 to trigger readout
{
 CameraWriteEnable(); 
 While ( INPUT(StatusIn) == 0 ); // finished signal is low before going high during scan
 Polling = 1;
}


/******************************************************************************/
/* Device is triggered either internally or via a software command. A value of*/
/* 0x01 sets the device for internal "self-triggering", a value of 0x02 sets  */
/* the device for "software-triggering"                                       */
/* If device is configured for internal triggering then after the number of   */
/* programmed readouts has occured, pin 12 will return low, it goes high      */
/* after the first trigger of the sequence. If however the device is          */
/* configured for external or software triggering then pin 12 functions as an */
/* indicator of the diode array scan period, when low the array is being      */
/* scanned upon returning high the diode array scan period has finished (4ms) */
/******************************************************************************/

void MBARIScan()
{
 int8 i;

 output_bit(RTS,0);     	//assert RTS, let the controller know we're now scanning
 output_bit(LED_GRN, 0);
 
 EppAddrOut(SetEnables);  	// put electronics into E2 "PC" trigger mode
 EppDataOut(2);

 EppAddrOut(PCtrigger);
 for(i=1; i <= MBARI_PRE_SCANS; i++) //clear the array of any residual charge
 {
  EppDataOut(0);
  while( INPUT(StatusIn) == 1 );	//wait for the Status Pin to go low
  while( INPUT(StatusIn) == 0 );    //wait for the Status Pin to go high
 }

 EppDataOut(0);
 for(i=1; i <= Readouts; i++)    //loop Readout times to get spectrum
 {
  delay_ms(Period);    			//wait the preset integration time
  EppAddrOut(SetStoreEnable); 	//enable data storage
  EppDataOut(0);
  EppAddrOut(PCtrigger);
  EppDataOut(0);
 }
 delay_ms(5);					//wait for detector to finish last scan 

 output_bit(RTS,1);   //unassert RTS, let the controller know we're now done (controller should monitor RTS for hung condition and timeout)
 output_bit(LED_GRN, 1);
 
// printdone();
}


void LEDs()
{
int state;	
GetSubString();
state=atoi32(SubString);
 switch (state)
 {
  case  1: output_bit(LED_GRN, 1);			break;   
  case  2: output_bit(LED_GRN, 0);			break;   
  case  3: output_bit(LED_RED, 1);			break;   
  case  4: output_bit(LED_RED, 0);			break;   
  default: break;	
 }

}


void Start()
{
 if (enabled != 0) 
  { switch (Enabled)
   {
    case  1: TriggerReadouts1();  break; // 
    case  2: TriggerReadouts2();  break;  
    case  4: TriggerReadouts1();  break;  //for external trigger, just enable data store. Wait for finished 
    case  8: MBARIScan();  		  break;  //Special procedure for MBARI
    case 16: TriggerReadouts1();  break;  //MUltiple laser trigger  
    default: printf("E:%s\r",command);
   }
  }
  else printf("E:%s\r",command); // no trig mode enabled
}


void ProgPixels(int loc, long TempValue)
{
 int High,Low;
 if (Resolution != 8) TempValue = TempValue * 2;
 High = TempValue >> 8;
 Low = TempValue;
 EPPAddrOut(PixelSelect);
 EPPDataOut(loc);
 EPPAddrOut(PixelValue);
 EPPDataOut(low);
 loc = loc + 1;
 EPPAddrOut(PixelSelect);
 EPPDataOut(loc);
 EPPAddrOut(PixelValue);
 EPPDataOut(high);
}


void IOOut(int Value)
{
 EppAddrOut(SetIO);
 EppDataOut(Value);
 LampStatusRequired = 1; 
}


void Enables()
{
//int index, Gain;
GetSubString();
index=atoi32(SubString);
enabled = 1;
 switch (index)
 {
  case  0: index=0;	    enabled = 0;		break;  // enable nothing
  case  1: index=1;		enabled = 1;		break;  // enable Int timer trig
  case  2: index=2;		enabled = 2;		break;  // enable PC trig
  case  3: index=4;		enabled = 4;		break;  // enable Ext trig
  case  4: index=0x11; 	enabled = 0x10;		break;  // enable Ext bunch trig ie multiple laser pulses
  case  5: index=2;		enabled = 8;		break;  // enable group trig
  case 64: Gain = 0; 				    	break;
  case 65: Gain = 0x80; 				   	break;
  case 128: triggerReadouts3(); 			break;	// use enable trigger mode 0x80 to trigger readout
  case 130: IOOut(0); 						break;
  case 131: IOOut(1);						break; 
  case 132: IOOut(2); 						break;
  case 133: IOOut(3);						break; 
  default: index=0;		enabled = 0;	
 }
 if (index < 128)	
 {
  EppAddrOut(5);
  EppDataOut(index | Gain);          //gain bit is at D7
  SetTimer(Period);
 }
//printf("Q %U\r",index);
}


void GetIntPeriod()
{
 GetSubString();
 Period = atoi32(SubString);
 SetTimer(Period);
}

void GetReadouts()
{
 GetSubString();
 Readouts = atol(SubString);
 SetReadouts();
}


void SetUart(int b)
{
 switch(b)
 {
  case 0x1: Set_uart_speed(9600); break;
  case 0x2: Set_uart_speed(19200); break;
  case 0x3: Set_uart_speed(38400); break;
  case 0x4: Set_uart_speed(57600); break;
  case 0x5: Set_uart_speed(115200); break;
  default: Set_uart_speed(9600); break;    //if eeprom not set start at 9600
 }
}


void SetCommSpeed()
{ 
 int32 b;
 int c;
 GetSubString();
 b = atoi32(SubString);
 c = b;
 SetUart(c);
 write_eeprom(0,c);
}


void ReadBack()
{
  GetSubString();
}


#int_rda
void serial_isr() 
{
 buffer[next_in]=getc();
 if (measuring == 1) 
 if (buffer[next_in]!= 10)
  {
   resetHardware();
  }
 next_in=(next_in+1) % BUFFER_SIZE; 
}


void SetDataFormat()
{
 GetSubString();
 Format = SubString[0];
 write_eeprom(1,Format);
}


void OutputByteFormat()
{
 int8 high_b, low_b;
 int16 x;

 output_bit(RTS,0);       	//signal program it's time to start burping the data out 

 for (x = 1; x <= Pixels; x++)
  {
   high_b = Eppdatain();
   low_b  = EppDatain();  
 
   while( input(CTS) );  	//wait for CTS input to go low

   putc( high_b );
   putc( low_b );
  }

  output_bit(RTS,1);    	//signal program we're done burping the data out

}


void OutputWordFormat()
{
 int8 high_b, low_b;
 int16 x, result;
 
 output_bit(RTS,0);       //signal program it's time to start burping the data out 
  
 for (x = 1; x <= Pixels; x++)
 {
  high_b = Eppdatain();
  low_b  = EppDatain();
  result = high_b << 8;
  result = result + low_b;
  
  while ( input(CTS) );     	//wait for CTS input to go low
  
  if( Format == 0x31 )				//unsigned 16bit value in decimal with CR
    printf("%Lu\n\r", result);  	
  else if( Format == 0x32 )			//unsigned 16bit value in hexidecimal with CR
    printf("%Lx\n\r", result);   	
  else{								//unsigned 16bit value in decimal with Format char				
	if( x != Pixels )  
      printf("%Lu%C", result, Format);
    else
	  printf("%Lu\n\r", result);	
    }
 }  
 
 output_bit(RTS,1);       //signal program we're done burping the data out
 
}


void GetData()
{

  EppAddrOut(DataByte);

  if (Format == 0x30)     	//send pixel A/D counts as two binary bytes 
    OutputByteFormat();
  else          			//send pixel A/D counts as decimal or hexidecimal ASCII value
    OutputWordFormat();

}


void GetDataSingleValue()
{
 int8 y;
 int16 x;
  
 EppAddrOut(DataByte);          //Data port
 
 output_bit(RTS,0);       		//signal program in time to start burping the data out 
 output_bit(LED_RED, 0);
  
 for(x=1; x <= 512; x++)
  {
   while (input(CTS));			//wait for CTS input to go low
   y = Eppdatain();
   putc(y);
   output_toggle(LED_RED);
   while (!input(CTS));      	///wait for CTS input to go high
  }

  output_bit(RTS,1);       		//signal program we're done burping the data out
  output_bit(LED_RED, 1);
  
}


void GetString() 
{
   char c;
   next_out = 0;
   c = 0;
   while(c != 13) 
   {
    if (next_in > next_out)
    {
     c = buffer[next_out];
     command[next_out]= c;
     next_out=(next_out+1) % BUFFER_SIZE;
    }
   }
   command[next_out]=0;
   next_in = 0;
}


void GetValue()
{
 GetSubString();
 Tempvalue=atol(SubString);
}


void EchoCommand()
{
 if (error == 1) 
  {
   PUTC(0x45);  // send E
   PUTC(0x3A);  // send :
   error = 0;
  }
Printf("%s\r",Command);

}


int ReadEEPROM(int loc)
{
int Data;
Data=read_eeprom(loc);
return  data;
}


void RangeChange()
{
 GetSubString();
 Tempvalue=atol(SubString);
 if (Tempvalue == 1919) EnableRangeChange = 1;
}


void SetPixelStart()
{
 if (EnableRangeChange == 1)
 {
  GetValue();
  ProgPixels(0,TempValue);
  High = TempValue >> 8;
  Low = TempValue;
  write_eeprom(8,low);
  write_eeprom(9,high);
  StartPixel = TempValue;
  Pixels = (EndPixel - StartPixel);  // + 9;// 9 dark pixels
 }
 else error = 1;
 EnableRangeChange = 0;
}


void SetPixelEnd()
{
 if (EnableRangeChange == 1)
 {
  GetValue();
  ProgPixels(2,TempValue);
  High = TempValue >> 8;
  Low = TempValue;
  write_eeprom(10,low);
  write_eeprom(11,high);
  endPixel = TempValue;
  Pixels = (EndPixel - StartPixel);// + 9;// 9 dark pixels
 }
 else error = 1;
 EnableRangeChange = 0;
}


void GetCommand()
{
 char cmd;
 GetString();
 iStart = 0;
 while(!isalnum(Command[iStart]))
	++iStart;
 //cmd = TOUPPER(Command[iStart]);
 cmd = Command[iStart];
 switch (cmd)
  {
   case 'I': GetIntperiod();	EchoCommand();		break;
   case  R: GetReadouts();		EchoCommand();		break;  
   case 'S': Start();         						break;  
   case 's': Start();       						break;    
   case 'G': GetData();	         					break;
   case 'g': GetDataSingleValue(); 					break;		
   case 'T': Type();          						break;    	//pixels, resolution, clk
   case 'V': Version();        						break;    	//firmware version and compilation timestamp
   case 'L': LEDs();								break;	
   case 'E': Enables();  		EchoCommand();		break;
   case 'B': GetSubString();	Printf(SubString);	break;		//ASCII B
   case  F: SetDataFormat(); 	EchoCommand();		break;
   case  A: SetCommSpeed();		EchoCommand();    	break; 
   case  W: SetPixelStart();	EchoCommand();		break;		//only using DoExtendedInstruction
   case  Z: SetPixelEnd();		EchoCommand();		break;  	//only using DoExtendedInstruction
   case  D: DoneMeasurement(); 						break;   	//Just for Stellarnet
   case 'M': Measure(); 		EchoCommand(); 		break;      //Just for Stellarnet
   case  H: RangeChange();   	EchoCommand(); 		break; 
   default: printf("V:%s",command);  
  }
}


void StartUpReset()
{
  EppAddrOut(SetStoreEnable); 	//enable data storage
  EppDataOut(0);
  delay_ms(20);                 //wait for one readout
  EppAddrOut(DataByte);	        //get one byte of data, will reset address pointer
  EppDataIn();
}


void main() 
{
   delay_ms(100);				//give PLD time to load configuration from EEPROM
 
   clk    = read_eeprom(0);		//retrieve desired baud rate 
   Format = read_eeprom(1); 	//retrieve desired data format, this value was getting clobered by init() so I commented out Format = 0x30 in the S3904256Params.C file
   //  SetUart(clk);  		    //don't bother as we will use the default set by: use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7)
 
 
 //  SET_TRIS_A(0x18);           	//A0, A1, A2 output  A3, A4 input
   set_tris_a(0x08);           	//A0, A1, A2 output  A3 input, A4, A5 output
   SET_TRIS_C(0x97);           	//C7 = RX & C4 = CTS set input C6 = TX & C5 RTS Set output 
   PORT_B_PULLUPS(true);

   output_bit(RTS,0);          	//set RTS output low, let the controller know we're initializing
 
 
   Setup_Timer_0(0);
   set_tris_b(ALL_OUT);        	//data port output
   Output_Bit(AddrStb,1);      	//Addr Strobe high 
   Output_Bit(DataStb,1);      	//Data Strobe high 
   Output_Bit(EPPWr,1);        	//EPP Write high 

   resetHardware();
   Enabled = 0; 
   Readouts = 1;               	//for starters
   Period = 1;
   PORTB = 0xff;   
   ENABLE_INTERRUPTS(GLOBAL);
   ENABLE_INTERRUPTS(INT_RDA);
   EppAddrOut(ResetCircuit);
   EppDataOut(0);             	//reset hardware Finished signal low.

   init();						//see S3904256Params.C file, Format = 0x30 has been commented out

   SetTimer(1);
   SetReadouts();

   polling = 0;
   LampStatusRequired = 0;
  
   StartPixel = 1;
   ProgPixels(0,StartPixel);
   EndPixel = 257;
   ProgPixels(2,EndPixel);
   Pixels = (EndPixel - StartPixel); //+ 9;   // dark value pixels
  
   Enabled = 0;
   Gain = 0; 
   EppAddrOut(SetEnables);
   EppDataOut(1);  
   //EppDataOut(2);  
   
   
   StartUpReset();				//perform a simple one byte read to reset the PLD SRAM memory pointer, which seems to develop a one pixel offset after the first power-on measurement cycle
 
   output_bit(RTS,1);           //clear RTS, let the controller know we're now done (controller should monitor RTS to validate power up)
   
   output_bit(LED_RED, 1);
   output_bit(LED_GRN, 1);		//turn off for now, LED is wired backwards.
   
   while(1)  //loop forever
   {
    GetCommand();
   }

}  //end main()
