找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 168|回复: 4
打印 上一主题 下一主题
收起左侧

EEPROM存储的数据,断电后再上电,数据发生变化

[复制链接]
跳转到指定楼层
楼主
ID:74143 发表于 2026-4-11 09:27 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
请教,EEPROM存储的数据,断电后再上电,数据发生变化的原因。

#include"STC8G.H"
#include"intrins.H"
#include"ADC.h"
#include"NTC.h"
#include"GPIO.h"
#include"TIMER.h"
#include"HC595_DISPLAY.h"

//==================== 引脚定义 ====================
sbit KEY_ADD  = P3^2;   // 加键
sbit KEY_SUB  = P1^4;   // 减键
sbit KEY_OK   = P1^7;   // 确认键

sbit BEEP     = P1^5;   // 蜂鸣器

sbit ADC_TEMP = P1^1;   

unsigned int temp_real;    // 实际温度值
unsigned int temp_high;     // 高温阈值
unsigned int temp_low;      // 低温阈值
unsigned char set_mode = 0;        // 设置模式标志:0=正常模式,1=设置高温,2=设置低温

// EEPROM 地址定义
#define EEPROM_ADDR_H 0x0000  
#define EEPROM_ADDR_L 0x0001  
#define EEPROM_ADDR   0x0000

void IAP_IDLE(void);
unsigned char IAP_Read(unsigned int addr);
void IAP_Write(unsigned int addr,unsigned char dat);
void IAP_Erase(unsigned int addr);

// 延时函数
void Delay_ms(unsigned int ms)
{
        unsigned int i,j;
                for(i=0;i<ms;i++)
                        for(j=0;j<24*85;j++);
}


void IAP_IDLE(void)
{
        IAP_CONTR = 0;   
        IAP_CMD = 0;      
        IAP_TRIG = 0;     
        IAP_ADDRH = 0x80;
        IAP_ADDRL = 0;   
}

// 单字节读
unsigned char IAP_Read(unsigned int addr)
{
        char dat;
        IAP_CONTR = 0x80;   
        IAP_TPS = 24;
        IAP_CMD = 0x01;   
        IAP_ADDRL = addr;   
        IAP_ADDRH = addr >> 8;
        IAP_TRIG = 0x5a;     
        IAP_TRIG = 0xa5;   
        _nop_();            
        dat = IAP_DATA;             
        IAP_IDLE();         
        return dat;
}

// 单字节写
void IAP_Write(unsigned int addr,unsigned char dat)
{
        IAP_CONTR = 0x80;      
        IAP_TPS = 24;
        IAP_CMD = 0x02;     
        IAP_ADDRL = addr;   
        IAP_ADDRH = addr >> 8;
        IAP_DATA = dat;             
        IAP_TRIG = 0x5a;     
        IAP_TRIG = 0xa5;      
        _nop_();           
        IAP_IDLE();
}

void IAP_Erase(unsigned int addr)
{
        IAP_CONTR = 0x80;   
        IAP_TPS = 24;
        IAP_CMD   = 0x03;   
        IAP_ADDRL = addr;
        IAP_ADDRH = addr >> 8;
        IAP_TRIG  = 0x5A;
        IAP_TRIG  = 0xA5;
        _nop_();              
        IAP_IDLE();
}


void Key_Scan(void)
{
  if(KEY_OK == 0)  // 确认键按下
  {
    Delay_ms(20);
    if(KEY_OK == 0)
    {
      set_mode++;
      if(set_mode > 2) set_mode = 0;
      
      // 退出设置模式时,保存参数到EEPROM
      if(set_mode == 0)
      {
        IAP_Erase(EEPROM_ADDR);  // 先擦除片区
                                IAP_Write(EEPROM_ADDR_H, temp_high);   // 保存高温
                                IAP_Write(EEPROM_ADDR_L, temp_low);  // 保存低温      }
      
      while(KEY_OK == 0); // 等待松开
    }
  }
  
  if(set_mode != 0) // 设置模式下才响应加减键
  {
    if(KEY_ADD == 0)
    {
      Delay_ms(20);
      if(KEY_ADD == 0)
      {
        if(set_mode == 1) temp_high++;  // 设置高温
        if(set_mode == 2) temp_low++;   // 设置低温
        while(KEY_ADD == 0);
      }                                                 
    }
   
    if(KEY_SUB == 0)
    {
      Delay_ms(20);
      if(KEY_SUB == 0)
      {
        if(set_mode == 1 && temp_high > 0) temp_high--;
        if(set_mode == 2 && temp_low > 0) temp_low--;
        while(KEY_SUB == 0);
      }
    }
  }
}

// 报警判断函数
void Alarm_Check(void)
{
  if(set_mode == 0) // 正常模式才报警
  {
    if(temp_real > temp_high || temp_real < temp_low)
                        BEEP = 0;  // 超阈值,蜂鸣器响
    else
                        BEEP = 1;  // 正常,蜂鸣器关
  }
  else
  {
                BEEP = 1; // 设置模式关闭报警
  }
}

// 主函数
void main(void)
{
  unsigned char        i;
        unsigned int        j;
       
        // 初始化
  GPIO_Init();         
  ADC_Init();// ADC初始化
        Timer0_Init();

        BEEP = 1;

        for(i=0; i<8; i++)        DisplayBuff [i] = 0xff;        //上电消隐        1111 1111
  

  temp_high = IAP_Read(EEPROM_ADDR_H);
        temp_low  = IAP_Read(EEPROM_ADDR_L);

  
  // 上电默认值(防止首次使用EEPROM无数据)
  if(temp_high == 0xFF || temp_high == 0) temp_high = 300;
  if(temp_low  == 0xFF || temp_low  == 0) temp_low  = 50;

  while(1)
  {
    if(!AUTO_ISP) IAP_CONTR = 0x60;

                Key_Scan();        // 按键扫描

                while(!g_bSystemTime1Ms)        ;        //等待1ms到
                g_bSystemTime1Ms = 0;
               
                if(++j >= 40)                //40ms到
                {
                        j = 0;

                        temp_real = Get_temperature(ADC_Read_Oversample(1)); // 采集实际温度

                        if(temp_real >= 0)
                        {
                                F0 = 0;       
        temp_real = temp_real;      
            }
                        else               
            {
        F0 = 1;       
        temp_real = -temp_real;   
            }
                       
                        DisplayBuff[7] = (temp_real / 1000);     //显示数值
      DisplayBuff[6] = (temp_real / 100) % 10;
      DisplayBuff[5] = (temp_real / 10) % 10 + DIS_DOT;        //十位 + 小数点(带小数点显示)
      DisplayBuff[4] = temp_real % 10;                                       

                        // 千位为 0 时不显示(消零),显示黑位(不亮)
            if(DisplayBuff[7] == 0)   
      DisplayBuff[7] = DIS_BLACK;

           
            if(F0 == 1)  
      DisplayBuff[7] = DIS_;

                        if((temp_real > 1200) && (temp_real < TEMP_MIN*10))
                        {
                                for(i=0; i<4; i++)        DisplayBuff[i] = DIS_;
                        }


                        if(temp_high >= 0)
                        {
                                F0 = 0;       
        temp_high = temp_high;      
            }
                        else               
            {
        F0 = 1;       
        temp_high = -temp_high;   
            }
                        DisplayBuff[3] = (temp_high / 1000);     //显示数值
      DisplayBuff[2] = (temp_high / 100) % 10;
      DisplayBuff[1] = (temp_high / 10) % 10 + DIS_DOT;        //十位 + 小数点(带小数点显示)
      DisplayBuff[0] = temp_high % 10;                                               

                       
            if(DisplayBuff[3] == 0)   
      DisplayBuff[3] = DIS_BLACK;

           
            if(F0 == 1)  
      DisplayBuff[3] = DIS_;

                        if((temp_high > 1200) && (temp_high < TEMP_MIN*10))
                        {
                                for(i=0; i<4; i++)        DisplayBuff[i] = DIS_;
                        }   
                }
    Alarm_Check();        // 报警判断
  }
}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:76027 发表于 2026-4-11 19:46 | 只看该作者
重新上电后,你的数据是这个: // 上电默认值(防止首次使用EEPROM无数据)
  if(temp_high == 0xFF || temp_high == 0) temp_high = 300;
  if(temp_low  == 0xFF || temp_low  == 0) temp_low  = 50;
回复

使用道具 举报

板凳
ID:76027 发表于 2026-4-11 19:49 | 只看该作者
unsigned int temp_high = 300;     // 高温阈值
unsigned int temp_low = 50;      // 低温阈值 再注释点这个试试 // 上电默认值(防止首次使用EEPROM无数据)
  //if(temp_high == 0xFF || temp_high == 0) temp_high = 300;
// if(temp_low  == 0xFF || temp_low  == 0) temp_low  = 50;
回复

使用道具 举报

地板
ID:1167595 发表于 2026-4-12 18:50 | 只看该作者
花了点时间,给楼主的源码加上适合初学者学习使用的全注释源码

代码功能概述:这是一个基于STC8G单片机的温度监控系统,具有以下功能:
1)通过NTC测温并显示实际温度
2)可通过按键设置高温/低温报警阈值
3)阈值参数存储于EEPROM
4)温度超限时蜂鸣器报警
5)使用74HC595驱动数码管显示温度值


  1. #include"STC8G.H"          // 包含STC8G系列单片机头文件
  2. #include"intrins.H"        // 包含内联函数头文件(如_nop_延时)
  3. #include"ADC.h"            // 包含ADC模块头文件
  4. #include"NTC.h"            // 包含NTC温度传感器头文件
  5. #include"GPIO.h"           // 包含GPIO初始化头文件
  6. #include"TIMER.h"          // 包含定时器头文件
  7. #include"HC595_DISPLAY.h"  // 包含74HC595显示驱动头文件

  8. //==================== 引脚定义 ====================
  9. sbit KEY_ADD  = P3 ^ 2; // 定义加键连接到P3.2引脚
  10. sbit KEY_SUB  = P1 ^ 4; // 定义减键连接到P1.4引脚
  11. sbit KEY_OK   = P1 ^ 7; // 定义确认键连接到P1.7引脚

  12. sbit BEEP     = P1 ^ 5; // 定义蜂鸣器连接到P1.5引脚
  13. sbit ADC_TEMP = P1 ^ 1; // 定义温度ADC输入引脚P1.1

  14. unsigned int temp_real;    // 存储实际温度值
  15. unsigned int temp_high;    // 存储高温报警阈值
  16. unsigned int temp_low;     // 存储低温报警阈值
  17. unsigned char set_mode = 0;  // 设置模式标志:0=正常模式,1=设置高温,2=设置低温

  18. // EEPROM 地址定义
  19. #define EEPROM_ADDR_H 0x0000  // 高温阈值存储地址
  20. #define EEPROM_ADDR_L 0x0001  // 低温阈值存储地址
  21. #define EEPROM_ADDR   0x0000  // EEPROM起始地址(备用)

  22. void IAP_IDLE(void);                       // 声明IAP空闲函数
  23. unsigned char IAP_Read(unsigned int addr); // 声明IAP读函数
  24. void IAP_Write(unsigned int addr, unsigned char dat); // 声明IAP写函数
  25. void IAP_Erase(unsigned int addr);         // 声明IAP擦除函数

  26. // 延时函数(毫秒级)
  27. void Delay_ms(unsigned int ms)
  28. {
  29.     unsigned int i, j;
  30.     for(i = 0; i < ms; i++)     // 外层循环控制毫秒数
  31.         for(j = 0; j < 24 * 85; j++); // 内层循环实现约1ms延时
  32. }

  33. // IAP空闲函数(关闭IAP功能)
  34. void IAP_IDLE(void)
  35. {
  36.     IAP_CONTR = 0;   // 关闭IAP功能
  37.     IAP_CMD = 0;     // 清除命令寄存器
  38.     IAP_TRIG = 0;    // 清除触发寄存器
  39.     IAP_ADDRH = 0x80; // 设置地址高字节
  40.     IAP_ADDRL = 0;   // 设置地址低字节
  41. }

  42. // 单字节读函数
  43. unsigned char IAP_Read(unsigned int addr)
  44. {
  45.     char dat;                          // 定义数据存储变量
  46.     IAP_CONTR = 0x80;   // 使能IAP,设置等待时间
  47.     IAP_TPS = 24;       // 设置IAP时钟分频
  48.     IAP_CMD = 0x01;   // 设置读命令
  49.     IAP_ADDRL = addr;   // 设置地址低字节
  50.     IAP_ADDRH = addr >> 8; // 设置地址高字节
  51.     IAP_TRIG = 0x5a;   // 触发访问(先写0x5A)
  52.     IAP_TRIG = 0xa5;   // 触发访问(再写0xA5)
  53.     _nop_();           // 空操作延时
  54.     dat = IAP_DATA;    // 读取数据
  55.     IAP_IDLE();        // 进入IAP空闲模式
  56.     return dat;        // 返回读取的数据
  57. }

  58. // 单字节写函数
  59. void IAP_Write(unsigned int addr, unsigned char dat)
  60. {
  61.     IAP_CONTR = 0x80;   // 使能IAP,设置等待时间
  62.     IAP_TPS = 24;       // 设置IAP时钟分频
  63.     IAP_CMD = 0x02;   // 设置写命令
  64.     IAP_ADDRL = addr;   // 设置地址低字节
  65.     IAP_ADDRH = addr >> 8; // 设置地址高字节
  66.     IAP_DATA = dat;    // 写入数据
  67.     IAP_TRIG = 0x5a;   // 触发访问(先写0x5A)
  68.     IAP_TRIG = 0xa5;   // 触发访问(再写0xA5)
  69.     _nop_();           // 空操作延时
  70.     IAP_IDLE();        // 进入IAP空闲模式
  71. }

  72. // 扇区擦除函数
  73. void IAP_Erase(unsigned int addr)
  74. {
  75.     IAP_CONTR = 0x80;   // 使能IAP,设置等待时间
  76.     IAP_TPS = 24;       // 设置IAP时钟分频
  77.     IAP_CMD   = 0x03;   // 设置擦除命令
  78.     IAP_ADDRL = addr;   // 设置地址低字节
  79.     IAP_ADDRH = addr >> 8; // 设置地址高字节
  80.     IAP_TRIG  = 0x5A;   // 触发访问(先写0x5A)
  81.     IAP_TRIG  = 0xA5;   // 触发访问(再写0xA5)
  82.     _nop_();            // 空操作延时
  83.     IAP_IDLE();         // 进入IAP空闲模式
  84. }

  85. // 按键扫描函数
  86. void Key_Scan(void)
  87. {
  88.     if(KEY_OK == 0)  // 检测确认键是否按下(低电平有效)
  89.     {
  90.         Delay_ms(20);  // 延时消抖
  91.         if(KEY_OK == 0) // 再次确认按键按下
  92.         {
  93.             set_mode++;   // 模式切换
  94.             if(set_mode > 2) set_mode = 0; // 模式循环:0->1->2->0

  95.             // 退出设置模式时,保存参数到EEPROM
  96.             if(set_mode == 0)
  97.             {
  98.                 IAP_Erase(EEPROM_ADDR);  // 先擦除扇区
  99.                 IAP_Write(EEPROM_ADDR_H, temp_high);   // 保存高温阈值
  100.                 IAP_Write(EEPROM_ADDR_L, temp_low);  // 保存低温阈值
  101.             } // 注意:原代码这里缺少右花括号,已补全

  102.             while(KEY_OK == 0); // 等待按键释放
  103.         }
  104.     }

  105.     if(set_mode != 0) // 仅在设置模式下响应加减键
  106.     {
  107.         if(KEY_ADD == 0) // 检测加键
  108.         {
  109.             Delay_ms(20);
  110.             if(KEY_ADD == 0)
  111.             {
  112.                 if(set_mode == 1) temp_high++;  // 增加高温阈值
  113.                 if(set_mode == 2) temp_low++;   // 增加低温阈值
  114.                 while(KEY_ADD == 0); // 等待按键释放
  115.             }
  116.         }

  117.         if(KEY_SUB == 0) // 检测减键
  118.         {
  119.             Delay_ms(20);
  120.             if(KEY_SUB == 0)
  121.             {
  122.                 if(set_mode == 1 && temp_high > 0) temp_high--; // 减少高温阈值(不低于0)
  123.                 if(set_mode == 2 && temp_low > 0) temp_low--;  // 减少低温阈值(不低于0)
  124.                 while(KEY_SUB == 0); // 等待按键释放
  125.             }
  126.         }
  127.     }
  128. }

  129. // 报警判断函数
  130. void Alarm_Check(void)
  131. {
  132.     if(set_mode == 0) // 仅在正常模式进行报警判断
  133.     {
  134.         if(temp_real > temp_high || temp_real < temp_low)
  135.             BEEP = 0;  // 温度超限,蜂鸣器响(低电平驱动)
  136.         else
  137.             BEEP = 1;  // 温度正常,关闭蜂鸣器
  138.     }
  139.     else
  140.     {
  141.         BEEP = 1; // 设置模式下关闭蜂鸣器
  142.     }
  143. }

  144. // 主函数
  145. void main(void)
  146. {
  147.     unsigned char        i;        // 循环变量
  148.     unsigned int        j;       // 时间计数变量

  149.     // 初始化
  150.     GPIO_Init();         // GPIO初始化
  151.     ADC_Init();          // ADC初始化
  152.     Timer0_Init();       // 定时器0初始化

  153.     BEEP = 1;             // 初始化关闭蜂鸣器

  154.     for(i = 0; i < 8; i++)        DisplayBuff [i] = 0xff; // 显示缓冲区初始化(全灭)


  155.     temp_high = IAP_Read(EEPROM_ADDR_H); // 从EEPROM读取高温阈值
  156.     temp_low  = IAP_Read(EEPROM_ADDR_L);  // 从EEPROM读取低温阈值


  157.     // 上电默认值(防止首次使用EEPROM无数据)
  158.     if(temp_high == 0xFF || temp_high == 0) temp_high = 300; // 默认高温阈值300
  159.     if(temp_low  == 0xFF || temp_low  == 0) temp_low  = 50;  // 默认低温阈值50

  160.     while(1)  // 主循环
  161.     {
  162.         if(!AUTO_ISP) IAP_CONTR = 0x60;  // 如果不在ISP模式,设置IAP等待时间

  163.         Key_Scan();        // 调用按键扫描函数

  164.         while(!g_bSystemTime1Ms)        ;        // 等待1ms定时标志
  165.         g_bSystemTime1Ms = 0;                    // 清除1ms标志

  166.         if(++j >= 40)                // 40ms定时到达
  167.         {
  168.             j = 0;  // 重置计时

  169.             temp_real = Get_temperature(ADC_Read_Oversample(1)); // 采集实际温度值

  170.             // 处理实际温度显示
  171.             if(temp_real >= 0)  // 温度为正
  172.             {
  173.                 F0 = 0;       // 正数标志
  174.                 temp_real = temp_real;      // 保持原值
  175.             }
  176.             else                // 温度为负
  177.             {
  178.                 F0 = 1;       // 负数标志
  179.                 temp_real = -temp_real;   // 取绝对值
  180.             }

  181.             DisplayBuff[7] = (temp_real / 1000);     // 千位显示
  182.             DisplayBuff[6] = (temp_real / 100) % 10;  // 百位显示
  183.             DisplayBuff[5] = (temp_real / 10) % 10 + DIS_DOT;  // 十位显示(带小数点)
  184.             DisplayBuff[4] = temp_real % 10;         // 个位显示

  185.             // 千位消零处理
  186.             if(DisplayBuff[7] == 0)
  187.                 DisplayBuff[7] = DIS_BLACK;  // 千位为0则不显示

  188.             // 负号显示处理
  189.             if(F0 == 1)
  190.                 DisplayBuff[7] = DIS_MINUS;  // 显示负号(注意:原代码为DIS_,应为DIS_MINUS)

  191.             // 温度异常处理(超出量程)
  192.             if((temp_real > 1200) && (temp_real < TEMP_MIN * 10))
  193.             {
  194.                 for(i = 0; i < 4; i++)        DisplayBuff[i] = DIS_MINUS; // 显示----
  195.             }

  196.             // 处理高温阈值显示(类似实际温度处理流程)
  197.             if(temp_high >= 0)
  198.             {
  199.                 F0 = 0;
  200.                 temp_high = temp_high;
  201.             }
  202.             else
  203.             {
  204.                 F0 = 1;
  205.                 temp_high = -temp_high;
  206.             }
  207.             DisplayBuff[3] = (temp_high / 1000);     // 千位显示
  208.             DisplayBuff[2] = (temp_high / 100) % 10;  // 百位显示
  209.             DisplayBuff[1] = (temp_high / 10) % 10 + DIS_DOT;  // 十位显示(带小数点)
  210.             DisplayBuff[0] = temp_high % 10;         // 个位显示

  211.             // 千位消零处理
  212.             if(DisplayBuff[3] == 0)
  213.                 DisplayBuff[3] = DIS_BLACK;

  214.             // 负号显示处理
  215.             if(F0 == 1)
  216.                 DisplayBuff[3] = DIS_MINUS;

  217.             // 阈值异常处理
  218.             if((temp_high > 1200) && (temp_high < TEMP_MIN * 10))
  219.             {
  220.                 for(i = 0; i < 4; i++)        DisplayBuff[i] = DIS_MINUS; // 显示----
  221.             }
  222.         }
  223.         Alarm_Check();        // 报警检测
  224.     }
  225. }
复制代码

评分

参与人数 1黑币 +50 收起 理由
admin + 50 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

5#
ID:57657 发表于 2026-4-12 19:31 | 只看该作者
关于EEPROM数据不正确的问题,99%的新手没有了解EEPROM的扇区存储结构:
STC单片机的EEPROM不是真正的EEPROM,而是flash闪存。
读:unsigned char code 指针或汇编MOVC指令读取,不需要初始化EEPROM (STC89/STC90等老版本除外,具体见数据手册)
写: 二进制1可以写为0:0xFF→Q,0不可写为1 以下是无法完成的操作:Q→0xFF或Q→R  Q=0x00~0xFE R=0x00~0xFE 且Q与R的值不同
擦除:只有扇区擦除,没有字节擦除,一次擦512字节,擦除后全部为:0xFF
回复

使用道具 举报

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

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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