📄 serial.c
字号:
portSHORT sIn;
unsigned portLONG ulDivisor;
unsigned portCHAR usDivisorLow;
unsigned portCHAR usDivisorHigh;
unsigned portCHAR ucCommParam;
/* IRQ numbers - standard */
if( ( ePort == serCOM1 ) || ( ePort == serCOM3 ) || ( ePort == serCOM5 ) || ( ePort == serCOM7 ) )
{
pxPort->ucIRQ = serCOM1_STANDARD_IRQ;
pxPort->sPort = 0x3f8;
}
else
{
pxPort->ucIRQ = serCOM2_STANDARD_IRQ;
pxPort->sPort = 0x2f8;
}
/* Set up variables in port making it easy to see which sIn/o address is which */
pxPort->usTransmitHoldReg = pxPort->sPort + serTRANSMIT_HOLD_OFFSET;
pxPort->usReceiveDataRegister = pxPort->sPort + serRECEIVE_DATA_OFFSET;
pxPort->usBaudRateDivisorLow = pxPort->sPort + serBAUD_RATE_DIVISOR_LOW_OFFSET;
pxPort->usBaudRateDivisorHigh = pxPort->sPort + serBAUD_RATE_DIVISOR_HIGH_OFFSET;
pxPort->usInterruptEnableReg = pxPort->sPort + serINTERRUPT_ENABLE_OFFSET;
pxPort->usInterruptIDReg = pxPort->sPort + serINTERRUPT_ID_OFFSET;
pxPort->usFIFOCtrlReg = pxPort->sPort + serFIFO_CTRL_OFFSET;
pxPort->usLineCtrlReg = pxPort->sPort + serLINE_CTRL_OFFSET;
pxPort->usModemCtrlReg = pxPort->sPort + serMODEM_CTRL_OFFSET;
pxPort->usLineStatusReg = pxPort->sPort + serLINE_STATUS_OFFSET;
pxPort->usModemStatusReg = pxPort->sPort + serMODEM_STATUS_OFFSET;
pxPort->usSCRReg = pxPort->sPort + serSCR_OFFSET;
/* Set communication parameters. */
ulDivisor = serMAX_BAUD / ulBaudFromEnum[ eWantedBaud ];
usDivisorLow = ( unsigned portSHORT ) ulDivisor & ( unsigned portSHORT ) 0xff;
usDivisorHigh = ( ( unsigned portSHORT ) ulDivisor >> 8 ) & 0xff;
switch( eWantedParity )
{
case serNO_PARITY: ucCommParam = 0x00;
break;
case serODD_PARITY: ucCommParam = 0x08;
break;
case serEVEN_PARITY: ucCommParam = 0x18;
break;
case serMARK_PARITY: ucCommParam = 0x28;
break;
case serSPACE_PARITY: ucCommParam = 0x38;
break;
default: ucCommParam = 0x00;
break;
}
switch ( eWantedDataBits )
{
case serBITS_5: ucCommParam |= 0x00;
break;
case serBITS_6: ucCommParam |= 0x01;
break;
case serBITS_7: ucCommParam |= 0x02;
break;
case serBITS_8: ucCommParam |= 0x03;
break;
default: ucCommParam |= 0x03;
break;
}
if( eWantedStopBits == serSTOP_2 )
{
ucCommParam |= 0x04;
}
/* Reset UART into known state - Thanks to Bradley Town */
portOUTPUT_BYTE( pxPort->usLineCtrlReg, 0x00 ); /* Access usTransmitHoldReg/RBR/usInterruptEnableReg */
portOUTPUT_BYTE( pxPort->usInterruptEnableReg, 0x00 ); /* Disable interrupts from UART */
portOUTPUT_BYTE( pxPort->usModemCtrlReg, 0x04 ); /* Enable some multi-port cards */
/* Code based on stuff from SVAsync lib. Clear UART Status and data registers
setting up FIFO if possible */
sIn = portINPUT_BYTE( pxPort->usSCRReg );
portOUTPUT_BYTE( pxPort->usSCRReg, 0x55 );
if( portINPUT_BYTE( pxPort->usSCRReg ) == 0x55 )
{
/* The chip is better than an 8250 */
portOUTPUT_BYTE( pxPort->usSCRReg, sIn ); /* Set usSCRReg back to what it was before */
portINPUT_BYTE( pxPort->usSCRReg); /* Give slow motherboards a chance */
/* Try and start the FIFO. It appears that some chips need a two call
protocol, but those that don't seem to work even if you do start it twice.
The first call is simply to start it, the second starts it and sets an 8
byte FIFO trigger level. */
portOUTPUT_BYTE( pxPort->usFIFOCtrlReg, 0x01 );
portINPUT_BYTE( pxPort->usFIFOCtrlReg ); /* Give slow motherboards a chance to catch up */
portOUTPUT_BYTE( pxPort->usFIFOCtrlReg, 0x87 );
/* Check that the FIFO initialised */
if( ( portINPUT_BYTE( pxPort->usInterruptIDReg ) & 0xc0 ) != 0xc0 )
{
/* It didn't so we assume it isn't there but disable it to be on the
safe side. */
portOUTPUT_BYTE( pxPort->usInterruptIDReg, 0xfe );
}
}
/* End of (modified) SVAsync code.
Set interrupt parameters calculating mask for 8259 controller's
IMR and number of interrupt handler for given irq level */
if (pxPort->ucIRQ <= 7)
{
/* if 0<=irq<=7 first IMR address used */
pxPort->ucInterruptEnableMast = ~(0x01 << pxPort->ucIRQ);
pxPort->usIRQVector = pxPort->ucIRQ + 8;
pxPort->us8259InterruptMaskReg = serIMR_8259_0;
pxPort->us8259InterruptServiceReg = serISR_8259_0;
}
else
{
pxPort->ucInterruptEnableMast = ~( 0x01 << ( pxPort->ucIRQ % 8 ) );
pxPort->usIRQVector = 0x70 + ( pxPort->ucIRQ - 8) ;
pxPort->us8259InterruptMaskReg = serIMR_8259_1;
pxPort->us8259InterruptServiceReg = serISR_8259_1;
}
/* Set Port Toggle to usBaudRateDivisorLow/usBaudRateDivisorHigh registers
to set baud rate */
portOUTPUT_BYTE( pxPort->usLineCtrlReg, ucCommParam | 0x80 );
portOUTPUT_BYTE( pxPort->usBaudRateDivisorLow, usDivisorLow );
portOUTPUT_BYTE( pxPort->usBaudRateDivisorHigh, usDivisorHigh );
/* reset usLineCtrlReg and Port Toggleout */
portOUTPUT_BYTE( pxPort->usLineCtrlReg, ucCommParam & 0x7F );
portENTER_CRITICAL();
if( xPortStatus[ pxPort->ucIRQ ] == NULL )
{
xPortStatus[ pxPort->ucIRQ ] = pxPort;
}
xOldISRs[ pxPort->ucIRQ ] = _dos_getvect( pxPort->usIRQVector );
_dos_setvect( pxPort->usIRQVector, xISRs[ pxPort->ucIRQ ] );
/* enable interrupt pxPort->ucIRQ level */
portOUTPUT_BYTE( pxPort->us8259InterruptMaskReg, portINPUT_BYTE( pxPort->us8259InterruptMaskReg ) & pxPort->ucInterruptEnableMast );
/* And allow interrupts again now the hairy bit's done */
portEXIT_CRITICAL();
/* This version does not allow flow control. */
portOUTPUT_BYTE( pxPort->usModemCtrlReg, serALL_MODEM_CTRL_INTERRUPTS );
/* enable all communication's interrupts */
portOUTPUT_BYTE( pxPort->usInterruptEnableReg, serALL_COMS_INTERRUPTS );
}
/*-----------------------------------------------------------*/
static portSHORT sComPortISR( const xComPort * const pxPort )
{
portSHORT sInterruptID;
portCHAR cIn, cOut, cTaskWokenByPost = ( portCHAR ) pdFALSE, cAnotherTaskWokenByPost = ( portCHAR ) pdFALSE, cTaskWokenByTx = ( portCHAR ) pdFALSE;
portOUTPUT_BYTE( pxPort->us8259InterruptMaskReg, ( portINPUT_BYTE( pxPort->us8259InterruptMaskReg) | ~pxPort->ucInterruptEnableMast ) );
/* Decide which UART has issued the interrupt */
sInterruptID = portINPUT_BYTE( pxPort->usInterruptIDReg );
/* service whatever requests the calling UART may have. The top 4 bits are
either unused or indicate the presence of a functioning FIFO, which we don't
need to know. So trim them off to simplify the switch statement below. */
sInterruptID &= 0x0f;
do
{
switch( sInterruptID )
{
case 0x0c: /* Timeout
Called when FIFO not up to trigger level but no activity for
a while. Handled exactly as RDAINT, see below for
description. */
do
{
cIn = ( portCHAR ) portINPUT_BYTE( pxPort->usReceiveDataRegister );
cTaskWokenByPost = cQueueSendFromISR( pxPort->xRxedChars, &cIn, cTaskWokenByPost );
/* Also release the semaphore - this does nothing interesting and is just a test. */
cAnotherTaskWokenByPost = cSemaphoreGiveFromISR( pxPort->xTestSem, cAnotherTaskWokenByPost );
} while( portINPUT_BYTE( pxPort->usLineStatusReg ) & 0x01 );
break;
case 0x06: /* LSINT */
portINPUT_BYTE( pxPort->usLineStatusReg );
break;
case 0x04: /* RDAINT */
/* The usInterruptIDReg flag tested above stops when the
FIFO is below the trigger level rather than empty, whereas
this flag allows one to empty it: (do loop because there
must be at least one to read by virtue of having got here.) */
do
{
cIn = ( portCHAR ) portINPUT_BYTE( pxPort->usReceiveDataRegister );
cTaskWokenByPost = cQueueSendFromISR( pxPort->xRxedChars, &cIn, cTaskWokenByPost );
/* Also release the semaphore - this does nothing interesting and is just a test. */
cAnotherTaskWokenByPost = cSemaphoreGiveFromISR( pxPort->xTestSem, cAnotherTaskWokenByPost );
} while( portINPUT_BYTE( pxPort->usLineStatusReg ) & 0x01 );
break;
case 0x02: /* serTRANSMIT_HOLD_EMPTY_INT */
if( cQueueReceiveFromISR( pxPort->xCharsForTx, &cOut, &cTaskWokenByTx ) != pdTRUE )
{
/* Queue empty, nothing to send */
vInterruptOff( pxPort, serTRANSMIT_HOLD_EMPTY_INT);
}
else
{
portOUTPUT_BYTE( pxPort->usTransmitHoldReg, ( portSHORT ) cOut );
}
break;
case 0x00: /* MSINT */
portINPUT_BYTE( pxPort->usModemStatusReg );
break;
}
/* Get the next instruction, trimming as above */
sInterruptID = portINPUT_BYTE( pxPort->usInterruptIDReg ) & 0x0f;
} while( !( sInterruptID & 0x01 ) );
if( pxPort->ucIRQ > 7 )
{
portOUTPUT_BYTE( 0xA0, 0x60 + ( pxPort->ucIRQ & 0x07 ) );
portOUTPUT_BYTE( 0x20, 0x62);
}
else
{
portOUTPUT_BYTE( 0x20, 0x60 + pxPort->ucIRQ );
}
portOUTPUT_BYTE( pxPort->us8259InterruptMaskReg, portINPUT_BYTE( pxPort->us8259InterruptMaskReg ) & pxPort->ucInterruptEnableMast );
/* If posting any of the characters to a queue woke a task that was blocked on
the queue we may want to return to the task just woken (depending on its
priority relative to the task this ISR interrupted. */
if( cTaskWokenByPost || cAnotherTaskWokenByPost || cTaskWokenByTx )
{
return pdTRUE;
}
else
{
return pdFALSE;
}
}
/*-----------------------------------------------------------*/
portCHAR cSerialGetChar( xComPortHandle pxPort, portCHAR *pcRxedChar, portTickType xBlockTime )
{
/* Get the next character from the buffer, note that this routine is only
called having checked that the is (at least) one to get */
if( cQueueReceive( pxPort->xRxedChars, pcRxedChar, xBlockTime ) )
{
return ( portCHAR ) pdTRUE;
}
else
{
return ( portCHAR ) pdFALSE;
}
}
/*-----------------------------------------------------------*/
portCHAR cSerialPutChar( xComPortHandle pxPort, portCHAR cOutChar, portTickType xBlockTime )
{
if( cQueueSend( pxPort->xCharsForTx, &cOutChar, xBlockTime ) != pdPASS )
{
return ( portCHAR ) pdFAIL;
}
vInterruptOn( pxPort, serTRANSMIT_HOLD_EMPTY_INT );
return ( portCHAR ) pdPASS;
}
/*-----------------------------------------------------------*/
void vSerialPutString( xComPortHandle pxPort, const portCHAR * const pcString, unsigned portSHORT usStringLength )
{
portCHAR * pcNextChar;
const portTickType xNoBlock = ( portTickType ) 0;
/* Stop warnings. */
( void ) usStringLength;
pcNextChar = ( portCHAR * ) pcString;
while( *pcNextChar )
{
cSerialPutChar( pxPort, *pcNextChar, xNoBlock );
pcNextChar++;
}
}
/*-----------------------------------------------------------*/
portSHORT sSerialWaitForSemaphore( xComPortHandle xPort )
{
const portTickType xBlockTime = ( portTickType ) 0xffff;
/* This function does nothing interesting, but test the
semaphore from ISR mechanism. */
return ( portSHORT ) cSemaphoreTake( xPort->xTestSem, xBlockTime );
}
/*-----------------------------------------------------------*/
void vSerialClose( xComPortHandle xPort )
{
portENTER_CRITICAL();
/* Turn off the interrupts. */
portOUTPUT_BYTE( xPort->usModemCtrlReg, serNO_INTERRUPTS );
portOUTPUT_BYTE( xPort->usInterruptEnableReg, serNO_INTERRUPTS );
/* Put back the original ISR. */
_dos_setvect( xPort->usIRQVector, xOldISRs[ xPort->ucIRQ ] );
/* Remove the reference in the array of xComPort structures. */
xPortStatus[ xPort->ucIRQ ] = NULL;
/* Delete the queues. */
vQueueDelete( xPort->xRxedChars );
vQueueDelete( xPort->xCharsForTx );
vPortFree( ( void * ) xPort );
portEXIT_CRITICAL();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -