EEPROM为什么会写入不成功?
请问EEPROM为什么会写入不成功?有时候cw=0时,不会进入死循环,有看门狗死循环时MCU会复位void IapIdle()
{
IAP_CONTR = 0; //关闭IAP功能
IAP_CMD = 0; //清除命令寄存器
IAP_TRIG = 0; //清除触发寄存器
IAP_ADDRH = 0x80; //将地址设置到非IAP区域
IAP_ADDRL = 0;
}
char IapRead(int addr)
{
char dat;
EA = 0;
_nop_();
IAP_CONTR = 0x80; //使能IAP
IAP_TPS = 22; //设置等待参数22.1184MHz
IAP_CMD = 1; //设置IAP读命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_();
dat = IAP_DATA; //读IAP数据
IapIdle(); //关闭IAP功能
EA = 1;
_nop_();
return dat;
}
// 从指定地址读取16位整数
unsigned int ReadData(unsigned int addr)
{
unsigned int dat = 0;
// 读取低字节
dat = IapRead(addr);
// 读取高字节
dat |= (IapRead(addr + 1) << 8);
return dat;
}
void IapProgram(int addr, char dat)
{
EA = 0;
_nop_();
IAP_CONTR = 0x80; //使能IAP
IAP_TPS = 22; //设置等待参数22.1184MHz
IAP_CMD = 2; //设置IAP写命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_DATA = dat; //写IAP数据
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_();
IapIdle(); //关闭IAP功能
EA = 1;
_nop_();
}
// 存储16位整数到指定地址
void StoreData(unsigned int addr, unsigned int dat)
{
// 写入低字节
IapProgram(addr, dat & 0xFF);
// 写入高字节
IapProgram(addr + 1, (dat >> 8) & 0xFF);
}
void IapErase(int addr)
{
EA = 0;
_nop_();
IAP_CONTR = 0x80; //使能IAP
IAP_TPS = 22; //设置等待参数22.1184MHz
IAP_CMD = 3; //设置IAP擦除命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_(); //
IapIdle(); //关闭IAP功能
EA = 1;
_nop_();
}
if(eeprom_time > 30000)
{
eeprom_time = 0;
eeprom_Flag = 0;
if(eeprom_LED)
{
eeprom_LED = 0;
temp_led = ReadData(0x0000);
if(temp_led != brightness)
{
IapErase(0x0000);
StoreData(0x0000, brightness);
temp_led = ReadData(0x0200);
while(temp_led != brightness);
;
}
}
if(LorH)
{
if(eeprom_cw)
{
eeprom_cw = 0;
temp_cw = ReadData(0x0200);
if(temp_cw != cw)
{
IapErase(0x0200);
StoreData(0x0200, cw);
temp_cw = ReadData(0x0200);
while(cw != temp_cw);
;
}
}
}
}
EEPROM写入不成功是嵌入式开发中较为常见的问题之一,尤其是在使用IAP(在应用编程)方式进行EEPROM操作时,可能受到硬件配置、时序控制、中断干扰、电源稳定性等多种因素的影响。下面将从多个角度分析可能导致EEPROM写入失败的原因,并结合您提供的代码片段进行分析和建议。
一、EEPROM写入失败的常见原因
1. IAP功能未正确使能
在进行EEPROM写入操作前,必须正确配置IAP相关的寄存器,包括IAPCONTR、IAPTPS、IAPCMD等。如果未正确设置使能位或命令字,将导致写入操作无法启动。
建议:
确保IAPCONTR被设置为正确的使能值(如0x80),并根据MCU手册设置合适的等待周期(IAPTPS)。
2. 地址设置错误
写入EEPROM时,必须确保地址设置正确。如果地址指向了受保护区域(如IAP引导区)或超出EEPROM地址范围,写入操作将失败或导致系统异常。
建议:
检查IAPADDRH和IAPADDRL的设置是否正确,确保目标地址在合法的EEPROM地址范围内。
3. 触发寄存器未正确写入
在IAP操作中,IAPTRIG寄存器用于触发实际的写入或读取动作,通常需要按照特定顺序写入特定值(如0x5a和0xa5)。若触发值写入错误或未完成,操作将不会执行。
建议:
确认触发值写入顺序是否符合MCU手册要求,例如某些芯片需要先写0x5a再写0xa5,且中间不能有其他操作打断。
4. 中断或看门狗干扰
在执行IAP写入操作期间,若发生中断或看门狗复位,可能会导致写入中断或数据不完整。特别是在使用看门狗的情况下,若未及时“喂狗”,MCU可能会复位,中断当前写入流程。
建议:
在执行IAP操作前关闭全局中断(EA = 0);
若使用看门狗,确保在写入过程中适当位置“喂狗”;
写入完成后恢复中断和看门狗功能。
5. 电源电压不稳定或低于工作电压
EEPROM写入操作通常需要较高的电压(如高于4.5V)来完成电荷泵操作。若电源电压不足,可能导致写入失败甚至损坏存储单元。
建议:
确保系统供电稳定;
在低电压环境下避免执行写入操作;
可加入电压检测机制,在电压不足时禁止写入。
6. 多次快速写入未等待完成
EEPROM写入操作有一定的时间延迟,若连续多次调用写入函数而未等待上一次操作完成,可能导致冲突或失败。
建议:
在每次写入后加入适当的延时或等待操作完成标志位(如通过轮询状态寄存器)。
7. EEPROM寿命耗尽
EEPROM的擦写次数是有限的(通常为10万次左右),如果某地址已被频繁写入,可能导致单元损坏,无法再次写入。
建议:
实现磨损均衡算法以延长EEPROM寿命;
定期检测关键数据区的写入状态;
使用外部EEPROM芯片作为替代方案。
二、针对您提供的代码片段分析
您提供的代码中,IapRead函数部分未完整展示,但从已有代码来看,存在以下几点需要注意:
c
EA = 0;
nop();
关闭中断是合理的做法,避免写入过程中被中断打断;
nop()延时可能用于稳定操作,但建议使用明确的延时函数(如delayms())以提高可读性和可移植性。
c
IAPCONTR = 0x80; // 使能IAP
IAPTPS = 22; // 设置等待参数
IAPCMD = 1; // 设置为读命令
若该函数为读操作,命令1是正确的;
若后续为写操作,需改为写命令(如IAPCMD = 2);
确保IAPTPS的值与系统时钟匹配,否则可能导致操作失败。
c
IAPTRIG = 0x5a;
该触发值是否正确取决于MCU型号,有些芯片需要两次写入(如先0x5a再0xa5);
建议查阅MCU的数据手册确认触发顺序。
三、关于看门狗与死循环的问题
您提到“有时候CW=0时,不会进入死循环,有看门狗死循环时MCU会复位”,这说明:
当前代码中可能存在未正确喂狗的情况;
若程序进入死循环而未喂狗,看门狗会触发复位;
CW=0可能是指某种控制标志或看门狗使能位,需结合具体MCU文档确认。
建议:
在所有可能长时间运行的循环中加入喂狗语句(如CLRWDT(););
在IAP操作前后恢复看门狗状态;
对于关键操作,设置超时机制防止程序卡死。
四、总结与建议
EEPROM写入失败通常不是单一因素造成的,而是由多个环节共同影响的结果。建议从以下几个方面进行排查和优化:
1. 检查IAP寄存器配置是否符合MCU手册要求;
2. 确保地址和命令设置正确;
3. 在写入过程中关闭中断并适当喂狗;
4. 加入延时或等待机制,确保操作完成;
5. 检查电源电压是否稳定;
6. 避免频繁写入同一地址;
7. 对关键操作加入状态判断和错误处理机制。
通过系统性地排查以上可能原因,并结合示波器或调试器监控关键信号(如触发信号、中断标志、状态寄存器等),可以有效定位问题并提升EEPROM操作的可靠性。
如能提供更完整的代码或具体MCU型号,可进一步分析并给出更具体的建议。
[本文内容由国芯人工智能辅助生成,仅供参考] 有时候cw=0时,可以写入成功,不会进入死循环 简清 发表于 2025-9-10 17:27
有时候cw=0时,可以写入成功,不会进入死循环
1.IapRead返回char类型,转为u16再进行位操作,一防溢出二防符号位进位
2.增加nop数量
3.检查主频和IAP_TPS匹配,以及ISP软件下载时设置匹配,EEPROM大小分配匹配
ercircle 发表于 2025-9-10 19:44
1.IapRead返回char类型,转为u16再进行位操作,一防溢出二防符号位进位
2.增加nop数量
3.检查主频和IAP_T ...
增加了nop数量,
主频,IAP_TPS和ISP软件下载的设置匹配,22.1184MHz,EEPROM大小固定的
效果和之前一样,有时候能保存成功
void IapIdle()
{
IAP_CONTR = 0; //关闭IAP功能
IAP_CMD = 0; //清除命令寄存器
IAP_TRIG = 0; //清除触发寄存器
IAP_ADDRH = 0x80; //将地址设置到非IAP区域
IAP_ADDRL = 0;
}
char IapRead(int addr)
{
char dat;
EA = 0;
_nop_();
IAP_CONTR = 0x80; //使能IAP
IAP_TPS = 22; //设置等待参数22.1184MHz
IAP_CMD = 1; //设置IAP读命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_();
_nop_();
_nop_();
_nop_();
dat = IAP_DATA; //读IAP数据
IapIdle(); //关闭IAP功能
EA = 1;
_nop_();
return dat;
}
// 从指定地址读取16位整数
unsigned int ReadData(unsigned int addr)
{
unsigned int dat = 0;
// 读取低字节
(u16)dat = IapRead(addr);
// 读取高字节
(u16)dat |= ((u16)IapRead(addr + 1) << 8);
return dat;
}
void IapProgram(int addr, char dat)
{
EA = 0;
_nop_();
IAP_CONTR = 0x80; //使能IAP
IAP_TPS = 22; //设置等待参数22.1184MHz
IAP_CMD = 2; //设置IAP写命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_DATA = dat; //写IAP数据
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_();
_nop_();
_nop_();
_nop_();
IapIdle(); //关闭IAP功能
EA = 1;
_nop_();
}
// 存储16位整数到指定地址
void StoreData(unsigned int addr, unsigned int dat)
{
// 写入低字节
IapProgram(addr, dat & 0xFF);
// 写入高字节
IapProgram(addr + 1, (dat >> 8) & 0xFF);
}
void IapErase(int addr)
{
EA = 0;
_nop_();
IAP_CONTR = 0x80; //使能IAP
IAP_TPS = 22; //设置等待参数22.1184MHz
IAP_CMD = 3; //设置IAP擦除命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_(); //
_nop_();
_nop_();
_nop_();
IapIdle(); //关闭IAP功能
EA = 1;
_nop_();
}
if(eeprom_LED)
{
eeprom_LED = 0;
temp_led = ReadData(0x0000);
if(temp_led != brightness)
{
IapErase(0x0000);
Delay8ms();
StoreData(0x0000, brightness);
temp_led = ReadData(0x0200);
while(temp_led != brightness);
;
}
}
if(LorH)
{
if(eeprom_cw)
{
eeprom_cw = 0;
temp_cw = ReadData(0x0200);
if(temp_cw != cw)
{
IapErase(0x0200);
Delay8ms();
StoreData(0x0200, cw);
temp_cw = ReadData(0x0200);
while(cw != temp_cw);
;
}
}
}
temp_cw 和cw声明类型不一样?
上传可复现工程看看
ercircle 发表于 2025-9-10 22:21
temp_cw 和cw声明类型不一样?
temp_cw 和cw声明类型是一样的
谢谢了,现在写入,读取正常了 11
页:
[1]