From a74e4fabeb0d7bc4dc68284047248fa173f5237b Mon Sep 17 00:00:00 2001 From: yn386 Date: Mon, 19 Sep 2022 10:14:27 +0900 Subject: [PATCH] stm32/i2c: Fix I2C frequency calc so it doesn't exceed requested rate. Prior to this commit, the actual I2C frequency can be faster than specified one and it may exceed the I2C's specification for Fast Mode. The frequency of SCL should be less than or equal to 400KHz in Fast Mode. This commit fixes this issue for F4 MCUs by rounding up the division in the frequency calculation. --- docs/library/machine.I2C.rst | 4 ++++ docs/library/pyb.I2C.rst | 4 ++++ ports/stm32/i2c.c | 5 +++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/library/machine.I2C.rst b/docs/library/machine.I2C.rst index 2a33b1da47..0eb1b67f58 100644 --- a/docs/library/machine.I2C.rst +++ b/docs/library/machine.I2C.rst @@ -91,6 +91,10 @@ General Methods - *sda* is a pin object for the SDA line - *freq* is the SCL clock rate + In the case of hardware I2C the actual clock frequency may be lower than the + requested frequency. This is dependant on the platform hardware. The actual + rate may be determined by printing the I2C object. + .. method:: I2C.deinit() Turn off the I2C bus. diff --git a/docs/library/pyb.I2C.rst b/docs/library/pyb.I2C.rst index 24b9cb8c3a..2c526854aa 100644 --- a/docs/library/pyb.I2C.rst +++ b/docs/library/pyb.I2C.rst @@ -96,6 +96,10 @@ Methods that DMA transfers have more precise timing but currently do not handle bus errors properly) + The actual clock frequency may be lower than the requested frequency. + This is dependant on the platform hardware. The actual rate may be determined + by printing the I2C object. + .. method:: I2C.is_ready(addr) Check if an I2C device responds to the given address. Only valid when in controller mode. diff --git a/ports/stm32/i2c.c b/ports/stm32/i2c.c index c63fe21624..a05a0954af 100644 --- a/ports/stm32/i2c.c +++ b/ports/stm32/i2c.c @@ -70,10 +70,11 @@ int i2c_init(i2c_t *i2c, mp_hal_pin_obj_t scl, mp_hal_pin_obj_t sda, uint32_t fr // SM: MAX(4, PCLK1 / (F * 2)) // FM, 16:9 duty: 0xc000 | MAX(1, (PCLK1 / (F * (16 + 9)))) + // (the PCLK1-1 and +1 at the end is to round the division up) if (freq <= 100000) { - i2c->CCR = MAX(4, PCLK1 / (freq * 2)); + i2c->CCR = MAX(4, ((PCLK1 - 1) / (freq * 2) + 1)); } else { - i2c->CCR = 0xc000 | MAX(1, PCLK1 / (freq * 25)); + i2c->CCR = 0xc000 | MAX(1, ((PCLK1 - 1) / (freq * 25) + 1)); } // SM: 1000ns / (1/PCLK1) + 1 = PCLK1 * 1e-6 + 1