STM32软件模拟I2C驱动AT24C02 EEPROM详解

发布时间:2026/7/19 2:56:20
STM32软件模拟I2C驱动AT24C02 EEPROM详解 1. I2C通信协议基础与STM32实现方案选择在嵌入式系统开发中I2CInter-Integrated Circuit总线是一种广泛使用的同步串行通信协议。它由Philips公司现NXP在1980年代设计主要用于连接低速外设与微控制器。I2C总线仅需两根线即可实现全双工通信串行数据线SDA和串行时钟线SCL这种简洁的设计使其在资源受限的嵌入式系统中特别受欢迎。STM32F103作为一款经典的ARM Cortex-M3内核微控制器提供了两种实现I2C通信的方式硬件I2C和软件模拟I2C。硬件I2C利用芯片内置的专用外设通过配置寄存器自动生成符合I2C协议的时序。这种方式效率高且占用CPU资源少但存在两个主要限制一是必须使用芯片指定的固定引脚如STM32F103的PB6/PB7或PB10/PB11二是在某些复杂场景下调试难度较大。软件模拟I2C则是通过GPIO引脚的电平控制完全由程序代码实现I2C协议时序。这种方式的主要优势在于引脚选择灵活可以使用任意GPIO引脚调试方便可以精确控制每个时序细节兼容性强不受芯片硬件限制可移植性好在资源受限的STM32F103RCT6开发板上由于硬件I2C专用引脚可能已被其他功能占用软件模拟方案成为更实用的选择。特别是当我们需要连接AT24C02这类I2C接口的EEPROM存储器时软件模拟可以提供更好的灵活性和可控性。提示选择软件模拟I2C时建议优先选用具有中断能力的GPIO引脚这样可以在不增加CPU负担的情况下实现更精确的时序控制。2. AT24C02存储器特性与硬件连接AT24C02是Atmel现Microchip公司生产的2Kbit256×8位串行EEPROM存储器采用I2C接口通信。其主要特性包括工作电压1.8V至5.5V16字节页写缓冲区100kHz1.8V和400kHz2.5V及以上兼容的I2C时序100万次擦写周期数据保存期100年在硬件连接方面AT24C02的典型电路设计需要注意以下几个关键点地址引脚配置AT24C02的A0-A2引脚用于设置器件地址。当这些引脚全部接地时器件的基本地址为0xA0写和0xA1读。如果系统中有多个AT24C02器件需要通过这些引脚设置不同的地址。上拉电阻I2C总线需要4.7kΩ左右的上拉电阻具体值根据总线电容调整确保SDA和SCL线在无设备驱动时能保持高电平。写保护WP引脚接高电平时禁止写入操作接低电平时允许写入。在大多数应用中建议直接接地以允许写入。在STM32F103开发板上典型的连接方式如下AT24C02的SCL引脚 → STM32的PC12可配置为任意GPIOAT24C02的SDA引脚 → STM32的PC11可配置为任意GPIOAT24C02的VCC → 3.3VAT24C02的GND → 地SDA和SCL线各接4.7kΩ上拉电阻至3.3V3. GPIO模拟I2C的底层驱动实现3.1 GPIO初始化配置软件模拟I2C的第一步是正确配置用于SDA和SCL的GPIO引脚。对于STM32F103的PC11和PC12引脚初始化代码如下// 宏定义I2C端口 #define I2C_SCL PCout(12) #define I2C_SDA PCout(11) // SDA数据方向控制宏 #define SDA_OUT() GPIO_Init(PC11, GPIO_Mode_Out_PP, GPIO_Speed_50MHz) #define SCL_OUT() GPIO_Init(PC12, GPIO_Mode_Out_PP, GPIO_Speed_50MHz) // 读取SDA数据 #define Read_SDA PCin(11) void SDA_IN(void) { GPIO_Init(PC11, GPIO_Mode_IPD); // 输入下拉模式 } void I2C_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); SDA_OUT(); SCL_OUT(); I2C_SCL 1; I2C_SDA 1; }这段代码实现了使能GPIOC时钟将PC11和PC12配置为推挽输出模式速度50MHz初始状态下拉高SCL和SDA线提供SDA方向切换功能输出/输入3.2 I2C基本信号生成I2C协议定义了四种基本信号起始条件、停止条件、数据有效和应答。以下是这些信号的软件实现起始信号STARTvoid I2C_Start(void) { SDA_OUT(); I2C_SDA 1; delay_us(5); I2C_SCL 1; delay_us(5); I2C_SDA 0; delay_us(5); I2C_SCL 0; }起始信号在SCL高电平时SDA从高到低的跳变。这个时序必须严格保持5μs的延时确保了信号稳定。停止信号STOPvoid I2C_Stop(void) { SDA_OUT(); I2C_SDA 0; delay_us(5); I2C_SCL 1; delay_us(5); I2C_SDA 1; delay_us(5); }停止信号在SCL高电平时SDA从低到高的跳变。这个信号表示通信结束。数据有效性I2C协议规定数据线SDA上的数据在SCL高电平期间必须保持稳定只有在SCL低电平时才能改变。每个字节传输后需要等待应答信号。应答信号处理u8 I2C_WaitAck(void) { u16 timeout 0; SDA_IN(); I2C_SCL 0; delay_us(5); I2C_SDA 1; delay_us(5); I2C_SCL 1; delay_us(5); while(Read_SDA) { timeout; if(timeout 2500) { I2C_Stop(); return 0; } } I2C_SCL 0; return 1; }这段代码实现了主机等待从机应答的功能。如果在超时时间内约12.5ms没有收到低电平应答则返回0表示失败。4. AT24C02的读写操作实现4.1 单字节写入操作AT24C02的单字节写入时序如下发送起始条件发送器件地址0xA0 写操作发送要写入的内存地址发送数据字节发送停止条件实现代码void Write24c02(u8 address, u8 byte) { I2C_Start(); I2C_WriteData(0xA0); I2C_WaitAck(); I2C_WriteData(address); I2C_WaitAck(); I2C_WriteData(byte); I2C_WaitAck(); I2C_Stop(); delay_ms(10); // 必须的写入周期等待 }重要提示AT24C02完成内部写入操作需要约5ms时间最大10ms在此期间不会响应任何操作。因此写入后必须加入足够延时否则后续操作会失败。4.2 随机读取操作AT24C02的随机读取时序较为复杂发送起始条件发送器件地址0xA0 写操作发送要读取的内存地址发送重复起始条件发送器件地址0xA1 读操作读取数据字节发送非应答信号NACK发送停止条件实现代码u8 Read24c02(u8 address) { u8 byte; I2C_Start(); I2C_WriteData(0xA0); I2C_WaitAck(); I2C_WriteData(address); I2C_WaitAck(); I2C_Start(); I2C_WriteData(0xA1); I2C_WaitAck(); byte I2C_ReadData(); I2C_Ack(0); // NACK I2C_Stop(); return byte; }4.3 页写入和顺序读取AT24C02支持页写入最多16字节和顺序读取功能可以显著提高大数据量传输效率。页写入实现void Write_24c02Buffer(u8 address, const u8 *str) { u8 length strlen((char *)str); for(u8 i0; ilength; i) { Write24c02(address, str[i]); } }顺序读取实现void Read_24c02Buffer(u8 address, u8 *str, u8 length) { for(u8 i0; ilength; i) { str[i] Read24c02(address); } str[length] \0; }5. 实际应用与调试技巧5.1 典型应用场景在STM32F103项目中AT24C02常用于存储系统配置参数用户设置信息运行日志记录校准数据固件更新标志5.2 调试技巧与常见问题逻辑分析仪调试使用Saleae Logic或PulseView等工具捕获I2C波形可以直观检查时序是否符合规范。常见问题排查无应答检查器件地址是否正确、上拉电阻是否合适、电源电压是否正常写入失败确保写入周期延时足够WP引脚未接高电平数据错误检查时序延时是否满足器件要求特别是SCL高电平时间性能优化适当减少延时可以提高通信速度但必须确保满足AT24C02的时序要求批量数据读写使用页操作减少通信开销关键数据考虑写入校验机制5.3 完整测试示例以下是一个完整的测试程序演示了AT24C02的读写功能int main(void) { u8 test_data[] STM32 I2C Test; u8 read_buffer[20]; // 系统初始化 SystemInit(); USART_Init(115200); I2C_Init(); // 写入测试数据 Write_24c02Buffer(0x00, test_data); // 读取并验证数据 Read_24c02Buffer(0x00, read_buffer, sizeof(test_data)-1); if(strcmp((char*)test_data, (char*)read_buffer) 0) { printf(I2C EEPROM Test PASSED!\r\n); } else { printf(I2C EEPROM Test FAILED!\r\n); } while(1); }在实际项目中建议为AT24C02操作添加错误重试机制提高通信可靠性。同时对于关键数据可以采用校验和或CRC校验确保数据完整性。