1 |
586 |
jeremybenn |
/*
|
2 |
|
|
This serial port driver is borrowed heavily from DZComm. I have
|
3 |
|
|
simplified it by removing a lot of the functionality (hardware
|
4 |
|
|
flow control, etc.). For more details and the full version see
|
5 |
|
|
http://dzcomm.sourceforge.net
|
6 |
|
|
*/
|
7 |
|
|
|
8 |
|
|
/*
|
9 |
|
|
FreeRTOS V6.1.1 - Copyright (C) 2011 Real Time Engineers Ltd.
|
10 |
|
|
|
11 |
|
|
***************************************************************************
|
12 |
|
|
* *
|
13 |
|
|
* If you are: *
|
14 |
|
|
* *
|
15 |
|
|
* + New to FreeRTOS, *
|
16 |
|
|
* + Wanting to learn FreeRTOS or multitasking in general quickly *
|
17 |
|
|
* + Looking for basic training, *
|
18 |
|
|
* + Wanting to improve your FreeRTOS skills and productivity *
|
19 |
|
|
* *
|
20 |
|
|
* then take a look at the FreeRTOS books - available as PDF or paperback *
|
21 |
|
|
* *
|
22 |
|
|
* "Using the FreeRTOS Real Time Kernel - a Practical Guide" *
|
23 |
|
|
* http://www.FreeRTOS.org/Documentation *
|
24 |
|
|
* *
|
25 |
|
|
* A pdf reference manual is also available. Both are usually delivered *
|
26 |
|
|
* to your inbox within 20 minutes to two hours when purchased between 8am *
|
27 |
|
|
* and 8pm GMT (although please allow up to 24 hours in case of *
|
28 |
|
|
* exceptional circumstances). Thank you for your support! *
|
29 |
|
|
* *
|
30 |
|
|
***************************************************************************
|
31 |
|
|
|
32 |
|
|
This file is part of the FreeRTOS distribution.
|
33 |
|
|
|
34 |
|
|
FreeRTOS is free software; you can redistribute it and/or modify it under
|
35 |
|
|
the terms of the GNU General Public License (version 2) as published by the
|
36 |
|
|
Free Software Foundation AND MODIFIED BY the FreeRTOS exception.
|
37 |
|
|
***NOTE*** The exception to the GPL is included to allow you to distribute
|
38 |
|
|
a combined work that includes FreeRTOS without being obliged to provide the
|
39 |
|
|
source code for proprietary components outside of the FreeRTOS kernel.
|
40 |
|
|
FreeRTOS is distributed in the hope that it will be useful, but WITHOUT
|
41 |
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
42 |
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
43 |
|
|
more details. You should have received a copy of the GNU General Public
|
44 |
|
|
License and the FreeRTOS license exception along with FreeRTOS; if not it
|
45 |
|
|
can be viewed here: http://www.freertos.org/a00114.html and also obtained
|
46 |
|
|
by writing to Richard Barry, contact details for whom are available on the
|
47 |
|
|
FreeRTOS WEB site.
|
48 |
|
|
|
49 |
|
|
1 tab == 4 spaces!
|
50 |
|
|
|
51 |
|
|
http://www.FreeRTOS.org - Documentation, latest information, license and
|
52 |
|
|
contact details.
|
53 |
|
|
|
54 |
|
|
http://www.SafeRTOS.com - A version that is certified for use in safety
|
55 |
|
|
critical systems.
|
56 |
|
|
|
57 |
|
|
http://www.OpenRTOS.com - Commercial support, development, porting,
|
58 |
|
|
licensing and training services.
|
59 |
|
|
*/
|
60 |
|
|
|
61 |
|
|
|
62 |
|
|
|
63 |
|
|
#include <stdlib.h>
|
64 |
|
|
#include <dos.h>
|
65 |
|
|
#include "FreeRTOS.h"
|
66 |
|
|
#include "queue.h"
|
67 |
|
|
#include "task.h"
|
68 |
|
|
#include "semphr.h"
|
69 |
|
|
#include "portasm.h"
|
70 |
|
|
|
71 |
|
|
#define serMAX_IRQs ( 16 )
|
72 |
|
|
#define serTRANSMIT_HOLD_EMPTY_INT ( 0x02 )
|
73 |
|
|
#define serCOM1_STANDARD_IRQ ( ( unsigned char ) 4 )
|
74 |
|
|
#define serCOM2_STANDARD_IRQ ( ( unsigned char ) 3 )
|
75 |
|
|
|
76 |
|
|
|
77 |
|
|
#define serIMR_8259_0 ( ( unsigned char ) 0x21 )
|
78 |
|
|
#define serIMR_8259_1 ( ( unsigned char ) 0xa1 )
|
79 |
|
|
#define serISR_8259_0 ( ( unsigned char ) 0x20 )
|
80 |
|
|
#define serISR_8259_1 ( ( unsigned char ) 0xa0 )
|
81 |
|
|
#define serALL_COMS_INTERRUPTS ( ( unsigned char ) 0x0f )
|
82 |
|
|
#define serALL_MODEM_CTRL_INTERRUPTS ( ( unsigned char ) 0x0f )
|
83 |
|
|
|
84 |
|
|
#define serTRANSMIT_HOLD_OFFSET ( 0 )
|
85 |
|
|
#define serRECEIVE_DATA_OFFSET ( 0 )
|
86 |
|
|
#define serBAUD_RATE_DIVISOR_LOW_OFFSET ( 0 )
|
87 |
|
|
#define serBAUD_RATE_DIVISOR_HIGH_OFFSET ( 1 )
|
88 |
|
|
#define serINTERRUPT_ENABLE_OFFSET ( 1 )
|
89 |
|
|
#define serINTERRUPT_ID_OFFSET ( 2 )
|
90 |
|
|
#define serFIFO_CTRL_OFFSET ( 2 )
|
91 |
|
|
#define serLINE_CTRL_OFFSET ( 3 )
|
92 |
|
|
#define serMODEM_CTRL_OFFSET ( 4 )
|
93 |
|
|
#define serLINE_STATUS_OFFSET ( 5 )
|
94 |
|
|
#define serMODEM_STATUS_OFFSET ( 6 )
|
95 |
|
|
#define serSCR_OFFSET ( 7 )
|
96 |
|
|
|
97 |
|
|
#define serMAX_BAUD ( ( unsigned long ) 115200UL )
|
98 |
|
|
|
99 |
|
|
#define serNO_INTERRUPTS ( 0x00 )
|
100 |
|
|
|
101 |
|
|
#define vInterruptOn( pxPort, ucInterrupt ) \
|
102 |
|
|
{ \
|
103 |
|
|
unsigned char ucIn = portINPUT_BYTE( pxPort->usInterruptEnableReg ); \
|
104 |
|
|
if( !( ucIn & ucInterrupt ) ) \
|
105 |
|
|
{ \
|
106 |
|
|
portOUTPUT_BYTE( pxPort->usInterruptEnableReg, ucIn | ucInterrupt ); \
|
107 |
|
|
} \
|
108 |
|
|
}
|
109 |
|
|
/*-----------------------------------------------------------*/
|
110 |
|
|
|
111 |
|
|
#define vInterruptOff( pxPort, ucInterrupt ) \
|
112 |
|
|
{ \
|
113 |
|
|
unsigned char ucIn = portINPUT_BYTE( pxPort->usInterruptEnableReg ); \
|
114 |
|
|
if( ucIn & ucInterrupt ) \
|
115 |
|
|
{ \
|
116 |
|
|
portOUTPUT_BYTE( pxPort->usInterruptEnableReg, ucIn & ~ucInterrupt); \
|
117 |
|
|
} \
|
118 |
|
|
}
|
119 |
|
|
/*-----------------------------------------------------------*/
|
120 |
|
|
|
121 |
|
|
typedef enum
|
122 |
|
|
{
|
123 |
|
|
serCOM1,
|
124 |
|
|
serCOM2,
|
125 |
|
|
serCOM3,
|
126 |
|
|
serCOM4,
|
127 |
|
|
serCOM5,
|
128 |
|
|
serCOM6,
|
129 |
|
|
serCOM7,
|
130 |
|
|
serCOM8
|
131 |
|
|
} eCOMPort;
|
132 |
|
|
|
133 |
|
|
typedef enum
|
134 |
|
|
{
|
135 |
|
|
serNO_PARITY,
|
136 |
|
|
serODD_PARITY,
|
137 |
|
|
serEVEN_PARITY,
|
138 |
|
|
serMARK_PARITY,
|
139 |
|
|
serSPACE_PARITY
|
140 |
|
|
} eParity;
|
141 |
|
|
|
142 |
|
|
typedef enum
|
143 |
|
|
{
|
144 |
|
|
serSTOP_1,
|
145 |
|
|
serSTOP_2
|
146 |
|
|
} eStopBits;
|
147 |
|
|
|
148 |
|
|
typedef enum
|
149 |
|
|
{
|
150 |
|
|
serBITS_5,
|
151 |
|
|
serBITS_6,
|
152 |
|
|
serBITS_7,
|
153 |
|
|
serBITS_8
|
154 |
|
|
} eDataBits;
|
155 |
|
|
|
156 |
|
|
typedef enum
|
157 |
|
|
{
|
158 |
|
|
ser50,
|
159 |
|
|
ser75,
|
160 |
|
|
ser110,
|
161 |
|
|
ser134,
|
162 |
|
|
ser150,
|
163 |
|
|
ser200,
|
164 |
|
|
ser300,
|
165 |
|
|
ser600,
|
166 |
|
|
ser1200,
|
167 |
|
|
ser1800,
|
168 |
|
|
ser2400,
|
169 |
|
|
ser4800,
|
170 |
|
|
ser9600,
|
171 |
|
|
ser19200,
|
172 |
|
|
ser38400,
|
173 |
|
|
ser57600,
|
174 |
|
|
ser115200
|
175 |
|
|
} eBaud;
|
176 |
|
|
|
177 |
|
|
/* This *MUST* match the order in the eBaud definition. */
|
178 |
|
|
unsigned long ulBaudFromEnum[] =
|
179 |
|
|
{
|
180 |
|
|
( unsigned long ) 50,
|
181 |
|
|
( unsigned long ) 75,
|
182 |
|
|
( unsigned long ) 110,
|
183 |
|
|
( unsigned long ) 134,
|
184 |
|
|
( unsigned long ) 150,
|
185 |
|
|
( unsigned long ) 200,
|
186 |
|
|
( unsigned long ) 300,
|
187 |
|
|
( unsigned long ) 600,
|
188 |
|
|
( unsigned long ) 1200,
|
189 |
|
|
( unsigned long ) 1800,
|
190 |
|
|
( unsigned long ) 2400,
|
191 |
|
|
( unsigned long ) 4800,
|
192 |
|
|
( unsigned long ) 9600,
|
193 |
|
|
( unsigned long ) 19200,
|
194 |
|
|
( unsigned long ) 38400UL,
|
195 |
|
|
( unsigned long ) 57600UL,
|
196 |
|
|
( unsigned long ) 115200UL
|
197 |
|
|
};
|
198 |
|
|
|
199 |
|
|
typedef struct xCOM_PORT
|
200 |
|
|
{
|
201 |
|
|
unsigned short sPort; /* comm port address eg. 0x3f8 */
|
202 |
|
|
unsigned char ucIRQ; /* comm IRQ eg. 3 */
|
203 |
|
|
|
204 |
|
|
/* Next two fields used for setting up the IRQ routine and
|
205 |
|
|
* (un)masking the interrupt in certain circumstances.
|
206 |
|
|
*/
|
207 |
|
|
unsigned short usIRQVector;
|
208 |
|
|
unsigned char ucInterruptEnableMast;
|
209 |
|
|
|
210 |
|
|
/* Read/Write buffers. */
|
211 |
|
|
xQueueHandle xRxedChars;
|
212 |
|
|
xQueueHandle xCharsForTx;
|
213 |
|
|
|
214 |
|
|
/* This lot are set up to minimise CPU time where accessing the comm
|
215 |
|
|
* port's registers.
|
216 |
|
|
*/
|
217 |
|
|
unsigned short usTransmitHoldReg;
|
218 |
|
|
unsigned short usReceiveDataRegister;
|
219 |
|
|
unsigned short usBaudRateDivisorLow;
|
220 |
|
|
unsigned short usBaudRateDivisorHigh;
|
221 |
|
|
unsigned short usInterruptEnableReg;
|
222 |
|
|
unsigned short usInterruptIDReg;
|
223 |
|
|
unsigned short usFIFOCtrlReg;
|
224 |
|
|
unsigned short usLineCtrlReg;
|
225 |
|
|
unsigned short usModemCtrlReg;
|
226 |
|
|
unsigned short usLineStatusReg;
|
227 |
|
|
unsigned short usModemStatusReg;
|
228 |
|
|
unsigned short usSCRReg;
|
229 |
|
|
unsigned short us8259InterruptServiceReg;
|
230 |
|
|
unsigned short us8259InterruptMaskReg;
|
231 |
|
|
|
232 |
|
|
/* This semaphore does nothing useful except test a feature of the
|
233 |
|
|
scheduler. */
|
234 |
|
|
xSemaphoreHandle xTestSem;
|
235 |
|
|
|
236 |
|
|
} xComPort;
|
237 |
|
|
|
238 |
|
|
typedef xComPort *xComPortHandle;
|
239 |
|
|
|
240 |
|
|
/* A xComPort structure can be associated with each IRQ. Initially none
|
241 |
|
|
are create/installed. */
|
242 |
|
|
xComPort *xPortStatus[ serMAX_IRQs ] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
|
243 |
|
|
|
244 |
|
|
/*-----------------------------------------------------------*/
|
245 |
|
|
|
246 |
|
|
/* These prototypes are repeated here so we don't have to include the serial header. This allows
|
247 |
|
|
the xComPortHandle structure details to be private to this file. */
|
248 |
|
|
xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength );
|
249 |
|
|
portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, char *pcRxedChar, portTickType xBlockTime );
|
250 |
|
|
portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, char cOutChar, portTickType xBlockTime );
|
251 |
|
|
portBASE_TYPE xSerialWaitForSemaphore( xComPortHandle xPort );
|
252 |
|
|
|
253 |
|
|
static void prvSetupPortHardware( xComPort *pxPort, eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits );
|
254 |
|
|
static short sComPortISR( const xComPort * const pxPort );
|
255 |
|
|
|
256 |
|
|
/*-----------------------------------------------------------*/
|
257 |
|
|
|
258 |
|
|
/* Define an interrupt handler for each slot in the xPortStatus array. */
|
259 |
|
|
|
260 |
|
|
#define COM_IRQ_WRAPPER(N) \
|
261 |
|
|
static void __interrupt COM_IRQ##N##_WRAPPER( void ) \
|
262 |
|
|
{ \
|
263 |
|
|
portDISABLE_INTERRUPTS(); \
|
264 |
|
|
if( sComPortISR( xPortStatus[##N##] ) ) \
|
265 |
|
|
{ \
|
266 |
|
|
portSWITCH_CONTEXT(); \
|
267 |
|
|
} \
|
268 |
|
|
}
|
269 |
|
|
|
270 |
|
|
COM_IRQ_WRAPPER( 0 )
|
271 |
|
|
COM_IRQ_WRAPPER( 1 )
|
272 |
|
|
COM_IRQ_WRAPPER( 2 )
|
273 |
|
|
COM_IRQ_WRAPPER( 3 )
|
274 |
|
|
COM_IRQ_WRAPPER( 4 )
|
275 |
|
|
COM_IRQ_WRAPPER( 5 )
|
276 |
|
|
COM_IRQ_WRAPPER( 6 )
|
277 |
|
|
COM_IRQ_WRAPPER( 7 )
|
278 |
|
|
COM_IRQ_WRAPPER( 8 )
|
279 |
|
|
COM_IRQ_WRAPPER( 9 )
|
280 |
|
|
COM_IRQ_WRAPPER( 10 )
|
281 |
|
|
COM_IRQ_WRAPPER( 11 )
|
282 |
|
|
COM_IRQ_WRAPPER( 12 )
|
283 |
|
|
COM_IRQ_WRAPPER( 13 )
|
284 |
|
|
COM_IRQ_WRAPPER( 14 )
|
285 |
|
|
COM_IRQ_WRAPPER( 15 )
|
286 |
|
|
|
287 |
|
|
static pxISR xISRs[ serMAX_IRQs ] =
|
288 |
|
|
{
|
289 |
|
|
COM_IRQ0_WRAPPER,
|
290 |
|
|
COM_IRQ1_WRAPPER,
|
291 |
|
|
COM_IRQ2_WRAPPER,
|
292 |
|
|
COM_IRQ3_WRAPPER,
|
293 |
|
|
COM_IRQ4_WRAPPER,
|
294 |
|
|
COM_IRQ5_WRAPPER,
|
295 |
|
|
COM_IRQ6_WRAPPER,
|
296 |
|
|
COM_IRQ7_WRAPPER,
|
297 |
|
|
COM_IRQ8_WRAPPER,
|
298 |
|
|
COM_IRQ9_WRAPPER,
|
299 |
|
|
COM_IRQ10_WRAPPER,
|
300 |
|
|
COM_IRQ11_WRAPPER,
|
301 |
|
|
COM_IRQ12_WRAPPER,
|
302 |
|
|
COM_IRQ13_WRAPPER,
|
303 |
|
|
COM_IRQ14_WRAPPER,
|
304 |
|
|
COM_IRQ15_WRAPPER
|
305 |
|
|
};
|
306 |
|
|
|
307 |
|
|
static pxISR xOldISRs[ serMAX_IRQs ] = { NULL };
|
308 |
|
|
|
309 |
|
|
/*-----------------------------------------------------------*/
|
310 |
|
|
|
311 |
|
|
|
312 |
|
|
xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength )
|
313 |
|
|
{
|
314 |
|
|
xComPort *pxPort;
|
315 |
|
|
|
316 |
|
|
/* Create a structure to handle this port. */
|
317 |
|
|
pxPort = ( xComPort * ) pvPortMalloc( sizeof( xComPort ) );
|
318 |
|
|
|
319 |
|
|
if( pxPort != NULL )
|
320 |
|
|
{
|
321 |
|
|
/* Create the queues used by the comtest task. */
|
322 |
|
|
pxPort->xRxedChars = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( char ) );
|
323 |
|
|
pxPort->xCharsForTx = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( char ) );
|
324 |
|
|
|
325 |
|
|
/* Create the test semaphore. This does nothing useful except test a feature of the scheduler. */
|
326 |
|
|
vSemaphoreCreateBinary( pxPort->xTestSem );
|
327 |
|
|
|
328 |
|
|
prvSetupPortHardware( pxPort, ePort, eWantedBaud, eWantedParity, eWantedDataBits, eWantedStopBits );
|
329 |
|
|
|
330 |
|
|
return pxPort;
|
331 |
|
|
}
|
332 |
|
|
|
333 |
|
|
return NULL;
|
334 |
|
|
}
|
335 |
|
|
/*-----------------------------------------------------------*/
|
336 |
|
|
|
337 |
|
|
static void prvSetupPortHardware( xComPort *pxPort, eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits )
|
338 |
|
|
{
|
339 |
|
|
short sIn;
|
340 |
|
|
unsigned long ulDivisor;
|
341 |
|
|
unsigned char ucDivisorLow;
|
342 |
|
|
unsigned char ucDivisorHigh;
|
343 |
|
|
unsigned char ucCommParam;
|
344 |
|
|
|
345 |
|
|
/* IRQ numbers - standard */
|
346 |
|
|
if( ( ePort == serCOM1 ) || ( ePort == serCOM3 ) || ( ePort == serCOM5 ) || ( ePort == serCOM7 ) )
|
347 |
|
|
{
|
348 |
|
|
pxPort->ucIRQ = serCOM1_STANDARD_IRQ;
|
349 |
|
|
pxPort->sPort = 0x3f8;
|
350 |
|
|
}
|
351 |
|
|
else
|
352 |
|
|
{
|
353 |
|
|
pxPort->ucIRQ = serCOM2_STANDARD_IRQ;
|
354 |
|
|
pxPort->sPort = 0x2f8;
|
355 |
|
|
}
|
356 |
|
|
|
357 |
|
|
/* Set up variables in port making it easy to see which sIn/o address is which */
|
358 |
|
|
pxPort->usTransmitHoldReg = pxPort->sPort + serTRANSMIT_HOLD_OFFSET;
|
359 |
|
|
pxPort->usReceiveDataRegister = pxPort->sPort + serRECEIVE_DATA_OFFSET;
|
360 |
|
|
pxPort->usBaudRateDivisorLow = pxPort->sPort + serBAUD_RATE_DIVISOR_LOW_OFFSET;
|
361 |
|
|
pxPort->usBaudRateDivisorHigh = pxPort->sPort + serBAUD_RATE_DIVISOR_HIGH_OFFSET;
|
362 |
|
|
pxPort->usInterruptEnableReg = pxPort->sPort + serINTERRUPT_ENABLE_OFFSET;
|
363 |
|
|
pxPort->usInterruptIDReg = pxPort->sPort + serINTERRUPT_ID_OFFSET;
|
364 |
|
|
pxPort->usFIFOCtrlReg = pxPort->sPort + serFIFO_CTRL_OFFSET;
|
365 |
|
|
pxPort->usLineCtrlReg = pxPort->sPort + serLINE_CTRL_OFFSET;
|
366 |
|
|
pxPort->usModemCtrlReg = pxPort->sPort + serMODEM_CTRL_OFFSET;
|
367 |
|
|
pxPort->usLineStatusReg = pxPort->sPort + serLINE_STATUS_OFFSET;
|
368 |
|
|
pxPort->usModemStatusReg = pxPort->sPort + serMODEM_STATUS_OFFSET;
|
369 |
|
|
pxPort->usSCRReg = pxPort->sPort + serSCR_OFFSET;
|
370 |
|
|
|
371 |
|
|
/* Set communication parameters. */
|
372 |
|
|
ulDivisor = serMAX_BAUD / ulBaudFromEnum[ eWantedBaud ];
|
373 |
|
|
ucDivisorLow = ( unsigned char ) ulDivisor & ( unsigned char ) 0xff;
|
374 |
|
|
ucDivisorHigh = ( unsigned char ) ( ( ( unsigned short ) ulDivisor >> 8 ) & 0xff );
|
375 |
|
|
|
376 |
|
|
switch( eWantedParity )
|
377 |
|
|
{
|
378 |
|
|
case serNO_PARITY: ucCommParam = 0x00;
|
379 |
|
|
break;
|
380 |
|
|
case serODD_PARITY: ucCommParam = 0x08;
|
381 |
|
|
break;
|
382 |
|
|
case serEVEN_PARITY: ucCommParam = 0x18;
|
383 |
|
|
break;
|
384 |
|
|
case serMARK_PARITY: ucCommParam = 0x28;
|
385 |
|
|
break;
|
386 |
|
|
case serSPACE_PARITY: ucCommParam = 0x38;
|
387 |
|
|
break;
|
388 |
|
|
default: ucCommParam = 0x00;
|
389 |
|
|
break;
|
390 |
|
|
}
|
391 |
|
|
|
392 |
|
|
switch ( eWantedDataBits )
|
393 |
|
|
{
|
394 |
|
|
case serBITS_5: ucCommParam |= 0x00;
|
395 |
|
|
break;
|
396 |
|
|
case serBITS_6: ucCommParam |= 0x01;
|
397 |
|
|
break;
|
398 |
|
|
case serBITS_7: ucCommParam |= 0x02;
|
399 |
|
|
break;
|
400 |
|
|
case serBITS_8: ucCommParam |= 0x03;
|
401 |
|
|
break;
|
402 |
|
|
default: ucCommParam |= 0x03;
|
403 |
|
|
break;
|
404 |
|
|
}
|
405 |
|
|
|
406 |
|
|
if( eWantedStopBits == serSTOP_2 )
|
407 |
|
|
{
|
408 |
|
|
ucCommParam |= 0x04;
|
409 |
|
|
}
|
410 |
|
|
|
411 |
|
|
/* Reset UART into known state - Thanks to Bradley Town */
|
412 |
|
|
portOUTPUT_BYTE( pxPort->usLineCtrlReg, 0x00 ); /* Access usTransmitHoldReg/RBR/usInterruptEnableReg */
|
413 |
|
|
portOUTPUT_BYTE( pxPort->usInterruptEnableReg, 0x00 ); /* Disable interrupts from UART */
|
414 |
|
|
portOUTPUT_BYTE( pxPort->usModemCtrlReg, 0x04 ); /* Enable some multi-port cards */
|
415 |
|
|
|
416 |
|
|
/* Code based on stuff from SVAsync lib. Clear UART Status and data registers
|
417 |
|
|
setting up FIFO if possible */
|
418 |
|
|
sIn = portINPUT_BYTE( pxPort->usSCRReg );
|
419 |
|
|
portOUTPUT_BYTE( pxPort->usSCRReg, 0x55 );
|
420 |
|
|
|
421 |
|
|
if( portINPUT_BYTE( pxPort->usSCRReg ) == 0x55 )
|
422 |
|
|
{
|
423 |
|
|
/* The chip is better than an 8250 */
|
424 |
|
|
portOUTPUT_BYTE( pxPort->usSCRReg, sIn ); /* Set usSCRReg back to what it was before */
|
425 |
|
|
portINPUT_BYTE( pxPort->usSCRReg); /* Give slow motherboards a chance */
|
426 |
|
|
|
427 |
|
|
/* Try and start the FIFO. It appears that some chips need a two call
|
428 |
|
|
protocol, but those that don't seem to work even if you do start it twice.
|
429 |
|
|
The first call is simply to start it, the second starts it and sets an 8
|
430 |
|
|
byte FIFO trigger level. */
|
431 |
|
|
portOUTPUT_BYTE( pxPort->usFIFOCtrlReg, 0x01 );
|
432 |
|
|
portINPUT_BYTE( pxPort->usFIFOCtrlReg ); /* Give slow motherboards a chance to catch up */
|
433 |
|
|
portOUTPUT_BYTE( pxPort->usFIFOCtrlReg, 0x87 );
|
434 |
|
|
|
435 |
|
|
/* Check that the FIFO initialised */
|
436 |
|
|
if( ( portINPUT_BYTE( pxPort->usInterruptIDReg ) & 0xc0 ) != 0xc0 )
|
437 |
|
|
{
|
438 |
|
|
/* It didn't so we assume it isn't there but disable it to be on the
|
439 |
|
|
safe side. */
|
440 |
|
|
portOUTPUT_BYTE( pxPort->usInterruptIDReg, 0xfe );
|
441 |
|
|
}
|
442 |
|
|
}
|
443 |
|
|
|
444 |
|
|
/* End of (modified) SVAsync code.
|
445 |
|
|
Set interrupt parameters calculating mask for 8259 controller's
|
446 |
|
|
IMR and number of interrupt handler for given irq level */
|
447 |
|
|
if (pxPort->ucIRQ <= 7)
|
448 |
|
|
{
|
449 |
|
|
/* if 0<=irq<=7 first IMR address used */
|
450 |
|
|
pxPort->ucInterruptEnableMast = ~(0x01 << pxPort->ucIRQ);
|
451 |
|
|
pxPort->usIRQVector = pxPort->ucIRQ + 8;
|
452 |
|
|
pxPort->us8259InterruptMaskReg = serIMR_8259_0;
|
453 |
|
|
pxPort->us8259InterruptServiceReg = serISR_8259_0;
|
454 |
|
|
}
|
455 |
|
|
else
|
456 |
|
|
{
|
457 |
|
|
pxPort->ucInterruptEnableMast = ~( 0x01 << ( pxPort->ucIRQ % 8 ) );
|
458 |
|
|
pxPort->usIRQVector = 0x70 + ( pxPort->ucIRQ - 8) ;
|
459 |
|
|
pxPort->us8259InterruptMaskReg = serIMR_8259_1;
|
460 |
|
|
pxPort->us8259InterruptServiceReg = serISR_8259_1;
|
461 |
|
|
}
|
462 |
|
|
|
463 |
|
|
/* Set Port Toggle to usBaudRateDivisorLow/usBaudRateDivisorHigh registers
|
464 |
|
|
to set baud rate */
|
465 |
|
|
portOUTPUT_BYTE( pxPort->usLineCtrlReg, ucCommParam | 0x80 );
|
466 |
|
|
portOUTPUT_BYTE( pxPort->usBaudRateDivisorLow, ucDivisorLow );
|
467 |
|
|
portOUTPUT_BYTE( pxPort->usBaudRateDivisorHigh, ucDivisorHigh );
|
468 |
|
|
|
469 |
|
|
/* reset usLineCtrlReg and Port Toggleout */
|
470 |
|
|
portOUTPUT_BYTE( pxPort->usLineCtrlReg, ucCommParam & 0x7F );
|
471 |
|
|
|
472 |
|
|
portENTER_CRITICAL();
|
473 |
|
|
|
474 |
|
|
if( xPortStatus[ pxPort->ucIRQ ] == NULL )
|
475 |
|
|
{
|
476 |
|
|
xPortStatus[ pxPort->ucIRQ ] = pxPort;
|
477 |
|
|
}
|
478 |
|
|
|
479 |
|
|
xOldISRs[ pxPort->ucIRQ ] = _dos_getvect( pxPort->usIRQVector );
|
480 |
|
|
_dos_setvect( pxPort->usIRQVector, xISRs[ pxPort->ucIRQ ] );
|
481 |
|
|
|
482 |
|
|
/* enable interrupt pxPort->ucIRQ level */
|
483 |
|
|
portOUTPUT_BYTE( pxPort->us8259InterruptMaskReg, portINPUT_BYTE( pxPort->us8259InterruptMaskReg ) & pxPort->ucInterruptEnableMast );
|
484 |
|
|
|
485 |
|
|
/* And allow interrupts again now the hairy bit's done */
|
486 |
|
|
portEXIT_CRITICAL();
|
487 |
|
|
|
488 |
|
|
/* This version does not allow flow control. */
|
489 |
|
|
portOUTPUT_BYTE( pxPort->usModemCtrlReg, serALL_MODEM_CTRL_INTERRUPTS );
|
490 |
|
|
|
491 |
|
|
/* enable all communication's interrupts */
|
492 |
|
|
portOUTPUT_BYTE( pxPort->usInterruptEnableReg, serALL_COMS_INTERRUPTS );
|
493 |
|
|
}
|
494 |
|
|
/*-----------------------------------------------------------*/
|
495 |
|
|
|
496 |
|
|
static short sComPortISR( const xComPort * const pxPort )
|
497 |
|
|
{
|
498 |
|
|
short sInterruptID;
|
499 |
|
|
char cIn, cOut;
|
500 |
|
|
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
|
501 |
|
|
extern void vComTestUnsuspendTask( void );
|
502 |
|
|
|
503 |
|
|
portOUTPUT_BYTE( pxPort->us8259InterruptMaskReg, ( portINPUT_BYTE( pxPort->us8259InterruptMaskReg) | ~pxPort->ucInterruptEnableMast ) );
|
504 |
|
|
|
505 |
|
|
/* Decide which UART has issued the interrupt */
|
506 |
|
|
sInterruptID = portINPUT_BYTE( pxPort->usInterruptIDReg );
|
507 |
|
|
|
508 |
|
|
/* service whatever requests the calling UART may have. The top 4 bits are
|
509 |
|
|
either unused or indicate the presence of a functioning FIFO, which we don't
|
510 |
|
|
need to know. So trim them off to simplify the switch statement below. */
|
511 |
|
|
sInterruptID &= 0x0f;
|
512 |
|
|
do
|
513 |
|
|
{
|
514 |
|
|
switch( sInterruptID )
|
515 |
|
|
{
|
516 |
|
|
case 0x0c: /* Timeout
|
517 |
|
|
Called when FIFO not up to trigger level but no activity for
|
518 |
|
|
a while. Handled exactly as RDAINT, see below for
|
519 |
|
|
description. */
|
520 |
|
|
do
|
521 |
|
|
{
|
522 |
|
|
cIn = ( char ) portINPUT_BYTE( pxPort->usReceiveDataRegister );
|
523 |
|
|
xQueueSendFromISR( pxPort->xRxedChars, &cIn, &xHigherPriorityTaskWoken );
|
524 |
|
|
|
525 |
|
|
/* Also release the semaphore - this does nothing interesting and is just a test.
|
526 |
|
|
We first attempt to unsuspend the task to check the scheduler correctely detects
|
527 |
|
|
this as an invalid call, then give the semaphore for real. */
|
528 |
|
|
vComTestUnsuspendTask();
|
529 |
|
|
xSemaphoreGiveFromISR( pxPort->xTestSem, &xHigherPriorityTaskWoken );
|
530 |
|
|
|
531 |
|
|
} while( portINPUT_BYTE( pxPort->usLineStatusReg ) & 0x01 );
|
532 |
|
|
break;
|
533 |
|
|
|
534 |
|
|
case 0x06: /* LSINT */
|
535 |
|
|
portINPUT_BYTE( pxPort->usLineStatusReg );
|
536 |
|
|
break;
|
537 |
|
|
|
538 |
|
|
case 0x04: /* RDAINT */
|
539 |
|
|
/* The usInterruptIDReg flag tested above stops when the
|
540 |
|
|
FIFO is below the trigger level rather than empty, whereas
|
541 |
|
|
this flag allows one to empty it: (do loop because there
|
542 |
|
|
must be at least one to read by virtue of having got here.) */
|
543 |
|
|
do
|
544 |
|
|
{
|
545 |
|
|
cIn = ( char ) portINPUT_BYTE( pxPort->usReceiveDataRegister );
|
546 |
|
|
xQueueSendFromISR( pxPort->xRxedChars, &cIn, &xHigherPriorityTaskWoken );
|
547 |
|
|
|
548 |
|
|
/* Also release the semaphore - this does nothing interesting and is just a test.
|
549 |
|
|
We first attempt to unsuspend the task to check the scheduler correctely detects
|
550 |
|
|
this as an invalid call, then give the semaphore for real. */
|
551 |
|
|
vComTestUnsuspendTask();
|
552 |
|
|
xSemaphoreGiveFromISR( pxPort->xTestSem, &xHigherPriorityTaskWoken );
|
553 |
|
|
|
554 |
|
|
} while( portINPUT_BYTE( pxPort->usLineStatusReg ) & 0x01 );
|
555 |
|
|
break;
|
556 |
|
|
|
557 |
|
|
case 0x02: /* serTRANSMIT_HOLD_EMPTY_INT */
|
558 |
|
|
if( xQueueReceiveFromISR( pxPort->xCharsForTx, &cOut, &xHigherPriorityTaskWoken ) != pdTRUE )
|
559 |
|
|
{
|
560 |
|
|
/* Queue empty, nothing to send */
|
561 |
|
|
vInterruptOff( pxPort, serTRANSMIT_HOLD_EMPTY_INT);
|
562 |
|
|
}
|
563 |
|
|
else
|
564 |
|
|
{
|
565 |
|
|
portOUTPUT_BYTE( pxPort->usTransmitHoldReg, ( short ) cOut );
|
566 |
|
|
}
|
567 |
|
|
break;
|
568 |
|
|
|
569 |
|
|
case 0x00: /* MSINT */
|
570 |
|
|
portINPUT_BYTE( pxPort->usModemStatusReg );
|
571 |
|
|
break;
|
572 |
|
|
}
|
573 |
|
|
|
574 |
|
|
/* Get the next instruction, trimming as above */
|
575 |
|
|
sInterruptID = portINPUT_BYTE( pxPort->usInterruptIDReg ) & 0x0f;
|
576 |
|
|
|
577 |
|
|
} while( !( sInterruptID & 0x01 ) );
|
578 |
|
|
|
579 |
|
|
if( pxPort->ucIRQ > 7 )
|
580 |
|
|
{
|
581 |
|
|
portOUTPUT_BYTE( 0xA0, 0x60 + ( pxPort->ucIRQ & 0x07 ) );
|
582 |
|
|
portOUTPUT_BYTE( 0x20, 0x62);
|
583 |
|
|
}
|
584 |
|
|
else
|
585 |
|
|
{
|
586 |
|
|
portOUTPUT_BYTE( 0x20, 0x60 + pxPort->ucIRQ );
|
587 |
|
|
}
|
588 |
|
|
|
589 |
|
|
portOUTPUT_BYTE( pxPort->us8259InterruptMaskReg, portINPUT_BYTE( pxPort->us8259InterruptMaskReg ) & pxPort->ucInterruptEnableMast );
|
590 |
|
|
|
591 |
|
|
/* If posting any of the characters to a queue woke a task that was blocked on
|
592 |
|
|
the queue we may want to return to the task just woken (depending on its
|
593 |
|
|
priority relative to the task this ISR interrupted. */
|
594 |
|
|
return xHigherPriorityTaskWoken;
|
595 |
|
|
}
|
596 |
|
|
/*-----------------------------------------------------------*/
|
597 |
|
|
|
598 |
|
|
portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, char *pcRxedChar, portTickType xBlockTime )
|
599 |
|
|
{
|
600 |
|
|
/* Get the next character from the buffer, note that this routine is only
|
601 |
|
|
called having checked that the is (at least) one to get */
|
602 |
|
|
if( xQueueReceive( pxPort->xRxedChars, pcRxedChar, xBlockTime ) )
|
603 |
|
|
{
|
604 |
|
|
return pdTRUE;
|
605 |
|
|
}
|
606 |
|
|
else
|
607 |
|
|
{
|
608 |
|
|
return pdFALSE;
|
609 |
|
|
}
|
610 |
|
|
}
|
611 |
|
|
/*-----------------------------------------------------------*/
|
612 |
|
|
|
613 |
|
|
portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, char cOutChar, portTickType xBlockTime )
|
614 |
|
|
{
|
615 |
|
|
if( xQueueSend( pxPort->xCharsForTx, &cOutChar, xBlockTime ) != pdPASS )
|
616 |
|
|
{
|
617 |
|
|
return pdFAIL;
|
618 |
|
|
}
|
619 |
|
|
|
620 |
|
|
vInterruptOn( pxPort, serTRANSMIT_HOLD_EMPTY_INT );
|
621 |
|
|
|
622 |
|
|
return pdPASS;
|
623 |
|
|
}
|
624 |
|
|
/*-----------------------------------------------------------*/
|
625 |
|
|
|
626 |
|
|
void vSerialPutString( xComPortHandle pxPort, const char * const pcString, unsigned short usStringLength )
|
627 |
|
|
{
|
628 |
|
|
char * pcNextChar;
|
629 |
|
|
const portTickType xNoBlock = ( portTickType ) 0;
|
630 |
|
|
|
631 |
|
|
/* Stop warnings. */
|
632 |
|
|
( void ) usStringLength;
|
633 |
|
|
|
634 |
|
|
pcNextChar = ( char * ) pcString;
|
635 |
|
|
while( *pcNextChar )
|
636 |
|
|
{
|
637 |
|
|
xSerialPutChar( pxPort, *pcNextChar, xNoBlock );
|
638 |
|
|
pcNextChar++;
|
639 |
|
|
}
|
640 |
|
|
}
|
641 |
|
|
/*-----------------------------------------------------------*/
|
642 |
|
|
|
643 |
|
|
portBASE_TYPE xSerialWaitForSemaphore( xComPortHandle xPort )
|
644 |
|
|
{
|
645 |
|
|
const portTickType xBlockTime = ( portTickType ) 0xffff;
|
646 |
|
|
|
647 |
|
|
/* This function does nothing interesting, but test the
|
648 |
|
|
semaphore from ISR mechanism. */
|
649 |
|
|
return xSemaphoreTake( xPort->xTestSem, xBlockTime );
|
650 |
|
|
}
|
651 |
|
|
/*-----------------------------------------------------------*/
|
652 |
|
|
|
653 |
|
|
void vSerialClose( xComPortHandle xPort )
|
654 |
|
|
{
|
655 |
|
|
portENTER_CRITICAL();
|
656 |
|
|
|
657 |
|
|
/* Turn off the interrupts. */
|
658 |
|
|
portOUTPUT_BYTE( xPort->usModemCtrlReg, serNO_INTERRUPTS );
|
659 |
|
|
portOUTPUT_BYTE( xPort->usInterruptEnableReg, serNO_INTERRUPTS );
|
660 |
|
|
|
661 |
|
|
/* Put back the original ISR. */
|
662 |
|
|
_dos_setvect( xPort->usIRQVector, xOldISRs[ xPort->ucIRQ ] );
|
663 |
|
|
|
664 |
|
|
/* Remove the reference in the array of xComPort structures. */
|
665 |
|
|
xPortStatus[ xPort->ucIRQ ] = NULL;
|
666 |
|
|
|
667 |
|
|
/* Delete the queues. */
|
668 |
|
|
vQueueDelete( xPort->xRxedChars );
|
669 |
|
|
vQueueDelete( xPort->xCharsForTx );
|
670 |
|
|
|
671 |
|
|
vPortFree( ( void * ) xPort );
|
672 |
|
|
|
673 |
|
|
portEXIT_CRITICAL();
|
674 |
|
|
}
|
675 |
|
|
|