eric2013 发表于 2023-8-25 14:18:57

STM32的硬件I2C时序配置算法




I2C的硬件时序配置非常麻烦,涉及到好几个参数

所以现在基本都是CubeMX来实现,不需要用户自己倒腾了。




如果要用户自己倒腾怎么办,CMSIS-Driver里面提供了计算方法:

/* -----------------------------------------------------------------------------
* Copyright (c) 2013-2022 Arm Limited (or its affiliates). All
* rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* $Date:      11. February 2022
* $Revision:    V1.4
*
* Driver:       Driver_I2C1, Driver_I2C2, Driver_I2C3, Driver_I2C4,
*               Driver_I2C5
* Configured:   via STM32CubeMx configuration tool
* Project:      I2C Driver for ST STM32H7xx
* --------------------------------------------------------------------------
* Use the following configuration settings in the middleware component
* to connect to this driver.
*
*   Configuration Setting               Value   I2C Interface
*   ---------------------               -----   -------------
*   Connect to hardware via Driver_I2C# = 1       use I2C1
*   Connect to hardware via Driver_I2C# = 2       use I2C2
*   Connect to hardware via Driver_I2C# = 3       use I2C3
*   Connect to hardware via Driver_I2C# = 4       use I2C4
*   Connect to hardware via Driver_I2C# = 5       use I2C5
* --------------------------------------------------------------------------
* Note:
*DMA transfers are disabled. IRQ transfer will be performed even if
*DMA channels for I2C are configured in STM32CubeMX.
* -------------------------------------------------------------------------- */

/* History:
*Version 1.4
*    - Added I2C5 interface accessed by Driver_I2C5
*Version 1.3
*    - Corrected pin configuration: MX_I2Cx_yyy_GPIO_PuPd replaced with MX_I2Cx_yyy_GPIO_Pu
*    - Disabled DMA transfers
*Version 1.2
*    - Synchronized to HAL V1.10.0 (HAL_I2C_Master_Seq_Receive_IT() and HAL_I2C_Slave_Seq_Receive_IT())
*Version 1.1
*    Master clock frequency generation corrected
*Version 1.0
*    Initial release
*/

/*! \page stm32h7_i2c CMSIS-Driver I2C Setup

The CMSIS-Driver I2C requires:
- Setup of I2Cx input clock
- Setup of I2Cx in I2C mode with optional DMA for Rx and Tx transfers <b> (DMA transfers are disabled in current driver)</b>

The example below uses correct settings for STM32H743I-EVAL:
- I2C1 Mode: I2C

For different boards, refer to the hardware schematics to reflect correct setup values.

The STM32CubeMX configuration steps for Pinout, Clock, and System Configuration are
listed below. Enter the values that are marked \b bold.
   
Pinout tab
----------
1. Configure mode
    - Peripherals \b I2C1: Mode=<b>I2C</b>

2. Configure pin PH7 and pin PH8 as I2C3 peripheral alternative pins
    - Click in chip diagram on pin \b PH7 and select \b I2C3_SCL
    - Click in chip diagram on pin \b PH8 and select \b I2C3_SDA

Clock Configuration tab
-----------------------
1. Configure I2C1 Clock
    - Setup "To I2C1,2,3 (MHz)" to match application requirements

Configuration tab
-----------------
1. Under Connectivity open \b I2C1 Configuration:
   - \e optional <b>DMA Settings</b>: setup DMA transfers for Rx and Tx <b> (DMA transfers are disabled in current driver) </b>\n
       \b Add - Select \b I2C1_RX: Stream=DMA1 Stream 0, Direction=Peripheral to Memory, Priority=Low
          DMA Request Settings         | Label             | Peripheral | Memory
          :----------------------------|:------------------|:-----------|:-------------
          Mode: Normal               | Increment Address | OFF      |\b ON
          Use Fifo OFF Threshold: Full | Data Width      |\b Byte   | Byte
          .                            | Burst Size      | Single   | Single
       \b Add - Select \b I2C1_TX: Stream=DMA1 Stream 1, Direction=Memory to Peripheral, Priority=Low
          DMA Request Settings         | Label             | Peripheral | Memory
          :----------------------------|:------------------|:-----------|:-------------
          Mode: Normal               | Increment Address | OFF      |\b ON
          Use Fifo OFF Threshold: Full | Data Width      |\b Byte   | Byte
          .                            | Burst Size      | Single   | Single
   \n\note
   I2C4 transfers with BDMA:\n
         BDMA can access only SRAM4 memory. Ensure that Tx and Rx buffers are be positioned in SRAM4 memory, when I2C4 is used in DMA mode (BDMA).

   \n
   - <b>GPIO Settings</b>: review settings, no changes required
          Pin Name | Signal on Pin | GPIO mode | GPIO Pull-up/Pull..| Maximum out | User Label
          :--------|:--------------|:----------|:-------------------|:------------|:----------
          PB6      | I2C1_SCL      | Alternate | Pull-up            | High      |.
          PB7      | I2C1_SDA      | Alternate | Pull-up            | High      |.

   - <b>NVIC Settings</b>: enable interrupts
          Interrupt Table                      | Enable | Preemption Priority | Sub Priority
          :------------------------------------|:-------|:--------------------|:--------------
          DMA1 stream0 global interrupt      |   ON   | 0                   | 0
          DMA1 stream1 global interrupt      |   ON   | 0                   | 0
          I2C1 event interrupt               |\b ON   | 0                   | 0
          I2C1 error interrupt               |\b ON   | 0                   | 0

   - Parameter Settings: not used
   - User Constants: not used
   
   Click \b OK to close the I2C1 Configuration dialog
*/

/*! \cond */

#include "I2C_STM32H7xx.h"

#define ARM_I2C_DRV_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(1,4)    /* driver version */

#define I2C_DMA_ENABLED            0

/* Analog noise filter state: 0 - disable, 1 - enable */
#ifndef I2C1_ANF_ENABLE
#define I2C1_ANF_ENABLE            0
#endif
#ifndef I2C2_ANF_ENABLE
#define I2C2_ANF_ENABLE            0
#endif
#ifndef I2C3_ANF_ENABLE
#define I2C3_ANF_ENABLE            0
#endif
#ifndef I2C4_ANF_ENABLE
#define I2C4_ANF_ENABLE            0
#endif
#ifndef I2C5_ANF_ENABLE
#define I2C5_ANF_ENABLE            0
#endif

/* Digital noise filter coefficient: 0 - disable, - enabled */
#ifndef I2C1_DNF_COEFFICIENT
#define I2C1_DNF_COEFFICIENT         0
#endif
#ifndef I2C2_DNF_COEFFICIENT
#define I2C2_DNF_COEFFICIENT         0
#endif
#ifndef I2C3_DNF_COEFFICIENT
#define I2C3_DNF_COEFFICIENT         0
#endif
#ifndef I2C4_DNF_COEFFICIENT
#define I2C4_DNF_COEFFICIENT         0
#endif
#ifndef I2C5_DNF_COEFFICIENT
#define I2C5_DNF_COEFFICIENT         0
#endif

#ifndef I2C_DCACHE_MAINTENANCE
#define I2C_DCACHE_MAINTENANCE       (1U)
#endif
#ifndef I2C_DCACHE_DATA_RX_ALIGNMENT
#define I2C_DCACHE_DATA_RX_ALIGNMENT (1U)
#endif
#ifndef I2C_DCACHE_DATA_RX_SIZE
#define I2C_DCACHE_DATA_RX_SIZE      (1U)
#endif
#ifndef I2C_DCACHE_DATA_TX_ALIGNMENT
#define I2C_DCACHE_DATA_TX_ALIGNMENT (1U)
#endif
#ifndef I2C_DCACHE_DATA_TX_SIZE
#define I2C_DCACHE_DATA_TX_SIZE      (1U)
#endif

#define I2C_DCACHE_DATA_SIZE(rxtx)       rxtx ? I2C_DCACHE_DATA_TX_SIZE   : I2C_DCACHE_DATA_RX_SIZE
#define I2C_DCACHE_DATA_ALIGNMENT(rxtx)rxtx ? I2C_DCACHE_DATA_TX_ALIGNMENT: I2C_DCACHE_DATA_RX_ALIGNMENT

/* Driver Version */
static const ARM_DRIVER_VERSION DriverVersion = {
ARM_I2C_API_VERSION,
ARM_I2C_DRV_VERSION
};

/* Driver Capabilities */
static const ARM_I2C_CAPABILITIES DriverCapabilities = {
0U                  /* Does not support 10-bit addressing */
#if (I2C_API_PADDING_EN != 0U)
, 0U                  /* Reserved bits */
#endif
};


#if defined(MX_I2C1)
extern I2C_HandleTypeDef hi2c1;

/* I2C1 Information (Run-Time) */
static I2C_INFO I2C1_Info = {
NULL,
{ 0U, 0U, 0U, 0U, 0U, 0U
#if (I2C_API_PADDING_EN != 0U)
, 0U
#endif
},
0U,
0U,
0U,
0U,
NULL,
0U
};

/* I2C1 Resources */
static I2C_RESOURCES I2C1_Resources = {
&hi2c1,
I2C1,
{
    MX_I2C1_SCL_GPIOx,
    MX_I2C1_SDA_GPIOx,
    MX_I2C1_SCL_GPIO_Pin,
    MX_I2C1_SDA_GPIO_Pin,
    MX_I2C1_SCL_GPIO_Pu,
    MX_I2C1_SDA_GPIO_Pu,
    MX_I2C1_SCL_GPIO_AF,
    MX_I2C1_SDA_GPIO_AF
},
I2C1_EV_IRQn,
I2C1_ER_IRQn,
I2C1_ANF_ENABLE,
I2C1_DNF_COEFFICIENT,
&I2C1_Info
};

#endif /* MX_I2C1 */

#if defined(MX_I2C2)
extern I2C_HandleTypeDef hi2c2;

/* I2C2 Information (Run-Time) */
static I2C_INFO I2C2_Info = {
NULL,
{ 0U, 0U, 0U, 0U, 0U, 0U
#if (I2C_API_PADDING_EN != 0U)
, 0U
#endif
},
0U,
0U,
0U,
0U,
NULL,
0U
};

/* I2C2 Resources */
static I2C_RESOURCES I2C2_Resources = {
&hi2c2,
I2C2,
{
    MX_I2C2_SCL_GPIOx,
    MX_I2C2_SDA_GPIOx,
    MX_I2C2_SCL_GPIO_Pin,
    MX_I2C2_SDA_GPIO_Pin,
    MX_I2C2_SCL_GPIO_Pu,
    MX_I2C2_SDA_GPIO_Pu,
    MX_I2C2_SCL_GPIO_AF,
    MX_I2C2_SDA_GPIO_AF
},
I2C2_EV_IRQn,
I2C2_ER_IRQn,
I2C2_ANF_ENABLE,
I2C2_DNF_COEFFICIENT,
&I2C2_Info
};

#endif /* MX_I2C2 */

#if defined(MX_I2C3)
extern I2C_HandleTypeDef hi2c3;

/* I2C3 Information (Run-Time) */
static I2C_INFO I2C3_Info = {
NULL,
{ 0U, 0U, 0U, 0U, 0U, 0U
#if (I2C_API_PADDING_EN != 0U)
, 0U
#endif
},
0U,
0U,
0U,
0U,
NULL,
0U
};

/* I2C3 Resources */
static I2C_RESOURCES I2C3_Resources = {
&hi2c3,
I2C3,
{
    MX_I2C3_SCL_GPIOx,
    MX_I2C3_SDA_GPIOx,
    MX_I2C3_SCL_GPIO_Pin,
    MX_I2C3_SDA_GPIO_Pin,
    MX_I2C3_SCL_GPIO_Pu,
    MX_I2C3_SDA_GPIO_Pu,
    MX_I2C3_SCL_GPIO_AF,
    MX_I2C3_SDA_GPIO_AF
},
I2C3_EV_IRQn,
I2C3_ER_IRQn,
I2C3_ANF_ENABLE,
I2C3_DNF_COEFFICIENT,
&I2C3_Info
};

#endif /* MX_I2C3 */

#if defined(MX_I2C4)
extern I2C_HandleTypeDef hi2c4;

/* I2C4 Information (Run-Time) */
static I2C_INFO I2C4_Info = {
NULL,
{ 0U, 0U, 0U, 0U, 0U, 0U
#if (I2C_API_PADDING_EN != 0U)
, 0U
#endif
},
0U,
0U,
0U,
0U,
NULL,
0U
};

/* I2C4 Resources */
static I2C_RESOURCES I2C4_Resources = {
&hi2c4,
I2C4,
{
    MX_I2C4_SCL_GPIOx,
    MX_I2C4_SDA_GPIOx,
    MX_I2C4_SCL_GPIO_Pin,
    MX_I2C4_SDA_GPIO_Pin,
    MX_I2C4_SCL_GPIO_Pu,
    MX_I2C4_SDA_GPIO_Pu,
    MX_I2C4_SCL_GPIO_AF,
    MX_I2C4_SDA_GPIO_AF
},
I2C4_EV_IRQn,
I2C4_ER_IRQn,
I2C4_ANF_ENABLE,
I2C4_DNF_COEFFICIENT,
&I2C4_Info
};

#endif /* MX_I2C4 */

#if defined(MX_I2C5)
extern I2C_HandleTypeDef hi2c5;

/* I2C5 Information (Run-Time) */
static I2C_INFO I2C5_Info = {
NULL,
{ 0U, 0U, 0U, 0U, 0U, 0U
#if (I2C_API_PADDING_EN != 0U)
, 0U
#endif
},
0U,
0U,
0U,
0U,
NULL,
0U
};

/* I2C5 Resources */
static I2C_RESOURCES I2C5_Resources = {
&hi2c5,
I2C5,
{
    MX_I2C5_SCL_GPIOx,
    MX_I2C5_SDA_GPIOx,
    MX_I2C5_SCL_GPIO_Pin,
    MX_I2C5_SDA_GPIO_Pin,
    MX_I2C5_SCL_GPIO_Pu,
    MX_I2C5_SDA_GPIO_Pu,
    MX_I2C5_SCL_GPIO_AF,
    MX_I2C5_SDA_GPIO_AF
},
I2C5_EV_IRQn,
I2C5_ER_IRQn,
I2C5_ANF_ENABLE,
I2C5_DNF_COEFFICIENT,
&I2C5_Info
};

#endif /* MX_I2C5 */

/* I2C standard timing specification */
static I2C_STD_TIME i2c_spec_standard = {
100000, /* clk_max    */
300,    /* tf_max   */
1000,   /* tr_max   */
0,      /* hddat_min*/
3450,   /* vddat_max*/
250,    /* sudat_min*/
4700,   /* scll_min   */
4000,   /* sclh_min   */
};

static I2C_STD_TIME i2c_spec_fast = {
400000, /* clk_max    */
300,    /* tf_max   */
300,    /* tr_max   */
0,      /* hddat_min*/
900,    /* vddat_max*/
100,    /* sudat_min*/
1300,   /* scll_min   */
600,    /* sclh_min   */
};

static I2C_STD_TIME i2c_spec_fast_plus = {
1000000, /* clk_max    */
100,   /* tf_max   */
120,   /* tr_max   */
0,       /* hddat_min*/
450,   /* vddat_max*/
50,      /* sudat_min*/
500,   /* scll_min   */
260,   /* sclh_min   */
};

#if (I2C_DMA_ENABLED == 1U)
#if (I2C_DCACHE_MAINTENANCE == 1U)
/**
\fn          int32_t I2C_GetDCacheMemBlockSize (uint32_t rxtx, uint32_t *data, uint32_t size)
\brief       Get DCache memory block size
\param   rxtx0 = rx, 1 = tx
\param   dataData address
\param   sizeData size
\return      DCache memory block size (n *32) or 0 = invalid size
*/
__STATIC_INLINE int32_t I2C_GetDCacheMemBlockSize (uint32_t rxtx, uint32_t *data, uint32_t size) {
int32_t sz = 0;

#if (I2C_DCACHE_DATA_SIZE(rxtx) == 1U)
if ((size & 0x1FU) == 0U) {
    sz = (int32_t)size;
}
#else
sz = (int32_t)((((uint32_t)data + size + 0x1FU) & ~0x1FU) - ((uint32_t)data & ~0x1FU));
#endif

return sz;
}

/**
\fn          uint32_t * I2C_GetDCacheMemBlockAddr (uint32_t rxtx, uint32_t *data)
\brief       Get DCache memory block address
\param   rxtx0 = rx, 1 = tx
\param   dataData address
\param   sizeData size
\return      DCache memory block address (aligned to 32-byte boundary) or 0 = invalid data address
*/
__STATIC_INLINE uint32_t * I2C_GetDCacheMemBlockAddr (uint32_t rxtx, uint32_t *data) {
uint32_t *addr = NULL;

#if (I2C_DCACHE_DATA_ALIGNMENT(rxtx) == 1U)
if (((uint32_t)data & 0x1FU) == 0U) {
    addr = data;
}
#else
addr = (uint32_t *)((uint32_t)data & ~0x1FU);
#endif

return addr;
}
#endif
#endif

/* Private functions */
static I2C_RESOURCES *I2C_GetResources   (I2C_HandleTypeDef *hi2c);
static uint32_t       I2C_GetKernelClock (I2C_TypeDef *i2c);
static int32_t      I2C_GetSCLRatio    (I2C_CLK_SETUP *setup, I2C_STD_TIME *spec, I2C_TIMING *cfg);
static uint32_t       I2C_GetTimingValue (I2C_CLK_SETUP *setup, I2C_STD_TIME *spec);

/* Retrieve pointer to I2C instance resources */
static I2C_RESOURCES *I2C_GetResources (I2C_HandleTypeDef *hi2c) {
I2C_RESOURCES *i2c = NULL;

#if defined(MX_I2C1)
if (hi2c->Instance == I2C1) { i2c = &I2C1_Resources; }
#endif

#if defined(MX_I2C2)
if (hi2c->Instance == I2C2) { i2c = &I2C2_Resources; }
#endif

#if defined(MX_I2C3)
if (hi2c->Instance == I2C3) { i2c = &I2C3_Resources; }
#endif

#if defined(MX_I2C4)
if (hi2c->Instance == I2C4) { i2c = &I2C4_Resources; }
#endif

#if defined(MX_I2C5)
if (hi2c->Instance == I2C5) { i2c = &I2C5_Resources; }
#endif

return (i2c);
}

/* Determine I2C kernel clock */
static uint32_t I2C_GetKernelClock (I2C_TypeDef *i2c) {
PLL3_ClocksTypeDef pll3;
uint32_t src, clk;
uint32_t hclk, div;

if (i2c == I2C4) {
    src = __HAL_RCC_GET_I2C4_SOURCE();
}
else /* I2C1, I2C2, I2C3 */ {
    src = __HAL_RCC_GET_I2C1_SOURCE();
}

if ((src == RCC_I2C1CLKSOURCE_D2PCLK1) || (src == RCC_I2C4CLKSOURCE_D3PCLK1)) {
    /* Get HCLK frequency (clock freq after HPRE prescaler) */
    hclk = HAL_RCC_GetHCLKFreq();
    div= 1U;

    if (i2c == I2C4) {
      /* Determine D3PPRE value */
      if (RCC->D3CFGR & RCC_D3CFGR_D3PPRE_2) {
      div = 2U << ((RCC->D3CFGR >> 4U) & 0x03U);
      }
    }
    else {
      /* Determine D2PPRE1 value */
      if (RCC->D2CFGR & RCC_D2CFGR_D2PPRE1_2) {
      div = 2U << ((RCC->D2CFGR >> 4U) & 0x03U);
      }
    }
    clk = hclk / div;
}
else if ((src == RCC_I2C1CLKSOURCE_PLL3) || (src == RCC_I2C4CLKSOURCE_PLL3)) {
    /* Get PLL3 frequencies */
    HAL_RCCEx_GetPLL3ClockFreq(&pll3);
   
    /* I2C kernel clock runs from PLL3 R divider */
    clk = pll3.PLL3_R_Frequency;
}
else if ((src == RCC_I2C1CLKSOURCE_HSI) || (src == RCC_I2C4CLKSOURCE_HSI)) {
    /* Get HSI frequency (64MHz) */
    clk = MX_HSI_VALUE;
}
else /* if ((src == RCC_I2C1CLKSOURCE_CSI) || (src == RCC_I2C4CLKSOURCE_CSI)) */ {
    /* Get CSI frequency (4MHz) */
    clk = 4000000;
}

return (clk);
}

/* TIMING setup: Evaluate SCL low/high ratio */
static int32_t I2C_GetSCLRatio (I2C_CLK_SETUP *setup, I2C_STD_TIME *spec, I2C_TIMING *cfg) {
uint32_t clk_max, clk_min;
uint32_t tpresc, tsync;
uint32_t tscl, tscll, tsclh;
uint32_t scll, sclh;
int32_terr;

/* Set minimum bus clock frequency to 80% of max */
clk_min = (spec->clk_max * 80) / 100;

/* Convert values to ns */
clk_max = 1000000000 / clk_min;
clk_min = 1000000000 / spec->clk_max;
tsync   = setup->afd_min + setup->dfd + (2 * setup->i2cclk);
tpresc = (cfg->presc + 1) * setup->i2cclk;

err = 0;

/* Evaluate all values of SCLL and SCLH */
for (scll = 0; scll < I2C_TIMINGR_SCLL_MAX; scll++) {
    tscll = ((scll + 1) * tpresc) + tsync;

    if ((tscll >= spec->scll_min) && (setup->i2cclk < ((tscll - setup->afd_min - setup->dfd) / 4))) {
      /* SCL low meets specification */

      for (sclh = 0; sclh < I2C_TIMINGR_SCLH_MAX; sclh++) {
      tsclh = ((sclh + 1) * tpresc) + tsync;

      if ((tsclh >= spec->sclh_min) && (tsclh > setup->i2cclk)) {
          /* SCL high meets specification */
          tscl = tscll + tsclh;

          if ((tscl >= clk_min) && (tscl <= clk_max)) {
            /* Evaluate SCL period error */
            err = (int32_t)(tscl - setup->busclk);

            if (err >= 0) {
            cfg->sclh = (uint8_t)sclh;
            cfg->scll = (uint8_t)scll;
            return (err);
            }
          }
      }
      }
    }
}
/* No solution */
return (-1);
}

/* TIMING setup: Determine TIMING register settings based on input structures */
static uint32_t I2C_GetTimingValue (I2C_CLK_SETUP *setup, I2C_STD_TIME *spec) {
I2C_TIMING time;
uint32_t dnf_en;
uint32_t presc;
uint32_t sdadel, sdadel_min, sdadel_max;
uint32_t scldel, scldel_min;
uint32_t p, l, a;
uint32_t timing;
int32_tval, err;

/* Set digital noise filter enabled flag */
if (setup->dfd > 0U) {
    dnf_en = 1U;
} else {
    dnf_en = 0U;
}

/* SDADEL (max) */
val = (int32_t)(spec->vddat_max - spec->tr_max - setup->afd_max - ((dnf_en + 4) * setup->i2cclk));

if (val > 0) {
    sdadel_max = (uint32_t)val;
} else {
    sdadel_max = 0U;
}

/* SDADEL (min) */
val = (int32_t)(spec->tf_max + spec->hddat_min - setup->afd_min - ((dnf_en + 3) * setup->i2cclk));

if (val > 0) {
    sdadel_min = (uint32_t)val;
} else {
    sdadel_min = 0U;
}

/* SCLDEL (min) */
scldel_min = spec->tr_max + spec->sudat_min;

/* Set timing register max value */
timing = 0xF0FFFFFF;

/* Evaluate all values of PRESC, SCLDEL and SDADEL */
for (p = 0; p < I2C_TIMINGR_PRESC_MAX; p++) {
    presc = (p + 1) * setup->i2cclk;

    for (l = 0; l < I2C_TIMINGR_SCLDEL_MAX; l++) {
      /* tSCLDEL = (SCLDEL + 1) * ((PRESC + 1) * tI2CCLK) */
      scldel = (l + 1) * presc;

      if (scldel >= scldel_min) {
      /* SCLDEL is above low limit, evaluate SDADEL */
      for (a = 0; a < I2C_TIMINGR_SDADEL_MAX; a++) {
          /* tSDADEL = SDADEL * ((PRESC + 1) * tI2CCLK) */
          sdadel = a * presc;

          if ((sdadel >= sdadel_min) && (sdadel <= sdadel_max)) {
            /* Valid presc (p), scldel (l) and sdadel (a) */
            time.presc= (uint8_t)p;
            time.scldel = (uint8_t)l;
            time.sdadel = (uint8_t)a;

            /* Determine SCLL and SCLH values */
            err = I2C_GetSCLRatio (setup, spec, &time);

            if (err >= 0) {
            if (err < setup->error) {
                /* Truncate error since it will never be bigger than 16-bit */
                setup->error = (uint16_t)err;

                /* Save timing settings */
                timing= (time.scll   & 0xFFU);
                timing |= (time.sclh   & 0xFFU) <<8;
                timing |= (time.sdadel & 0x0FU) << 16;
                timing |= (time.scldel & 0x0FU) << 20;
                timing |= (time.presc& 0x0FU) << 28;
            }
            }
          }
      }
      }
    }
}

return (timing);
}

/**
\fn          ARM_DRV_VERSION I2C_GetVersion (void)
\brief       Get driver version.
\return      \ref ARM_DRV_VERSION
*/
static ARM_DRIVER_VERSION I2CX_GetVersion (void) {
return DriverVersion;
}


/**
\fn          ARM_I2C_CAPABILITIES I2C_GetCapabilities (void)
\brief       Get driver capabilities.
\return      \ref ARM_I2C_CAPABILITIES
*/
static ARM_I2C_CAPABILITIES I2CX_GetCapabilities (void) {
return DriverCapabilities;
}


/**
\fn          int32_t I2C_Initialize (ARM_I2C_SignalEvent_t cb_event, I2C_RESOURCES *i2c)
\brief       Initialize I2C Interface.
\param   cb_eventPointer to \ref ARM_I2C_SignalEvent
\param   i2c   Pointer to I2C resources
\return      \ref ARM_I2C_STATUS
*/
static int32_t I2C_Initialize (ARM_I2C_SignalEvent_t cb_event, I2C_RESOURCES *i2c) {

if (i2c->info->flags & I2C_INIT) { return ARM_DRIVER_OK; }

i2c->h->Instance = i2c->reg;

i2c->h->Init.Timing         = 0U;
i2c->h->Init.OwnAddress1      = 0U;
i2c->h->Init.AddressingMode   = I2C_ADDRESSINGMODE_7BIT;
i2c->h->Init.DualAddressMode= I2C_DUALADDRESS_DISABLE;
i2c->h->Init.OwnAddress2      = 0U;
i2c->h->Init.OwnAddress2Masks = I2C_OA2_NOMASK;
i2c->h->Init.GeneralCallMode= I2C_GENERALCALL_DISABLE;
i2c->h->Init.NoStretchMode    = I2C_NOSTRETCH_DISABLE;

/* Reset Run-Time information structure */
memset ((void *)i2c->info, 0x00, sizeof (I2C_INFO));

i2c->info->cb_event = cb_event;
i2c->info->flags    = I2C_INIT;

return ARM_DRIVER_OK;
}


/**
\fn          int32_t I2C_Uninitialize (I2C_RESOURCES *i2c)
\brief       De-initialize I2C Interface.
\param   i2cPointer to I2C resources
\return      \ref execution_status
*/
static int32_t I2C_Uninitialize (I2C_RESOURCES *i2c) {

i2c->h->Instance = NULL;
i2c->info->flags = 0U;

return ARM_DRIVER_OK;
}


/**
\fn          int32_t ARM_I2C_PowerControl (ARM_POWER_STATE state, I2C_RESOURCES *i2c)
\brief       Control I2C Interface Power.
\param   statePower state
\param   i2cPointer to I2C resources
\return      \ref execution_status
*/
static int32_t I2C_PowerControl (ARM_POWER_STATE state, I2C_RESOURCES *i2c) {
uint32_t val;

switch (state) {
    case ARM_POWER_OFF:

      if (HAL_I2C_DeInit (i2c->h) != HAL_OK) {
      return ARM_DRIVER_ERROR;
      }

      i2c->info->status.busy             = 0U;
      i2c->info->status.mode             = 0U;
      i2c->info->status.direction      = 0U;
      i2c->info->status.general_call   = 0U;
      i2c->info->status.arbitration_lost = 0U;
      i2c->info->status.bus_error      = 0U;

      i2c->info->flags &= ~I2C_POWER;
      break;

    case ARM_POWER_LOW:
      return ARM_DRIVER_ERROR_UNSUPPORTED;

    case ARM_POWER_FULL:
      if ((i2c->info->flags & I2C_INIT)== 0U) {
      return ARM_DRIVER_ERROR;
      }
      if ((i2c->info->flags & I2C_POWER) != 0U) {
      return ARM_DRIVER_OK;
      }
      
      if (HAL_I2C_Init (i2c->h) != HAL_OK) {
      return ARM_DRIVER_ERROR;
      }

      /* Configure analog noise filter */
      if (i2c->anf_enable != 0) {
      val = I2C_ANALOGFILTER_ENABLE;
      } else {
      val = I2C_ANALOGFILTER_DISABLE;
      }

      if (HAL_I2CEx_ConfigAnalogFilter (i2c->h, val) != HAL_OK) {
      return ARM_DRIVER_ERROR;
      }
      
      /* Configure digital noise filter */
      if (HAL_I2CEx_ConfigDigitalFilter (i2c->h, i2c->dnf_coef) != HAL_OK) {
      return ARM_DRIVER_ERROR;
      }

      if (i2c->h->hdmarx != NULL) {
      i2c->info->flags |= I2C_DMA_RX;
      }
      if (i2c->h->hdmatx != NULL) {
      i2c->info->flags |= I2C_DMA_TX;
      }

      /* Ready for operation */
      i2c->info->flags |= I2C_POWER;
      break;
}

return ARM_DRIVER_OK;
}


/**
\fn          int32_t I2C_MasterTransmit (uint32_t       addr,
                                           const uint8_t *data,
                                           uint32_t       num,
                                           bool         xfer_pending,
                                           I2C_RESOURCES *i2c)
\brief       Start transmitting data as I2C Master.
\param   addr          Slave address (7-bit or 10-bit)
\param   data          Pointer to buffer with data to send to I2C Slave
\param   num         Number of data bytes to send
\param   xfer_pendingTransfer operation is pending - Stop condition will not be generated
\param   i2c         Pointer to I2C resources
\return      \ref execution_status
*/
static int32_t I2C_MasterTransmit (uint32_t       addr,
                                 const uint8_t *data,
                                 uint32_t       num,
                                 bool         xfer_pending,
                                 I2C_RESOURCES *i2c) {
HAL_StatusTypeDef status;
uint32_t buf = (uint32_t)data;
uint16_t cnt, saddr;
uint32_t opt;
#if (I2C_DMA_ENABLED == 1U)
uint8_ttx_dma = 0U;
#if (I2C_DCACHE_MAINTENANCE == 1U)
int32_t   mem_sz;
uint32_t *mem_addr;
#endif
#endif

if ((data == NULL) || (num == 0U)) {
    return ARM_DRIVER_ERROR_PARAMETER;
}

if ((addr & ~((uint32_t)ARM_I2C_ADDRESS_10BIT | (uint32_t)ARM_I2C_ADDRESS_GC)) > 0x3FFU) {
    return ARM_DRIVER_ERROR_PARAMETER;
}

if (num > UINT16_MAX) {
    /* HAL does not handle 32-bit count in transfer */
    return ARM_DRIVER_ERROR;
}

cnt = (uint16_t)num;

if (i2c->info->status.busy) {
    return (ARM_DRIVER_ERROR_BUSY);
}

saddr = (addr & 0x3FFU);
if (i2c->h->Init.AddressingMode == I2C_ADDRESSINGMODE_7BIT) {
    saddr <<= 1;
}

i2c->info->status.busy             = 1U;
i2c->info->status.mode             = 1U;
i2c->info->status.direction      = 0U;
i2c->info->status.bus_error      = 0U;
i2c->info->status.arbitration_lost = 0U;

i2c->info->xfer_sz = cnt;

if (xfer_pending == true) {
    if ((i2c->info->flags & I2C_XFER_FIRST) == 0U) {
      i2c->info->flags |= I2C_XFER_FIRST;
      opt = I2C_FIRST_FRAME;
    } else {
      opt = I2C_NEXT_FRAME;
    }
    status = HAL_I2C_Master_Seq_Transmit_IT (i2c->h, saddr, (uint8_t *)buf, cnt, opt);
}
else {
#if (I2C_DMA_ENABLED == 1U)
    if ((i2c->info->flags & (I2C_DMA_TX | I2C_XFER_FIRST)) == I2C_DMA_TX) {
      
#if (I2C_DCACHE_MAINTENANCE == 1U)
      /* Is DCache enabled */
      if ((SCB->CCR & SCB_CCR_DC_Msk) != 0U) {
      mem_addr = I2C_GetDCacheMemBlockAddr(1U, (uint32_t *)data);
      mem_sz   = I2C_GetDCacheMemBlockSize(1U, (uint32_t *)data, num);
      if ((mem_addr != NULL) && (mem_sz != 0U)) {
          tx_dma = 1U;
          /* Clean data cache: ensure data coherency between DMA and CPU */
          SCB_CleanDCache_by_Addr (mem_addr, mem_sz);
      }
      }
      else {
      tx_dma = 1U;
      }
#else
      tx_dma = 1U;
#endif
    }

    if (tx_dma != 0U) {
      status = HAL_I2C_Master_Transmit_DMA (i2c->h, saddr, (uint8_t *)buf, cnt);
    }
    else {
#endif
      i2c->info->flags &= ~I2C_XFER_FIRST;
      status = HAL_I2C_Master_Seq_Transmit_IT (i2c->h, saddr, (uint8_t *)buf, cnt, I2C_LAST_FRAME);
#if (I2C_DMA_ENABLED == 1U)
    }
#endif
}

if (status != HAL_OK) {
    return ARM_DRIVER_ERROR;
}

return ARM_DRIVER_OK;
}

/**
\fn          int32_t I2C_MasterReceive (uint32_t       addr,
                                          uint8_t       *data,
                                          uint32_t       num,
                                          bool         xfer_pending,
                                          I2C_RESOURCES *i2c)
\brief       Start receiving data as I2C Master.
\param   addr          Slave address (7-bit or 10-bit)
\paramdata          Pointer to buffer for data to receive from I2C Slave
\param   num         Number of data bytes to receive
\param   xfer_pendingTransfer operation is pending - Stop condition will not be generated
\param   i2c         Pointer to I2C resources
\return      \ref execution_status
*/
static int32_t I2C_MasterReceive (uint32_t       addr,
                                  uint8_t       *data,
                                  uint32_t       num,
                                  bool         xfer_pending,
                                  I2C_RESOURCES *i2c) {
HAL_StatusTypeDef status;
uint16_t cnt, saddr;
uint32_t opt;
#if (I2C_DMA_ENABLED == 1U)
#if (I2C_DCACHE_MAINTENANCE == 1U)
int32_t   mem_sz;
uint32_t *mem_addr;
#endif
#endif

if ((data == NULL) || (num == 0U)) {
    return ARM_DRIVER_ERROR_PARAMETER;
}

if ((addr & ~((uint32_t)ARM_I2C_ADDRESS_10BIT | (uint32_t)ARM_I2C_ADDRESS_GC)) > 0x3FFU) {
    return ARM_DRIVER_ERROR_PARAMETER;
}

if (num > UINT16_MAX) {
    /* HAL does not handle 32-bit count in transfer */
    return ARM_DRIVER_ERROR;
}

cnt = (uint16_t)num;

if (i2c->info->status.busy) {
    return (ARM_DRIVER_ERROR_BUSY);
}

saddr = (addr & 0x3FFU);
if (i2c->h->Init.AddressingMode == I2C_ADDRESSINGMODE_7BIT) {
    saddr <<= 1;
}

i2c->info->status.busy             = 1U;
i2c->info->status.mode             = 1U;
i2c->info->status.direction      = 1U;
i2c->info->status.bus_error      = 0U;
i2c->info->status.arbitration_lost = 0U;

addr = (addr & 0x3FFU);

if (i2c->h->Init.AddressingMode == I2C_ADDRESSINGMODE_7BIT) {
    addr <<= 1;
}

i2c->info->xfer_sz = cnt;
i2c->info->rx_data = data;
i2c->info->rx_dma= 0U;

if (xfer_pending == true) {
    if ((i2c->info->flags & I2C_XFER_FIRST) == 0U) {
      i2c->info->flags |= I2C_XFER_FIRST;
      opt = I2C_FIRST_FRAME;
    } else {
      opt = I2C_NEXT_FRAME;
    }
    status = HAL_I2C_Master_Seq_Receive_IT (i2c->h, saddr, data, cnt, opt);
}
else {
#if (I2C_DMA_ENABLED == 1U)
    if ((i2c->info->flags & (I2C_DMA_RX | I2C_XFER_FIRST)) == I2C_DMA_RX) {
#if (I2C_DCACHE_MAINTENANCE == 1U)
      /* Is DCache enabled */
      if ((SCB->CCR & SCB_CCR_DC_Msk) != 0U) {
      mem_addr = I2C_GetDCacheMemBlockAddr(0U, (uint32_t *)data);
      mem_sz   = I2C_GetDCacheMemBlockSize(0U, (uint32_t *)data, num);
      if ((mem_addr != NULL) && (mem_sz != 0U)) {
          i2c->info->rx_dma = 1U;
      }
      } else {
      i2c->info->rx_dma = 1U;
      }
#else
      i2c->info->rx_dma = 1U;
#endif
    }

    if (i2c->info->rx_dma != 0U) {
      status = HAL_I2C_Master_Receive_DMA (i2c->h, saddr, data, cnt);
    }
    else {
#endif
      i2c->info->flags &= ~I2C_XFER_FIRST;
      status = HAL_I2C_Master_Seq_Receive_IT (i2c->h, saddr, data, cnt, I2C_LAST_FRAME);
#if (I2C_DMA_ENABLED == 1U)
    }
#endif
}

if (status != HAL_OK) {
    return ARM_DRIVER_ERROR;
}

return ARM_DRIVER_OK;
}

/**
\fn          int32_t I2C_SlaveTransmit (const uint8_t *data, uint32_t num, I2C_RESOURCES *i2c)
\brief       Start transmitting data as I2C Slave.
\param   data          Pointer to buffer with data to send to I2C Master
\param   num         Number of data bytes to send
\param   i2c         Pointer to I2C resources
\return      \ref execution_status
*/
static int32_t I2C_SlaveTransmit (const uint8_t *data, uint32_t num, I2C_RESOURCES *i2c) {
HAL_StatusTypeDef status;
uint32_t buf = (uint32_t)data;
uint16_t cnt;

if ((data == NULL) || (num == 0U)) {
    return ARM_DRIVER_ERROR_PARAMETER;
}

if (num > UINT16_MAX) {
    /* HAL does not handle 32-bit count in transfer */
    return ARM_DRIVER_ERROR;
}

cnt = (uint16_t)num;

if (i2c->info->status.busy) {
    return (ARM_DRIVER_ERROR_BUSY);
}

if (HAL_I2C_GetState (i2c->h) != HAL_I2C_STATE_LISTEN) {
    return ARM_DRIVER_ERROR;
}

i2c->info->status.bus_error    = 0U;
i2c->info->status.general_call = 0U;

i2c->info->flags   = I2C_XFER_SET;
i2c->info->xfer_sz = cnt;

status = HAL_I2C_Slave_Seq_Transmit_IT (i2c->h, (uint8_t *)buf, cnt, I2C_NEXT_FRAME);

if (status == HAL_ERROR) {
    return ARM_DRIVER_ERROR;
}

return ARM_DRIVER_OK;
}

/**
\fn          int32_t I2C_SlaveReceive (uint8_t *data, uint32_t num, I2C_RESOURCES *i2c)
\brief       Start receiving data as I2C Slave.
\paramdata          Pointer to buffer for data to receive from I2C Master
\param   num         Number of data bytes to receive
\param   i2c         Pointer to I2C resources
\return      \ref execution_status
*/
static int32_t I2C_SlaveReceive (uint8_t *data, uint32_t num, I2C_RESOURCES *i2c) {
HAL_StatusTypeDef status;
uint16_t cnt;

if ((data == NULL) || (num == 0U)) {
    return ARM_DRIVER_ERROR_PARAMETER;
}

if (num > UINT16_MAX) {
    /* HAL does not handle 32-bit count in transfer */
    return ARM_DRIVER_ERROR;
}

cnt = (uint16_t)num;

if (i2c->info->status.busy) {
    return (ARM_DRIVER_ERROR_BUSY);
}

i2c->info->status.bus_error    = 0U;
i2c->info->status.general_call = 0U;

i2c->info->flags   = I2C_XFER_SET;
i2c->info->xfer_sz = cnt;

status = HAL_I2C_Slave_Seq_Receive_IT (i2c->h, (uint8_t *)data, cnt, I2C_NEXT_FRAME);

if (status == HAL_ERROR) {
    return ARM_DRIVER_ERROR;
}

return ARM_DRIVER_OK;
}


/**
\fn          int32_t I2C_GetDataCount (void)
\brief       Get transferred data count.
\return      number of data bytes transferred; -1 when Slave is not addressed by Master
*/
static int32_t I2C_GetDataCount (I2C_RESOURCES *i2c) {
int32_t cnt;

if (HAL_I2C_GetState (i2c->h) == HAL_I2C_STATE_LISTEN) {
    cnt = -1;
} else {
    cnt = (int32_t)(i2c->info->xfer_sz - i2c->h->XferCount);
}

return (cnt);
}


/**
\fn          int32_t I2C_Control (uint32_t control, uint32_t arg, I2C_RESOURCES *i2c)
\brief       Control I2C Interface.
\param   controloperation
\param   arg      argument of operation (optional)
\param   i2c      pointer to I2C resources
\return      \ref execution_status
*/
static int32_t I2C_Control (uint32_t control, uint32_t arg, I2C_RESOURCES *i2c) {
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_PinState state;
uint32_t i, val;
uint32_t fpclk, fscl;

I2C_CLK_SETUPclk_setup;
I2C_STD_TIME*clk_spec;

if ((i2c->info->flags & I2C_POWER) == 0U) {
    /* I2C not powered */
    return ARM_DRIVER_ERROR;
}

switch (control) {
    case ARM_I2C_OWN_ADDRESS:
      if (arg == 0) {
      /* Disable slave */
      HAL_I2C_DisableListen_IT (i2c->h);
      }
      else {
      if ((arg & ARM_I2C_ADDRESS_GC) != 0) {
          /* Enable general call */
          i2c->h->Init.GeneralCallMode = I2C_GENERALCALL_ENABLE;
      } else {
          /* Disable general call */
          i2c->h->Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
      }

      if ((arg & ARM_I2C_ADDRESS_10BIT) != 0) {
          /* Own address is a 10-bit address */
          i2c->h->Init.AddressingMode = I2C_ADDRESSINGMODE_10BIT;
      } else {
          /* Own address is a 7-bit address */
          i2c->h->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
      }
      
      i2c->h->Init.OwnAddress1 = (arg << 1) & 0x03FFU;

      HAL_I2C_Init (i2c->h);
      HAL_I2C_EnableListen_IT (i2c->h);
      }
      break;

    case ARM_I2C_BUS_SPEED:
      fpclk = I2C_GetKernelClock(i2c->reg);
      switch (arg) {
      case ARM_I2C_BUS_SPEED_STANDARD: /* Clock = 100kHz */
          clk_spec = &i2c_spec_standard;
          fscl = 100000;
          break;

      case ARM_I2C_BUS_SPEED_FAST: /* Clock = 400kHz */
          clk_spec = &i2c_spec_fast;
          fscl = 400000;
          break;
      
      case ARM_I2C_BUS_SPEED_FAST_PLUS: /* Clock = 1MHz */
          clk_spec = &i2c_spec_fast_plus;
          fscl = 1000000;
          break;

      default:
          return ARM_DRIVER_ERROR_UNSUPPORTED;
      }

      /* Determine kernel and bus clock period (ns) */
      clk_setup.i2cclk = (uint16_t)((1000000000 + (fpclk / 2)) / fpclk);
      clk_setup.busclk = (uint16_t)((1000000000 + (fscl/ 2)) / fscl);

      /* Determine digital filter delay (ns) */
      clk_setup.dfd = clk_setup.i2cclk * i2c->dnf_coef;
      
      /* Set analog filter delay (ns) */
      clk_setup.afd_min = ((i2c->anf_enable != 0U) ? (I2C_ANALOG_FILTER_DELAY_MIN) : (0));
      clk_setup.afd_max = ((i2c->anf_enable != 0U) ? (I2C_ANALOG_FILTER_DELAY_MAX) : (0));

      /* Set max iteration error */
      clk_setup.error= 0xFFFF;
      
      /* Get TIMING register values */
      val = I2C_GetTimingValue (&clk_setup, clk_spec);

      /* Set register values */
      i2c->reg->CR1    &= ~I2C_CR1_PE;
      i2c->reg->TIMINGR = val;
      i2c->reg->CR1    |=I2C_CR1_PE;
      break;

    case ARM_I2C_BUS_CLEAR:
      /* Configure SCl and SDA pins as GPIO pin */
      GPIO_InitStruct.Mode= GPIO_MODE_OUTPUT_OD;
      GPIO_InitStruct.Pull= GPIO_NOPULL;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

      GPIO_InitStruct.Pin = i2c->io.scl_pin;
      HAL_GPIO_Init(i2c->io.scl_port, &GPIO_InitStruct);
      GPIO_InitStruct.Pin = i2c->io.sda_pin;
      HAL_GPIO_Init(i2c->io.sda_port, &GPIO_InitStruct);

      /* Pull SCL and SDA high */
      HAL_GPIO_WritePin (i2c->io.scl_port, i2c->io.scl_pin, GPIO_PIN_SET);
      HAL_GPIO_WritePin (i2c->io.sda_port, i2c->io.sda_pin, GPIO_PIN_SET);

      HAL_Delay (I2C_BUS_CLEAR_CLOCK_PERIOD);

      for (i = 0U; i < 9U; i++) {
      if (HAL_GPIO_ReadPin (i2c->io.sda_port, i2c->io.sda_pin) == GPIO_PIN_SET) {
          /* Break if slave released SDA line */
          break;
      }
      /* Clock high */
      HAL_GPIO_WritePin (i2c->io.scl_port, i2c->io.scl_pin, GPIO_PIN_SET);
      HAL_Delay (I2C_BUS_CLEAR_CLOCK_PERIOD/2);

      /* Clock low */
      HAL_GPIO_WritePin (i2c->io.scl_port, i2c->io.scl_pin, GPIO_PIN_RESET);
      HAL_Delay (I2C_BUS_CLEAR_CLOCK_PERIOD/2);
      }

      /* Check SDA state */
      state = HAL_GPIO_ReadPin (i2c->io.sda_port, i2c->io.sda_pin);

      /* Configure SDA and SCL pins as I2C peripheral pins */
      GPIO_InitStruct.Mode= GPIO_MODE_AF_OD;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

      GPIO_InitStruct.Pin       = i2c->io.scl_pin;
      GPIO_InitStruct.Pull      = i2c->io.scl_pull;
      GPIO_InitStruct.Alternate = i2c->io.scl_af;

      HAL_GPIO_Init (i2c->io.scl_port, &GPIO_InitStruct);

      GPIO_InitStruct.Pin       = i2c->io.sda_pin;
      GPIO_InitStruct.Pull      = i2c->io.sda_pull;
      GPIO_InitStruct.Alternate = i2c->io.sda_af;

      HAL_GPIO_Init (i2c->io.sda_port, &GPIO_InitStruct);
      
      if (i2c->info->cb_event != NULL) {
      i2c->info->cb_event (ARM_I2C_EVENT_BUS_CLEAR);
      }

      return (state == GPIO_PIN_SET) ? ARM_DRIVER_OK : ARM_DRIVER_ERROR;

    case ARM_I2C_ABORT_TRANSFER:
      if (HAL_I2C_GetMode (i2c->h) == HAL_I2C_MODE_SLAVE) {
      /* Generate NACK when in slave mode */
      __HAL_I2C_GENERATE_NACK (i2c->h);
      } else {
      /* Get slave address */
      i2c->info->abort = 0U;
      val = i2c->reg->CR2 & 0x3FFU;

      HAL_I2C_Master_Abort_IT (i2c->h, (uint16_t)val);

      /* Wait until abort operation complete */
      while (i2c->info->abort == 0);
      }

      i2c->info->status.busy             = 0U;
      i2c->info->status.mode             = 0U;
      i2c->info->status.direction      = 0U;
      i2c->info->status.general_call   = 0U;
      i2c->info->status.arbitration_lost = 0U;
      i2c->info->status.bus_error      = 0U;
      break;

    default: return ARM_DRIVER_ERROR;
}
return ARM_DRIVER_OK;
}


/**
\fn          ARM_I2C_STATUS I2C_GetStatus (I2C_RESOURCES *i2c)
\brief       Get I2C status.
\param   i2c      pointer to I2C resources
\return      I2C status \ref ARM_I2C_STATUS
*/
static ARM_I2C_STATUS I2C_GetStatus (I2C_RESOURCES *i2c) {
return (i2c->info->status);
}


/* Master tx transfer completed */
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) {
I2C_RESOURCES *i2c;

i2c = I2C_GetResources (hi2c);

if (i2c != NULL) {
    i2c->info->status.busy = 0U;

    if (i2c->info->cb_event != NULL) {
      i2c->info->cb_event (ARM_I2C_EVENT_TRANSFER_DONE);
    }
}
}

/* Master rx transfer completed */
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) {
I2C_RESOURCES *i2c;
#if (I2C_DMA_ENABLED == 1U)
#if (I2C_DCACHE_MAINTENANCE == 1U)
int32_t   mem_sz;
uint32_t *mem_addr;
#endif
#endif

i2c = I2C_GetResources (hi2c);

#if (I2C_DMA_ENABLED == 1U)
#if (I2C_DCACHE_MAINTENANCE == 1U)
if (i2c->info->rx_dma != 0U) {
    if ((SCB->CCR & SCB_CCR_DC_Msk) != 0U) {
      mem_addr = I2C_GetDCacheMemBlockAddr(0U, (uint32_t *)i2c->info->rx_data);
      mem_sz   = I2C_GetDCacheMemBlockSize(0U, (uint32_t *)i2c->info->rx_data, i2c->info->xfer_sz);
      if ((mem_addr != NULL) && (mem_sz != 0U)) {
      /* Invalidate data cache: ensure data coherency between DMA and CPU */
      SCB_InvalidateDCache_by_Addr (mem_addr, mem_sz);
      }
    }
}
#endif
#endif

if (i2c != NULL) {
    i2c->info->status.busy = 0U;

    if (i2c->info->cb_event != NULL) {
      i2c->info->cb_event (ARM_I2C_EVENT_TRANSFER_DONE);
    }
}
}

/* Slave addressed */
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode) {
I2C_RESOURCES *i2c;
uint32_t event;

i2c = I2C_GetResources (hi2c);

if (i2c != NULL) {
    i2c->info->status.mode = 0U;

    if ((i2c->info->flags & I2C_XFER_SET) == 0U) {

      if (TransferDirection == I2C_DIRECTION_TRANSMIT) {
      /* Master is transmitter, slave enters receiver mode */
      event = ARM_I2C_EVENT_SLAVE_RECEIVE;
      i2c->info->status.direction = 1U;
      }
      else {
      /* Master is receiver, slave enters transmitter mode */
      event = ARM_I2C_EVENT_SLAVE_TRANSMIT;
      i2c->info->status.direction = 0U;
      }

      if (AddrMatchCode == 0U) {
      /* General call address */
      event |= ARM_I2C_EVENT_GENERAL_CALL;
      i2c->info->status.general_call = 1U;
      }

      if (i2c->info->cb_event != NULL) {
      i2c->info->cb_event (event);
      }
    }

    if ((i2c->info->flags & I2C_XFER_SET) == 0) {
      __HAL_I2C_GENERATE_NACK (i2c->h);
      __HAL_I2C_ENABLE_IT (i2c->h, I2C_IT_ADDRI | I2C_IT_STOPI | I2C_IT_NACKI | I2C_IT_ERRI);
    }
    else {
      i2c->info->status.busy = 1U;
    }
    __HAL_I2C_CLEAR_FLAG(i2c->h, I2C_FLAG_ADDR);
}
}

/* Slave tx transfer completed */
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c) {
I2C_RESOURCES *i2c;

i2c = I2C_GetResources (hi2c);

if (i2c != NULL) {
    i2c->info->flags &= ~I2C_XFER_SET;

    i2c->info->status.busy = 0U;

    if (i2c->info->cb_event != NULL) {
      i2c->info->cb_event (ARM_I2C_EVENT_TRANSFER_DONE);
    }
    /* Re-enable listen mode */
    __HAL_I2C_ENABLE_IT (i2c->h, I2C_IT_ADDRI | I2C_IT_STOPI | I2C_IT_NACKI | I2C_IT_ERRI);
}
}

/* Slave rx transfer completed */
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c) {
I2C_RESOURCES *i2c;

i2c = I2C_GetResources (hi2c);

if (i2c != NULL) {
    i2c->info->flags &= ~I2C_XFER_SET;

    i2c->info->status.busy = 0U;

    if (i2c->info->cb_event != NULL) {
      i2c->info->cb_event (ARM_I2C_EVENT_TRANSFER_DONE);
    }
    /* Re-enable listen mode */
    __HAL_I2C_ENABLE_IT (i2c->h, I2C_IT_ADDRI | I2C_IT_STOPI | I2C_IT_NACKI | I2C_IT_ERRI);
}
}

/* Listen transfer complete */
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c) {
I2C_RESOURCES *i2c;

i2c = I2C_GetResources (hi2c);

if (i2c != NULL) {
    /* Re-enable listen mode */
    HAL_I2C_EnableListen_IT (i2c->h);
}
}

/* Transfer error */
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) {
I2C_RESOURCES *i2c;
uint32_t err, event;

err = HAL_I2C_GetError (hi2c);
i2c = I2C_GetResources (hi2c);

if (i2c != NULL) {
    event = ARM_I2C_EVENT_TRANSFER_DONE | ARM_I2C_EVENT_TRANSFER_INCOMPLETE;

    if ((err & HAL_I2C_ERROR_BERR) != 0U) {
      /* Bus error */
      event |= ARM_I2C_EVENT_BUS_ERROR;
      i2c->info->status.bus_error = 1U;
    }

    if ((err & HAL_I2C_ERROR_ARLO) != 0U) {
      /* Arbitration lost */
      event |= ARM_I2C_EVENT_ARBITRATION_LOST;
      i2c->info->status.arbitration_lost = 1U;
    }

    if((err & HAL_I2C_ERROR_AF) != 0U) {
      /* Acknowledge not received */
      if ((hi2c->XferCount == 0) && (hi2c->XferSize > 0)) {
      /* Slave address was not acknowledged */
      event |= ARM_I2C_EVENT_ADDRESS_NACK;
      }
    }

    i2c->info->status.busy = 0U;

    if (i2c->info->cb_event != NULL) {
      i2c->info->cb_event (event);
    }
}
}

/* Abort completed */
void HAL_I2C_AbortCpltCallback(I2C_HandleTypeDef *hi2c) {
I2C_RESOURCES *i2c;

i2c = I2C_GetResources (hi2c);

if (i2c != NULL) {
    i2c->info->abort = 1U;
}
}


#if defined(MX_I2C1)
/* I2C1 Driver wrapper functions */
static int32_t I2C1_Initialize (ARM_I2C_SignalEvent_t cb_event) {
return I2C_Initialize(cb_event, &I2C1_Resources);
}
static int32_t I2C1_Uninitialize (void) {
return I2C_Uninitialize(&I2C1_Resources);
}
static int32_t I2C1_PowerControl (ARM_POWER_STATE state) {
return I2C_PowerControl(state, &I2C1_Resources);
}
static int32_t I2C1_MasterTransmit (uint32_t addr, const uint8_t *data, uint32_t num, bool xfer_pending) {
return I2C_MasterTransmit(addr, data, num, xfer_pending, &I2C1_Resources);
}
static int32_t I2C1_MasterReceive (uint32_t addr, uint8_t *data, uint32_t num, bool xfer_pending) {
return I2C_MasterReceive(addr, data, num, xfer_pending, &I2C1_Resources);
}
static int32_t I2C1_SlaveTransmit (const uint8_t *data, uint32_t num) {
return I2C_SlaveTransmit(data, num, &I2C1_Resources);
}
static int32_t I2C1_SlaveReceive (uint8_t *data, uint32_t num) {
return I2C_SlaveReceive(data, num, &I2C1_Resources);
}
static int32_t I2C1_GetDataCount (void) {
return I2C_GetDataCount(&I2C1_Resources);
}
static int32_t I2C1_Control (uint32_t control, uint32_t arg) {
return I2C_Control(control, arg, &I2C1_Resources);
}
static ARM_I2C_STATUS I2C1_GetStatus (void) {
return I2C_GetStatus(&I2C1_Resources);
}

/* I2C1 Driver Control Block */
ARM_DRIVER_I2C Driver_I2C1 = {
I2CX_GetVersion,
I2CX_GetCapabilities,
I2C1_Initialize,
I2C1_Uninitialize,
I2C1_PowerControl,
I2C1_MasterTransmit,
I2C1_MasterReceive,
I2C1_SlaveTransmit,
I2C1_SlaveReceive,
I2C1_GetDataCount,
I2C1_Control,
I2C1_GetStatus
};
#endif /* MX_I2C1 */


#if defined(MX_I2C2)
/* I2C2 Driver wrapper functions */
static int32_t I2C2_Initialize (ARM_I2C_SignalEvent_t cb_event) {
return I2C_Initialize(cb_event, &I2C2_Resources);
}
static int32_t I2C2_Uninitialize (void) {
return I2C_Uninitialize(&I2C2_Resources);
}
static int32_t I2C2_PowerControl (ARM_POWER_STATE state) {
return I2C_PowerControl(state, &I2C2_Resources);
}
static int32_t I2C2_MasterTransmit (uint32_t addr, const uint8_t *data, uint32_t num, bool xfer_pending) {
return I2C_MasterTransmit(addr, data, num, xfer_pending, &I2C2_Resources);
}
static int32_t I2C2_MasterReceive (uint32_t addr, uint8_t *data, uint32_t num, bool xfer_pending) {
return I2C_MasterReceive(addr, data, num, xfer_pending, &I2C2_Resources);
}
static int32_t I2C2_SlaveTransmit (const uint8_t *data, uint32_t num) {
return I2C_SlaveTransmit(data, num, &I2C2_Resources);
}
static int32_t I2C2_SlaveReceive (uint8_t *data, uint32_t num) {
return I2C_SlaveReceive(data, num, &I2C2_Resources);
}
static int32_t I2C2_GetDataCount (void) {
return I2C_GetDataCount(&I2C2_Resources);
}
static int32_t I2C2_Control (uint32_t control, uint32_t arg) {
return I2C_Control(control, arg, &I2C2_Resources);
}
static ARM_I2C_STATUS I2C2_GetStatus (void) {
return I2C_GetStatus(&I2C2_Resources);
}

/* I2C2 Driver Control Block */
ARM_DRIVER_I2C Driver_I2C2 = {
I2CX_GetVersion,
I2CX_GetCapabilities,
I2C2_Initialize,
I2C2_Uninitialize,
I2C2_PowerControl,
I2C2_MasterTransmit,
I2C2_MasterReceive,
I2C2_SlaveTransmit,
I2C2_SlaveReceive,
I2C2_GetDataCount,
I2C2_Control,
I2C2_GetStatus
};
#endif /* MX_I2C2 */


#if defined(MX_I2C3)
/* I2C3 Driver wrapper functions */
static int32_t I2C3_Initialize (ARM_I2C_SignalEvent_t cb_event) {
return I2C_Initialize(cb_event, &I2C3_Resources);
}
static int32_t I2C3_Uninitialize (void) {
return I2C_Uninitialize(&I2C3_Resources);
}
static int32_t I2C3_PowerControl (ARM_POWER_STATE state) {
return I2C_PowerControl(state, &I2C3_Resources);
}
static int32_t I2C3_MasterTransmit (uint32_t addr, const uint8_t *data, uint32_t num, bool xfer_pending) {
return I2C_MasterTransmit(addr, data, num, xfer_pending, &I2C3_Resources);
}
static int32_t I2C3_MasterReceive (uint32_t addr, uint8_t *data, uint32_t num, bool xfer_pending) {
return I2C_MasterReceive(addr, data, num, xfer_pending, &I2C3_Resources);
}
static int32_t I2C3_SlaveTransmit (const uint8_t *data, uint32_t num) {
return I2C_SlaveTransmit(data, num, &I2C3_Resources);
}
static int32_t I2C3_SlaveReceive (uint8_t *data, uint32_t num) {
return I2C_SlaveReceive(data, num, &I2C3_Resources);
}
static int32_t I2C3_GetDataCount (void) {
return I2C_GetDataCount(&I2C3_Resources);
}
static int32_t I2C3_Control (uint32_t control, uint32_t arg) {
return I2C_Control(control, arg, &I2C3_Resources);
}
static ARM_I2C_STATUS I2C3_GetStatus (void) {
return I2C_GetStatus(&I2C3_Resources);
}

/* I2C3 Driver Control Block */
ARM_DRIVER_I2C Driver_I2C3 = {
I2CX_GetVersion,
I2CX_GetCapabilities,
I2C3_Initialize,
I2C3_Uninitialize,
I2C3_PowerControl,
I2C3_MasterTransmit,
I2C3_MasterReceive,
I2C3_SlaveTransmit,
I2C3_SlaveReceive,
I2C3_GetDataCount,
I2C3_Control,
I2C3_GetStatus
};
#endif /* MX_I2C3 */


#if defined(MX_I2C4)
/* I2C4 Driver wrapper functions */
static int32_t I2C4_Initialize (ARM_I2C_SignalEvent_t cb_event) {
return I2C_Initialize(cb_event, &I2C4_Resources);
}
static int32_t I2C4_Uninitialize (void) {
return I2C_Uninitialize(&I2C4_Resources);
}
static int32_t I2C4_PowerControl (ARM_POWER_STATE state) {
return I2C_PowerControl(state, &I2C4_Resources);
}
static int32_t I2C4_MasterTransmit (uint32_t addr, const uint8_t *data, uint32_t num, bool xfer_pending) {
return I2C_MasterTransmit(addr, data, num, xfer_pending, &I2C4_Resources);
}
static int32_t I2C4_MasterReceive (uint32_t addr, uint8_t *data, uint32_t num, bool xfer_pending) {
return I2C_MasterReceive(addr, data, num, xfer_pending, &I2C4_Resources);
}
static int32_t I2C4_SlaveTransmit (const uint8_t *data, uint32_t num) {
return I2C_SlaveTransmit(data, num, &I2C4_Resources);
}
static int32_t I2C4_SlaveReceive (uint8_t *data, uint32_t num) {
return I2C_SlaveReceive(data, num, &I2C4_Resources);
}
static int32_t I2C4_GetDataCount (void) {
return I2C_GetDataCount(&I2C4_Resources);
}
static int32_t I2C4_Control (uint32_t control, uint32_t arg) {
return I2C_Control(control, arg, &I2C4_Resources);
}
static ARM_I2C_STATUS I2C4_GetStatus (void) {
return I2C_GetStatus(&I2C4_Resources);
}

/* I2C4 Driver Control Block */
ARM_DRIVER_I2C Driver_I2C4 = {
I2CX_GetVersion,
I2CX_GetCapabilities,
I2C4_Initialize,
I2C4_Uninitialize,
I2C4_PowerControl,
I2C4_MasterTransmit,
I2C4_MasterReceive,
I2C4_SlaveTransmit,
I2C4_SlaveReceive,
I2C4_GetDataCount,
I2C4_Control,
I2C4_GetStatus
};
#endif /* MX_I2C4 */


#if defined(MX_I2C5)
/* I2C5 Driver wrapper functions */
static int32_t I2C5_Initialize (ARM_I2C_SignalEvent_t cb_event) {
return I2C_Initialize(cb_event, &I2C5_Resources);
}
static int32_t I2C5_Uninitialize (void) {
return I2C_Uninitialize(&I2C5_Resources);
}
static int32_t I2C5_PowerControl (ARM_POWER_STATE state) {
return I2C_PowerControl(state, &I2C5_Resources);
}
static int32_t I2C5_MasterTransmit (uint32_t addr, const uint8_t *data, uint32_t num, bool xfer_pending) {
return I2C_MasterTransmit(addr, data, num, xfer_pending, &I2C5_Resources);
}
static int32_t I2C5_MasterReceive (uint32_t addr, uint8_t *data, uint32_t num, bool xfer_pending) {
return I2C_MasterReceive(addr, data, num, xfer_pending, &I2C5_Resources);
}
static int32_t I2C5_SlaveTransmit (const uint8_t *data, uint32_t num) {
return I2C_SlaveTransmit(data, num, &I2C5_Resources);
}
static int32_t I2C5_SlaveReceive (uint8_t *data, uint32_t num) {
return I2C_SlaveReceive(data, num, &I2C5_Resources);
}
static int32_t I2C5_GetDataCount (void) {
return I2C_GetDataCount(&I2C5_Resources);
}
static int32_t I2C5_Control (uint32_t control, uint32_t arg) {
return I2C_Control(control, arg, &I2C5_Resources);
}
static ARM_I2C_STATUS I2C5_GetStatus (void) {
return I2C_GetStatus(&I2C5_Resources);
}

/* I2C5 Driver Control Block */
ARM_DRIVER_I2C Driver_I2C5 = {
I2CX_GetVersion,
I2CX_GetCapabilities,
I2C5_Initialize,
I2C5_Uninitialize,
I2C5_PowerControl,
I2C5_MasterTransmit,
I2C5_MasterReceive,
I2C5_SlaveTransmit,
I2C5_SlaveReceive,
I2C5_GetDataCount,
I2C5_Control,
I2C5_GetStatus
};
#endif /* MX_I2C5 */

/*! \endcond */

qq1646544 发表于 2023-8-25 22:36:21

Rise Time 和 Fall Time的选取有什么规则吗?

eric2013 发表于 2023-8-26 01:15:01

qq1646544 发表于 2023-8-25 22:36
Rise Time 和 Fall Time的选取有什么规则吗?

手册里面有对应的范围,然后算法就是在各种参数的范围内取个值。

zhang0352505 发表于 2023-8-26 09:59:08

第一个图是什么软件,很好奇

eric2013 发表于 2023-8-26 10:17:21

zhang0352505 发表于 2023-8-26 09:59
第一个图是什么软件,很好奇

ST早期的excel计算软件,现在都在CubeMX上面计算了。

zhang0352505 发表于 2023-8-26 11:16:09

eric2013 发表于 2023-8-26 10:17
ST早期的excel计算软件,现在都在CubeMX上面计算了。

CubeMX只有数据,没有这么直观

庄永 发表于 2023-8-26 11:37:25

qq1646544 发表于 2023-8-25 22:36
Rise Time 和 Fall Time的选取有什么规则吗?

和你的通信频率有关

liandao 发表于 2023-8-26 14:25:25

eric2013 发表于 2023-8-26 10:17
ST早期的excel计算软件,现在都在CubeMX上面计算了。

cubemx的图形配置是挺好的,但是哪里有文档说明这些配置项的意义?
毕竟不知道意义是有配错的可能,难不成还得看手册?但手册里的寄存器说明和cubemx的界面的配置项之间有不一致(配置项可能是比寄存器更高抽象的)

eric2013 发表于 2023-8-27 09:27:59

liandao 发表于 2023-8-26 14:25
cubemx的图形配置是挺好的,但是哪里有文档说明这些配置项的意义?
毕竟不知道意义是有配错的可能,难不 ...

看参考手册就行,对应的硬件I2C章节有相应参数说明。

lindahnu 发表于 2024-1-9 17:29:13

找到的一个文档,另外还有H7例子里有提供算法文件。

特别不理解那个Rise Time 和 Fall Time,该怎么填?怎么算?查数据手册都不知道对应的IO AC是什么样的?
SCLH and SCLL values depend on the rise and fall time.
The rise time is defined by:
tr= Rp x Cb x 0.8473 (Rp is the pull_up resistor and Cb is the bus capacitance)
The fall time depends on the software configuration of the I/O. Please refer to “I/O AC
characteristics” table in STM32 products datasheets to get the value of fall time.

eric2013 发表于 2024-1-10 00:48:24

lindahnu 发表于 2024-1-9 17:29
找到的一个文档,另外还有H7例子里有提供算法文件。

特别不理解那个Rise Time 和 Fall Time,该怎么填? ...

谢谢分享。

lindahnu 发表于 2024-1-12 14:37:38

eric2013 发表于 2024-1-10 00:48
谢谢分享。

我帖子里的疑问硬汉能解答吗?这个上升和下降时间到底是要怎么配置的
页: [1]
查看完整版本: STM32的硬件I2C时序配置算法