I'd like to share my code for Portenta H7, my experiences and hints... for using I2C on Portenta H7.
My I2C code files:
I2C.h (624 Bytes)
I2C.cpp (7.0 KB)
Overview
The Portenta H7 has THREE (3) I2C busses available. As:
Wire //like "Wire0", as I2C0 on breakout and I2C on Digital Header, but as I2C3 on MCU itself
//this is the "User I2C"
Wire 1 //as I2C1 on breakout - BUT internal board I2C, e.g. for PMIC chip onboard!
//this is "SYS I2C", used also for onboard components!
Wire 2 //as I2C2 on breakout board
//a second "User I2C"
are available in Arduino LIB.
Requirements
-
I need 4.2 MHz for I2C (testing an external chip for this speed)
-
Even I know it is "outside the spec." - it works (I use it on other STM32H7 boards)
-
How can I configure the I2C clock speed?
(I could not find API code to set the I2C clock speed in Arduino. It seems to be always 100 KHz)
So, I have added code to go to I2C Peripheral registers in MCU directly (via STM32 HAL approach). It works, but it has some strange "issues".
Other remark:
Pull-Ups
The internal I2C bus (used for PMIC and other chips, as Wire1) has 3V3 pull-ups. But the other Wire and Wire2 do not have any pull-ups (not on MCU module, as Wire, neither on breakout board, as Wire and Wire2).
Be careful with Wire1
The Wire1 is the I2C also used to configure onboard chips. I have used to test it (e.g. PMIC slave address 0x8 and read register 0x0 - see 0x7C as chip ID coming back).
But if you fire I2C transactions and they would end up in writing to PMIC chip - you would brick your board!
My Clock Configuration
In general, it works: I can trim the I2C clock speed, configure 4 different values (100, 400 KHz, 1 MHz, 4 MHz). I use the I2C Peripheral config value (register I2C_TIMINGR) directly.
But the behavior is a bit strange: everything faster as 1 MHz seems to interfere with Arduino LIB for this board.
BTW: the default of I2C clock seems to be approx. 100 KHz (113 KHz measured).
Master Code and a "smart LIB"?
Strange is this:
-
I configure 1 MHz or even my 4 MHz - OK
-
But when I do my I2C transaction (for testing using PMIC with slave address 0x8 - see more on "Testing"):
For a register read via I2C I see: the first part with slave address comes with 1 MHz (always, independent of my config). Just the second part with writing register address, reading the register value is done with my configured frequency (now with 4 MHz). -
When I try the same with another slave address (not the 0x8 for PMIC, with a not-existing slave address, e.g. 0x20) - it generates the I2C as 100 KHz (always and all part of transaction). Why?
So, I am thinking this:
Everything faster as 1 MHz (maybe already with 1 MHz) needs actually a "Master Code". This tells the slave that the clock will be changed. It is correct to "require" a Master Code sent for all speeds faster as 1 MHz (and maybe included).
This 0x8 for PMIC slave is already a "Master Code": Master code is 0x0000_1XXX!
So, I guess: the LIB sees based on very first byte to send (this 0x8 as slave address for PMIC) - it is a Master Code. And LIB functions change to do as: send Master Code (MC) just with 1 MHz max., afterwards change speed and do rest of transaction with this faster speed configured.
("smart").
It works and I see it this way on I2C signals. And using another I2C slave address, which does not look like a Master Code - works completely differently (falls back to 100 KHz for all parts of transaction, does not let me change to my I2C clock speed).
So far OK, just a bit strange what the involved LIB functions would do (in terms of Master Code seen, clock config etc.). If my transaction has failed (e.g. due to wrong slave address) - all my clock config is gone (I had to call my function again).
Testing
I am lazy. I do not want to grab an external I2C device, connect and try. Instead: there are some I2C devices on the MCU module - why not using those ones?
With the breakout board (I use for ETH): use Wire (which is I2C0 on breakout) and bridge from I2C1 on breakout board to I2C0).
This connects my "User I2C" with the internal I2C bus (where the PMIC chip is), the "SYS I2C".
(to access onboard chips and having the pull-ups, 1K8).
Send a register read command to the PMIC:
slave address is 0x8, try to read register 0x0 (the ChipID). It should give you 0x7C.
But do not try to write to it: do NOT KILL the PMIC config and board power!
Interesting: it works even with 4 MHz to read this PMIC register. Cool.
Conclusion
-
Without to study the schematics and some trials to figure out - you would not know which I2C is which (naming is confusing):
the silk screen tells you I2C0 (SDA0, SCL0) which is Wire, like "Wire0") but other I2C numberings are very strange (I2C0 = Wire - is actually I2C3 on MCU, Wire1 is actually I2C0, internal on MCU module...) -
If you want to change the I2C clock speed - no idea how (hard to find an API function to do so).
-
You can configure any I2C clock speed, but the I2C functions used later might interfere, e.g. to realize the first is a Master Code, and they change the clock speed again (in a pretty correct way, but strange to see LIB code changes my config).
It would be great to get a more detailed insight to the API code (a full Open Source support would be nice), or at least a better documentation, e.g. how to change I2C clock speed, how to deal with faster speeds, how to deal with a Master Code, is the code capable to deal with a Master Code etc.
Have fun with it.
(it works in an acceptable way for me, even for 4 MHz, and potentially the max. is 5 MHz I2C)