GD32 IIC(I2C)通信(使用示例为SD2068)

发布于:2025-05-24 ⋅ 阅读:(15) ⋅ 点赞:(0)

一、前言

        最近需要用到GD32的I2C通信,虽然是第一次做I2C通信,但是GD32完整的标准库有现存的I2C通信示例,虽然示例是EEPROM的通信,但是调用的函数应该是大差不差,所以上手比较简单,这里简单记录一下笔记,方便下次使用。

二、GD32与SD2068 的连接

  • SDA:GD32的I2C数据线(我选用的PB10)。

  • SCL:GD32的I2C时钟线(我选用的PB11)。

三、I2C初始化

宏定义和头文件:

#include "gd32e23x.h" //已经包含了gd32e23x_i2c.h

#define I2C_PERIPH             I2C1
#define I2C_SPEED              100000
#define I2C_BUS_ADDRESS        0x32
#define I2C_RCUX               RCU_I2C1
#define I2C_RCU_GPIOX          RCU_GPIOB
#define I2C_SCL_PIN            GPIO_PIN_10
#define I2C_SDA_PIN            GPIO_PIN_11

1、GPIO初始化

void i2c_gpio_config(void)
{
    /* 时钟初始化 */
    rcu_periph_clock_enable(I2C_RCU_GPIOX);

    /* I2C_SCL 引脚复用 */
    gpio_af_set(GPIOB, GPIO_AF_1, I2C_SCL_PIN);
    /* I2C_SDA 引脚复用 */
    gpio_af_set(GPIOB, GPIO_AF_1, I2C_SDA_PIN);
    /* 初始化GPIO复用功能模式 */
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SCL_PIN);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SDA_PIN);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN);
}

 2、I2C功能初始化

void i2c_config(void)
{
    // 启用 I2C 外设的时钟
    rcu_periph_clock_enable(I2C_RCUX);

    // 配置 I2C 的时钟参数:
    // I2C_PERIPH:这里使用的I2C1
    // I2C_SPEED:通信速率(单位为 Hz,常用为 100000 或 400000),这里使用100000
    i2c_clock_config(I2C_PERIPH, I2C_SPEED, I2C_DTCY_2);

    // 配置 I2C 工作模式和地址:
    // I2C_ADDFORMAT_7BITS:使用 7 位地址模式
    // I2C_BUS_ADDRESS:SD2068器件代码0110010 = 0x32
    i2c_mode_addr_config(I2C_PERIPH, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, I2C_BUS_ADDRESS);

    // 启用 I2C 外设
    i2c_enable(I2C_PERIPH);

    // 使能 ACK 应答功能,确保在接收数据后自动发送 ACK
    i2c_ack_config(I2C_PERIPH, I2C_ACK_ENABLE);
}

四、I2C写实现

        主要过程就是:空闲->发送START 信号->发送设备、寄存器地址和方向->发送数据。封装函数如下:

void i2c_write_multi(uint8_t reg_addr, uint8_t *data, uint8_t dataSize)
{
    // 等待 I2C 总线空闲,防止冲突
    while(i2c_flag_get(I2C_PERIPH, I2C_FLAG_I2CBSY));

    // 发送 START 信号,起始 I2C 通信
    i2c_start_on_bus(I2C_PERIPH);
    while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_SBSEND));
    
    // 发送设备地址和写入方向(最低位0)
    i2c_master_addressing(I2C_PERIPH, I2C_BUS_ADDRESS << 1, I2C_TRANSMITTER);
		//i2c_data_transmit(I2C_PERIPH, I2C_BUS_ADDRESS << 1);
    while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_ADDSEND));

    // 清除地址发送标志位
    i2c_flag_clear(I2C_PERIPH, I2C_FLAG_ADDSEND);

    // 发送寄存器地址(子地址)
    i2c_data_transmit(I2C_PERIPH, reg_addr);
    while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_TBE));

    // 开始循环写入多个数据字节
    while(dataSize--) {
				delay_1ms(5);
        i2c_data_transmit(I2C_PERIPH, *data++);
        while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_TBE));
    }

    // 发送 STOP 信号,结束通信
    i2c_stop_on_bus(I2C_PERIPH);
    while(I2C_CTL0(I2C_PERIPH) & I2C_CTL0_STOP);
}

五、I2C读实现

        通信过程与写类似,直接上实现代码:

void i2c_read_multi(uint8_t reg_addr, uint8_t *data, uint8_t dataSize)
{
    // 等待 I2C 总线空闲
    while(i2c_flag_get(I2C_PERIPH, I2C_FLAG_I2CBSY));

    // 发送起始信号
    i2c_start_on_bus(I2C_PERIPH);
    while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_SBSEND));

    // 发送设备地址
    i2c_master_addressing(I2C_PERIPH, I2C_BUS_ADDRESS << 1, I2C_TRANSMITTER);
    while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_ADDSEND));

    //清地址标志
    i2c_flag_clear(I2C_PERIPH, I2C_FLAG_ADDSEND);

    // 写入要读取的寄存器地址(子地址)
    i2c_data_transmit(I2C_PERIPH, reg_addr);
    while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_TBE));
    while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_BTC));

    // 第二次起始信号,重新启动为“读模式”
    i2c_start_on_bus(I2C_PERIPH);
    while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_SBSEND));

    // 发送设备地址,读方向(1)
    i2c_master_addressing(I2C_PERIPH, I2C_BUS_ADDRESS << 1, I2C_RECEIVER);
    while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_ADDSEND));
    i2c_flag_clear(I2C_PERIPH, I2C_FLAG_ADDSEND);

    // 如果只读取1个字节,提前关闭ACK,准备发送STOP
    if (dataSize == 1) {
        i2c_ack_config(I2C_PERIPH, I2C_ACK_DISABLE);
        i2c_stop_on_bus(I2C_PERIPH);
    }
    // 读取多个字节循环
    for (uint8_t i = 0; i < dataSize; i++) {
				delay_1ms(5);
        if (i == dataSize - 2) {// 读取即将完成,关闭ACK,准备发送STOP
            i2c_ack_config(I2C_PERIPH, I2C_ACK_DISABLE);
            i2c_stop_on_bus(I2C_PERIPH);
        }

        while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_RBNE));
        data[i] = i2c_data_receive(I2C_PERIPH);
    }

    // 等待 STOP 传输完毕
    while(I2C_CTL0(I2C_PERIPH) & I2C_CTL0_STOP);

    // 恢复 ACK 设置(便于下次通信)
    i2c_ack_config(I2C_PERIPH, I2C_ACK_ENABLE);
    i2c_ackpos_config(I2C_PERIPH, I2C_ACKPOS_CURRENT);
}


网站公告

今日签到

点亮在社区的每一天
去签到