找回密码
 立即注册
查看: 64|回复: 7

EEPROM为什么会写入不成功?

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:9
  • 最近打卡:2025-09-12 08:47:51
已绑定手机

6

主题

21

回帖

102

积分

注册会员

积分
102
发表于 4 天前 | 显示全部楼层 |阅读模式
请问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);
                                                        ;
                                        }
                                }
                        }
                }





回复

使用道具 举报 送花

3

主题

1987

回帖

822

积分

等待验证会员

积分
822
发表于 4 天前 | 显示全部楼层
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函数部分未完整展示,但从已有代码来看,存在以下几点需要注意:
  1. c
  2. EA = 0;
  3. nop();
复制代码
关闭中断是合理的做法,避免写入过程中被中断打断;
nop()延时可能用于稳定操作,但建议使用明确的延时函数(如delayms())以提高可读性和可移植性。
  1. c
  2. IAPCONTR = 0x80; // 使能IAP
  3. IAPTPS = 22;     // 设置等待参数
  4. IAPCMD = 1;      // 设置为读命令
复制代码
若该函数为读操作,命令1是正确的;
若后续为写操作,需改为写命令(如IAPCMD = 2);
确保IAPTPS的值与系统时钟匹配,否则可能导致操作失败。
  1. c
  2. 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型号,可进一步分析并给出更具体的建议。

[本文内容由国芯人工智能辅助生成,仅供参考]
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:9
  • 最近打卡:2025-09-12 08:47:51
已绑定手机

6

主题

21

回帖

102

积分

注册会员

积分
102
发表于 4 天前 | 显示全部楼层
有时候cw=0时,可以写入成功,不会进入死循环

点评

1.IapRead返回char类型,转为u16再进行位操作,一防溢出二防符号位进位 2.增加nop数量 [attachimg]114579[/attachimg] [attachimg]114580[/attachimg]  详情 回复 发表于 4 天前
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:298
  • 最近打卡:2025-09-14 11:31:57
已绑定手机

82

主题

3111

回帖

6883

积分

荣誉版主

无情的代码机器

积分
6883
发表于 4 天前 | 显示全部楼层
简*** 发表于 2025-9-10 17:27
有时候cw=0时,可以写入成功,不会进入死循环


1.IapRead返回char类型,转为u16再进行位操作,一防溢出二防符号位进位
2.增加nop数量
3.检查主频和IAP_TPS匹配,以及ISP软件下载时设置匹配,EEPROM大小分配匹配
截图202509101942567743.jpg
截图202509101943572742.jpg




三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:9
  • 最近打卡:2025-09-12 08:47:51
已绑定手机

6

主题

21

回帖

102

积分

注册会员

积分
102
发表于 4 天前 | 显示全部楼层
erci*** 发表于 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);
                                                        ;
                                        }
                                }
                        }


截图202509102037229458.jpg
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:298
  • 最近打卡:2025-09-14 11:31:57
已绑定手机

82

主题

3111

回帖

6883

积分

荣誉版主

无情的代码机器

积分
6883
发表于 4 天前 | 显示全部楼层
temp_cw   和cw声明类型不一样?


上传可复现工程看看
三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:9
  • 最近打卡:2025-09-12 08:47:51
已绑定手机

6

主题

21

回帖

102

积分

注册会员

积分
102
发表于 3 天前 | 显示全部楼层
erci*** 发表于 2025-9-10 22:21
temp_cw   和cw声明类型不一样?

temp_cw   和cw声明类型是一样的
谢谢了,现在写入,读取正常了
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:9
  • 最近打卡:2025-09-12 08:47:51
已绑定手机

6

主题

21

回帖

102

积分

注册会员

积分
102
发表于 3 天前 | 显示全部楼层
11
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-9-14 13:22 , Processed in 0.144809 second(s), 92 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表