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

ATmega328P EEPROM写入失败求助:写入后读取值始终为0xFF

ATmega328P EEPROM写入无效问题排查

问题详情

我使用以下代码对ATmega328P的EEPROM执行写入操作:

eeprom_write_byte(address, (U8_T) 0);

写入函数实现:

void eeprom_write_byte(void* address, U8_T data)
{
    cli();
    /* 等待上一次写入完成 */
    while (TRUE == EECR.bits.EEPE) {
    }

    /* 设置擦除后写入模式 */
    EECR.bits.EEPM0 = FALSE;
    EECR.bits.EEPM1 = FALSE;

    /* 配置写入参数 */
    EEAR.byte = (U8_T) address;
    EEDR.byte = data;

    /* 使能主写入 */
    EECR.bits.EEMPE = TRUE;
    /* 启动写入 */
    EECR.bits.EEPE = TRUE;
    sei();
}

寄存器地址映射如下:

SECTIONS {
  EEAR          = 0x41;
  EEDR          = 0x40;
  EECR          = 0x3F;
  ... 
}

我有个疑问:EEMPE和EEPE是否必须在4个时钟周期内先后置位?

读取EEPROM的代码如下:

U8_T eeprom_read_byte(void* address)
{
    cli();
    /* 等待上一次写入完成 */
    while (TRUE == EECR.bits.EEPE) {
    }

    /* 配置读取参数 */
    EEAR.byte = (U8_T) address;
    /* 启动读取 */
    EECR.bits.EERE = TRUE;

    sei();

    return EEDR.byte;
}

但读取结果始终是0xFF(EEPROM擦除后的默认值)。 datasheet明确EEPM0和EEPM1置0时为擦除后写入模式,但写入操作完全未生效。我试过用-O2编译无效果,-O3会导致链接失败,且航空领域开发禁止编译优化,必须在无优化环境下解决问题。

寄存器定义如下:

/** EEPROM控制寄存器 */
typedef union {
    struct {
        /** EEPROM读取使能 */
        VBOOL_T EERE        : 1;
        /** EEPROM写入使能 */
        VBOOL_T EEPE        : 1;
        /** EEPROM主写入使能 */
        VBOOL_T EEMPE       : 1;
        /** EEPROM就绪中断使能 */
        VBOOL_T EERIE       : 1;
        /** EEPROM模式位 */
        VBOOL_T EEPM0       : 1;
        VBOOL_T EEPM1       : 1;
        VBOOL_T Reserved6   : 1;
        VBOOL_T Reserved7   : 1;
    } bits;
    VU8_T byte;
} EECR_T;
/** EEPROM控制寄存器 */
extern volatile EECR_T EECR;

可能原因及修复方案

1. EEAR地址赋值不完整

ATmega328P的EEPROM是1KB容量,地址为16位(0x000~0x3FF),EEAR由EEARL(0x41)和EEARH(0x42)两个8位寄存器组成。你的代码仅将address强制转换为U8_T赋值给EEAR.byte(对应EEARL),高8位EEARH未被正确设置,导致写入地址与预期不符,读取自然得不到目标数据。

修复:
按16位完整地址赋值:

uint16_t eeprom_addr = (uint16_t)address;
EEARL = (uint8_t)(eeprom_addr & 0xFF);
EEARH = (uint8_t)(eeprom_addr >> 8);

2. 位字段顺序与硬件寄存器不匹配

ATmega328P的EECR寄存器位定义为:bit0=EERE、bit1=EEPE、bit2=EEMPE、bit3=EERIE、bit4=EEPM0、bit5=EEPM1。若编译器默认位字段从高到低排列(bit7对应结构体第一个成员),则你通过EECR.bits操作的是错误寄存器位,写入序列完全无效。

验证与修复:
改用字节位操作替代位字段,测试是否生效:

// 设置擦除后写入模式
EECR.byte &= ~((1 << 4) | (1 << 5)); // EEPM0=bit4,EEPM1=bit5
// 执行写入序列
EECR.byte |= (1 << 2); // 置位EEMPE(bit2)
EECR.byte |= (1 << 1); // 置位EEPE(bit1)

若此操作生效,需调整结构体位成员顺序,或统一使用字节位操作方式。

3. EEMPE与EEPE的时序不满足要求

是的,根据datasheet,EEMPE置位后必须在4个时钟周期内置位EEPE,否则EEMPE会自动清零,写入不触发。

无优化编译时,位字段操作可能生成多条指令(读字节→修改位→写回),导致两个置位操作间隔超过4个时钟周期。

修复:
使用直接字节操作保证时序紧凑:

// 连续执行置位操作,确保时序符合要求
EECR.byte |= (1 << 2);
EECR.byte |= (1 << 1);

4. 电源或时钟异常

ATmega328P的EEPROM写入对电源电压和时钟频率有要求(如5V供电时最低4.5V,时钟需满足时序规范),若电源不稳定、电压不足,或时钟配置错误,都会导致写入失败。

验证:
检查电源是否稳定在器件要求范围内,确认时钟配置(内部RC校准、外部晶振工作状态)符合EEPROM操作时序。

内容的提问来源于stack exchange,提问作者Matt Murphy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 10:22:41