]> git.sur5r.net Git - freertos/blob - Demo/WizNET_DEMO_GCC_ARM7/i2cISR.c
git-svn-id: https://svn.code.sf.net/p/freertos/code/trunk@14 1d2547de-c912-0410-9cb9...
[freertos] / Demo / WizNET_DEMO_GCC_ARM7 / i2cISR.c
1 /*\r
2         FreeRTOS.org V4.0.3 - copyright (C) 2003-2006 Richard Barry.\r
3 \r
4         This file is part of the FreeRTOS.org distribution.\r
5 \r
6         FreeRTOS.org is free software; you can redistribute it and/or modify\r
7         it under the terms of the GNU General Public License as published by\r
8         the Free Software Foundation; either version 2 of the License, or\r
9         (at your option) any later version.\r
10 \r
11         FreeRTOS.org is distributed in the hope that it will be useful,\r
12         but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14         GNU General Public License for more details.\r
15 \r
16         You should have received a copy of the GNU General Public License\r
17         along with FreeRTOS.org; if not, write to the Free Software\r
18         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19 \r
20         A special exception to the GPL can be applied should you wish to distribute\r
21         a combined work that includes FreeRTOS.org, without being obliged to provide\r
22         the source code for any proprietary components.  See the licensing section \r
23         of http://www.FreeRTOS.org for full details of how and when the exception\r
24         can be applied.\r
25 \r
26         ***************************************************************************\r
27         See http://www.FreeRTOS.org for documentation, latest information, license \r
28         and contact details.  Please ensure to read the configuration and relevant \r
29         port sections of the online documentation.\r
30         ***************************************************************************\r
31 */\r
32 \r
33 \r
34 /* Standard includes. */\r
35 #include <stdlib.h>\r
36 \r
37 /* Scheduler include files. */\r
38 #include "FreeRTOS.h"\r
39 #include "task.h"\r
40 #include "queue.h"\r
41 #include "semphr.h"\r
42 \r
43 /* Application includes. */\r
44 #include "i2c.h"\r
45 \r
46 /*-----------------------------------------------------------*/\r
47 \r
48 /* Bit definitions within the I2CONCLR register. */\r
49 #define i2cSTA_BIT              ( ( unsigned portCHAR ) 0x20 )\r
50 #define i2cSI_BIT               ( ( unsigned portCHAR ) 0x08 )\r
51 #define i2cSTO_BIT              ( ( unsigned portCHAR ) 0x10 )\r
52 #define i2cAA_BIT               ( ( unsigned portCHAR ) 0x04 )\r
53 \r
54 /* Status codes for the I2STAT register. */\r
55 #define i2cSTATUS_START_TXED                    ( 0x08 )\r
56 #define i2cSTATUS_REP_START_TXED                ( 0x10 )\r
57 #define i2cSTATUS_TX_ADDR_ACKED                 ( 0x18 )\r
58 #define i2cSTATUS_DATA_TXED                             ( 0x28 )\r
59 #define i2cSTATUS_RX_ADDR_ACKED                 ( 0x40 )\r
60 #define i2cSTATUS_DATA_RXED                             ( 0x50 )\r
61 #define i2cSTATUS_LAST_BYTE_RXED                ( 0x58 )\r
62 \r
63 /* Constants for operation of the VIC. */\r
64 #define i2cCLEAR_VIC_INTERRUPT  ( 0 )\r
65 \r
66 /* Misc constants. */\r
67 #define i2cJUST_ONE_BYTE_TO_RX  ( 1 )\r
68 #define i2cBUFFER_ADDRESS_BYTES ( 2 )\r
69 \r
70 /* End the current transmission and free the bus. */\r
71 #define i2cEND_TRANSMISSION( lStatus )                                  \\r
72 {                                                                                                               \\r
73         I2C_I2CONCLR = i2cAA_BIT;                                                       \\r
74         I2C_I2CONSET = i2cSTO_BIT;                                                      \\r
75         eCurrentState = eSentStart;                                                     \\r
76         lTransactionCompleted = lStatus;                                        \\r
77 }\r
78 /*-----------------------------------------------------------*/\r
79 \r
80 /* Valid i2c communication states. */\r
81 typedef enum\r
82 {\r
83         eSentStart,                             /*<< Last action was the transmission of a start bit. */\r
84         eSentAddressForWrite,   /*<< Last action was the transmission of the slave address we are to write to. */\r
85         eSentAddressForRead,    /*<< Last action was the transmission of the slave address we are to read from. */\r
86         eSentData,                              /*<< Last action was the transmission of a data byte. */\r
87         eReceiveData                    /*<< We expected data to be received. */\r
88 } I2C_STATE;\r
89 /*-----------------------------------------------------------*/\r
90 \r
91 /* Points to the message currently being sent. */\r
92 volatile xI2CMessage *pxCurrentMessage = NULL;  \r
93 \r
94 /* The queue of messages waiting to be transmitted. */\r
95 static xQueueHandle xMessagesForTx;\r
96 \r
97 /* Flag used to indicate whether or not the ISR is amid sending a message. */\r
98 unsigned portLONG ulBusFree = ( unsigned portLONG ) pdTRUE;\r
99 \r
100 /* Setting this to true will cause the TCP task to think a message is \r
101 complete and thus restart.  It can therefore be used under error states\r
102 to force a restart. */\r
103 volatile portLONG lTransactionCompleted = pdTRUE;\r
104 \r
105 /*-----------------------------------------------------------*/\r
106 \r
107 void vI2CISRCreateQueues( unsigned portBASE_TYPE uxQueueLength, xQueueHandle *pxTxMessages, unsigned portLONG **ppulBusFree )\r
108 {\r
109         /* Create the queues used to hold Rx and Tx characters. */\r
110         xMessagesForTx = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( xI2CMessage * ) );\r
111 \r
112         /* Pass back a reference to the queue and bus free flag so the I2C API file \r
113         can post messages. */\r
114         *pxTxMessages = xMessagesForTx;\r
115         *ppulBusFree = &ulBusFree;\r
116 }\r
117 /*-----------------------------------------------------------*/\r
118 \r
119 void vI2C_ISR( void ) __attribute__ (( naked ));\r
120 void vI2C_ISR( void )\r
121 {\r
122         portENTER_SWITCHING_ISR();\r
123 \r
124         /* Holds the current transmission state. */                                                     \r
125         static I2C_STATE eCurrentState = eSentStart;\r
126         static portLONG lMessageIndex = -i2cBUFFER_ADDRESS_BYTES; /* There are two address bytes to send prior to the data. */\r
127         portBASE_TYPE xTaskWokenByTx = pdFALSE;\r
128         portLONG lBytesLeft;\r
129 \r
130         /* The action taken for this interrupt depends on our current state. */\r
131         switch( eCurrentState )\r
132         {\r
133                 case eSentStart :       \r
134 \r
135                                 /* We sent a start bit, if it was successful we can\r
136                                 go on to send the slave address. */\r
137                                 if( ( I2C_I2STAT == i2cSTATUS_START_TXED ) || ( I2C_I2STAT == i2cSTATUS_REP_START_TXED ) )\r
138                                 {\r
139                                         /* Send the slave address. */\r
140                                         I2C_I2DAT = pxCurrentMessage->ucSlaveAddress;\r
141 \r
142                                         if( pxCurrentMessage->ucSlaveAddress & i2cREAD )\r
143                                         {\r
144                                                 /* We are then going to read bytes back from the \r
145                                                 slave. */\r
146                                                 eCurrentState = eSentAddressForRead;\r
147                                                 \r
148                                                 /* Initialise the buffer index so the first byte goes\r
149                                                 into the first buffer position. */\r
150                                                 lMessageIndex = 0;\r
151                                         }\r
152                                         else\r
153                                         {\r
154                                                 /* We are then going to write some data to the slave. */\r
155                                                 eCurrentState = eSentAddressForWrite;\r
156 \r
157                                                 /* When writing bytes we first have to send the two\r
158                                                 byte buffer address so lMessageIndex is set negative,\r
159                                                 when it reaches 0 it is time to send the actual data. */\r
160                                                 lMessageIndex = -i2cBUFFER_ADDRESS_BYTES;\r
161                                         }\r
162                                 }\r
163                                 else\r
164                                 {\r
165                                         /* Could not send the start bit so give up. */\r
166                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
167                                 }\r
168 \r
169                                 I2C_I2CONCLR = i2cSTA_BIT;                              \r
170 \r
171                                 break;\r
172 \r
173                 case eSentAddressForWrite :\r
174 \r
175                                 /* We sent the address of the slave we are going to write to.\r
176                                 If this was acknowledged we     can go on to send the data. */\r
177                                 if( I2C_I2STAT == i2cSTATUS_TX_ADDR_ACKED )\r
178                                 {\r
179                                         /* Start the first byte transmitting which is the \r
180                                         first byte of the buffer address to which the data will \r
181                                         be sent. */\r
182                                         I2C_I2DAT = pxCurrentMessage->ucBufferAddressHighByte;\r
183                                         eCurrentState = eSentData;\r
184                                 }\r
185                                 else\r
186                                 {\r
187                                         /* Address was not acknowledged so give up. */\r
188                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
189                                 }                                       \r
190                                 break;\r
191 \r
192                 case eSentAddressForRead :\r
193 \r
194                                 /* We sent the address of the slave we are going to read from.\r
195                                 If this was acknowledged we can go on to read the data. */\r
196                                 if( I2C_I2STAT == i2cSTATUS_RX_ADDR_ACKED )\r
197                                 {\r
198                                         eCurrentState = eReceiveData;\r
199                                         if( pxCurrentMessage->lMessageLength > i2cJUST_ONE_BYTE_TO_RX )\r
200                                         {\r
201                                                 /* Don't ack the last byte of the message. */\r
202                                                 I2C_I2CONSET = i2cAA_BIT;\r
203                                         }                                       \r
204                                 }\r
205                                 else\r
206                                 {\r
207                                         /* Something unexpected happened - give up. */\r
208                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
209                                 }\r
210                                 break;\r
211 \r
212                 case eReceiveData :\r
213                                 \r
214                                 /* We have just received a byte from the slave. */\r
215                                 if( ( I2C_I2STAT == i2cSTATUS_DATA_RXED ) || ( I2C_I2STAT == i2cSTATUS_LAST_BYTE_RXED ) )\r
216                                 {\r
217                                         /* Buffer the byte just received then increment the index \r
218                                         so it points to the next free space. */\r
219                                         pxCurrentMessage->pucBuffer[ lMessageIndex ] = I2C_I2DAT;\r
220                                         lMessageIndex++;\r
221 \r
222                                         /* How many more bytes are we expecting to receive? */\r
223                                         lBytesLeft = pxCurrentMessage->lMessageLength - lMessageIndex;\r
224                                         if( lBytesLeft == ( unsigned portLONG ) 0 )\r
225                                         {\r
226                                                 /* This was the last byte in the message. */\r
227                                                 i2cEND_TRANSMISSION( pdPASS );\r
228 \r
229                                                 /* If xMessageCompleteSemaphore is not null then there\r
230                                                 is a task waiting for this message to complete and we\r
231                                                 must 'give' the semaphore so the task is woken.*/\r
232                                                 if( pxCurrentMessage->xMessageCompleteSemaphore )\r
233                                                 {\r
234                                                         xTaskWokenByTx = xSemaphoreGiveFromISR( pxCurrentMessage->xMessageCompleteSemaphore, xTaskWokenByTx );\r
235                                                 }\r
236 \r
237                                                 /* Are there any other messages to transact? */\r
238                                                 if( xQueueReceiveFromISR( xMessagesForTx, &pxCurrentMessage, &xTaskWokenByTx ) == pdTRUE )\r
239                                                 {\r
240                                                         /* Start the next message - which was\r
241                                                         retrieved from the queue. */\r
242                                                         I2C_I2CONSET = i2cSTA_BIT;\r
243                                                 }\r
244                                                 else\r
245                                                 {\r
246                                                         /* No more messages were found to be waiting for\r
247                                                         transaction so the bus is free. */\r
248                                                         ulBusFree = ( unsigned portLONG ) pdTRUE;                       \r
249                                                 }                                               \r
250                                         }\r
251                                         else\r
252                                         {\r
253                                                 /* There are more bytes to receive but don't ack the \r
254                                                 last byte. */\r
255                                                 if( lBytesLeft <= i2cJUST_ONE_BYTE_TO_RX )\r
256                                                 {\r
257                                                         I2C_I2CONCLR = i2cAA_BIT;\r
258                                                 }                                                        \r
259                                         }\r
260                                 }\r
261                                 else\r
262                                 {\r
263                                         /* Something unexpected happened - give up. */\r
264                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
265                                 }\r
266 \r
267                                 break;\r
268                                 \r
269                 case eSentData  :       \r
270 \r
271                                 /* We sent a data byte, if successful send the  next byte in \r
272                                 the message. */\r
273                                 if( I2C_I2STAT == i2cSTATUS_DATA_TXED )\r
274                                 {\r
275                                         /* Index to the next byte to send. */\r
276                                         lMessageIndex++;\r
277                                         if( lMessageIndex < 0 )\r
278                                         {\r
279                                                 /* lMessage index is still negative so we have so far \r
280                                                 only sent the first byte of the buffer address.  Send \r
281                                                 the second byte now, then initialise the buffer index\r
282                                                 to zero so the next byte sent comes from the actual \r
283                                                 data buffer. */\r
284                                                 I2C_I2DAT = pxCurrentMessage->ucBufferAddressLowByte;\r
285                                         }\r
286                                         else if( lMessageIndex < pxCurrentMessage->lMessageLength )\r
287                                         {\r
288                                                 /* Simply send the next byte in the tx buffer. */\r
289                                                 I2C_I2DAT = pxCurrentMessage->pucBuffer[ lMessageIndex ];                                                                               \r
290                                         }\r
291                                         else\r
292                                         {\r
293                                                 /* No more bytes in this message to be send.  Finished \r
294                                                 sending message - send a stop bit. */\r
295                                                 i2cEND_TRANSMISSION( pdPASS );\r
296 \r
297                                                 /* If xMessageCompleteSemaphore is not null then there\r
298                                                 is a task waiting for this message to be sent and the\r
299                                                 semaphore must be 'given' to wake the task. */\r
300                                                 if( pxCurrentMessage->xMessageCompleteSemaphore )\r
301                                                 {\r
302                                                         xTaskWokenByTx = xSemaphoreGiveFromISR( pxCurrentMessage->xMessageCompleteSemaphore, xTaskWokenByTx );\r
303                                                 }\r
304 \r
305                                                 /* Are there any other messages to transact? */\r
306                                                 if( xQueueReceiveFromISR( xMessagesForTx, &pxCurrentMessage, &xTaskWokenByTx ) == pdTRUE )\r
307                                                 {\r
308                                                         /* Start the next message from the Tx queue. */\r
309                                                         I2C_I2CONSET = i2cSTA_BIT;\r
310                                                 }\r
311                                                 else\r
312                                                 {\r
313                                                         /* No more message were queues for transaction so \r
314                                                         the bus is free. */\r
315                                                         ulBusFree = ( unsigned portLONG ) pdTRUE;                       \r
316                                                 }\r
317                                         }\r
318                                 }\r
319                                 else\r
320                                 {\r
321                                         /* Something unexpected happened, give up. */\r
322                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
323                                 }\r
324                                 break;\r
325 \r
326                 default :       \r
327                 \r
328                                 /* Should never get here. */\r
329                                 eCurrentState = eSentStart;\r
330                                 break;\r
331         }       \r
332 \r
333         /* Clear the interrupt. */\r
334         I2C_I2CONCLR = i2cSI_BIT;\r
335         VICVectAddr = i2cCLEAR_VIC_INTERRUPT;\r
336 \r
337         portEXIT_SWITCHING_ISR( ( xTaskWokenByTx ) );\r
338 }\r
339 /*-----------------------------------------------------------*/\r
340 \r