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