Skip to main content
User16669512935851271792
Associate III
December 2, 2022
Question

How to calculate the I2C Timing Register value.

  • December 2, 2022
  • 8 replies
  • 20464 views

HI Team,

We are using the STM32WB controller for our project, in that we are using the I2C1 for to communicate with Eeprom Module.

Can anyone explain how to configure/Calculate the Exact Timing Register value. If we have any sample calculator for different I2C Speeds along with other register settings it is very helpful to us.

Currently My Register value:  hi2c1.hi2c1.Init. Timing = 0x00000E14, for which I2C speed it is calculated we are not understanding.

Regards,

Srinivas.V

8 replies

Andrei Chichak
Lead
December 2, 2022

Why would you just not use CubeMX to give you the setup code? You just type in the I2C speed that you want and it gives you the timing value.

Is learning the minutiae of poking timing registers the important bit (is this to pass some course) or is getting your device running the important thing?

Either way, read the I1C section of the reference manual.

Tesla DeLorean
Guru
December 2, 2022

Well it does cause a lot of people not knowing how anything actually works.

Perhaps one could take the Reference Manual, and unpack the register, and see how it divides the APB clock, and modulates the waveform.

If you're dealing with a lot of I2C peripherals, assorted STM32, and perhaps want to do some adaptive clock-gearing, being able to change it on the fly becomes more important.

This gets to be more important if people just randomly cut-n-paste things between projects or families of parts, as they are context sensitive, and often dependent on them even being commented correctly.

ST should really provide algorithms to pack and unpack these "constants" so the process is less opaque.

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
Piranha
Principal III
December 4, 2022

Proper calculation of the I2C timings is not just a single formula, but is an algorithm. If the HAL developers would be competent, they would implement this algorithm in the HAL initialization/configuration functions. After all HAL is meant to be a high level Hardware Abstraction Layer!

User16669512935851271792
Associate III
December 5, 2022

Hi Piranha,

Thanks for your reply.

Can you just explain how the value is calculated in example project, "hi2c1.hi2c1.Init. Timing 0x00000E14". we are trying to recalculate the same but not able to understand and same value is not getting with any of the settings which are in STMCubeMx Tool.

Could you please explain and corelate the example project value with STMCubeMx Tool, how it is generated, based on that we will generate the values for our project with Different I2C speeds.

We have downloaded the one excel exe which is for Timing Regester value generation, with that one also we are not able to corelate the Example project Register value.

Example project Path:

ST\STM32CubeWB-master\Projects\P-NUCLEO-WB55.Nucleo\Examples\I2C\I2C_TwoBoards_ComDMA

Excel sheet Name:

I2C_Timing_Configuration_V1.0.1

Regards,

Srinivas

Foued_KH
ST Employee
December 8, 2022

Hello @User16669512935851271792​,

I suggest you look to the Reference manual (RM0478) in Sections for more details:

  • I2C timings
  • 26.4.10 I2C_TIMINGR register configuration examples
  • 26.7.5 Timing register (I2C_TIMINGR)
  • 26.7.6 Timeout register (I2C_TIMEOUTR)

You find formulas to understand as a reference.

Multiprotocol wireless 32-bit MCU Arm<Sup>®</Sup>-based Cortex<Sup>®</Sup>-M4 with FPU, Bluetooth<Sup>®</Sup> 5.3 radio solution - Reference manual (st.com)

Foued

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
User16669512935851271792
Associate III
December 8, 2022

Hi Khalsi,

Thanks for your reply.

I already overlooked into this document and crosschecked the registers, but still i am unable to simulate the TIMING REGISTER Value.

We have referred the Example code and they are using the value hi2c1.hi2c1.Init. Timing = 0x00000E14, we also reusing the same example code and only change is we need to run in Higher speeds like 400Khz and 1 Mz. For this we need to update the Timing Reg value, and rest of the settings are same. For that only we are asking how this value is generated and what are the values they have used for Rise and Fall Time values. If we know these values, we will generate the reg Value for different speeds.

Just explain the Example project Timing Reg value, that is sufficient for us.

Regards,

Srinivas.V

Foued_KH
ST Employee
December 8, 2022

Hello,

For the Timing register value : 0x00000E14, the Rise Time = 0 ns and the Fall time = 0 ns too,

For example : for Fast mode (400KHz) :

0693W00000WKZDSQA5.pngthis timing register value is generated automatically by the STM32CubeMX.

Foued

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
User16669512935851271792
Associate III
December 8, 2022

Thanks for your reply.

In your image The Timing Reg value is showing 0x00004, But the value is example project is 0x00000E14.

Please mention the I2C Clock source Frequency, Analog Filter Delay, Coefficient of Digital Filter and Rise and Fall times.

we are using the excel sheet for generating the Timing Register value. Attached the same in reply.

Regards,

Srinivas.V

Foued_KH
ST Employee
December 8, 2022

Could you please share with me your inputs.

Foued

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
Foued_KH
ST Employee
December 8, 2022

The timing register value is 0x00000004 generated automatically by the STM32CubeMX for :

  • I2C Clock Source Frequency : 4MHz

0693W00000WKZQ7QAP.png

  • I2C Speed Frequency : 400 KHz
  • Rise Time : 0 ns
  • Fall Time : 0 ns

Foued

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
User16669512935851271792
Associate III
December 8, 2022

Hi Khalsi,

Thanks for your reply. Here with I am sharing my inputs.

Please suggest me the correct settings.

hi2c1.Instance = I2C1;

 hi2c1.Init.Timing = 0x00000E14; //100Khz //

 hi2c1.Init.OwnAddress1 = I2C_ADDRESS;

 hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;

 hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;

 hi2c1.Init.OwnAddress2 = 0;

 hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;

 hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;

 hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;

HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE);

HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0x0)

HAL_I2CEx_EnableFastModePlus(I2C_FASTMODEPLUS_I2C1);

/*##-1- Configure the I2C clock source. The clock is derived from the SYSCLK #*/

  RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2C1;

  RCC_PeriphCLKInitStruct.I2c1ClockSelection = RCC_I2C1CLKSOURCE_SYSCLK;

//GPIO Configuration

GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;

GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;

GPIO_InitStruct.Pull = GPIO_PULLUP;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//GPIO_SPEED_FREQ_LOW,GPIO_SPEED_FREQ_HIGH,GPIO_SPEED_FREQ_MEDIUM,GPIO_SPEED_FREQ_VERY_HIGH;

GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;

I have added source file also for your reference in attachments.

Please let me know if any more inputs are required from my side.

Foued_KH
ST Employee
December 9, 2022

Hello,

The "Excel spreadsheet I2C_Timing_Configuration_V1.0.1" is only for the STM32F3 and STM32F0

(I2C timing configuration tool for STM32F3xx and STM32F0xx microcontrollers)

I recommend you to use the STM32CubeMX for getting Higher speeds like 400Khz and 1 MHz, it helps you to configure/Calculate the Exact Timing Register value.

Foued.

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
User16669512935851271792
Associate III
December 9, 2022

Hi Khalsi,

Once again thanks for your reply and support. In STM32CubeMX Some of the fields are not able to calculate like Rise & Fall times, and what is the impact of these two variables also we do not know and currently we keep it as zero's only.

If you have any other I2C example code integrated with specific application is useful to us.

Regards,

Srinivas.V

Foued_KH
ST Employee
December 9, 2022

Hello,

You can check the I2C examples from :

\Repository\STM32Cube_FW_WB_V1.15.0RC2\Projects\P-NUCLEO-WB55.Nucleo\Examples\I2C

Download the STM32Cube MCU Package for STM32WB series :

STM32CubeWB - STM32Cube MCU Package for STM32WB series (HAL, Low-Layer APIs and CMSIS, USB, File system, RTOS, BLE, Thread and Zigbee stacks - and examples running on ST boards) - STMicroelectronics

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
Foued_KH
ST Employee
December 9, 2022

Example description:

-I2C pins

  • SCL Pin: PB8 (CN10, pin3)
  • SDA Pin: PB9 (CN10, pin5)

-I2C Clock Source Frequency : 32 MHz

0693W00000WKdxPQAT.png-I2C Speed Frequency : 1MHz

-Rise Time : 0 ns

-Fall Time : 0 ns

0693W00000WKdv4QAD.png 

  • TIMING REGISTER = 0x00100413

Code:

/**
 * @brief System Clock Configuration
 * @retval None
 */
void SystemClock_Config(void)
{
 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 
 /** Configure the main internal regulator output voltage
 */
 __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
 
 /** Initializes the RCC Oscillators according to the specified parameters
 * in the RCC_OscInitTypeDef structure.
 */
 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
 RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
 RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
 RCC_OscInitStruct.PLL.PLLN = 8;
 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
 RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
 RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
 {
 Error_Handler();
 }
 
 /** Configure the SYSCLKSource, HCLK, PCLK1 and PCLK2 clocks dividers
 */
 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK4|RCC_CLOCKTYPE_HCLK2
 |RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 RCC_ClkInitStruct.AHBCLK2Divider = RCC_SYSCLK_DIV2;
 RCC_ClkInitStruct.AHBCLK4Divider = RCC_SYSCLK_DIV1;
 
 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
 {
 Error_Handler();
 }
}
 
/**
 * @brief Peripherals Common Clock Configuration
 * @retval None
 */
void PeriphCommonClock_Config(void)
{
 RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
 
 /** Initializes the peripherals clock
 */
 PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SMPS;
 PeriphClkInitStruct.SmpsClockSelection = RCC_SMPSCLKSOURCE_HSI;
 PeriphClkInitStruct.SmpsDivSelection = RCC_SMPSCLKDIV_RANGE1;
 
 if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN Smps */
 
 /* USER CODE END Smps */
}
 
/**
 * @brief I2C1 Initialization Function
 * @param None
 * @retval None
 */
static void MX_I2C1_Init(void)
{
 
 /* USER CODE BEGIN I2C1_Init 0 */
 
 /* USER CODE END I2C1_Init 0 */
 
 /* USER CODE BEGIN I2C1_Init 1 */
 
 /* USER CODE END I2C1_Init 1 */
 hi2c1.Instance = I2C1;
 hi2c1.Init.Timing = 0x00100413;
 hi2c1.Init.OwnAddress1 = 0;
 hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
 hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
 hi2c1.Init.OwnAddress2 = 0;
 hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
 hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
 hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
 if (HAL_I2C_Init(&hi2c1) != HAL_OK)
 {
 Error_Handler();
 }
 
 /** Configure Analogue filter
 */
 if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
 {
 Error_Handler();
 }
 
 /** Configure Digital filter
 */
 if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
 {
 Error_Handler();
 }
 
 /** I2C Enable Fast Mode Plus
 */
 HAL_I2CEx_EnableFastModePlus(I2C_FASTMODEPLUS_I2C1);
 /* USER CODE BEGIN I2C1_Init 2 */
 
 /* USER CODE END I2C1_Init 2 */
 
}

 Foued

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
User16669512935851271792
Associate III
December 9, 2022

Hi Khalsi,

Once again thanks for your support and sharing the source code to us.

Almost code is the same except the Clock Source settings, in your code RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK.

our code,

RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE.

but the Input Clock to I2C1 is 32 MHz Only, Mx settings also same. We will use Same Timing Reg value for our as well.

Similar way can you have any code base for Flash operations initializations and Read, Write and Erase as well. We will use the same for our purpose.

Regards,

Srinivas.V

Associate
November 4, 2024

I know it's a bit late, but I use the following algorithm for stm32g030.
It might be useful to someone

 

 

 

/* ======================================================================
 * The function calculates optimal timing of the I2C bus
 *
 * @PAram pclk1_hz - PCLK frequency (Hz)
 * @PAram i2c_freq_hz - target I2C clock frequency (Hz)
 * @retvalue - timing register value
 * ======================================================================*/
uint32_t I2C_calculate_timing(const uint32_t pclk1_hz, const uint32_t i2c_freq_hz) 
{
 const uint32_t clock_period_ns = 1000000000 / pclk1_hz;
 const uint32_t i2c_period_ns = 1000000000 / i2c_freq_hz;
 const uint32_t af_delay_ns = 50; // Analog filter delay (ns)
 
 // Determine speed mode limits & digital filter delay (ns)
 uint32_t tlow_min, thigh_min, tsudat_min, dfn;
 
 if (i2c_freq_hz <= 100000) {
 tlow_min = i2c_period_ns * 470 >> 10;
 thigh_min = i2c_period_ns * 400 >> 10;
 tsudat_min = 250;
 dfn = 4;
 } else if (i2c_freq_hz <= 400000) {
 tlow_min = i2c_period_ns * 540 >> 10;
 thigh_min = i2c_period_ns * 250 >> 10;
 tsudat_min = 100;
 dfn = 2;
 } else if (i2c_freq_hz <= 1000000) {
 tlow_min = i2c_period_ns * 520 >> 10;
 thigh_min = i2c_period_ns * 270 >> 10;
 tsudat_min = 50;
 dfn = 0;
 } else {
 return 0;
 }
 
 const uint32_t filter_delay_ns = af_delay_ns + (dfn * clock_period_ns);
 const uint32_t sdadel_min_ns = filter_delay_ns;
 const uint32_t sdadel_max_ns = i2c_period_ns >> 2;
 const uint32_t scldel_min_ns = tsudat_min + filter_delay_ns;
 
 // Target period with 20% margin
 const uint32_t period_max = i2c_period_ns + (i2c_period_ns / 5);
 const uint32_t period_min = i2c_period_ns - (i2c_period_ns / 5);
 
 uint32_t tmp_presc = 0;
 uint32_t tmp_scldel = 0;
 uint32_t tmp_sdadel = 0;
 uint32_t tmp_sclh = 0;
 uint32_t tmp_scll = 0;
 uint32_t tmp_error = UINT32_MAX;

 // Binary search helper function for SCLH and SCLL
 for (uint32_t presc = 0; presc < 16; presc++) {
 const uint32_t ti2cclk = clock_period_ns * (presc + 1);
 
 for (uint32_t scldel = 0; scldel < 16; scldel++) {
 const uint32_t tscldel = (scldel + 1) * ti2cclk;
 if (tscldel < scldel_min_ns) continue;
 
 for (uint32_t sdadel = 0; sdadel < 16; sdadel++) {
 const uint32_t tsdadel = sdadel * ti2cclk;
 if (tsdadel < sdadel_min_ns || tsdadel > sdadel_max_ns) continue;
 
 const uint32_t tsync = filter_delay_ns + 2 * ti2cclk;
 
 // Binary search for SCLH
 uint32_t sclh_min = (thigh_min - filter_delay_ns + ti2cclk - 1) / ti2cclk - 1;
 if (sclh_min >= 256) continue;
 uint32_t sclh_max = 255;
 
 while (sclh_min <= sclh_max) {
 const uint32_t sclh = (sclh_min + sclh_max) >> 1;
 const uint32_t tsclh = (sclh + 1) * ti2cclk;
 const uint32_t thigh = tsclh + filter_delay_ns;
 
 if (thigh < thigh_min) {
 sclh_min = sclh + 1;
 continue;
 }
 
 if (ti2cclk >= thigh) {
 sclh_min = sclh + 1;
 continue;
 }
 
 // Binary search for SCLL
 uint32_t scll_min = (tlow_min - filter_delay_ns + ti2cclk - 1) / ti2cclk - 1;
 if (scll_min >= 256) {
 sclh_min = sclh + 1;
 continue;
 }
 uint32_t scll_max = 255;
 
 while (scll_min <= scll_max) {
 const uint32_t scll = (scll_min + scll_max) >> 1;
 const uint32_t tscll = (scll + 1) * ti2cclk;
 const uint32_t tlow = tscll + filter_delay_ns;
 
 if (tlow < tlow_min) {
 scll_min = scll + 1;
 continue;
 }
 
 if (ti2cclk >= (tlow - filter_delay_ns) / 4) {
 scll_min = scll + 1;
 continue;
 }
 
 const uint32_t ttotal = tscll + tsclh + 2 * tsync;
 
 if (ttotal < period_min) {
 scll_min = scll + 1;
 continue;
 }
 
 if (ttotal > period_max) {
 scll_max = scll - 1;
 continue;
 }
 
 // Calculate error
 const uint32_t error = (ttotal > i2c_period_ns) 
 ? ttotal - i2c_period_ns 
 : i2c_period_ns - ttotal;
 
 // Update if better timing found
 if (error < tmp_error) {
 tmp_error = error;
 tmp_presc = presc;
 tmp_scldel = scldel;
 tmp_sdadel = sdadel;
 tmp_sclh = sclh;
 tmp_scll = scll;
 }
 
 // Try to find better error by decreasing SCLL
 scll_max = scll - 1;
 }
 // Try to find better error by decreasing SCLH
 sclh_max = sclh - 1;
 }
 }
 }
 }
 
 if (tmp_error == UINT32_MAX) {
 return 0;
 }
 
 return (tmp_presc << 28) | (tmp_scldel << 20) | (tmp_sdadel << 16) | (tmp_sclh << 8) | tmp_scll;
}