/*********************************************************************
 *                  Flowcode RS232 Component Code
 *
 * File: PIC_RS232.c
 *
 * (c) 2009 Matrix Multimedia Ltd.
 * http://www.matrixmultimedia.com
 *
 * Software License Agreement
 *
 * The software supplied herewith by Matrix Multimedia Ltd (the
 * “Company”) for its Flowcode graphical programming language is
 * intended and supplied to you, the Company’s customer, for use
 * solely and exclusively on the Company's products. The software
 * is owned by the Company, and is protected under applicable
 * copyright laws. All rights are reserved. Any use in violation
 * of the foregoing restrictions may subject the user to criminal
 * sanctions under applicable laws, as well as to civil liability
 * for the breach of the terms and conditions of this licence.
 *
 * THIS SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION. NO WARRANTIES,
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
 * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
 * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *
 * Changelog:
 *
 *  date  | by | description
 * -------+----+-----------------------------------------------------
 * 230407 | ST | Created
 * 160108 | BR | Added Error messages
 * 260309 | BR | Moves private functions from AddDefines section
 * 260309 | BR | Added 7 & 9 bit functionality  and String functions
 * 170409 | BR | Modified parameter order for RX String function
 * 130509 | BR | Added % definitions to DefinesCode section
 * 100909 | ST | Fixed errors with RTS and CTS definitions (when used in hardware f/c mode)
 * 100909 | BR | Added HiTECH compatible defines
 * 161209 | BR | Fixed the 9-bit reception
 * 010211 | BR | Attempt to fix a bug on some 18F devices where data is not being transferred correctly
 * 230311 | BR | Changed code to work for hitech and boostc without ifdefs
 ********************************************************************/

/*********************************************************************

 Return & parameter types:
   void
   char
   short
   char*

 Pin directions:
   0 = OUTPUT
   2 = INPUT
   3 = BIDIRECTIONAL

**********************************************************************

[Settings]
CLSID={7A37AFCE-0B5A-4084-B3D3-C0FD2A50E165}
IsAnalogue=0
MultipleAllowed=1
Description=RS232 Flowcode Component

**********************************************************************

[Port]
DesiredPort=0
PortMandatory=0
SamePortAsID=0

[Pins]
Count=2
1=RX
2=TX

[PinPort]
# PORTA = 0
# PORTB = 1
# PORTC = 2
# PORTD = 3
# PORTE = 4
1=-1
2=-1

[PinDesiredBit]
1=0
2=1

[PinDirection]
# DIR_INPUT = 2
# DIR_OUTPUT = 0
# DIR_BIDIRECTIONAL = 3
1=2
2=0

[PinMustUsePort]
# boolean
1=1
2=1

[SamePortAsPinX]
# -1 = any pin
# otherwise, the pin number
1=-1
2=-1

[PinMustUsePin]
# boolean
1=1
2=1

[PinValue]
# boolean (0 = off, 1 = on)

**********************************************************************

[MacroNames]
Count=5
1=SendRS232Char
2=SendRS232String
3=ReceiveRS232Char
4=ReceiveRS232String
5=RS232_Delay

[MacroReturns]
1=void
2=void
3=short
4=char*
5=void

[MacroIsPrivate]
1=0
2=0
3=0
4=0
5=1


[MacroParameters_SendRS232Char]
Count=1
1=nChar

[MacroParamTypes_SendRS232Char]
1=short


[MacroParameters_SendRS232String]
Count=1
1=String

[MacroParamTypes_SendRS232String]
1=char*


[MacroParameters_ReceiveRS232Char]
Count=1
1=nTimeout

[MacroParamTypes_ReceiveRS232Char]
1=short


[MacroParameters_ReceiveRS232String]
Count=2
1=nTimeout
2=NumBytes

[MacroParamTypes_ReceiveRS232String]
1=char
2=char


[MacroParameters_RS232_Delay]
Count=1
1=mode

[MacroParamTypes_RS232_Delay]
1=char

 ********************************************************************/

/********************************************************************
 * ADDITIONAL CODE
 ********************************************************************/

/*DefinesCode_Start*/

/**** Macro Substitutions ****
%a = RTS Port
%b = RTS Data Direction
%c = CTS Port
%d = CTS Data Direction
%e = RTS Pin
%f = CTS Pin
%g = UART Selection		(0-SW / 1-UART1 / 2-UART2)
%h = Flow Control		(0-Off / 1-On)
%i = Debug Enable		(0-Off / 1-On)
%j = Echo Enable		(0-Off / 1-On)
%k = UART TXSTA Value
%l = UART SPBRG Value
%m = Unique ID
%n = Bitbanged Receive Port Register
%o = Bitbanged Receive Data Direction Register
%p = Bitbanged Receive Pin
%q = Bitbanged Transmit Pin
%r = Bitbanged BAUD Rate Delay
%s = Timout	Selection	(0-Legacy / 1-MS Timeout)
%t = Data Size			(0-8 bits / 1-9 bits / 2-7 bits & Only available on BitBanged components)
%u = Parity Enable		(0-No Parity / 1-Odd Parity / 2-Even Parity)
%v = Legacy Return		(0-Legacy mode return 255 / 1-New mode return MSB err flags)
%w = Bitbanged Transmit Port Register
%x = Bitbanged Transmit Data Direction Register
******************************/

#define %m_RTS_PORT			%a
#define %m_RTS_TRIS			%b
#define %m_CTS_PORT			%c
#define %m_CTS_TRIS			%d
#define %m_RTS_PIN			%e
#define %m_CTS_PIN			%f
#define %m_UART				%g
#define %m_TOUT  			%s
#define %m_DATASIZE			%t
#define %m_PARITY			%u
#define %m_LEGACY_RV			%v

#if (%h == 1)
  	#define %m_HARDWARE
#endif

#if (%i == 1)
  	#define %m_DEBUG
#endif

#if (%j == 1)
  	#define %m_ECHO
#endif

#if (%m_UART == 0)
	#define %m_RX_PORT		%n
	#define %m_RX_TRIS		%o
	#define %m_SW_RX		%p
	#define %m_SW_TX		%q
	#define %m_SW_BAUD		%r
	#define %m_TX_PORT		%w
	#define %m_TX_TRIS		%x
#else
	#define %m_TXSTA_VAL  	%k
	#define %m_SPBRG_VAL  	%l
	#define %m_SW_BAUD		0
#endif

#if (%m_LEGACY_RV == 0)
	#if (%m_DATASIZE == 1)
		#error "Chip does not have second UART capability"
	#endif
#endif

#define %m_STATUS_LOOP		0
#define %m_STATUS_TIMEOUT	1
#define %m_STATUS_RXBYTE	2


/*DefinesCode_End*/

/*InitialisationCode_Start*/

#if (%m_UART == 0)
	set_bit(%m_RX_TRIS, %m_SW_RX);				// Receive pin is a input
	clear_bit(%m_TX_TRIS, %m_SW_TX);			// Transmit pin is a output
	set_bit(%m_TX_PORT, %m_SW_TX);				// Transmit pin is default high
#endif

#if (%m_UART == 1)
	txsta = %m_TXSTA_VAL;   					// 8-bit, async, low speed, off
	spbrg = %m_SPBRG_VAL;   					// set the baud rate
	rcsta = 0;                    				// 8-bit, disabled

	if(%m_DATASIZE == 1)
	{
		st_bit(txsta, TX9);   					// 9-bit TX
		st_bit(rcsta, RX9);                    	// 9-bit RX
	}

	st_bit(rcsta, SPEN);         				// turn on serial interface
	st_bit(txsta, TXEN);
	st_bit(rcsta, CREN);
#endif

#if (%m_UART == 2)

	txsta2 = %m_TXSTA_VAL;   					// 8-bit, async, low speed, off
	spbrg2 = %m_SPBRG_VAL;   					// set the baud rate
	rcsta2 = 0;                    				// 8-bit, disabled

	if(%m_DATASIZE == 1)
	{
		st_bit(txsta2, TX9);   					// 9-bit TX
		st_bit(rcsta2, RX9);                    // 9-bit RX
	}

	st_bit(rcsta2, SPEN);         				// turn on serial interface
	st_bit(txsta2, TXEN);
	st_bit(rcsta2, CREN);
#endif

#ifdef %m_HARDWARE
	set_bit( %m_CTS_TRIS, %m_CTS_PIN);			//CTS is an input
	clear_bit( %m_RTS_TRIS, %m_RTS_PIN);		//RTS is an output
	set_bit( %m_RTS_PORT, %m_RTS_PIN); 			//not ready to accept data
#endif

/*InitialisationCode_End*/

/*InterruptCode_Start*/
/*InterruptCode_End*/


/********************************************************************
 * FUNCTIONS
 ********************************************************************/


void SendRS232Char(short nChar)
{
/*Macro_SendRS232Char_Start*/

#if (%m_UART == 0)

	char dMask;
	char idx;
	char count = 8;

	#ifdef %m_HARDWARE
		//wait until CTS is low
		while (( %m_CTS_PORT & (1 << %m_CTS_PIN) ) != 0);
	#endif

	#if(%m_DATASIZE == 1)
		count = 9;
	#endif
	#if(%m_DATASIZE == 2)
		count = 7;
	#endif

	clear_bit( %m_TX_PORT, %m_SW_TX);			// Send Start bit
	RS232_Delay(0);

	for (idx = 0; idx < count; idx++)
	{
		dMask = nChar & 0x01;					// Mask off data bit

		if (dMask)
			set_bit( %m_TX_PORT, %m_SW_TX);
		else
			clear_bit( %m_TX_PORT, %m_SW_TX);

		RS232_Delay(0);
		nChar = nChar >> 1;						// Move to next data bit
	}

	set_bit( %m_TX_PORT, %m_SW_TX);				// Send Stop bit
	RS232_Delay(0);

#endif

#if (%m_UART == 1)

	#ifndef MX_UART
		#error "Chip does not have second UART capability"
	#endif

	while (ts_bit(pir1, TXIF) == 0);

	#ifdef %m_HARDWARE
		//wait until CTS is low
		while (( %m_CTS_PORT & (1 << %m_CTS_PIN) ) != 0);
	#endif

	#if(%m_DATASIZE == 1)
		if (test_bit(nChar, 8))
			st_bit(txsta, TX9D);
		else
			cr_bit(txsta, TX9D);
	#endif

	txreg = nChar;

#endif

#if (%m_UART == 2)

	#ifndef MX_UART2
		#error "Chip does not have second UART capability"
	#endif

	while (ts_bit(pir3, TX2IF) == 0);

	#ifdef %m_HARDWARE
		//wait until CTS is low
		while (( %m_CTS_PORT & (1 << %m_CTS_PIN) ) != 0);
	#endif

	#if( %m_DATASIZE == 1)
		if (test_bit(nChar, 8))
			st_bit(txsta2, TX9D);
		else
			cr_bit(txsta2, TX9D);
	#endif

	txreg2 = nChar;

#endif

/*Macro_SendRS232Char_End*/
}


void SendRS232String(char* String, char MSZ_String)
{
/*Macro_SendRS232String_Start*/
	char idx;
	for(idx = 0; idx < MSZ_String; idx++)
	{
		if (String[idx] == 0)
			break;
		else SendRS232Char(String[idx]);
	}
/*Macro_SendRS232String_End*/
}


short ReceiveRS232Char(char nTimeout)
{
/*Macro_ReceiveRS232Char_Start*/

	char delay1 = 0;
	char delay2 = 0;
	char regcheck = 0;
	char dummy = 0;
	short retVal = 512;
	char bWaitForever = 0;
	char rxStatus = %m_STATUS_LOOP;
	char idx;
	char count = 8;

	#if ( %m_LEGACY_RV == 0)
		retVal = 255;
	#endif

	#if ( %m_UART == 1)
		#ifndef MX_UART
			#error "Chip does not have UART1 capability"
		#endif
	#endif

	#if ( %m_UART == 2)
		#ifndef MX_UART2
			#error "Chip does not have UART2 capability"
		#endif
	#endif

	#ifdef %m_HARDWARE
		//ready to accept data
		clear_bit( %m_RTS_PORT, %m_RTS_PIN);
	#endif

	if (nTimeout == 255)
		bWaitForever = 1;

	while (rxStatus == %m_STATUS_LOOP)
	{
		if (bWaitForever == 0)
		{
			//don't wait forever, so do timeout thing...
			if (nTimeout == 0)
			{
				rxStatus = %m_STATUS_TIMEOUT;
			}
			else
			{
				if ( %m_TOUT)
				{
					delay_us(10);
					delay1 = delay1 + 1;
					if(delay1 == 100)
					{
						nTimeout = nTimeout - 1;
						delay1 = 0;
					}
				}
				else
				{
					//decrement timeout
					delay1 = delay1 - 1;
					if (delay1 == 0)
					{
						nTimeout = nTimeout - 1;
					}
				}
			}
		}

		#if ( %m_UART == 0)
			regcheck = test_bit(%m_RX_PORT, %m_SW_RX);	//Test for start bit
			if (regcheck == 0)
				rxStatus = %m_STATUS_RXBYTE;
		#endif

		#if ( %m_UART == 1)
			regcheck = ts_bit(pir1, RCIF);
			if (regcheck != 0)
				rxStatus = %m_STATUS_RXBYTE;
		#endif

		#if ( %m_UART == 2)
			regcheck = ts_bit(pir3, RC2IF);
			if (regcheck != 0)
				rxStatus = %m_STATUS_RXBYTE;
		#endif
	}

	if (rxStatus == %m_STATUS_RXBYTE)
	{
		#if ( %m_UART > 0)

			#if ( %m_UART == 1)
				regcheck = ts_bit(rcsta, FERR);
			#endif

			#if ( %m_UART == 2)
				regcheck = ts_bit(rcsta2, FERR);
			#endif

			if (regcheck != 0)
			{
				#if ( %m_UART == 1)
					dummy = rcreg;      //need to read the rcreg to clear FERR
				#endif

				#if ( %m_UART == 2)
					dummy = rcreg2;      //need to read the rcreg to clear FERR
				#endif

				#ifdef %m_DEBUG
					FCD_RS2320_SendRS232Char('<');
					FCD_RS2320_SendRS232Char('F');
					FCD_RS2320_SendRS232Char('E');
					FCD_RS2320_SendRS232Char('R');
					FCD_RS2320_SendRS232Char('R');
					FCD_RS2320_SendRS232Char('>');
				#endif

				#if ( %m_LEGACY_RV == 1)
					retVal = 0x400;					//Framing Error Flag
				#endif
			}
			else
			{
				#if ( %m_UART == 1)
					regcheck = ts_bit(rcsta, OERR);
				#endif

				#if ( %m_UART == 2)
					regcheck = ts_bit(rcsta2, OERR);
				#endif

				if (regcheck != 0)
				{
					//need to read the rcreg to clear error
					#if ( %m_UART == 1)
						cr_bit(rcsta, CREN);
						st_bit(rcsta, CREN);
					#endif

					#if ( %m_UART == 2)
						cr_bit(rcsta2, CREN);
						st_bit(rcsta2, CREN);
					#endif

					#ifdef %m_DEBUG
						FCD_RS2320_SendRS232Char('<');
						FCD_RS2320_SendRS232Char('O');
						FCD_RS2320_SendRS232Char('E');
						FCD_RS2320_SendRS232Char('R');
						FCD_RS2320_SendRS232Char('R');
						FCD_RS2320_SendRS232Char('>');
					#endif

					#if ( %m_LEGACY_RV == 1)
						retVal = 0x800;					//Overrun Error Flag
					#endif

				}
				else
				{
					retVal = 0;

					#if ( %m_UART == 1)

						#if( %m_DATASIZE == 1)
							if(ts_bit(rcsta, RX9D));
								retVal = 0x100;
						#endif
						retVal = retVal | rcreg; 			//no error, so rx byte is valid

					#endif

					#if ( %m_UART == 2)

						#if( %m_DATASIZE == 1)
							if(ts_bit(rcsta2, RX9D));
								retVal = 0x100;
						#endif
						retVal = retVal | rcreg2; 			//no error, so rx byte is valid

					#endif

					#ifdef %m_ECHO
						SendRS232Char(retVal);
					#endif
				}
			}
		#else
			#if( %m_DATASIZE == 1)
				count = 9;
			#endif

			RS232_Delay(1);
			for (idx = 0; idx < count; idx++)
			{
				retVal = retVal >> 1;

				if(count == 9)
				{
					if (test_bit( %m_RX_PORT, %m_SW_RX))
						retVal = retVal | 0x100;
				}
				else
				{
					if (test_bit( %m_RX_PORT, %m_SW_RX))
						retVal = retVal | 0x80;
				}
				RS232_Delay(1);
			}

			#ifdef %m_ECHO
				SendRS232Char(retVal);
			#endif

		#endif
	}
	#ifdef %m_HARDWARE
		//not ready to accept data
		set_bit( %m_RTS_PORT, %m_RTS_PIN);
	#endif

	return (retVal);
/*Macro_ReceiveRS232Char_End*/
}


void ReceiveRS232String(char* FCR_RETVAL, char FCR_RETVAL_SIZE, char nTimeout, char NumBytes)
{
/*Macro_ReceiveRS232String_Start*/
	char idx;
	short in;

	#if ( %m_LEGACY_RV == 0 )
		#define RS232_TO 	255
	#else
		#define RS232_TO 	256
	#endif

	if (NumBytes > FCR_RETVAL_SIZE)
		NumBytes = FCR_RETVAL_SIZE;

	for (idx = 0; idx < NumBytes; idx++)
	{
		in = ReceiveRS232Char(nTimeout);
		if(in < RS232_TO)
			FCR_RETVAL[idx] = in & 0xFF;
		else
			break;
	}

	if (idx < FCR_RETVAL_SIZE)
		FCR_RETVAL[idx] = 0;

	#undef RS232_TO
/*Macro_ReceiveRS232String_End*/
}


void RS232_Delay(char mode)
{
/*Macro_RS232_Delay_Start*/
	unsigned int iterations;
	unsigned int delay = %m_SW_BAUD;

	if (mode)
		delay = delay + 1;

	for (iterations = 0; iterations < delay; iterations++);
/*Macro_RS232_Delay_End*/
}
