Many changes from 2018
[owSlave2.git] / common / I2C / TWI_Master.c
diff --git a/common/I2C/TWI_Master.c b/common/I2C/TWI_Master.c
new file mode 100644 (file)
index 0000000..ed125ec
--- /dev/null
@@ -0,0 +1,367 @@
+\r
+#ifdef  __4MHZ__\r
+#define F_CPU 4000000UL\r
+#else\r
+#define F_CPU 8000000UL\r
+#endif\r
+#include <avr/io.h>\r
+#include "TWI_Master.h"\r
+#include <util/delay.h>\r
+\r
+#if defined(__AVR_AT90Mega169__) | defined(__AVR_ATmega169PA__) | \\r
+defined(__AVR_AT90Mega165__) | defined(__AVR_ATmega165__) | \\r
+defined(__AVR_ATmega325__) | defined(__AVR_ATmega3250__) | \\r
+defined(__AVR_ATmega645__) | defined(__AVR_ATmega6450__) | \\r
+defined(__AVR_ATmega329__) | defined(__AVR_ATmega3290__) | \\r
+defined(__AVR_ATmega649__) | defined(__AVR_ATmega6490__) |\\r
+defined(__AVR_ATtiny25__) | defined(__AVR_ATtiny45__) | defined(__AVR_ATtiny85__) | \\r
+defined(__AVR_AT90Tiny26__) | defined(__AVR_ATtiny26__) |\\r
+defined(__AVR_AT90Tiny2313__) | defined(__AVR_ATtiny2313__) |\\r
+defined(__AVR_ATtiny84__) | defined(__AVR_ATtiny84A__)\r
+\r
+unsigned char USI_TWI_Master_Transfer( unsigned char );\r
+unsigned char USI_TWI_Master_Stop( void );\r
+\r
+union  USI_TWI_state\r
+{\r
+  unsigned char errorState;         // Can reuse the TWI_state for error states due to that it will not be need if there exists an error.\r
+  struct\r
+  {\r
+    unsigned char addressMode         : 1;\r
+    unsigned char masterWriteDataMode : 1;\r
+    unsigned char unused              : 6;\r
+  }; \r
+}   USI_TWI_state;\r
+\r
+/*---------------------------------------------------------------\r
+ USI TWI single master initialization function\r
+---------------------------------------------------------------*/\r
+void TWI_Master_Initialise( void )\r
+{\r
+  PORT_USI |= (1<<PIN_USI_SDA);           // Enable pullup on SDA, to set high as released state.\r
+  PORT_USI |= (1<<PIN_USI_SCL);           // Enable pullup on SCL, to set high as released state.\r
+  \r
+  DDR_USI  |= (1<<PIN_USI_SCL);           // Enable SCL as output.\r
+  DDR_USI  |= (1<<PIN_USI_SDA);           // Enable SDA as output.\r
+  \r
+  USIDR    =  0xFF;                       // Preload dataregister with "released level" data.\r
+  USICR    =  (0<<USISIE)|(0<<USIOIE)|                            // Disable Interrupts.\r
+              (1<<USIWM1)|(0<<USIWM0)|                            // Set USI in Two-wire mode.\r
+              (1<<USICS1)|(0<<USICS0)|(1<<USICLK)|                // Software stobe as counter clock source\r
+              (0<<USITC);\r
+  USISR   =   (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|      // Clear flags,\r
+              (0x0<<USICNT0);                                     // and reset counter.\r
+}\r
+\r
+/*---------------------------------------------------------------\r
+Use this function to get hold of the error message from the last transmission\r
+---------------------------------------------------------------*/\r
+unsigned char USI_TWI_Get_State_Info( void )\r
+{\r
+  return ( USI_TWI_state.errorState );                            // Return error state.\r
+}\r
+\r
+/*---------------------------------------------------------------\r
+ USI Transmit and receive function. LSB of first byte in data \r
+ indicates if a read or write cycles is performed. If set a read\r
+ operation is performed.\r
+\r
+ Function generates (Repeated) Start Condition, sends address and\r
+ R/W, Reads/Writes Data, and verifies/sends ACK.\r
\r
+ Success or error code is returned. Error codes are defined in \r
+ USI_TWI_Master.h\r
+---------------------------------------------------------------*/\r
+unsigned char USI_TWI_Start_Transceiver_With_Data( unsigned char *msg, unsigned char msgSize)\r
+{\r
+  unsigned char tempUSISR_8bit = (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|      // Prepare register value to: Clear flags, and\r
+                                 (0x0<<USICNT0);                                     // set USI to shift 8 bits i.e. count 16 clock edges.\r
+  unsigned char tempUSISR_1bit = (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|      // Prepare register value to: Clear flags, and\r
+                                 (0xE<<USICNT0);                                     // set USI to shift 1 bit i.e. count 2 clock edges.\r
+\r
+  USI_TWI_state.errorState = 0;\r
+  USI_TWI_state.addressMode = TRUE;\r
+\r
+#ifdef PARAM_VERIFICATION\r
+  if(msg > (unsigned char*)RAMEND)                 // Test if address is outside SRAM space\r
+  {\r
+    USI_TWI_state.errorState = USI_TWI_DATA_OUT_OF_BOUND;\r
+    return (FALSE);\r
+  }\r
+  if(msgSize <= 1)                                 // Test if the transmission buffer is empty\r
+  {\r
+    USI_TWI_state.errorState = USI_TWI_NO_DATA;\r
+    return (FALSE);\r
+  }\r
+#endif\r
+\r
+#ifdef NOISE_TESTING                                // Test if any unexpected conditions have arrived prior to this execution.\r
+  if( USISR & (1<<USISIF) )\r
+  {\r
+    USI_TWI_state.errorState = USI_TWI_UE_START_CON;\r
+    return (FALSE);\r
+  }\r
+  if( USISR & (1<<USIPF) )\r
+  {\r
+    USI_TWI_state.errorState = USI_TWI_UE_STOP_CON;\r
+    return (FALSE);\r
+  }\r
+  if( USISR & (1<<USIDC) )\r
+  {\r
+    USI_TWI_state.errorState = USI_TWI_UE_DATA_COL;\r
+    return (FALSE);\r
+  }\r
+#endif\r
+\r
+  if ( !(*msg & (1<<TWI_READ_BIT)) )                // The LSB in the address byte determines if is a masterRead or masterWrite operation.\r
+  {\r
+    USI_TWI_state.masterWriteDataMode = TRUE;\r
+  }\r
+\r
+/* Release SCL to ensure that (repeated) Start can be performed */\r
+  PORT_USI |= (1<<PIN_USI_SCL);                     // Release SCL.\r
+  while( !(PIN_USI & (1<<PIN_USI_SCL)) );          // Verify that SCL becomes high.\r
+#ifdef TWI_FAST_MODE\r
+  _delay_us( T4_TWI/4 );                         // Delay for T4TWI if TWI_FAST_MODE\r
+#else\r
+  _delay_us( T2_TWI/4 );                         // Delay for T2TWI if TWI_STANDARD_MODE\r
+#endif\r
+\r
+/* Generate Start Condition */\r
+  PORT_USI &= ~(1<<PIN_USI_SDA);                    // Force SDA LOW.\r
+  _delay_us( T4_TWI/4 );                         \r
+  PORT_USI &= ~(1<<PIN_USI_SCL);                    // Pull SCL LOW.\r
+  PORT_USI |= (1<<PIN_USI_SDA);                     // Release SDA.\r
+\r
+#ifdef SIGNAL_VERIFY\r
+  if( !(USISR & (1<<USISIF)) )\r
+  {\r
+    USI_TWI_state.errorState = USI_TWI_MISSING_START_CON;  \r
+    return (FALSE);\r
+  }\r
+#endif\r
+\r
+/*Write address and Read/Write data */\r
+  do\r
+  {\r
+    /* If masterWrite cycle (or inital address tranmission)*/\r
+    if (USI_TWI_state.addressMode || USI_TWI_state.masterWriteDataMode)\r
+    {\r
+      /* Write a byte */\r
+      PORT_USI &= ~(1<<PIN_USI_SCL);                // Pull SCL LOW.\r
+      USIDR     = *(msg++);                        // Setup data.\r
+      USI_TWI_Master_Transfer( tempUSISR_8bit );    // Send 8 bits on bus.\r
+      \r
+      /* Clock and verify (N)ACK from slave */\r
+      DDR_USI  &= ~(1<<PIN_USI_SDA);                // Enable SDA as input.\r
+      if( USI_TWI_Master_Transfer( tempUSISR_1bit ) & (1<<TWI_NACK_BIT) ) \r
+      {\r
+        if ( USI_TWI_state.addressMode )\r
+          USI_TWI_state.errorState = USI_TWI_NO_ACK_ON_ADDRESS;\r
+        else\r
+          USI_TWI_state.errorState = USI_TWI_NO_ACK_ON_DATA;\r
+        return (FALSE);\r
+      }\r
+      USI_TWI_state.addressMode = FALSE;            // Only perform address transmission once.\r
+    }\r
+    /* Else masterRead cycle*/\r
+    else\r
+    {\r
+      /* Read a data byte */\r
+      DDR_USI   &= ~(1<<PIN_USI_SDA);               // Enable SDA as input.\r
+      *(msg++)  = USI_TWI_Master_Transfer( tempUSISR_8bit );\r
+\r
+      /* Prepare to generate ACK (or NACK in case of End Of Transmission) */\r
+      if( msgSize == 1)                            // If transmission of last byte was performed.\r
+      {\r
+        USIDR = 0xFF;                              // Load NACK to confirm End Of Transmission.\r
+      }\r
+      else\r
+      {\r
+        USIDR = 0x00;                              // Load ACK. Set data register bit 7 (output for SDA) low.\r
+      }\r
+      USI_TWI_Master_Transfer( tempUSISR_1bit );   // Generate ACK/NACK.\r
+    }\r
+  }while( --msgSize) ;                             // Until all data sent/received.\r
+  \r
+  USI_TWI_Master_Stop();                           // Send a STOP condition on the TWI bus.\r
+\r
+/* Transmission successfully completed*/\r
+  return (TRUE);\r
+}\r
+\r
+/*---------------------------------------------------------------\r
+ Core function for shifting data in and out from the USI.\r
+ Data to be sent has to be placed into the USIDR prior to calling\r
+ this function. Data read, will be return'ed from the function.\r
+---------------------------------------------------------------*/\r
+unsigned char USI_TWI_Master_Transfer( unsigned char temp )\r
+{\r
+  USISR = temp;                                     // Set USISR according to temp.\r
+                                                    // Prepare clocking.\r
+  temp  =  (0<<USISIE)|(0<<USIOIE)|                 // Interrupts disabled\r
+           (1<<USIWM1)|(0<<USIWM0)|                 // Set USI in Two-wire mode.\r
+           (1<<USICS1)|(0<<USICS0)|(1<<USICLK)|     // Software clock strobe as source.\r
+           (1<<USITC);                              // Toggle Clock Port.\r
+  do\r
+  {\r
+    _delay_us( T2_TWI/4 );              \r
+    USICR = temp;                          // Generate positve SCL edge.\r
+    while( !(PIN_USI & (1<<PIN_USI_SCL)) );// Wait for SCL to go high.\r
+    _delay_us( T4_TWI/4 );              \r
+    USICR = temp;                          // Generate negative SCL edge.\r
+  }while( !(USISR & (1<<USIOIF)) );        // Check for transfer complete.\r
+  \r
+  _delay_us( T2_TWI/4 );                \r
+  temp  = USIDR;                           // Read out data.\r
+  USIDR = 0xFF;                            // Release SDA.\r
+  DDR_USI |= (1<<PIN_USI_SDA);             // Enable SDA as output.\r
+\r
+  return temp;                             // Return the data from the USIDR\r
+}\r
+\r
+/*---------------------------------------------------------------\r
+ Function for generating a TWI Stop Condition. Used to release \r
+ the TWI bus.\r
+---------------------------------------------------------------*/\r
+unsigned char USI_TWI_Master_Stop( void )\r
+{\r
+  PORT_USI &= ~(1<<PIN_USI_SDA);           // Pull SDA low.\r
+  PORT_USI |= (1<<PIN_USI_SCL);            // Release SCL.\r
+  while( !(PIN_USI & (1<<PIN_USI_SCL)) );  // Wait for SCL to go high.\r
+  _delay_us( T4_TWI/4 );               \r
+  PORT_USI |= (1<<PIN_USI_SDA);            // Release SDA.\r
+  _delay_us( T2_TWI/4 );                \r
+  \r
+#ifdef SIGNAL_VERIFY\r
+  if( !(USISR & (1<<USIPF)) )\r
+  {\r
+    USI_TWI_state.errorState = USI_TWI_MISSING_STOP_CON;    \r
+    return (FALSE);\r
+  }\r
+#endif\r
+\r
+  return (TRUE);\r
+}\r
+\r
+\r
+\r
+unsigned char I2c_WriteByte(unsigned char msg) {\r
+  unsigned char tempUSISR_8bit = (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|      // Prepare register value to: Clear flags, and\r
+  (0x0<<USICNT0);                                     // set USI to shift 8 bits i.e. count 16 clock edges.\r
+  unsigned char tempUSISR_1bit = (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|      // Prepare register value to: Clear flags, and\r
+  (0xE<<USICNT0);                                     // set USI to shift 1 bit i.e. count 2 clock edges.\r
+\r
+       /* Write a byte */\r
+       PORT_USI &= ~(1<<PIN_USI_SCL);                // Pull SCL LOW.\r
+       USIDR     = msg;                        // Setup data.\r
+       USI_TWI_Master_Transfer( tempUSISR_8bit );    // Send 8 bits on bus.\r
+       /* Clock and verify (N)ACK from slave */\r
+       DDR_USI  &= ~(1<<PIN_USI_SDA);                // Enable SDA as input.\r
+       if( USI_TWI_Master_Transfer( tempUSISR_1bit ) & (1<<TWI_NACK_BIT) ){\r
+               if ( USI_TWI_state.addressMode )\r
+                       USI_TWI_state.errorState = USI_TWI_NO_ACK_ON_ADDRESS;\r
+               else\r
+                       USI_TWI_state.errorState = USI_TWI_NO_ACK_ON_DATA;\r
+               return 2;\r
+       }\r
+       return 0;\r
+}\r
+unsigned char I2c_ReadByte(unsigned char ack_mode) {\r
+  unsigned char tempUSISR_8bit = (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|      // Prepare register value to: Clear flags, and\r
+  (0x0<<USICNT0);                                     // set USI to shift 8 bits i.e. count 16 clock edges.\r
+  unsigned char tempUSISR_1bit = (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|      // Prepare register value to: Clear flags, and\r
+  (0xE<<USICNT0);                                     // set USI to shift 1 bit i.e. count 2 clock edges.\r
+       \r
+       /* Read a data byte */\r
+       DDR_USI   &= ~(1<<PIN_USI_SDA);               // Enable SDA as input.\r
+    unsigned char msg = USI_TWI_Master_Transfer( tempUSISR_8bit );\r
+\r
+    /* Prepare to generate ACK (or NACK in case of End Of Transmission) */\r
+    if( ack_mode == NO_ACK) {                           // If transmission of last byte was performed.\r
+               USIDR = 0xFF;                              // Load NACK to confirm End Of Transmission.\r
+    } else   {\r
+               USIDR = 0x00;                              // Load ACK. Set data register bit 7 (output for SDA) low.\r
+    }\r
+    USI_TWI_Master_Transfer( tempUSISR_1bit );   // Generate ACK/NACK.\r
+       return msg;\r
+}\r
+\r
+void I2c_StartCondition(void) {\r
+/* Release SCL to ensure that (repeated) Start can be performed */\r
+PORT_USI |= (1<<PIN_USI_SCL);                     // Release SCL.\r
+while( !(PIN_USI & (1<<PIN_USI_SCL)) );          // Verify that SCL becomes high.\r
+#ifdef TWI_FAST_MODE\r
+_delay_us( T4_TWI/4 );                         // Delay for T4TWI if TWI_FAST_MODE\r
+#else\r
+_delay_us( T2_TWI/4 );                         // Delay for T2TWI if TWI_STANDARD_MODE\r
+#endif\r
+\r
+/* Generate Start Condition */\r
+PORT_USI &= ~(1<<PIN_USI_SDA);                    // Force SDA LOW.\r
+_delay_us( T4_TWI/4 );\r
+PORT_USI &= ~(1<<PIN_USI_SCL);                    // Pull SCL LOW.\r
+PORT_USI |= (1<<PIN_USI_SDA);                     // Release SDA.\r
+       \r
+       \r
+}\r
+void I2c_StopCondition(void) {\r
+       USI_TWI_Master_Stop();\r
+}\r
+\r
+#endif\r
+\r
+\r
+\r
+\r
+#if defined(__AVR_ATmega328PB__)| defined(__AVR_ATmega328P__)\r
+\r
+\r
+#include <util/twi.h>\r
+\r
+void TWI_Wait_Busy() {\r
+       _delay_us(100);\r
+       while (!( TWCR & (1<<TWINT) )); \r
+}\r
+\r
+\r
+void TWI_Master_Initialise( void ) {\r
+       TWBR = TWI_TWBR;                                  // Set bit rate register (Baud rate). Defined in header file.Driver presumes prescaler to be 00.\r     TWSR=0;\r        TWDR = 0xFF;                                      // Default content = SDA released.\r   TWCR = (1<<TWEN)|                                 // Enable TWI-interface and release TWI pins.\r                (0<<TWIE)|(0<<TWINT)|                      // Disable Interrupt.\r               (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|           // No Signal requests.\r              (0<<TWWC);\r
+               \r
+}\r
+\r
+unsigned char I2c_WriteByte(unsigned char msg) {\r
+       uint8_t   twst;\r        TWDR = msg;\r    TWCR = (1<<TWINT) | (1<<TWEN);\r TWI_Wait_Busy();\r        // check value of TWI Status Register. Mask prescaler bits\r     twst = TW_STATUS & 0xF8;\r       if( twst != TW_MT_DATA_ACK) return 1;\r return 0;\r
+}\r
+unsigned char I2c_ReadByte(unsigned char ack_mode) {\r
+       TWCR = (1<<TWINT) | (1<<TWEN) |ack_mode;\r       TWI_Wait_Busy();\r    return TWDR;\r
+\r
+}\r
+void I2c_StartCondition(void) {\r
+       TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);\r
+       TWI_Wait_Busy();\r
+       uint8_t twst = TW_STATUS & 0xF8;\r       if ( (twst != TW_START) && (twst != TW_REP_START)) return ;// return 1;\r}\r
+/*\r\r
+// send device address\r\r
+TWDR = address;\r\r
+TWCR = (1<<TWINT) | (1<<TWEN);\r\r
+\r\r
+// wail until transmission completed and ACK/NACK has been received\r\r
+while(!(TWCR & (1<<TWINT)));\r\r
+\r\r
+// check value of TWI Status Register. Mask prescaler bits.\r\r
+twst = TW_STATUS & 0xF8;\r\r
+if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;\r\r
+\r\r
+return 0;\r\r
+\r
+}\r
+*/\r
+void I2c_StopCondition(void) {\r
+\r
+   TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);\r\r
+   while(TWCR & (1<<TWSTO));\r
+\r
+}\r
+\r
+#endif
\ No newline at end of file