2 FreeRTOS V6.1.1 - Copyright (C) 2011 Real Time Engineers Ltd.
\r
4 ***************************************************************************
\r
8 * + New to FreeRTOS, *
\r
9 * + Wanting to learn FreeRTOS or multitasking in general quickly *
\r
10 * + Looking for basic training, *
\r
11 * + Wanting to improve your FreeRTOS skills and productivity *
\r
13 * then take a look at the FreeRTOS books - available as PDF or paperback *
\r
15 * "Using the FreeRTOS Real Time Kernel - a Practical Guide" *
\r
16 * http://www.FreeRTOS.org/Documentation *
\r
18 * A pdf reference manual is also available. Both are usually delivered *
\r
19 * to your inbox within 20 minutes to two hours when purchased between 8am *
\r
20 * and 8pm GMT (although please allow up to 24 hours in case of *
\r
21 * exceptional circumstances). Thank you for your support! *
\r
23 ***************************************************************************
\r
25 This file is part of the FreeRTOS distribution.
\r
27 FreeRTOS is free software; you can redistribute it and/or modify it under
\r
28 the terms of the GNU General Public License (version 2) as published by the
\r
29 Free Software Foundation AND MODIFIED BY the FreeRTOS exception.
\r
30 ***NOTE*** The exception to the GPL is included to allow you to distribute
\r
31 a combined work that includes FreeRTOS without being obliged to provide the
\r
32 source code for proprietary components outside of the FreeRTOS kernel.
\r
33 FreeRTOS is distributed in the hope that it will be useful, but WITHOUT
\r
34 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
\r
35 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
\r
36 more details. You should have received a copy of the GNU General Public
\r
37 License and the FreeRTOS license exception along with FreeRTOS; if not it
\r
38 can be viewed here: http://www.freertos.org/a00114.html and also obtained
\r
39 by writing to Richard Barry, contact details for whom are available on the
\r
44 http://www.FreeRTOS.org - Documentation, latest information, license and
\r
47 http://www.SafeRTOS.com - A version that is certified for use in safety
\r
50 http://www.OpenRTOS.com - Commercial support, development, porting,
\r
51 licensing and training services.
\r
57 + Call to the more efficient portSWITCH_CONTEXT() replaces the call to
\r
58 taskYIELD() in the ISR.
\r
62 + The semaphore task is not operational. This does nothing but check
\r
63 the semaphore from ISR functionality.
\r
64 + ISR modified slightly so only Rx or Tx is serviced per ISR - not both.
\r
66 Changes from V1.2.0:
\r
68 + Change so Tx uses a DMA channel, and Rx uses an interrupt.
\r
72 + The function xPortInitMinimal() has been renamed to
\r
73 xSerialPortInitMinimal() and the function xPortInit() has been renamed
\r
74 to xSerialPortInit().
\r
78 + Reverted back to the non-DMA serial port driver, with a slightly modified
\r
79 ISR. This is a better test of the scheduler mechanisms.
\r
80 + A critical section is now used in vInterruptOn().
\r
81 + Flag sTxInterruptOn has been added to the port structure. This allows
\r
82 checking of the interrupt enable status without performing any IO.
\r
86 + Use portTickType in place of unsigned pdLONG for delay periods.
\r
87 + Slightly more efficient vSerialSendString() implementation.
\r
88 + cQueueReieveFromISR() used in place of xQueueReceive() in ISR.
\r
93 #include "FreeRTOS.h"
\r
96 #include "portasm.h"
\r
99 #define serMAX_PORTS ( ( unsigned short ) 2 )
\r
101 #define serPORT_0_INT_REG ( 0xff44 )
\r
102 #define serPORT_0_BAUD_REG ( 0xff88 )
\r
103 #define serPORT_0_RX_REG ( 0xff86 )
\r
104 #define serPORT_0_TX_REG ( 0xff84 )
\r
105 #define serPORT_0_STATUS_REG ( 0xff82 )
\r
106 #define serPORT_0_CTRL_REG ( 0xff80 )
\r
107 #define serPORT_0_IRQ ( 0x14 )
\r
109 #define serPORT_1_INT_REG ( 0xff42 )
\r
110 #define serPORT_1_BAUD_REG ( 0xff18 )
\r
111 #define serPORT_1_RX_REG ( 0xff16 )
\r
112 #define serPORT_1_TX_REG ( 0xff14 )
\r
113 #define serPORT_1_STATUS_REG ( 0xff12 )
\r
114 #define serPORT_1_CTRL_REG ( 0xff10 )
\r
115 #define serPORT_1_IRQ ( 0x11 )
\r
117 #define serTX_EMPTY ( ( unsigned short ) 0x40 )
\r
118 #define serRX_READY ( ( unsigned short ) 0x80 )
\r
120 #define serRESET_PIC( usEOI_TYPE ) portOUTPUT_WORD( ( unsigned short ) 0xff22, usEOI_TYPE )
\r
121 #define serTX_HOLD_EMPTY_INT ( ( unsigned short ) 0x100 )
\r
123 #define serENABLE_INTERRUPTS ( ( unsigned short ) 0x80 )
\r
124 #define serMODE ( ( unsigned short ) 0x01 )
\r
125 #define serENABLE_TX_MACHINES ( ( unsigned short ) 0x40 )
\r
126 #define serENABLE_RX_MACHINES ( ( unsigned short ) 0x20 )
\r
127 #define serINTERRUPT_MASK ( ( unsigned short ) 0x08 )
\r
128 #define serCLEAR_ALL_STATUS_BITS ( ( unsigned short ) 0x00 )
\r
129 #define serINTERRUPT_PRIORITY ( ( unsigned short ) 0x01 ) /*< Just below the scheduler priority. */
\r
131 #define serDONT_BLOCK ( ( portTickType ) 0 )
\r
189 /* Must be same order as eBaud definitions. */
\r
190 static const unsigned short usBaudRateDivisor[] =
\r
192 0, /* Not sure if the first 6 are correct. First cannot be used. */
\r
212 typedef struct xCOM_PORT
\r
214 /* Hardware parameters for this port. */
\r
215 short sTxInterruptOn;
\r
216 unsigned short usIntReg;
\r
217 unsigned short usBaudReg;
\r
218 unsigned short usRxReg;
\r
219 unsigned short usTxReg;
\r
220 unsigned short usStatusReg;
\r
221 unsigned short usCtrlReg;
\r
223 unsigned short usIRQVector;
\r
225 /* Queues used for communications with com test task. */
\r
226 xQueueHandle xRxedChars;
\r
227 xQueueHandle xCharsForTx;
\r
229 /* This semaphore does nothing useful except test a feature of the
\r
231 xSemaphoreHandle xTestSem;
\r
235 static xComPort xPorts[ serMAX_PORTS ] =
\r
237 { 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
238 { 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
241 typedef xComPort * xComPortHandle;
\r
243 /* These prototypes are repeated here so we don't have to include the serial header. This allows
\r
244 the xComPortHandle structure details to be private to this file. */
\r
245 xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength );
\r
246 portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, char *pcRxedChar, portTickType xBlockTime );
\r
247 portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, char cOutChar, portTickType xBlockTime );
\r
248 void vSerialClose( xComPortHandle xPort );
\r
249 short sSerialWaitForSemaphore( xComPortHandle xPort );
\r
250 /*-----------------------------------------------------------*/
\r
252 static short xComPortISR( xComPort * const pxPort );
\r
254 #define vInterruptOn( pxPort, usInterrupt ) \
\r
256 unsigned short usIn; \
\r
258 portENTER_CRITICAL(); \
\r
260 if( pxPort->sTxInterruptOn == pdFALSE ) \
\r
262 usIn = portINPUT_WORD( pxPort->usCtrlReg ); \
\r
263 portOUTPUT_WORD( pxPort->usCtrlReg, usIn | usInterrupt ); \
\r
265 pxPort->sTxInterruptOn = pdTRUE; \
\r
268 portEXIT_CRITICAL(); \
\r
270 /*-----------------------------------------------------------*/
\r
272 #define vInterruptOff( pxPort, usInterrupt ) \
\r
274 unsigned short usIn = portINPUT_WORD( pxPort->usCtrlReg ); \
\r
275 if( usIn & usInterrupt ) \
\r
277 portOUTPUT_WORD( pxPort->usCtrlReg, usIn & ~usInterrupt); \
\r
278 pxPort->sTxInterruptOn = pdFALSE; \
\r
281 /*-----------------------------------------------------------*/
\r
284 /* Define an interrupt handler for each port */
\r
285 #define COM_IRQ_WRAPPER(N) \
\r
286 static void __interrupt COM_IRQ##N##_WRAPPER( void ) \
\r
288 if( xComPortISR( &( xPorts[##N##] ) ) ) \
\r
290 portSWITCH_CONTEXT(); \
\r
296 COM_IRQ_WRAPPER( 0 )
\r
297 COM_IRQ_WRAPPER( 1 )
\r
299 static pxISR xISRs[ serMAX_PORTS ] =
\r
305 /*-----------------------------------------------------------*/
\r
307 xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength )
\r
309 unsigned short usPort;
\r
310 xComPortHandle pxPort = NULL;
\r
312 /* BAUDDIV = ( Microprocessor Clock / Baud Rate ) / 16 */
\r
314 /* Only n, 8, 1 is supported so these parameters are not required for this
\r
316 ( void ) eWantedParity;
\r
317 ( void ) eWantedDataBits;
\r
318 ( void ) eWantedStopBits;
\r
320 /* Currently only n,8,1 is supported. */
\r
322 usPort = ( unsigned short ) ePort;
\r
324 if( usPort < serMAX_PORTS )
\r
326 pxPort = &( xPorts[ usPort ] );
\r
328 portENTER_CRITICAL();
\r
330 unsigned short usInWord;
\r
332 /* Create the queues used by the com test task. */
\r
333 pxPort->xRxedChars = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( char ) );
\r
334 pxPort->xCharsForTx = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( char ) );
\r
336 /* Create the test semaphore. This does nothing useful except test a feature of the scheduler. */
\r
337 vSemaphoreCreateBinary( pxPort->xTestSem );
\r
339 /* There is no ISR here already to restore later. */
\r
340 _dos_setvect( ( short ) pxPort->usIRQVector, xISRs[ usPort ] );
\r
342 usInWord = portINPUT_WORD( pxPort->usIntReg );
\r
343 usInWord &= ~serINTERRUPT_MASK;
\r
344 usInWord |= serINTERRUPT_PRIORITY;
\r
345 portOUTPUT_WORD( pxPort->usIntReg, usInWord );
\r
347 portOUTPUT_WORD( pxPort->usBaudReg, usBaudRateDivisor[ eWantedBaud ] );
\r
348 portOUTPUT_WORD( pxPort->usCtrlReg, serENABLE_INTERRUPTS | serMODE | serENABLE_TX_MACHINES | serENABLE_RX_MACHINES );
\r
350 portOUTPUT_WORD( pxPort->usStatusReg, serCLEAR_ALL_STATUS_BITS );
\r
352 portEXIT_CRITICAL();
\r
356 } /*lint !e715 Some parameters are not used as only a subset of the serial port functionality is currently implemented. */
\r
357 /*-----------------------------------------------------------*/
\r
359 void vSerialPutString( xComPortHandle pxPort, const char * const pcString, unsigned short usStringLength )
\r
361 unsigned short usByte;
\r
364 pcNextChar = ( char * ) pcString;
\r
366 for( usByte = 0; usByte < usStringLength; usByte++ )
\r
368 xQueueSend( pxPort->xCharsForTx, pcNextChar, serDONT_BLOCK );
\r
372 vInterruptOn( pxPort, serTX_HOLD_EMPTY_INT );
\r
374 /*-----------------------------------------------------------*/
\r
376 portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, char *pcRxedChar, portTickType xBlockTime )
\r
378 /* Get the next character from the buffer, note that this routine is only
\r
379 called having checked that the is (at least) one to get */
\r
380 if( xQueueReceive( pxPort->xRxedChars, pcRxedChar, xBlockTime ) )
\r
389 /*-----------------------------------------------------------*/
\r
391 portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, char cOutChar, portTickType xBlockTime )
\r
393 if( xQueueSend( pxPort->xCharsForTx, &cOutChar, xBlockTime ) != pdPASS )
\r
398 vInterruptOn( pxPort, serTX_HOLD_EMPTY_INT );
\r
402 /*-----------------------------------------------------------*/
\r
404 portBASE_TYPE xSerialWaitForSemaphore( xComPortHandle xPort )
\r
406 const portTickType xBlockTime = ( portTickType ) 0xffff;
\r
408 /* This function does nothing interesting, but test the
\r
409 semaphore from ISR mechanism. */
\r
410 return xSemaphoreTake( xPort->xTestSem, xBlockTime );
\r
412 /*-----------------------------------------------------------*/
\r
414 void vSerialClose( xComPortHandle xPort )
\r
416 unsigned short usOutput;
\r
418 /* Turn off the interrupts. We may also want to delete the queues and/or
\r
419 re-install the original ISR. */
\r
421 portENTER_CRITICAL();
\r
423 usOutput = portINPUT_WORD( xPort->usCtrlReg );
\r
425 usOutput &= ~serENABLE_INTERRUPTS;
\r
426 usOutput &= ~serENABLE_TX_MACHINES;
\r
427 usOutput &= ~serENABLE_RX_MACHINES;
\r
428 portOUTPUT_WORD( xPort->usCtrlReg, usOutput );
\r
430 usOutput = portINPUT_WORD( xPort->usIntReg );
\r
431 usOutput |= serINTERRUPT_MASK;
\r
432 portOUTPUT_WORD( xPort->usIntReg, usOutput );
\r
434 portEXIT_CRITICAL();
\r
436 /*-----------------------------------------------------------*/
\r
438 static portBASE_TYPE xComPortISR( xComPort * const pxPort )
\r
440 unsigned short usStatusRegister;
\r
442 portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE, xContinue = pdTRUE;
\r
444 /* NOTE: THIS IS NOT AN EFFICIENT ISR AS IT IS DESIGNED SOLELY TO TEST
\r
445 THE SCHEDULER FUNCTIONALITY. REAL APPLICATIONS SHOULD NOT USE THIS
\r
449 while( xContinue == pdTRUE )
\r
451 xContinue = pdFALSE;
\r
452 usStatusRegister = portINPUT_WORD( pxPort->usStatusReg );
\r
454 if( usStatusRegister & serRX_READY )
\r
456 cChar = ( char ) portINPUT_WORD( pxPort->usRxReg );
\r
457 xQueueSendFromISR( pxPort->xRxedChars, &cChar, &xHigherPriorityTaskWoken );
\r
459 /* Also release the semaphore - this does nothing interesting and is just a test. */
\r
460 xSemaphoreGiveFromISR( pxPort->xTestSem, &xHigherPriorityTaskWoken );
\r
462 /* We have performed an action this cycle - there may be other to perform. */
\r
463 xContinue = pdTRUE;
\r
466 if( pxPort->sTxInterruptOn && ( usStatusRegister & serTX_EMPTY ) )
\r
468 if( xQueueReceiveFromISR( pxPort->xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE )
\r
470 portOUTPUT_WORD( pxPort->usTxReg, ( unsigned short ) cChar );
\r
472 /* We have performed an action this cycle - there may be others to perform. */
\r
473 xContinue = pdTRUE;
\r
477 /* Queue empty, nothing to send */
\r
478 vInterruptOff( pxPort, serTX_HOLD_EMPTY_INT );
\r
483 serRESET_PIC( pxPort->usIRQVector );
\r
485 /* If posting to the queue woke a task that was blocked on the queue we may
\r
486 want to switch to the woken task - depending on its priority relative to
\r
487 the task interrupted by this ISR. */
\r
488 return xHigherPriorityTaskWoken;
\r