i2c overclocking up to 1Mhz

victor_pv
Fri Oct 06, 2017 6:43 pm
Roger and everyone else.
Following on some discussion on another thread with racemaniac, I tested overclocking i2c1 up to 1.3Mhz successfully.

I was wondering if it would be of value adding the code to the core, perhaps limited down to 1Mhz.
This is OUT OF SPECS for the F1 i2c port. Official STM documents indicate up to 400Khz. But they also indicate SPI1 should run at no more than 18Mhz, and we routinely run it at 36Mhz and is allowed in the core, with most people probably not knowing 36Mhz is out of specs.

Should I submit the PR for this?


RogerClark
Fri Oct 06, 2017 10:02 pm
I thought there was a API call, that set the data rate ( though my memory is a bit hazy and I cant check easily as I’m not at my main computer at the moment)

So I dont see a problem with allowing people to set arbitary higher or lower data rates if the hardware supports it.


dannyf
Fri Oct 06, 2017 11:36 pm
You can set thebi2c clock divider however you want. The key question is: are there i2c slaves fast enough to respond to that.

If you need speed, spi is better, I think.


racemaniac
Sat Oct 07, 2017 5:52 am
[dannyf – Fri Oct 06, 2017 11:36 pm] –
You can set thebi2c clock divider however you want. The key question is: are there i2c slaves fast enough to respond to that.

obviously the answer is yes ^^
victor used it for i2c memory which can handle those speeds, i used it for a popular i2c dac that can handle those speeds :).
i was also wondering how currently the speed is set. ideally it would be something easy (an enum you can use to set the speed, containing certain often used values (100khz, 400khz, 800khz_overclock, 1mhz_overclock) or something like that). not messing with dividers.


Pito
Sat Oct 07, 2017 6:02 am
There are such I2C slaves, ie ADS11xx – up to 3.4MHz clock (and there are other chips sure).
They communicate 100/400kHz (Standard/Fast Speed) from init(), but the High-Speed mode (up to 3.4MHz) needs to be activated (a command sent to the master).
It seems you have to be able to change the clock on-the-fly.
Wikipedia:
. In 1992, Version 1 added 400 kHz Fast-mode (Fm) and a 10-bit addressing mode to increase capacity to 1008 nodes. This was the first standardized version.
. In 1998, Version 2 added 3.4 MHz High-speed mode (Hs) with power-saving requirements for electric voltage and current.
. In 2000, Version 2.1 clarified version 2, without significant functional changes.
. In 2007, Version 3 added 1 MHz Fast-mode plus (Fm+) (using 20 mA drivers), and a device ID mechanism.
. In 2012, Version 4 added 5 MHz Ultra Fast-mode (UFm) for new USDA (data) and USCL (clock) lines using push-pull logic without pull-up resistors, and added an assigned manufacturer ID table. It is only a unidirectional bus.

victor_pv
Sat Oct 07, 2017 2:50 pm
Dannyf the problem is the Wire class implementation does not allow you to pass any arbitrary speed and then calculates the right values for setting the i2c peripherals. It’s limited to 100Khz and 400Khz. So that’s what my change was about adding the capability in the class to use other speeds, so a normal user doesn’t have to go and calculate the register values and apply those values directly to the registers.
You can look at the code of SetClock() to see what I mean.

victor_pv
Sat Oct 07, 2017 2:53 pm
[racemaniac – Sat Oct 07, 2017 5:52 am] –
i was also wondering how currently the speed is set. ideally it would be something easy (an enum you can use to set the speed, containing certain often used values (100khz, 400khz, 800khz_overclock, 1mhz_overclock) or something like that). not messing with dividers.

The wire class uses SetClock(), but our implementation only allows 100 and 400Khz. Then setclock calls a libmaple function, can’t remember the name, and that one doesn’t calculate all the right registers for other than 100 and 400. I added a new function in libmaple to calculate them for any overclocking speed, and modified SetClock() to call the new function when the speed is over 400Khz. I could integrate it in the existing libmaple function, but did it a separate one just for testing.


dannyf
Sat Oct 07, 2017 4:34 pm
the problem is the Wire class implementation does not allow you to pass any arbitrary speed…

initialize the i2c module and then change the clock divider, like this:



void myI2C_init(int i2c_ps) {
I2C_init(); //stock i2c initialization
I2C_PS = i2c_ps; //set i2c clock divider based on parameter passed to myI2C_init() – figuratively speaking
}

so in the future, rather than calling I2C_init() to reset the module, use myI2C_init().


RogerClark
Sat Oct 07, 2017 9:14 pm
I think we should add this feature..

Im pretty sure I looked at the Due code and it supported other speeds – though just an expanded list of speed settings.
( I presumed perhaps the SAM architecture only had a liminted number of speeds which the hardware could operate at, because the API call accepts the frequency, and the code just finds the nearest available freq that the hardware supports. Either that or someone at Arduino just decided on those arbitary steps)


dannyf
Sat Oct 07, 2017 9:41 pm
it is CWGR register, per the datasheet.

TWI_CWGR is only used in Master mode.
• CLDIV: Clock Low Divider
The SCL low period is defined as follows:
• CHDIV: Clock High Divider
The SCL high period is defined as follows:
• CKDIV: Clock Divider
The CKDIV is used to increase both SCL high and low periods

The formula are given as well.

you may also look into what sets Tmck.


victor_pv
Sun Oct 08, 2017 5:16 am
Dannyf sorry for any confusion. I know how to overclock the port, I was asking if other people considers that it would be helpful if I send my core in as PR to be incorporated in the core.

victor_pv
Sun Oct 08, 2017 5:28 am
[RogerClark – Sat Oct 07, 2017 9:14 pm] –
I think we should add this feature..

Im pretty sure I looked at the Due code and it supported other speeds – though just an expanded list of speed settings.
( I presumed perhaps the SAM architecture only had a liminted number of speeds which the hardware could operate at, because the API call accepts the frequency, and the code just finds the nearest available freq that the hardware supports. Either that or someone at Arduino just decided on those arbitary steps)

The arduino API documents several other speeds (1Mhz, 3.4Mhz) but mentions that the user needs to check what’s supported in his hardware.
From my tests with FRAM I was able to realibly get up to 1.3Mhz, and Racemaniac was able to get around the same speed before having problems.
The peripheral can work at many different speeds with the right settings, but I understand the standard i2c speeds are only a few.
So we can take both paths, set only the standard speeds with an enum (100Khz, 400Khz, 1Mhz), or just allow any speed within the range. I wrote my code in the second form since I wanted to test how fast it could go beyong 400Khz.
For reference the DUE allows any speed and just calculates the register values that match the best, but imposes to limit:
https://github.com/arduino/ArduinoCore- … e.cpp#L140
https://github.com/arduino/ArduinoCore- … twi.c#L114


stevestrong
Sun Oct 08, 2017 8:43 am
Maybe these predefined values would make sense:
100kHz
400kHz
1MHz
MAX_FREQUENCY

dannyf
Sun Oct 08, 2017 11:34 am
Set the i2c frequency based on F_I2C, which can be mapped to any number the user wishes to, including a few pre-defined macros, like F_I2C_100Khz, F_I2C_400Khz, ….

best of both worlds.


Leave a Reply

Your email address will not be published. Required fields are marked *