我正在向 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;
我没有使用过这部分,但是手册13.6.4描述了该过程:
- 等到 EEPE 变为零。
- 等待SPMCSR中的SPMEN变为零。
- 将新的 EEPROM 地址写入 EEAR(可选)。
- 将新的 EEPROM 数据写入 EEDR(可选)。
- 向 EEMPE 位写入“1”,同时向 EECR 中的 EEPE 写入 0。
- 设置 EEMPE 后的四个时钟周期内,向 EEPE 写入“1”。
好的。
你不这样做。
您可以执行此操作,但仅限于 ls 字节 EEARL。这意味着如果地址大于 eeprom 偏移量 256,您的代码将无法工作,因为您没有写入 EEARH。或者,如果 EEARH 中留下杂散值,您的程序将会崩溃。
好的。
嗯。您正在使用不推荐的 C 位字段功能。我不确定
EECR.bits.EERE = TRUE;
是否会导致汇编程序级别的字节指令或位指令。如果是后者,EEPE 将不会像手册所说的那样被写入零。
建议的做法是永远不要使用位字段,而是立即写入整个寄存器,这样您就可以确定 EEPE 会被设置。
是的,在执行
EECR.bits.EEPE = TRUE;
之前没有4个时钟周期的延迟似乎是合理的。但这又取决于所有位域的结果。也许在最坏的情况下,某些东西会被转换为读入寄存器、修改寄存器、写入寄存器序列。在这种情况下,执行的时钟周期远远超过 4 个。
理解发生了什么的关键是查看程序的反汇编,看看实际生成了什么。即使对于像我这样从未使用过它的人来说,AVR asm 也很容易阅读。
与此无关,驱动程序中还有另一个严重的错误。驱动程序应该永远触摸全局中断掩码
cli
/sei
。特别是不要像这段代码那样破坏它以前的值。 EEPROM 驱动程序可能可以执行此操作,但前提是它通过重置 MCU 来结束整个写入会话。
通常的做法是通过安全模式进入 EEPROM 编程,在开始编程之前所有中断都被禁用。一旦完成,MCU 将通过等待 wdog 或向其寄存器写入不正确的序列来重新启动。
在 EEPROM 更新后重新开始执行是危险的做法,因为程序的所有部分不一定连续从 EEPROM 获取值。