stm32/i2c: Support setting the I2C TIMINGR value via keyword arg.

On MCUs that have an I2C TIMINGR register, this can now be explicitly set
via the "timingr" keyword argument to the I2C constructor, for both
machine.I2C and pyb.I2C.  This allows to configure precise timing values
when the defaults are inadequate.
pull/1/head
Damien George 2019-05-21 13:45:17 +10:00
parent ddc657658a
commit c769da1aaa
2 changed files with 43 additions and 3 deletions

View File

@ -187,15 +187,24 @@ STATIC void machine_hard_i2c_init(machine_hard_i2c_obj_t *self, uint32_t freq, u
/******************************************************************************/
/* MicroPython bindings for machine API */
#if defined(STM32F0) || defined(STM32F7)
#define MACHINE_I2C_TIMINGR (1)
#else
#define MACHINE_I2C_TIMINGR (0)
#endif
mp_obj_t machine_hard_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
// parse args
enum { ARG_id, ARG_scl, ARG_sda, ARG_freq, ARG_timeout };
enum { ARG_id, ARG_scl, ARG_sda, ARG_freq, ARG_timeout, ARG_timingr };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 400000} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2C_POLL_DEFAULT_TIMEOUT_US} },
#if MACHINE_I2C_TIMINGR
{ MP_QSTR_timingr, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
#endif
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
@ -241,6 +250,13 @@ mp_obj_t machine_hard_i2c_make_new(const mp_obj_type_t *type, size_t n_args, siz
// initialise the I2C peripheral
machine_hard_i2c_init(self, args[ARG_freq].u_int, args[ARG_timeout].u_int);
#if MACHINE_I2C_TIMINGR
// If given, explicitly set the TIMINGR value
if (args[ARG_timingr].u_obj != mp_const_none) {
self->i2c->TIMINGR = mp_obj_get_int_truncated(args[ARG_timingr].u_obj);
}
#endif
return MP_OBJ_FROM_PTR(self);
}

View File

@ -135,6 +135,8 @@ const pyb_i2c_obj_t pyb_i2c_obj[] = {
// The STM32F0, F3, F7, H7 and L4 use a TIMINGR register rather than ClockSpeed and
// DutyCycle.
#define PYB_I2C_TIMINGR (1)
#if defined(STM32F746xx)
// The value 0x40912732 was obtained from the DISCOVERY_I2Cx_TIMING constant
@ -213,6 +215,8 @@ uint32_t pyb_i2c_get_baudrate(I2C_HandleTypeDef *i2c) {
#else
#define PYB_I2C_TIMINGR (0)
#define MICROPY_HW_I2C_BAUDRATE_DEFAULT (PYB_I2C_SPEED_FULL)
#define MICROPY_HW_I2C_BAUDRATE_MAX (PYB_I2C_SPEED_FULL)
@ -564,7 +568,15 @@ STATIC void pyb_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki
mp_printf(print, "I2C(%u)", i2c_num);
} else {
if (in_master_mode(self)) {
mp_printf(print, "I2C(%u, I2C.MASTER, baudrate=%u)", i2c_num, pyb_i2c_get_baudrate(self->i2c));
mp_printf(print, "I2C(%u, I2C.MASTER, baudrate=%u"
#if PYB_I2C_TIMINGR
", timingr=0x%08x"
#endif
")", i2c_num, pyb_i2c_get_baudrate(self->i2c)
#if PYB_I2C_TIMINGR
, self->i2c->Init.Timing
#endif
);
} else {
mp_printf(print, "I2C(%u, I2C.SLAVE, addr=0x%02x)", i2c_num, (self->i2c->Instance->OAR1 >> 1) & 0x7f);
}
@ -586,6 +598,9 @@ STATIC mp_obj_t pyb_i2c_init_helper(const pyb_i2c_obj_t *self, size_t n_args, co
{ MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MICROPY_HW_I2C_BAUDRATE_DEFAULT} },
{ MP_QSTR_gencall, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_dma, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
#if PYB_I2C_TIMINGR
{ MP_QSTR_timingr, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
#endif
};
// parse args
@ -602,7 +617,16 @@ STATIC mp_obj_t pyb_i2c_init_helper(const pyb_i2c_obj_t *self, size_t n_args, co
init->OwnAddress1 = (args[1].u_int << 1) & 0xfe;
}
i2c_set_baudrate(init, MIN(args[2].u_int, MICROPY_HW_I2C_BAUDRATE_MAX));
// Set baudrate or timing value (if supported)
#if PYB_I2C_TIMINGR
if (args[5].u_obj != mp_const_none) {
init->Timing = mp_obj_get_int_truncated(args[5].u_obj);
} else
#endif
{
i2c_set_baudrate(init, MIN(args[2].u_int, MICROPY_HW_I2C_BAUDRATE_MAX));
}
init->AddressingMode = I2C_ADDRESSINGMODE_7BIT;
init->DualAddressMode = I2C_DUALADDRESS_DISABLED;
init->GeneralCallMode = args[3].u_bool ? I2C_GENERALCALL_ENABLED : I2C_GENERALCALL_DISABLED;