uRTCLib icon indicating copy to clipboard operation
uRTCLib copied to clipboard

Unlock I2C if DS3231 is in unknown state

Open rtek1000 opened this issue 3 months ago • 0 comments

I'm developing a program with PIC16F1938, and I'm based on this library, which is the simplest and most informative, especially in relation to the oscillator activation bit in case of a lack of battery.

But I had several incidents of I2C crashing when reprogramming the uC. To get out of this condition, there is no point in restarting the power supply, you must also remove the battery.

The PIC only has the DS3231 module (and the I2C memory built into the module).

The DS3231 datasheet warns about this problem of having an unknown state, if the uC is restarted and the DS3231 is being read:

I2C Interface The I2C interface is accessible whenever either VCC or VBAT is at a valid level. If a microcontroller connected to the DS3231 resets because of a loss of VCC or other event, it is possible that the microcontroller and DS3231 I2C communications could become unsynchronized, e.g., the microcontroller resets while reading data from the DS3231. When the microcontroller resets, the DS3231 I2C interface may be placed into a known state by toggling SCL until SDA is observed to be at a high level. At that point the microcontroller should pull SDA low while SCL is high, generating a START condition.

I was successful on the first attempt, I just read the SDA line, before initializing the RTC, and switched the SCL line, as suggested in the DS3231 datasheet. I greatly exceeded the amount of alternation, but I believe that a few SCL pulses are enough.

If someone is suffering from this problem, it may be necessary to implement this unlocking, and it may be interesting to have this in the library, or at least the information.

void I2C_Init(void) {
    SCL = 1; // HIGH
    SDA = 1; // HIGH
    
    SCL_Direction = 1; // INPUT to enable I2C
    SDA_Direction = 1; // INPUT to enable I2C
    
    if (SDA == 0) { // Check I2C if is in unknown state (DS3231)
        for(uint16_t i = 0; i < 1000; i++) {
           SCL_Direction = 0; // SCL output
           SCL = 0; // LOW
           
           __delay_us(250);

           SCL_Direction = 1; // SCL input
           SCL = 1; // HIGH
           
           __delay_us(250);
           
           if (SDA == 1) { // Check I2C if is in known state
               break;
           }
        }
    }

    // I2C_clock = FOSC/(4 * (SSPADD + 1))
    // I2C_clock = 20'000'000 / (4 * (50 + 1))
    // I2C_clock = 20'000'000 / (4 * 51)
    // I2C_clock = 20'000'000 / 204
    // I2C_clock = 98 kHz
    //
    // I2C_clock = 20'000'000 / (4 * (11 + 1))
    // I2C_clock = 20'000'000 / (4 * 12)
    // I2C_clock = 20'000'000 / 48
    // I2C_clock = 417 kHz


    SSPSTAT |= 0x80; /* Slew rate disabled */
    SSPCON = 0x28; /* SSPEN = 1, I2C Master mode, I2C_clock = FOSC/(4 * (SSPADD + 1)) */
    SSPADD = 50; /* 100Khz @ 20Mhz Fosc: 50*/
}

rtek1000 avatar Apr 07 '24 01:04 rtek1000