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);
}
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
Hi, I don't think it's that simple for a Teensy 4.... see picture
about 100KHz
Best,
Johan
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.The maximum I can get is 375 KHz, instead of 1MHz.......
// 2 MHz
port->MCCR0 = LPI2C_MCCR0_CLKHI(3) | LPI2C_MCCR0_CLKLO(4) |
LPI2C_MCCR0_DATAVD(2) | LPI2C_MCCR0_SETHOLD(3);
port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(1) | LPI2C_MCFGR2_FILTSCL(1) |
LPI2C_MCFGR2_BUSIDLE(1200); // idle timeout 50 us
port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 24 / 256 + 1);
// 2 MHz
port->MCCR0 = LPI2C_MCCR0_CLKHI(3) | LPI2C_MCCR0_CLKLO(4) |
LPI2C_MCCR0_DATAVD(2) | LPI2C_MCCR0_SETHOLD(3);
port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(1) | LPI2C_MCFGR2_FILTSCL(1) |
LPI2C_MCFGR2_BUSIDLE(1200); // idle timeout 50 us
port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 24 / 256 + 1);
This looks like it keeps the same base clock (24 MHz), right?
I would like to see this become 3 fixed cases for the 3 official I2C speeds, and "else" code that tries to compute values for those registers for any unofficial speed. But maybe adding more fixed cases for now won't hurt too much, just slightly larger code size?
A concern I have is long-term API planning for the Wire library to someday support I2C high speed mode. HS mode implies a different protocol, where a start byte is transmitted at 400 kHz. Then the speed switches to much faster (up to 3.4 Mbit according to the spec) with some changes to the protocol, mostly electrical and when clock stretching is allowed. If we start supporting faster than 1 Mbit speed now (the highest the I2C standard says is allowed for the normal protocol), then in the future we can't use only setClock() to activate this special high speed mode.
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.
I have been able to push SSD1306 OLEDs to run at 1-2Mhz on other MCUs, so I wanted to see how far I could get with the Teensy 4. I did some experiments with the clock values and was able to get what appears to be a stable 2MHz. Here's the code change to WireIMXRT.cpp:
Code:// 2 MHz port->MCCR0 = LPI2C_MCCR0_CLKHI(3) | LPI2C_MCCR0_CLKLO(4) | LPI2C_MCCR0_DATAVD(2) | LPI2C_MCCR0_SETHOLD(3); port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0); port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(1) | LPI2C_MCFGR2_FILTSCL(1) | LPI2C_MCFGR2_BUSIDLE(1200); // idle timeout 50 us port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 24 / 256 + 1);
Here's a video of it in action: