diff --git a/bsp/gd32/arm/gd32e230-lckfb/.ci/attachconfig/ci.attachconfig.yml b/bsp/gd32/arm/gd32e230-lckfb/.ci/attachconfig/ci.attachconfig.yml index 506885b13f7..216b8f01901 100644 --- a/bsp/gd32/arm/gd32e230-lckfb/.ci/attachconfig/ci.attachconfig.yml +++ b/bsp/gd32/arm/gd32e230-lckfb/.ci/attachconfig/ci.attachconfig.yml @@ -22,4 +22,9 @@ hw_drv_onchip.i2c0: kconfig: - CONFIG_BSP_USING_I2C0=y - CONFIG_BSP_I2C0_SCL_PIN=22 - - CONFIG_BSP_I2C0_SDA_PIN=23 \ No newline at end of file + - CONFIG_BSP_I2C0_SDA_PIN=23 +hw_drv_onchip.i2c1: + kconfig: + - CONFIG_BSP_USING_HW_I2C=y + - CONFIG_BSP_USING_HW_I2C1=y + - CONFIG_BSP_HW_I2C1_CLOCK_SPEED=400000 \ No newline at end of file diff --git a/bsp/gd32/arm/gd32e230-lckfb/.config b/bsp/gd32/arm/gd32e230-lckfb/.config index 528af6c48f2..2c028281353 100644 --- a/bsp/gd32/arm/gd32e230-lckfb/.config +++ b/bsp/gd32/arm/gd32e230-lckfb/.config @@ -108,7 +108,7 @@ # CONFIG_RT_UTEST_TC_USING_KLIBC is not set # end of klibc options -CONFIG_RT_NAME_MAX=8 +CONFIG_RT_NAME_MAX=20 # CONFIG_RT_USING_ARCH_DATA_TYPE is not set # CONFIG_RT_USING_NANO is not set # CONFIG_RT_USING_SMART is not set diff --git a/bsp/gd32/arm/gd32e230-lckfb/README.md b/bsp/gd32/arm/gd32e230-lckfb/README.md index 9c5164d67f0..1ea8ead0842 100644 --- a/bsp/gd32/arm/gd32e230-lckfb/README.md +++ b/bsp/gd32/arm/gd32e230-lckfb/README.md @@ -36,9 +36,18 @@ | :--- | :---: | :--- | | GPIO | 支持 | PAx, PBx... | | UART | 支持 | USART0, USART1 | +| I2C | 支持 | I2C0, I2C1 硬件I2C | | **扩展模块** | **支持情况** | **备注** | | 暂无 | 暂不支持 | 暂不支持 | +### 补充说明 + +I2C 引脚分配: +- **I2C0**: SCL(PB6), SDA(PB7) +- **I2C1**: SCL(PB10), SDA(PB11) +默认速率400KHz,可以配置调整。 + + ## 使用说明 使用说明分为如下两个章节: @@ -95,7 +104,7 @@ pyocd flash -t gd32e230c8t6 rtthread.bin 4. 输入 `scons -j12` 编译。 ## 注意事项 -1. GD32E230资源受限,虽然通过调整参数极限运行msh,但是意义不大,建议关闭。 +1. GD32E230资源受限,虽然通过调整参数可以运行msh,但是意义不大,建议关闭。 2. **GPIOA-13** 和 **GPIOA-14** 默认用于 SWD 调试下载功能,建议不要配置为其它功能,否则将可能导致无法正常下载和调试。 ## 联系人信息 diff --git a/bsp/gd32/arm/gd32e230-lckfb/board/Kconfig b/bsp/gd32/arm/gd32e230-lckfb/board/Kconfig index d8a53a95625..a1a63c5981e 100644 --- a/bsp/gd32/arm/gd32e230-lckfb/board/Kconfig +++ b/bsp/gd32/arm/gd32e230-lckfb/board/Kconfig @@ -146,13 +146,35 @@ menu "On-chip Peripheral Drivers" select RT_USING_I2C default n if BSP_USING_HW_I2C - config BSP_USING_I2C0 + config BSP_USING_HW_I2C0 bool "Enable I2C0" default y - config BSP_USING_I2C1 + config BSP_HW_I2C0_CLOCK_SPEED + int "I2C0 Clock Speed (Hz)" + depends on BSP_USING_HW_I2C0 + range 100000 1000000 + default 400000 + help + Set the I2C0 clock speed in Hz. + Standard mode: 100000 (100KHz) + Fast mode: 400000 (400KHz) + Fast mode plus: 1000000 (1MHz) + + config BSP_USING_HW_I2C1 bool "Enable I2C1" default n + + config BSP_HW_I2C1_CLOCK_SPEED + int "I2C1 Clock Speed (Hz)" + depends on BSP_USING_HW_I2C1 + range 100000 1000000 + default 400000 + help + Set the I2C1 clock speed in Hz. + Standard mode: 100000 (100KHz) + Fast mode: 400000 (400KHz) + Fast mode plus: 1000000 (1MHz) endif menuconfig BSP_USING_I2C0 bool "Enable I2C0 BUS (software simulation)" diff --git a/bsp/gd32/arm/gd32e230-lckfb/board/linker_scripts/link.ld b/bsp/gd32/arm/gd32e230-lckfb/board/linker_scripts/link.ld index bbe40cb864e..d0e152f4047 100644 --- a/bsp/gd32/arm/gd32e230-lckfb/board/linker_scripts/link.ld +++ b/bsp/gd32/arm/gd32e230-lckfb/board/linker_scripts/link.ld @@ -129,10 +129,6 @@ SECTIONS /* Discard all debugging and comment sections using the correct wildcard syntax */ *(.comment) - *(.debug*) - *(.line) - *(.stab) - *(.stabstr) *(.note.gnu.build-id) *(.ARM.attributes) } diff --git a/bsp/gd32/arm/gd32e230-lckfb/rtconfig.h b/bsp/gd32/arm/gd32e230-lckfb/rtconfig.h index 9fc76ea3276..d2594f4e675 100644 --- a/bsp/gd32/arm/gd32e230-lckfb/rtconfig.h +++ b/bsp/gd32/arm/gd32e230-lckfb/rtconfig.h @@ -61,7 +61,7 @@ /* end of rt_strnlen options */ /* end of klibc options */ -#define RT_NAME_MAX 8 +#define RT_NAME_MAX 20 #define RT_CPUS_NR 1 #define RT_ALIGN_SIZE 8 #define RT_THREAD_PRIORITY_32 diff --git a/bsp/gd32/arm/gd32e230-lckfb/rtconfig.py b/bsp/gd32/arm/gd32e230-lckfb/rtconfig.py index e9949303c89..b532a18cca2 100644 --- a/bsp/gd32/arm/gd32e230-lckfb/rtconfig.py +++ b/bsp/gd32/arm/gd32e230-lckfb/rtconfig.py @@ -28,7 +28,7 @@ if os.getenv('RTT_EXEC_PATH'): EXEC_PATH = os.getenv('RTT_EXEC_PATH') -BUILD = 'release' +BUILD = 'debug' if PLATFORM == 'gcc': # toolchains diff --git a/bsp/gd32/arm/libraries/gd32_drivers/SConscript b/bsp/gd32/arm/libraries/gd32_drivers/SConscript index cb2193d0f16..dca1ac329f4 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/SConscript +++ b/bsp/gd32/arm/libraries/gd32_drivers/SConscript @@ -23,6 +23,11 @@ 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'] + + # add i2c hard drivers. +if GetDepend(['RT_USING_I2C']): + if GetDepend('BSP_USING_HW_I2C0') or GetDepend('BSP_USING_HW_I2C1'): + src += ['drv_hw_i2c.c'] # add spi drivers. if GetDepend('RT_USING_SPI'): diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_hw_i2c.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_hw_i2c.c new file mode 100644 index 00000000000..03269763edf --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_hw_i2c.c @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#include "drv_hw_i2c.h" + + +#ifdef RT_USING_I2C + +#define LOG_TAG "drv.i2c" +#include + +static const struct gd32_i2c_config i2c_configs[] = +{ +#ifdef BSP_USING_HW_I2C0 + { + .i2c_periph = I2C0, .device_name = "i2c0", .periph_clk = RCU_I2C0, .i2c_clock_hz = BSP_HW_I2C0_CLOCK_SPEED, + .scl_clk = RCU_GPIOB, .scl_port = GPIOB, .scl_pin = GPIO_PIN_6, .scl_af = GPIO_AF_1, + .sda_clk = RCU_GPIOB, .sda_port = GPIOB, .sda_pin = GPIO_PIN_7, .sda_af = GPIO_AF_1, + .ev_irq_type = I2C0_EV_IRQn, .er_irq_type = I2C0_ER_IRQn, + }, +#endif +#ifdef BSP_USING_HW_I2C1 + { + .i2c_periph = I2C1, .device_name = "i2c1", .periph_clk = RCU_I2C1, .i2c_clock_hz = BSP_HW_I2C1_CLOCK_SPEED, + .scl_clk = RCU_GPIOB, .scl_port = GPIOB, .scl_pin = GPIO_PIN_10, .scl_af = GPIO_AF_1, + .sda_clk = RCU_GPIOB, .sda_port = GPIOB, .sda_pin = GPIO_PIN_11, .sda_af = GPIO_AF_1, + .ev_irq_type = I2C1_EV_IRQn, .er_irq_type = I2C1_ER_IRQn, + }, +#endif +}; + +static struct gd32_i2c i2c_objs[sizeof(i2c_configs) / sizeof(i2c_configs[0])]; + +#define I2C_GD32_ERR_BERR (1U << 0) /* Bus error */ +#define I2C_GD32_ERR_LARB (1U << 1) /* Arbitration lost */ +#define I2C_GD32_ERR_AERR (1U << 2) /* No ACK received */ +#define I2C_GD32_ERR_BUSY (1U << 4) /* I2C bus busy */ +static inline void i2c_enable_interrupts(uint32_t i2c_periph) +{ + i2c_interrupt_enable(i2c_periph, I2C_INT_ERR); + i2c_interrupt_enable(i2c_periph, I2C_INT_EV); + i2c_interrupt_enable(i2c_periph, I2C_INT_BUF); +} + +static inline void i2c_disable_interrupts(uint32_t i2c_periph) +{ + i2c_interrupt_disable(i2c_periph, I2C_INT_ERR); + i2c_interrupt_disable(i2c_periph, I2C_INT_EV); + i2c_interrupt_disable(i2c_periph, I2C_INT_BUF); +} + +static inline void i2c_log_error(struct gd32_i2c *i2c_dev) +{ + if (i2c_dev->errs & I2C_GD32_ERR_BERR) LOG_E("[%s] Bus error", i2c_dev->config->device_name); + if (i2c_dev->errs & I2C_GD32_ERR_LARB) LOG_E("[%s] Arbitration lost", i2c_dev->config->device_name); + if (i2c_dev->errs & I2C_GD32_ERR_AERR) LOG_E("[%s] No ACK received", i2c_dev->config->device_name); + if (i2c_dev->errs & I2C_GD32_ERR_BUSY) LOG_E("[%s] I2C bus is busy", i2c_dev->config->device_name); +} + +static inline void gd32_i2c_xfer_read(struct gd32_i2c *i2c_obj) +{ + const struct gd32_i2c_config *cfg = i2c_obj->config; + rt_uint8_t read_byte; + + i2c_obj->current->len--; + read_byte = I2C_DATA(cfg->i2c_periph); + *i2c_obj->current->buf = read_byte; + + LOG_D("[%s] < Read byte: 0x%02X", cfg->device_name, read_byte); + + i2c_obj->current->buf++; + + if ((i2c_obj->xfer_len > 0U) && (i2c_obj->current->len == 0U)) + { + i2c_obj->current++; + } +} + +static inline void gd32_i2c_xfer_write(struct gd32_i2c *i2c_obj) +{ + const struct gd32_i2c_config *cfg = i2c_obj->config; + + rt_uint8_t write_byte = *i2c_obj->current->buf; + LOG_D("[%s] > Write byte: 0x%02X", cfg->device_name, write_byte); + + i2c_obj->current->len--; + I2C_DATA(cfg->i2c_periph) = *i2c_obj->current->buf; + i2c_obj->current->buf++; + + if ((i2c_obj->xfer_len > 0U) && (i2c_obj->current->len == 0U)) + { + i2c_obj->current++; + } +} + +static void gd32_i2c_handle_tbe(struct gd32_i2c *i2c_obj) +{ + const struct gd32_i2c_config *cfg = i2c_obj->config; + LOG_D("[%s] TBE event: xfer_len=%d", cfg->device_name, i2c_obj->xfer_len); + + if (i2c_obj->xfer_len > 0U) + { + i2c_obj->xfer_len--; + if (i2c_obj->xfer_len == 0U) + { + LOG_D("[%s] Last byte to send, disabling BUFIE, waiting for BTC.", cfg->device_name); + I2C_CTL1(cfg->i2c_periph) &= ~I2C_CTL1_BUFIE; + } + gd32_i2c_xfer_write(i2c_obj); + } + else + { + LOG_D("[%s] TBE indicates all data sent. Generating STOP.", cfg->device_name); + I2C_CTL0(cfg->i2c_periph) |= I2C_CTL0_STOP; + rt_completion_done(&i2c_obj->sync_sem); + } +} + +static void gd32_i2c_handle_rbne(struct gd32_i2c *i2c_obj) +{ + const struct gd32_i2c_config *cfg = i2c_obj->config; + LOG_D("[%s] RBNE event: xfer_len=%d", cfg->device_name, i2c_obj->xfer_len); + + switch (i2c_obj->xfer_len) + { + case 0: + LOG_W("[%s] Unexpected RBNE, waking thread.", cfg->device_name); + rt_completion_done(&i2c_obj->sync_sem); + break; + case 1: + i2c_obj->xfer_len--; + gd32_i2c_xfer_read(i2c_obj); + LOG_D("[%s] RBNE: Last byte received, waking thread.", cfg->device_name); + rt_completion_done(&i2c_obj->sync_sem); + break; + case 2: + case 3: + LOG_D("[%s] RBNE: %d bytes left, disabling BUFIE, waiting for BTC.", cfg->device_name, i2c_obj->xfer_len); + I2C_CTL1(cfg->i2c_periph) &= ~I2C_CTL1_BUFIE; + break; + default: + i2c_obj->xfer_len--; + gd32_i2c_xfer_read(i2c_obj); + break; + } +} + +static void gd32_i2c_handle_btc(struct gd32_i2c *i2c_obj) +{ + const struct gd32_i2c_config *cfg = i2c_obj->config; + LOG_D("[%s] BTC event: xfer_len=%d, flags=0x%X", cfg->device_name, i2c_obj->xfer_len, i2c_obj->current->flags); + + if (i2c_obj->current->flags & RT_I2C_RD) + { + switch (i2c_obj->xfer_len) + { + case 2: + LOG_D("[%s] BTC: N=2, generating STOP.", cfg->device_name); + I2C_CTL0(cfg->i2c_periph) |= I2C_CTL0_STOP; + i2c_obj->xfer_len -= 2; + gd32_i2c_xfer_read(i2c_obj); + gd32_i2c_xfer_read(i2c_obj); + rt_completion_done(&i2c_obj->sync_sem); + break; + case 3: + LOG_D("[%s] BTC: N=3, clearing ACKEN for NACK.", cfg->device_name); + I2C_CTL0(cfg->i2c_periph) &= ~I2C_CTL0_ACKEN; + i2c_obj->xfer_len--; + gd32_i2c_xfer_read(i2c_obj); + break; + default: + LOG_W("[%s] Unexpected BTC in read mode, handling as RBNE.", cfg->device_name); + gd32_i2c_handle_rbne(i2c_obj); + break; + } + } + else + { + LOG_D("[%s] BTC in write mode, means last byte sent.", cfg->device_name); + gd32_i2c_handle_tbe(i2c_obj); + } +} + +static void gd32_i2c_handle_addsend(struct gd32_i2c *i2c_obj) +{ + const struct gd32_i2c_config *cfg = i2c_obj->config; + LOG_D("[%s] ADDSEND event: xfer_len=%d, flags=0x%X, is_restart=%d", cfg->device_name, i2c_obj->xfer_len, i2c_obj->current->flags, i2c_obj->is_restart); + + if ((i2c_obj->current->flags & RT_I2C_RD) && (i2c_obj->xfer_len <= 1U)) + { + LOG_D("[%s] ADDSEND: N<=1 read, clearing ACKEN.", cfg->device_name); + I2C_CTL0(cfg->i2c_periph) &= ~I2C_CTL0_ACKEN; + } + + (void)I2C_STAT0(cfg->i2c_periph); + (void)I2C_STAT1(cfg->i2c_periph); + + if (i2c_obj->is_restart) + { + i2c_obj->is_restart = RT_FALSE; + i2c_obj->current->flags |= RT_I2C_RD; + LOG_D("[%s] ADDSEND: 10-bit read restart. Generating RESTART.", cfg->device_name); + I2C_CTL0(cfg->i2c_periph) |= I2C_CTL0_START; + return; + } + + if ((i2c_obj->current->flags & RT_I2C_RD) && (i2c_obj->xfer_len == 1U)) + { + LOG_D("[%s] ADDSEND: N=1 read, generating STOP.", cfg->device_name); + I2C_CTL0(cfg->i2c_periph) |= I2C_CTL0_STOP; + } +} + +static void gd32_i2c_event_handler(struct gd32_i2c *i2c_obj) +{ + const struct gd32_i2c_config *cfg; + uint32_t stat0; + + RT_ASSERT(i2c_obj != RT_NULL); + cfg = i2c_obj->config; + + stat0 = I2C_STAT0(cfg->i2c_periph); + LOG_D("[%s] EV_IRQ: STAT0=0x%08X", cfg->device_name, stat0); + + if (stat0 & I2C_STAT0_SBSEND) + { + uint8_t addr_byte; + if (i2c_obj->current->flags & RT_I2C_RD) + { + addr_byte = (i2c_obj->addr1 << 1) | 1; + } + else + { + addr_byte = (i2c_obj->addr1 << 1) | 0; + } + LOG_D("[%s] SBSEND: Sending address byte 0x%02X", cfg->device_name, addr_byte); + I2C_DATA(cfg->i2c_periph) = addr_byte; + } + else if (stat0 & I2C_STAT0_ADD10SEND) + { + LOG_D("[%s] ADD10SEND: Sending 2nd address byte 0x%02X", cfg->device_name, i2c_obj->addr2); + I2C_DATA(cfg->i2c_periph) = i2c_obj->addr2; + } + else if (stat0 & I2C_STAT0_ADDSEND) + { + gd32_i2c_handle_addsend(i2c_obj); + } + else if (stat0 & I2C_STAT0_BTC) + { + gd32_i2c_handle_btc(i2c_obj); + } + else if (stat0 & I2C_STAT0_RBNE) + { + gd32_i2c_handle_rbne(i2c_obj); + } + else if (stat0 & I2C_STAT0_TBE) + { + gd32_i2c_handle_tbe(i2c_obj); + } +} + +void gd32_i2c_error_handler(struct gd32_i2c *i2c_obj) +{ + const struct gd32_i2c_config *cfg; + uint32_t stat0; + + RT_ASSERT(i2c_obj != RT_NULL); + cfg = i2c_obj->config; + + stat0 = I2C_STAT0(cfg->i2c_periph); + LOG_E("[%s] ER_IRQ: STAT0=0x%08X", cfg->device_name, stat0); + + if (stat0 & I2C_STAT0_BERR) + { + I2C_STAT0(cfg->i2c_periph) &= ~I2C_STAT0_BERR; + i2c_obj->errs |= I2C_GD32_ERR_BERR; + } + + if (stat0 & I2C_STAT0_LOSTARB) + { + I2C_STAT0(cfg->i2c_periph) &= ~I2C_STAT0_LOSTARB; + i2c_obj->errs |= I2C_GD32_ERR_LARB; + } + + if (stat0 & I2C_STAT0_AERR) + { + I2C_STAT0(cfg->i2c_periph) &= ~I2C_STAT0_AERR; + i2c_obj->errs |= I2C_GD32_ERR_AERR; + } + + if (i2c_obj->errs != 0) + { + LOG_D("[%s] Error detected, sending STOP and waking thread.", cfg->device_name); + I2C_CTL0(cfg->i2c_periph) |= I2C_CTL0_STOP; + rt_completion_done(&i2c_obj->sync_sem); + } +} + +static rt_ssize_t gd32_i2c_master_xfer(struct rt_i2c_bus_device *bus, struct rt_i2c_msg msgs[], rt_uint32_t num) +{ + struct gd32_i2c *i2c_dev; + const struct gd32_i2c_config *cfg; + rt_ssize_t i, itr; + rt_err_t result = RT_EOK; + + RT_ASSERT(bus != RT_NULL); + RT_ASSERT(msgs != RT_NULL); + RT_ASSERT(num > 0); + + i2c_dev = rt_container_of(bus, struct gd32_i2c, parent); + cfg = i2c_dev->config; + + LOG_D("[%s] master_xfer: num_msgs=%d", cfg->device_name, num); + + for (i = 0; i < num; i++) + { + if ((i < num - 1) && !(msgs[i].flags & RT_I2C_NO_STOP)) + { + LOG_E("[%s] Only the last message can have a STOP signal", cfg->device_name); + return -RT_EINVAL; + } + + if (msgs[i].len == 0 || msgs[i].buf == RT_NULL) + { + LOG_E("[%s] Invalid message buffer or length at index %d", cfg->device_name, i); + return -RT_EINVAL; + } + } + + rt_mutex_take(&i2c_dev->bus_mutex, RT_WAITING_FOREVER); + + I2C_CTL0(cfg->i2c_periph) |= I2C_CTL0_I2CEN; + + for (i = 0; i < num; i = itr) + { + LOG_D("[%s] Processing msg segment starting at index %d", cfg->device_name, i); + i2c_dev->current = &msgs[i]; + i2c_dev->xfer_len = msgs[i].len; + rt_uint16_t current_addr = i2c_dev->current->addr; + + for (itr = i + 1; itr < num; itr++) + { + if ((msgs[itr].flags & RT_I2C_RD) != (i2c_dev->current->flags & RT_I2C_RD) || + msgs[itr].addr != current_addr) + { + break; + } + i2c_dev->xfer_len += msgs[itr].len; + } + LOG_D("[%s] Merged %d msgs. Total len=%d, addr=0x%X, flags=0x%X", + cfg->device_name, itr - i, i2c_dev->xfer_len, current_addr, i2c_dev->current->flags); + + LOG_D("[%s] Checking for bus busy...", cfg->device_name); + if (I2C_STAT1(cfg->i2c_periph) & I2C_STAT1_I2CBSY) + { + LOG_E("[%s] Bus is busy!", cfg->device_name); + i2c_dev->errs = I2C_GD32_ERR_BUSY; + result = -RT_EBUSY; + break; + } + + if (i2c_dev->current->flags & RT_I2C_ADDR_10BIT) + { + i2c_dev->addr1 = 0xF0 | ((current_addr >> 8) & 0x03); + i2c_dev->addr2 = current_addr & 0xFF; + LOG_D("[%s] 10-bit address: 0x%X -> addr1=0x%X, addr2=0x%X", cfg->device_name, current_addr, i2c_dev->addr1, i2c_dev->addr2); + } + else + { + i2c_dev->addr1 = current_addr & 0x7F; + } + + i2c_dev->errs = 0; + i2c_dev->is_restart = RT_FALSE; + rt_completion_init(&i2c_dev->sync_sem); + + I2C_CTL0(cfg->i2c_periph) |= I2C_CTL0_ACKEN; + + if ((i2c_dev->current->flags & RT_I2C_RD) && (i2c_dev->current->flags & RT_I2C_ADDR_10BIT)) + { + LOG_D("[%s] Preparing for 10-bit read restart.", cfg->device_name); + i2c_dev->is_restart = RT_TRUE; + i2c_dev->current->flags &= ~RT_I2C_RD; + } + + if ((i2c_dev->current->flags & RT_I2C_RD) && (i2c_dev->xfer_len == 2)) + { + LOG_D("[%s] N=2 read, setting POAP.", cfg->device_name); + I2C_CTL0(cfg->i2c_periph) |= I2C_CTL0_POAP; + } + + LOG_D("[%s] Generating START, enabling IRQs, waiting for completion...", cfg->device_name); + i2c_enable_interrupts(cfg->i2c_periph); + I2C_CTL0(cfg->i2c_periph) |= I2C_CTL0_START; + + result = rt_completion_wait(&i2c_dev->sync_sem, bus->timeout); + + if (i2c_dev->is_restart == RT_TRUE) + { + i2c_dev->current->flags |= RT_I2C_RD; + } + + I2C_CTL0(cfg->i2c_periph) &= ~I2C_CTL0_POAP; + i2c_disable_interrupts(cfg->i2c_periph); + + if (result != RT_EOK) + { + LOG_E("[%s] I2C transaction timeout!", cfg->device_name); + result = -RT_ETIMEOUT; + I2C_CTL0(cfg->i2c_periph) |= I2C_CTL0_STOP; + break; + } + + if (i2c_dev->errs != 0) + { + LOG_E("[%s] Hardware error detected by ISR.", cfg->device_name); + i2c_log_error(i2c_dev); + result = -RT_EIO; + break; + } + LOG_D("[%s] Transaction segment completed successfully.", cfg->device_name); + } + + if (result == RT_EOK) + { + if (!(msgs[num - 1].flags & RT_I2C_NO_STOP)) + { + LOG_D("[%s] Last message, Waiting STOP.", cfg->device_name); + rt_uint32_t timeout = 1000; + while (I2C_STAT1(cfg->i2c_periph) & I2C_STAT1_I2CBSY && timeout--) + { + rt_hw_us_delay(1); + } + } + else + { + LOG_D("[%s] Last message has NO_STOP flag, leaving bus active.", cfg->device_name); + } + } + + I2C_CTL0(cfg->i2c_periph) &= ~I2C_CTL0_I2CEN; + + rt_mutex_release(&i2c_dev->bus_mutex); + + LOG_D("[%s] master_xfer exiting with code: %d", cfg->device_name, (result == RT_EOK) ? i : result); + if (result == RT_EOK) + { + return i; + } + else + { + return result; + } +} + + +static struct gd32_i2c *_get_i2c_obj(uint32_t i2c_periph) +{ + for (size_t i = 0; i < sizeof(i2c_objs) / sizeof(i2c_objs[0]); i++) + { + if (i2c_objs[i].config->i2c_periph == i2c_periph) + { + return &i2c_objs[i]; + } + } + /* Should not happen in a correctly configured system. */ + LOG_E("Cannot find i2c_obj for periph: 0x%08X", i2c_periph); + return RT_NULL; +} + +#ifdef BSP_USING_HW_I2C0 +void I2C0_EV_IRQHandler(void) +{ + rt_interrupt_enter(); + + struct gd32_i2c *i2c_obj = _get_i2c_obj(I2C0); + if (i2c_obj) + { + gd32_i2c_event_handler(i2c_obj); + } + + rt_interrupt_leave(); +} + +void I2C0_ER_IRQHandler(void) +{ + rt_interrupt_enter(); + + struct gd32_i2c *i2c_obj = _get_i2c_obj(I2C0); + if (i2c_obj) + { + gd32_i2c_error_handler(i2c_obj); + } + + rt_interrupt_leave(); +} +#endif + + +#ifdef BSP_USING_HW_I2C1 +void I2C1_EV_IRQHandler(void) +{ + rt_interrupt_enter(); + + struct gd32_i2c *i2c_obj = _get_i2c_obj(I2C1); + if (i2c_obj) + { + gd32_i2c_event_handler(i2c_obj); + } + + rt_interrupt_leave(); +} + +void I2C1_ER_IRQHandler(void) +{ + rt_interrupt_enter(); + + struct gd32_i2c *i2c_obj = _get_i2c_obj(I2C1); + if (i2c_obj) + { + gd32_i2c_error_handler(i2c_obj); + } + + rt_interrupt_leave(); +} +#endif + + +static const struct rt_i2c_bus_device_ops gd32_i2c_ops = +{ + .master_xfer = gd32_i2c_master_xfer, + .slave_xfer = RT_NULL, + .i2c_bus_control = RT_NULL, +}; + +int rt_hw_i2c_init(void) +{ + rt_size_t obj_num = sizeof(i2c_objs) / sizeof(struct gd32_i2c); + rt_err_t result; + + for (int i = 0; i < obj_num; i++) + { + const struct gd32_i2c_config *config = &i2c_configs[i]; + struct gd32_i2c *i2c_obj = &i2c_objs[i]; + + LOG_D("Initializing %s...", config->device_name); + + i2c_obj->config = config; + i2c_obj->parent.ops = &gd32_i2c_ops; + i2c_obj->parent.timeout = RT_TICK_PER_SECOND; + + rcu_periph_clock_enable(config->scl_clk); + if (config->scl_clk != config->sda_clk) + { + rcu_periph_clock_enable(config->sda_clk); + } + rcu_periph_clock_enable(config->periph_clk); + + gpio_af_set(config->scl_port, config->scl_af, config->scl_pin); + gpio_mode_set(config->scl_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, config->scl_pin); + gpio_output_options_set(config->scl_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, config->scl_pin); + + gpio_af_set(config->sda_port, config->sda_af, config->sda_pin); + gpio_mode_set(config->sda_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, config->sda_pin); + gpio_output_options_set(config->sda_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, config->sda_pin); + + i2c_deinit(config->i2c_periph); + i2c_clock_config(config->i2c_periph, config->i2c_clock_hz, I2C_DTCY_2); + i2c_mode_addr_config(config->i2c_periph, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0); + i2c_enable(config->i2c_periph); + i2c_ack_config(config->i2c_periph, I2C_ACK_ENABLE); + + nvic_irq_enable(config->ev_irq_type, 2); + nvic_irq_enable(config->er_irq_type, 1); + + result = rt_i2c_bus_device_register(&i2c_obj->parent, config->device_name); + if (result != RT_EOK) + { + LOG_E("Failed to register i2c bus %s", config->device_name); + return result; + } + + rt_mutex_init(&i2c_obj->bus_mutex, + config->device_name, + RT_IPC_FLAG_FIFO); + + rt_completion_init(&i2c_obj->sync_sem); + + LOG_I("I2C bus %s registered successfully.", config->device_name); + } + return RT_EOK; +} +INIT_BOARD_EXPORT(rt_hw_i2c_init); + +#endif /* RT_USING_I2C */ + diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_hw_i2c.h b/bsp/gd32/arm/libraries/gd32_drivers/drv_hw_i2c.h new file mode 100644 index 00000000000..8cfe16e480e --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_hw_i2c.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#ifndef __DRV_HW_I2C_H__ +#define __DRV_HW_I2C_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* I2C hardware configuration */ +struct gd32_i2c_config +{ + rt_uint32_t i2c_periph; /* I2C peripheral base address */ + rcu_periph_enum periph_clk; /* I2C peripheral clock */ + + rcu_periph_enum scl_clk; /* SCL pin clock */ + rt_uint32_t scl_port; /* SCL pin port */ + rt_uint32_t scl_pin; /* SCL pin */ + rt_uint32_t scl_af; /* SCL pin alternate function */ + + rcu_periph_enum sda_clk; /* SDA pin clock */ + rt_uint32_t sda_port; /* SDA pin port */ + rt_uint32_t sda_pin; /* SDA pin */ + rt_uint32_t sda_af; /* SDA pin alternate function */ + + IRQn_Type ev_irq_type; /* Event IRQn */ + IRQn_Type er_irq_type; /* Error IRQn */ + + rt_uint32_t i2c_clock_hz; /* I2C clock speed in Hz, e.g., 100000 for 100kHz */ + + const char *device_name; /* Device name */ +}; + +/* I2C runtime context */ +struct gd32_i2c +{ + struct rt_i2c_bus_device parent; + const struct gd32_i2c_config *config; + + struct rt_mutex bus_mutex; + struct rt_completion sync_sem; + uint32_t dev_config; + uint16_t addr1; + uint16_t addr2; + uint32_t xfer_len; + struct rt_i2c_msg *current; + uint8_t errs; + rt_bool_t is_restart; +}; + +int rt_hw_i2c_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __DRV_HW_I2C_H__ */ +