2 * FreeRTOS Kernel V10.1.0
\r
3 * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
6 * this software and associated documentation files (the "Software"), to deal in
\r
7 * the Software without restriction, including without limitation the rights to
\r
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
9 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
10 * subject to the following conditions:
\r
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\r
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
22 * http://www.FreeRTOS.org
\r
23 * http://aws.amazon.com/freertos
\r
25 * 1 tab == 4 spaces!
\r
31 + Call to the more efficient portSWITCH_CONTEXT() replaces the call to
\r
32 taskYIELD() in the ISR.
\r
36 + The semaphore task is not operational. This does nothing but check
\r
37 the semaphore from ISR functionality.
\r
38 + ISR modified slightly so only Rx or Tx is serviced per ISR - not both.
\r
40 Changes from V1.2.0:
\r
42 + Change so Tx uses a DMA channel, and Rx uses an interrupt.
\r
46 + The function xPortInitMinimal() has been renamed to
\r
47 xSerialPortInitMinimal() and the function xPortInit() has been renamed
\r
48 to xSerialPortInit().
\r
52 + Reverted back to the non-DMA serial port driver, with a slightly modified
\r
53 ISR. This is a better test of the scheduler mechanisms.
\r
54 + A critical section is now used in vInterruptOn().
\r
55 + Flag sTxInterruptOn has been added to the port structure. This allows
\r
56 checking of the interrupt enable status without performing any IO.
\r
60 + Use TickType_t in place of unsigned pdLONG for delay periods.
\r
61 + Slightly more efficient vSerialSendString() implementation.
\r
62 + cQueueReieveFromISR() used in place of xQueueReceive() in ISR.
\r
67 #include "FreeRTOS.h"
\r
70 #include "portasm.h"
\r
73 #define serMAX_PORTS ( ( unsigned short ) 2 )
\r
75 #define serPORT_0_INT_REG ( 0xff44 )
\r
76 #define serPORT_0_BAUD_REG ( 0xff88 )
\r
77 #define serPORT_0_RX_REG ( 0xff86 )
\r
78 #define serPORT_0_TX_REG ( 0xff84 )
\r
79 #define serPORT_0_STATUS_REG ( 0xff82 )
\r
80 #define serPORT_0_CTRL_REG ( 0xff80 )
\r
81 #define serPORT_0_IRQ ( 0x14 )
\r
83 #define serPORT_1_INT_REG ( 0xff42 )
\r
84 #define serPORT_1_BAUD_REG ( 0xff18 )
\r
85 #define serPORT_1_RX_REG ( 0xff16 )
\r
86 #define serPORT_1_TX_REG ( 0xff14 )
\r
87 #define serPORT_1_STATUS_REG ( 0xff12 )
\r
88 #define serPORT_1_CTRL_REG ( 0xff10 )
\r
89 #define serPORT_1_IRQ ( 0x11 )
\r
91 #define serTX_EMPTY ( ( unsigned short ) 0x40 )
\r
92 #define serRX_READY ( ( unsigned short ) 0x80 )
\r
94 #define serRESET_PIC( usEOI_TYPE ) portOUTPUT_WORD( ( unsigned short ) 0xff22, usEOI_TYPE )
\r
95 #define serTX_HOLD_EMPTY_INT ( ( unsigned short ) 0x100 )
\r
97 #define serENABLE_INTERRUPTS ( ( unsigned short ) 0x80 )
\r
98 #define serMODE ( ( unsigned short ) 0x01 )
\r
99 #define serENABLE_TX_MACHINES ( ( unsigned short ) 0x40 )
\r
100 #define serENABLE_RX_MACHINES ( ( unsigned short ) 0x20 )
\r
101 #define serINTERRUPT_MASK ( ( unsigned short ) 0x08 )
\r
102 #define serCLEAR_ALL_STATUS_BITS ( ( unsigned short ) 0x00 )
\r
103 #define serINTERRUPT_PRIORITY ( ( unsigned short ) 0x01 ) /*< Just below the scheduler priority. */
\r
105 #define serDONT_BLOCK ( ( TickType_t ) 0 )
\r
163 /* Must be same order as eBaud definitions. */
\r
164 static const unsigned short usBaudRateDivisor[] =
\r
166 0, /* Not sure if the first 6 are correct. First cannot be used. */
\r
186 typedef struct xCOM_PORT
\r
188 /* Hardware parameters for this port. */
\r
189 short sTxInterruptOn;
\r
190 unsigned short usIntReg;
\r
191 unsigned short usBaudReg;
\r
192 unsigned short usRxReg;
\r
193 unsigned short usTxReg;
\r
194 unsigned short usStatusReg;
\r
195 unsigned short usCtrlReg;
\r
197 unsigned short usIRQVector;
\r
199 /* Queues used for communications with com test task. */
\r
200 QueueHandle_t xRxedChars;
\r
201 QueueHandle_t xCharsForTx;
\r
203 /* This semaphore does nothing useful except test a feature of the
\r
205 SemaphoreHandle_t xTestSem;
\r
209 static xComPort xPorts[ serMAX_PORTS ] =
\r
211 { 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
212 { 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
215 typedef xComPort * xComPortHandle;
\r
217 /* These prototypes are repeated here so we don't have to include the serial header. This allows
\r
218 the xComPortHandle structure details to be private to this file. */
\r
219 xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength );
\r
220 portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, char *pcRxedChar, TickType_t xBlockTime );
\r
221 portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, char cOutChar, TickType_t xBlockTime );
\r
222 void vSerialClose( xComPortHandle xPort );
\r
223 short sSerialWaitForSemaphore( xComPortHandle xPort );
\r
224 /*-----------------------------------------------------------*/
\r
226 static short xComPortISR( xComPort * const pxPort );
\r
228 #define vInterruptOn( pxPort, usInterrupt ) \
\r
230 unsigned short usIn; \
\r
232 portENTER_CRITICAL(); \
\r
234 if( pxPort->sTxInterruptOn == pdFALSE ) \
\r
236 usIn = portINPUT_WORD( pxPort->usCtrlReg ); \
\r
237 portOUTPUT_WORD( pxPort->usCtrlReg, usIn | usInterrupt ); \
\r
239 pxPort->sTxInterruptOn = pdTRUE; \
\r
242 portEXIT_CRITICAL(); \
\r
244 /*-----------------------------------------------------------*/
\r
246 #define vInterruptOff( pxPort, usInterrupt ) \
\r
248 unsigned short usIn = portINPUT_WORD( pxPort->usCtrlReg ); \
\r
249 if( usIn & usInterrupt ) \
\r
251 portOUTPUT_WORD( pxPort->usCtrlReg, usIn & ~usInterrupt); \
\r
252 pxPort->sTxInterruptOn = pdFALSE; \
\r
255 /*-----------------------------------------------------------*/
\r
258 /* Define an interrupt handler for each port */
\r
259 #define COM_IRQ_WRAPPER(N) \
\r
260 static void __interrupt COM_IRQ##N##_WRAPPER( void ) \
\r
262 if( xComPortISR( &( xPorts[##N##] ) ) ) \
\r
264 portSWITCH_CONTEXT(); \
\r
270 COM_IRQ_WRAPPER( 0 )
\r
271 COM_IRQ_WRAPPER( 1 )
\r
273 static pxISR xISRs[ serMAX_PORTS ] =
\r
279 /*-----------------------------------------------------------*/
\r
281 xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength )
\r
283 unsigned short usPort;
\r
284 xComPortHandle pxPort = NULL;
\r
286 /* BAUDDIV = ( Microprocessor Clock / Baud Rate ) / 16 */
\r
288 /* Only n, 8, 1 is supported so these parameters are not required for this
\r
290 ( void ) eWantedParity;
\r
291 ( void ) eWantedDataBits;
\r
292 ( void ) eWantedStopBits;
\r
294 /* Currently only n,8,1 is supported. */
\r
296 usPort = ( unsigned short ) ePort;
\r
298 if( usPort < serMAX_PORTS )
\r
300 pxPort = &( xPorts[ usPort ] );
\r
302 portENTER_CRITICAL();
\r
304 unsigned short usInWord;
\r
306 /* Create the queues used by the com test task. */
\r
307 pxPort->xRxedChars = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( char ) );
\r
308 pxPort->xCharsForTx = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( char ) );
\r
310 /* Create the test semaphore. This does nothing useful except test a feature of the scheduler. */
\r
311 vSemaphoreCreateBinary( pxPort->xTestSem );
\r
313 /* There is no ISR here already to restore later. */
\r
314 _dos_setvect( ( short ) pxPort->usIRQVector, xISRs[ usPort ] );
\r
316 usInWord = portINPUT_WORD( pxPort->usIntReg );
\r
317 usInWord &= ~serINTERRUPT_MASK;
\r
318 usInWord |= serINTERRUPT_PRIORITY;
\r
319 portOUTPUT_WORD( pxPort->usIntReg, usInWord );
\r
321 portOUTPUT_WORD( pxPort->usBaudReg, usBaudRateDivisor[ eWantedBaud ] );
\r
322 portOUTPUT_WORD( pxPort->usCtrlReg, serENABLE_INTERRUPTS | serMODE | serENABLE_TX_MACHINES | serENABLE_RX_MACHINES );
\r
324 portOUTPUT_WORD( pxPort->usStatusReg, serCLEAR_ALL_STATUS_BITS );
\r
326 portEXIT_CRITICAL();
\r
330 } /*lint !e715 Some parameters are not used as only a subset of the serial port functionality is currently implemented. */
\r
331 /*-----------------------------------------------------------*/
\r
333 void vSerialPutString( xComPortHandle pxPort, const char * const pcString, unsigned short usStringLength )
\r
335 unsigned short usByte;
\r
338 pcNextChar = ( char * ) pcString;
\r
340 for( usByte = 0; usByte < usStringLength; usByte++ )
\r
342 xQueueSend( pxPort->xCharsForTx, pcNextChar, serDONT_BLOCK );
\r
346 vInterruptOn( pxPort, serTX_HOLD_EMPTY_INT );
\r
348 /*-----------------------------------------------------------*/
\r
350 portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, char *pcRxedChar, TickType_t xBlockTime )
\r
352 /* Get the next character from the buffer, note that this routine is only
\r
353 called having checked that the is (at least) one to get */
\r
354 if( xQueueReceive( pxPort->xRxedChars, pcRxedChar, xBlockTime ) )
\r
363 /*-----------------------------------------------------------*/
\r
365 portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, char cOutChar, TickType_t xBlockTime )
\r
367 if( xQueueSend( pxPort->xCharsForTx, &cOutChar, xBlockTime ) != pdPASS )
\r
372 vInterruptOn( pxPort, serTX_HOLD_EMPTY_INT );
\r
376 /*-----------------------------------------------------------*/
\r
378 portBASE_TYPE xSerialWaitForSemaphore( xComPortHandle xPort )
\r
380 const TickType_t xBlockTime = ( TickType_t ) 0xffff;
\r
382 /* This function does nothing interesting, but test the
\r
383 semaphore from ISR mechanism. */
\r
384 return xSemaphoreTake( xPort->xTestSem, xBlockTime );
\r
386 /*-----------------------------------------------------------*/
\r
388 void vSerialClose( xComPortHandle xPort )
\r
390 unsigned short usOutput;
\r
392 /* Turn off the interrupts. We may also want to delete the queues and/or
\r
393 re-install the original ISR. */
\r
395 portENTER_CRITICAL();
\r
397 usOutput = portINPUT_WORD( xPort->usCtrlReg );
\r
399 usOutput &= ~serENABLE_INTERRUPTS;
\r
400 usOutput &= ~serENABLE_TX_MACHINES;
\r
401 usOutput &= ~serENABLE_RX_MACHINES;
\r
402 portOUTPUT_WORD( xPort->usCtrlReg, usOutput );
\r
404 usOutput = portINPUT_WORD( xPort->usIntReg );
\r
405 usOutput |= serINTERRUPT_MASK;
\r
406 portOUTPUT_WORD( xPort->usIntReg, usOutput );
\r
408 portEXIT_CRITICAL();
\r
410 /*-----------------------------------------------------------*/
\r
412 static portBASE_TYPE xComPortISR( xComPort * const pxPort )
\r
414 unsigned short usStatusRegister;
\r
416 portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE, xContinue = pdTRUE;
\r
418 /* NOTE: THIS IS NOT AN EFFICIENT ISR AS IT IS DESIGNED SOLELY TO TEST
\r
419 THE SCHEDULER FUNCTIONALITY. REAL APPLICATIONS SHOULD NOT USE THIS
\r
423 while( xContinue == pdTRUE )
\r
425 xContinue = pdFALSE;
\r
426 usStatusRegister = portINPUT_WORD( pxPort->usStatusReg );
\r
428 if( usStatusRegister & serRX_READY )
\r
430 cChar = ( char ) portINPUT_WORD( pxPort->usRxReg );
\r
431 xQueueSendFromISR( pxPort->xRxedChars, &cChar, &xHigherPriorityTaskWoken );
\r
433 /* Also release the semaphore - this does nothing interesting and is just a test. */
\r
434 xSemaphoreGiveFromISR( pxPort->xTestSem, &xHigherPriorityTaskWoken );
\r
436 /* We have performed an action this cycle - there may be other to perform. */
\r
437 xContinue = pdTRUE;
\r
440 if( pxPort->sTxInterruptOn && ( usStatusRegister & serTX_EMPTY ) )
\r
442 if( xQueueReceiveFromISR( pxPort->xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE )
\r
444 portOUTPUT_WORD( pxPort->usTxReg, ( unsigned short ) cChar );
\r
446 /* We have performed an action this cycle - there may be others to perform. */
\r
447 xContinue = pdTRUE;
\r
451 /* Queue empty, nothing to send */
\r
452 vInterruptOff( pxPort, serTX_HOLD_EMPTY_INT );
\r
457 serRESET_PIC( pxPort->usIRQVector );
\r
459 /* If posting to the queue woke a task that was blocked on the queue we may
\r
460 want to switch to the woken task - depending on its priority relative to
\r
461 the task interrupted by this ISR. */
\r
462 return xHigherPriorityTaskWoken;
\r