stevemarple / SoftWire

Software I2C implementation for Arduino and other Wiring-type environments
GNU Lesser General Public License v2.1
136 stars 31 forks source link

Minimum SCL frequency #29

Open taherrera opened 2 years ago

taherrera commented 2 years ago

Hello,

¿Why does the library have a lower bound to the I2C frequency? It seems that the lowest frequency allowed by setClock is 1961 Hz. ¿Whats the reasoning behind this?

Nice library btw.

stevemarple commented 2 years ago

The delay between HIGH-to-LOW, and LOW-to-HIGH transitions of the SCL clock signal are stored in a uint8_t variable. For the largest value it can store (255) this gives a maximum period for the SCL clock of 2 × 255 µs = 0.00051 s. So the lowest frequency is given by 1 ÷ 0.00051 = 1961 Hz. I2C devices can normally run at 100 kHz, some can be operated at faster clock speeds. I didn't expect there to be any requirements for the SCL clock frequency to be less than 2 kHz so storing in a uint8_t variable was acceptable, especially as RAM is such a precious resource in most microcontroller architectures.

taherrera commented 2 years ago

Thanks, sounds reasonable. I will modify the library in mi application however, because I need to use I2C over a 6 meter fire-resistant wire. With 10kHz (using ESP32 hardware I2C) we have seen sporadic errors, especially at high temperatures, presumably because of signal degradation due to an increase in impedance. I will be trying out baud rate in the 500Hz range.

Btw, I am using this library because with the hardware I2C on the ESP32 we cannot set a clock lower than 10kHz for some reason.

stevemarple commented 2 years ago

I don't know what you have done with the hardware but when I needed to drive I2C over a distance of about 2 metres there were some modifications I made:

  1. I reduced the value of effective pull-up resistance to decrease the effects of noise.
  2. I used pull-up resistors at both ends of the cable, on SDA and SCL.

If the drive capability of the ESP32 is weak you might find a I2C driver specifically designed for I2C helpful. I used a PCA9517A which is sold as a level-translating I2C bus repeater.

On the software-side I am not convinced that that reducing the clock speed much lower will be helpful unless your cabling has really high capacitance. If the ESP32 has read errors from measurement noise there is one thing you could try. You could take multiple measurements of the SDA logic-level and accept the majority result. You don't even need to modify SoftWire to do this - there is the capability to set custom read and write functions. See setReadSda(). So you could do something like

uint8_t myReadSda(const SoftWire *p)
{
    uint8_t r = 0;
    uint8_t n = 5; // Should be odd
    for (int i = 0; i < n; ++i) {
        r += SoftWire::readSda(p);
    }

    return r >= ((n / 2) + 1); // Take a majority vote
}

i2c.setReadSda(myReadSda);

This of course will not help much if the device at the far end is also suffering noise when decoding the outgoing I2C message (even if just decoding the address).

stevemarple commented 2 years ago

I just realised that you could also call setSetSclLow(), setSetSclHigh(), setSetSdaLow() and setSetSdaHigh() to set custom versions that add extra delay after changing the state of SCL/SDA. That should be equivalent of increasing the delay.

taherrera commented 2 years ago

Thanks for your guidance steve, greatly appreciate it.

In our hardware we are using 1k resistors on the ESP32 side. We will evaluate using two resistors on the line, maybe it is an issue with noise as you say, high currents are flowing in the ovens where the sensor is installed and this could potencially be the source of the problem.

With 100kHz, the capacitance on the line can be clearly seen using an osciloscope in the lab, with 10kHz it is greatly reduced, so that's why I am interested in lowering even further. I will send you pictures, today or tomorrow.