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