/*
* $Header: c:/cvsroot/bart/rt_serial.c,v 1.5 2004/03/10 20:13:46 mjames Exp $
RCS Log file
$Log: rt_serial.c,v $
Revision 1.5 2004/03/10 20:13:46 mjames
Correcting hard flow
Revision 1.4 2004/03/09 00:45:20 mjames
Corrected mistakes, made task numbers visible
Revision 1.3 2004/03/06 12:17:48 mjames
Moved headers around, made it clearer that there are no configurable
parts to the OS unless it is rebuilt
Revision 1.2 2004/03/04 21:52:59 mjames
Made the files work with a demo project
Revision 1.1.1.1 2004/03/03 22:54:33 mjames
no message
*/
/*******************
* INCLUDE FILES *
********************/
#include "mcs51reg.h"
#include "rt_int.h"
#include "rt_ext.h"
#define SIO1_RATIO (PRESCALE/SIO1_BAUD)
#define SIO1_BAUDCODE (255-SIO1_RATIO)
/** compute baud timing for T2, an independent UART */
#define SIO2_RATIO (PRESCALE2/SIO2_BAUD)
/** after 1->0 transition on T2EX then wait 1/2bit, check = 0
then wait 1 bit , shift in bit 0 and so on.... */
#define SIO2_BAUDCODE_START (65535-(SIO2_RATIO/2))
#define SIO2_BAUDCODE_BIT (65535-(SIO2_RATIO))
#if SIO2_BAUDCODE_START <=0
#error SIO2 baudcode out of range
#endif
/* phone buffer size */
#define SIO1_TX_FIFOBITS 7 /* Phone Tx may have quite a few chars in the buffer */
#define SIO1_RX_FIFOBITS 5 /* Expect to buffer fewer chars incoming */
#define SIO2_RX_FIFOBITS 6 /* Second recieve buffer */
#define SIO2_TX_FIFOBITS 5 /* Second transmit buffer */
#if SIO1_TX_FIFOBITS > 7
#error Max value of SIO1_TX_FIFOBITS is 7
#endif
#if SIO1_RX_FIFOBITS > 7
#error Max value of SIO1_RX_FIFOBITS is 7
#endif
/********************************************************************************/
/* Setup fifo for Phone Writing */
/********************************************************************************/
#define SIO1_TX_FIFOSIZE (1<<SIO1_TX_FIFOBITS)
/* determine the byte count limit : 1<<8 == 0 in byte arithmetic :-) */
#if SIO1_TX_FIFOBITS == 8
#define SIO1_TX_FIFOFULL (SIO1_TX_FIFOSIZE-1)
#else
#define SIO1_TX_FIFOFULL SIO1_TX_FIFOSIZE
#endif
#define SIO1_TX_FIFOMASK (SIO1_TX_FIFOSIZE-1)
#define SIO1_TX_FIFO_LWM (SIO1_TX_FIFOSIZE/2)
/********************************************************************************/
/* Setup fifo for Phone Reading */
/********************************************************************************/
#define SIO1_RX_FIFOSIZE (1<<SIO1_RX_FIFOBITS)
/* determine the byte count limit : 1<<8 == 0 in byte arithmetic :-) */
#if SIO1_RX_FIFOBITS == 8
#define SIO1_RX_FIFOFULL (SIO1_RX_FIFOSIZE-1)
#else
#define SIO1_RX_FIFOFULL SIO1_RX_FIFOSIZE
#endif
#define SIO1_RX_FIFOMASK (SIO1_RX_FIFOSIZE-1)
#define SIO1_RX_FIFO_LWM (SIO1_RX_FIFOSIZE/4) /* Low Water Mark for Xon/Xoff flow control */
#define SIO1_RX_FIFO_HWM (SIO1_RX_FIFOSIZE/2) /* High Water Mark for Xon/Xoff flow control */
/********************************************************************************/
/* Setup fifo for SIO2 reading */
/********************************************************************************/
#define SIO2_RX_FIFOSIZE (1<<SIO2_RX_FIFOBITS)
/* determine the byte count limit : 1<<8 == 0 in byte arithmetic :-) */
#if SIO2_RX_FIFOBITS == 8
#define SIO2_RX_FIFOFULL (SIO2_RX_FIFOSIZE-1)
#else
#define SIO2_RX_FIFOFULL SIO2_RX_FIFOSIZE
#endif
#define SIO2_RX_FIFOMASK (SIO2_RX_FIFOSIZE-1)
#define SIO2_RX_FIFO_LWM (SIO2_RX_FIFOSIZE/4) /* Low Water Mark for Xon/Xoff flow control */
#define SIO2_RX_FIFO_HWM (SIO2_RX_FIFOSIZE/2) /* High Water Mark for Xon/Xoff flow control */
/*******************
* LOCAL TYPEDEFS *
********************/
/* this code is for the main uart of the 8051 */
static volatile char SIO1_TXFIFO[SIO1_TX_FIFOSIZE]; /* Data FIFO for transmit data */
static volatile char SIO1_RXFIFO[SIO1_RX_FIFOSIZE]; /* Data FIFO for receive data */
#if defined SIO2_TX_EN
static volatile char SIO2_TXFIFO2[SIO2_TX_FIFOSIZE]; /* Data FIFO for transmit2 data */
#endif
static volatile char SIO2_RXFIFO[SIO2_RX_FIFOSIZE]; /* Data FIFO for receive 2 data */
static volatile unsigned char SIO1_TxPtr; /**< next byte in FIFO to be transmitted */
volatile unsigned char SIO1_TxCount; /**< num of un-transmitted byted in FIFO */
volatile Bool SIO1_TxBusy; /**< TRUE when UART is transmitting */
static volatile Byte SIO1_RxPtr; /**< next byte to fetch from Rx buffer */
static volatile Byte SIO1_RxCount; /**< num of un-readin bytes in FIFO */
#if defined SOFT_FLOW
static volatile Bool SIO1_TxOn; /**< true if we think we are able to transmit, i.e.
* we haven't (yet) recieved an XOFF from host */
static volatile Bool SIO1_RxOn; /**< true if we think we are ready to receive, i.e. *
* we haven't tried to stop the host with an XOFF */
static Byte SIO1_FlowChar; /* if this is non-zero, then send this char in place */
/* of the byte queued in the FIFO (used for XON/XOFF) */
#endif
/* this is storage for the second virtual uart */
static volatile Byte SIO2_RxPtr; /* next byte to fetch from Rx buffer */
volatile Byte SIO2_RxCount; /* num of un-readin bytes in FIFO */
#if defined SIO2_TX_EN
static volatile Byte SIO2_TxPtr; /* next byte to send from Tx buffer */
static volatile Byte SIO2_TxCount; /* num of un-sent bytes in FIFO
(non zero, transmitter enabled) */
#endif
/* these counters are shared between transmit and recieve on the SIO2 */
static volatile Byte SIO2_Byte;
volatile Byte SIO2_BitCnt;
static volatile Bool SIO_put_semaphore;
/********************************************************************************
* Name : rt_serial_init
* This function sets up all of the timers for the serial ports
*
*******************************************************************************/
void rt_serial_init (void)
{
SIO1_TxPtr = 0;
SIO1_RxPtr = 0;
SIO1_TxCount = 0;
SIO1_RxCount = 0;
SIO1_TxBusy = FALSE;
SIO1_TxPtr = 0;
SIO2_RxPtr = 0;
SIO2_RxCount = 0;
#if defined SIO2_TX_EN
SIO2_TxPtr = 0;
SIO2_TxCount = 0;
#endif
#if defined SOFT_FLOW
SIO1_FlowChar = 0;
SIO1_TxOn = TRUE;
SIO1_RxOn = TRUE;
#endif
#if defined HARD_FLOW
SIO1_CTS = FALSE; /* assert CTS */
#endif
SIO_put_semaphore = TRUE;
TR1 = FALSE;
SCON= 0x50; /* select mode 1 (8bit variable baudrate) */
TH1 = SIO1_BAUDCODE; /* set timer 1 for SIO1 baud */
TL1 = SIO1_BAUDCODE; /* set timer 1*/
TR1 = TRUE; /* set timer 1 running */
/* initialise T2EX to generate a falling edge interrupt and
use timer 2 as an SIO baud rate timer for the second soft UART */
T2CON = 0; /* 16 bit auto reload */
EXEN2 = TRUE; /* allow for falling edge IRQ */
ET2 = TRUE; /*TURNED OFF to allow further debug allow for T2 interrupt */
TR2 = TRUE; /* only edges now */
PT2 = TRUE; /* up priority */
IPH |=PT2H;
RCAP2H = SIO2_BAUDCODE_START / 256; /* setup for 4800 baud */
RCAP2L = SIO2_BAUDCODE_START & 255; /* start bit */
REN = TRUE; // Receive characters.
/* PS = TRUE; */ // Low priority.
ES = TRUE;
#if defined HARD_FLOW && defined SIMULATOR_BUILD
SIO1_RTS = FALSE; /* enable output by forcing ready status*/
#endif
}
/********************************************************************************
* Name : SIOInterrupt
* This function handles the RI and TI Interrupts from the SIO on the 8051
*
*******************************************************************************/
void SIOInterrupt(void) interrupt SIO_INTVEC using SIO_INTERRUPT_BANK
{
/*
* do
* {
*/
if(RI) /* receive handler */
{
Byte Chr;
RI = 0;
Chr = SBUF;
#if defined SOFT_FLOW
if (Chr == XON)
{
SIO1_TxOn = TRUE;
if (!SIO1_TxBusy)
{
TI=1;
}
}
else if (Chr == XOFF)
{
SIO1_TxOn = FALSE;
}
else /* char was not an xon/xoff */
#endif
{
#if defined SOFT_FLOW
if ((SIO1_RxCount >= (Byte) SIO1_RX_FIFO_HWM) && SIO1_RxOn)
{
SIO1_RxOn = FALSE;
SIO1_FlowChar = XOFF; /* buffer is filling, try and shutup host */
if (!SIO1_TxBusy) TI = 1; /* force the XOFF to be sent */
}
#endif
#if defined HARD_FLOW
if (SIO1_RxCount >= SIO1_RX_FIFOFULL-1)
{
SIO1_CTS = TRUE;
}
#endif
if(SIO1_RxCount != SIO1_RX_FIFOFULL) /* is there room left in the Rx FIFO ? */
{
Byte TempPtr = (SIO1_RxPtr+SIO1_RxCount) & SIO1_RX_FIFOMASK;
SIO1_RXFIFO[TempPtr] = Chr;
SIO1_RxCount++;
}
}
#if defined SIO1_TASK
INT_SIGNAL(SIO1_TASK,SERIAL_SIG);
#endif
}
if(TI) /* handle transmit interrupt */
{
TI = 0;
#if defined SOFT_FLOW
if(SIO1_FlowChar)
{
SBUF = SIO1_FlowChar; /* a flow control char to be sent pre-empts the */
SIO1_TxBusy = TRUE; /* normal fifo content transmission */
SIO1_FlowChar = 0;
}
else
#endif
{
#if defined SOFT_FLOW
if(SIO1_TxOn && SIO1_TxCount)
#else
#if defined HARD_FLOW
if(!SIO1_RTS && SIO1_TxCount)
#else
if(SIO1_TxCount)
#endif
#endif
{ register char c;
SIO1_TxCount--;
c = SIO1_TXFIFO[SIO1_TxPtr++]; /* send next char if there is one, and */
SIO1_TxPtr &= SIO1_TX_FIFOMASK;
SIO1_TxBusy = TRUE; /* flow has not been stopped */
SBUF = c;
}
else
{
SIO1_TxBusy = FALSE;
}
}
}
}
/********************************************************************************
** Name : T2Interrupt
* Timer 2 uses its T2EX pin as a falling edge IRQ pin for incoming data
** It also can transmit data in a half-duplex mode
*******************************************************************************/
void T2Interrupt(void) interrupt T2_INTVEC using T2_INTERRUPT_BANK
{
/* Falling edge interrupt */
if (EXF2)
{
EXF2 = FALSE;
TF2 = FALSE;
EXEN2 = FALSE; /* turn off the ext2 IRQ */
// TH2 = SIO2_BAUDCODE_START / 256; /* setup for 4800 baud */
// TL2 = SIO2_BAUDCODE_START & 255; /* start bit */
RCAP2H = SIO2_BAUDCODE_BIT / 256;
RCAP2L = SIO2_BAUDCODE_BIT & 255; /* set timer 2 for 4800 baud */
SIO2_BitCnt = 0; /* count up incl start bit to 8 */
#if defined SIO2_TX_EN
Rx_Run2 = TRUE;
#endif
}
/* timeout ? */
if (TF2)
{
TF2 = FALSE;
#if defined SIO2_TX_EN
/* TRANSMIT USAGE OF T2 */
/* interrupt with no reciever running now must be transmit */
if(!SIO2_Rx_Run)
{
if(SIO2_BitCnt == 0)
{
/* load next Tx byte */
if(SIO2_TxCount)
{
SIO2_Byte = SIO2_FIFO[SIO2_TxPtr]; /* send next char if there is one, and */
SIO2_TxPtr2++;
TxPtr2 &= TX2_FIFOMASK;
TxCount2--;
}
}
else if (SIO2_BitCnt == 8)
{
SIO2_OUTPIN = TRUE;
SIO2_BitCnt++;
}
else if (SIO2_BitCnt == 9)
{ /* finished stop bit : any more bytes to send ? */
if (TxCount2)
{ /* If so preserve the preload values and continue with the start bit */
Ch2BitCnt = 0;
SIO2_OUTPIN = FALSE:
}
else
{ /* reenable reciever */
Rx_Run2 = TRUE;
}
}
else
{
SIO2_OUTPIN = SIO2_Byte & 1;
SIO2_Byte >>= 1;
SIO2_BitCnt++;
}
}
/* END OF TRANSMIT USAGE */
else
{
#endif
if(!EXEN2)
{
if(SIO2_BitCnt == 0)
{
if( SIO2_RXD != 0)
{
/* Start bit preload is due to EX2 falling, check */
RCAP2H = SIO2_BAUDCODE_START / 256; /* setup for 4800 baud */
RCAP2L = SIO2_BAUDCODE_START & 255; /* start bit */
EXF2 = FALSE;
EXEN2 = TRUE;
TR2 = TRUE; /* Keep running counter */
#if defined SIO2_TX_EN
SIO2_Rx_Run = FALSE; /* not recieving at present */
#endif
}
else
{
SIO2_BitCnt++;
}
}
else
{
SIO2_Byte = (SIO2_Byte >> 1) | (SIO2_RXD << 7);
if (SIO2_BitCnt==8 )
{
if(SIO2_RxCount != SIO2_RX_FIFOFULL) /* is there room left in the Rx FIFO ? */
{
Byte TempPtr = (SIO2_RxPtr+SIO2_RxCount) & SIO2_RX_FIFOMASK;
SIO2_RXFIFO[TempPtr] = SIO2_Byte;
SIO2_RxCount++;
}
#if defined SIO2_TASK
INT_SIGNAL(SIO2_TASK,SERIAL_SIG);
#endif
}
if(SIO2_BitCnt <8 )
{
SIO2_BitCnt++;
}
else /* stop bit */
{
#if defined SIO2_TX_EN
SIO2_Rx_Run = FALSE; /* not recieving at present */
/* go straight to start Tx if anything to send now */
if(SIO2_TxCount)
{
EXEN2 = FALSE; /* turn off the ext2 IRQ */
SIO2_BitCnt = 0; /* count up incl start bit to 8 */
SIO2_TXD = FALSE;
}
else
#endif
{
RCAP2H = SIO2_BAUDCODE_START / 256; /* setup for 4800 baud */
RCAP2L = SIO2_BAUDCODE_START & 255; /* start bit */
EXF2 = FALSE;
EXEN2 = TRUE;
TR2 = TRUE; /* Keep running counter */
}
}
}
}
}
}
/********************************************************************************
*
* Description
* This is the putchar routine that tries to fill the SIO transmit FIFO.
* If the FIFO is full it waits for a FIFO becoming less than full signal
* it then places a char in the buffer and uses the set TI flag trick
* to force a transmit interrupt.
*
**************************************************************************/
#pragma save
#pragma noinduction
{
USE_CRITICAL;
BEGIN_CRITICAL;
while(SIO1_TxCount == SIO1_TX_FIFOFULL)
{
/* STORE_A16 = 0; */
END_CRITICAL;
RESCHEDULE;
/* STORE_A16 = 1; */
BEGIN_CRITICAL;
}
SIO1_TXFIFO[(SIO1_TxPtr+SIO1_TxCount) & SIO1_TX_FIFOMASK] = c; /* place character into buffer */
SIO1_TxCount++;
END_CRITICAL;
if (!SIO1_TxBusy)
{
TI=1;
}
#if defined USE_CRLF_MAP
if (c==LF) /* handle LF -> LF,CR mapping */
{
c = CR;
while(SIO1_TxCount == SIO1_TX_FIFOFULL)
{
RESCHEDULE; /* wait for space in the FIFO */
}
BEGIN_CRITICAL;
SIO1_TXFIFO[(SIO1_TxPtr+SIO1_TxCount) & SIO1_TX_FIFOMASK] = c; /* place character into buffer */
SIO1_TxCount++;
END_CRITICAL;
if (!SIO1_TxBusy)
{
TI=1;
}
}
#endif
}
#pragma restore
/**********************************************************************************
Transmit character on SIO2. If there is nothing being recieved then we take
over the T2 interrupt immediately. If there is something being recieved then
wait until this character has been recieved, and on final Rx T2 IRQ switch to
transmit T2 IRQ and send a start bit.
***********************************************************************************/
#if defined SIO2_TX_EN
void SIO2_putchar(char c)
{
USE_CRITICAL;
while(SIO2_TxCount == SIO2_TX_FIFOFULL)
{
RESCHEDULE; /* wait for space in the FIFO */
}
BEGIN_CRITICAL;
SIO2_TxFIFO[(SIO2_TxPtr+SIO2_TxCount) & SIO2_TX_FIFOMASK] = c; /* place character into buffer */
SIO2_TxCount2++;
/* if the reciever is not running any more, start the transmitter interrupts */
if (!SIO2_Rx_Run)
{
EXEN2 = FALSE; /* turn off the ext2 IRQ */
TF2 = FALSE;
TR2 = FALSE;
T2H = SIO2_BAUDCODE_BIT >> 8 ;
T2L = SIO2_BAUDCODE_BIT & 255;
RCAP2H = SIO2_BAUDCODE_BIT >> 8;
RCAP2L = SIO2_BAUDCODE_BIT & 255; /* set timer 2 for 4800 baud */
TR2 =TRUE;
SIO2_BitCnt = 0; /* count up incl start bit to 8 */
SIO2_TXD = FALSE;
}
END_CRITICAL;
}
#endif
/** This is the getchar routine that tries to empty the receive FIFO for SIO1
* If the FIFO is empty then it will wait until there is a character in the buffer
* it then removes a character from the buffer.
* if the buffer empties below the FIFO_LWM and the RxOn flag is clear,
* force the transmitter to send an XON.
*/
{
Byte Chr;
USE_CRITICAL;
while (!SIO1_RxCount)
{
RESCHEDULE;
}
BEGIN_CRITICAL;
Chr = SIO1_RXFIFO[SIO1_RxPtr]; /* get next char out of RxFIFO */
SIO1_RxPtr++;
SIO1_RxPtr &= SIO1_RX_FIFOMASK;
SIO1_RxCount--;
SIO1_RxCount &= SIO1_RX_FIFOMASK;
#if defined SOFT_FLOW
if((SIO1_RxCount <= (Byte) SIO1_RX_FIFO_LWM) && !SIO1_RxOn) /* is reception is off, and is FIFO */
{ /* empty enough to allow a restart ?? */
SIO1_RxOn = TRUE;
SIO1_FlowChar = XON;
if (!SIO1_TxBusy) TI = 1; /* force the XON char to be sent */
}
#endif
#if defined HARD_FLOW
if(SIO1_RxCount <= (Byte) SIO1_RX_FIFO_LWM) /* when space in FIFO restart */
{
SIO1_CTS = FALSE;
}
#endif
END_CRITICAL;
return Chr;
}
/** This is the getchar routine that tries to empty the receive FIFO for SIO2
* If the FIFO is empty then it will wait until there is a character in the buffer
* it then removes a character from the buffer.
*/
char SIO2_getchar(void)
{
USE_CRITICAL;
Byte Chr;
while (!SIO2_RxCount)
{
RESCHEDULE;
}
BEGIN_CRITICAL;
Chr = SIO2_RXFIFO[SIO2_RxPtr]; /* get next char out of RxFIFO */
SIO2_RxPtr++;
SIO2_RxPtr &= SIO2_RX_FIFOMASK;
SIO2_RxCount--;
SIO2_RxCount &= SIO2_RX_FIFOMASK;
END_CRITICAL;
return Chr;
}
/********************************************************************************
* Name : SIO_pollchar
*
* Module : SIO
*
* Returns : the number of characters in the SIO recieve buffer
*
*******************************************************************************/
char SIO1_pollchar(void)
{
return SIO1_RxCount;
}
char SIO2_pollchar(void)
{
return SIO2_RxCount;
}
void SIO1_flush(void)
{
USE_CRITICAL;
BEGIN_CRITICAL;
SIO1_RxPtr = 0;
SIO1_RxCount = 0;
END_CRITICAL;
}
void SIO2_flush(void)
{
USE_CRITICAL;
BEGIN_CRITICAL;
SIO2_RxPtr = 0;
SIO2_RxCount = 0;
END_CRITICAL;
}
/********************************************************************************/
/** this drives the SIO1_TXD line to level for a number of ticks (USE 2 OR MORE ticks)
* timed by the T0 interrupt
* This function needs to be bracketed by a claim on the SIO semaphore. */
/********************************************************************************/
void SIO1_break(char level,unsigned char ticks)
{
/* wait forTx buffer to clear */
while(SIO1_TxCount)
{
RESCHEDULE;
};
SIO1_TXD = level;
sleep(ticks);
SIO1_TXD = 1;
}
/********************************************************************************/
#pragma save
#pragma noinduction
/** This function sends a string from xdata removing the inefficiency of
* generic pointers */
void SendStringCode(code char * s)
{
char c;
if (!s) return;
while(*s)
{
c= *s++;
}
}
/** This function sends a string from xdata removing the inefficiency of
* generic pointers */
void SendStringXdata(xdata char * s)
{
char c;
if (!s) return;
while(*s)
{
c= *s++;
}
}
#pragma restore
void SendHex(unsigned char x)
{
unsigned char p;
p = x / 16;
putchar(p
>15?'-':p
>9?p
+'A'-10:p
+'0');
p = x & 15;
putchar(p
>15?'-':p
>9?p
+'A'-10:p
+'0');
}
#pragma save
#pragma noinduction
/** This is the semaphore used throughout for claiming the serial port. It also
has a busy wait if there are no ready to run tasks */
void SIOTakeSemaphore(void)
{
USE_CRITICAL;
while(1){
BEGIN_CRITICAL;
if(SIO_put_semaphore)
{
SIO_put_semaphore = FALSE;
END_CRITICAL;
return;
}
else
{
END_CRITICAL;
RESCHEDULE;
}
}
}
void SIOPutSemaphore(void)
{
SIO_put_semaphore = TRUE;
}
char SIOSetSemaphore(Bool value)
{
USE_CRITICAL;
char rc;
BEGIN_CRITICAL;
rc= SIO_put_semaphore;
SIO_put_semaphore = value;
END_CRITICAL;
return rc;
}
#pragma restore