找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 896|回复: 14
收起左侧

关于STC8G1K08单片机掉电保存问题。

[复制链接]
ID:293079 发表于 2025-2-28 09:42 | 显示全部楼层 |阅读模式
各位大神帮看一下程序,谢谢。
以下程序功能是实现单独按键控制两个LED灯,短按切换色温,长按调节亮度。掉电保存功能(保存掉电前色温及亮度)。
可是现在无法实现掉电保存功能。
  1. #include "STC8G.H"
  2. #include <intrins.h>

  3. #define u8  unsigned char
  4. #define u16 unsigned int

  5. // 硬件定义
  6. sbit KEY         = P5^4;    // 按键引脚
  7. sbit WHITE_LED   = P3^2;    // 白灯控制引脚
  8. sbit YELLOW_LED  = P3^3;    // 黄灯控制引脚

  9. // EEPROM地址定义
  10. #define EEPROM_BASE 0x0000  // EEPROM基地址

  11. // 全局变量
  12. u8 mode = 0;                // 工作模式(0/1/2)
  13. u8 white_duty = 100;        // 白灯亮度(0 - 100)
  14. u8 yellow_duty = 100;       // 黄灯亮度(0 - 100)
  15. u8 dual_duty = 50;          // 双灯模式亮度(0 - 100)
  16. u8 pwm_counter = 0;         // PWM计数器

  17. // 按键状态变量
  18. u16 key_press_time = 0;     // 按键计时
  19. bit key_long_flag = 0;      // 长按标志
  20. u16 brightness_update_counter = 0; // 亮度更新计数器
  21. bit brightness_direction = 1; // 亮度调节方向,1 为增加,0 为减少


  22. u8 EEPROM_Read(u16 addr) {
  23.     u8 readData;
  24.                 EA = 0;  // 关闭中断
  25.     IAP_CONTR = 0x80;       // 使能IAP
  26.     IAP_CMD = 1;            // 读命令
  27.     IAP_ADDRH = (u8)(addr >> 8);
  28.     IAP_ADDRL = (u8)addr;
  29.     IAP_TRIG = 0x5A;
  30.     IAP_TRIG = 0xA5;
  31.     readData = IAP_DATA;
  32.     IAP_CONTR = 0;          // 关闭IAP
  33.     EA = 1;  // 恢复中断
  34.     return readData;
  35. }

  36. void EEPROM_Write(u16 addr, u8 dat) {
  37.     EA = 0;  // 关闭中断
  38.     IAP_CONTR = 0x80;       // 使能IAP
  39.     IAP_CMD = 2;            // 写命令
  40.     IAP_ADDRH = (u8)(addr >> 8);
  41.     IAP_ADDRL = (u8)addr;
  42.     IAP_DATA = dat;
  43.     IAP_TRIG = 0x5A;
  44.     IAP_TRIG = 0xA5;
  45.     IAP_CONTR = 0;          // 关闭IAP
  46.     _nop_();
  47.     EA = 1;  // 恢复中断
  48. }

  49. void EEPROM_Save()
  50. {
  51.     EEPROM_Write(EEPROM_BASE + 0, mode);
  52.     EEPROM_Write(EEPROM_BASE + 1, white_duty);
  53.     EEPROM_Write(EEPROM_BASE + 2, yellow_duty);
  54.     EEPROM_Write(EEPROM_BASE + 3, dual_duty);
  55. }

  56. void EEPROM_Load() {
  57.     u8 temp;
  58.     if ((temp = EEPROM_Read(EEPROM_BASE + 0)) != 0xFF)
  59.                 {
  60.         mode = temp;
  61.     }
  62.     if ((temp = EEPROM_Read(EEPROM_BASE + 1)) != 0xFF)
  63.                 {
  64.         white_duty = temp;
  65.     }
  66.     if ((temp = EEPROM_Read(EEPROM_BASE + 2)) != 0xFF)
  67.                 {
  68.         yellow_duty = temp;
  69.     }
  70.     if ((temp = EEPROM_Read(EEPROM_BASE + 3)) != 0xFF)
  71.                 {
  72.         dual_duty = temp;
  73.     }
  74. }

  75. // 定时器0初始化
  76. void Timer0_Init() {
  77.     AUXR |= 0x80;           // 1T模式
  78.     TMOD &= 0xF0;           // 定时器0模式0
  79.     TL0 = 0x30;             // 25us@24MHz
  80.     TH0 = 0xF3;            
  81.     ET0 = 1;                // 使能中断
  82.     TR0 = 1;                // 启动定时器
  83.     EA = 1;                 // 总中断
  84. }

  85. // PWM输出控制
  86. void PWM_Update() {
  87.     static u8 pwm_tick = 0;

  88.     if (++pwm_counter >= 20) {           // 0.5ms周期,提高PWM频率
  89.         pwm_counter = 0;
  90.         pwm_tick = (pwm_tick + 1) % 100;

  91.         switch (mode) {
  92.             case 0:                     // 白灯模式
  93.                 WHITE_LED = (pwm_tick < white_duty);
  94.                 YELLOW_LED = 0;
  95.                 break;
  96.             case 1:                     // 黄灯模式
  97.                 WHITE_LED = 0;
  98.                 YELLOW_LED = (pwm_tick < yellow_duty);
  99.                 break;
  100.             case 2:                     // 双灯模式
  101.                 WHITE_LED = (pwm_tick < dual_duty);
  102.                 YELLOW_LED = (pwm_tick < dual_duty);
  103.                 break;
  104.         }
  105.     }
  106. }

  107. // 按键处理
  108. void Key_Process() {
  109.     if (KEY == 0) {                      // 按键按下
  110.         if (key_press_time < 1000) key_press_time++;

  111.         if (key_press_time > 300) {      // 长按判定(300ms)
  112.             key_long_flag = 1;
  113.             brightness_update_counter++;
  114.             // 10000ms / 100 = 100ms ,每100ms更新一次亮度
  115.             if (brightness_update_counter >= 10) {
  116.                 brightness_update_counter = 0;
  117.                 // 亮度调节
  118.                 switch (mode) {
  119.                     case 0:
  120.                         if (brightness_direction) {
  121.                             if (white_duty < 100) {
  122.                                 white_duty++;
  123.                             } else {
  124.                                 brightness_direction = 0;
  125.                                 white_duty--;
  126.                             }
  127.                         } else {
  128.                             if (white_duty > 0) {
  129.                                 white_duty--;
  130.                             } else {
  131.                                 brightness_direction = 1;
  132.                                 white_duty++;
  133.                             }
  134.                         }
  135.                         break;
  136.                     case 1:
  137.                         if (brightness_direction) {
  138.                             if (yellow_duty < 100) {
  139.                                 yellow_duty++;
  140.                             } else {
  141.                                 brightness_direction = 0;
  142.                                 yellow_duty--;
  143.                             }
  144.                         } else {
  145.                             if (yellow_duty > 0) {
  146.                                 yellow_duty--;
  147.                             } else {
  148.                                 brightness_direction = 1;
  149.                                 yellow_duty++;
  150.                             }
  151.                         }
  152.                         break;
  153.                     case 2:
  154.                         if (brightness_direction) {
  155.                             if (dual_duty < 100) {
  156.                                 dual_duty++;
  157.                             } else {
  158.                                 brightness_direction = 0;
  159.                                 dual_duty--;
  160.                             }
  161.                         } else {
  162.                             if (dual_duty > 0) {
  163.                                 dual_duty--;
  164.                             } else {
  165.                                 brightness_direction = 1;
  166.                                 dual_duty++;
  167.                             }
  168.                         }
  169.                         break;
  170.                 }
  171.                 // 亮度改变后保存数据
  172.                 EEPROM_Save();
  173.             }
  174.         }
  175.     } else {                            // 按键释放
  176.         if (key_press_time > 0) {
  177.             if (!key_long_flag) {        // 短按切换模式
  178.                 u8 prev_mode = mode;
  179.                 mode = (mode + 1) % 3;
  180.                 // 保持前一种色温的亮度
  181.                 switch (prev_mode) {
  182.                     case 0: // 前一个模式是白灯模式
  183.                         switch (mode) {
  184.                             case 1:
  185.                                 yellow_duty = white_duty;
  186.                                 break;
  187.                             case 2:
  188.                                 dual_duty = white_duty;
  189.                                 break;
  190.                         }
  191.                         break;
  192.                     case 1: // 前一个模式是黄灯模式
  193.                         switch (mode) {
  194.                             case 0:
  195.                                 white_duty = yellow_duty;
  196.                                 break;
  197.                             case 2:
  198.                                 dual_duty = yellow_duty;
  199.                                 break;
  200.                         }
  201.                         break;
  202.                     case 2: // 前一个模式是双灯模式
  203.                         switch (mode) {
  204.                             case 0:
  205.                                 white_duty = dual_duty;
  206.                                 break;
  207.                             case 1:
  208.                                 yellow_duty = dual_duty;
  209.                                 break;
  210.                         }
  211.                         break;
  212.                 }

  213.                 // 短按切换模式时,保存模式和亮度
  214.                 EEPROM_Save();
  215.             } else {                    // 长按保存亮度
  216.                 EEPROM_Save();
  217.                 key_long_flag = 0;
  218.             }
  219.             key_press_time = 0;
  220.             brightness_update_counter = 0;
  221.         }
  222.     }
  223. }

  224. // 主函数
  225. void main() {
  226.     P3M0 = 0x00;                       
  227.     P3M1 = 0x00;
  228.     P5M0 = 0x00;                       
  229.     P5M1 = 0x00;

  230.     EEPROM_Load();                      // 加载存储数据
  231.     Timer0_Init();                      // 初始化定时器

  232.     while (1) {
  233.         PWM_Update();                   // 更新PWM输出
  234.     }
  235. }

  236. // 定时器0中断服务函数
  237. void timer0_isr() interrupt 1 {
  238.     static u8 key_scan_cnt = 0;

  239.     if (++key_scan_cnt >= 10) {         // 10ms按键扫描
  240.         key_scan_cnt = 0;
  241.         Key_Process();
  242.     }
  243. }
复制代码


回复

使用道具 举报

ID:161164 发表于 2025-2-28 11:36 | 显示全部楼层
掉电保存要用比较器配合硬件电路的
你有吗?
回复

使用道具 举报

ID:744809 发表于 2025-2-28 15:58 | 显示全部楼层
1、保存之前需要进行擦除
2、stc下载的时候,有些型号需要设置eeprom大小
回复

使用道具 举报

ID:140489 发表于 2025-2-28 16:28 | 显示全部楼层
eeprom部分按STC的例程写吧

  1. void IapIdle()
  2. {
  3.     IAP_CONTR = 0;                              //关闭IAP功能
  4.     IAP_CMD = 0;                                //清除命令寄存器
  5.     IAP_TRIG = 0;                               //清除触发寄存器
  6.     IAP_ADDRH = 0x80;                           //将地址设置到非IAP区域
  7.     IAP_ADDRL = 0;
  8. }

  9. char IapRead(int addr)
  10. {
  11.     char dat;

  12.     IAP_CONTR = 0x80;                           //使能IAP
  13.     IAP_TPS = 12;                               //设置等待参数12MHz
  14.     IAP_CMD = 1;                                //设置IAP读命令
  15.     IAP_ADDRL = addr;                           //设置IAP低地址
  16.     IAP_ADDRH = addr >> 8;                      //设置IAP高地址
  17.     IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
  18.     IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
  19.     _nop_();
  20.     dat = IAP_DATA;                             //读IAP数据
  21.     IapIdle();                                  //关闭IAP功能

  22.     return dat;
  23. }

  24. void IapProgram(int addr, char dat)
  25. {
  26.     IAP_CONTR = 0x80;                           //使能IAP
  27.     IAP_TPS = 12;                               //设置等待参数12MHz
  28.     IAP_CMD = 2;                                //设置IAP写命令
  29.     IAP_ADDRL = addr;                           //设置IAP低地址
  30.     IAP_ADDRH = addr >> 8;                      //设置IAP高地址
  31.     IAP_DATA = dat;                             //写IAP数据
  32.     IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
  33.     IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
  34.     _nop_();
  35.     IapIdle();                                  //关闭IAP功能
  36. }

  37. void IapErase(int addr)
  38. {
  39.     IAP_CONTR = 0x80;                           //使能IAP
  40.     IAP_TPS = 12;                               //设置等待参数12MHz
  41.     IAP_CMD = 3;                                //设置IAP擦除命令
  42.     IAP_ADDRL = addr;                           //设置IAP低地址
  43.     IAP_ADDRH = addr >> 8;                      //设置IAP高地址
  44.     IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
  45.     IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
  46.     _nop_();                                    //
  47.     IapIdle();                                  //关闭IAP功能
  48. }
复制代码
回复

使用道具 举报

ID:235691 发表于 2025-2-28 16:54 | 显示全部楼层
STC8G单片机的片上EEPROM程序 - 51单片机  http://www.51hei.com/bbs/dpj-203901-1.html
回复

使用道具 举报

ID:57657 发表于 2025-2-28 22:18 | 显示全部楼层
掉电保存实际上是低压保存,有一个LVD低电压中断或ADC获取单片机电压的比较器中断,低于电压视为掉电,需要并联个大电容。
回复

使用道具 举报

ID:1133081 发表于 2025-3-1 08:14 | 显示全部楼层
初略的看了下楼主的代码。使用的并不是掉电保存EEPROM模式,但确实也无需使用掉电保存EEPROM模式,使用常规的EEPROM保存方式即可。楼主采用单健长短按操作,可以采用只要是进入设置模式就开始计时,每次按动键计时变量清0,当停止按动达若干时间(通常5~10秒)表示设置结束,即自动将设置的参数保存在EEPROM并退出设置模式。
回复

使用道具 举报

ID:230500 发表于 2025-3-1 09:15 | 显示全部楼层
由于单片机内部的EEPROM有擦写寿命; 所以不能时时刻刻都在不停的保存数据;其次掉电保存需要额外的外围硬件电路做配合;一般最简单的电路就是单片机的供电 与外部供电用二极管隔离;同时加大单片机电源的滤波电容容量 用于掉电后延长单片机的续航 用来保存数据;再额外用一个IO口配合三极管 来检测电源是否掉电 ;  一旦检测到掉电 单片机就执行数据保存;

总结下来有三种方案; 第一种是做外部掉电检测;通过检测掉电后保存数据
第二种是 通过你的按键做数据保存; 如果操作了按键, 同时新旧数据有不一样,说明有命令修改;操作完按键后, 把新数据刷新保存到EEPROM
第三种,使用FRAM铁电存储器; 几乎可以无限制的擦写寿命;
回复

使用道具 举报

ID:230500 发表于 2025-3-1 09:23 | 显示全部楼层
另外;你的程序有问题; 你仔细看下STC的例程; 单片机自带的EEPROM,写数据之前需要先整个扇区擦除后才能写; 下面这一段是你的程序没有做擦除处理; 直接就开始写数据了;
void EEPROM_Save()
{
    EEPROM_Write(EEPROM_BASE + 0, mode);
    EEPROM_Write(EEPROM_BASE + 1, white_duty);
    EEPROM_Write(EEPROM_BASE + 2, yellow_duty);
    EEPROM_Write(EEPROM_BASE + 3, dual_duty);
}
回复

使用道具 举报

ID:195666 发表于 2025-3-1 14:11 | 显示全部楼层
1、先判断能不能存储成功;
2、判断掉电检测是否成功;
3、将这2个功能联调成功。
回复

使用道具 举报

ID:293079 发表于 2025-3-1 14:24 | 显示全部楼层
lkc8210 发表于 2025-2-28 11:36
掉电保存要用比较器配合硬件电路的
你有吗?

谢谢您建议。已经解决啦。这部分是没有的。发现可以不需要。
回复

使用道具 举报

ID:293079 发表于 2025-3-1 14:24 | 显示全部楼层
123156fsadf 发表于 2025-2-28 15:58
1、保存之前需要进行擦除
2、stc下载的时候,有些型号需要设置eeprom大小

谢谢建议,已经解决啦。
回复

使用道具 举报

ID:293079 发表于 2025-3-1 14:25 | 显示全部楼层
lids 发表于 2025-2-28 16:28
eeprom部分按STC的例程写吧

谢谢建议,已经解决啦。
回复

使用道具 举报

ID:293079 发表于 2025-3-1 14:25 | 显示全部楼层
a399288395 发表于 2025-3-1 09:15
由于单片机内部的EEPROM有擦写寿命; 所以不能时时刻刻都在不停的保存数据;其次掉电保存需要额外的外围硬 ...

谢谢建议,已经解决啦。
回复

使用道具 举报

ID:293079 发表于 2025-3-1 14:25 | 显示全部楼层
a399288395 发表于 2025-3-1 09:23
另外;你的程序有问题; 你仔细看下STC的例程; 单片机自带的EEPROM,写数据之前需要先整个扇区擦除后才能 ...

谢谢建议,已经解决啦。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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