/ libraries / Wire / USI_TWI_Master.cpp
USI_TWI_Master.cpp
  1  /*****************************************************************************
  2  *
  3  *
  4  * File              USI_TWI_Master.c compiled with gcc
  5  * Date              Friday, 10/31/08		Boo!
  6  * Updated by        jkl
  7  *
  8  
  9  * AppNote           : AVR310 - Using the USI module as a TWI Master
 10  *
 11  *		Extensively modified to provide complete I2C driver.
 12  *	
 13  *Notes: 
 14  *		- T4_TWI and T2_TWI delays are modified to work with 1MHz default clock
 15  *			and now use hard code values. They would need to change
 16  *			for other clock rates. Refer to the Apps Note.
 17  *
 18  *	12/17/08	Added USI_TWI_Start_Memory_Read Routine		-jkl
 19  *		Note msg buffer will have slave adrs ( with write bit set) and memory adrs;
 20  *			length should be these two bytes plus the number of bytes to read.
 21  ****************************************************************************/
 22  
 23  #ifdef __AVR_ATtiny85__
 24  
 25  #include <avr/interrupt.h>
 26  #include <util/delay.h>
 27  #include <avr/io.h>
 28  #include "USI_TWI_Master.h"
 29  
 30  unsigned char TinyM_USI_TWI_Start_Transceiver_With_Data( unsigned char * , unsigned char );
 31  unsigned char TinyM_USI_TWI_Master_Transfer( unsigned char );
 32  unsigned char TinyM_USI_TWI_Master_Start( void );
 33  
 34  union  TinyM_USI_TWI_state
 35  {
 36    unsigned char errorState;         // Can reuse the TWI_state for error states since it will not be needed if there is an error.
 37    struct
 38    {
 39      unsigned char addressMode         : 1;
 40      unsigned char masterWriteDataMode : 1;
 41  	unsigned char memReadMode		  : 1;
 42      unsigned char unused              : 5;
 43    }; 
 44  }   TinyM_USI_TWI_state;
 45  
 46  /*---------------------------------------------------------------
 47   USI TWI single master initialization function
 48  ---------------------------------------------------------------*/
 49  void TinyM_USI_TWI_Master_Initialise( void )
 50  {
 51    PORT_USI |= (1<<PIN_USI_SDA);           // Enable pullup on SDA, to set high as released state.
 52    PORT_USI |= (1<<PIN_USI_SCL);           // Enable pullup on SCL, to set high as released state.
 53    
 54    DDR_USI  |= (1<<PIN_USI_SCL);           // Enable SCL as output.
 55    DDR_USI  |= (1<<PIN_USI_SDA);           // Enable SDA as output.
 56    
 57    USIDR    =  0xFF;                       // Preload dataregister with "released level" data.
 58    USICR    =  (0<<USISIE)|(0<<USIOIE)|                            // Disable Interrupts.
 59                (1<<USIWM1)|(0<<USIWM0)|                            // Set USI in Two-wire mode.
 60                (1<<USICS1)|(0<<USICS0)|(1<<USICLK)|                // Software stobe as counter clock source
 61                (0<<USITC);
 62    USISR   =   (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|      // Clear flags,
 63                (0x0<<USICNT0);                                     // and reset counter.
 64  }
 65  
 66  /*---------------------------------------------------------------
 67  Use this function to get hold of the error message from the last transmission
 68  ---------------------------------------------------------------*/
 69  unsigned char TinyM_USI_TWI_Get_State_Info( void )
 70  {
 71    return ( TinyM_USI_TWI_state.errorState );                            // Return error state.
 72  }
 73  /*---------------------------------------------------------------
 74   USI Random (memory) Read function. This function sets up for call
 75   to USI_TWI_Start_Transceiver_With_Data which does the work.
 76   Doesn't matter if read/write bit is set or cleared, it'll be set
 77   correctly in this function.
 78   
 79   The msgSize is passed to USI_TWI_Start_Transceiver_With_Data.
 80   
 81   Success or error code is returned. Error codes are defined in 
 82   USI_TWI_Master.h
 83  ---------------------------------------------------------------*/
 84  unsigned char TinyM_USI_TWI_Start_Random_Read( unsigned char *msg, unsigned char msgSize)
 85  {
 86    *(msg) &= ~(TRUE<<TWI_READ_BIT);		// clear the read bit if it's set
 87    TinyM_USI_TWI_state.errorState = 0;
 88    TinyM_USI_TWI_state.memReadMode = TRUE;
 89    
 90    return (TinyM_USI_TWI_Start_Transceiver_With_Data( msg, msgSize));
 91  }
 92  /*---------------------------------------------------------------
 93   USI Normal Read / Write Function
 94   Transmit and receive function. LSB of first byte in buffer 
 95   indicates if a read or write cycles is performed. If set a read
 96   operation is performed.
 97  
 98   Function generates (Repeated) Start Condition, sends address and
 99   R/W, Reads/Writes Data, and verifies/sends ACK.
100   
101   Success or error code is returned. Error codes are defined in 
102   USI_TWI_Master.h
103  ---------------------------------------------------------------*/
104  unsigned char TinyM_USI_TWI_Start_Read_Write( unsigned char *msg, unsigned char msgSize)
105  {
106      
107  	TinyM_USI_TWI_state.errorState = 0;				// Clears all mode bits also
108    
109  	return (TinyM_USI_TWI_Start_Transceiver_With_Data( msg, msgSize));
110  	
111  }
112  /*---------------------------------------------------------------
113   USI Transmit and receive function. LSB of first byte in buffer 
114   indicates if a read or write cycles is performed. If set a read
115   operation is performed.
116  
117   Function generates (Repeated) Start Condition, sends address and
118   R/W, Reads/Writes Data, and verifies/sends ACK.
119   
120   This function also handles Random Read function if the memReadMode
121   bit is set. In that case, the function will:
122   The address in memory will be the second
123   byte and is written *without* sending a STOP. 
124   Then the Read bit is set (lsb of first byte), the byte count is 
125   adjusted (if needed), and the function function starts over by sending
126   the slave address again and reading the data.
127   
128   Success or error code is returned. Error codes are defined in 
129   USI_TWI_Master.h
130  ---------------------------------------------------------------*/
131  unsigned char TinyM_USI_TWI_Start_Transceiver_With_Data( unsigned char *msg, unsigned char msgSize)
132  {
133    unsigned char const tempUSISR_8bit = (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|      // Prepare register value to: Clear flags, and
134                                   (0x0<<USICNT0);                                     // set USI to shift 8 bits i.e. count 16 clock edges.
135    unsigned char const tempUSISR_1bit = (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|      // Prepare register value to: Clear flags, and
136                                   (0xE<<USICNT0); 									// set USI to shift 1 bit i.e. count 2 clock edges.
137  	unsigned char *savedMsg;
138  	unsigned char savedMsgSize; 
139  
140  //This clear must be done before calling this function so that memReadMode can be specified.
141  //  TinyM_USI_TWI_state.errorState = 0;				// Clears all mode bits also
142  
143    TinyM_USI_TWI_state.addressMode = TRUE;			// Always true for first byte
144  
145  #ifdef PARAM_VERIFICATION
146    if(msg > (unsigned char*)RAMEND)                 // Test if address is outside SRAM space
147    {
148      TinyM_USI_TWI_state.errorState = USI_TWI_DATA_OUT_OF_BOUND;
149      return (FALSE);
150    }
151    if(msgSize <= 1)                                 // Test if the transmission buffer is empty
152    {
153      TinyM_USI_TWI_state.errorState = USI_TWI_NO_DATA;
154      return (FALSE);
155    }
156  #endif
157  
158  #ifdef NOISE_TESTING                                // Test if any unexpected conditions have arrived prior to this execution.
159    if( USISR & (1<<USISIF) )
160    {
161      TinyM_USI_TWI_state.errorState = USI_TWI_UE_START_CON;
162      return (FALSE);
163    }
164    if( USISR & (1<<USIPF) )
165    {
166      TinyM_USI_TWI_state.errorState = USI_TWI_UE_STOP_CON;
167      return (FALSE);
168    }
169    if( USISR & (1<<USIDC) )
170    {
171      TinyM_USI_TWI_state.errorState = USI_TWI_UE_DATA_COL;
172      return (FALSE);
173    }
174  #endif
175  
176    if ( !(*msg & (1<<TWI_READ_BIT)) )                // The LSB in the address byte determines if is a masterRead or masterWrite operation.
177    {
178      TinyM_USI_TWI_state.masterWriteDataMode = TRUE;
179    }
180  
181  //	if (TinyM_USI_TWI_state.memReadMode)
182  //	{
183  		savedMsg = msg;
184  		savedMsgSize = msgSize;
185  //	}
186  
187  	if ( !TinyM_USI_TWI_Master_Start( ))
188    {
189  	return (FALSE);                           // Send a START condition on the TWI bus.
190    }
191  
192  /*Write address and Read/Write data */
193    do
194    {
195      /* If masterWrite cycle (or inital address tranmission)*/
196      if (TinyM_USI_TWI_state.addressMode || TinyM_USI_TWI_state.masterWriteDataMode)
197      {
198        /* Write a byte */
199        PORT_USI &= ~(1<<PIN_USI_SCL);                // Pull SCL LOW.
200        USIDR     = *(msg++);                        // Setup data.
201        TinyM_USI_TWI_Master_Transfer( tempUSISR_8bit );    // Send 8 bits on bus.
202        
203        /* Clock and verify (N)ACK from slave */
204        DDR_USI  &= ~(1<<PIN_USI_SDA);                // Enable SDA as input.
205        if( TinyM_USI_TWI_Master_Transfer( tempUSISR_1bit ) & (1<<TWI_NACK_BIT) ) 
206        {
207          if ( TinyM_USI_TWI_state.addressMode )
208            TinyM_USI_TWI_state.errorState = USI_TWI_NO_ACK_ON_ADDRESS;
209          else
210            TinyM_USI_TWI_state.errorState = USI_TWI_NO_ACK_ON_DATA;
211          return (FALSE);
212        }
213  	  
214  	  if ((!TinyM_USI_TWI_state.addressMode) && TinyM_USI_TWI_state.memReadMode)// means memory start address has been written
215  	  {
216  		msg = savedMsg;					// start at slave address again
217  		*(msg) |= (TRUE<<TWI_READ_BIT);  // set the Read Bit on Slave address
218  		TinyM_USI_TWI_state.errorState = 0;
219  		TinyM_USI_TWI_state.addressMode = TRUE;	// Now set up for the Read cycle
220  		msgSize = savedMsgSize;				// Set byte count correctly
221  		// NOte that the length should be Slave adrs byte + # bytes to read + 1 (gets decremented below)
222  		if ( !TinyM_USI_TWI_Master_Start( ))
223  		{
224  			TinyM_USI_TWI_state.errorState = USI_TWI_BAD_MEM_READ;
225  			return (FALSE);                           // Send a START condition on the TWI bus.
226  		}
227  	  }
228  	  else
229  	  {
230  		TinyM_USI_TWI_state.addressMode = FALSE;            // Only perform address transmission once.
231  	  }
232      }
233      /* Else masterRead cycle*/
234      else
235      {
236        /* Read a data byte */
237        DDR_USI   &= ~(1<<PIN_USI_SDA);               // Enable SDA as input.
238        *(msg++)  = TinyM_USI_TWI_Master_Transfer( tempUSISR_8bit );
239  
240        /* Prepare to generate ACK (or NACK in case of End Of Transmission) */
241        if( msgSize == 1)                            // If transmission of last byte was performed.
242        {
243          USIDR = 0xFF;                              // Load NACK to confirm End Of Transmission.
244        }
245        else
246        {
247          USIDR = 0x00;                              // Load ACK. Set data register bit 7 (output for SDA) low.
248        }
249        TinyM_USI_TWI_Master_Transfer( tempUSISR_1bit );   // Generate ACK/NACK.
250      }
251    }while( --msgSize) ;                             // Until all data sent/received.
252  
253    // usually a stop condition is sent here, but TinyWireM needs to choose whether or not to send it
254  
255  /* Transmission successfully completed*/
256    return (TRUE);
257  }
258  
259  /*---------------------------------------------------------------
260   Core function for shifting data in and out from the USI.
261   Data to be sent has to be placed into the USIDR prior to calling
262   this function. Data read, will be return'ed from the function.
263  ---------------------------------------------------------------*/
264  unsigned char TinyM_USI_TWI_Master_Transfer( unsigned char temp )
265  {
266    USISR = temp;                                     // Set USISR according to temp.
267                                                      // Prepare clocking.
268    temp  =  (0<<USISIE)|(0<<USIOIE)|                 // Interrupts disabled
269             (1<<USIWM1)|(0<<USIWM0)|                 // Set USI in Two-wire mode.
270             (1<<USICS1)|(0<<USICS0)|(1<<USICLK)|     // Software clock strobe as source.
271             (1<<USITC);                              // Toggle Clock Port.
272    do
273    { 
274  	_delay_us(T2_TWI);
275      USICR = temp;                          // Generate positve SCL edge.
276      while( !(PIN_USI & (1<<PIN_USI_SCL)) );// Wait for SCL to go high.
277  	_delay_us(T4_TWI);
278      USICR = temp;                          // Generate negative SCL edge.
279    }while( !(USISR & (1<<USIOIF)) );        // Check for transfer complete.
280    
281  	_delay_us(T2_TWI);
282    temp  = USIDR;                           // Read out data.
283    USIDR = 0xFF;                            // Release SDA.
284    DDR_USI |= (1<<PIN_USI_SDA);             // Enable SDA as output.
285  
286    return temp;                             // Return the data from the USIDR
287  }
288  /*---------------------------------------------------------------
289   Function for generating a TWI Start Condition. 
290  ---------------------------------------------------------------*/
291  unsigned char TinyM_USI_TWI_Master_Start( void )
292  {
293  /* Release SCL to ensure that (repeated) Start can be performed */
294    PORT_USI |= (1<<PIN_USI_SCL);                     // Release SCL.
295    while( !(PORT_USI & (1<<PIN_USI_SCL)) );          // Verify that SCL becomes high.
296    _delay_us(T2_TWI);
297  
298  /* Generate Start Condition */
299    PORT_USI &= ~(1<<PIN_USI_SDA);                    // Force SDA LOW.
300  	_delay_us(T4_TWI);                         
301    PORT_USI &= ~(1<<PIN_USI_SCL);                    // Pull SCL LOW.
302    PORT_USI |= (1<<PIN_USI_SDA);                     // Release SDA.
303  
304  #ifdef SIGNAL_VERIFY
305    if( !(USISR & (1<<USISIF)) )
306    {
307      TinyM_USI_TWI_state.errorState = USI_TWI_MISSING_START_CON;  
308      return (FALSE);
309    }
310  #endif
311    return (TRUE);
312  }
313  /*---------------------------------------------------------------
314   Function for generating a TWI Stop Condition. Used to release 
315   the TWI bus.
316  ---------------------------------------------------------------*/
317  unsigned char TinyM_USI_TWI_Master_Stop( void )
318  {
319    PORT_USI &= ~(1<<PIN_USI_SDA);           // Pull SDA low.
320    PORT_USI |= (1<<PIN_USI_SCL);            // Release SCL.
321    while( !(PIN_USI & (1<<PIN_USI_SCL)) );  // Wait for SCL to go high.  
322  	_delay_us(T4_TWI);
323    PORT_USI |= (1<<PIN_USI_SDA);            // Release SDA.
324  	_delay_us(T2_TWI);
325    
326  #ifdef SIGNAL_VERIFY
327    if( !(USISR & (1<<USIPF)) )
328    {
329      TinyM_USI_TWI_state.errorState = USI_TWI_MISSING_STOP_CON;    
330      return (FALSE);
331    }
332  #endif
333  
334    return (TRUE);
335  }
336  
337  #endif //attiny85