2 FreeRTOS V5.4.2 - Copyright (C) 2009 Real Time Engineers Ltd.
\r
4 This file is part of the FreeRTOS distribution.
\r
6 FreeRTOS is free software; you can redistribute it and/or modify it under
\r
7 the terms of the GNU General Public License (version 2) as published by the
\r
8 Free Software Foundation and modified by the FreeRTOS exception.
\r
9 **NOTE** The exception to the GPL is included to allow you to distribute a
\r
10 combined work that includes FreeRTOS without being obliged to provide the
\r
11 source code for proprietary components outside of the FreeRTOS kernel.
\r
12 Alternative commercial license and support terms are also available upon
\r
13 request. See the licensing section of http://www.FreeRTOS.org for full
\r
16 FreeRTOS is distributed in the hope that it will be useful, but WITHOUT
\r
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
\r
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
\r
21 You should have received a copy of the GNU General Public License along
\r
22 with FreeRTOS; if not, write to the Free Software Foundation, Inc., 59
\r
23 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
\r
26 ***************************************************************************
\r
28 * Looking for a quick start? Then check out the FreeRTOS eBook! *
\r
29 * See http://www.FreeRTOS.org/Documentation for details *
\r
31 ***************************************************************************
\r
35 Please ensure to read the configuration and relevant port sections of the
\r
36 online documentation.
\r
38 http://www.FreeRTOS.org - Documentation, latest information, license and
\r
41 http://www.SafeRTOS.com - A version that is certified for use in safety
\r
44 http://www.OpenRTOS.com - Commercial support, development, porting,
\r
45 licensing and training services.
\r
49 /* Standard includes. */
\r
52 /* Scheduler include files. */
\r
53 #include "FreeRTOS.h"
\r
58 /* Application includes. */
\r
61 /*-----------------------------------------------------------*/
\r
63 /* Bit definitions within the I2CONCLR register. */
\r
64 #define i2cSTA_BIT ( ( unsigned portCHAR ) 0x20 )
\r
65 #define i2cSI_BIT ( ( unsigned portCHAR ) 0x08 )
\r
66 #define i2cSTO_BIT ( ( unsigned portCHAR ) 0x10 )
\r
67 #define i2cAA_BIT ( ( unsigned portCHAR ) 0x04 )
\r
69 /* Status codes for the I2STAT register. */
\r
70 #define i2cSTATUS_START_TXED ( 0x08 )
\r
71 #define i2cSTATUS_REP_START_TXED ( 0x10 )
\r
72 #define i2cSTATUS_TX_ADDR_ACKED ( 0x18 )
\r
73 #define i2cSTATUS_DATA_TXED ( 0x28 )
\r
74 #define i2cSTATUS_RX_ADDR_ACKED ( 0x40 )
\r
75 #define i2cSTATUS_DATA_RXED ( 0x50 )
\r
76 #define i2cSTATUS_LAST_BYTE_RXED ( 0x58 )
\r
78 /* Constants for operation of the VIC. */
\r
79 #define i2cCLEAR_VIC_INTERRUPT ( 0 )
\r
81 /* Misc constants. */
\r
82 #define i2cJUST_ONE_BYTE_TO_RX ( 1 )
\r
83 #define i2cBUFFER_ADDRESS_BYTES ( 2 )
\r
85 /* End the current transmission and free the bus. */
\r
86 #define i2cEND_TRANSMISSION( lStatus ) \
\r
88 I2C_I2CONCLR = i2cAA_BIT; \
\r
89 I2C_I2CONSET = i2cSTO_BIT; \
\r
90 eCurrentState = eSentStart; \
\r
91 lTransactionCompleted = lStatus; \
\r
93 /*-----------------------------------------------------------*/
\r
95 /* Valid i2c communication states. */
\r
98 eSentStart, /*<< Last action was the transmission of a start bit. */
\r
99 eSentAddressForWrite, /*<< Last action was the transmission of the slave address we are to write to. */
\r
100 eSentAddressForRead, /*<< Last action was the transmission of the slave address we are to read from. */
\r
101 eSentData, /*<< Last action was the transmission of a data byte. */
\r
102 eReceiveData /*<< We expected data to be received. */
\r
104 /*-----------------------------------------------------------*/
\r
106 /* Points to the message currently being sent. */
\r
107 volatile xI2CMessage *pxCurrentMessage = NULL;
\r
109 /* The queue of messages waiting to be transmitted. */
\r
110 static xQueueHandle xMessagesForTx;
\r
112 /* Flag used to indicate whether or not the ISR is amid sending a message. */
\r
113 unsigned portLONG ulBusFree = ( unsigned portLONG ) pdTRUE;
\r
115 /* Setting this to true will cause the TCP task to think a message is
\r
116 complete and thus restart. It can therefore be used under error states
\r
117 to force a restart. */
\r
118 volatile portLONG lTransactionCompleted = pdTRUE;
\r
120 /*-----------------------------------------------------------*/
\r
122 void vI2CISRCreateQueues( unsigned portBASE_TYPE uxQueueLength, xQueueHandle *pxTxMessages, unsigned portLONG **ppulBusFree )
\r
124 /* Create the queues used to hold Rx and Tx characters. */
\r
125 xMessagesForTx = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( xI2CMessage * ) );
\r
127 /* Pass back a reference to the queue and bus free flag so the I2C API file
\r
128 can post messages. */
\r
129 *pxTxMessages = xMessagesForTx;
\r
130 *ppulBusFree = &ulBusFree;
\r
132 /*-----------------------------------------------------------*/
\r
134 /* The ISR entry point. */
\r
135 void vI2C_ISR_Wrapper( void ) __attribute__ (( naked ));
\r
137 /* The ISR function to perform the actual work. This must be a separate
\r
138 function from the wrapper to ensure the correct stack frame is set up. */
\r
139 void vI2C_ISR_Handler( void );
\r
141 /*-----------------------------------------------------------*/
\r
143 void vI2C_ISR_Wrapper( void )
\r
145 /* Save the context of the interrupted task. */
\r
146 portSAVE_CONTEXT();
\r
148 /* Call the handler to perform the actual work. This must be a
\r
149 separate function to ensure the correct stack frame is set up. */
\r
150 vI2C_ISR_Handler();
\r
152 /* Restore the context of whichever task is going to run next. */
\r
153 portRESTORE_CONTEXT();
\r
155 /*-----------------------------------------------------------*/
\r
157 void vI2C_ISR_Handler( void )
\r
159 /* Holds the current transmission state. */
\r
160 static I2C_STATE eCurrentState = eSentStart;
\r
161 static portLONG lMessageIndex = -i2cBUFFER_ADDRESS_BYTES; /* There are two address bytes to send prior to the data. */
\r
162 portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
\r
163 portLONG lBytesLeft;
\r
165 /* The action taken for this interrupt depends on our current state. */
\r
166 switch( eCurrentState )
\r
170 /* We sent a start bit, if it was successful we can
\r
171 go on to send the slave address. */
\r
172 if( ( I2C_I2STAT == i2cSTATUS_START_TXED ) || ( I2C_I2STAT == i2cSTATUS_REP_START_TXED ) )
\r
174 /* Send the slave address. */
\r
175 I2C_I2DAT = pxCurrentMessage->ucSlaveAddress;
\r
177 if( pxCurrentMessage->ucSlaveAddress & i2cREAD )
\r
179 /* We are then going to read bytes back from the
\r
181 eCurrentState = eSentAddressForRead;
\r
183 /* Initialise the buffer index so the first byte goes
\r
184 into the first buffer position. */
\r
189 /* We are then going to write some data to the slave. */
\r
190 eCurrentState = eSentAddressForWrite;
\r
192 /* When writing bytes we first have to send the two
\r
193 byte buffer address so lMessageIndex is set negative,
\r
194 when it reaches 0 it is time to send the actual data. */
\r
195 lMessageIndex = -i2cBUFFER_ADDRESS_BYTES;
\r
200 /* Could not send the start bit so give up. */
\r
201 i2cEND_TRANSMISSION( pdFAIL );
\r
204 I2C_I2CONCLR = i2cSTA_BIT;
\r
208 case eSentAddressForWrite :
\r
210 /* We sent the address of the slave we are going to write to.
\r
211 If this was acknowledged we can go on to send the data. */
\r
212 if( I2C_I2STAT == i2cSTATUS_TX_ADDR_ACKED )
\r
214 /* Start the first byte transmitting which is the
\r
215 first byte of the buffer address to which the data will
\r
217 I2C_I2DAT = pxCurrentMessage->ucBufferAddressHighByte;
\r
218 eCurrentState = eSentData;
\r
222 /* Address was not acknowledged so give up. */
\r
223 i2cEND_TRANSMISSION( pdFAIL );
\r
227 case eSentAddressForRead :
\r
229 /* We sent the address of the slave we are going to read from.
\r
230 If this was acknowledged we can go on to read the data. */
\r
231 if( I2C_I2STAT == i2cSTATUS_RX_ADDR_ACKED )
\r
233 eCurrentState = eReceiveData;
\r
234 if( pxCurrentMessage->lMessageLength > i2cJUST_ONE_BYTE_TO_RX )
\r
236 /* Don't ack the last byte of the message. */
\r
237 I2C_I2CONSET = i2cAA_BIT;
\r
242 /* Something unexpected happened - give up. */
\r
243 i2cEND_TRANSMISSION( pdFAIL );
\r
247 case eReceiveData :
\r
249 /* We have just received a byte from the slave. */
\r
250 if( ( I2C_I2STAT == i2cSTATUS_DATA_RXED ) || ( I2C_I2STAT == i2cSTATUS_LAST_BYTE_RXED ) )
\r
252 /* Buffer the byte just received then increment the index
\r
253 so it points to the next free space. */
\r
254 pxCurrentMessage->pucBuffer[ lMessageIndex ] = I2C_I2DAT;
\r
257 /* How many more bytes are we expecting to receive? */
\r
258 lBytesLeft = pxCurrentMessage->lMessageLength - lMessageIndex;
\r
259 if( lBytesLeft == ( unsigned portLONG ) 0 )
\r
261 /* This was the last byte in the message. */
\r
262 i2cEND_TRANSMISSION( pdPASS );
\r
264 /* If xMessageCompleteSemaphore is not null then there
\r
265 is a task waiting for this message to complete and we
\r
266 must 'give' the semaphore so the task is woken.*/
\r
267 if( pxCurrentMessage->xMessageCompleteSemaphore )
\r
269 xSemaphoreGiveFromISR( pxCurrentMessage->xMessageCompleteSemaphore, &xHigherPriorityTaskWoken );
\r
272 /* Are there any other messages to transact? */
\r
273 if( xQueueReceiveFromISR( xMessagesForTx, &pxCurrentMessage, &xHigherPriorityTaskWoken ) == pdTRUE )
\r
275 /* Start the next message - which was
\r
276 retrieved from the queue. */
\r
277 I2C_I2CONSET = i2cSTA_BIT;
\r
281 /* No more messages were found to be waiting for
\r
282 transaction so the bus is free. */
\r
283 ulBusFree = ( unsigned portLONG ) pdTRUE;
\r
288 /* There are more bytes to receive but don't ack the
\r
290 if( lBytesLeft <= i2cJUST_ONE_BYTE_TO_RX )
\r
292 I2C_I2CONCLR = i2cAA_BIT;
\r
298 /* Something unexpected happened - give up. */
\r
299 i2cEND_TRANSMISSION( pdFAIL );
\r
306 /* We sent a data byte, if successful send the next byte in
\r
308 if( I2C_I2STAT == i2cSTATUS_DATA_TXED )
\r
310 /* Index to the next byte to send. */
\r
312 if( lMessageIndex < 0 )
\r
314 /* lMessage index is still negative so we have so far
\r
315 only sent the first byte of the buffer address. Send
\r
316 the second byte now, then initialise the buffer index
\r
317 to zero so the next byte sent comes from the actual
\r
319 I2C_I2DAT = pxCurrentMessage->ucBufferAddressLowByte;
\r
321 else if( lMessageIndex < pxCurrentMessage->lMessageLength )
\r
323 /* Simply send the next byte in the tx buffer. */
\r
324 I2C_I2DAT = pxCurrentMessage->pucBuffer[ lMessageIndex ];
\r
328 /* No more bytes in this message to be send. Finished
\r
329 sending message - send a stop bit. */
\r
330 i2cEND_TRANSMISSION( pdPASS );
\r
332 /* If xMessageCompleteSemaphore is not null then there
\r
333 is a task waiting for this message to be sent and the
\r
334 semaphore must be 'given' to wake the task. */
\r
335 if( pxCurrentMessage->xMessageCompleteSemaphore )
\r
337 xSemaphoreGiveFromISR( pxCurrentMessage->xMessageCompleteSemaphore, &xHigherPriorityTaskWoken );
\r
340 /* Are there any other messages to transact? */
\r
341 if( xQueueReceiveFromISR( xMessagesForTx, &pxCurrentMessage, &xHigherPriorityTaskWoken ) == pdTRUE )
\r
343 /* Start the next message from the Tx queue. */
\r
344 I2C_I2CONSET = i2cSTA_BIT;
\r
348 /* No more message were queues for transaction so
\r
349 the bus is free. */
\r
350 ulBusFree = ( unsigned portLONG ) pdTRUE;
\r
356 /* Something unexpected happened, give up. */
\r
357 i2cEND_TRANSMISSION( pdFAIL );
\r
363 /* Should never get here. */
\r
364 eCurrentState = eSentStart;
\r
368 /* Clear the interrupt. */
\r
369 I2C_I2CONCLR = i2cSI_BIT;
\r
370 VICVectAddr = i2cCLEAR_VIC_INTERRUPT;
\r
372 if( xHigherPriorityTaskWoken )
\r
374 portYIELD_FROM_ISR();
\r
377 /*-----------------------------------------------------------*/
\r