]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/WizNET_DEMO_GCC_ARM7/i2cISR.c
d57f814210072c30a9e7c2627d3691d3cbea9526
[freertos] / FreeRTOS / Demo / WizNET_DEMO_GCC_ARM7 / i2cISR.c
1 /*\r
2     FreeRTOS V8.2.0 - Copyright (C) 2015 Real Time Engineers Ltd.\r
3     All rights reserved\r
4 \r
5     VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.\r
6 \r
7     This file is part of the FreeRTOS distribution.\r
8 \r
9     FreeRTOS is free software; you can redistribute it and/or modify it under\r
10     the terms of the GNU General Public License (version 2) as published by the\r
11     Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.\r
12 \r
13         ***************************************************************************\r
14     >>!   NOTE: The modification to the GPL is included to allow you to     !<<\r
15     >>!   distribute a combined work that includes FreeRTOS without being   !<<\r
16     >>!   obliged to provide the source code for proprietary components     !<<\r
17     >>!   outside of the FreeRTOS kernel.                                   !<<\r
18         ***************************************************************************\r
19 \r
20     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY\r
21     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\r
22     FOR A PARTICULAR PURPOSE.  Full license text is available on the following\r
23     link: http://www.freertos.org/a00114.html\r
24 \r
25     ***************************************************************************\r
26      *                                                                       *\r
27      *    FreeRTOS provides completely free yet professionally developed,    *\r
28      *    robust, strictly quality controlled, supported, and cross          *\r
29      *    platform software that is more than just the market leader, it     *\r
30      *    is the industry's de facto standard.                               *\r
31      *                                                                       *\r
32      *    Help yourself get started quickly while simultaneously helping     *\r
33      *    to support the FreeRTOS project by purchasing a FreeRTOS           *\r
34      *    tutorial book, reference manual, or both:                          *\r
35      *    http://www.FreeRTOS.org/Documentation                              *\r
36      *                                                                       *\r
37     ***************************************************************************\r
38 \r
39     http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading\r
40         the FAQ page "My application does not run, what could be wrong?".  Have you\r
41         defined configASSERT()?\r
42 \r
43         http://www.FreeRTOS.org/support - In return for receiving this top quality\r
44         embedded software for free we request you assist our global community by\r
45         participating in the support forum.\r
46 \r
47         http://www.FreeRTOS.org/training - Investing in training allows your team to\r
48         be as productive as possible as early as possible.  Now you can receive\r
49         FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers\r
50         Ltd, and the world's leading authority on the world's leading RTOS.\r
51 \r
52     http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,\r
53     including FreeRTOS+Trace - an indispensable productivity tool, a DOS\r
54     compatible FAT file system, and our tiny thread aware UDP/IP stack.\r
55 \r
56     http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.\r
57     Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.\r
58 \r
59     http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High\r
60     Integrity Systems ltd. to sell under the OpenRTOS brand.  Low cost OpenRTOS\r
61     licenses offer ticketed support, indemnification and commercial middleware.\r
62 \r
63     http://www.SafeRTOS.com - High Integrity Systems also provide a safety\r
64     engineered and independently SIL3 certified version for use in safety and\r
65     mission critical applications that require provable dependability.\r
66 \r
67     1 tab == 4 spaces!\r
68 */\r
69 \r
70 \r
71 /* Standard includes. */\r
72 #include <stdlib.h>\r
73 \r
74 /* Scheduler include files. */\r
75 #include "FreeRTOS.h"\r
76 #include "task.h"\r
77 #include "queue.h"\r
78 #include "semphr.h"\r
79 \r
80 /* Application includes. */\r
81 #include "i2c.h"\r
82 \r
83 /*-----------------------------------------------------------*/\r
84 \r
85 /* Bit definitions within the I2CONCLR register. */\r
86 #define i2cSTA_BIT              ( ( unsigned char ) 0x20 )\r
87 #define i2cSI_BIT               ( ( unsigned char ) 0x08 )\r
88 #define i2cSTO_BIT              ( ( unsigned char ) 0x10 )\r
89 #define i2cAA_BIT               ( ( unsigned char ) 0x04 )\r
90 \r
91 /* Status codes for the I2STAT register. */\r
92 #define i2cSTATUS_START_TXED                    ( 0x08 )\r
93 #define i2cSTATUS_REP_START_TXED                ( 0x10 )\r
94 #define i2cSTATUS_TX_ADDR_ACKED                 ( 0x18 )\r
95 #define i2cSTATUS_DATA_TXED                             ( 0x28 )\r
96 #define i2cSTATUS_RX_ADDR_ACKED                 ( 0x40 )\r
97 #define i2cSTATUS_DATA_RXED                             ( 0x50 )\r
98 #define i2cSTATUS_LAST_BYTE_RXED                ( 0x58 )\r
99 \r
100 /* Constants for operation of the VIC. */\r
101 #define i2cCLEAR_VIC_INTERRUPT  ( 0 )\r
102 \r
103 /* Misc constants. */\r
104 #define i2cJUST_ONE_BYTE_TO_RX  ( 1 )\r
105 #define i2cBUFFER_ADDRESS_BYTES ( 2 )\r
106 \r
107 /* End the current transmission and free the bus. */\r
108 #define i2cEND_TRANSMISSION( lStatus )                                  \\r
109 {                                                                                                               \\r
110         I2C_I2CONCLR = i2cAA_BIT;                                                       \\r
111         I2C_I2CONSET = i2cSTO_BIT;                                                      \\r
112         eCurrentState = eSentStart;                                                     \\r
113         lTransactionCompleted = lStatus;                                        \\r
114 }\r
115 /*-----------------------------------------------------------*/\r
116 \r
117 /* Valid i2c communication states. */\r
118 typedef enum\r
119 {\r
120         eSentStart,                             /*<< Last action was the transmission of a start bit. */\r
121         eSentAddressForWrite,   /*<< Last action was the transmission of the slave address we are to write to. */\r
122         eSentAddressForRead,    /*<< Last action was the transmission of the slave address we are to read from. */\r
123         eSentData,                              /*<< Last action was the transmission of a data byte. */\r
124         eReceiveData                    /*<< We expected data to be received. */\r
125 } I2C_STATE;\r
126 /*-----------------------------------------------------------*/\r
127 \r
128 /* Points to the message currently being sent. */\r
129 volatile xI2CMessage *pxCurrentMessage = NULL;  \r
130 \r
131 /* The queue of messages waiting to be transmitted. */\r
132 static QueueHandle_t xMessagesForTx;\r
133 \r
134 /* Flag used to indicate whether or not the ISR is amid sending a message. */\r
135 unsigned long ulBusFree = ( unsigned long ) pdTRUE;\r
136 \r
137 /* Setting this to true will cause the TCP task to think a message is \r
138 complete and thus restart.  It can therefore be used under error states\r
139 to force a restart. */\r
140 volatile long lTransactionCompleted = pdTRUE;\r
141 \r
142 /*-----------------------------------------------------------*/\r
143 \r
144 void vI2CISRCreateQueues( unsigned portBASE_TYPE uxQueueLength, QueueHandle_t *pxTxMessages, unsigned long **ppulBusFree )\r
145 {\r
146         /* Create the queues used to hold Rx and Tx characters. */\r
147         xMessagesForTx = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( xI2CMessage * ) );\r
148 \r
149         /* Pass back a reference to the queue and bus free flag so the I2C API file \r
150         can post messages. */\r
151         *pxTxMessages = xMessagesForTx;\r
152         *ppulBusFree = &ulBusFree;\r
153 }\r
154 /*-----------------------------------------------------------*/\r
155 \r
156 /* The ISR entry point. */\r
157 void vI2C_ISR_Wrapper( void ) __attribute__ (( naked ));\r
158 \r
159 /* The ISR function to perform the actual work.  This must be a separate\r
160 function from the wrapper to ensure the correct stack frame is set up. */\r
161 void vI2C_ISR_Handler( void );\r
162 \r
163 /*-----------------------------------------------------------*/\r
164 \r
165 void vI2C_ISR_Wrapper( void )\r
166 {\r
167         /* Save the context of the interrupted task. */\r
168         portSAVE_CONTEXT();\r
169 \r
170         /* Call the handler to perform the actual work.  This must be a\r
171         separate function to ensure the correct stack frame is set up. */\r
172         vI2C_ISR_Handler();\r
173 \r
174         /* Restore the context of whichever task is going to run next. */\r
175         portRESTORE_CONTEXT();\r
176 }\r
177 /*-----------------------------------------------------------*/\r
178 \r
179 void vI2C_ISR_Handler( void )\r
180 {\r
181 /* Holds the current transmission state. */                                                     \r
182 static I2C_STATE eCurrentState = eSentStart;\r
183 static long lMessageIndex = -i2cBUFFER_ADDRESS_BYTES; /* There are two address bytes to send prior to the data. */\r
184 portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;\r
185 long lBytesLeft;\r
186 \r
187         /* The action taken for this interrupt depends on our current state. */\r
188         switch( eCurrentState )\r
189         {\r
190                 case eSentStart :       \r
191 \r
192                                 /* We sent a start bit, if it was successful we can\r
193                                 go on to send the slave address. */\r
194                                 if( ( I2C_I2STAT == i2cSTATUS_START_TXED ) || ( I2C_I2STAT == i2cSTATUS_REP_START_TXED ) )\r
195                                 {\r
196                                         /* Send the slave address. */\r
197                                         I2C_I2DAT = pxCurrentMessage->ucSlaveAddress;\r
198 \r
199                                         if( pxCurrentMessage->ucSlaveAddress & i2cREAD )\r
200                                         {\r
201                                                 /* We are then going to read bytes back from the \r
202                                                 slave. */\r
203                                                 eCurrentState = eSentAddressForRead;\r
204                                                 \r
205                                                 /* Initialise the buffer index so the first byte goes\r
206                                                 into the first buffer position. */\r
207                                                 lMessageIndex = 0;\r
208                                         }\r
209                                         else\r
210                                         {\r
211                                                 /* We are then going to write some data to the slave. */\r
212                                                 eCurrentState = eSentAddressForWrite;\r
213 \r
214                                                 /* When writing bytes we first have to send the two\r
215                                                 byte buffer address so lMessageIndex is set negative,\r
216                                                 when it reaches 0 it is time to send the actual data. */\r
217                                                 lMessageIndex = -i2cBUFFER_ADDRESS_BYTES;\r
218                                         }\r
219                                 }\r
220                                 else\r
221                                 {\r
222                                         /* Could not send the start bit so give up. */\r
223                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
224                                 }\r
225 \r
226                                 I2C_I2CONCLR = i2cSTA_BIT;                              \r
227 \r
228                                 break;\r
229 \r
230                 case eSentAddressForWrite :\r
231 \r
232                                 /* We sent the address of the slave we are going to write to.\r
233                                 If this was acknowledged we     can go on to send the data. */\r
234                                 if( I2C_I2STAT == i2cSTATUS_TX_ADDR_ACKED )\r
235                                 {\r
236                                         /* Start the first byte transmitting which is the \r
237                                         first byte of the buffer address to which the data will \r
238                                         be sent. */\r
239                                         I2C_I2DAT = pxCurrentMessage->ucBufferAddressHighByte;\r
240                                         eCurrentState = eSentData;\r
241                                 }\r
242                                 else\r
243                                 {\r
244                                         /* Address was not acknowledged so give up. */\r
245                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
246                                 }                                       \r
247                                 break;\r
248 \r
249                 case eSentAddressForRead :\r
250 \r
251                                 /* We sent the address of the slave we are going to read from.\r
252                                 If this was acknowledged we can go on to read the data. */\r
253                                 if( I2C_I2STAT == i2cSTATUS_RX_ADDR_ACKED )\r
254                                 {\r
255                                         eCurrentState = eReceiveData;\r
256                                         if( pxCurrentMessage->lMessageLength > i2cJUST_ONE_BYTE_TO_RX )\r
257                                         {\r
258                                                 /* Don't ack the last byte of the message. */\r
259                                                 I2C_I2CONSET = i2cAA_BIT;\r
260                                         }                                       \r
261                                 }\r
262                                 else\r
263                                 {\r
264                                         /* Something unexpected happened - give up. */\r
265                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
266                                 }\r
267                                 break;\r
268 \r
269                 case eReceiveData :\r
270                                 \r
271                                 /* We have just received a byte from the slave. */\r
272                                 if( ( I2C_I2STAT == i2cSTATUS_DATA_RXED ) || ( I2C_I2STAT == i2cSTATUS_LAST_BYTE_RXED ) )\r
273                                 {\r
274                                         /* Buffer the byte just received then increment the index \r
275                                         so it points to the next free space. */\r
276                                         pxCurrentMessage->pucBuffer[ lMessageIndex ] = I2C_I2DAT;\r
277                                         lMessageIndex++;\r
278 \r
279                                         /* How many more bytes are we expecting to receive? */\r
280                                         lBytesLeft = pxCurrentMessage->lMessageLength - lMessageIndex;\r
281                                         if( lBytesLeft == ( unsigned long ) 0 )\r
282                                         {\r
283                                                 /* This was the last byte in the message. */\r
284                                                 i2cEND_TRANSMISSION( pdPASS );\r
285 \r
286                                                 /* If xMessageCompleteSemaphore is not null then there\r
287                                                 is a task waiting for this message to complete and we\r
288                                                 must 'give' the semaphore so the task is woken.*/\r
289                                                 if( pxCurrentMessage->xMessageCompleteSemaphore )\r
290                                                 {\r
291                                                         xSemaphoreGiveFromISR( pxCurrentMessage->xMessageCompleteSemaphore, &xHigherPriorityTaskWoken );\r
292                                                 }\r
293 \r
294                                                 /* Are there any other messages to transact? */\r
295                                                 if( xQueueReceiveFromISR( xMessagesForTx, &pxCurrentMessage, &xHigherPriorityTaskWoken ) == pdTRUE )\r
296                                                 {\r
297                                                         /* Start the next message - which was\r
298                                                         retrieved from the queue. */\r
299                                                         I2C_I2CONSET = i2cSTA_BIT;\r
300                                                 }\r
301                                                 else\r
302                                                 {\r
303                                                         /* No more messages were found to be waiting for\r
304                                                         transaction so the bus is free. */\r
305                                                         ulBusFree = ( unsigned long ) pdTRUE;                   \r
306                                                 }                                               \r
307                                         }\r
308                                         else\r
309                                         {\r
310                                                 /* There are more bytes to receive but don't ack the \r
311                                                 last byte. */\r
312                                                 if( lBytesLeft <= i2cJUST_ONE_BYTE_TO_RX )\r
313                                                 {\r
314                                                         I2C_I2CONCLR = i2cAA_BIT;\r
315                                                 }                                                        \r
316                                         }\r
317                                 }\r
318                                 else\r
319                                 {\r
320                                         /* Something unexpected happened - give up. */\r
321                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
322                                 }\r
323 \r
324                                 break;\r
325                                 \r
326                 case eSentData  :       \r
327 \r
328                                 /* We sent a data byte, if successful send the  next byte in \r
329                                 the message. */\r
330                                 if( I2C_I2STAT == i2cSTATUS_DATA_TXED )\r
331                                 {\r
332                                         /* Index to the next byte to send. */\r
333                                         lMessageIndex++;\r
334                                         if( lMessageIndex < 0 )\r
335                                         {\r
336                                                 /* lMessage index is still negative so we have so far \r
337                                                 only sent the first byte of the buffer address.  Send \r
338                                                 the second byte now, then initialise the buffer index\r
339                                                 to zero so the next byte sent comes from the actual \r
340                                                 data buffer. */\r
341                                                 I2C_I2DAT = pxCurrentMessage->ucBufferAddressLowByte;\r
342                                         }\r
343                                         else if( lMessageIndex < pxCurrentMessage->lMessageLength )\r
344                                         {\r
345                                                 /* Simply send the next byte in the tx buffer. */\r
346                                                 I2C_I2DAT = pxCurrentMessage->pucBuffer[ lMessageIndex ];                                                                               \r
347                                         }\r
348                                         else\r
349                                         {\r
350                                                 /* No more bytes in this message to be send.  Finished \r
351                                                 sending message - send a stop bit. */\r
352                                                 i2cEND_TRANSMISSION( pdPASS );\r
353 \r
354                                                 /* If xMessageCompleteSemaphore is not null then there\r
355                                                 is a task waiting for this message to be sent and the\r
356                                                 semaphore must be 'given' to wake the task. */\r
357                                                 if( pxCurrentMessage->xMessageCompleteSemaphore )\r
358                                                 {\r
359                                                         xSemaphoreGiveFromISR( pxCurrentMessage->xMessageCompleteSemaphore, &xHigherPriorityTaskWoken );\r
360                                                 }\r
361 \r
362                                                 /* Are there any other messages to transact? */\r
363                                                 if( xQueueReceiveFromISR( xMessagesForTx, &pxCurrentMessage, &xHigherPriorityTaskWoken ) == pdTRUE )\r
364                                                 {\r
365                                                         /* Start the next message from the Tx queue. */\r
366                                                         I2C_I2CONSET = i2cSTA_BIT;\r
367                                                 }\r
368                                                 else\r
369                                                 {\r
370                                                         /* No more message were queues for transaction so \r
371                                                         the bus is free. */\r
372                                                         ulBusFree = ( unsigned long ) pdTRUE;                   \r
373                                                 }\r
374                                         }\r
375                                 }\r
376                                 else\r
377                                 {\r
378                                         /* Something unexpected happened, give up. */\r
379                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
380                                 }\r
381                                 break;\r
382 \r
383                 default :       \r
384                 \r
385                                 /* Should never get here. */\r
386                                 eCurrentState = eSentStart;\r
387                                 break;\r
388         }       \r
389 \r
390         /* Clear the interrupt. */\r
391         I2C_I2CONCLR = i2cSI_BIT;\r
392         VICVectAddr = i2cCLEAR_VIC_INTERRUPT;\r
393 \r
394         if( xHigherPriorityTaskWoken )\r
395         {\r
396                 portYIELD_FROM_ISR();\r
397         }\r
398 }\r
399 /*-----------------------------------------------------------*/\r
400 \r