I2C Startup

From The TinkerNet Wiki
Revision as of 12:45, 26 April 2021 by Tinker (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

I2C_functions.cpp

  1 #include "libraries.h"
  2 #include "functions.h"
  3 
  4 ////////////////////////////////////////////////////////////////////////////////
  5 bool I2C_Test(int SDA, int SCL)
  6 {
  7     debug_SectionTitle("I2C Test &/or repair...");
  8 
  9     bool SCL_state = (digitalRead(SCL) == HIGH);
 10     bool SDA_state = (digitalRead(SDA) == HIGH);
 11 
 12     if (SCL_state && SDA_state)
 13     {
 14         debug_LineOut("I2C is good.");
 15         return (true);
 16     }
 17     else
 18     {
 19         if (!SCL_state)
 20             debug_Trouble("SCL is LOW.");
 21         if (!SDA_state)
 22             debug_Trouble("SDA is LOW.");
 23         debug_Action("Clearing I2C");
 24 
 25         switch (I2C_ClearBus(SDA, SCL))
 26         {
 27         case 0:
 28             debug_LineOut("I2C is clear.");
 29             return (true);
 30             break;
 31         case 1:
 32             debug_Trouble("I2C bus error. Could not clear");
 33             debug_Trouble("SCL clock line held low");
 34             break;
 35         case 2:
 36             debug_Trouble("I2C bus error. Could not clear SCL clock");
 37             debug_Trouble("line held low by slave clock stretch");
 38             break;
 39         case 3:
 40             debug_Trouble("I2C bus error. Could not clear");
 41             debug_Trouble("SDA data line held low");
 42             break;
 43         }
 44         debug_Trouble("++++++++++++++++ STOPPING!!! +++++++++++++++++");
 45         while (true) // Slam on da brakes!
 46             blinkLED(100);
 47         return (false);
 48     }
 49 }
 50 ////////////////////////////////////////////////////////////////////////////////
 51 
 52 /**
 53  * This routine turns off the I2C bus and clears it
 54  * on return SCA and SCL pins are tri-state inputs.
 55  * You need to call Wire.begin() after this to re-enable I2C
 56  * This routine does NOT use the Wire library at all.
 57  *
 58  * returns 0 if bus cleared
 59  *         1 if SCL held low.
 60  *         2 if SDA held low by slave clock stretch for > 2sec
 61  *         3 if SDA held low after 20 clocks.
 62  * 
 63  * Source:  http://www.forward.com.au/pfod/ArduinoProgramming/I2C_ClearBus/index.html
 64  */
 65 
 66 int I2C_ClearBus(int SDA, int SCL)
 67 {
 68 #if defined(TWCR) && defined(TWEN)
 69     TWCR &= ~(_BV(TWEN)); // Disable the Atmel 2-Wire interface
 70                           // so we can control the SDA and SCL pins directly
 71 #endif
 72 
 73     pinMode(SDA, INPUT_PULLUP); // Make SDA (data) and SCL (clock) pins Inputs with pullup.
 74     pinMode(SCL, INPUT_PULLUP);
 75 
 76     delay(2500); // Wait 2.5 secs. This is strictly only necessary on the first power
 77     // up of the DS3231 module to allow it to initialize properly,
 78     // but is also assists in reliable programming of FioV3 boards as it gives the
 79     // IDE a chance to start uploaded the program
 80     // before existing sketch confuses the IDE by sending Serial data.
 81 
 82     boolean SCL_LOW = (digitalRead(SCL) == LOW); // Check is SCL is Low.
 83     if (SCL_LOW)
 84     {             //If it is held low Arduno cannot become the I2C master.
 85         return 1; //I2C bus error. Could not clear SCL clock line held low
 86     }
 87 
 88     boolean SDA_LOW = (digitalRead(SDA) == LOW); // vi. Check SDA input.
 89     int clockCount = 20;                         // > 2x9 clock
 90 
 91     while (SDA_LOW && (clockCount > 0))
 92     { //  vii. If SDA is Low,
 93         clockCount--;
 94         // Note: I2C bus is open collector so do NOT drive SCL or SDA high.
 95         pinMode(SCL, INPUT);        // release SCL pullup so that when made output it will be LOW
 96         pinMode(SCL, OUTPUT);       // then clock SCL Low
 97         delayMicroseconds(10);      //  for >5uS
 98         pinMode(SCL, INPUT);        // release SCL LOW
 99         pinMode(SCL, INPUT_PULLUP); // turn on pullup resistors again
100         // do not force high as slave may be holding it low for clock stretching.
101         delayMicroseconds(10); //  for >5uS
102         // The >5uS is so that even the slowest I2C devices are handled.
103         SCL_LOW = (digitalRead(SCL) == LOW); // Check if SCL is Low.
104         int counter = 20;
105         while (SCL_LOW && (counter > 0))
106         { //  loop waiting for SCL to become High only wait 2sec.
107             counter--;
108             delay(100);
109             SCL_LOW = (digitalRead(SCL) == LOW);
110         }
111         if (SCL_LOW)
112         {             // still low after 2 sec error
113             return 2; // I2C bus error. Could not clear.
114                       // SCL clock line held low by slave clock stretch for >2sec
115         }
116         SDA_LOW = (digitalRead(SDA) == LOW); //   and check SDA input again and loop
117     }
118     if (SDA_LOW)
119     {             // still low
120         return 3; // I2C bus error. Could not clear. SDA data line held low
121     }
122 
123     // else pull SDA line low for Start or Repeated Start
124     pinMode(SDA, INPUT);        // remove pullup.
125     pinMode(SDA, OUTPUT);       // and then make it LOW i.e. send an I2C Start or Repeated start control.
126                                 // When there is only one I2C master a Start or Repeat Start
127                                 // has the same function as a Stop and clears the bus.
128                                 // A Repeat Start is a Start occurring after a Start with
129                                 // no intervening Stop.
130     delayMicroseconds(10);      // wait >5uS
131     pinMode(SDA, INPUT);        // remove output low
132     pinMode(SDA, INPUT_PULLUP); // and make SDA high i.e. send I2C STOP control.
133     delayMicroseconds(10);      // x. wait >5uS
134     pinMode(SDA, INPUT);        // and reset pins as tri-state inputs which is the default state on reset
135     pinMode(SCL, INPUT);
136     return 0; // all ok
137 }

Calling it from setup()

 1  if (I2C_Test(SSD1306_SDA, SSD1306_SCL)) // Force a reset of the I2C bus if needed
 2   {
 3     Wire.begin(SSD1306_SDA, SSD1306_SCL); // Select SDA & SCL pins
 4                                           /////////////////////////////
 5     pinMode(SSD1306_SDA, INPUT);
 6     pinMode(SSD1306_SCL, INPUT);
 7   }
 8   else
 9   {
10     debug_Action("POOP!");
11   }