2 * FreeRTOS Kernel V10.1.1
\r
3 * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\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
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\r
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
22 * http://www.FreeRTOS.org
\r
23 * http://aws.amazon.com/freertos
\r
25 * 1 tab == 4 spaces!
\r
29 /* Standard includes. */
\r
32 /* Scheduler include files. */
\r
33 #include "FreeRTOS.h"
\r
38 /* Application includes. */
\r
41 /*-----------------------------------------------------------*/
\r
43 /* Bit definitions within the I2CONCLR register. */
\r
44 #define i2cSTA_BIT ( ( unsigned char ) 0x20 )
\r
45 #define i2cSI_BIT ( ( unsigned char ) 0x08 )
\r
46 #define i2cSTO_BIT ( ( unsigned char ) 0x10 )
\r
47 #define i2cAA_BIT ( ( unsigned char ) 0x04 )
\r
49 /* Status codes for the I2STAT register. */
\r
50 #define i2cSTATUS_START_TXED ( 0x08 )
\r
51 #define i2cSTATUS_REP_START_TXED ( 0x10 )
\r
52 #define i2cSTATUS_TX_ADDR_ACKED ( 0x18 )
\r
53 #define i2cSTATUS_DATA_TXED ( 0x28 )
\r
54 #define i2cSTATUS_RX_ADDR_ACKED ( 0x40 )
\r
55 #define i2cSTATUS_DATA_RXED ( 0x50 )
\r
56 #define i2cSTATUS_LAST_BYTE_RXED ( 0x58 )
\r
58 /* Constants for operation of the VIC. */
\r
59 #define i2cCLEAR_VIC_INTERRUPT ( 0 )
\r
61 /* Misc constants. */
\r
62 #define i2cJUST_ONE_BYTE_TO_RX ( 1 )
\r
63 #define i2cBUFFER_ADDRESS_BYTES ( 2 )
\r
65 /* End the current transmission and free the bus. */
\r
66 #define i2cEND_TRANSMISSION( lStatus ) \
\r
68 I2C_I2CONCLR = i2cAA_BIT; \
\r
69 I2C_I2CONSET = i2cSTO_BIT; \
\r
70 eCurrentState = eSentStart; \
\r
71 lTransactionCompleted = lStatus; \
\r
73 /*-----------------------------------------------------------*/
\r
75 /* Valid i2c communication states. */
\r
78 eSentStart, /*<< Last action was the transmission of a start bit. */
\r
79 eSentAddressForWrite, /*<< Last action was the transmission of the slave address we are to write to. */
\r
80 eSentAddressForRead, /*<< Last action was the transmission of the slave address we are to read from. */
\r
81 eSentData, /*<< Last action was the transmission of a data byte. */
\r
82 eReceiveData /*<< We expected data to be received. */
\r
84 /*-----------------------------------------------------------*/
\r
86 /* Points to the message currently being sent. */
\r
87 volatile xI2CMessage *pxCurrentMessage = NULL;
\r
89 /* The queue of messages waiting to be transmitted. */
\r
90 static QueueHandle_t xMessagesForTx;
\r
92 /* Flag used to indicate whether or not the ISR is amid sending a message. */
\r
93 unsigned long ulBusFree = ( unsigned long ) pdTRUE;
\r
95 /* Setting this to true will cause the TCP task to think a message is
\r
96 complete and thus restart. It can therefore be used under error states
\r
97 to force a restart. */
\r
98 volatile long lTransactionCompleted = pdTRUE;
\r
100 /*-----------------------------------------------------------*/
\r
102 void vI2CISRCreateQueues( unsigned portBASE_TYPE uxQueueLength, QueueHandle_t *pxTxMessages, unsigned long **ppulBusFree )
\r
104 /* Create the queues used to hold Rx and Tx characters. */
\r
105 xMessagesForTx = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( xI2CMessage * ) );
\r
107 /* Pass back a reference to the queue and bus free flag so the I2C API file
\r
108 can post messages. */
\r
109 *pxTxMessages = xMessagesForTx;
\r
110 *ppulBusFree = &ulBusFree;
\r
112 /*-----------------------------------------------------------*/
\r
114 /* The ISR entry point. */
\r
115 void vI2C_ISR_Wrapper( void ) __attribute__ (( naked ));
\r
117 /* The ISR function to perform the actual work. This must be a separate
\r
118 function from the wrapper to ensure the correct stack frame is set up. */
\r
119 void vI2C_ISR_Handler( void );
\r
121 /*-----------------------------------------------------------*/
\r
123 void vI2C_ISR_Wrapper( void )
\r
125 /* Save the context of the interrupted task. */
\r
126 portSAVE_CONTEXT();
\r
128 /* Call the handler to perform the actual work. This must be a
\r
129 separate function to ensure the correct stack frame is set up. */
\r
130 vI2C_ISR_Handler();
\r
132 /* Restore the context of whichever task is going to run next. */
\r
133 portRESTORE_CONTEXT();
\r
135 /*-----------------------------------------------------------*/
\r
137 void vI2C_ISR_Handler( void )
\r
139 /* Holds the current transmission state. */
\r
140 static I2C_STATE eCurrentState = eSentStart;
\r
141 static long lMessageIndex = -i2cBUFFER_ADDRESS_BYTES; /* There are two address bytes to send prior to the data. */
\r
142 portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
\r
145 /* The action taken for this interrupt depends on our current state. */
\r
146 switch( eCurrentState )
\r
150 /* We sent a start bit, if it was successful we can
\r
151 go on to send the slave address. */
\r
152 if( ( I2C_I2STAT == i2cSTATUS_START_TXED ) || ( I2C_I2STAT == i2cSTATUS_REP_START_TXED ) )
\r
154 /* Send the slave address. */
\r
155 I2C_I2DAT = pxCurrentMessage->ucSlaveAddress;
\r
157 if( pxCurrentMessage->ucSlaveAddress & i2cREAD )
\r
159 /* We are then going to read bytes back from the
\r
161 eCurrentState = eSentAddressForRead;
\r
163 /* Initialise the buffer index so the first byte goes
\r
164 into the first buffer position. */
\r
169 /* We are then going to write some data to the slave. */
\r
170 eCurrentState = eSentAddressForWrite;
\r
172 /* When writing bytes we first have to send the two
\r
173 byte buffer address so lMessageIndex is set negative,
\r
174 when it reaches 0 it is time to send the actual data. */
\r
175 lMessageIndex = -i2cBUFFER_ADDRESS_BYTES;
\r
180 /* Could not send the start bit so give up. */
\r
181 i2cEND_TRANSMISSION( pdFAIL );
\r
184 I2C_I2CONCLR = i2cSTA_BIT;
\r
188 case eSentAddressForWrite :
\r
190 /* We sent the address of the slave we are going to write to.
\r
191 If this was acknowledged we can go on to send the data. */
\r
192 if( I2C_I2STAT == i2cSTATUS_TX_ADDR_ACKED )
\r
194 /* Start the first byte transmitting which is the
\r
195 first byte of the buffer address to which the data will
\r
197 I2C_I2DAT = pxCurrentMessage->ucBufferAddressHighByte;
\r
198 eCurrentState = eSentData;
\r
202 /* Address was not acknowledged so give up. */
\r
203 i2cEND_TRANSMISSION( pdFAIL );
\r
207 case eSentAddressForRead :
\r
209 /* We sent the address of the slave we are going to read from.
\r
210 If this was acknowledged we can go on to read the data. */
\r
211 if( I2C_I2STAT == i2cSTATUS_RX_ADDR_ACKED )
\r
213 eCurrentState = eReceiveData;
\r
214 if( pxCurrentMessage->lMessageLength > i2cJUST_ONE_BYTE_TO_RX )
\r
216 /* Don't ack the last byte of the message. */
\r
217 I2C_I2CONSET = i2cAA_BIT;
\r
222 /* Something unexpected happened - give up. */
\r
223 i2cEND_TRANSMISSION( pdFAIL );
\r
227 case eReceiveData :
\r
229 /* We have just received a byte from the slave. */
\r
230 if( ( I2C_I2STAT == i2cSTATUS_DATA_RXED ) || ( I2C_I2STAT == i2cSTATUS_LAST_BYTE_RXED ) )
\r
232 /* Buffer the byte just received then increment the index
\r
233 so it points to the next free space. */
\r
234 pxCurrentMessage->pucBuffer[ lMessageIndex ] = I2C_I2DAT;
\r
237 /* How many more bytes are we expecting to receive? */
\r
238 lBytesLeft = pxCurrentMessage->lMessageLength - lMessageIndex;
\r
239 if( lBytesLeft == ( unsigned long ) 0 )
\r
241 /* This was the last byte in the message. */
\r
242 i2cEND_TRANSMISSION( pdPASS );
\r
244 /* If xMessageCompleteSemaphore is not null then there
\r
245 is a task waiting for this message to complete and we
\r
246 must 'give' the semaphore so the task is woken.*/
\r
247 if( pxCurrentMessage->xMessageCompleteSemaphore )
\r
249 xSemaphoreGiveFromISR( pxCurrentMessage->xMessageCompleteSemaphore, &xHigherPriorityTaskWoken );
\r
252 /* Are there any other messages to transact? */
\r
253 if( xQueueReceiveFromISR( xMessagesForTx, &pxCurrentMessage, &xHigherPriorityTaskWoken ) == pdTRUE )
\r
255 /* Start the next message - which was
\r
256 retrieved from the queue. */
\r
257 I2C_I2CONSET = i2cSTA_BIT;
\r
261 /* No more messages were found to be waiting for
\r
262 transaction so the bus is free. */
\r
263 ulBusFree = ( unsigned long ) pdTRUE;
\r
268 /* There are more bytes to receive but don't ack the
\r
270 if( lBytesLeft <= i2cJUST_ONE_BYTE_TO_RX )
\r
272 I2C_I2CONCLR = i2cAA_BIT;
\r
278 /* Something unexpected happened - give up. */
\r
279 i2cEND_TRANSMISSION( pdFAIL );
\r
286 /* We sent a data byte, if successful send the next byte in
\r
288 if( I2C_I2STAT == i2cSTATUS_DATA_TXED )
\r
290 /* Index to the next byte to send. */
\r
292 if( lMessageIndex < 0 )
\r
294 /* lMessage index is still negative so we have so far
\r
295 only sent the first byte of the buffer address. Send
\r
296 the second byte now, then initialise the buffer index
\r
297 to zero so the next byte sent comes from the actual
\r
299 I2C_I2DAT = pxCurrentMessage->ucBufferAddressLowByte;
\r
301 else if( lMessageIndex < pxCurrentMessage->lMessageLength )
\r
303 /* Simply send the next byte in the tx buffer. */
\r
304 I2C_I2DAT = pxCurrentMessage->pucBuffer[ lMessageIndex ];
\r
308 /* No more bytes in this message to be send. Finished
\r
309 sending message - send a stop bit. */
\r
310 i2cEND_TRANSMISSION( pdPASS );
\r
312 /* If xMessageCompleteSemaphore is not null then there
\r
313 is a task waiting for this message to be sent and the
\r
314 semaphore must be 'given' to wake the task. */
\r
315 if( pxCurrentMessage->xMessageCompleteSemaphore )
\r
317 xSemaphoreGiveFromISR( pxCurrentMessage->xMessageCompleteSemaphore, &xHigherPriorityTaskWoken );
\r
320 /* Are there any other messages to transact? */
\r
321 if( xQueueReceiveFromISR( xMessagesForTx, &pxCurrentMessage, &xHigherPriorityTaskWoken ) == pdTRUE )
\r
323 /* Start the next message from the Tx queue. */
\r
324 I2C_I2CONSET = i2cSTA_BIT;
\r
328 /* No more message were queues for transaction so
\r
329 the bus is free. */
\r
330 ulBusFree = ( unsigned long ) pdTRUE;
\r
336 /* Something unexpected happened, give up. */
\r
337 i2cEND_TRANSMISSION( pdFAIL );
\r
343 /* Should never get here. */
\r
344 eCurrentState = eSentStart;
\r
348 /* Clear the interrupt. */
\r
349 I2C_I2CONCLR = i2cSI_BIT;
\r
350 VICVectAddr = i2cCLEAR_VIC_INTERRUPT;
\r
352 if( xHigherPriorityTaskWoken )
\r
354 portYIELD_FROM_ISR();
\r
357 /*-----------------------------------------------------------*/
\r