找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 672|回復(fù): 14
打印 上一主題 下一主題
收起左側(cè)

關(guān)于STC8G1K08單片機(jī)掉電保存問題。

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:293079 發(fā)表于 2025-2-28 09:42 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
各位大神幫看一下程序,謝謝。
以下程序功能是實(shí)現(xiàn)單獨(dú)按鍵控制兩個(gè)LED燈,短按切換色溫,長按調(diào)節(jié)亮度。掉電保存功能(保存掉電前色溫及亮度)。
可是現(xiàn)在無法實(shí)現(xiàn)掉電保存功能。
  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計(jì)數(shù)器

  17. // 按鍵狀態(tài)變量
  18. u16 key_press_time = 0;     // 按鍵計(jì)時(shí)
  19. bit key_long_flag = 0;      // 長按標(biāo)志
  20. u16 brightness_update_counter = 0; // 亮度更新計(jì)數(shù)器
  21. bit brightness_direction = 1; // 亮度調(diào)節(jié)方向,1 為增加,0 為減少


  22. u8 EEPROM_Read(u16 addr) {
  23.     u8 readData;
  24.                 EA = 0;  // 關(guān)閉中斷
  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;          // 關(guān)閉IAP
  33.     EA = 1;  // 恢復(fù)中斷
  34.     return readData;
  35. }

  36. void EEPROM_Write(u16 addr, u8 dat) {
  37.     EA = 0;  // 關(guān)閉中斷
  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;          // 關(guān)閉IAP
  46.     _nop_();
  47.     EA = 1;  // 恢復(fù)中斷
  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. // 定時(shí)器0初始化
  76. void Timer0_Init() {
  77.     AUXR |= 0x80;           // 1T模式
  78.     TMOD &= 0xF0;           // 定時(shí)器0模式0
  79.     TL0 = 0x30;             // 25us@24MHz
  80.     TH0 = 0xF3;            
  81.     ET0 = 1;                // 使能中斷
  82.     TR0 = 1;                // 啟動(dòng)定時(shí)器
  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.                 // 亮度調(diào)節(jié)
  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.                 // 亮度改變后保存數(shù)據(jù)
  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: // 前一個(gè)模式是白燈模式
  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: // 前一個(gè)模式是黃燈模式
  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: // 前一個(gè)模式是雙燈模式
  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.                 // 短按切換模式時(shí),保存模式和亮度
  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. // 主函數(shù)
  225. void main() {
  226.     P3M0 = 0x00;                       
  227.     P3M1 = 0x00;
  228.     P5M0 = 0x00;                       
  229.     P5M1 = 0x00;

  230.     EEPROM_Load();                      // 加載存儲(chǔ)數(shù)據(jù)
  231.     Timer0_Init();                      // 初始化定時(shí)器

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

  236. // 定時(shí)器0中斷服務(wù)函數(shù)
  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. }
復(fù)制代碼


分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏1 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:161164 發(fā)表于 2025-2-28 11:36 | 只看該作者
掉電保存要用比較器配合硬件電路的
你有嗎?
回復(fù)

使用道具 舉報(bào)

板凳
ID:744809 發(fā)表于 2025-2-28 15:58 | 只看該作者
1、保存之前需要進(jìn)行擦除
2、stc下載的時(shí)候,有些型號(hào)需要設(shè)置eeprom大小
回復(fù)

使用道具 舉報(bào)

地板
ID:140489 發(fā)表于 2025-2-28 16:28 | 只看該作者
eeprom部分按STC的例程寫吧

  1. void IapIdle()
  2. {
  3.     IAP_CONTR = 0;                              //關(guān)閉IAP功能
  4.     IAP_CMD = 0;                                //清除命令寄存器
  5.     IAP_TRIG = 0;                               //清除觸發(fā)寄存器
  6.     IAP_ADDRH = 0x80;                           //將地址設(shè)置到非IAP區(qū)域
  7.     IAP_ADDRL = 0;
  8. }

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

  12.     IAP_CONTR = 0x80;                           //使能IAP
  13.     IAP_TPS = 12;                               //設(shè)置等待參數(shù)12MHz
  14.     IAP_CMD = 1;                                //設(shè)置IAP讀命令
  15.     IAP_ADDRL = addr;                           //設(shè)置IAP低地址
  16.     IAP_ADDRH = addr >> 8;                      //設(shè)置IAP高地址
  17.     IAP_TRIG = 0x5a;                            //寫觸發(fā)命令(0x5a)
  18.     IAP_TRIG = 0xa5;                            //寫觸發(fā)命令(0xa5)
  19.     _nop_();
  20.     dat = IAP_DATA;                             //讀IAP數(shù)據(jù)
  21.     IapIdle();                                  //關(guān)閉IAP功能

  22.     return dat;
  23. }

  24. void IapProgram(int addr, char dat)
  25. {
  26.     IAP_CONTR = 0x80;                           //使能IAP
  27.     IAP_TPS = 12;                               //設(shè)置等待參數(shù)12MHz
  28.     IAP_CMD = 2;                                //設(shè)置IAP寫命令
  29.     IAP_ADDRL = addr;                           //設(shè)置IAP低地址
  30.     IAP_ADDRH = addr >> 8;                      //設(shè)置IAP高地址
  31.     IAP_DATA = dat;                             //寫IAP數(shù)據(jù)
  32.     IAP_TRIG = 0x5a;                            //寫觸發(fā)命令(0x5a)
  33.     IAP_TRIG = 0xa5;                            //寫觸發(fā)命令(0xa5)
  34.     _nop_();
  35.     IapIdle();                                  //關(guān)閉IAP功能
  36. }

  37. void IapErase(int addr)
  38. {
  39.     IAP_CONTR = 0x80;                           //使能IAP
  40.     IAP_TPS = 12;                               //設(shè)置等待參數(shù)12MHz
  41.     IAP_CMD = 3;                                //設(shè)置IAP擦除命令
  42.     IAP_ADDRL = addr;                           //設(shè)置IAP低地址
  43.     IAP_ADDRH = addr >> 8;                      //設(shè)置IAP高地址
  44.     IAP_TRIG = 0x5a;                            //寫觸發(fā)命令(0x5a)
  45.     IAP_TRIG = 0xa5;                            //寫觸發(fā)命令(0xa5)
  46.     _nop_();                                    //
  47.     IapIdle();                                  //關(guān)閉IAP功能
  48. }
復(fù)制代碼
回復(fù)

使用道具 舉報(bào)

5#
ID:235691 發(fā)表于 2025-2-28 16:54 | 只看該作者
STC8G單片機(jī)的片上EEPROM程序 - 51單片機(jī)  http://www.torrancerestoration.com/bbs/dpj-203901-1.html
回復(fù)

使用道具 舉報(bào)

6#
ID:57657 發(fā)表于 2025-2-28 22:18 | 只看該作者
掉電保存實(shí)際上是低壓保存,有一個(gè)LVD低電壓中斷或ADC獲取單片機(jī)電壓的比較器中斷,低于電壓視為掉電,需要并聯(lián)個(gè)大電容。
回復(fù)

使用道具 舉報(bào)

7#
ID:1133081 發(fā)表于 2025-3-1 08:14 | 只看該作者
初略的看了下樓主的代碼。使用的并不是掉電保存EEPROM模式,但確實(shí)也無需使用掉電保存EEPROM模式,使用常規(guī)的EEPROM保存方式即可。樓主采用單健長短按操作,可以采用只要是進(jìn)入設(shè)置模式就開始計(jì)時(shí),每次按動(dòng)鍵計(jì)時(shí)變量清0,當(dāng)停止按動(dòng)達(dá)若干時(shí)間(通常5~10秒)表示設(shè)置結(jié)束,即自動(dòng)將設(shè)置的參數(shù)保存在EEPROM并退出設(shè)置模式。
回復(fù)

使用道具 舉報(bào)

8#
ID:230500 發(fā)表于 2025-3-1 09:15 | 只看該作者
由于單片機(jī)內(nèi)部的EEPROM有擦寫壽命; 所以不能時(shí)時(shí)刻刻都在不停的保存數(shù)據(jù);其次掉電保存需要額外的外圍硬件電路做配合;一般最簡單的電路就是單片機(jī)的供電 與外部供電用二極管隔離;同時(shí)加大單片機(jī)電源的濾波電容容量 用于掉電后延長單片機(jī)的續(xù)航 用來保存數(shù)據(jù);再額外用一個(gè)IO口配合三極管 來檢測電源是否掉電 ;  一旦檢測到掉電 單片機(jī)就執(zhí)行數(shù)據(jù)保存;

總結(jié)下來有三種方案; 第一種是做外部掉電檢測;通過檢測掉電后保存數(shù)據(jù)
第二種是 通過你的按鍵做數(shù)據(jù)保存; 如果操作了按鍵, 同時(shí)新舊數(shù)據(jù)有不一樣,說明有命令修改;操作完按鍵后, 把新數(shù)據(jù)刷新保存到EEPROM
第三種,使用FRAM鐵電存儲(chǔ)器; 幾乎可以無限制的擦寫壽命;
回復(fù)

使用道具 舉報(bào)

9#
ID:230500 發(fā)表于 2025-3-1 09:23 | 只看該作者
另外;你的程序有問題; 你仔細(xì)看下STC的例程; 單片機(jī)自帶的EEPROM,寫數(shù)據(jù)之前需要先整個(gè)扇區(qū)擦除后才能寫; 下面這一段是你的程序沒有做擦除處理; 直接就開始寫數(shù)據(jù)了;
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);
}
回復(fù)

使用道具 舉報(bào)

10#
ID:195666 發(fā)表于 2025-3-1 14:11 | 只看該作者
1、先判斷能不能存儲(chǔ)成功;
2、判斷掉電檢測是否成功;
3、將這2個(gè)功能聯(lián)調(diào)成功。
回復(fù)

使用道具 舉報(bào)

11#
ID:293079 發(fā)表于 2025-3-1 14:24 | 只看該作者
lkc8210 發(fā)表于 2025-2-28 11:36
掉電保存要用比較器配合硬件電路的
你有嗎?

謝謝您建議。已經(jīng)解決啦。這部分是沒有的。發(fā)現(xiàn)可以不需要。
回復(fù)

使用道具 舉報(bào)

12#
ID:293079 發(fā)表于 2025-3-1 14:24 | 只看該作者
123156fsadf 發(fā)表于 2025-2-28 15:58
1、保存之前需要進(jìn)行擦除
2、stc下載的時(shí)候,有些型號(hào)需要設(shè)置eeprom大小

謝謝建議,已經(jīng)解決啦。
回復(fù)

使用道具 舉報(bào)

13#
ID:293079 發(fā)表于 2025-3-1 14:25 | 只看該作者
lids 發(fā)表于 2025-2-28 16:28
eeprom部分按STC的例程寫吧

謝謝建議,已經(jīng)解決啦。
回復(fù)

使用道具 舉報(bào)

14#
ID:293079 發(fā)表于 2025-3-1 14:25 | 只看該作者
a399288395 發(fā)表于 2025-3-1 09:15
由于單片機(jī)內(nèi)部的EEPROM有擦寫壽命; 所以不能時(shí)時(shí)刻刻都在不停的保存數(shù)據(jù);其次掉電保存需要額外的外圍硬 ...

謝謝建議,已經(jīng)解決啦。
回復(fù)

使用道具 舉報(bào)

15#
ID:293079 發(fā)表于 2025-3-1 14:25 | 只看該作者
a399288395 發(fā)表于 2025-3-1 09:23
另外;你的程序有問題; 你仔細(xì)看下STC的例程; 單片機(jī)自帶的EEPROM,寫數(shù)據(jù)之前需要先整個(gè)扇區(qū)擦除后才能 ...

謝謝建議,已經(jīng)解決啦。
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表