You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PIC32MK1024无法向外部24LC512 EEPROM写入数据求助

PIC32MK1024驱动24LC512 EEPROM写入无数据问题排查

我正在学习PIC32MK1024MCM064编程,开发项目的I2C通信模块,此前从未接触过MCU的I2C协议。当前目标是通过I2C向24LC512 EEPROM写入数据,用TL866II+编程器读取验证。逻辑分析仪显示SDA/SCL有正常100kbit/s(50kHz)数据包,但TL866II+读取EEPROM内存为空(已确认TL866II+工作正常)。

24LC512接线:Vdd接+3.3V,A2/A1/A0接地,WP接地(试过接Vdd结果相同),SDA和SCL均通过5.1kΩ上拉电阻连接,接线符合datasheet要求。怀疑是地址错误、SMBus设置、SDA保持时间或其他I2C配置细节问题,求助排查。

相关代码

// PIC32MK1024MCM064 
// DEVCFG3
#pragma config USERID = 0xFFFF          // Enter Hexadecimal value (Enter Hexadecimal value)
#pragma config PWMLOCK = OFF            // PWM IOxCON lock (PWM IOxCON register writes accesses are not locked or protected)
#pragma config FUSBIDIO2 = ON           // USB2 USBID Selection (USBID pin is controlled by the USB2 module)
#pragma config FVBUSIO2 = ON            // USB2 VBUSON Selection bit (VBUSON pin is controlled by the USB2 module)
#pragma config PGL1WAY = OFF            // Permission Group Lock One Way Configuration bit (Allow multiple reconfigurations)
#pragma config PMDL1WAY = OFF           // Peripheral Module Disable Configuration (Allow multiple reconfigurations)
#pragma config IOL1WAY = OFF            // Peripheral Pin Select Configuration (Allow multiple reconfigurations)
#pragma config FUSBIDIO1 = ON           // USB1 USBID Selection (USBID pin is controlled by the USB1 module)
#pragma config FVBUSIO1 = ON            // USB2 VBUSON Selection bit (VBUSON pin is controlled by the USB1 module)
// DEVCFG2
#pragma config FPLLIDIV = DIV_1         // System PLL Input Divider (1x Divider)
#pragma config FPLLRNG = RANGE_BYPASS   // System PLL Input Range (Bypass)
#pragma config FPLLICLK = PLL_FRC     // System PLL Input Clock Selection (FRC is input to the System PLL)
#pragma config FPLLMULT = MUL_2         // System PLL Multiplier (PLL Multiply by 1)
#pragma config FPLLODIV = DIV_2        // System PLL Output Clock Divider (2x Divider)
#pragma config BORSEL = HIGH            // Brown-out trip voltage (BOR trip voltage 2.1v (Non-OPAMP deviced operation))
#pragma config UPLLEN = OFF             // USB PLL Enable (USB PLL Disabled)
// DEVCFG1
#pragma config FNOSC = FRC              // Oscillator Selection Bits (Internal Fast RC (FRC))
#pragma config DMTINTV = WIN_0          // DMT Count Window Interval (Window/Interval value is zero)
#pragma config FSOSCEN = OFF            // Secondary Oscillator Enable (Disable Secondary Oscillator)
#pragma config IESO = OFF               // Internal/External Switch Over (Disabled)
#pragma config POSCMOD = OFF            // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = OFF           // CLKO Output Signal Active on the OSCO Pin (Disabled)
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor Selection (Clock Switch Disabled, FSCM Disabled)
#pragma config WDTPS = PS1              // Watchdog Timer Postscaler (1:1)
#pragma config WDTSPGM = STOP           // Watchdog Timer Stop During Flash Programming (WDT stops during Flash programming)
#pragma config WINDIS = NORMAL          // Watchdog Timer Window Mode (Watchdog Timer is in non-Window mode)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (WDT Disabled)
#pragma config FWDTWINSZ = WINSZ_25     // Watchdog Timer Window Size (Window size is 25%)
#pragma config DMTCNT = DMT31           // Deadman Timer Count Selection (2^31 (2147483648))
#pragma config FDMTEN = OFF             // Deadman Timer Enable (Deadman Timer is disabled)
// DEVCFG0
#pragma config DEBUG = OFF              // Background Debugger Enable (Debugger is disabled)
#pragma config JTAGEN = OFF             // JTAG Enable (JTAG Disabled)
#pragma config ICESEL = ICS_PGx3        // ICE/ICD Comm Channel Select (Communicate on PGEC3/PGED3)
#pragma config TRCEN = OFF              // Trace Enable (Trace features in the CPU are disabled)
#pragma config BOOTISA = MIPS32         // Boot ISA Selection (Boot code and Exception code is MIPS32)
#pragma config FECCCON = ECC_DECC_DISABLE_ECCON_WRITABLE// Dynamic Flash ECC Configuration Bits (ECC and Dynamic ECC are disabled (ECCCON<1:0> bits are writable))
#pragma config FSLEEP = OFF             // Flash Sleep Mode (Flash is powered down when the device is in Sleep mode)
#pragma config DBGPER = PG_ALL          // Debug Mode CPU Access Permission (Allow CPU access to all permission regions)
#pragma config SMCLR = MCLR_NORM        // Soft Master Clear Enable (MCLR pin generates a normal system Reset)
#pragma config SOSCGAIN = G3            // Secondary Oscillator Gain Control bits (Gain is G3)
#pragma config SOSCBOOST = ON           // Secondary Oscillator Boost Kick Start Enable bit (Boost the kick start of the oscillator)
#pragma config POSCGAIN = G3            // Primary Oscillator Coarse Gain Control bits (Gain Level 3 (highest))
#pragma config POSCBOOST = ON           // Primary Oscillator Boost Kick Start Enable bit (Boost the kick start of the oscillator)
#pragma config POSCFGAIN = G3           // Primary Oscillator Fine Gain Control bits (Gain is G3)
#pragma config POSCAGCDLY = AGCRNG_x_25ms// AGC Gain Search Step Settling Time Control (Settling time = 25ms x AGCRNG)
#pragma config POSCAGCRNG = ONE_X       // AGC Lock Range bit (Range 1x)
#pragma config POSCAGC = Automatic      // Primary Oscillator Gain Control bit (Automatic Gain Control for Oscillator)
#pragma config EJTAGBEN = NORMAL        // EJTAG Boot Enable (Normal EJTAG functionality)
// DEVCP
#pragma config CP = OFF                 // Code Protect (Protection Disabled)
// SEQ
#pragma config TSEQ = 0x0               // Boot Flash True Sequence Number (Enter Hexadecimal value)
#pragma config CSEQ = 0xFFFF            // Boot Flash Complement Sequence Number (Enter Hexadecimal value)
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include <xc.h>                         
#include <toolchain_specifics.h>         
#include <stddef.h>                     
#include <stdint.h>                     
#include <stdbool.h>                     
#include <stdlib.h> 
#include "stdio.h" 
#include <sys/attribs.h>                        
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#define CPU_CLOCK_FREQUENCY 8000000
#define _CP0_GET_COUNT()  _mfc0 (_CP0_COUNT, _CP0_COUNT_SELECT)
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#define MASTER_WRITE_24LC512_ADDRESS (0B10100000)     //Last bit is R/W (1 for reading, and 0 for writing)
#define MASTER_READ_24LC512_ADDRESS (0B10100001)      //Last bit is R/W (1 for reading, and 0 for writing)
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void delay_ms ( uint32_t delay_ms)
{
    uint32_t startCount, endCount;

    endCount=((CPU_CLOCK_FREQUENCY/1000)*delay_ms)/2;

    startCount=_CP0_GET_COUNT();
    while((_CP0_GET_COUNT()-startCount)<endCount);
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void I2C__initialization(void){
    
    //The datasheet declares that when the I2C module is enabled pins are assigned automatically 
    //(For I2C module nr.1 SCL is pin 5 and SDA is pin 6 ) and TRIS bits are overriden anyway.
    //I believe there is nothing else to do with I2C pin setup other than enabling the I2C module
    
    //PIC is operating in master mode, so address and address mask register are irrelevant for this application.
    
    //PB2CLK is same as SYSCLK here (8Mhz)
    
    ANSELG = 0xFFFFFE7F;          //Enable digital inputs for I2C pins (Clear ANSEL RG7 and RG8)        
    
    I2C1BRG = 0x00000010;         //Baud rate setup register (Here 50 kHz for the 100kb/s data rate)
    //---------------------------------------------------------------------------
    I2C1CON = 0x00000000;         //Resetting register to clear all bits (to ensure right setup)  
    I2C1CONbits.SDAHT = 0b0;      //Minimum of 100 ns hold time on SDA after the falling edge of SCL
    I2C1CONbits.SIDL = 0b1;       //Discontinue module operation when device enters Idle mode
    I2C1CONbits.SCLREL = 0b1;     //Release SCL clock
    I2C1CONbits.DISSLW = 0b1;     //Slew rate control is disabled
    I2C1CONbits.SMEN = 0b0;       //Disable SMBus input thresholds
    I2C1CONbits.ON = 0b1;         //Enable I2C module
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void I2C__state(void){
    while( (I2C1CON & 0x0000001F) || (I2C1STAT & 0x00000004) );  //Checking is the I2C bus is idle, waiting until it becomes idle (checking all I2C status bits)
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void I2C__Start(void){
    I2C__state();
    I2C1CONbits.SEN = 0b1;     //Initiate Start condition on SDAx and SCLx pins
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void I2C__Stop(void){
    I2C__state();
    I2C1CONbits.PEN = 0b1;    //Initiate Stop condition on SDAx and SCLx pins
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void I2C_Repeated_Start(void){
    I2C__state();
    I2C1CONbits.RSEN = 0b1;   //Initiate Repeated Start condition on SDAx and SCLx pins
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void I2C__Write(uint8_t I2C_data){
    I2C__state();
    while(I2C1STATbits.TBF != 0);        //Ensuring the transmit buffer is completely empty before the new transmission
    while(I2C1STATbits.TRSTAT != 0);     //Ensuring the transmission has ended
    I2C1TRN = I2C_data;                  //Loading the data to the I2C transmit buffer for transmission
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t I2C__Read (uint8_t ACK){
   uint8_t data = 0;
   I2C__state();
   I2C1CONbits.RCEN = 0b1;            //Enables Receive mode for I2C
   while(I2C1STATbits.RBF != 0);      //Ensuring the receive buffer is completely empty before new reception
   data = (I2C1RCV & 0x000000FF);     //Extracting lower 8 bits of the I2C data from a 32 bit I2C receive register
   I2C1CONbits.ACKEN = ACK;
   return data;
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int main ( void )
{ 
    TRISCbits.TRISC0 = 1;    //Button input setup
    
    ANSELC = 0x00000000;     //Enable PORT C digital inputs (VERY IMPORTANT) 
    
    TRISAbits.TRISA7 = 0; 
    LATAbits.LATA7 = 0;      //LED 1
    
    TRISBbits.TRISB14 = 0; 
    LATBbits.LATB14 = 0;     //LED 2
    
    TRISBbits.TRISB15 = 0; 
    LATBbits.LATB15 = 0;     //LED 3
    
    TRISGbits.TRISG6 = 0; 
    LATGbits.LATG6 = 0;      //LED 4
    
    TRISGbits.TRISG9 = 0; 
    LATGbits.LATG9 = 0;      //LED 5
    
    TRISAbits.TRISA12 = 0; 
    LATAbits.LATA12 = 0;     //LED 6
    
    TRISAbits.TRISA11 = 0; 
    LATAbits.LATA11 = 0;     //LED 7
     
    I2C__initialization();
     
    while (1)
    {       
        //Background blinking leds
        
        LATBbits.LATB14 = 1;
        delay_ms(200);
        LATBbits.LATB14 = 0;
        LATBbits.LATB15 = 1;
        delay_ms(200);
        LATBbits.LATB15 = 0;
        
        if(!PORTCbits.RC0){    //Button pressed condition
            LATAbits.LATA7 = 1; 
            I2C__Start();
            I2C__Write(MASTER_WRITE_24LC512_ADDRESS);
            I2C__Write(0x00);                          //EEPROM`s address higher 8 bits (random number for testing)
            I2C__Write(0x11);                          //EEPROM`s address lower 8 bits (random number for testing)
            I2C__Write(0x49);                          //Writing random number to the EEPROM memory cell
            I2C__Stop();
            delay_ms(200);
            LATAbits.LATA7 = 0; 
        }
            
    } 
    return (EXIT_FAILURE); 
}

逻辑分析仪截图

逻辑分析仪截图1
逻辑分析仪截图2
逻辑分析仪截图3
逻辑分析仪截图4


问题排查与解决方案

1. 未检查EEPROM的ACK响应

当前I2C写入函数未验证EEPROM返回的ACK信号,即使设备未正确接收数据,程序仍会继续执行。24LC512在接收每个字节(设备地址、内存地址、数据)后都会返回ACK,无ACK则说明通信异常。

修改I2C__Write函数添加ACK检查:

void I2C__Write(uint8_t I2C_data){
    I2C__state();
    while(I2C1STATbits.TBF != 0);        
    while(I2C1STATbits.TRSTAT != 0);     
    I2C1TRN = I2C_data;
    // 等待传输完成并检查ACK
    while(I2C1STATbits.TRSTAT != 0);
    if(I2C1STATbits.ACKSTAT){
        // 未收到ACK,添加错误提示(如点亮LED)
        LATGbits.LATG6 = 1;
        while(1);
    }
}

2. EEPROM写入后缺少必要等待时间

24LC512完成内部写入需要5~10ms周期,当前仅延迟200ms,若在此期间断电或读取,数据尚未写入存储单元。在I2C__Stop()后添加至少10ms延迟:

I2C__Stop();
delay_ms(10); // 等待EEPROM完成写入
delay_ms(200);
LATAbits.LATA7 = 0;

3. I2C引脚配置需手动确认

虽然模块会自动分配引脚,但建议手动配置引脚复用确保正确性,添加到I2C__initialization函数:

RPG7R = 0b0101; // RG7 分配给 SCL1
SDI1R = 0b0111; // RG8 分配给 SDA1

4. SDA保持时间优化

当前SDAHT=0对应100ns,若硬件布线或上拉电阻有延迟,可尝试设置为SDAHT=1(300ns),提升兼容性。

5. 逻辑分析仪细节验证

从截图确认每个字节发送后,EEPROM是否在第9个时钟周期拉低SDA返回ACK。若无ACK,需重新检查设备地址

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 05:24:17