STM32(F0)与24LC16B EEPROM I2C通信失败求助(Arduino可正常工作)
分析STM32F0读取24LC16B EEPROM出现NACK的问题
嘿,我来帮你拆解下这个问题!既然Arduino测试完全正常,说明硬件接线和EEPROM本身没毛病,问题肯定出在STM32的I2C配置或者代码逻辑上,我整理了几个最可能的原因和解决思路:
1. 代码逻辑的核心错误:没发内存地址就终止传输
你的测试函数里,发起写传输时声明要发2字节,但实际上啥数据都没发(比如关键的EEPROM内存地址)就直接调用了停止指令。24LC16B的通信规则很明确:
- 读数据前,必须先通过写传输告诉它要读的内存地址(这是11位地址,要拆成两部分:高3位拼在设备地址的最后3位,低8位作为单独的字节发送)
- 你现在的代码只发了设备地址
0xA2,之后直接停了,EEPROM根本搞不懂你要干嘛,自然会返回NACK。
给你一个正确的读操作流程参考:
uint8_t readEEPROMData(uint16_t memAddress){ // 第一步:发送设备写地址 + 内存地址低8位 // 设备写地址 = 0xA0 | ((memAddress >> 8) & 0x07) << 1 (高3位是块地址) uint16_t deviceWriteAddr = 0xA0 | ((memAddress >> 8) & 0x07) << 1; I2C_TransferHandling(I2C1, deviceWriteAddr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); // 等待发送寄存器空,写入内存地址低8位 while(!(I2C1->ISR & I2C_ISR_TXIS)); I2C1->TXDR = (uint8_t)memAddress; // 等待写传输完成 while(!(I2C1->ISR & I2C_ISR_TC)); // 第二步:重复起始,发送设备读地址,准备接收1字节数据 uint16_t deviceReadAddr = deviceWriteAddr | 0x01; // 读地址是写地址+1 I2C_TransferHandling(I2C1, deviceReadAddr, 1, I2C_AutoEnd_Mode, I2C_Generate_Start_Read); // 等待接收数据 while(!(I2C1->ISR & I2C_ISR_RXNE)); uint8_t data = I2C1->RXDR; return data; }
2. STM32 I2C初始化的潜在坑
2.1 引脚配置必须是复用开漏
STM32的I2C引脚(比如PB6/SCL、PB7/SDA)一定要配置成复用开漏输出,而且必须带上拉电阻(建议用外部10kΩ的,内部上拉驱动能力弱,容易出问题)。要是你配置成推挽输出,I2C总线电平会乱套,EEPROM直接不响应,返回NACK。
检查你的引脚初始化代码,确保模式是GPIO_MODE_AF_OD,并且选对了对应的复用功能(比如I2C1的复用功能编号是4,不同引脚可能有差异,查下STM32F0的 datasheet)。
2.2 I2C时钟配置不能错
24LC16B支持最高400kHz的快速模式,但STM32的I2C时序配置必须严格符合要求:
- 首先,I2C外设的时钟来自APB1,必须是SCL频率的整数倍。比如APB1是48MHz,要跑100kHz的标准模式,CCR寄存器的值得是
48000000 / (2 * 100000) = 240;如果是400kHz快速模式,CCR要设成60。 - 要是时钟配置错了,SCL频率太高或者时序不匹配,EEPROM会直接拒绝响应,给你NACK。
另外,STM32F0用TIMINGR寄存器配置I2C时序,里面的上升/下降时间参数也要符合24LC16B datasheet的要求,别瞎填。
2.3 BUSY标志的等待逻辑搞反了吗?
你代码里的I2C_WaitForFlag(I2C_ISR_BUSY);,得确认这个函数是等BUSY标志清零(也就是总线空闲),而不是等它置1。要是逻辑反了,在总线忙的时候硬发起传输,肯定失败。
3. 容易忽略的细节问题
- ACK功能要开:初始化I2C的时候,记得开启ACK响应(设置
I2C_CR1的ACK位)。虽然写传输时是EEPROM给STM32发ACK,但如果STM32的ACK配置不对,可能会误把EEPROM的ACK当成NACK,导致判断错误。 - 电源要稳:虽然Arduino测着没问题,但STM32的电源波动可能影响I2C总线电平,确保EEPROM的VCC在2.5V~5.5V的范围内,别掉电压。
内容的提问来源于stack exchange,提问作者Mathlight




