Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ extern "C" {
#define I2C1_BUS_CONFIG \
{ \
.Instance = I2C1, \
.timing = 100000, \
.timeout=0x1000, \
.name = "hwi2c1", \
.evirq_type = I2C1_EV_IRQn, \
Expand Down Expand Up @@ -57,6 +58,7 @@ extern "C" {
#define I2C2_BUS_CONFIG \
{ \
.Instance = I2C2, \
.timing = 100000, \
.timeout=0x1000, \
.name = "hwi2c2", \
.evirq_type = I2C2_EV_IRQn, \
Expand Down Expand Up @@ -92,6 +94,7 @@ extern "C" {
#define I2C3_BUS_CONFIG \
{ \
.Instance = I2C3, \
.timing = 100000, \
.timeout=0x1000, \
.name = "hwi2c3", \
.evirq_type = I2C3_EV_IRQn, \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ extern "C" {
#define I2C1_BUS_CONFIG \
{ \
.Instance = I2C1, \
.timing=0x10707DBC, \
.timing = 100000, \
.timeout=0x1000, \
.name = "hwi2c1", \
.evirq_type = I2C1_EV_IRQn, \
Expand Down Expand Up @@ -81,7 +81,7 @@ extern "C" {
#define I2C2_BUS_CONFIG \
{ \
.Instance = I2C2, \
.timing=0x10707DBC, \
.timing = 100000, \
.timeout=0x1000, \
.name = "hwi2c2", \
.evirq_type = I2C2_EV_IRQn, \
Expand Down Expand Up @@ -139,7 +139,7 @@ extern "C" {
#define I2C3_BUS_CONFIG \
{ \
.Instance = I2C3, \
.timing=0x10707DBC, \
.timing = 100000, \
.timeout=0x1000, \
.name = "hwi2c3", \
.evirq_type = I2C3_EV_IRQn, \
Expand Down Expand Up @@ -197,7 +197,7 @@ extern "C" {
#define I2C4_BUS_CONFIG \
{ \
.Instance = I2C4, \
.timing = 0x10707DBC, \
.timing = 100000, \
.timeout = 0x1000, \
.name = "hwi2c4", \
.evirq_type = I2C4_EV_IRQn, \
Expand Down
134 changes: 101 additions & 33 deletions bsp/stm32/libraries/HAL_Drivers/drivers/drv_hard_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* 2024-04-23 Zeidan fix bugs, test on STM32F429IGTx
* 2024-12-10 zzk597 add support for STM32F1 series
* 2024-06-23 wdfk-prog Add blocking modes and distinguish POLL,INT,DMA modes
* 2024-06-23 wdfk-prog Distinguish STM32 I2C timing semantics by IP generation
*/
Comment thread
wdfk-prog marked this conversation as resolved.

#include "drv_hard_i2c.h"
Expand All @@ -19,6 +20,18 @@
#define LOG_TAG "drv.i2c.hw"
#include <drv_log.h>

/*
* Default timing values follow the IP generation split declared in
* drv_hard_i2c.h:
* - legacy IP (F1/F4): timing is the bus speed in Hz
* - TIMINGR IP (F7/H7): timing is the raw I2C_TIMINGR value
*/
#if defined(STM32_I2C_TIMINGR_IP)
#define DEFAULT_I2C_TIMING_VALUE 0x307075B1U // 100K
#else
#define DEFAULT_I2C_TIMING_VALUE (100*1000U) //HZ
#endif

/* only buffer length >= DMA_TRANS_MIN_LEN will use DMA mode */
#define DMA_TRANS_MIN_LEN 2

Expand Down Expand Up @@ -56,6 +69,16 @@ static struct stm32_i2c_config i2c_config[] =

static struct stm32_i2c i2c_objs[sizeof(i2c_config) / sizeof(i2c_config[0])] = {0};

static void stm32_i2c_apply_default_config(struct stm32_i2c_config *cfg)
{
RT_ASSERT(cfg != RT_NULL);

if (cfg->timing == 0U)
{
cfg->timing = DEFAULT_I2C_TIMING_VALUE;
}
}

static rt_err_t stm32_i2c_init(struct stm32_i2c *i2c_drv)
{
RT_ASSERT(i2c_drv != RT_NULL);
Expand All @@ -64,14 +87,16 @@ static rt_err_t stm32_i2c_init(struct stm32_i2c *i2c_drv)
struct stm32_i2c_config *cfg = i2c_drv->config;

rt_memset(i2c_handle, 0, sizeof(I2C_HandleTypeDef));
stm32_i2c_apply_default_config(cfg);

i2c_handle->Instance = cfg->Instance;
#if defined(SOC_SERIES_STM32H7)
#if defined(STM32_I2C_TIMINGR_IP)
i2c_handle->Init.Timing = cfg->timing;
i2c_handle->Init.OwnAddress2Masks = I2C_OA2_NOMASK;
#else
i2c_handle->Init.ClockSpeed = 100000;
i2c_handle->Init.ClockSpeed = cfg->timing;
i2c_handle->Init.DutyCycle = I2C_DUTYCYCLE_2;
#endif /* defined(SOC_SERIES_STM32H7) */
#endif /* defined(STM32_I2C_TIMINGR_IP) */
i2c_handle->Init.OwnAddress1 = 0;
i2c_handle->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
i2c_handle->Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
Expand All @@ -87,7 +112,7 @@ static rt_err_t stm32_i2c_init(struct stm32_i2c *i2c_drv)
{
return -RT_EFAULT;
}
#if defined(SOC_SERIES_STM32H7)
#if defined(STM32_I2C_TIMINGR_IP)
/* Configure Analogue filter */
if (HAL_I2CEx_ConfigAnalogFilter(i2c_handle, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Expand All @@ -99,7 +124,7 @@ static rt_err_t stm32_i2c_init(struct stm32_i2c *i2c_drv)
{
return -RT_EFAULT;
}
#endif /* defined(SOC_SERIES_STM32H7) */
#endif /* defined(STM32_I2C_TIMINGR_IP) */
#if defined(BSP_I2C_RX_USING_DMA)
/* I2C2 DMA Init */
if (i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_DMA_RX)
Expand Down Expand Up @@ -325,6 +350,10 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg msgs[],
rt_uint32_t num)
{
/* timeout = data_time + dev_addr_time + reserve_time */
#define TIMEOUT_FREQ_KHZ(freq) (((freq) / 1000U) ? ((freq) / 1000U) : 1U)
#define TIMEOUT_CALC(msg) (((msg)->len * 8U) / TIMEOUT_FREQ_KHZ(bus->config.usage_freq) + 1U + 5U)

Comment thread
wdfk-prog marked this conversation as resolved.
rt_uint32_t i = 0;
rt_int32_t ret = 0;
struct rt_i2c_msg *msg = RT_NULL;
Expand All @@ -348,13 +377,6 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus,
completion = &i2c_obj->completion;
#endif /* defined(BSP_I2C_USING_IRQ) */
LOG_D("xfer start %d megs", num);

rt_uint32_t freq_khz = bus->config.usage_freq / 1000;
if (freq_khz == 0)
{
freq_khz = 1;
}

for (i = 0; i < num; i++)
{
rt_bool_t need_wait = RT_FALSE;
Expand All @@ -363,10 +385,11 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus,
next_msg = is_last ? RT_NULL : &msgs[i + 1];
mode = stm32_i2c_get_xfer_mode(i, msg, next_msg, is_last);
LOG_D("xfer msgs[%d] addr=0x%2x buf=0x%x len= 0x%x flags= 0x%x", i, msg->addr, msg->buf, msg->len, msg->flags);

// timeout= data_time + dev_addr_time + reserve_time
timeout = (msg->len * 8) / freq_khz + 1 + 5;

#if defined(STM32_I2C_TIMINGR_IP)
timeout = bus->timeout ? bus->timeout : 100U;
#else
timeout = TIMEOUT_CALC(msg);
#endif
if (msg->flags & RT_I2C_RD)
{
LOG_D("xfer rec msgs[%d] hal mode = %s", i, stm32_i2c_mode_name(mode));
Expand Down Expand Up @@ -421,38 +444,78 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus,
out:
ret = i;
/*
* On STM32H7, STOPI only enables STOP-event interrupt handling.
* On TIMINGR-based STM32 I2C IPs (currently F7/H7 in this driver),
* STOPI only enables STOP-event interrupt handling.
* It does not actively generate a STOP condition on the bus.
*
* For non-H7 STM32 series, the legacy HAL error handler already
* generates a STOP condition on AF in master/memory modes, so
* this driver does not manually issue another STOP in the AF path.
* For legacy STM32 I2C IPs, the HAL error handler already generates a
* STOP condition on AF in master/memory modes, so this driver does not
* manually issue another STOP in the AF path.
*/
if (handle->ErrorCode & HAL_I2C_ERROR_AF)
{
LOG_W("I2C[%s] NACK Error", bus->parent.parent.name);
#if defined(SOC_SERIES_STM32H7)
#if defined(STM32_I2C_TIMINGR_IP)
handle->Instance->CR1 |= I2C_IT_STOPI;
#endif /* defined(SOC_SERIES_STM32H7) */
#endif /* defined(STM32_I2C_TIMINGR_IP) */
}
if (handle->ErrorCode & HAL_I2C_ERROR_BERR)
{
LOG_W("I2C[%s] BUS Error", bus->parent.parent.name);
#if defined(SOC_SERIES_STM32H7)
#if defined(STM32_I2C_TIMINGR_IP)
handle->Instance->CR1 |= I2C_IT_STOPI;
#else
handle->Instance->CR1 |= I2C_CR1_STOP;
#endif /* defined(SOC_SERIES_STM32H7) */
#endif /* defined(STM32_I2C_TIMINGR_IP) */
}

return ret;
#undef TIMEOUT_FREQ_KHZ
#undef TIMEOUT_CALC
}

rt_err_t stm_i2c_bus_control(struct rt_i2c_bus_device *bus, int cmd, void *args)
{
struct stm32_i2c *i2c_obj;
RT_ASSERT(bus != RT_NULL);

i2c_obj = rt_container_of(bus, struct stm32_i2c, i2c_bus);
RT_ASSERT(i2c_obj != RT_NULL);

switch (cmd)
{
case BSP_I2C_CTRL_SET_TIMING:
{
if (args == RT_NULL)
{
return -RT_EINVAL;
}

rt_uint32_t timing = *(rt_uint32_t *)args;
if(timing <= 0)
{
return -RT_ERROR;
}

i2c_obj->i2c_bus.config.usage_freq = i2c_obj->config->timing = timing;
return stm32_i2c_configure(&i2c_obj->i2c_bus);
break;
}
Comment thread
wdfk-prog marked this conversation as resolved.
default:
{
break;
}
}
return -RT_EINVAL;
}

static const struct rt_i2c_bus_device_ops stm32_i2c_ops =
{
.master_xfer = stm32_i2c_master_xfer,
RT_NULL,
RT_NULL,
#if defined(STM32_I2C_LEGACY_IP)
.i2c_bus_control = stm_i2c_bus_control,
#endif
.slave_xfer = RT_NULL,
};

int RT_hw_i2c_bus_init(void)
Expand All @@ -465,6 +528,12 @@ int RT_hw_i2c_bus_init(void)
i2c_objs[i].i2c_bus.ops = &stm32_i2c_ops;
i2c_objs[i].config = &i2c_config[i];
i2c_objs[i].i2c_bus.timeout = i2c_config[i].timeout;
stm32_i2c_apply_default_config(&i2c_config[i]);
#if defined(STM32_I2C_LEGACY_IP)
i2c_objs[i].i2c_bus.config.max_hz = i2c_config[i].timing;
i2c_objs[i].i2c_bus.config.usage_freq = i2c_config[i].timing;
#endif

#ifdef BSP_I2C_USING_DMA
if ((i2c_objs[i].i2c_dma_flag & RT_DEVICE_FLAG_DMA_RX))
{
Expand All @@ -485,9 +554,9 @@ int RT_hw_i2c_bus_init(void)
#endif
#if defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32H7)
i2c_objs[i].dma.handle_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
i2c_objs[i].dma.handle_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
i2c_objs[i].dma.handle_tx.Init.MemBurst = DMA_MBURST_INC4;
i2c_objs[i].dma.handle_tx.Init.PeriphBurst = DMA_PBURST_INC4;
i2c_objs[i].dma.handle_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
i2c_objs[i].dma.handle_rx.Init.MemBurst = DMA_MBURST_INC4;
i2c_objs[i].dma.handle_rx.Init.PeriphBurst = DMA_PBURST_INC4;
#endif /* defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32H7) */
{
rt_uint32_t tmpreg = 0x00U;
Expand All @@ -508,7 +577,6 @@ int RT_hw_i2c_bus_init(void)
}
}

#ifdef BSP_I2C_USING_DMA
if (i2c_objs[i].i2c_dma_flag & RT_DEVICE_FLAG_DMA_TX)
{
i2c_objs[i].dma.handle_tx.Instance = i2c_config[i].dma_tx->Instance;
Expand All @@ -525,7 +593,7 @@ int RT_hw_i2c_bus_init(void)
i2c_objs[i].dma.handle_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
i2c_objs[i].dma.handle_tx.Init.Mode = DMA_NORMAL;
i2c_objs[i].dma.handle_tx.Init.Priority = DMA_PRIORITY_LOW;
#endif
#endif /* SOC_SERIES_STM32U5 */
#if defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32H7)

i2c_objs[i].dma.handle_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
Expand Down Expand Up @@ -685,7 +753,7 @@ void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
#endif /* I2C4 */
: "unknown",
hi2c->ErrorCode);
#if defined(SOC_SERIES_STM32H7)
#if defined(STM32_I2C_TIMINGR_IP)
/* Send stop signal to prevent bus lock-up */
if (hi2c->ErrorCode == HAL_I2C_ERROR_AF)
{
Expand All @@ -697,7 +765,7 @@ void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
LOG_W("I2C BUS Error now stoped");
hi2c->Instance->CR1 |= I2C_IT_STOPI;
}
#endif /* defined(SOC_SERIES_STM32H7) */
#endif /* defined(STM32_I2C_TIMINGR_IP) */
}

#ifdef BSP_USING_HARD_I2C1
Expand Down
34 changes: 32 additions & 2 deletions bsp/stm32/libraries/HAL_Drivers/drivers/drv_hard_i2c.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
Comment thread
wdfk-prog marked this conversation as resolved.
* Change Logs:
* Date Author Notes
* 2024-02-17 Dyyt587 first version
* 2024-06-23 wdfk-prog Add mode selection scaffolding
* 2024-06-23 wdfk-prog Distinguish STM32 I2C timing semantics by IP generation
*/

#ifndef __DRV_HARD_I2C_H__
Expand Down Expand Up @@ -97,10 +98,39 @@ extern "C"
#define BSP_I2C_USING_IRQ
#endif

/*
* timing semantics are intentionally split by I2C IP generation:
*
* - legacy I2C IP:
* use bus frequency in Hz (for example 100000/400000), because HAL
* configures the peripheral through ClockSpeed/DutyCycle.
*
* - TIMINGR-based I2C IP:
* keep using the raw TIMINGR register value for now. Exact timing
* generation depends on board-level inputs such as kernel clock,
* rise/fall time, digital/analog filter settings, and target bus mode,
* so this change does not convert TIMINGR platforms to a unified Hz API.
*/
#if defined(SOC_SERIES_STM32F1) \
|| defined(SOC_SERIES_STM32F2) \
|| defined(SOC_SERIES_STM32F4) \
|| defined(SOC_SERIES_STM32L1)
#define STM32_I2C_LEGACY_IP
#else
#define STM32_I2C_TIMINGR_IP
#endif
Comment thread
wdfk-prog marked this conversation as resolved.

#define BSP_I2C_CTRL_SET_TIMING 0x40

struct stm32_i2c_config
{
const char *name;
I2C_TypeDef *Instance;
/*
* timing semantics depend on the I2C IP generation:
* - legacy IP (F1/F4): bus speed in Hz
* - TIMINGR IP (F7/H7): raw TIMINGR register value
*/
rt_uint32_t timing;
rt_uint32_t timeout;
IRQn_Type evirq_type;
Expand Down Expand Up @@ -139,4 +169,4 @@ struct stm32_i2c
}
#endif

#endif /* __DRV_I2C_H__ */
#endif /* __DRV_HARD_I2C_H__ */
Loading