]> begriffs open source - freertos/blob - FreeRTOS/Demo/Flshlite/serial/serial.c
Update to MIT licensed FreeRTOS V10.0.0 - see https://www.freertos.org/History.txt
[freertos] / FreeRTOS / Demo / Flshlite / serial / serial.c
1 /*\r
2  * FreeRTOS Kernel V10.0.0\r
3  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\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
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software. If you wish to use our Amazon\r
14  * FreeRTOS name, please do so in a fair use way that does not cause confusion.\r
15  *\r
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
18  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
19  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
22  *\r
23  * http://www.FreeRTOS.org\r
24  * http://aws.amazon.com/freertos\r
25  *\r
26  * 1 tab == 4 spaces!\r
27  */\r
28 \r
29 /*\r
30 Changes from V1.00:\r
31         \r
32         + Call to the more efficient portSWITCH_CONTEXT() replaces the call to \r
33           taskYIELD() in the ISR.\r
34 \r
35 Changes from V1.01:\r
36 \r
37         + The semaphore task is not operational.  This does nothing but check\r
38           the semaphore from ISR functionality.\r
39         + ISR modified slightly so only Rx or Tx is serviced per ISR - not both.\r
40 \r
41 Changes from V1.2.0:\r
42 \r
43         + Change so Tx uses a DMA channel, and Rx uses an interrupt.\r
44 \r
45 Changes from V1.2.3\r
46 \r
47         + The function xPortInitMinimal() has been renamed to \r
48           xSerialPortInitMinimal() and the function xPortInit() has been renamed\r
49           to xSerialPortInit().\r
50 \r
51 Changes from V1.2.5\r
52 \r
53         + Reverted back to the non-DMA serial port driver, with a slightly modified\r
54           ISR.  This is a better test of the scheduler mechanisms.\r
55         + A critical section is now used in vInterruptOn().\r
56         + Flag sTxInterruptOn has been added to the port structure.  This allows\r
57           checking of the interrupt enable status without performing any IO.\r
58 \r
59 Changes from V2.0.0\r
60 \r
61         + Use TickType_t in place of unsigned pdLONG for delay periods.\r
62         + Slightly more efficient vSerialSendString() implementation.\r
63         + cQueueReieveFromISR() used in place of xQueueReceive() in ISR.\r
64 */\r
65 \r
66 #include <stdlib.h>\r
67 #include <dos.h>\r
68 #include "FreeRTOS.h"\r
69 #include "queue.h"\r
70 #include "task.h"\r
71 #include "portasm.h"\r
72 #include "semphr.h"\r
73 \r
74 #define serMAX_PORTS                    ( ( unsigned short ) 2 )\r
75 \r
76 #define serPORT_0_INT_REG               ( 0xff44 )\r
77 #define serPORT_0_BAUD_REG              ( 0xff88 )\r
78 #define serPORT_0_RX_REG                ( 0xff86 )\r
79 #define serPORT_0_TX_REG                ( 0xff84 )\r
80 #define serPORT_0_STATUS_REG    ( 0xff82 )\r
81 #define serPORT_0_CTRL_REG              ( 0xff80 )\r
82 #define serPORT_0_IRQ                   ( 0x14 )\r
83 \r
84 #define serPORT_1_INT_REG               ( 0xff42 )\r
85 #define serPORT_1_BAUD_REG              ( 0xff18 )\r
86 #define serPORT_1_RX_REG                ( 0xff16 )\r
87 #define serPORT_1_TX_REG                ( 0xff14 )\r
88 #define serPORT_1_STATUS_REG    ( 0xff12 )\r
89 #define serPORT_1_CTRL_REG              ( 0xff10 )\r
90 #define serPORT_1_IRQ                   ( 0x11 )\r
91 \r
92 #define serTX_EMPTY                             ( ( unsigned short ) 0x40 )\r
93 #define serRX_READY                             ( ( unsigned short ) 0x80 )\r
94 \r
95 #define serRESET_PIC( usEOI_TYPE )      portOUTPUT_WORD( ( unsigned short ) 0xff22, usEOI_TYPE )\r
96 #define serTX_HOLD_EMPTY_INT            ( ( unsigned short ) 0x100 )\r
97 \r
98 #define serENABLE_INTERRUPTS            ( ( unsigned short ) 0x80 )\r
99 #define serMODE                                         ( ( unsigned short ) 0x01 )\r
100 #define serENABLE_TX_MACHINES           ( ( unsigned short ) 0x40 )\r
101 #define serENABLE_RX_MACHINES           ( ( unsigned short ) 0x20 )\r
102 #define serINTERRUPT_MASK                       ( ( unsigned short ) 0x08 )\r
103 #define serCLEAR_ALL_STATUS_BITS        ( ( unsigned short ) 0x00 )\r
104 #define serINTERRUPT_PRIORITY           ( ( unsigned short ) 0x01 ) /*< Just below the scheduler priority. */\r
105 \r
106 #define serDONT_BLOCK                           ( ( TickType_t ) 0 )\r
107 \r
108 typedef enum\r
109\r
110         serCOM1 = 0, \r
111         serCOM2, \r
112         serCOM3, \r
113         serCOM4, \r
114         serCOM5, \r
115         serCOM6, \r
116         serCOM7, \r
117         serCOM8 \r
118 } eCOMPort;\r
119 \r
120 typedef enum \r
121\r
122         serNO_PARITY, \r
123         serODD_PARITY, \r
124         serEVEN_PARITY, \r
125         serMARK_PARITY, \r
126         serSPACE_PARITY \r
127 } eParity;\r
128 \r
129 typedef enum \r
130\r
131         serSTOP_1, \r
132         serSTOP_2 \r
133 } eStopBits;\r
134 \r
135 typedef enum \r
136\r
137         serBITS_5, \r
138         serBITS_6, \r
139         serBITS_7, \r
140         serBITS_8 \r
141 } eDataBits;\r
142 \r
143 typedef enum \r
144\r
145         ser50 = 0,\r
146         ser75,          \r
147         ser110,         \r
148         ser134,         \r
149         ser150,    \r
150         ser200,\r
151         ser300,         \r
152         ser600,         \r
153         ser1200,        \r
154         ser1800,        \r
155         ser2400,   \r
156         ser4800,\r
157         ser9600,                \r
158         ser19200,       \r
159         ser38400,       \r
160         ser57600,       \r
161         ser115200\r
162 } eBaud;\r
163 \r
164 /* Must be same order as eBaud definitions. */\r
165 static const unsigned short usBaudRateDivisor[] = \r
166 {\r
167         0, /* Not sure if the first 6 are correct.  First cannot be used. */\r
168         29127,\r
169         19859,\r
170         16302,\r
171         14564,\r
172         10923,  \r
173         6879,\r
174         3437,\r
175         1718,\r
176         1145,\r
177         859,\r
178         429,\r
179         214,\r
180         107,\r
181         54,\r
182         35,\r
183         18\r
184 };\r
185 \r
186 \r
187 typedef struct xCOM_PORT\r
188 {\r
189         /* Hardware parameters for this port. */\r
190         short sTxInterruptOn;\r
191         unsigned short usIntReg;\r
192         unsigned short usBaudReg;\r
193         unsigned short usRxReg;\r
194         unsigned short usTxReg;\r
195         unsigned short usStatusReg;\r
196         unsigned short usCtrlReg;\r
197 \r
198         unsigned short usIRQVector;\r
199 \r
200         /* Queues used for communications with com test task. */\r
201         QueueHandle_t xRxedChars; \r
202         QueueHandle_t xCharsForTx;\r
203 \r
204         /* This semaphore does nothing useful except test a feature of the\r
205         scheduler. */\r
206         SemaphoreHandle_t xTestSem;\r
207 \r
208 } xComPort;\r
209 \r
210 static xComPort xPorts[ serMAX_PORTS ] = \r
211 {\r
212         { 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
213         { 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
214 };\r
215 \r
216 typedef xComPort * xComPortHandle;\r
217 \r
218 /* These prototypes are repeated here so we don't have to include the serial header.  This allows\r
219 the xComPortHandle structure details to be private to this file. */\r
220 xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength );\r
221 portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, char *pcRxedChar, TickType_t xBlockTime );\r
222 portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, char cOutChar, TickType_t xBlockTime );\r
223 void vSerialClose( xComPortHandle xPort );\r
224 short sSerialWaitForSemaphore( xComPortHandle xPort );\r
225 /*-----------------------------------------------------------*/\r
226 \r
227 static short xComPortISR( xComPort * const pxPort );\r
228 \r
229 #define vInterruptOn( pxPort, usInterrupt )                                                                             \\r
230 {                                                                                                                                                               \\r
231 unsigned short usIn;                                                                                                            \\r
232                                                                                                                                                                 \\r
233         portENTER_CRITICAL();                                                                                                           \\r
234         {                                                                                                                                                       \\r
235                 if( pxPort->sTxInterruptOn == pdFALSE )                                                                 \\r
236                 {                                                                                                                                               \\r
237                         usIn = portINPUT_WORD( pxPort->usCtrlReg );                                                     \\r
238                         portOUTPUT_WORD( pxPort->usCtrlReg, usIn | usInterrupt );                       \\r
239                                                                                                                                                                 \\r
240                         pxPort->sTxInterruptOn = pdTRUE;                                                                        \\r
241                 }                                                                                                                                               \\r
242         }                                                                                                                                                       \\r
243         portEXIT_CRITICAL();                                                                                                                    \\r
244 }                                                                                                                                                               \r
245 /*-----------------------------------------------------------*/\r
246 \r
247 #define vInterruptOff( pxPort, usInterrupt )                                                                    \\r
248 {                                                                                                                                                               \\r
249         unsigned short usIn = portINPUT_WORD( pxPort->usCtrlReg );                              \\r
250         if( usIn & usInterrupt )                                                                                                        \\r
251         {                                                                                                                                                       \\r
252                 portOUTPUT_WORD( pxPort->usCtrlReg, usIn & ~usInterrupt);                               \\r
253                 pxPort->sTxInterruptOn = pdFALSE;                                                                               \\r
254         }                                                                                                                                                       \\r
255 }\r
256 /*-----------------------------------------------------------*/\r
257 \r
258 \r
259 /* Define an interrupt handler for each port */\r
260 #define COM_IRQ_WRAPPER(N)                                                                              \\r
261         static void __interrupt COM_IRQ##N##_WRAPPER( void )            \\r
262         {                                                                                                                       \\r
263         if( xComPortISR( &( xPorts[##N##] ) ) )                 \\r
264         {                                                       \\r
265                         portSWITCH_CONTEXT();                             \\r
266                 }                                                       \\r
267         }\r
268 \r
269   \r
270 \r
271 COM_IRQ_WRAPPER( 0 )\r
272 COM_IRQ_WRAPPER( 1 )\r
273 \r
274 static pxISR xISRs[ serMAX_PORTS ] = \r
275 {\r
276         COM_IRQ0_WRAPPER, \r
277         COM_IRQ1_WRAPPER\r
278 };\r
279 \r
280 /*-----------------------------------------------------------*/\r
281 \r
282 xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength )\r
283 {\r
284 unsigned short usPort;\r
285 xComPortHandle pxPort = NULL;\r
286 \r
287 /* BAUDDIV = ( Microprocessor Clock / Baud Rate ) / 16 */\r
288 \r
289         /* Only n, 8, 1 is supported so these parameters are not required for this\r
290         port. */\r
291         ( void ) eWantedParity;\r
292         ( void ) eWantedDataBits;\r
293     ( void ) eWantedStopBits;\r
294 \r
295         /* Currently only n,8,1 is supported. */\r
296 \r
297         usPort = ( unsigned short ) ePort;\r
298         \r
299         if( usPort < serMAX_PORTS )\r
300         {\r
301                 pxPort = &( xPorts[ usPort ] );\r
302 \r
303                 portENTER_CRITICAL();\r
304                 {\r
305                         unsigned short usInWord;\r
306 \r
307                         /* Create the queues used by the com test task. */\r
308                         pxPort->xRxedChars = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( char ) );\r
309                         pxPort->xCharsForTx = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( char ) );\r
310 \r
311                         /* Create the test semaphore.  This does nothing useful except test a feature of the scheduler. */\r
312                         vSemaphoreCreateBinary( pxPort->xTestSem );\r
313 \r
314                         /* There is no ISR here already to restore later. */\r
315                         _dos_setvect( ( short ) pxPort->usIRQVector, xISRs[ usPort ] );\r
316 \r
317                         usInWord = portINPUT_WORD( pxPort->usIntReg );\r
318                         usInWord &= ~serINTERRUPT_MASK;\r
319                         usInWord |= serINTERRUPT_PRIORITY;\r
320                         portOUTPUT_WORD( pxPort->usIntReg, usInWord );\r
321 \r
322                         portOUTPUT_WORD( pxPort->usBaudReg, usBaudRateDivisor[ eWantedBaud ] );\r
323                         portOUTPUT_WORD( pxPort->usCtrlReg, serENABLE_INTERRUPTS | serMODE | serENABLE_TX_MACHINES | serENABLE_RX_MACHINES );\r
324 \r
325                         portOUTPUT_WORD( pxPort->usStatusReg, serCLEAR_ALL_STATUS_BITS );\r
326                 }\r
327                 portEXIT_CRITICAL();\r
328         }\r
329 \r
330         return pxPort;\r
331 } /*lint !e715 Some parameters are not used as only a subset of the serial port functionality is currently implemented. */\r
332 /*-----------------------------------------------------------*/\r
333 \r
334 void vSerialPutString( xComPortHandle pxPort, const char * const pcString, unsigned short usStringLength )\r
335 {\r
336 unsigned short usByte;\r
337 char *pcNextChar;\r
338 \r
339         pcNextChar = ( char * ) pcString;\r
340 \r
341         for( usByte = 0; usByte < usStringLength; usByte++ )\r
342         {\r
343                 xQueueSend( pxPort->xCharsForTx, pcNextChar, serDONT_BLOCK );\r
344                 pcNextChar++;\r
345         }\r
346 \r
347         vInterruptOn( pxPort, serTX_HOLD_EMPTY_INT );\r
348 }\r
349 /*-----------------------------------------------------------*/\r
350 \r
351 portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, char *pcRxedChar, TickType_t xBlockTime )\r
352 {\r
353         /* Get the next character from the buffer, note that this routine is only \r
354         called having checked that the is (at least) one to get */\r
355         if( xQueueReceive( pxPort->xRxedChars, pcRxedChar, xBlockTime ) )\r
356         {\r
357                 return pdTRUE;\r
358         }\r
359         else\r
360         {\r
361                 return pdFALSE;\r
362         }\r
363 }\r
364 /*-----------------------------------------------------------*/\r
365 \r
366 portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, char cOutChar, TickType_t xBlockTime )\r
367 {\r
368         if( xQueueSend( pxPort->xCharsForTx, &cOutChar, xBlockTime ) != pdPASS )\r
369         {\r
370                 return pdFAIL;\r
371         }\r
372 \r
373         vInterruptOn( pxPort, serTX_HOLD_EMPTY_INT );\r
374 \r
375         return pdPASS;\r
376 }\r
377 /*-----------------------------------------------------------*/\r
378 \r
379 portBASE_TYPE xSerialWaitForSemaphore( xComPortHandle xPort )\r
380 {\r
381 const TickType_t xBlockTime = ( TickType_t ) 0xffff;\r
382 \r
383         /* This function does nothing interesting, but test the \r
384         semaphore from ISR mechanism. */\r
385         return xSemaphoreTake( xPort->xTestSem, xBlockTime );\r
386 }\r
387 /*-----------------------------------------------------------*/\r
388 \r
389 void vSerialClose( xComPortHandle xPort )\r
390 {\r
391 unsigned short usOutput;\r
392 \r
393         /* Turn off the interrupts.  We may also want to delete the queues and/or\r
394         re-install the original ISR. */\r
395 \r
396         portENTER_CRITICAL();\r
397         {\r
398                 usOutput = portINPUT_WORD( xPort->usCtrlReg );\r
399 \r
400                 usOutput &= ~serENABLE_INTERRUPTS;\r
401                 usOutput &= ~serENABLE_TX_MACHINES;\r
402                 usOutput &= ~serENABLE_RX_MACHINES;\r
403                 portOUTPUT_WORD( xPort->usCtrlReg, usOutput );\r
404 \r
405                 usOutput = portINPUT_WORD( xPort->usIntReg );\r
406                 usOutput |= serINTERRUPT_MASK;\r
407                 portOUTPUT_WORD( xPort->usIntReg, usOutput );\r
408         }\r
409         portEXIT_CRITICAL();\r
410 }\r
411 /*-----------------------------------------------------------*/\r
412 \r
413 static portBASE_TYPE xComPortISR( xComPort * const pxPort )\r
414 {\r
415 unsigned short usStatusRegister;\r
416 char cChar;\r
417 portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE, xContinue = pdTRUE;\r
418 \r
419         /* NOTE:  THIS IS NOT AN EFFICIENT ISR AS IT IS DESIGNED SOLELY TO TEST\r
420         THE SCHEDULER FUNCTIONALITY.  REAL APPLICATIONS SHOULD NOT USE THIS\r
421         FUNCTION. */\r
422 \r
423 \r
424         while( xContinue == pdTRUE )\r
425         {\r
426                 xContinue = pdFALSE;\r
427                 usStatusRegister = portINPUT_WORD( pxPort->usStatusReg );\r
428 \r
429                 if( usStatusRegister & serRX_READY )\r
430                 {\r
431                         cChar = ( char ) portINPUT_WORD( pxPort->usRxReg );\r
432                         xQueueSendFromISR( pxPort->xRxedChars, &cChar, &xHigherPriorityTaskWoken );\r
433 \r
434                         /* Also release the semaphore - this does nothing interesting and is just a test. */\r
435                         xSemaphoreGiveFromISR( pxPort->xTestSem, &xHigherPriorityTaskWoken );\r
436 \r
437                         /* We have performed an action this cycle - there may be other to perform. */\r
438                         xContinue = pdTRUE;\r
439                 }\r
440 \r
441                 if( pxPort->sTxInterruptOn && ( usStatusRegister & serTX_EMPTY ) )\r
442                 {\r
443                         if( xQueueReceiveFromISR( pxPort->xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE )\r
444                         {\r
445                                 portOUTPUT_WORD( pxPort->usTxReg, ( unsigned short ) cChar );\r
446 \r
447                                 /* We have performed an action this cycle - there may be others to perform. */\r
448                                 xContinue = pdTRUE;\r
449                         }\r
450                         else\r
451                         {\r
452                                 /* Queue empty, nothing to send */\r
453                                 vInterruptOff( pxPort, serTX_HOLD_EMPTY_INT );\r
454                         }\r
455                 }\r
456         }\r
457 \r
458         serRESET_PIC( pxPort->usIRQVector );\r
459 \r
460         /* If posting to the queue woke a task that was blocked on the queue we may\r
461         want to switch to the woken task - depending on its priority relative to\r
462         the task interrupted by this ISR. */\r
463         return xHigherPriorityTaskWoken;\r
464 }\r
465 \r
466 \r
467 \r
468 \r
469 \r
470 \r