]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Flshlite/serial/serial.c
Update version number to 8.1.1 for patch release that re-enables mutexes to be given...
[freertos] / FreeRTOS / Demo / Flshlite / serial / serial.c
1 /*\r
2     FreeRTOS V8.1.1 - Copyright (C) 2014 Real Time Engineers Ltd. \r
3     All rights reserved\r
4 \r
5     VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.\r
6 \r
7     ***************************************************************************\r
8      *                                                                       *\r
9      *    FreeRTOS provides completely free yet professionally developed,    *\r
10      *    robust, strictly quality controlled, supported, and cross          *\r
11      *    platform software that has become a de facto standard.             *\r
12      *                                                                       *\r
13      *    Help yourself get started quickly and support the FreeRTOS         *\r
14      *    project by purchasing a FreeRTOS tutorial book, reference          *\r
15      *    manual, or both from: http://www.FreeRTOS.org/Documentation        *\r
16      *                                                                       *\r
17      *    Thank you!                                                         *\r
18      *                                                                       *\r
19     ***************************************************************************\r
20 \r
21     This file is part of the FreeRTOS distribution.\r
22 \r
23     FreeRTOS is free software; you can redistribute it and/or modify it under\r
24     the terms of the GNU General Public License (version 2) as published by the\r
25     Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.\r
26 \r
27     >>!   NOTE: The modification to the GPL is included to allow you to     !<<\r
28     >>!   distribute a combined work that includes FreeRTOS without being   !<<\r
29     >>!   obliged to provide the source code for proprietary components     !<<\r
30     >>!   outside of the FreeRTOS kernel.                                   !<<\r
31 \r
32     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY\r
33     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\r
34     FOR A PARTICULAR PURPOSE.  Full license text is available from the following\r
35     link: http://www.freertos.org/a00114.html\r
36 \r
37     1 tab == 4 spaces!\r
38 \r
39     ***************************************************************************\r
40      *                                                                       *\r
41      *    Having a problem?  Start by reading the FAQ "My application does   *\r
42      *    not run, what could be wrong?"                                     *\r
43      *                                                                       *\r
44      *    http://www.FreeRTOS.org/FAQHelp.html                               *\r
45      *                                                                       *\r
46     ***************************************************************************\r
47 \r
48     http://www.FreeRTOS.org - Documentation, books, training, latest versions,\r
49     license and Real Time Engineers Ltd. contact details.\r
50 \r
51     http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,\r
52     including FreeRTOS+Trace - an indispensable productivity tool, a DOS\r
53     compatible FAT file system, and our tiny thread aware UDP/IP stack.\r
54 \r
55     http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High\r
56     Integrity Systems to sell under the OpenRTOS brand.  Low cost OpenRTOS\r
57     licenses offer ticketed support, indemnification and middleware.\r
58 \r
59     http://www.SafeRTOS.com - High Integrity Systems also provide a safety\r
60     engineered and independently SIL3 certified version for use in safety and\r
61     mission critical applications that require provable dependability.\r
62 \r
63     1 tab == 4 spaces!\r
64 */\r
65 \r
66 /*\r
67 Changes from V1.00:\r
68         \r
69         + Call to the more efficient portSWITCH_CONTEXT() replaces the call to \r
70           taskYIELD() in the ISR.\r
71 \r
72 Changes from V1.01:\r
73 \r
74         + The semaphore task is not operational.  This does nothing but check\r
75           the semaphore from ISR functionality.\r
76         + ISR modified slightly so only Rx or Tx is serviced per ISR - not both.\r
77 \r
78 Changes from V1.2.0:\r
79 \r
80         + Change so Tx uses a DMA channel, and Rx uses an interrupt.\r
81 \r
82 Changes from V1.2.3\r
83 \r
84         + The function xPortInitMinimal() has been renamed to \r
85           xSerialPortInitMinimal() and the function xPortInit() has been renamed\r
86           to xSerialPortInit().\r
87 \r
88 Changes from V1.2.5\r
89 \r
90         + Reverted back to the non-DMA serial port driver, with a slightly modified\r
91           ISR.  This is a better test of the scheduler mechanisms.\r
92         + A critical section is now used in vInterruptOn().\r
93         + Flag sTxInterruptOn has been added to the port structure.  This allows\r
94           checking of the interrupt enable status without performing any IO.\r
95 \r
96 Changes from V2.0.0\r
97 \r
98         + Use TickType_t in place of unsigned pdLONG for delay periods.\r
99         + Slightly more efficient vSerialSendString() implementation.\r
100         + cQueueReieveFromISR() used in place of xQueueReceive() in ISR.\r
101 */\r
102 \r
103 #include <stdlib.h>\r
104 #include <dos.h>\r
105 #include "FreeRTOS.h"\r
106 #include "queue.h"\r
107 #include "task.h"\r
108 #include "portasm.h"\r
109 #include "semphr.h"\r
110 \r
111 #define serMAX_PORTS                    ( ( unsigned short ) 2 )\r
112 \r
113 #define serPORT_0_INT_REG               ( 0xff44 )\r
114 #define serPORT_0_BAUD_REG              ( 0xff88 )\r
115 #define serPORT_0_RX_REG                ( 0xff86 )\r
116 #define serPORT_0_TX_REG                ( 0xff84 )\r
117 #define serPORT_0_STATUS_REG    ( 0xff82 )\r
118 #define serPORT_0_CTRL_REG              ( 0xff80 )\r
119 #define serPORT_0_IRQ                   ( 0x14 )\r
120 \r
121 #define serPORT_1_INT_REG               ( 0xff42 )\r
122 #define serPORT_1_BAUD_REG              ( 0xff18 )\r
123 #define serPORT_1_RX_REG                ( 0xff16 )\r
124 #define serPORT_1_TX_REG                ( 0xff14 )\r
125 #define serPORT_1_STATUS_REG    ( 0xff12 )\r
126 #define serPORT_1_CTRL_REG              ( 0xff10 )\r
127 #define serPORT_1_IRQ                   ( 0x11 )\r
128 \r
129 #define serTX_EMPTY                             ( ( unsigned short ) 0x40 )\r
130 #define serRX_READY                             ( ( unsigned short ) 0x80 )\r
131 \r
132 #define serRESET_PIC( usEOI_TYPE )      portOUTPUT_WORD( ( unsigned short ) 0xff22, usEOI_TYPE )\r
133 #define serTX_HOLD_EMPTY_INT            ( ( unsigned short ) 0x100 )\r
134 \r
135 #define serENABLE_INTERRUPTS            ( ( unsigned short ) 0x80 )\r
136 #define serMODE                                         ( ( unsigned short ) 0x01 )\r
137 #define serENABLE_TX_MACHINES           ( ( unsigned short ) 0x40 )\r
138 #define serENABLE_RX_MACHINES           ( ( unsigned short ) 0x20 )\r
139 #define serINTERRUPT_MASK                       ( ( unsigned short ) 0x08 )\r
140 #define serCLEAR_ALL_STATUS_BITS        ( ( unsigned short ) 0x00 )\r
141 #define serINTERRUPT_PRIORITY           ( ( unsigned short ) 0x01 ) /*< Just below the scheduler priority. */\r
142 \r
143 #define serDONT_BLOCK                           ( ( TickType_t ) 0 )\r
144 \r
145 typedef enum\r
146\r
147         serCOM1 = 0, \r
148         serCOM2, \r
149         serCOM3, \r
150         serCOM4, \r
151         serCOM5, \r
152         serCOM6, \r
153         serCOM7, \r
154         serCOM8 \r
155 } eCOMPort;\r
156 \r
157 typedef enum \r
158\r
159         serNO_PARITY, \r
160         serODD_PARITY, \r
161         serEVEN_PARITY, \r
162         serMARK_PARITY, \r
163         serSPACE_PARITY \r
164 } eParity;\r
165 \r
166 typedef enum \r
167\r
168         serSTOP_1, \r
169         serSTOP_2 \r
170 } eStopBits;\r
171 \r
172 typedef enum \r
173\r
174         serBITS_5, \r
175         serBITS_6, \r
176         serBITS_7, \r
177         serBITS_8 \r
178 } eDataBits;\r
179 \r
180 typedef enum \r
181\r
182         ser50 = 0,\r
183         ser75,          \r
184         ser110,         \r
185         ser134,         \r
186         ser150,    \r
187         ser200,\r
188         ser300,         \r
189         ser600,         \r
190         ser1200,        \r
191         ser1800,        \r
192         ser2400,   \r
193         ser4800,\r
194         ser9600,                \r
195         ser19200,       \r
196         ser38400,       \r
197         ser57600,       \r
198         ser115200\r
199 } eBaud;\r
200 \r
201 /* Must be same order as eBaud definitions. */\r
202 static const unsigned short usBaudRateDivisor[] = \r
203 {\r
204         0, /* Not sure if the first 6 are correct.  First cannot be used. */\r
205         29127,\r
206         19859,\r
207         16302,\r
208         14564,\r
209         10923,  \r
210         6879,\r
211         3437,\r
212         1718,\r
213         1145,\r
214         859,\r
215         429,\r
216         214,\r
217         107,\r
218         54,\r
219         35,\r
220         18\r
221 };\r
222 \r
223 \r
224 typedef struct xCOM_PORT\r
225 {\r
226         /* Hardware parameters for this port. */\r
227         short sTxInterruptOn;\r
228         unsigned short usIntReg;\r
229         unsigned short usBaudReg;\r
230         unsigned short usRxReg;\r
231         unsigned short usTxReg;\r
232         unsigned short usStatusReg;\r
233         unsigned short usCtrlReg;\r
234 \r
235         unsigned short usIRQVector;\r
236 \r
237         /* Queues used for communications with com test task. */\r
238         QueueHandle_t xRxedChars; \r
239         QueueHandle_t xCharsForTx;\r
240 \r
241         /* This semaphore does nothing useful except test a feature of the\r
242         scheduler. */\r
243         SemaphoreHandle_t xTestSem;\r
244 \r
245 } xComPort;\r
246 \r
247 static xComPort xPorts[ serMAX_PORTS ] = \r
248 {\r
249         { 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
250         { 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
251 };\r
252 \r
253 typedef xComPort * xComPortHandle;\r
254 \r
255 /* These prototypes are repeated here so we don't have to include the serial header.  This allows\r
256 the xComPortHandle structure details to be private to this file. */\r
257 xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength );\r
258 portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, char *pcRxedChar, TickType_t xBlockTime );\r
259 portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, char cOutChar, TickType_t xBlockTime );\r
260 void vSerialClose( xComPortHandle xPort );\r
261 short sSerialWaitForSemaphore( xComPortHandle xPort );\r
262 /*-----------------------------------------------------------*/\r
263 \r
264 static short xComPortISR( xComPort * const pxPort );\r
265 \r
266 #define vInterruptOn( pxPort, usInterrupt )                                                                             \\r
267 {                                                                                                                                                               \\r
268 unsigned short usIn;                                                                                                            \\r
269                                                                                                                                                                 \\r
270         portENTER_CRITICAL();                                                                                                           \\r
271         {                                                                                                                                                       \\r
272                 if( pxPort->sTxInterruptOn == pdFALSE )                                                                 \\r
273                 {                                                                                                                                               \\r
274                         usIn = portINPUT_WORD( pxPort->usCtrlReg );                                                     \\r
275                         portOUTPUT_WORD( pxPort->usCtrlReg, usIn | usInterrupt );                       \\r
276                                                                                                                                                                 \\r
277                         pxPort->sTxInterruptOn = pdTRUE;                                                                        \\r
278                 }                                                                                                                                               \\r
279         }                                                                                                                                                       \\r
280         portEXIT_CRITICAL();                                                                                                                    \\r
281 }                                                                                                                                                               \r
282 /*-----------------------------------------------------------*/\r
283 \r
284 #define vInterruptOff( pxPort, usInterrupt )                                                                    \\r
285 {                                                                                                                                                               \\r
286         unsigned short usIn = portINPUT_WORD( pxPort->usCtrlReg );                              \\r
287         if( usIn & usInterrupt )                                                                                                        \\r
288         {                                                                                                                                                       \\r
289                 portOUTPUT_WORD( pxPort->usCtrlReg, usIn & ~usInterrupt);                               \\r
290                 pxPort->sTxInterruptOn = pdFALSE;                                                                               \\r
291         }                                                                                                                                                       \\r
292 }\r
293 /*-----------------------------------------------------------*/\r
294 \r
295 \r
296 /* Define an interrupt handler for each port */\r
297 #define COM_IRQ_WRAPPER(N)                                                                              \\r
298         static void __interrupt COM_IRQ##N##_WRAPPER( void )            \\r
299         {                                                                                                                       \\r
300         if( xComPortISR( &( xPorts[##N##] ) ) )                 \\r
301         {                                                       \\r
302                         portSWITCH_CONTEXT();                             \\r
303                 }                                                       \\r
304         }\r
305 \r
306   \r
307 \r
308 COM_IRQ_WRAPPER( 0 )\r
309 COM_IRQ_WRAPPER( 1 )\r
310 \r
311 static pxISR xISRs[ serMAX_PORTS ] = \r
312 {\r
313         COM_IRQ0_WRAPPER, \r
314         COM_IRQ1_WRAPPER\r
315 };\r
316 \r
317 /*-----------------------------------------------------------*/\r
318 \r
319 xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength )\r
320 {\r
321 unsigned short usPort;\r
322 xComPortHandle pxPort = NULL;\r
323 \r
324 /* BAUDDIV = ( Microprocessor Clock / Baud Rate ) / 16 */\r
325 \r
326         /* Only n, 8, 1 is supported so these parameters are not required for this\r
327         port. */\r
328         ( void ) eWantedParity;\r
329         ( void ) eWantedDataBits;\r
330     ( void ) eWantedStopBits;\r
331 \r
332         /* Currently only n,8,1 is supported. */\r
333 \r
334         usPort = ( unsigned short ) ePort;\r
335         \r
336         if( usPort < serMAX_PORTS )\r
337         {\r
338                 pxPort = &( xPorts[ usPort ] );\r
339 \r
340                 portENTER_CRITICAL();\r
341                 {\r
342                         unsigned short usInWord;\r
343 \r
344                         /* Create the queues used by the com test task. */\r
345                         pxPort->xRxedChars = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( char ) );\r
346                         pxPort->xCharsForTx = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( char ) );\r
347 \r
348                         /* Create the test semaphore.  This does nothing useful except test a feature of the scheduler. */\r
349                         vSemaphoreCreateBinary( pxPort->xTestSem );\r
350 \r
351                         /* There is no ISR here already to restore later. */\r
352                         _dos_setvect( ( short ) pxPort->usIRQVector, xISRs[ usPort ] );\r
353 \r
354                         usInWord = portINPUT_WORD( pxPort->usIntReg );\r
355                         usInWord &= ~serINTERRUPT_MASK;\r
356                         usInWord |= serINTERRUPT_PRIORITY;\r
357                         portOUTPUT_WORD( pxPort->usIntReg, usInWord );\r
358 \r
359                         portOUTPUT_WORD( pxPort->usBaudReg, usBaudRateDivisor[ eWantedBaud ] );\r
360                         portOUTPUT_WORD( pxPort->usCtrlReg, serENABLE_INTERRUPTS | serMODE | serENABLE_TX_MACHINES | serENABLE_RX_MACHINES );\r
361 \r
362                         portOUTPUT_WORD( pxPort->usStatusReg, serCLEAR_ALL_STATUS_BITS );\r
363                 }\r
364                 portEXIT_CRITICAL();\r
365         }\r
366 \r
367         return pxPort;\r
368 } /*lint !e715 Some parameters are not used as only a subset of the serial port functionality is currently implemented. */\r
369 /*-----------------------------------------------------------*/\r
370 \r
371 void vSerialPutString( xComPortHandle pxPort, const char * const pcString, unsigned short usStringLength )\r
372 {\r
373 unsigned short usByte;\r
374 char *pcNextChar;\r
375 \r
376         pcNextChar = ( char * ) pcString;\r
377 \r
378         for( usByte = 0; usByte < usStringLength; usByte++ )\r
379         {\r
380                 xQueueSend( pxPort->xCharsForTx, pcNextChar, serDONT_BLOCK );\r
381                 pcNextChar++;\r
382         }\r
383 \r
384         vInterruptOn( pxPort, serTX_HOLD_EMPTY_INT );\r
385 }\r
386 /*-----------------------------------------------------------*/\r
387 \r
388 portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, char *pcRxedChar, TickType_t xBlockTime )\r
389 {\r
390         /* Get the next character from the buffer, note that this routine is only \r
391         called having checked that the is (at least) one to get */\r
392         if( xQueueReceive( pxPort->xRxedChars, pcRxedChar, xBlockTime ) )\r
393         {\r
394                 return pdTRUE;\r
395         }\r
396         else\r
397         {\r
398                 return pdFALSE;\r
399         }\r
400 }\r
401 /*-----------------------------------------------------------*/\r
402 \r
403 portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, char cOutChar, TickType_t xBlockTime )\r
404 {\r
405         if( xQueueSend( pxPort->xCharsForTx, &cOutChar, xBlockTime ) != pdPASS )\r
406         {\r
407                 return pdFAIL;\r
408         }\r
409 \r
410         vInterruptOn( pxPort, serTX_HOLD_EMPTY_INT );\r
411 \r
412         return pdPASS;\r
413 }\r
414 /*-----------------------------------------------------------*/\r
415 \r
416 portBASE_TYPE xSerialWaitForSemaphore( xComPortHandle xPort )\r
417 {\r
418 const TickType_t xBlockTime = ( TickType_t ) 0xffff;\r
419 \r
420         /* This function does nothing interesting, but test the \r
421         semaphore from ISR mechanism. */\r
422         return xSemaphoreTake( xPort->xTestSem, xBlockTime );\r
423 }\r
424 /*-----------------------------------------------------------*/\r
425 \r
426 void vSerialClose( xComPortHandle xPort )\r
427 {\r
428 unsigned short usOutput;\r
429 \r
430         /* Turn off the interrupts.  We may also want to delete the queues and/or\r
431         re-install the original ISR. */\r
432 \r
433         portENTER_CRITICAL();\r
434         {\r
435                 usOutput = portINPUT_WORD( xPort->usCtrlReg );\r
436 \r
437                 usOutput &= ~serENABLE_INTERRUPTS;\r
438                 usOutput &= ~serENABLE_TX_MACHINES;\r
439                 usOutput &= ~serENABLE_RX_MACHINES;\r
440                 portOUTPUT_WORD( xPort->usCtrlReg, usOutput );\r
441 \r
442                 usOutput = portINPUT_WORD( xPort->usIntReg );\r
443                 usOutput |= serINTERRUPT_MASK;\r
444                 portOUTPUT_WORD( xPort->usIntReg, usOutput );\r
445         }\r
446         portEXIT_CRITICAL();\r
447 }\r
448 /*-----------------------------------------------------------*/\r
449 \r
450 static portBASE_TYPE xComPortISR( xComPort * const pxPort )\r
451 {\r
452 unsigned short usStatusRegister;\r
453 char cChar;\r
454 portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE, xContinue = pdTRUE;\r
455 \r
456         /* NOTE:  THIS IS NOT AN EFFICIENT ISR AS IT IS DESIGNED SOLELY TO TEST\r
457         THE SCHEDULER FUNCTIONALITY.  REAL APPLICATIONS SHOULD NOT USE THIS\r
458         FUNCTION. */\r
459 \r
460 \r
461         while( xContinue == pdTRUE )\r
462         {\r
463                 xContinue = pdFALSE;\r
464                 usStatusRegister = portINPUT_WORD( pxPort->usStatusReg );\r
465 \r
466                 if( usStatusRegister & serRX_READY )\r
467                 {\r
468                         cChar = ( char ) portINPUT_WORD( pxPort->usRxReg );\r
469                         xQueueSendFromISR( pxPort->xRxedChars, &cChar, &xHigherPriorityTaskWoken );\r
470 \r
471                         /* Also release the semaphore - this does nothing interesting and is just a test. */\r
472                         xSemaphoreGiveFromISR( pxPort->xTestSem, &xHigherPriorityTaskWoken );\r
473 \r
474                         /* We have performed an action this cycle - there may be other to perform. */\r
475                         xContinue = pdTRUE;\r
476                 }\r
477 \r
478                 if( pxPort->sTxInterruptOn && ( usStatusRegister & serTX_EMPTY ) )\r
479                 {\r
480                         if( xQueueReceiveFromISR( pxPort->xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE )\r
481                         {\r
482                                 portOUTPUT_WORD( pxPort->usTxReg, ( unsigned short ) cChar );\r
483 \r
484                                 /* We have performed an action this cycle - there may be others to perform. */\r
485                                 xContinue = pdTRUE;\r
486                         }\r
487                         else\r
488                         {\r
489                                 /* Queue empty, nothing to send */\r
490                                 vInterruptOff( pxPort, serTX_HOLD_EMPTY_INT );\r
491                         }\r
492                 }\r
493         }\r
494 \r
495         serRESET_PIC( pxPort->usIRQVector );\r
496 \r
497         /* If posting to the queue woke a task that was blocked on the queue we may\r
498         want to switch to the woken task - depending on its priority relative to\r
499         the task interrupted by this ISR. */\r
500         return xHigherPriorityTaskWoken;\r
501 }\r
502 \r
503 \r
504 \r
505 \r
506 \r
507 \r