使用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

相关推荐
Max_uuc1 天前
【硬件心法】打破软硬边界:从原理图剖析探秘“微安级”精密电流采样的底层架构
单片机·嵌入式硬件
2501_918126911 天前
stm32核心板是什么属性?
linux·c语言·stm32·嵌入式硬件·个人开发
古译汉书1 天前
RTOS:ISR与互斥量的关系
运维·服务器·stm32·嵌入式硬件
国科安芯1 天前
实战验证:ASM1042S2S CANFD收发器的质子单粒子效应试验与在轨性能
网络·人工智能·单片机·嵌入式硬件·物联网·fpga开发
Zevalin爱灰灰1 天前
基于STM32实现OTA&BootLoader 第二章——外设功能开发
stm32·单片机·物联网·嵌入式
2501_918126911 天前
stm32能刷什么程序?
linux·stm32·单片机·嵌入式硬件·学习
国科安芯1 天前
ASP4644S电源芯片引脚功能与参考设计输出电压计算方法
网络·单片机·嵌入式硬件·fpga开发·性能优化
国科安芯1 天前
抗辐照MCU芯片在核工业水下探测耐辐照数字摄像机中的应用研究
网络·单片机·嵌入式硬件
VALENIAN瓦伦尼安教学设备1 天前
品牌故事:1964年塞纳河畔ASHOOTER激光对中仪诞生的夜晚
数据库·人工智能·嵌入式硬件
Zevalin爱灰灰1 天前
基于STM32实现OTA&BootLoader 第一章——概述
stm32·单片机·物联网·嵌入式