Explorar el Código

[gd32][i2c] Add GD32VW553xx hardware I2C driver

yefeng hace 1 semana
padre
commit
9aff6f8646

+ 12 - 1
bsp/gd32/risc-v/gd32vw553h-eval/.ci/attachconfig/ci.attachconfig.yml

@@ -6,4 +6,15 @@ scons.args: &scons
 component.pwm:
     kconfig:
       - CONFIG_BSP_USING_PWM=y
-      - CONFIG_BSP_USING_PWM0=y
+      - CONFIG_BSP_USING_PWM0=y
+
+# ------ devices ------
+devices.i2c:
+    kconfig:
+      - CONFIG_BSP_USING_HW_I2C=y
+      - CONFIG_BSP_USING_HW_I2C0=y
+      - CONFIG_BSP_HW_I2C0_PIN_PA2_PA3=y
+      - CONFIG_BSP_HW_I2C0_CLK=100
+      - CONFIG_BSP_USING_HW_I2C1=y
+      - CONFIG_BSP_HW_I2C1_PIN_PB12_PB13=y
+      - CONFIG_BSP_HW_I2C1_CLK=100

+ 55 - 0
bsp/gd32/risc-v/gd32vw553h-eval/board/Kconfig

@@ -79,8 +79,63 @@ menu "On-chip Peripheral Drivers"
                 bool "Enable PWM16"
                 default n
         endif
+    menuconfig BSP_USING_HW_I2C
+        bool "Enable Hardware I2C"
+        default n
+        select RT_USING_I2C
+        if BSP_USING_HW_I2C
+            config BSP_USING_HW_I2C0
+                bool "Enable Hardware I2C0"
+                default n
 
+            # config i2c0 pins
+            choice
+                prompt "Select I2C0 pins"
+                depends on BSP_USING_HW_I2C0
+                config BSP_HW_I2C0_PIN_PA2_PA3
+                    bool "SCL=PA2,  SDA=PA3"
+                config BSP_HW_I2C0_PIN_PA15_PC8
+                    bool "SCL=PA15, SDA=PC8"
+                config BSP_HW_I2C0_PIN_PB0_PB1
+                    bool "SCL=PB0,  SDA=PB1"
+                config BSP_HW_I2C0_PIN_PB15_PA8
+                    bool "SCL=PB15, SDA=PA8"
+            endchoice
+
+            # config i2c0 clock
+            config BSP_HW_I2C0_CLK
+                int "I2C0 clock frequency(KHz)"
+                default 100
+                depends on BSP_USING_HW_I2C0
+                range 10 1000
+
+            config BSP_USING_HW_I2C1
+                bool "Enable Hardware I2C1"
+                default n
 
+            # config i2c1 pins
+            choice
+                prompt "Select I2C1 pins"
+                depends on BSP_USING_HW_I2C1
+                config BSP_HW_I2C1_PIN_PA6_PA7
+                    bool "SCL=PA6,  SDA=PA7"
+                config BSP_HW_I2C1_PIN_PA13_PA14
+                    bool "SCL=PA13, SDA=PA14"
+                config BSP_HW_I2C1_PIN_PA15_PC8
+                    bool "SCL=PA15, SDA=PC8"
+                config BSP_HW_I2C1_PIN_PB12_PB13
+                    bool "SCL=PB12, SDA=PB13"
+                config BSP_HW_I2C1_PIN_PB15_PA8
+                    bool "SCL=PB15, SDA=PA8"
+            endchoice
+
+            # config i2c1 clock
+            config BSP_HW_I2C1_CLK
+                int "I2C1 clock frequency(KHz)"
+                default 100
+                depends on BSP_USING_HW_I2C1
+                range 10 1000
+        endif
 
     source "$(BSP_DIR)/../libraries/gd32_drivers/Kconfig"
 

+ 3 - 0
bsp/gd32/risc-v/libraries/gd32_drivers/SConscript

@@ -20,6 +20,9 @@ if GetDepend(['RT_USING_SERIAL']):
 if GetDepend(['RT_USING_I2C', 'RT_USING_I2C_BITOPS']):
     if GetDepend('BSP_USING_I2C0') or GetDepend('BSP_USING_I2C1') or GetDepend('BSP_USING_I2C2') or GetDepend('BSP_USING_I2C3'):
         src += ['drv_soft_i2c.c']
+if GetDepend(['RT_USING_I2C', 'BSP_USING_HW_I2C']):
+    if GetDepend('BSP_USING_HW_I2C0') or GetDepend('BSP_USING_HW_I2C1'):
+        src += ['drv_i2c.c']
 
 # add spi drivers.
 if GetDepend('RT_USING_SPI'):

+ 744 - 0
bsp/gd32/risc-v/libraries/gd32_drivers/drv_i2c.c

@@ -0,0 +1,744 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2026-01-13     yefeng       first implementation for GD32VW55x
+ */
+
+#include "drv_i2c.h"
+
+#if defined(RT_USING_I2C) && defined(BSP_USING_HW_I2C)
+
+#if !defined(BSP_USING_HW_I2C0) && !defined(BSP_USING_HW_I2C1)
+#error "Please define at least one BSP_USING_HW_I2Cx"
+    /* This driver can be enabled in menuconfig:
+     * Hardware Drivers Config ->
+     *   On-chip Peripheral Drivers ->
+     *     Enable Hardware I2C
+     */
+#endif
+
+#if defined(SOC_SERIES_GD32VF103V)
+#error "not implement GD32VF103V i2c support!"
+#endif
+
+#define DBG_TAG "drv.i2c"
+#ifdef RT_I2C_DEBUG
+#define DBG_LVL DBG_LOG
+#else
+#define DBG_LVL DBG_ERROR
+#endif
+#include <rtdbg.h>
+
+#define I2C_TIME_OUT    (uint32_t)(20000)
+#define MAX_RELOAD_SIZE 255
+
+typedef enum
+{
+    I2C_PROC_START = 0,
+    I2C_PROC_READ_INIT,
+    I2C_PROC_TRANSMIT_DATA,
+    I2C_PROC_RELOAD,
+    I2C_PROC_STOP,
+    I2C_PROC_END
+} i2c_process_enum;
+
+struct gd32_pin_cfg_param
+{
+    uint32_t rcu[2];
+    uint32_t af[2];
+    uint32_t port[2];
+    uint32_t pin[2];
+};
+
+/**
+ * @brief i2c configuration params
+ *
+ */
+static struct gd32_i2c_config i2c_config[] = {
+#ifdef BSP_USING_HW_I2C0
+    {
+        "i2c0",
+        I2C0,
+        RCU_I2C0,
+        BSP_HW_I2C0_CLK,
+    },
+#endif
+#ifdef BSP_USING_HW_I2C1
+    {
+        "i2c1",
+        I2C1,
+        RCU_I2C1,
+        BSP_HW_I2C1_CLK,
+    },
+#endif
+};
+
+/**
+ * @brief i2c pin configuration param array
+ *
+ */
+static const struct gd32_pin_cfg_param pin_param[] = {
+#if defined(BSP_USING_HW_I2C0)
+    {
+#if defined(BSP_HW_I2C0_PIN_PA2_PA3)
+        .rcu = { RCU_GPIOA, RCU_GPIOA },
+        .af = { GPIO_AF_4, GPIO_AF_4 },
+        .port = { GPIOA, GPIOA },
+        .pin = { GPIO_PIN_2, GPIO_PIN_3 }
+#elif defined(BSP_HW_I2C0_PIN_PA15_PC8)
+        .rcu = { RCU_GPIOA, RCU_GPIOC },
+        .af = { GPIO_AF_4, GPIO_AF_4 },
+        .port = { GPIOA, GPIOC },
+        .pin = { GPIO_PIN_15, GPIO_PIN_8 }
+#elif defined(BSP_HW_I2C0_PIN_PB0_PB1)
+        .rcu = { RCU_GPIOB, RCU_GPIOB },
+        .af = { GPIO_AF_6, GPIO_AF_6 },
+        .port = { GPIOB, GPIOB },
+        .pin = { GPIO_PIN_0, GPIO_PIN_1 }
+#elif defined(BSP_HW_I2C0_PIN_PB15_PA8)
+        .rcu = { RCU_GPIOB, RCU_GPIOA },
+        .af = { GPIO_AF_4, GPIO_AF_5 },
+        .port = { GPIOB, GPIOA },
+        .pin = { GPIO_PIN_15, GPIO_PIN_8 }
+#else
+#error "i2c0 not support pin"
+#endif
+    },
+#endif
+
+#if defined(BSP_USING_HW_I2C1)
+    {
+#if defined(BSP_HW_I2C1_PIN_PA6_PA7)
+        .rcu = { RCU_GPIOA, RCU_GPIOA },
+        .af = { GPIO_AF_4, GPIO_AF_0 },
+        .port = { GPIOA, GPIOA },
+        .pin = { GPIO_PIN_6, GPIO_PIN_7 }
+#elif defined(BSP_HW_I2C1_PIN_PA13_PA14)
+        .rcu = { RCU_GPIOA, RCU_GPIOA },
+        .af = { GPIO_AF_6, GPIO_AF_6 },
+        .port = { GPIOA, GPIOA },
+        .pin = { GPIO_PIN_13, GPIO_PIN_14 }
+#elif defined(BSP_HW_I2C1_PIN_PA15_PC8)
+        .rcu = { RCU_GPIOA, RCU_GPIOC },
+        .af = { GPIO_AF_6, GPIO_AF_6 },
+        .port = { GPIOA, GPIOC },
+        .pin = { GPIO_PIN_15, GPIO_PIN_8 }
+#elif defined(BSP_HW_I2C1_PIN_PB12_PB13)
+        .rcu = { RCU_GPIOB, RCU_GPIOB },
+        .af = { GPIO_AF_6, GPIO_AF_6 },
+        .port = { GPIOB, GPIOB },
+        .pin = { GPIO_PIN_12, GPIO_PIN_13 }
+#elif defined(BSP_HW_I2C1_PIN_PB15_PA8)
+        .rcu = { RCU_GPIOB, RCU_GPIOA },
+        .af = { GPIO_AF_6, GPIO_AF_6 },
+        .port = { GPIOB, GPIOA },
+        .pin = { GPIO_PIN_15, GPIO_PIN_8 }
+#else
+#error "i2c1 not support pin"
+#endif
+    },
+#endif
+};
+
+/**
+ * @brief Device object
+ *
+ */
+static struct gd32_i2c_device i2c_obj[sizeof(i2c_config) / sizeof(i2c_config[0])] = { 0 };
+
+/**
+ * @brief i2c read
+ *
+ * @param device operate device
+ * @param msg deal i2c message pointer
+ * @return rt_err_t return RT_OK if success, other value failed.
+ */
+static rt_err_t gd32_i2c_read(struct gd32_i2c_device *device, struct rt_i2c_msg *msg)
+{
+    rt_err_t ret = RT_EOK;
+    i2c_process_enum state = I2C_PROC_READ_INIT;
+    rt_uint32_t timeout = 0;
+    rt_uint8_t end_flag = 0;
+    rt_uint8_t first_reload_flag = 1;
+    rt_uint8_t reload_mode_flag = 0;
+
+    RT_ASSERT(device != RT_NULL);
+    RT_ASSERT(msg != RT_NULL);
+
+    rt_uint32_t i2c_periph = device->config->i2c_periph;
+    rt_uint16_t nbytes_reload = 0;
+    rt_uint16_t number_of_byte = msg->len;
+    rt_uint8_t *p_buffer = msg->buf;
+
+    while (!end_flag)
+    {
+        switch (state)
+        {
+        case I2C_PROC_START:
+            /* start */
+            i2c_start_on_bus(i2c_periph);
+            state = I2C_PROC_TRANSMIT_DATA;
+            break;
+        case I2C_PROC_READ_INIT:
+            /* configure the device address */
+            i2c_master_addressing(i2c_periph, msg->addr << 1, I2C_MASTER_RECEIVE);
+            /* disable I2C automatic end mode in master mode */
+            i2c_automatic_end_disable(i2c_periph);
+            state = I2C_PROC_RELOAD;
+            break;
+        case I2C_PROC_RELOAD:
+            if (number_of_byte > MAX_RELOAD_SIZE)
+            {
+                number_of_byte = number_of_byte - MAX_RELOAD_SIZE;
+                nbytes_reload = MAX_RELOAD_SIZE;
+                reload_mode_flag = 1;
+            }
+            else
+            {
+                nbytes_reload = number_of_byte;
+                reload_mode_flag = 0;
+            }
+
+            LOG_D("configure number of bytes: %d", nbytes_reload);
+            /* configure number of bytes to be transferred */
+            i2c_transfer_byte_number_config(i2c_periph, nbytes_reload);
+
+            if (reload_mode_flag)
+            {
+                LOG_D("enable I2C reload mode");
+                /* enable I2C reload mode */
+                i2c_reload_enable(i2c_periph);
+            }
+            else
+            {
+                LOG_D("disable I2C reload mode");
+                /* disable I2C reload mode */
+                i2c_reload_disable(i2c_periph);
+            }
+
+            if (first_reload_flag)
+            {
+                LOG_D("first reload");
+                first_reload_flag = 0;
+                state = I2C_PROC_START;
+            }
+            else
+            {
+                state = I2C_PROC_TRANSMIT_DATA;
+            }
+            break;
+        case I2C_PROC_TRANSMIT_DATA:
+            LOG_D("read: transmit data: %d", nbytes_reload);
+            timeout = 0;
+            while (nbytes_reload)
+            {
+                /* wait until the RBNE bit is set and clear it */
+                if (i2c_flag_get(i2c_periph, I2C_FLAG_RBNE))
+                {
+                    /* read a byte from the device */
+                    *p_buffer = i2c_data_receive(i2c_periph);
+                    /* point to the next location where the byte read will be saved */
+                    p_buffer++;
+                    /* decrement the read bytes counter */
+                    nbytes_reload--;
+
+                    /* reset timeout counter */
+                    timeout = 0;
+                }
+                else
+                {
+                    timeout++;
+                    if (timeout >= I2C_TIME_OUT)
+                    {
+                        LOG_E("receive data timeout, end");
+                        ret = -RT_ETIMEOUT;
+                        return ret;
+                    }
+                }
+            }
+
+            LOG_D("read: transmit data end");
+            timeout = 0;
+
+            /* check if the reload mode is enabled or not */
+            if (I2C_CTL1(i2c_periph) & I2C_CTL1_RELOAD)
+            {
+                /* wait for TCR flag */
+                while ((!i2c_flag_get(i2c_periph, I2C_FLAG_TCR)) && (timeout < I2C_TIME_OUT))
+                {
+                    timeout++;
+                }
+
+                if (timeout < I2C_TIME_OUT)
+                {
+                    LOG_D("reload read complete");
+                    state = I2C_PROC_RELOAD;
+                }
+                else
+                {
+                    LOG_E("reload read timeout, end");
+                    ret = -RT_ETIMEOUT;
+                    state = I2C_PROC_END;
+                }
+            }
+            else
+            {
+                LOG_D("read complete");
+
+                /* wait for TCR flag */
+                while ((!i2c_flag_get(i2c_periph, I2C_FLAG_TC)) && (timeout < I2C_TIME_OUT))
+                {
+                    timeout++;
+                }
+
+                if (timeout < I2C_TIME_OUT)
+                {
+                    if (msg->flags & RT_I2C_NO_STOP)
+                    {
+                        LOG_D("no stop end");
+                        state = I2C_PROC_END;
+                    }
+                    else
+                    {
+                        LOG_D("generate a STOP");
+                        i2c_stop_on_bus(i2c_periph);
+                        state = I2C_PROC_STOP;
+                    }
+                }
+                else
+                {
+                    LOG_E("read timeout, end");
+                    ret = -RT_ETIMEOUT;
+                    state = I2C_PROC_END;
+                }
+            }
+            break;
+        case I2C_PROC_STOP:
+            /* wait until the stop condition is finished */
+            while ((!i2c_flag_get(i2c_periph, I2C_FLAG_STPDET)) && (timeout < I2C_TIME_OUT))
+            {
+                timeout++;
+            }
+
+            if (timeout < I2C_TIME_OUT)
+            {
+                /* clear STPDET flag */
+                i2c_flag_clear(i2c_periph, I2C_FLAG_STPDET);
+                timeout = 0;
+                state = I2C_PROC_END;
+            }
+            else
+            {
+                timeout = 0;
+                state = I2C_PROC_END;
+                ret = -RT_ETIMEOUT;
+                LOG_E("i2c master sends stop signal timeout in read!\n");
+            }
+            break;
+        case I2C_PROC_END:
+            end_flag = 1;
+            timeout = 0;
+            break;
+        default:
+            /* default status */
+            end_flag = 1;
+            timeout = 0;
+            LOG_E("i2c state unknown, end!\n");
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * @brief i2c write
+ *
+ * @param device operate device
+ * @param msg deal i2c message pointer
+ * @return rt_err_t return RT_OK if success, other value failed.
+ */
+static rt_err_t gd32_i2c_write(struct gd32_i2c_device *device, struct rt_i2c_msg *msg)
+{
+    rt_err_t ret = RT_EOK;
+    i2c_process_enum state = I2C_PROC_RELOAD;
+    rt_uint16_t timeout = 0;
+    rt_uint8_t start_flag = 0;
+    rt_uint8_t end_flag = 0;
+    rt_uint8_t reload_mode_flag = 0;
+
+    RT_ASSERT(device != RT_NULL);
+    RT_ASSERT(msg != RT_NULL);
+
+    rt_uint32_t i2c_periph = device->config->i2c_periph;
+    rt_uint16_t number_of_byte = msg->len;
+    rt_uint16_t nbytes_reload = 0;
+    rt_uint8_t *p_buffer = msg->buf;
+
+    if (msg->flags & RT_I2C_NO_START)
+    {
+        start_flag = 0;
+    }
+    else
+    {
+        start_flag = 1;
+    }
+
+    /* configure the device address */
+    i2c_master_addressing(i2c_periph, msg->addr << 1, I2C_MASTER_TRANSMIT);
+    /* disable I2C automatic end mode in master mode */
+    i2c_automatic_end_disable(i2c_periph);
+
+    while (!end_flag)
+    {
+        switch (state)
+        {
+        case I2C_PROC_START:
+            /* start */
+            LOG_D("generate a START");
+            timeout = 0;
+            i2c_start_on_bus(i2c_periph);
+            state = I2C_PROC_TRANSMIT_DATA;
+            break;
+        case I2C_PROC_RELOAD:
+            if (number_of_byte > MAX_RELOAD_SIZE)
+            {
+                number_of_byte = number_of_byte - MAX_RELOAD_SIZE;
+                nbytes_reload = MAX_RELOAD_SIZE;
+                reload_mode_flag = 1;
+            }
+            else
+            {
+                nbytes_reload = number_of_byte;
+                reload_mode_flag = 0;
+            }
+
+            LOG_D("configure number of bytes: %d", nbytes_reload);
+
+            /* configure number of bytes to be transferred */
+            i2c_transfer_byte_number_config(i2c_periph, nbytes_reload);
+
+            if (reload_mode_flag)
+            {
+                LOG_D("enable I2C reload mode");
+                /* enable I2C reload mode */
+                i2c_reload_enable(i2c_periph);
+            }
+            else
+            {
+                LOG_D("disable I2C reload mode");
+                /* disable I2C reload mode */
+                i2c_reload_disable(i2c_periph);
+            }
+
+            if (start_flag)
+            {
+                start_flag = 0;
+                state = I2C_PROC_START;
+            }
+            else
+            {
+                state = I2C_PROC_TRANSMIT_DATA;
+            }
+            break;
+        case I2C_PROC_TRANSMIT_DATA:
+            timeout = 0;
+            LOG_D("master transmit bytes: %d", nbytes_reload);
+            while (nbytes_reload--)
+            {
+                /* wait until TI bit is set */
+                while ((!i2c_flag_get(i2c_periph, I2C_FLAG_TI)) && (timeout < I2C_TIME_OUT))
+                {
+                    timeout++;
+                }
+
+                if (timeout < I2C_TIME_OUT)
+                {
+                    /* while there is data to be written */
+                    i2c_data_transmit(i2c_periph, *p_buffer);
+                    /* point to the next byte to be written */
+                    p_buffer++;
+                    timeout = 0;
+                }
+                else
+                {
+                    /* wait TI timeout */
+                    LOG_E("i2c master sends data timeout when write!");
+                    ret = -RT_ETIMEOUT;
+                    return ret;
+                }
+            }
+
+            if (timeout < I2C_TIME_OUT)
+            {
+                rt_uint32_t wait_flag;
+
+                /* check if the reload mode is enabled or not */
+                if (I2C_CTL1(i2c_periph) & I2C_CTL1_RELOAD)
+                {
+                    wait_flag = I2C_FLAG_TCR;
+                }
+                else
+                {
+                    wait_flag = I2C_FLAG_TC;
+                }
+
+                timeout = 0;
+
+                /* wait TCR/TC bit is set */
+                while ((!i2c_flag_get(i2c_periph, wait_flag)) && (timeout < I2C_TIME_OUT))
+                {
+                    timeout++;
+                }
+
+                if (timeout < I2C_TIME_OUT)
+                {
+                    if (reload_mode_flag)
+                    {
+                        state = I2C_PROC_RELOAD;
+                    }
+                    else
+                    {
+                        if (msg->flags & RT_I2C_NO_STOP)
+                        {
+                            state = I2C_PROC_END;
+                        }
+                        else
+                        {
+                            LOG_D("generate a STOP");
+                            i2c_stop_on_bus(i2c_periph);
+                            state = I2C_PROC_STOP;
+                        }
+                    }
+                }
+                else
+                {
+                    LOG_E("i2c master wait %s timeout, end!", (wait_flag == I2C_FLAG_TCR) ? "TCR" : "TC");
+                    state = I2C_PROC_END;
+                    ret = -RT_ETIMEOUT;
+                }
+            }
+            else
+            {
+                LOG_E("i2c master send data timeout when write!");
+                state = I2C_PROC_END;
+                ret = -RT_ETIMEOUT;
+            }
+            break;
+        case I2C_PROC_STOP:
+            timeout = 0;
+            /* wait until the stop condition is finished */
+            while ((!i2c_flag_get(i2c_periph, I2C_FLAG_STPDET)) && (timeout < I2C_TIME_OUT))
+            {
+                timeout++;
+            }
+
+            if (timeout < I2C_TIME_OUT)
+            {
+                /* clear STPDET flag */
+                i2c_flag_clear(i2c_periph, I2C_FLAG_STPDET);
+            }
+            else
+            {
+                /* stop detect timeout */
+                LOG_E("i2c master sends stop signal timeout when write!");
+                ret = -RT_ETIMEOUT;
+            }
+
+            state = I2C_PROC_END;
+            break;
+        case I2C_PROC_END:
+            LOG_D("END");
+            end_flag = 1;
+            break;
+        default:
+            /* default status */
+            state = I2C_PROC_START;
+            end_flag = 1;
+            timeout = 0;
+            LOG_E("i2c master unknown state, end!\n");
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * @brief configure i2c alternate pin
+ *
+ * @param param configure param
+ */
+static void gd32_i2c_pin_configure(const struct gd32_pin_cfg_param *param)
+{
+    for (rt_uint8_t i = 0; i < 2; i++)
+    {
+        /* enable GPIO clock */
+        rcu_periph_clock_enable(param->rcu[i]);
+
+        /* connect I2C_SCL_PIN to I2C_SCL */
+        gpio_af_set(param->port[i], param->af[i], param->pin[i]);
+
+        /* configure GPIO pins of I2C */
+        gpio_mode_set(param->port[i], GPIO_MODE_AF, GPIO_PUPD_PULLUP, param->pin[i]);
+        gpio_output_options_set(param->port[i], GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, param->pin[i]);
+    }
+}
+
+/**
+ * @brief configure i2c timing
+ *
+ * @param i2c_cfg param pointer
+ *
+ * @note tSCL=tSYNC1+tSYNC2+{[(SCLH[7:0]+1)+(SLLL[7:0]+1)]*(PSC+1)*tI2CCLK}
+ */
+static void gd32_i2c_timing_configure(struct gd32_i2c_config *i2c_cfg)
+{
+#define I2C_CLK_PSC_LOW   15
+#define I2C_CLK_PSC_STAND 7
+#define I2C_CLK_PSC_FAST  1
+#define I2C_SCLDELY_LOW   9
+#define I2C_SCLDELY_FAST  3
+#define I2C_SDADELY       0
+
+    rt_uint32_t i2c_freq;
+    rt_uint32_t sclh_scll;
+    rt_uint8_t clk_psc;
+    rt_uint8_t scldely;
+
+    if (i2c_cfg->i2c_periph == I2C0)
+    {
+        i2c_freq = rcu_clock_freq_get(CK_I2C0);
+    }
+    else if (i2c_cfg->i2c_periph == I2C1)
+    {
+        /* I2C1 use APB1 clock */
+        i2c_freq = rcu_clock_freq_get(CK_APB1);
+    }
+    else
+    {
+        i2c_freq = rcu_clock_freq_get(CK_APB1);
+    }
+
+    if (i2c_cfg->speed < 100)
+    {
+        clk_psc = I2C_CLK_PSC_LOW;
+        scldely = I2C_SCLDELY_LOW;
+    }
+    else if (i2c_cfg->speed <= 400)
+    {
+        clk_psc = I2C_CLK_PSC_STAND;
+        scldely = I2C_SCLDELY_LOW;
+    }
+    else
+    {
+        clk_psc = I2C_CLK_PSC_STAND;
+        scldely = I2C_SCLDELY_FAST;
+    }
+
+    /* configure I2C timing */
+    i2c_timing_config(i2c_cfg->i2c_periph, clk_psc, scldely, I2C_SDADELY);
+
+    sclh_scll = (i2c_freq / (1000 * i2c_cfg->speed) - (I2C_SDADELY * (clk_psc + 1) + 1) - (scldely + 1) * (clk_psc + 1)) / (clk_psc + 1) - 2;
+
+    i2c_master_clock_config(i2c_cfg->i2c_periph, sclh_scll / 2, sclh_scll / 2);
+}
+
+/**
+ * @brief configure i2c clock, timing, enable i2c
+ *
+ * @param i2c_cfg
+ */
+static void gd32_i2c_configure(struct gd32_i2c_config *i2c_cfg)
+{
+    RT_ASSERT(i2c_cfg != RT_NULL);
+
+    /* enable i2c clock */
+    rcu_periph_clock_enable(i2c_cfg->rcu_clk);
+
+    /* reset I2C */
+    i2c_disable(i2c_cfg->i2c_periph);
+
+    /* i2c timing config MUST at I2CEN = 0 */
+    gd32_i2c_timing_configure(i2c_cfg);
+
+    /* enable I2C */
+    i2c_enable(i2c_cfg->i2c_periph);
+}
+
+static rt_ssize_t gd32_i2c_xfer(struct rt_i2c_bus_device *device, struct rt_i2c_msg msgs[], rt_uint32_t num)
+{
+    struct rt_i2c_msg *msg;
+    rt_uint32_t i;
+    rt_uint16_t last_flags;
+
+    RT_ASSERT(device != RT_NULL);
+
+    struct gd32_i2c_device *i2c_obj = (struct gd32_i2c_device *)(device);
+    struct gd32_i2c_config *i2c_cfg = (struct gd32_i2c_config *)(i2c_obj->config);
+
+    RT_ASSERT(i2c_cfg != RT_NULL);
+
+    for (i = 0; i < num; i++)
+    {
+        rt_err_t ret;
+        msg = &msgs[i];
+
+        if (msg->flags & RT_I2C_ADDR_10BIT)
+        {
+            LOG_E("not implement 10bit address mode");
+            return -RT_ENOSYS;
+        }
+
+        if (msg->flags & RT_I2C_RD)
+        {
+            ret = gd32_i2c_read(i2c_obj, msg);
+        }
+        else
+        {
+            ret = gd32_i2c_write(i2c_obj, msg);
+        }
+
+        if (ret != RT_EOK)
+        {
+            break;
+        }
+    }
+
+    return i;
+}
+
+static const struct rt_i2c_bus_device_ops i2c_ops = { gd32_i2c_xfer, RT_NULL, RT_NULL };
+
+static int rt_hw_i2c_init(void)
+{
+    rt_uint8_t index;
+    rt_size_t obj_num;
+    rt_err_t result = 0;
+
+    obj_num = sizeof(i2c_obj) / sizeof(struct gd32_i2c_device);
+
+    for (index = 0; index < obj_num; index++)
+    {
+        /* init i2c object */
+        i2c_obj[index].config = &i2c_config[index];
+        i2c_obj[index].parent.ops = &i2c_ops;
+
+        /* init i2c pin */
+        gd32_i2c_pin_configure(&pin_param[index]);
+
+        /* init i2c device */
+        gd32_i2c_configure(&i2c_config[index]);
+
+        /* register i2c device */
+        result = rt_i2c_bus_device_register(&i2c_obj[index].parent, i2c_obj[index].config->name);
+        RT_ASSERT(result == RT_EOK);
+    }
+
+    return 0;
+}
+INIT_DEVICE_EXPORT(rt_hw_i2c_init);
+
+#endif /* defined(RT_USING_I2C) && defined(BSP_USING_HW_I2C) */

+ 33 - 0
bsp/gd32/risc-v/libraries/gd32_drivers/drv_i2c.h

@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2026-01-13     yefeng       first implementation.
+ */
+
+#ifndef __DRV_I2C_H__
+#define __DRV_I2C_H__
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <rthw.h>
+#include <board.h>
+
+struct gd32_i2c_config
+{
+    const char *name;
+    rt_uint32_t i2c_periph;
+    rcu_periph_enum rcu_clk;
+    rt_uint32_t speed;
+};
+
+struct gd32_i2c_device
+{
+    struct rt_i2c_bus_device parent;
+    struct gd32_i2c_config *config;
+};
+
+#endif