Skip to content

Commit

Permalink
mcu/stm32: Calculate I2C timing register
Browse files Browse the repository at this point in the history
STM32 F1,F4,L1 do have ClockSpeed filed in I2C initialization API,
so it easy to specify requested clock speed for I2C.
For all other devices ST HAL does not provide such field and
expect that TIMINGR register value is precomputed.
This value is specified in each BSP and is likely incorrect
for some of them.

This change introduces hic_speed to MCUs that has timing register and
function that computes timing register base on requested speed and system
clock.

If timing register is specified in settings it is still being used to
to configure I2C. If timing register setting is 0 hic_speed is used.

Signed-off-by: Jerzy Kasenberg <[email protected]>
  • Loading branch information
kasjer committed Dec 4, 2023
1 parent 5d679e6 commit c801bb6
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 1 deletion.
74 changes: 73 additions & 1 deletion hw/mcu/stm/stm32_common/src/hal_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,73 @@ i2c_reset(I2C_HandleTypeDef *hi2c)
}
#endif

#if !MYNEWT_VAL(STM32_HAL_I2C_HAS_CLOCKSPEED)
#ifndef __LL_I2C_CONVERT_TIMINGS
/* This is copy of macro from STM HAL for HALs that don't have it */
#define __LL_I2C_CONVERT_TIMINGS(__PRESCALER__, __SETUP_TIME__, __HOLD_TIME__, __SCLH_PERIOD__, __SCLL_PERIOD__) \
((((uint32_t)(__PRESCALER__) << I2C_TIMINGR_PRESC_Pos) & I2C_TIMINGR_PRESC) | \
(((uint32_t)(__SETUP_TIME__) << I2C_TIMINGR_SCLDEL_Pos) & I2C_TIMINGR_SCLDEL) | \
(((uint32_t)(__HOLD_TIME__) << I2C_TIMINGR_SDADEL_Pos) & I2C_TIMINGR_SDADEL) | \
(((uint32_t)(__SCLH_PERIOD__) << I2C_TIMINGR_SCLH_Pos) & I2C_TIMINGR_SCLH) | \
(((uint32_t)(__SCLL_PERIOD__) << I2C_TIMINGR_SCLL_Pos) & I2C_TIMINGR_SCLL))
#endif

static uint32_t
hal_i2c_timing(uint32_t i2c_speed, uint32_t clock)
{
uint32_t i2c_hold_time_min, i2c_setup_time_min;
uint32_t i2c_h_min_time, i2c_l_min_time;
uint32_t presc;
uint32_t timing = 0U;

/* Timings from I2S specification for standard/fast/fast+ */
if (i2c_speed < 400000) {
i2c_h_min_time = 4000U;
i2c_l_min_time = 4700U;
i2c_hold_time_min = 500U;
i2c_setup_time_min = 1250U;
} else if (i2c_speed < 1000000) {
i2c_h_min_time = 600U;
i2c_l_min_time = 1300U;
i2c_hold_time_min = 375U;
i2c_setup_time_min = 500U;
} else {
i2c_h_min_time = 260U;
i2c_l_min_time = 500U;
i2c_hold_time_min = 130U;
i2c_setup_time_min = 50U;
}
uint32_t clock_khz = clock / 1000;
presc = ((i2c_h_min_time * clock_khz / 1000) + 255999) / 256000;
uint32_t presc_max = ((i2c_l_min_time * clock_khz / 1000) + 255999) / 256000;
presc = max(presc, presc_max);
presc_max = ((i2c_hold_time_min * clock_khz / 1000) + 14999) / 15000;
presc = max(presc, presc_max);
presc_max = ((i2c_setup_time_min * clock_khz / 1000) + 15999) / 16000;
presc = max(presc, presc_max);

if (presc <= 16) {
uint32_t t_presc = clock / presc;
uint32_t ns_presc = 1000000000 / t_presc;
uint32_t sclh = (i2c_h_min_time + ns_presc - 1) / ns_presc;
uint32_t scll = (i2c_l_min_time + ns_presc - 1) / ns_presc;
uint32_t sdadel = (i2c_hold_time_min + ns_presc - 1) / ns_presc;
uint32_t scldel = (i2c_setup_time_min + ns_presc - 1) / ns_presc;

uint32_t scl_h_l = (t_presc / i2c_speed) - 5;
if (scl_h_l > sclh + scll) {
uint32_t scl_h_l_fill = scl_h_l - (sclh + scll);
scll += scl_h_l_fill / 2;
sclh += (scl_h_l_fill + 1) / 2;
}
timing = __LL_I2C_CONVERT_TIMINGS(presc - 1,
scldel - 1, sdadel, sclh - 1, scll - 1);
}

return timing;
}
#endif

int
hal_i2c_init(uint8_t i2c_num, void *usercfg)
{
Expand All @@ -116,7 +183,11 @@ hal_i2c_init(uint8_t i2c_num, void *usercfg)
init = &dev->hid_handle.Init;
dev->hid_handle.Instance = cfg->hic_i2c;
#if !MYNEWT_VAL(STM32_HAL_I2C_HAS_CLOCKSPEED)
init->Timing = cfg->hic_timingr;
if (cfg->hic_timingr) {
init->Timing = cfg->hic_timingr;
} else {
init->Timing = hal_i2c_timing(cfg->hic_speed, HAL_RCC_GetPCLK1Freq());
}
#else
init->ClockSpeed = cfg->hic_speed;
#endif
Expand Down Expand Up @@ -258,3 +329,4 @@ hal_i2c_master_probe(uint8_t i2c_num, uint8_t address, uint32_t timo)

return rc;
}

1 change: 1 addition & 0 deletions hw/mcu/stm/stm32f0xx/include/mcu/stm32f0xx_mynewt_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ struct stm32_hal_i2c_cfg {
uint8_t hic_pin_af;
uint8_t hic_10bit;
uint32_t hic_timingr; /* TIMINGR register */
uint32_t hic_speed; /* Requested speed (used when hic_timingr is 0) */
};

#ifdef __cplusplus
Expand Down
1 change: 1 addition & 0 deletions hw/mcu/stm/stm32f3xx/include/mcu/stm32f3xx_mynewt_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ struct stm32_hal_i2c_cfg {
uint8_t hic_pin_af;
uint8_t hic_10bit;
uint32_t hic_timingr; /* TIMINGR register */
uint32_t hic_speed; /* Requested speed (used when hic_timingr is 0) */
};

#ifdef __cplusplus
Expand Down
1 change: 1 addition & 0 deletions hw/mcu/stm/stm32f7xx/include/mcu/stm32f7xx_mynewt_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct stm32_hal_i2c_cfg {
uint8_t hic_pin_af;
uint8_t hic_10bit;
uint32_t hic_timingr; /* TIMINGR register */
uint32_t hic_speed; /* Requested speed (used when hic_timingr is 0) */
};

#ifdef __cplusplus
Expand Down
1 change: 1 addition & 0 deletions hw/mcu/stm/stm32h7xx/include/mcu/stm32h7xx_mynewt_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct stm32_hal_i2c_cfg {
uint8_t hic_pin_af;
uint8_t hic_10bit;
uint32_t hic_timingr; /* TIMINGR register */
uint32_t hic_speed; /* Requested speed (used when hic_timingr is 0) */
};

#ifdef __cplusplus
Expand Down
1 change: 1 addition & 0 deletions hw/mcu/stm/stm32l0xx/include/mcu/stm32l0xx_mynewt_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct stm32_hal_i2c_cfg {
uint8_t hic_pin_af;
uint8_t hic_10bit;
uint32_t hic_timingr; /* TIMINGR register */
uint32_t hic_speed; /* Requested speed (used when hic_timingr is 0) */
};

#ifdef __cplusplus
Expand Down
1 change: 1 addition & 0 deletions hw/mcu/stm/stm32l4xx/include/mcu/stm32l4xx_mynewt_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct stm32_hal_i2c_cfg {
uint8_t hic_pin_af;
uint8_t hic_10bit;
uint32_t hic_timingr; /* TIMINGR register */
uint32_t hic_speed; /* Requested speed (used when hic_timingr is 0) */
};

#ifdef __cplusplus
Expand Down
1 change: 1 addition & 0 deletions hw/mcu/stm/stm32u5xx/include/mcu/stm32u5xx_mynewt_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct stm32_hal_i2c_cfg {
uint8_t hic_pin_af;
uint8_t hic_10bit;
uint32_t hic_timingr; /* TIMINGR register */
uint32_t hic_speed; /* Requested speed (used when hic_timingr is 0) */
};

#ifdef __cplusplus
Expand Down
1 change: 1 addition & 0 deletions hw/mcu/stm/stm32wbxx/include/mcu/stm32wbxx_mynewt_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct stm32_hal_i2c_cfg {
uint8_t hic_pin_af;
uint8_t hic_10bit;
uint32_t hic_timingr; /* TIMINGR register */
uint32_t hic_speed; /* Requested speed (used when hic_timingr is 0) */
};

#ifdef __cplusplus
Expand Down

0 comments on commit c801bb6

Please sign in to comment.