]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/WizNET_DEMO_GCC_ARM7/i2cISR.c
Update to MIT licensed FreeRTOS V10.0.0 - see https://www.freertos.org/History.txt
[freertos] / FreeRTOS / Demo / WizNET_DEMO_GCC_ARM7 / i2cISR.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 /* Standard includes. */\r
31 #include <stdlib.h>\r
32 \r
33 /* Scheduler include files. */\r
34 #include "FreeRTOS.h"\r
35 #include "task.h"\r
36 #include "queue.h"\r
37 #include "semphr.h"\r
38 \r
39 /* Application includes. */\r
40 #include "i2c.h"\r
41 \r
42 /*-----------------------------------------------------------*/\r
43 \r
44 /* Bit definitions within the I2CONCLR register. */\r
45 #define i2cSTA_BIT              ( ( unsigned char ) 0x20 )\r
46 #define i2cSI_BIT               ( ( unsigned char ) 0x08 )\r
47 #define i2cSTO_BIT              ( ( unsigned char ) 0x10 )\r
48 #define i2cAA_BIT               ( ( unsigned char ) 0x04 )\r
49 \r
50 /* Status codes for the I2STAT register. */\r
51 #define i2cSTATUS_START_TXED                    ( 0x08 )\r
52 #define i2cSTATUS_REP_START_TXED                ( 0x10 )\r
53 #define i2cSTATUS_TX_ADDR_ACKED                 ( 0x18 )\r
54 #define i2cSTATUS_DATA_TXED                             ( 0x28 )\r
55 #define i2cSTATUS_RX_ADDR_ACKED                 ( 0x40 )\r
56 #define i2cSTATUS_DATA_RXED                             ( 0x50 )\r
57 #define i2cSTATUS_LAST_BYTE_RXED                ( 0x58 )\r
58 \r
59 /* Constants for operation of the VIC. */\r
60 #define i2cCLEAR_VIC_INTERRUPT  ( 0 )\r
61 \r
62 /* Misc constants. */\r
63 #define i2cJUST_ONE_BYTE_TO_RX  ( 1 )\r
64 #define i2cBUFFER_ADDRESS_BYTES ( 2 )\r
65 \r
66 /* End the current transmission and free the bus. */\r
67 #define i2cEND_TRANSMISSION( lStatus )                                  \\r
68 {                                                                                                               \\r
69         I2C_I2CONCLR = i2cAA_BIT;                                                       \\r
70         I2C_I2CONSET = i2cSTO_BIT;                                                      \\r
71         eCurrentState = eSentStart;                                                     \\r
72         lTransactionCompleted = lStatus;                                        \\r
73 }\r
74 /*-----------------------------------------------------------*/\r
75 \r
76 /* Valid i2c communication states. */\r
77 typedef enum\r
78 {\r
79         eSentStart,                             /*<< Last action was the transmission of a start bit. */\r
80         eSentAddressForWrite,   /*<< Last action was the transmission of the slave address we are to write to. */\r
81         eSentAddressForRead,    /*<< Last action was the transmission of the slave address we are to read from. */\r
82         eSentData,                              /*<< Last action was the transmission of a data byte. */\r
83         eReceiveData                    /*<< We expected data to be received. */\r
84 } I2C_STATE;\r
85 /*-----------------------------------------------------------*/\r
86 \r
87 /* Points to the message currently being sent. */\r
88 volatile xI2CMessage *pxCurrentMessage = NULL;  \r
89 \r
90 /* The queue of messages waiting to be transmitted. */\r
91 static QueueHandle_t xMessagesForTx;\r
92 \r
93 /* Flag used to indicate whether or not the ISR is amid sending a message. */\r
94 unsigned long ulBusFree = ( unsigned long ) pdTRUE;\r
95 \r
96 /* Setting this to true will cause the TCP task to think a message is \r
97 complete and thus restart.  It can therefore be used under error states\r
98 to force a restart. */\r
99 volatile long lTransactionCompleted = pdTRUE;\r
100 \r
101 /*-----------------------------------------------------------*/\r
102 \r
103 void vI2CISRCreateQueues( unsigned portBASE_TYPE uxQueueLength, QueueHandle_t *pxTxMessages, unsigned long **ppulBusFree )\r
104 {\r
105         /* Create the queues used to hold Rx and Tx characters. */\r
106         xMessagesForTx = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( xI2CMessage * ) );\r
107 \r
108         /* Pass back a reference to the queue and bus free flag so the I2C API file \r
109         can post messages. */\r
110         *pxTxMessages = xMessagesForTx;\r
111         *ppulBusFree = &ulBusFree;\r
112 }\r
113 /*-----------------------------------------------------------*/\r
114 \r
115 /* The ISR entry point. */\r
116 void vI2C_ISR_Wrapper( void ) __attribute__ (( naked ));\r
117 \r
118 /* The ISR function to perform the actual work.  This must be a separate\r
119 function from the wrapper to ensure the correct stack frame is set up. */\r
120 void vI2C_ISR_Handler( void );\r
121 \r
122 /*-----------------------------------------------------------*/\r
123 \r
124 void vI2C_ISR_Wrapper( void )\r
125 {\r
126         /* Save the context of the interrupted task. */\r
127         portSAVE_CONTEXT();\r
128 \r
129         /* Call the handler to perform the actual work.  This must be a\r
130         separate function to ensure the correct stack frame is set up. */\r
131         vI2C_ISR_Handler();\r
132 \r
133         /* Restore the context of whichever task is going to run next. */\r
134         portRESTORE_CONTEXT();\r
135 }\r
136 /*-----------------------------------------------------------*/\r
137 \r
138 void vI2C_ISR_Handler( void )\r
139 {\r
140 /* Holds the current transmission state. */                                                     \r
141 static I2C_STATE eCurrentState = eSentStart;\r
142 static long lMessageIndex = -i2cBUFFER_ADDRESS_BYTES; /* There are two address bytes to send prior to the data. */\r
143 portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;\r
144 long lBytesLeft;\r
145 \r
146         /* The action taken for this interrupt depends on our current state. */\r
147         switch( eCurrentState )\r
148         {\r
149                 case eSentStart :       \r
150 \r
151                                 /* We sent a start bit, if it was successful we can\r
152                                 go on to send the slave address. */\r
153                                 if( ( I2C_I2STAT == i2cSTATUS_START_TXED ) || ( I2C_I2STAT == i2cSTATUS_REP_START_TXED ) )\r
154                                 {\r
155                                         /* Send the slave address. */\r
156                                         I2C_I2DAT = pxCurrentMessage->ucSlaveAddress;\r
157 \r
158                                         if( pxCurrentMessage->ucSlaveAddress & i2cREAD )\r
159                                         {\r
160                                                 /* We are then going to read bytes back from the \r
161                                                 slave. */\r
162                                                 eCurrentState = eSentAddressForRead;\r
163                                                 \r
164                                                 /* Initialise the buffer index so the first byte goes\r
165                                                 into the first buffer position. */\r
166                                                 lMessageIndex = 0;\r
167                                         }\r
168                                         else\r
169                                         {\r
170                                                 /* We are then going to write some data to the slave. */\r
171                                                 eCurrentState = eSentAddressForWrite;\r
172 \r
173                                                 /* When writing bytes we first have to send the two\r
174                                                 byte buffer address so lMessageIndex is set negative,\r
175                                                 when it reaches 0 it is time to send the actual data. */\r
176                                                 lMessageIndex = -i2cBUFFER_ADDRESS_BYTES;\r
177                                         }\r
178                                 }\r
179                                 else\r
180                                 {\r
181                                         /* Could not send the start bit so give up. */\r
182                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
183                                 }\r
184 \r
185                                 I2C_I2CONCLR = i2cSTA_BIT;                              \r
186 \r
187                                 break;\r
188 \r
189                 case eSentAddressForWrite :\r
190 \r
191                                 /* We sent the address of the slave we are going to write to.\r
192                                 If this was acknowledged we     can go on to send the data. */\r
193                                 if( I2C_I2STAT == i2cSTATUS_TX_ADDR_ACKED )\r
194                                 {\r
195                                         /* Start the first byte transmitting which is the \r
196                                         first byte of the buffer address to which the data will \r
197                                         be sent. */\r
198                                         I2C_I2DAT = pxCurrentMessage->ucBufferAddressHighByte;\r
199                                         eCurrentState = eSentData;\r
200                                 }\r
201                                 else\r
202                                 {\r
203                                         /* Address was not acknowledged so give up. */\r
204                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
205                                 }                                       \r
206                                 break;\r
207 \r
208                 case eSentAddressForRead :\r
209 \r
210                                 /* We sent the address of the slave we are going to read from.\r
211                                 If this was acknowledged we can go on to read the data. */\r
212                                 if( I2C_I2STAT == i2cSTATUS_RX_ADDR_ACKED )\r
213                                 {\r
214                                         eCurrentState = eReceiveData;\r
215                                         if( pxCurrentMessage->lMessageLength > i2cJUST_ONE_BYTE_TO_RX )\r
216                                         {\r
217                                                 /* Don't ack the last byte of the message. */\r
218                                                 I2C_I2CONSET = i2cAA_BIT;\r
219                                         }                                       \r
220                                 }\r
221                                 else\r
222                                 {\r
223                                         /* Something unexpected happened - give up. */\r
224                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
225                                 }\r
226                                 break;\r
227 \r
228                 case eReceiveData :\r
229                                 \r
230                                 /* We have just received a byte from the slave. */\r
231                                 if( ( I2C_I2STAT == i2cSTATUS_DATA_RXED ) || ( I2C_I2STAT == i2cSTATUS_LAST_BYTE_RXED ) )\r
232                                 {\r
233                                         /* Buffer the byte just received then increment the index \r
234                                         so it points to the next free space. */\r
235                                         pxCurrentMessage->pucBuffer[ lMessageIndex ] = I2C_I2DAT;\r
236                                         lMessageIndex++;\r
237 \r
238                                         /* How many more bytes are we expecting to receive? */\r
239                                         lBytesLeft = pxCurrentMessage->lMessageLength - lMessageIndex;\r
240                                         if( lBytesLeft == ( unsigned long ) 0 )\r
241                                         {\r
242                                                 /* This was the last byte in the message. */\r
243                                                 i2cEND_TRANSMISSION( pdPASS );\r
244 \r
245                                                 /* If xMessageCompleteSemaphore is not null then there\r
246                                                 is a task waiting for this message to complete and we\r
247                                                 must 'give' the semaphore so the task is woken.*/\r
248                                                 if( pxCurrentMessage->xMessageCompleteSemaphore )\r
249                                                 {\r
250                                                         xSemaphoreGiveFromISR( pxCurrentMessage->xMessageCompleteSemaphore, &xHigherPriorityTaskWoken );\r
251                                                 }\r
252 \r
253                                                 /* Are there any other messages to transact? */\r
254                                                 if( xQueueReceiveFromISR( xMessagesForTx, &pxCurrentMessage, &xHigherPriorityTaskWoken ) == pdTRUE )\r
255                                                 {\r
256                                                         /* Start the next message - which was\r
257                                                         retrieved from the queue. */\r
258                                                         I2C_I2CONSET = i2cSTA_BIT;\r
259                                                 }\r
260                                                 else\r
261                                                 {\r
262                                                         /* No more messages were found to be waiting for\r
263                                                         transaction so the bus is free. */\r
264                                                         ulBusFree = ( unsigned long ) pdTRUE;                   \r
265                                                 }                                               \r
266                                         }\r
267                                         else\r
268                                         {\r
269                                                 /* There are more bytes to receive but don't ack the \r
270                                                 last byte. */\r
271                                                 if( lBytesLeft <= i2cJUST_ONE_BYTE_TO_RX )\r
272                                                 {\r
273                                                         I2C_I2CONCLR = i2cAA_BIT;\r
274                                                 }                                                        \r
275                                         }\r
276                                 }\r
277                                 else\r
278                                 {\r
279                                         /* Something unexpected happened - give up. */\r
280                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
281                                 }\r
282 \r
283                                 break;\r
284                                 \r
285                 case eSentData  :       \r
286 \r
287                                 /* We sent a data byte, if successful send the  next byte in \r
288                                 the message. */\r
289                                 if( I2C_I2STAT == i2cSTATUS_DATA_TXED )\r
290                                 {\r
291                                         /* Index to the next byte to send. */\r
292                                         lMessageIndex++;\r
293                                         if( lMessageIndex < 0 )\r
294                                         {\r
295                                                 /* lMessage index is still negative so we have so far \r
296                                                 only sent the first byte of the buffer address.  Send \r
297                                                 the second byte now, then initialise the buffer index\r
298                                                 to zero so the next byte sent comes from the actual \r
299                                                 data buffer. */\r
300                                                 I2C_I2DAT = pxCurrentMessage->ucBufferAddressLowByte;\r
301                                         }\r
302                                         else if( lMessageIndex < pxCurrentMessage->lMessageLength )\r
303                                         {\r
304                                                 /* Simply send the next byte in the tx buffer. */\r
305                                                 I2C_I2DAT = pxCurrentMessage->pucBuffer[ lMessageIndex ];                                                                               \r
306                                         }\r
307                                         else\r
308                                         {\r
309                                                 /* No more bytes in this message to be send.  Finished \r
310                                                 sending message - send a stop bit. */\r
311                                                 i2cEND_TRANSMISSION( pdPASS );\r
312 \r
313                                                 /* If xMessageCompleteSemaphore is not null then there\r
314                                                 is a task waiting for this message to be sent and the\r
315                                                 semaphore must be 'given' to wake the task. */\r
316                                                 if( pxCurrentMessage->xMessageCompleteSemaphore )\r
317                                                 {\r
318                                                         xSemaphoreGiveFromISR( pxCurrentMessage->xMessageCompleteSemaphore, &xHigherPriorityTaskWoken );\r
319                                                 }\r
320 \r
321                                                 /* Are there any other messages to transact? */\r
322                                                 if( xQueueReceiveFromISR( xMessagesForTx, &pxCurrentMessage, &xHigherPriorityTaskWoken ) == pdTRUE )\r
323                                                 {\r
324                                                         /* Start the next message from the Tx queue. */\r
325                                                         I2C_I2CONSET = i2cSTA_BIT;\r
326                                                 }\r
327                                                 else\r
328                                                 {\r
329                                                         /* No more message were queues for transaction so \r
330                                                         the bus is free. */\r
331                                                         ulBusFree = ( unsigned long ) pdTRUE;                   \r
332                                                 }\r
333                                         }\r
334                                 }\r
335                                 else\r
336                                 {\r
337                                         /* Something unexpected happened, give up. */\r
338                                         i2cEND_TRANSMISSION( pdFAIL );                                  \r
339                                 }\r
340                                 break;\r
341 \r
342                 default :       \r
343                 \r
344                                 /* Should never get here. */\r
345                                 eCurrentState = eSentStart;\r
346                                 break;\r
347         }       \r
348 \r
349         /* Clear the interrupt. */\r
350         I2C_I2CONCLR = i2cSI_BIT;\r
351         VICVectAddr = i2cCLEAR_VIC_INTERRUPT;\r
352 \r
353         if( xHigherPriorityTaskWoken )\r
354         {\r
355                 portYIELD_FROM_ISR();\r
356         }\r
357 }\r
358 /*-----------------------------------------------------------*/\r
359 \r