GD32450i-EVAL硬件I2C实战:从零配置到读写EEPROM完整流程

张开发
2026/6/26 8:49:04 15 分钟阅读
GD32450i-EVAL硬件I2C实战:从零配置到读写EEPROM完整流程
GD32450i-EVAL硬件I2C实战从零配置到读写EEPROM完整流程在嵌入式开发中I2C总线因其简单的两线制SDA和SCL和灵活的多主从架构成为连接各类传感器的首选方案。GD32450i-EVAL开发板搭载的GD32F450系列MCU提供了强大的硬件I2C外设支持本文将带你从零开始通过读写EEPROM的完整案例掌握硬件I2C的配置与操作技巧。1. 硬件准备与环境搭建1.1 开发板与EEPROM模块连接GD32450i-EVAL开发板默认将I2C0的SCLPB6和SDAPB7引脚通过排针引出。连接AT24C02 EEPROM模块时需注意电源匹配确保EEPROM工作电压通常3.3V或5V与开发板供电一致上拉电阻I2C总线需4.7kΩ上拉电阻部分模块已内置地址设置AT24C02的A0-A2引脚决定设备地址默认0x50提示若使用其他I2C从设备需查阅其数据手册确认地址和时序要求1.2 开发环境配置确保已安装以下工具Keil MDK或IAR Embedded WorkbenchGD32F4xx_AddOn芯片支持包最新版GigaDevice提供的标准外设库# 示例使用STM32CubeMX生成基础代码需适配GD32 stm32cubecli --mcu GD32F450 --periph I2C0 --output ./i2c_demo2. I2C外设初始化详解2.1 GPIO端口配置硬件I2C需要将对应引脚设置为复用功能模式。对于I2C0PB6/PB7void I2C_GPIO_Config(void) { /* 使能GPIOB时钟 */ rcu_periph_clock_enable(RCU_GPIOB); /* 配置PB6(SCL)和PB7(SDA)为复用开漏模式 */ gpio_af_set(GPIOB, GPIO_AF_4, GPIO_PIN_6 | GPIO_PIN_7); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_6 | GPIO_PIN_7); gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7); }关键参数说明配置项推荐值作用说明GPIO_MODEGPIO_MODE_AF设置为复用功能模式GPIO_PUPDGPIO_PUPD_PULLUP内部上拉可省外接电阻GPIO_OTYPEGPIO_OTYPE_OD开漏输出I2C必需2.2 I2C主模式初始化void I2C_Configuration(void) { /* 使能I2C0时钟 */ rcu_periph_clock_enable(RCU_I2C0); /* 时钟配置标准模式100kHz */ i2c_clock_config(I2C0, 100000, I2C_DTCY_2); /* 模式与地址配置 */ i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0); /* 使能I2C外设 */ i2c_enable(I2C0); /* 使能ACK响应 */ i2c_ack_config(I2C0, I2C_ACK_ENABLE); }常见问题排查时钟配置错误使用示波器检查SCL频率是否符合预期ACK无响应确认从设备地址正确且上电正常总线锁死尝试发送STOP信号或重新初始化I2C3. EEPROM读写实战3.1 单字节写入操作向地址0x00写入数据0xAB的完整流程void EEPROM_WriteByte(uint8_t devAddr, uint16_t memAddr, uint8_t data) { /* 等待总线空闲 */ while(i2c_flag_get(I2C0, I2C_FLAG_I2CBSY)); /* 发送START条件 */ i2c_start_on_bus(I2C0); while(!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)); /* 发送设备地址写模式 */ i2c_master_addressing(I2C0, devAddr, I2C_TRANSMITTER); while(!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)); i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); /* 发送内存地址16位 */ i2c_data_transmit(I2C0, (uint8_t)(memAddr 8)); while(!i2c_flag_get(I2C0, I2C_FLAG_BTC)); i2c_data_transmit(I2C0, (uint8_t)(memAddr 0xFF)); while(!i2c_flag_get(I2C0, I2C_FLAG_BTC)); /* 发送数据 */ i2c_data_transmit(I2C0, data); while(!i2c_flag_get(I2C0, I2C_FLAG_BTC)); /* 发送STOP条件 */ i2c_stop_on_bus(I2C0); while(I2C_CTL0(I2C0) 0x0200); /* 等待EEPROM内部写周期完成约5ms */ delay_ms(5); }3.2 多字节连续读取从地址0x00开始读取4字节数据的实现void EEPROM_ReadBuffer(uint8_t devAddr, uint16_t memAddr, uint8_t *pData, uint16_t len) { /* 伪地址写入阶段与写操作相同 */ while(i2c_flag_get(I2C0, I2C_FLAG_I2CBSY)); i2c_start_on_bus(I2C0); while(!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)); i2c_master_addressing(I2C0, devAddr, I2C_TRANSMITTER); while(!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)); i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); i2c_data_transmit(I2C0, (uint8_t)(memAddr 8)); while(!i2c_flag_get(I2C0, I2C_FLAG_BTC)); i2c_data_transmit(I2C0, (uint8_t)(memAddr 0xFF)); while(!i2c_flag_get(I2C0, I2C_FLAG_BTC)); /* 重新发送START条件 */ i2c_start_on_bus(I2C0); while(!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)); /* 发送设备地址读模式 */ i2c_master_addressing(I2C0, devAddr, I2C_RECEIVER); while(!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)); /* 最后一个字节需禁用ACK */ if(len 1) { i2c_ack_config(I2C0, I2C_ACK_DISABLE); } i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); /* 接收数据 */ while(len 0) { if(i2c_flag_get(I2C0, I2C_FLAG_RBNE)) { /* 倒数第二个字节时禁用ACK */ if(len 2) { i2c_ack_config(I2C0, I2C_ACK_DISABLE); } *pData i2c_data_receive(I2C0); pData; len--; } } /* 发送STOP条件 */ i2c_stop_on_bus(I2C0); while(I2C_CTL0(I2C0) 0x0200); /* 恢复ACK使能 */ i2c_ack_config(I2C0, I2C_ACK_ENABLE); }4. 高级技巧与性能优化4.1 中断驱动实现为提高效率可使用中断替代轮询标志位void I2C_IRQHandler(void) { if(i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_SBSEND)) { /* START条件已发送处理 */ i2c_interrupt_flag_clear(I2C0, I2C_INT_FLAG_SBSEND); // ...后续处理 } // 其他中断标志处理... } /* 初始化时使能中断 */ void I2C_Interrupt_Init(void) { nvic_irq_enable(I2C0_EV_IRQn, 0, 0); i2c_interrupt_enable(I2C0, I2C_INT_ERR | I2C_INT_EV); }4.2 DMA传输加速对于大数据量传输配置DMA可显著提升效率void I2C_DMA_Config(void) { /* 配置DMA通道 */ dma_parameter_struct dma_init_struct; /* I2C0 TX DMA (内存到外设) */ dma_deinit(DMA0, DMA_CH6); dma_init_struct.direction DMA_MEMORY_TO_PERIPHERAL; dma_init_struct.memory_addr (uint32_t)tx_buffer; dma_init_struct.memory_inc DMA_MEMORY_INCREASE_ENABLE; dma_init_struct.memory_width DMA_MEMORY_WIDTH_8BIT; dma_init_struct.number BUFFER_SIZE; dma_init_struct.periph_addr (uint32_t)I2C_DATA(I2C0); dma_init_struct.periph_inc DMA_PERIPH_INCREASE_DISABLE; dma_init_struct.periph_width DMA_PERIPH_WIDTH_8BIT; dma_init_struct.priority DMA_PRIORITY_HIGH; dma_init(DMA0, DMA_CH6, dma_init_struct); /* 使能DMA请求 */ i2c_dma_enable(I2C0, I2C_DMA_ON); }4.3 常见问题解决方案问题1总线死锁现象SCL线被拉低无法恢复解决尝试发送多个STOP条件切换GPIO模式手动产生时钟脉冲重新初始化I2C外设问题2从设备无响应排查步骤用逻辑分析仪确认地址是否正确检查上拉电阻值通常4.7kΩ验证从设备供电是否正常问题3数据校验错误优化方案添加CRC校验实现重试机制建议最多3次降低通信速率测试稳定性在实际项目中我发现最有效的调试方法是使用逻辑分析仪捕获I2C波形可以直观看到START/STOP条件、地址和数据位的实际传输情况。当遇到异常时对比正常波形往往能快速定位问题根源。

更多文章