New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
stm32/i2c: Use timeout passed through from machine.I2C() if set, else existing default of 50ms. #4711
Conversation
ports/stm32/i2c.c
Outdated
@@ -30,11 +30,11 @@ | |||
|
|||
#if MICROPY_HW_ENABLE_HW_I2C | |||
|
|||
#define I2C_POLL_TIMEOUT_MS (50) | |||
#define I2C_POLL_TIMEOUT_MS(t) ((t)?t:50) // Use provided timeout if set, or 50ms by default |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be more efficient to do this "default check" when timeout is provided, rather than each time it's used. So I'd suggest to remove this macro completely and just use the timeout variable as-is in the functions below.
ports/stm32/i2c.c
Outdated
|
||
#if defined(STM32F4) | ||
|
||
int i2c_init(i2c_t *i2c, mp_hal_pin_obj_t scl, mp_hal_pin_obj_t sda, uint32_t freq) { | ||
int i2c_init(i2c_t *i2c, mp_hal_pin_obj_t scl, mp_hal_pin_obj_t sda, uint32_t freq, uint32_t timeout) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this was added in error.
ports/stm32/i2c.c
Outdated
uint32_t t0 = HAL_GetTick(); | ||
while (!(i2c->SR1 & mask)) { | ||
if (HAL_GetTick() - t0 >= I2C_POLL_TIMEOUT_MS) { | ||
if (HAL_GetTick() - t0 >= I2C_POLL_TIMEOUT_MS(timeout)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned above, this can just be HAL_GetTick() - t0 >= timeout
ports/stm32/machine_i2c.c
Outdated
@@ -44,28 +44,29 @@ typedef struct _machine_hard_i2c_obj_t { | |||
i2c_t *i2c; | |||
mp_hal_pin_obj_t scl; | |||
mp_hal_pin_obj_t sda; | |||
uint32_t timeout; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't work because the objects below are defined "const" (to not use RAM, they live in ROM). Best way to deal with it is to have a separate non-const array to hold just the timeouts (the mutable data).
And then have this as a pointer to an entry in that array: uint32_t *timeout
.
ports/stm32/machine_i2c.c
Outdated
@@ -44,28 +44,29 @@ typedef struct _machine_hard_i2c_obj_t { | |||
i2c_t *i2c; | |||
mp_hal_pin_obj_t scl; | |||
mp_hal_pin_obj_t sda; | |||
uint32_t timeout; | |||
} machine_hard_i2c_obj_t; | |||
|
|||
STATIC const machine_hard_i2c_obj_t machine_hard_i2c_obj[] = { | |||
#if defined(MICROPY_HW_I2C1_SCL) | |||
{{&machine_hard_i2c_type}, I2C1, MICROPY_HW_I2C1_SCL, MICROPY_HW_I2C1_SDA}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would become: {{&machine_hard_i2c_type}, I2C1, MICROPY_HW_I2C1_SCL, MICROPY_HW_I2C1_SDA, &timeout_array[0]}
ports/stm32/machine_i2c.c
Outdated
@@ -105,18 +106,18 @@ STATIC void machine_hard_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp | |||
} | |||
|
|||
void machine_hard_i2c_init(machine_hard_i2c_obj_t *self, uint32_t freq, uint32_t timeout) { | |||
(void)timeout; | |||
self->timeout = timeout; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would become: *self->timeout = timeout
Ports that use software I2C will have this timeout feature implemented, so I think it's a good idea to add it here (for HW I2C) in the same way. But note that the units of the timeout are microseconds for the software I2C, so they should also be microseconds here for consistency. Probably simplest way to do this is just divide the input timeout by 1000 to get milliseconds. (Note: it'd be a separate discussion if the |
Thanks, I really don't know what I was thinking sprinkling that macro through everything, really messy and inefficient. The default timeout for the arg in |
Good question... the timeout is really only there to prevent lock up in the case something goes wrong on the bus (a slave holds it low forever). If the bus is functioning properly (which is most of the time) then the timeout can be arbitrarily long. So maybe it's best to keep it 50ms to support slaves which stretch it for a while. And so just make it 50,000us in |
cb45f3d
to
1d4fc04
Compare
1d4fc04
to
1586810
Compare
I think everything should be resolved and good to go now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Can you please also fix accel.c (eg build for PYBV10). Just add a macro like at the top of accel.c:
#define I2C_TIMEOUT_MS (50)
then use it.
#endif | ||
}; | ||
|
||
STATIC uint32_t machine_hard_i2c_timeout[sizeof(machine_hard_i2c_obj)/sizeof(machine_hard_i2c_obj_t)] = {I2C_POLL_TIMEOUT_US_DEFAULT}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure all compilers will like this use of sizeof()
here... is it legal? Note there's the macro MP_ARRAY_SIZE()
for this.
Also, I don't think it's necessary to initialise the value, it can just stay "blank", ie in the BSS. The timeout should always be populated upon creation of an I2C object. (And being in the BSS is slightly more optimal, no code space taken, initing it at boot is faster.)
i2c_init(self->i2c, self->scl, self->sda, freq); | ||
if (self->us_timeout != NULL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's necessary to test for NULL, if the I2C is created it should exist and have a valid pointer (and there's no corresponding test in the read from this value below, and it's not needed there).
@andrewleech I'm happy to make the 3 fixes/changes mentioned above, just let me know either way. |
Oh sorry I missed your last set of reviews. That would be great if you wouldn't mind cleaning them up, I'll keep trying to get confirmation on the sd card issue tomorrow. |
Ok, this was reworked and merged in ed2b6ea (it now stores the configurable timeout in a small array in i2c.c itself, which simplifies it a lot). |
…n-main Translations update from Weblate
This PR ensure stm32 hardware i2c implementation honors the existing
timeout
argument onmachine.I2C
init commands.Previously the stm32 i2c timeout is hard coded to 50ms which isn't guaranteed to be enough depending on the clock stretching specs of the I2C device(s) in use.
Ah, I now see timeout is an undocumented arg, not described on: https://docs.micropython.org/en/latest/library/machine.I2C.html
I'm not sure if it's implemented for any other ports at this stage.