//---------------------------------------------------------------------------
// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// Except as contained in this notice, the name of Dallas Semiconductor
// shall not be used except as stated in the Dallas Semiconductor
// Branding Policy.
//---------------------------------------------------------------------------
//
//  ds550lnk.C - Serial functions using 8051 to be used as a test
//               for DS2480 based Universal Serial Adapter 'U'
//               functions.
//
//  Version: 1.00
//
//  TODO:	1) Clean up ugly code.
//			2) Allow for compile-time choice of using Timer2 for baud rate of Serial1
//			3) Package Serial0 and Serial1 SFRs into nice structs, hopefully to allow
//			   for removal of all the if-else statements in each function.
//			4) Do the math and write a better msDelay function.
//

// -- Keil c51 control directives
// Using inline assembly means we have to dump the compiler output of our code to
// an intermediate assembly source file, then assemble that file so we can have
// our object for linking.
#pragma SRC(ds550lnk.a51)
// -- End directives


#include "ownet.h"
#include "ser550.h"
#include "ds2480.h"

/* external functions */
/* serial0 routines */
extern void serial0_init(uchar);
extern void serial0_flush(void);
extern void serial0_putchar(char);
extern char serial0_getchar(void);
extern char serial0_peek(void);

/* serial1 routines */
extern void serial1_init(uchar);
extern void serial1_flush(void);
extern void serial1_putchar(char);
extern char serial1_getchar(void);
extern char serial1_peek(void);

// local routine
void usDelay(unsigned int);

//---------------------------------------------------------------------------
//-------- COM required functions for MLANU
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Attempt to open a com port.  Keep the handle in ComID.
// Set the starting baud rate to 9600.
//
// 'portnum'   - number 0 to MAX_PORTNUM-1.  This number provided will
//               be used to indicate the port number desired when calling
//               all other functions in this library.
//
//
// Returns: the port number if it was succesful otherwise -1
//
int OpenCOMEx(char *port_zstr)
{
   int portnum;

   if(port_zstr[0] == '0')
   {
      portnum = 0;
   }
   else
   {
      portnum = 1;
   }

   if(!OpenCOM(portnum, port_zstr))
   {
      return -1;
   }

   return portnum;
}

//---------------------------------------------------------------------------
// Attempt to open a com port.  Keep the handle in ComID.
// Set the starting baud rate to 9600.
//
// 'portnum'   - number 0 to MAX_PORTNUM-1.  This number provided will
//               be used to indicate the port number desired when calling
//               all other functions in this library.
//
// 'port_zstr' - zero terminate port name.  NOT USED.
//
//
// Returns: TRUE(1)  - success, COM port opened
//          FALSE(0) - failure, could not open specified port
//
SMALLINT OpenCOM(int portnum, char *port_zstr)
{
	port_zstr = 0; //to silence compiler

   if(portnum==0)
   {
      serial0_init(BAUD9600_TIMER_RELOAD_VALUE);
   }
   else
   {
      serial1_init(BAUD9600_TIMER_RELOAD_VALUE);
   }

   FlushCOM(portnum);

	return TRUE;
}

//---------------------------------------------------------------------------
// Closes the connection to the port.
//
// 'portnum'  - number 0 to MAX_PORTNUM-1.  This number was provided to
//              OpenCOM to indicate the port number.
//
void CloseCOM(int portnum)
{
	if(portnum==0)
	{
	   ES0 = 0; //disable serial interrupt
	   serial0_flush();
	}
	else
	{
	   ES1 = 0; //disable serial interrup
	   serial1_flush();
	}
}

//---------------------------------------------------------------------------
// Flush the rx and tx buffers
//
// 'portnum'  - number 0 to MAX_PORTNUM-1.  This number was provided to
//              OpenCOM to indicate the port number.
//
void FlushCOM(int portnum)
{
	if(portnum==0)
	   serial0_flush();
	else
	   serial1_flush();
}

//--------------------------------------------------------------------------
// Write an array of bytes to the COM port, verify that it was
// sent out.  Assume that baud rate has been set.
//
// 'portnum'  - number 0 to MAX_PORTNUM-1.  This number was provided to
//              OpenCOM to indicate the port number.
// 'outlen'   - number of bytes to write to COM port
// 'outbuf'   - pointer ot an array of bytes to write
//
// Returns:  TRUE(1)  - success
//           FALSE(0) - failure
//
SMALLINT WriteCOM(int portnum, int outlen, uchar *outbuf)
{
	int i = 0;
	if(portnum==0)
	{
   	for(; i<outlen; i++)
   	{
   		serial0_putchar(outbuf[i]);
   	}
   }
   else
   {
   	for(; i<outlen; i++)
   	{
   		serial1_putchar(outbuf[i]);
   	}
   }
	return (i==outlen?TRUE:FALSE); //unnecessary, but thinking ahead
}

//--------------------------------------------------------------------------
// Read an array of bytes to the COM port, verify that it was
// sent out.  Assume that baud rate has been set.
//
// 'portnum'  - number 0 to MAX_PORTNUM-1.  This number was provided to
//               OpenCOM to indicate the port number.
// 'inlen'     - number of bytes to read from COM port
// 'inbuf'     - pointer to a buffer to hold the incomming bytes
//
// Returns: number of characters read
//
int ReadCOM(int portnum, int inlen, uchar *inbuf)
{
	int i = 0;
	uchar cnt = 5; //50ms timeout
	uchar quit = FALSE;
	if(portnum==0)
	{
   	for(; !quit && i<inlen; cnt=5)
   	{
   	   while( !serial0_peek() && (cnt--)>0 )
   	      msDelay(10);

   	   if(!serial0_peek())
   	      quit = TRUE;
   	   else
      		inbuf[i++] = serial0_getchar();
   	}
   }
   else
   {
   	for(; !quit && i<inlen; cnt=5)
   	{
   	   while( !serial1_peek() && (cnt--)>0 )
   	      msDelay(10);

   	   if(!serial1_peek())
            quit = TRUE;
         else
      		inbuf[i++] = serial1_getchar();
   	}
   }

	return i;
}

//--------------------------------------------------------------------------
// Send a break on the com port for at least 2 ms
//
// 'portnum'  - number 0 to MAX_PORTNUM-1.  This number was provided to
//              OpenCOM to indicate the port number.
//
void BreakCOM(int portnum)
{
	if(portnum==0)
	{
	   //hold P3.1 low
	   //ES0 = 0;
      P3 &= 0xFD;
		msDelay(4);
      P3 |= 0x02;
      //ES0 = 1;
   }
   else
	{
	   //hold P1.7 low
	   //ES1 = 0;
   	P1 &= 0x7F;
		msDelay(4);
   	P1 |= 0x80;
   	//ES1 = 1;
	}
}

//--------------------------------------------------------------------------
// Set the baud rate on the com port.
//
// 'portnum'   - number 0 to MAX_PORTNUM-1.  This number was provided to
//               OpenCOM to indicate the port number.
// 'new_baud'  - new baud rate defined as
//                PARMSET_9600     0x00
//                PARMSET_19200    0x02
//                PARMSET_57600    0x04
//                PARMSET_115200   0x06
//
void SetBaudCOM(int portnum, uchar new_baud)
{
   int reload_value;
	switch (new_baud)
	{
		case PARMSET_9600:
			reload_value = BAUD9600_TIMER_RELOAD_VALUE;
			break;
		case PARMSET_19200:
			reload_value = BAUD19200_TIMER_RELOAD_VALUE;
			break;
		case PARMSET_57600:
			reload_value = BAUD57600_TIMER_RELOAD_VALUE;
			break;
		case PARMSET_115200:
			reload_value = BAUD115200_TIMER_RELOAD_VALUE;
			break;
		default:
		   return;
	}

#if USE_TIMER2_FOR_SERIAL0
   if(portnum==0)
   {
      TR2 = 0; // stop timer 2
      TL2 = RCAP2L = reload_value;
      TH2 = RCAP2H = reload_value>>8;
      TF2 = 0; // clear timer 2 overflow flag
      TR2 = 1; // start timer 2
   }
   else
   {
#else
      portnum = 0;
#endif
   	TR1 = 0; // stop timer 1
		TL1 = TH1 = reload_value;
		TF1 = 0; // clear timer 1 overflow flag
		TR1 = 1; // start timer 1
#if USE_TIMER2_FOR_SERIAL0
	}
#endif
}


//--------------------------------------------------------------------------
//  Description:
//     Delay for at least 'len' ms
//
void msDelay(int delay)
{
   while(delay--)
   {
      usDelay(1030);
   }
}

//--------------------------------------------------------------------------
//  Description:
//     Delay for at least 'len' microseconds
//
// Adapted from Kevin Vigor's TINI 1Wire code.
//
// Works only @33Mhz.  We need 8.25 machine cycles per iteration to get the microsecond
// delay.  Can't have .25 of a cycle, so this function uses 8 cycles per iteration. This
// produces an error of 3% ( .03 = (8.25-8)/8 ).  Most of the use of this function by
// 1-Wire routines calls for delays under 50 microseconds, in which case it is under by
// less than 1.5 microseconds.  It is assumed the overhead of calling the function will
// add enough to account for the difference.  For values much greater than 50 (only in
// reset function), a "fudge factor" is added to account for any error.
//
//
void usDelay(unsigned int delay)
{
#pragma asm
   ; delay is in r6/r7
   ; double-check in output assembly
   mov  a, r7
   orl  a, r6			; quick out for zero case.
   jz   _usDelayDone

   inc  r6
   cjne r7, #0, _usDelayLoop
   dec  r6

   _usDelayLoop:
   nop
   nop
   nop
   nop
   nop
   djnz r7, _usDelayLoop
   djnz r6, _usDelayLoop
   _usDelayDone:

#pragma endasm
   delay = 0;
}

//--------------------------------------------------------------------------
// Get the current millisecond tick count.  Does not have to represent
// an actual time, it just needs to be an incrementing timer.
//
long msGettick(void)
{
	return (long)TL1;
}



