ATmega328P EEPROM 无法写入

问题描述 投票:0回答:1

我正在向 ATmega328P EEPROM 写入数据,如下所示:

eeprom_write_byte(address, (U8_T) 0);
void eeprom_write_byte(void* address, U8_T data)
{
    cli();
    /* Wait for previous write to complete */
    while (TRUE == EECR.bits.EEPE) {
    }

    /* Set erase-and-write mode */
    EECR.bits.EEPM0 = FALSE;
    EECR.bits.EEPM1 = FALSE;

    /* Set up write */
    EEAR.byte = (U8_T) address;
    EEDR.byte = data;

    /* Enable master write */
    EECR.bits.EEMPE = TRUE;
    /* Enable write */
    EECR.bits.EEPE = TRUE;
    sei();
}

其中

EECR
EEAR
EEDR
的链接如下:

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

EEMPE
EEPE
肯定必须在彼此的 4 个时钟周期内设置,对吧?

然后我从 EEPROM 中读取如下:

U8_T eeprom_read_byte(void* address)
{
    cli();
    /* Wait for previous write to complete */
    while (TRUE == EECR.bits.EEPE) {
    }

    /* Set up read */
    EEAR.byte = (U8_T) address;
    /* Enable read */
    EECR.bits.EERE = TRUE;

    sei();

    return EEDR.byte;
}

但我只回来

0xFF
擦除值!数据表称 EEPM0 和 EEPM1 设置为 0 应该进行擦除和写入。但似乎没有任何书写发生。有什么想法吗?

我尝试使用 -O2 进行编译,但没有结果。另一篇文章告诉我使用 -O3,但是 -O3 破坏了我的程序(链接器失败)。不管怎样,我在航空航天领域工作,优化是被禁止的,所以它必须在没有它们的情况下工作。

寄存器定义如下:

/** EEPROM Control Register */
typedef union {
    struct {
        /** EEPROM Read Enable */
        VBOOL_T EERE        : 1;
        /** EEPROM Write Enable */
        VBOOL_T EEPE        : 1;
        /** EEPROM Master Write Enable */
        VBOOL_T EEMPE       : 1;
        /** EEPROM Ready Interrupt Enable */
        VBOOL_T EERIE       : 1;
        /** EEPROM Mode Bits */
        VBOOL_T EEPM0       : 1;
        VBOOL_T EEPM1       : 1;
        VBOOL_T Reserved6   : 1;
        VBOOL_T Reserved7   : 1;
    } bits;
    VU8_T byte;
} EECR_T;
/** EEPROM Control Register */
extern volatile EECR_T EECR;
c arduino avr arduino-uno avr-gcc
1个回答
0
投票

我没有使用过这部分,但是手册13.6.4描述了该过程:

  1. 等到 EEPE 变为零。
  2. 等待SPMCSR中的SPMEN变为零。
  3. 将新的 EEPROM 地址写入 EEAR(可选)。
  4. 将新的 EEPROM 数据写入 EEDR(可选)。
  5. 向 EEMPE 位写入“1”,同时向 EECR 中的 EEPE 写入 0。
  6. 设置 EEMPE 后的四个时钟周期内,向 EEPE 写入“1”。
  1. 好的。

  2. 你不这样做。

  3. 您可以执行此操作,但仅限于 ls 字节 EEARL。这意味着如果地址大于 eeprom 偏移量 256,您的代码将无法工作,因为您没有写入 EEARH。或者,如果 EEARH 中留下杂散值,您的程序将会崩溃。

  4. 好的。

  5. 嗯。您正在使用不推荐的 C 位字段功能。我不确定

    EECR.bits.EERE = TRUE;
    是否会导致汇编程序级别的字节指令或位指令。如果是后者,EEPE 将不会像手册所说的那样被写入零。

    建议的做法是永远不要使用位字段,而是立即写入整个寄存器,这样您就可以确定 EEPE 会被设置。

  6. 是的,在执行

    EECR.bits.EEPE = TRUE;
    之前没有4个时钟周期的延迟似乎是合理的。但这又取决于所有位域的结果。也许在最坏的情况下,某些东西会被转换为读入寄存器、修改寄存器、写入寄存器序列。在这种情况下,执行的时钟周期远远超过 4 个。

理解发生了什么的关键是查看程序的反汇编,看看实际生成了什么。即使对于像我这样从未使用过它的人来说,AVR asm 也很容易阅读。


与此无关,驱动程序中还有另一个严重的错误。驱动程序应该永远触摸全局中断掩码

cli
/
sei
。特别是不要像这段代码那样破坏它以前的值。 EEPROM 驱动程序可能可以执行此操作,但前提是它通过重置 MCU 来结束整个写入会话。

通常的做法是通过安全模式进入 EEPROM 编程,在开始编程之前所有中断都被禁用。一旦完成,MCU 将通过等待 wdog 或向其寄存器写入不正确的序列来重新启动。

在 EEPROM 更新后重新开始执行是危险的做法,因为程序的所有部分不一定连续从 EEPROM 获取值。

© www.soinside.com 2019 - 2024. All rights reserved.