2 FreeRTOS.org V4.1.1 - Copyright (C) 2003-2006 Richard Barry.
\r
4 This file is part of the FreeRTOS.org distribution.
\r
6 FreeRTOS.org is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; either version 2 of the License, or
\r
9 (at your option) any later version.
\r
11 FreeRTOS.org is distributed in the hope that it will be useful,
\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 GNU General Public License for more details.
\r
16 You should have received a copy of the GNU General Public License
\r
17 along with FreeRTOS.org; if not, write to the Free Software
\r
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\r
20 A special exception to the GPL can be applied should you wish to distribute
\r
21 a combined work that includes FreeRTOS.org, without being obliged to provide
\r
22 the source code for any proprietary components. See the licensing section
\r
23 of http://www.FreeRTOS.org for full details of how and when the exception
\r
26 ***************************************************************************
\r
27 See http://www.FreeRTOS.org for documentation, latest information, license
\r
28 and contact details. Please ensure to read the configuration and relevant
\r
29 port sections of the online documentation.
\r
30 ***************************************************************************
\r
36 + Call to the more efficient portSWITCH_CONTEXT() replaces the call to
\r
37 taskYIELD() in the ISR.
\r
41 + The semaphore task is not operational. This does nothing but check
\r
42 the semaphore from ISR functionality.
\r
43 + ISR modified slightly so only Rx or Tx is serviced per ISR - not both.
\r
45 Changes from V1.2.0:
\r
47 + Change so Tx uses a DMA channel, and Rx uses an interrupt.
\r
51 + The function xPortInitMinimal() has been renamed to
\r
52 xSerialPortInitMinimal() and the function xPortInit() has been renamed
\r
53 to xSerialPortInit().
\r
57 + Reverted back to the non-DMA serial port driver, with a slightly modified
\r
58 ISR. This is a better test of the scheduler mechanisms.
\r
59 + A critical section is now used in vInterruptOn().
\r
60 + Flag sTxInterruptOn has been added to the port structure. This allows
\r
61 checking of the interrupt enable status without performing any IO.
\r
65 + Use portTickType in place of unsigned pdLONG for delay periods.
\r
66 + Slightly more efficient vSerialSendString() implementation.
\r
67 + cQueueReieveFromISR() used in place of xQueueReceive() in ISR.
\r
72 #include "FreeRTOS.h"
\r
75 #include "portasm.h"
\r
78 #define serMAX_PORTS ( ( unsigned portSHORT ) 2 )
\r
80 #define serPORT_0_INT_REG ( 0xff44 )
\r
81 #define serPORT_0_BAUD_REG ( 0xff88 )
\r
82 #define serPORT_0_RX_REG ( 0xff86 )
\r
83 #define serPORT_0_TX_REG ( 0xff84 )
\r
84 #define serPORT_0_STATUS_REG ( 0xff82 )
\r
85 #define serPORT_0_CTRL_REG ( 0xff80 )
\r
86 #define serPORT_0_IRQ ( 0x14 )
\r
88 #define serPORT_1_INT_REG ( 0xff42 )
\r
89 #define serPORT_1_BAUD_REG ( 0xff18 )
\r
90 #define serPORT_1_RX_REG ( 0xff16 )
\r
91 #define serPORT_1_TX_REG ( 0xff14 )
\r
92 #define serPORT_1_STATUS_REG ( 0xff12 )
\r
93 #define serPORT_1_CTRL_REG ( 0xff10 )
\r
94 #define serPORT_1_IRQ ( 0x11 )
\r
96 #define serTX_EMPTY ( ( unsigned portSHORT ) 0x40 )
\r
97 #define serRX_READY ( ( unsigned portSHORT ) 0x80 )
\r
99 #define serRESET_PIC( usEOI_TYPE ) portOUTPUT_WORD( ( unsigned portSHORT ) 0xff22, usEOI_TYPE )
\r
100 #define serTX_HOLD_EMPTY_INT ( ( unsigned portSHORT ) 0x100 )
\r
102 #define serENABLE_INTERRUPTS ( ( unsigned portSHORT ) 0x80 )
\r
103 #define serMODE ( ( unsigned portSHORT ) 0x01 )
\r
104 #define serENABLE_TX_MACHINES ( ( unsigned portSHORT ) 0x40 )
\r
105 #define serENABLE_RX_MACHINES ( ( unsigned portSHORT ) 0x20 )
\r
106 #define serINTERRUPT_MASK ( ( unsigned portSHORT ) 0x08 )
\r
107 #define serCLEAR_ALL_STATUS_BITS ( ( unsigned portSHORT ) 0x00 )
\r
108 #define serINTERRUPT_PRIORITY ( ( unsigned portSHORT ) 0x01 ) /*< Just below the scheduler priority. */
\r
110 #define serDONT_BLOCK ( ( portTickType ) 0 )
\r
168 /* Must be same order as eBaud definitions. */
\r
169 static const unsigned portSHORT usBaudRateDivisor[] =
\r
171 0, /* Not sure if the first 6 are correct. First cannot be used. */
\r
191 typedef struct xCOM_PORT
\r
193 /* Hardware parameters for this port. */
\r
194 portSHORT sTxInterruptOn;
\r
195 unsigned portSHORT usIntReg;
\r
196 unsigned portSHORT usBaudReg;
\r
197 unsigned portSHORT usRxReg;
\r
198 unsigned portSHORT usTxReg;
\r
199 unsigned portSHORT usStatusReg;
\r
200 unsigned portSHORT usCtrlReg;
\r
202 unsigned portSHORT usIRQVector;
\r
204 /* Queues used for communications with com test task. */
\r
205 xQueueHandle xRxedChars;
\r
206 xQueueHandle xCharsForTx;
\r
208 /* This semaphore does nothing useful except test a feature of the
\r
210 xSemaphoreHandle xTestSem;
\r
214 static xComPort xPorts[ serMAX_PORTS ] =
\r
216 { pdFALSE, serPORT_0_INT_REG, serPORT_0_BAUD_REG, serPORT_0_RX_REG, serPORT_0_TX_REG, serPORT_0_STATUS_REG, serPORT_0_CTRL_REG, serPORT_0_IRQ, NULL, NULL, NULL },
\r
217 { pdFALSE, serPORT_1_INT_REG, serPORT_1_BAUD_REG, serPORT_1_RX_REG, serPORT_1_TX_REG, serPORT_1_STATUS_REG, serPORT_1_CTRL_REG, serPORT_1_IRQ, NULL, NULL, NULL }
\r
220 typedef xComPort * xComPortHandle;
\r
222 /* These prototypes are repeated here so we don't have to include the serial header. This allows
\r
223 the xComPortHandle structure details to be private to this file. */
\r
224 xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength );
\r
225 portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, portCHAR *pcRxedChar, portTickType xBlockTime );
\r
226 portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, portCHAR cOutChar, portTickType xBlockTime );
\r
227 void vSerialClose( xComPortHandle xPort );
\r
228 portSHORT sSerialWaitForSemaphore( xComPortHandle xPort );
\r
229 /*-----------------------------------------------------------*/
\r
231 static portSHORT xComPortISR( xComPort * const pxPort );
\r
233 #define vInterruptOn( pxPort, usInterrupt ) \
\r
235 unsigned portSHORT usIn; \
\r
237 portENTER_CRITICAL(); \
\r
239 if( pxPort->sTxInterruptOn == pdFALSE ) \
\r
241 usIn = portINPUT_WORD( pxPort->usCtrlReg ); \
\r
242 portOUTPUT_WORD( pxPort->usCtrlReg, usIn | usInterrupt ); \
\r
244 pxPort->sTxInterruptOn = pdTRUE; \
\r
247 portEXIT_CRITICAL(); \
\r
249 /*-----------------------------------------------------------*/
\r
251 #define vInterruptOff( pxPort, usInterrupt ) \
\r
253 unsigned portSHORT usIn = portINPUT_WORD( pxPort->usCtrlReg ); \
\r
254 if( usIn & usInterrupt ) \
\r
256 portOUTPUT_WORD( pxPort->usCtrlReg, usIn & ~usInterrupt); \
\r
257 pxPort->sTxInterruptOn = pdFALSE; \
\r
260 /*-----------------------------------------------------------*/
\r
263 /* Define an interrupt handler for each port */
\r
264 #define COM_IRQ_WRAPPER(N) \
\r
265 static void __interrupt COM_IRQ##N##_WRAPPER( void ) \
\r
267 if( xComPortISR( &( xPorts[##N##] ) ) ) \
\r
269 portSWITCH_CONTEXT(); \
\r
275 COM_IRQ_WRAPPER( 0 )
\r
276 COM_IRQ_WRAPPER( 1 )
\r
278 static pxISR xISRs[ serMAX_PORTS ] =
\r
284 /*-----------------------------------------------------------*/
\r
286 xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength )
\r
288 unsigned portSHORT usPort;
\r
289 xComPortHandle pxPort = NULL;
\r
291 /* BAUDDIV = ( Microprocessor Clock / Baud Rate ) / 16 */
\r
293 /* Only n, 8, 1 is supported so these parameters are not required for this
\r
295 ( void ) eWantedParity;
\r
296 ( void ) eWantedDataBits;
\r
297 ( void ) eWantedStopBits;
\r
299 /* Currently only n,8,1 is supported. */
\r
301 usPort = ( unsigned portSHORT ) ePort;
\r
303 if( usPort < serMAX_PORTS )
\r
305 pxPort = &( xPorts[ usPort ] );
\r
307 portENTER_CRITICAL();
\r
309 unsigned portSHORT usInWord;
\r
311 /* Create the queues used by the com test task. */
\r
312 pxPort->xRxedChars = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( portCHAR ) );
\r
313 pxPort->xCharsForTx = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( portCHAR ) );
\r
315 /* Create the test semaphore. This does nothing useful except test a feature of the scheduler. */
\r
316 vSemaphoreCreateBinary( pxPort->xTestSem );
\r
318 /* There is no ISR here already to restore later. */
\r
319 _dos_setvect( ( portSHORT ) pxPort->usIRQVector, xISRs[ usPort ] );
\r
321 usInWord = portINPUT_WORD( pxPort->usIntReg );
\r
322 usInWord &= ~serINTERRUPT_MASK;
\r
323 usInWord |= serINTERRUPT_PRIORITY;
\r
324 portOUTPUT_WORD( pxPort->usIntReg, usInWord );
\r
326 portOUTPUT_WORD( pxPort->usBaudReg, usBaudRateDivisor[ eWantedBaud ] );
\r
327 portOUTPUT_WORD( pxPort->usCtrlReg, serENABLE_INTERRUPTS | serMODE | serENABLE_TX_MACHINES | serENABLE_RX_MACHINES );
\r
329 portOUTPUT_WORD( pxPort->usStatusReg, serCLEAR_ALL_STATUS_BITS );
\r
331 portEXIT_CRITICAL();
\r
335 } /*lint !e715 Some parameters are not used as only a subset of the serial port functionality is currently implemented. */
\r
336 /*-----------------------------------------------------------*/
\r
338 void vSerialPutString( xComPortHandle pxPort, const portCHAR * const pcString, unsigned portSHORT usStringLength )
\r
340 unsigned portSHORT usByte;
\r
341 portCHAR *pcNextChar;
\r
343 pcNextChar = ( portCHAR * ) pcString;
\r
345 for( usByte = 0; usByte < usStringLength; usByte++ )
\r
347 xQueueSend( pxPort->xCharsForTx, pcNextChar, serDONT_BLOCK );
\r
351 vInterruptOn( pxPort, serTX_HOLD_EMPTY_INT );
\r
353 /*-----------------------------------------------------------*/
\r
355 portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, portCHAR *pcRxedChar, portTickType xBlockTime )
\r
357 /* Get the next character from the buffer, note that this routine is only
\r
358 called having checked that the is (at least) one to get */
\r
359 if( xQueueReceive( pxPort->xRxedChars, pcRxedChar, xBlockTime ) )
\r
368 /*-----------------------------------------------------------*/
\r
370 portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, portCHAR cOutChar, portTickType xBlockTime )
\r
372 if( xQueueSend( pxPort->xCharsForTx, &cOutChar, xBlockTime ) != pdPASS )
\r
377 vInterruptOn( pxPort, serTX_HOLD_EMPTY_INT );
\r
381 /*-----------------------------------------------------------*/
\r
383 portBASE_TYPE xSerialWaitForSemaphore( xComPortHandle xPort )
\r
385 const portTickType xBlockTime = ( portTickType ) 0xffff;
\r
387 /* This function does nothing interesting, but test the
\r
388 semaphore from ISR mechanism. */
\r
389 return xSemaphoreTake( xPort->xTestSem, xBlockTime );
\r
391 /*-----------------------------------------------------------*/
\r
393 void vSerialClose( xComPortHandle xPort )
\r
395 unsigned portSHORT usOutput;
\r
397 /* Turn off the interrupts. We may also want to delete the queues and/or
\r
398 re-install the original ISR. */
\r
400 portENTER_CRITICAL();
\r
402 usOutput = portINPUT_WORD( xPort->usCtrlReg );
\r
404 usOutput &= ~serENABLE_INTERRUPTS;
\r
405 usOutput &= ~serENABLE_TX_MACHINES;
\r
406 usOutput &= ~serENABLE_RX_MACHINES;
\r
407 portOUTPUT_WORD( xPort->usCtrlReg, usOutput );
\r
409 usOutput = portINPUT_WORD( xPort->usIntReg );
\r
410 usOutput |= serINTERRUPT_MASK;
\r
411 portOUTPUT_WORD( xPort->usIntReg, usOutput );
\r
413 portEXIT_CRITICAL();
\r
415 /*-----------------------------------------------------------*/
\r
417 static portBASE_TYPE xComPortISR( xComPort * const pxPort )
\r
419 unsigned portSHORT usStatusRegister;
\r
421 portBASE_TYPE xTaskWokenByPost = pdFALSE, xAnotherTaskWokenByPost = pdFALSE, xTaskWokenByTx = pdFALSE, xContinue = pdTRUE;
\r
423 /* NOTE: THIS IS NOT AN EFFICIENT ISR AS IT IS DESIGNED SOLELY TO TEST
\r
424 THE SCHEDULER FUNCTIONALITY. REAL APPLICATIONS SHOULD NOT USE THIS
\r
428 while( xContinue == pdTRUE )
\r
430 xContinue = pdFALSE;
\r
431 usStatusRegister = portINPUT_WORD( pxPort->usStatusReg );
\r
433 if( usStatusRegister & serRX_READY )
\r
435 cChar = ( portCHAR ) portINPUT_WORD( pxPort->usRxReg );
\r
436 xTaskWokenByPost = xQueueSendFromISR( pxPort->xRxedChars, &cChar, xTaskWokenByPost );
\r
438 /* Also release the semaphore - this does nothing interesting and is just a test. */
\r
439 xAnotherTaskWokenByPost = xSemaphoreGiveFromISR( pxPort->xTestSem, xAnotherTaskWokenByPost );
\r
441 /* We have performed an action this cycle - there may be other to perform. */
\r
442 xContinue = pdTRUE;
\r
445 if( pxPort->sTxInterruptOn && ( usStatusRegister & serTX_EMPTY ) )
\r
447 if( xQueueReceiveFromISR( pxPort->xCharsForTx, &cChar, &xTaskWokenByTx ) == pdTRUE )
\r
449 portOUTPUT_WORD( pxPort->usTxReg, ( unsigned portSHORT ) cChar );
\r
451 /* We have performed an action this cycle - there may be others to perform. */
\r
452 xContinue = pdTRUE;
\r
456 /* Queue empty, nothing to send */
\r
457 vInterruptOff( pxPort, serTX_HOLD_EMPTY_INT );
\r
462 serRESET_PIC( pxPort->usIRQVector );
\r
464 /* If posting to the queue woke a task that was blocked on the queue we may
\r
465 want to switch to the woken task - depending on its priority relative to
\r
466 the task interrupted by this ISR. */
\r
467 if( xTaskWokenByPost || xAnotherTaskWokenByPost || xTaskWokenByTx)
\r