基于iMX8MP的嵌入式设备Linux用户空间EEPROM读取异常求助
排查iMX8MP Linux用户空间读取I2C EEPROM失败的思路
核心问题分析
你的代码使用I2C_SMBUS_WORD_DATA方式读取EEPROM,但大多数I2C EEPROM(如AT24C系列)的访问时序要求先写入起始地址,再连续读取数据,SMBus的单步读操作不符合这个时序,这是导致读取错误的主要原因。
具体排查步骤
1. 验证用户空间基础访问能力
先通过系统工具确认硬件和内核层面的I2C连通性:
- 执行
i2cdetect -y 0(需root权限),查看0x50地址的状态:- 若显示
UU:说明内核的EEPROM驱动(如at24)已占用该地址,需先卸载驱动:rmmod at24,再测试。 - 若显示数字(如
50):说明地址可被用户空间直接访问。
- 若显示
- 执行
i2cdump -y 0 0x50,查看输出是否与U-boot中i2c md的结果一致:- 一致:硬件和内核配置正常,问题出在代码逻辑。
- 不一致:检查设备树中i2c-0的引脚复用、时钟频率是否匹配EEPROM规格,或硬件接线是否松动。
2. 修正代码的I2C操作逻辑
替换原有的SMBus方式,改用I2C_RDWR ioctl实现符合EEPROM时序的组合读写操作,示例代码如下:
#include <stdio.h> #include <fcntl.h> #include <linux/i2c-dev.h> #include <linux/i2c.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #define I2C_ADDRESS 0x50 #define I2C_BUS "/dev/i2c-0" int main(void) { int file; int res; unsigned char data[16]; unsigned char start_addr = 0x00; // 单字节起始地址,适配小容量EEPROM(如AT24C02) struct i2c_rdwr_ioctl_data msgset; struct i2c_msg msgs[2]; file = open(I2C_BUS, O_RDWR); if (file < 0) { perror("open"); exit(1); } // 第一步:向EEPROM写入起始读取地址 msgs[0].addr = I2C_ADDRESS; msgs[0].flags = 0; // 写操作 msgs[0].len = sizeof(start_addr); msgs[0].buf = &start_addr; // 第二步:连续读取16字节数据 msgs[1].addr = I2C_ADDRESS; msgs[1].flags = I2C_M_RD; // 读操作 msgs[1].len = sizeof(data); msgs[1].buf = data; msgset.msgs = msgs; msgset.nmsgs = 2; res = ioctl(file, I2C_RDWR, &msgset); if (res < 0) { perror("ioctl I2C_RDWR"); exit(1); } printf("Data read: "); for (int i = 0; i < sizeof(data); i++) { printf("%02x ", data[i]); } printf("\n"); close(file); return 0; }
注意:如果是大容量EEPROM(如AT24C16及以上),需要使用双字节地址,将
start_addr改为数组unsigned char start_addr[2] = {0x00, 0x00};,并设置msgs[0].len = 2。
3. 检查内核I2C配置
- 确认设备树中i2c-0的节点配置正确:引脚复用、时钟频率(如
clock-frequency = <400000>)需与EEPROM规格匹配。 - 若不需要内核管理EEPROM,可在设备树中禁用at24驱动的自动探测,或编译内核时移除该驱动模块。
内容的提问来源于stack exchange,提问作者void_brain
相关产品推荐
相关产品推荐

