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