Tiva TM4C123GXL I2C连接EEPROM读取返回0xFF问题求助
Tiva TM4C123GXL I2C读取EEPROM返回0xFF问题排查与修复
你当前的代码在I2C初始化、EEPROM读写流程上存在多处错误,导致通信失败,读取返回0xFF(通常表示未正确接收到数据)。以下是具体问题分析和修正方案:
问题代码
void write_i2c(unsigned int data) { int i; I2C3_MSA_R = 0xA0; I2C3_MDR_R = 0x00000000; I2C3_MCS_R = 0b00000011; while((I2C3_MCS_R & BIT_0) != 0); I2C3_MDR_R = 0x00000000; I2C3_MCS_R = 0b00000001; while((I2C3_MCS_R & BIT_0) != 0); I2C3_MDR_R = data; I2C3_MCS_R = 0b00000101; while((I2C3_MCS_R & BIT_0) != 0); for(i = 0; i < 10000; i++); } unsigned int read_i2c() { I2C3_MSA_R = 0xA0; I2C3_MDR_R = 0x00000000; I2C3_MCS_R = 0b00000011; while((I2C3_MCS_R & BIT_0) != 0); I2C3_MDR_R = 0x00000000; I2C3_MCS_R = 0b00000001; I2C3_MSA_R = 0xA1; unsigned int data; // 0xA0 is EEPROM address shifted by 1 I2C3_MCS_R = 0b00000101; // I2C3_MCS_R = 0b00000101; while((I2C3_MCS_R & BIT_0) != 0); data = I2C3_MDR_R; return data; } void init_i2c() { //address of eeprom is 1010000 // A0 is PB0, A1 is PB1, A2 is PB2 SYSCTL_RCGCI2C_R = BIT_3; GPIO_PORTD_PUR_R |= BIT_0 | BIT_1; // Port D Alternate Function // PD0 is SCL, PD1 is SDA GPIO_PORTD_AFSEL_R |= BIT_0 | BIT_1; GPIO_PORTD_PCTL_R |= 0x33; GPIO_PORTD_ODR_R |= BIT_1; GPIO_PORTD_DEN_R |= BIT_0 | BIT_1; // Enable I2C3 Master I2C3_MCR_R = BIT_4; I2C3_MTPR_R = 0x64; }
关键问题分析
- GPIO时钟未使能:初始化时只开启了I2C3的时钟,但未开启Port D的GPIO时钟,导致引脚功能无法正常工作,需添加Port D时钟使能。
- GPIO_PCTL_R赋值错误:当前
0x33仅设置了PD0/PD1的低2位,正确应使用0x00000033,避免干扰其他引脚的PCTL配置。 - I2C时钟速率配置错误:
I2C3_MTPR_R = 0x64会导致I2C速率远低于标准100kHz(假设系统时钟为16MHz,正确值应为0x4F)。 - 写入流程错误:EEPROM写入需遵循「设备写地址 → 内存地址 → 数据 → 停止」的流程,当前代码重复发送两次0x00,时序逻辑混乱。
- 读取流程错误:EEPROM读取需先通过写操作指定内存地址,再发送重启信号切换到读地址,当前代码未发送重启信号,直接切换地址导致通信中断。
修正后的代码
// 假设使用1字节内存地址的EEPROM(如24LC02),系统时钟为16MHz #define EEPROM_DEV_ADDR 0xA0 // 设备写地址,A0/A1/A2接地时为0xA0 #define EEPROM_READ_ADDR 0xA1 // 设备读地址 void init_i2c() { // 开启I2C3和Port D的时钟 SYSCTL_RCGCI2C_R |= BIT_3; SYSCTL_RCGCGPIO_R |= BIT_3; // 配置PD0(SCL)、PD1(SDA)为I2C功能 GPIO_PORTD_AFSEL_R |= BIT_0 | BIT_1; GPIO_PORTD_PCTL_R |= 0x00000033; // PD0/PD1的PCTL设置为I2C功能 GPIO_PORTD_ODR_R |= BIT_1; // SDA引脚开漏输出 GPIO_PORTD_DEN_R |= BIT_0 | BIT_1; GPIO_PORTD_PUR_R |= BIT_0 | BIT_1; // 上拉电阻增强信号 // 初始化I2C3为主模式 I2C3_MCR_R = BIT_4; // 使能主模式 // 16MHz系统时钟下,100kHz I2C速率对应的MTPR值 I2C3_MTPR_R = 0x4F; // (16000000/(2*100000)) -1 = 79 = 0x4F } // 向EEPROM指定内存地址写入1字节数据 void write_i2c(unsigned char mem_addr, unsigned char data) { // 1. 发送起始信号+设备写地址+内存地址 I2C3_MSA_R = EEPROM_DEV_ADDR; I2C3_MDR_R = mem_addr; I2C3_MCS_R = 0x03; // START + RUN // 等待传输完成 while((I2C3_MCS_R & BIT_0) == 0); // 检查NACK错误 if(I2C3_MCS_R & BIT_2) { I2C3_MCS_R = 0x04; // 发送STOP终止 return; } // 2. 发送数据+停止信号 I2C3_MDR_R = data; I2C3_MCS_R = 0x05; // RUN + STOP while((I2C3_MCS_R & BIT_0) == 0); if(I2C3_MCS_R & BIT_2) { I2C3_MCS_R = 0x04; return; } // EEPROM写入后需要等待写入周期(根据型号调整时长) for(int i=0; i<50000; i++); } // 从EEPROM指定内存地址读取1字节数据 unsigned char read_i2c(unsigned char mem_addr) { unsigned char data; // 1. 先发送写操作指定内存地址 I2C3_MSA_R = EEPROM_DEV_ADDR; I2C3_MDR_R = mem_addr; I2C3_MCS_R = 0x03; // START + RUN while((I2C3_MCS_R & BIT_0) == 0); if(I2C3_MCS_R & BIT_2) { I2C3_MCS_R = 0x04; return 0xFF; } // 2. 发送重启信号+设备读地址 I2C3_MSA_R = EEPROM_READ_ADDR; I2C3_MCS_R = 0x0B; // RESTART + RUN while((I2C3_MCS_R & BIT_0) == 0); if(I2C3_MCS_R & BIT_2) { I2C3_MCS_R = 0x04; return 0xFF; } // 3. 读取数据并发送停止信号 I2C3_MCS_R = 0x05; // RUN + STOP while((I2C3_MCS_R & BIT_0) == 0); data = I2C3_MDR_R; return data; }
额外注意事项
- 确认EEPROM的A0/A1/A2引脚连接是否与代码中的设备地址匹配(若引脚接VCC,需调整
EEPROM_DEV_ADDR的对应位)。 - 若使用2字节内存地址的EEPROM(如24LC256),需分两次发送内存地址的高字节和低字节。
- 每次读写后建议检查I2C状态寄存器的错误位(如NACK、BUSERROR),便于快速排查问题。
内容的提问来源于stack exchange,提问作者Lemon
相关产品推荐
相关产品推荐

