使用GD32F103C8T6开发板的标准库实现硬件I2C协议通信(附源码下载地址)

代码说明:

  1. 该I2C驱动实现了完整的硬件I2C配置,包括GPIO引脚设置、时钟配置和模式配置
  2. 包含全面的异常处理机制,能够检测和处理超时、NACK、总线忙、仲裁丢失等异常情况
  3. 提供了多种I2C操作函数,包括单字节读写和多字节读写操作
  4. 实现了超时检测机制,防止程序在异常情况下无限等待
  5. 主函数中包含了完整的测试流程,通过LED指示不同的通信状态
  6. 代码模块化设计,将I2C驱动分为头文件和实现文件,便于维护和复用
cpp 复制代码
//i2c_deriver.h


#ifndef __I2C_DRIVER_H
#define __I2C_DRIVER_H

#include "gd32f10x.h"

// I2C配置参数
#define I2C_PERIPH              I2C0
#define I2C_SPEED               100000
#define I2C_SCL_PIN             GPIO_PIN_6
#define I2C_SDA_PIN             GPIO_PIN_7
#define I2C_GPIO_PORT           GPIOB
#define I2C_TIMEOUT             0xFFFFF

// I2C状态码定义
typedef enum {
    I2C_OK = 0,
    I2C_ERROR_TIMEOUT,
    I2C_ERROR_NACK,
    I2C_ERROR_BUS_BUSY,
    I2C_ERROR_ARBITRATION_LOST,
    I2C_ERROR_UNKNOWN
} i2c_status_t;

// 函数声明
i2c_status_t i2c_master_init(void);
i2c_status_t i2c_master_write(uint8_t device_addr, uint8_t reg_addr, uint8_t *data, uint16_t length);
i2c_status_t i2c_master_read(uint8_t device_addr, uint8_t reg_addr, uint8_t *data, uint16_t length);
i2c_status_t i2c_master_write_byte(uint8_t device_addr, uint8_t data);
i2c_status_t i2c_master_read_byte(uint8_t device_addr, uint8_t *data);
void i2c_deinit(void);

#endif /* __I2C_DRIVER_H */
cpp 复制代码
//i2c_driver.c

#include "i2c_driver.h"

/**
 * @brief I2C超时检查函数
 * @param flag: 要检查的标志位
 * @param timeout: 超时计数
 * @return i2c_status_t: 操作状态
 */
static i2c_status_t i2c_wait_flag_timeout(uint32_t flag, uint32_t timeout)
{
    while(!i2c_flag_get(I2C_PERIPH, flag)) {
        if((timeout--) == 0) {
            return I2C_ERROR_TIMEOUT;
        }
    }
    return I2C_OK;
}

/**
 * @brief I2C主模式初始化
 * @return i2c_status_t: 初始化状态
 */
i2c_status_t i2c_master_init(void)
{
    uint32_t timeout = I2C_TIMEOUT;
    
    // 使能GPIO和I2C时钟
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_I2C0);
    
    // 配置I2C引脚
    gpio_init(I2C_GPIO_PORT, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN);
    gpio_init(I2C_GPIO_PORT, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN);
    
    // 复位I2C
    i2c_deinit(I2C_PERIPH);
    
    // 配置I2C参数
    i2c_clock_config(I2C_PERIPH, I2C_SPEED, I2C_DTCY_2);
    i2c_mode_addr_config(I2C_PERIPH, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x00);
    i2c_ack_config(I2C_PERIPH, I2C_ACK_ENABLE);
    
    // 使能I2C
    i2c_enable(I2C_PERIPH);
    
    // 等待I2C就绪
    while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_I2CBSY)) {
        if((timeout--) == 0) {
            return I2C_ERROR_TIMEOUT;
        }
    }
    
    return I2C_OK;
}

/**
 * @brief I2C主发送数据
 * @param device_addr: 设备地址
 * @param reg_addr: 寄存器地址
 * @param data: 数据指针
 * @param length: 数据长度
 * @return i2c_status_t: 发送状态
 */
i2c_status_t i2c_master_write(uint8_t device_addr, uint8_t reg_addr, uint8_t *data, uint16_t length)
{
    i2c_status_t status;
    uint32_t timeout = I2C_TIMEOUT;
    
    // 检查总线是否空闲
    if(i2c_flag_get(I2C_PERIPH, I2C_FLAG_I2CBSY)) {
        return I2C_ERROR_BUS_BUSY;
    }
    
    // 发送起始信号
    i2c_start_on_bus(I2C_PERIPH);
    status = i2c_wait_flag_timeout(I2C_FLAG_SBSEND, timeout);
    if(status != I2C_OK) return status;
    
    // 发送设备地址(写)
    i2c_master_addressing(I2C_PERIPH, device_addr, I2C_TRANSMITTER);
    status = i2c_wait_flag_timeout(I2C_FLAG_ADDSEND, timeout);
    if(status != I2C_OK) {
        // 检查是否收到NACK
        if(i2c_flag_get(I2C_PERIPH, I2C_FLAG_AERR)) {
            i2c_flag_clear(I2C_PERIPH, I2C_FLAG_AERR);
            return I2C_ERROR_NACK;
        }
        return status;
    }
    i2c_flag_clear(I2C_PERIPH, I2C_FLAG_ADDSEND);
    
    // 发送寄存器地址
    status = i2c_wait_flag_timeout(I2C_FLAG_TBE, timeout);
    if(status != I2C_OK) return status;
    i2c_data_transmit(I2C_PERIPH, reg_addr);
    status = i2c_wait_flag_timeout(I2C_FLAG_BTC, timeout);
    if(status != I2C_OK) return status;
    
    // 发送数据
    for(uint16_t i = 0; i < length; i++) {
        status = i2c_wait_flag_timeout(I2C_FLAG_TBE, timeout);
        if(status != I2C_OK) return status;
        i2c_data_transmit(I2C_PERIPH, data[i]);
        status = i2c_wait_flag_timeout(I2C_FLAG_BTC, timeout);
        if(status != I2C_OK) return status;
    }
    
    // 发送停止信号
    i2c_stop_on_bus(I2C_PERIPH);
    
    // 等待停止信号完成
    timeout = I2C_TIMEOUT;
    while(I2C_CTL0(I2C_PERIPH) & 0x0200) {
        if((timeout--) == 0) {
            return I2C_ERROR_TIMEOUT;
        }
    }
    
    return I2C_OK;
}

/**
 * @brief I2C主接收数据
 * @param device_addr: 设备地址
 * @param reg_addr: 寄存器地址
 * @param data: 数据指针
 * @param length: 数据长度
 * @return i2c_status_t: 接收状态
 */
i2c_status_t i2c_master_read(uint8_t device_addr, uint8_t reg_addr, uint8_t *data, uint16_t length)
{
    i2c_status_t status;
    uint32_t timeout = I2C_TIMEOUT;
    
    // 检查总线是否空闲
    if(i2c_flag_get(I2C_PERIPH, I2C_FLAG_I2CBSY)) {
        return I2C_ERROR_BUS_BUSY;
    }
    
    // 第一阶段:发送寄存器地址
    // 发送起始信号
    i2c_start_on_bus(I2C_PERIPH);
    status = i2c_wait_flag_timeout(I2C_FLAG_SBSEND, timeout);
    if(status != I2C_OK) return status;
    
    // 发送设备地址(写)
    i2c_master_addressing(I2C_PERIPH, device_addr, I2C_TRANSMITTER);
    status = i2c_wait_flag_timeout(I2C_FLAG_ADDSEND, timeout);
    if(status != I2C_OK) {
        if(i2c_flag_get(I2C_PERIPH, I2C_FLAG_AERR)) {
            i2c_flag_clear(I2C_PERIPH, I2C_FLAG_AERR);
            return I2C_ERROR_NACK;
        }
        return status;
    }
    i2c_flag_clear(I2C_PERIPH, I2C_FLAG_ADDSEND);
    
    // 发送寄存器地址
    status = i2c_wait_flag_timeout(I2C_FLAG_TBE, timeout);
    if(status != I2C_OK) return status;
    i2c_data_transmit(I2C_PERIPH, reg_addr);
    status = i2c_wait_flag_timeout(I2C_FLAG_BTC, timeout);
    if(status != I2C_OK) return status;
    
    // 第二阶段:读取数据
    // 发送重复起始信号
    i2c_start_on_bus(I2C_PERIPH);
    status = i2c_wait_flag_timeout(I2C_FLAG_SBSEND, timeout);
    if(status != I2C_OK) return status;
    
    // 发送设备地址(读)
    i2c_master_addressing(I2C_PERIPH, device_addr, I2C_RECEIVER);
    status = i2c_wait_flag_timeout(I2C_FLAG_ADDSEND, timeout);
    if(status != I2C_OK) {
        if(i2c_flag_get(I2C_PERIPH, I2C_FLAG_AERR)) {
            i2c_flag_clear(I2C_PERIPH, I2C_FLAG_AERR);
            return I2C_ERROR_NACK;
        }
        return status;
    }
    i2c_flag_clear(I2C_PERIPH, I2C_FLAG_ADDSEND);
    
    // 接收数据
    for(uint16_t i = 0; i < length; i++) {
        // 最后一个字节前关闭ACK
        if(i == (length - 1)) {
            i2c_ack_config(I2C_PERIPH, I2C_ACK_DISABLE);
        }
        
        status = i2c_wait_flag_timeout(I2C_FLAG_RBNE, timeout);
        if(status != I2C_OK) return status;
        data[i] = i2c_data_receive(I2C_PERIPH);
    }
    
    // 重新使能ACK
    i2c_ack_config(I2C_PERIPH, I2C_ACK_ENABLE);
    
    // 发送停止信号
    i2c_stop_on_bus(I2C_PERIPH);
    
    // 等待停止信号完成
    timeout = I2C_TIMEOUT;
    while(I2C_CTL0(I2C_PERIPH) & 0x0200) {
        if((timeout--) == 0) {
            return I2C_ERROR_TIMEOUT;
        }
    }
    
    return I2C_OK;
}

/**
 * @brief I2C主发送单字节数据
 * @param device_addr: 设备地址
 * @param data: 数据
 * @return i2c_status_t: 发送状态
 */
i2c_status_t i2c_master_write_byte(uint8_t device_addr, uint8_t data)
{
    return i2c_master_write(device_addr, 0x00, &data, 1);
}

/**
 * @brief I2C主接收单字节数据
 * @param device_addr: 设备地址
 * @param data: 数据指针
 * @return i2c_status_t: 接收状态
 */
i2c_status_t i2c_master_read_byte(uint8_t device_addr, uint8_t *data)
{
    return i2c_master_read(device_addr, 0x00, data, 1);
}

/**
 * @brief I2C去初始化
 */
void i2c_deinit(void)
{
    i2c_disable(I2C_PERIPH);
    rcu_periph_clock_disable(RCU_I2C0);
}
cpp 复制代码
//main.c


#include "i2c_driver.h"

/**
 * @brief I2C超时检查函数
 * @param flag: 要检查的标志位
 * @param timeout: 超时计数
 * @return i2c_status_t: 操作状态
 */
static i2c_status_t i2c_wait_flag_timeout(uint32_t flag, uint32_t timeout)
{
    while(!i2c_flag_get(I2C_PERIPH, flag)) {
        if((timeout--) == 0) {
            return I2C_ERROR_TIMEOUT;
        }
    }
    return I2C_OK;
}

/**
 * @brief I2C主模式初始化
 * @return i2c_status_t: 初始化状态
 */
i2c_status_t i2c_master_init(void)
{
    uint32_t timeout = I2C_TIMEOUT;
    
    // 使能GPIO和I2C时钟
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_I2C0);
    
    // 配置I2C引脚
    gpio_init(I2C_GPIO_PORT, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN);
    gpio_init(I2C_GPIO_PORT, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN);
    
    // 复位I2C
    i2c_deinit(I2C_PERIPH);
    
    // 配置I2C参数
    i2c_clock_config(I2C_PERIPH, I2C_SPEED, I2C_DTCY_2);
    i2c_mode_addr_config(I2C_PERIPH, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x00);
    i2c_ack_config(I2C_PERIPH, I2C_ACK_ENABLE);
    
    // 使能I2C
    i2c_enable(I2C_PERIPH);
    
    // 等待I2C就绪
    while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_I2CBSY)) {
        if((timeout--) == 0) {
            return I2C_ERROR_TIMEOUT;
        }
    }
    
    return I2C_OK;
}

/**
 * @brief I2C主发送数据
 * @param device_addr: 设备地址
 * @param reg_addr: 寄存器地址
 * @param data: 数据指针
 * @param length: 数据长度
 * @return i2c_status_t: 发送状态
 */
i2c_status_t i2c_master_write(uint8_t device_addr, uint8_t reg_addr, uint8_t *data, uint16_t length)
{
    i2c_status_t status;
    uint32_t timeout = I2C_TIMEOUT;
    
    // 检查总线是否空闲
    if(i2c_flag_get(I2C_PERIPH, I2C_FLAG_I2CBSY)) {
        return I2C_ERROR_BUS_BUSY;
    }
    
    // 发送起始信号
    i2c_start_on_bus(I2C_PERIPH);
    status = i2c_wait_flag_timeout(I2C_FLAG_SBSEND, timeout);
    if(status != I2C_OK) return status;
    
    // 发送设备地址(写)
    i2c_master_addressing(I2C_PERIPH, device_addr, I2C_TRANSMITTER);
    status = i2c_wait_flag_timeout(I2C_FLAG_ADDSEND, timeout);
    if(status != I2C_OK) {
        // 检查是否收到NACK
        if(i2c_flag_get(I2C_PERIPH, I2C_FLAG_AERR)) {
            i2c_flag_clear(I2C_PERIPH, I2C_FLAG_AERR);
            return I2C_ERROR_NACK;
        }
        return status;
    }
    i2c_flag_clear(I2C_PERIPH, I2C_FLAG_ADDSEND);
    
    // 发送寄存器地址
    status = i2c_wait_flag_timeout(I2C_FLAG_TBE, timeout);
    if(status != I2C_OK) return status;
    i2c_data_transmit(I2C_PERIPH, reg_addr);
    status = i2c_wait_flag_timeout(I2C_FLAG_BTC, timeout);
    if(status != I2C_OK) return status;
    
    // 发送数据
    for(uint16_t i = 0; i < length; i++) {
        status = i2c_wait_flag_timeout(I2C_FLAG_TBE, timeout);
        if(status != I2C_OK) return status;
        i2c_data_transmit(I2C_PERIPH, data[i]);
        status = i2c_wait_flag_timeout(I2C_FLAG_BTC, timeout);
        if(status != I2C_OK) return status;
    }
    
    // 发送停止信号
    i2c_stop_on_bus(I2C_PERIPH);
    
    // 等待停止信号完成
    timeout = I2C_TIMEOUT;
    while(I2C_CTL0(I2C_PERIPH) & 0x0200) {
        if((timeout--) == 0) {
            return I2C_ERROR_TIMEOUT;
        }
    }
    
    return I2C_OK;
}

/**
 * @brief I2C主接收数据
 * @param device_addr: 设备地址
 * @param reg_addr: 寄存器地址
 * @param data: 数据指针
 * @param length: 数据长度
 * @return i2c_status_t: 接收状态
 */
i2c_status_t i2c_master_read(uint8_t device_addr, uint8_t reg_addr, uint8_t *data, uint16_t length)
{
    i2c_status_t status;
    uint32_t timeout = I2C_TIMEOUT;
    
    // 检查总线是否空闲
    if(i2c_flag_get(I2C_PERIPH, I2C_FLAG_I2CBSY)) {
        return I2C_ERROR_BUS_BUSY;
    }
    
    // 第一阶段:发送寄存器地址
    // 发送起始信号
    i2c_start_on_bus(I2C_PERIPH);
    status = i2c_wait_flag_timeout(I2C_FLAG_SBSEND, timeout);
    if(status != I2C_OK) return status;
    
    // 发送设备地址(写)
    i2c_master_addressing(I2C_PERIPH, device_addr, I2C_TRANSMITTER);
    status = i2c_wait_flag_timeout(I2C_FLAG_ADDSEND, timeout);
    if(status != I2C_OK) {
        if(i2c_flag_get(I2C_PERIPH, I2C_FLAG_AERR)) {
            i2c_flag_clear(I2C_PERIPH, I2C_FLAG_AERR);
            return I2C_ERROR_NACK;
        }
        return status;
    }
    i2c_flag_clear(I2C_PERIPH, I2C_FLAG_ADDSEND);
    
    // 发送寄存器地址
    status = i2c_wait_flag_timeout(I2C_FLAG_TBE, timeout);
    if(status != I2C_OK) return status;
    i2c_data_transmit(I2C_PERIPH, reg_addr);
    status = i2c_wait_flag_timeout(I2C_FLAG_BTC, timeout);
    if(status != I2C_OK) return status;
    
    // 第二阶段:读取数据
    // 发送重复起始信号
    i2c_start_on_bus(I2C_PERIPH);
    status = i2c_wait_flag_timeout(I2C_FLAG_SBSEND, timeout);
    if(status != I2C_OK) return status;
    
    // 发送设备地址(读)
    i2c_master_addressing(I2C_PERIPH, device_addr, I2C_RECEIVER);
    status = i2c_wait_flag_timeout(I2C_FLAG_ADDSEND, timeout);
    if(status != I2C_OK) {
        if(i2c_flag_get(I2C_PERIPH, I2C_FLAG_AERR)) {
            i2c_flag_clear(I2C_PERIPH, I2C_FLAG_AERR);
            return I2C_ERROR_NACK;
        }
        return status;
    }
    i2c_flag_clear(I2C_PERIPH, I2C_FLAG_ADDSEND);
    
    // 接收数据
    for(uint16_t i = 0; i < length; i++) {
        // 最后一个字节前关闭ACK
        if(i == (length - 1)) {
            i2c_ack_config(I2C_PERIPH, I2C_ACK_DISABLE);
        }
        
        status = i2c_wait_flag_timeout(I2C_FLAG_RBNE, timeout);
        if(status != I2C_OK) return status;
        data[i] = i2c_data_receive(I2C_PERIPH);
    }
    
    // 重新使能ACK
    i2c_ack_config(I2C_PERIPH, I2C_ACK_ENABLE);
    
    // 发送停止信号
    i2c_stop_on_bus(I2C_PERIPH);
    
    // 等待停止信号完成
    timeout = I2C_TIMEOUT;
    while(I2C_CTL0(I2C_PERIPH) & 0x0200) {
        if((timeout--) == 0) {
            return I2C_ERROR_TIMEOUT;
        }
    }
    
    return I2C_OK;
}

/**
 * @brief I2C主发送单字节数据
 * @param device_addr: 设备地址
 * @param data: 数据
 * @return i2c_status_t: 发送状态
 */
i2c_status_t i2c_master_write_byte(uint8_t device_addr, uint8_t data)
{
    return i2c_master_write(device_addr, 0x00, &data, 1);
}

/**
 * @brief I2C主接收单字节数据
 * @param device_addr: 设备地址
 * @param data: 数据指针
 * @return i2c_status_t: 接收状态
 */
i2c_status_t i2c_master_read_byte(uint8_t device_addr, uint8_t *data)
{
    return i2c_master_read(device_addr, 0x00, data, 1);
}

/**
 * @brief I2C去初始化
 */
void i2c_deinit(void)
{
    i2c_disable(I2C_PERIPH);
    rcu_periph_clock_disable(RCU_I2C0);
}

代码下载地址: https://gitee.com/gd_15/gd32f103_i2c_driver.git

相关推荐
熙芯XiChip13 小时前
硬实时与软实时RTOS核心区别
嵌入式硬件
伟大的前程13 小时前
unsigned int 定义的变量 在 电脑的 64 位系统中 和 32 位 cortex-m0 和 8 位的 51 单片机
单片机·嵌入式硬件
小渔村的拉线工14 小时前
1.HPM6E80 解析芯片整体工作原理和存储架构
单片机·嵌入式硬件·mcu·架构·hpm6e80
熙芯XiChip15 小时前
CAN与CANopen的本质区别
单片机
lilihewo15 小时前
Altium Designer 封装库的创建(分享pcb 3D封装库)
嵌入式硬件·51单片机
神罚天下ET16 小时前
典型arm位单片机启动流程(从上电到main.c)
c语言·arm开发·单片机
碧海银沙音频科技研究院17 小时前
蓝牙耳机使用WSDF5361锂保芯片进入船运模式实现方法
嵌入式硬件·算法
Zevalin爱灰灰17 小时前
模拟信号链电路——基于TL431的电压基准电路
单片机·嵌入式硬件
csdn杰哥18 小时前
瑞萨单片机开发教程[二十七] DTC 外部中断触发传输实验
单片机·嵌入式硬件·mongodb·瑞萨·ra6m5
zcmodeltech18 小时前
农业教学实训沙盘物联网控制系统设计:基于STM32与Modbus RTU的“感知-控制-实训”一体化方案
分布式·stm32·嵌入式硬件·物联网