Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 14 of 14

Thread: I2C maximum speed

  1. #1
    Senior Member
    Join Date
    May 2017
    Location
    Netherlands
    Posts
    112

    I2C maximum speed

    Hi all,

    For my projects I must know the maximum speed of the I2C bus Teensy 4 this is needed to read my external A/D converter.

    If someone know please replay.

    Best regards,
    Johan

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    21,473
    If you use the Wire library, 1 MHz is the fastest speed it supports. Of course, you'll need strong pullup resistors and moderate to short length wires and not many chips connected for the signals to actually work that fast.

    If you hack the WireIMXRT.cpp or directly write to the MCCR0, MCFGR1, MCFGR2 registers, I hope you'll share anything you learn about the actual practical speed limits. The speed setting in this new chip isn't just 1 simple number. It's a bunch of settings that control various timing aspects of the waveform. Details can be found in the chip's reference manual.

    Here's the code you'll find inside WireIMXRT.cpp:

    Code:
            if (frequency < 400000) {
                    // 100 kHz
                    port->MCCR0 = LPI2C_MCCR0_CLKHI(55) | LPI2C_MCCR0_CLKLO(59) |
                            LPI2C_MCCR0_DATAVD(25) | LPI2C_MCCR0_SETHOLD(40);
                    port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(1);
                    port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(5) | LPI2C_MCFGR2_FILTSCL(5) |
                            LPI2C_MCFGR2_BUSIDLE(3900);
            } else if (frequency < 1000000) {
                    // 400 kHz
                    port->MCCR0 = LPI2C_MCCR0_CLKHI(26) | LPI2C_MCCR0_CLKLO(28) |
                            LPI2C_MCCR0_DATAVD(12) | LPI2C_MCCR0_SETHOLD(18);
                    port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
                    port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(2) | LPI2C_MCFGR2_FILTSCL(2) |
                            LPI2C_MCFGR2_BUSIDLE(3900);
            } else {
                    // 1 MHz
                    port->MCCR0 = LPI2C_MCCR0_CLKHI(9) | LPI2C_MCCR0_CLKLO(10) |
                            LPI2C_MCCR0_DATAVD(4) | LPI2C_MCCR0_SETHOLD(7);
                    port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
                    port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(1) | LPI2C_MCFGR2_FILTSCL(1) |
                            LPI2C_MCFGR2_BUSIDLE(3900);
            }
    If you reduce those numbers, it very likely will run faster. How good the waveforms really look, or how reliably it really works is a good question. The reality is Teensy 4.0 is very new. So far it's only been tested with those 3 settings. As far as I know, nobody using this forum has really tested it at other settings. I know I sure haven't. But I did run tests with all 3 of those settings during the beta test. 1 MHz definitely does work.

  3. #3
    Senior Member
    Join Date
    May 2017
    Location
    Netherlands
    Posts
    112
    Hi all,

    I like to run my AD7991 I2C on 1MHz Paul says change some numbers but what to change to get 1MHz for the I2C bus? In my case I use SDA1 an SCL1..

    Best,
    Johan

  4. #4
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,388
    Quote Originally Posted by pd0lew View Post
    Hi all,

    I like to run my AD7991 I2C on 1MHz Paul says change some numbers but what to change to get 1MHz for the I2C bus? In my case I use SDA1 an SCL1..

    Best,
    Johan
    Wire1.setClock(1000000)

  5. #5
    Senior Member
    Join Date
    May 2017
    Location
    Netherlands
    Posts
    112
    void setup()
    {

    Wire1.setClock(1000000) ;
    Right ?

  6. #6
    Senior Member
    Join Date
    May 2017
    Location
    Netherlands
    Posts
    112
    Hi, I don't think it's that simple for a Teensy 4.... see picture
    about 100KHz

    Click image for larger version. 

Name:	DeepinScreenshot_select-area_20190827192253.png 
Views:	31 
Size:	129.4 KB 
ID:	17342
    Best,
    Johan

  7. #7
    Senior Member
    Join Date
    May 2017
    Location
    Netherlands
    Posts
    112
    The maximum I can get is 375 KHz, instead of 1MHz.......


    void TwoWire::setClock(uint32_t frequency)
    {
    port->MCR = 0;
    if (frequency < 100000) {
    // 100 kHz
    port->MCCR0 = LPI2C_MCCR0_CLKHI(55) | LPI2C_MCCR0_CLKLO(59) |
    LPI2C_MCCR0_DATAVD(25) | LPI2C_MCCR0_SETHOLD(40);
    port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(1);
    port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(5) | LPI2C_MCFGR2_FILTSCL(5) |
    LPI2C_MCFGR2_BUSIDLE(3900);
    } else if (frequency < 1000000) {
    // 400 kHz
    port->MCCR0 = LPI2C_MCCR0_CLKHI(26) | LPI2C_MCCR0_CLKLO(28) |
    LPI2C_MCCR0_DATAVD(12) | LPI2C_MCCR0_SETHOLD(18);
    port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
    port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(2) | LPI2C_MCFGR2_FILTSCL(2) |
    LPI2C_MCFGR2_BUSIDLE(3900);
    } else {
    // 1 MHz
    port->MCCR0 = LPI2C_MCCR0_CLKHI(9) | LPI2C_MCCR0_CLKLO(10) |
    LPI2C_MCCR0_DATAVD(4) | LPI2C_MCCR0_SETHOLD(7);
    port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
    port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(1) | LPI2C_MCFGR2_FILTSCL(1) |
    LPI2C_MCFGR2_BUSIDLE(3900);
    }
    port->MCCR1 = port->MCCR0;
    port->MCFGR0 = 0;
    port->MCFGR3 = LPI2C_MCFGR3_PINLOW(3900);
    port->MFCR = LPI2C_MFCR_RXWATER(1) | LPI2C_MFCR_TXWATER(1);
    port->MCR = LPI2C_MCR_MEN;
    }

    #endif

  8. #8
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,388
    Quote Originally Posted by pd0lew View Post
    Hi, I don't think it's that simple for a Teensy 4.... see picture
    about 100KHz
    Best,
    Johan
    Works for me. you need to do Wire1.setClock() after Wire1.begin()
    With nothing connected to I2C pins and runnning I2C Scanner, scope shows 86 khz for clock 100khz, 238khz for clock 400khz, 380khz for clock 1mhz. With BMP085 breakout (10k pullups), scope shows 96KHz for I2C clock @100KHz, 325KHz for clock @400KHz, and 625KHz for clock 1MHz. Adding 2.2Kohm pullups in parallel on SDA and SCL, scope shows 99.2khz @100khz, 379khz @400khz, and 893khz @1mhz.
    Last edited by manitou; 08-28-2019 at 10:07 AM.

  9. #9
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,388
    Quote Originally Posted by pd0lew View Post
    The maximum I can get is 375 KHz, instead of 1MHz.......
    It's not uncommon for the actual clock rate to be less than requested on T3* and T4 and maybe many other MCU's. You can try and hack the core I2C driver code to increase the I2C clock closer to your target (read ref manual). But as Paul noted, changing I2C clock may also require changing high and low times for SDA and SCL waveforms.

    early in T4 beta testing, i experimented with I2C clock (pins 18,19) see
    https://forum.pjrc.com/threads/54711...l=1#post194538
    scope measured 926KHz with I2C@1MHz
    Last edited by manitou; 08-27-2019 at 07:11 PM.

  10. #10
    Senior Member
    Join Date
    May 2017
    Location
    Netherlands
    Posts
    112
    I think there can be a clear answer to the I2C bus instead of reading 1000 pages.

    This needs more research....

    Click image for larger version. 

Name:	DeepinScreenshot_select-area_20190827202305.jpg 
Views:	40 
Size:	147.2 KB 
ID:	17343

  11. #11
    Member Rena's Avatar
    Join Date
    Oct 2014
    Location
    Cambridge, Ontario, Canadia
    Posts
    32
    I2C can be pretty finicky. In your graph it looks like you have some bus capacitance which will interfere with the signal.

  12. #12
    Junior Member
    Join Date
    Mar 2020
    Posts
    3
    Hi all,

    My goal was to make a sine generator with a trigger signal at certain moment of the sine wave.

    I used teensy 4.0 and sparkfun MCP4725, Sparkfun breakout board uses pullup resistors for SCL SDA, both 4.7 kOhm. I powerd it from 3,3V Teensy board.
    After tweaking the WireIMXRT.cpp, I got improvement of frequency generation of 512 sample sine, from 50.96Hz to 117.26 Hz for default Wire.setClock(1000000) and the tweaked one, receptively.

    Code:
    void TwoWire::setClock(uint32_t frequency)
    {
    	port->MCR = 0;
    	if (frequency < 400000) {
    		// 100 kHz
    		port->MCCR0 = LPI2C_MCCR0_CLKHI(55) | LPI2C_MCCR0_CLKLO(59) |
    			LPI2C_MCCR0_DATAVD(25) | LPI2C_MCCR0_SETHOLD(40);
    		port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(1);
    		port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(5) | LPI2C_MCFGR2_FILTSCL(5) |
    			LPI2C_MCFGR2_BUSIDLE(3000); // idle timeout 250 us
    		port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 12 / 256 + 1);
    	} else if (frequency < 1000000) {
    		// 400 kHz
    		port->MCCR0 = LPI2C_MCCR0_CLKHI(26) | LPI2C_MCCR0_CLKLO(28) |
    			LPI2C_MCCR0_DATAVD(12) | LPI2C_MCCR0_SETHOLD(18);
    		port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
    		port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(2) | LPI2C_MCFGR2_FILTSCL(2) |
    			LPI2C_MCFGR2_BUSIDLE(3600); // idle timeout 150 us
    		port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 24 / 256 + 1);
    	} else if (frequency < 3400000){
    		// 1 MHz
    		port->MCCR0 = LPI2C_MCCR0_CLKHI(9) | LPI2C_MCCR0_CLKLO(10) |
    			LPI2C_MCCR0_DATAVD(4) | LPI2C_MCCR0_SETHOLD(7);
    		port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
    		port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(1) | LPI2C_MCFGR2_FILTSCL(1) |
    			LPI2C_MCFGR2_BUSIDLE(2400); // idle timeout 100 us
    		port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 24 / 256 + 1);
    	} else {
    	
    	// Overclocking
    	//LPI2C_MCCR0_CLKLO(7) less then 5 fail
    	//FILTSDA(0) | LPI2C_MCFGR2_FILTSCL(0) does not matter
    	//LPI2C_MCFGR2_BUSIDLE(2400); less then 2400 does not matter
    	//LPI2C_MCCR0_DATAVD(4) less then 4 does not matter
    	
    	port->MCCR0 = LPI2C_MCCR0_CLKHI(1) | LPI2C_MCCR0_CLKLO(5) |
    			LPI2C_MCCR0_DATAVD(3) | LPI2C_MCCR0_SETHOLD(4);
    		port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
    		port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(0) | LPI2C_MCFGR2_FILTSCL(0) |
    			LPI2C_MCFGR2_BUSIDLE(1800); // idle timeout 2400 or 1800 d
    		port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 24 / 256 + 1);
    	}
    	
    	port->MCCR1 = port->MCCR0;
    	port->MCFGR0 = 0;
    	port->MFCR = LPI2C_MFCR_RXWATER(1) | LPI2C_MFCR_TXWATER(1);
    	port->MCR = LPI2C_MCR_MEN;
    }

    My sine generator code
    Code:
    #include <Wire.h>
    #include <Adafruit_MCP4725.h>
    #define MCP4725_ADDR 0x60
    //default SDA_PIN 19
    //default SCL_PIN 18
    #define TRIG_PIN 14
    #define LED_PIN 13 
    Adafruit_MCP4725 MCP4725;
    const word N=512; //number of samples per period
    word MCP4725_value=0;
    word sinvalues[N]; // lookup table
    const float pi=3.141592653589793;
    word icount = 0;
    
    
    void setup(void){
    pinMode(LED_PIN, OUTPUT);
    pinMode(TRIG_PIN, OUTPUT);
    digitalWrite(TRIG_PIN,LOW);
    Wire.begin();
    Wire.setClock(1000000); //at least faster than 1000000
    
    //just blinks LED
    digitalWrite(LED_PIN, HIGH);
    delay (200);
    digitalWrite(LED_PIN, LOW);
    
    for (word i = 0; i < N; i++){
          sinvalues[i]=floor(2048*(1-cos(i*2*pi/N)));  //my sine has to start from zero value to 4096 value (12-bit raneg) 
        }
    digitalWrite(LED_PIN, HIGH);
    delay (200);
    digitalWrite(LED_PIN, LOW);
    delay (200);
    //just blinks LED 2nd - looking od delay beetween LED blink I can realize how
    //long takes to generate the lookup tabel  
    }
    
    void loop(void)
    {
    //triger generator
    if (icount==0) {
    digitalWrite(TRIG_PIN,HIGH); 
    //delayMicroseconds(1);
    digitalWrite(TRIG_PIN,LOW);
    } 
     
     Wire.beginTransmission(MCP4725_ADDR);
      Wire.write(64);                     // cmd to update the DAC
      Wire.write(sinvalues[icount] >> 4);        // the 8 most significant bits...
      Wire.write((sinvalues[icount] & 15) << 4); // the 4 least significant bits...
      Wire.endTransmission();
      icount = (icount + 1) & N-1;
    
     
        }

    Can someone tell me if Teensy 4.0 is capable to operate with I2C high speed mode 3400 000 . I think about testing with 2kOhm pull-up resistors, but MCP4725 datasheet states that MCP4725 is capable to work with. I am quiet new in micro-controllers, but I2C was designed by Philips, Teensy 4.0 uses state off the art NXP chip, I would expect that this chip supports High speed mode.

  13. #13
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,332
    I know that @mjs513 has/had a Pull request (https://github.com/PaulStoffregen/Wire/pull/17) that allowed for faster I2C Speeds. Which later got pulled into my PR... Which I think is now totally obsolete and I have closed out that one.

  14. #14
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    21,473
    Currently Wire.setClock supports up to 1 MBit. To go faster, you'll need to edit the Wire library or write directly to the hardware registers.

    Months ago some work was done to configure higher speeds, including running the I2C hardware from a 60 MHz clock from the USB PLL, instead of directly from the 24 MHz oscillator. Maybe you can find that with the forum search? Or maybe Kurt or Defragster will see this and comment.

    However, that work (as far as I know) was merely to increase the SCL clock speed. I2C high speed mode is a special protocol, not just the same thing at faster clock rates. Teensy 4.0's I2C hardware can do HS mode. But there is currently no software support. For example, if you look at page 2780 in the reference manual (IMXRT1060RM_rev2.pdf), you'll see the MTDR register's CMD field takes special commands to initiate HS mode. So far, the Wire library has no support for using those special commands. It only uses the normal one (100b) for all data transfer. Maybe someday the library will be extended to support Wire.setClock(3400000) and selecting that speed will cause the rest of the library to use those special commands. But so far, all work that's been attempted has simply increased the clock speeds without doing this special protocol stuff that's supposed to be used for HS mode. (and that is the reason why I've been reluctant to merge those changes into the Wire lib)

    Of course, you'll also need hardware capable of such speeds. That means lower value pullup resistors and keeping the capacitance on the SDA & SCL lines as low as possible. I believe HS mode is supposed to be done with a special active current source circuit, rather than a passive resistor.

    But using the normal Wire library we publish today, the fastest supported speed is 1 Mbit/sec.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •