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