数码之家

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信登录

微信扫一扫,快速登录

搜索
查看: 1072|回复: 18

[C51] 可以用STC8G1K08实现吗

[复制链接]
发表于 2025-1-29 20:22:59 来自手机浏览器 | 显示全部楼层 |阅读模式
这是某个茶盘上烧水抽水的操作,用于桶装水抽水器控制板,应该很好,用STC8G1K08单片机,代码应该怎么写,用一个按键操作,实现∶
长按进入设置模式,短按1次0.8L档,短按2次1.0L档,短按3次1.2L,以此类推,每短按多一下,增加0.2L,3秒无操作,保存当前选择并退出设置模式。非设置模式时,短按开始按上次设置进行抽水,抽水过程中提前短按时就停止抽水。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

x
发表于 2025-2-20 23:55:16 | 显示全部楼层
这肯定是可以的,具体是代码实现
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-2-10 12:20:10 | 显示全部楼层
aming_ou 发表于 2025-2-9 16:37
根据楼上的提示,使用官方库范例,再用最近非常大热的deepseek提供的样例代码,最终实现了自己的需求。取消 ...

更正,EEPROM中已有数据后,再次写入数据,需要先擦除,否则数据写入结果会异常,所以,请在Write_Led_Keep_Time(led_keep_time) 前面增加一行代码 EEPROM_SectorErase(EEPROM_ADDR);
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-2-9 16:37:24 | 显示全部楼层
根据楼上的提示,使用官方库范例,再用最近非常大热的deepseek提供的样例代码,最终实现了自己的需求。取消原按1~7档的固定定时设计,改为长按进入设置模式,设置一次即可。

欢迎继续探讨。

  1. /*----------------------------------------------------------------------*/
  2. /* --- STC8G1K08A 按键动作,按键中断唤醒,定时器练习--------------------*/
  3. /* --- 1.PIN脚低电平触发                                                */
  4. /* --- 2.短按启动或关闭 LED_PIN,默认定时5秒                            */
  5. /* --- 3.长按3~9秒,进入设置模式,再短按时,记录长按~短按之间的时长   */
  6. /* ---   下次短按时,已最新设置的时长进行定时,定时结束前短按,退出定时 */
  7. /* ---   断电记忆上次的设置时长                                         */
  8. /* --- 4.长按10秒以上,释放按键时擦除ERPROM第1扇区                      */
  9. /*----------------------------------------------------------------------*/
  10. #include "STC8G.H"

  11. #define MAIN_Fosc       24000000L   // 定义主时钟
  12. #define PX0H            0x01  // 定义PX0H为IPH寄存器的第0位
  13. #define MCU_IDLE()          PCON |= 1   /* MCU 进入 IDLE 模式 */
  14. #define MCU_POWER_DOWN()    PCON |= 2   /* MCU 进入 睡眠 模式 */

  15. #define CTR_PIN P55  // 控制/动作指示灯
  16. #define LED_PIN P54  // LED连接P54 低电平触发,作为对外控制输出脚
  17. #define KEY_PIN P32  // 按键连接P32

  18. #define SHORT_PRESS_TIME 10  // 短按时间阈值 毫秒
  19. #define LONG_PRESS_TIME 3000 // 长按时间阈值 毫秒
  20. #define IDLE_TIME 30000      // 空闲时间阈值 毫秒
  21. #define KEEP_TIME 5000L      // 默认LED保持时长  毫秒
  22. #define RESET_TIME 10000     // 重置时间阈值 毫秒,长按以上,清除EEPROM

  23. #define EEPROM_ADDR 0x0000 // EEPROM存储地址(根据实际需求设置)
  24. #define IAP_STANDBY()   IAP_CMD = 0     //IAP空闲命令(禁止)
  25. #define IAP_READ()      IAP_CMD = 1     //IAP读出命令
  26. #define IAP_WRITE()     IAP_CMD = 2     //IAP写入命令
  27. #define IAP_ERASE()     IAP_CMD = 3     //IAP擦除命令
  28. #define IAP_ENABLE()    IAP_CONTR = 0x80; IAP_TPS = MAIN_Fosc / 1000000 //激活IAP操作
  29. #define IAP_DISABLE()   IAP_CONTR = 0; IAP_CMD = 0; IAP_TRIG = 0; IAP_ADDRH = 0xff; IAP_ADDRL = 0xff // 禁止IAP操作

  30. extern void          _nop_     (void);

  31. // 定义全局变量
  32. unsigned long msTicks = 0;           // 定时器计数 毫秒
  33. unsigned long led_keep_time = KEEP_TIME; // 默认LED保持时长5秒
  34. unsigned long led_on_time = 0;       // LED已点亮时间 毫秒
  35. unsigned long idle_time = 0;         // 空闲时间计数器 毫秒

  36. bit led_state = 0;                 // LED状态(0:熄灭,1:点亮)
  37. bit setting_mode = 0;              // 设置模式标志
  38. bit key_pressed = 0;               // 按键按下标志
  39. bit key_released = 1;              // 按键释放标志
  40. bit key_long_press = 0;            // 长按标志
  41. bit wakeup_status = 1;             // 运行标志(0:休眠,1:活跃)
  42. bit erase_mode = 0;                // 设置EEPROM擦除标志

  43. void Timer0_Init(void);            // 定时器0初始化
  44. void Int0_Init(void);              // 外部中断0初始化
  45. void PowerDownMode(void);          // 掉电模式
  46. void WakeUpFromPowerDown(void);    // 从掉电模式唤醒

  47. // 初始化I/O端口
  48. void InitPorts()
  49. {
  50.     // 初始化为准双向
  51.     P5M0 = 0x00; P5M1 = 0xcf;
  52.     P3M0 = 0x00; P3M1 = 0xf8;
  53. }

  54. // 初始化全局变量
  55. void InitGlobals()
  56. {
  57.     CTR_PIN = 1;
  58.     LED_PIN = 1;
  59.     KEY_PIN = 1;
  60.     msTicks = 0;
  61.     wakeup_status = 1;
  62.     idle_time = msTicks;
  63. }

  64. // 毫秒级延时函数
  65. void Delay_Ms(unsigned long ms) {
  66.     unsigned long delay_start = msTicks + ms;
  67.     while(delay_start > msTicks);
  68. }

  69. void Flash_LED(unsigned char i,unsigned int j) {
  70.     bit  CTR_PIN_status = CTR_PIN; // 保存控制指示灯的状态
  71.     do{
  72.         CTR_PIN = !CTR_PIN;
  73.         Delay_Ms(j);
  74.     }while(--i);
  75.     CTR_PIN = CTR_PIN_status;     // 恢复控制指示灯的状态
  76.     idle_time = msTicks;
  77. }

  78. // 触发EEPROM操作
  79. void EEPROM_Trig(void)
  80. {
  81.     F0 = EA;    //保存全局中断
  82.     EA = 0;     //禁止中断, 避免触发命令无效
  83.     IAP_TRIG = 0x5A;
  84.     IAP_TRIG = 0xA5;  //先送5AH,再送A5H到IAP触发寄存器,每次都需要如此
  85.                       //送完A5H后,IAP命令立即被触发启动
  86.                       //CPU等待IAP完成后,才会继续执行程序。
  87.     _nop_();
  88.     _nop_();
  89.     EA = F0;    //恢复全局中断
  90.     _nop_();
  91. }

  92. // 写入EEPROM
  93. void EEPROM_Write(unsigned int addr, unsigned int dat) {
  94.     IAP_ENABLE();
  95.     IAP_WRITE();       // 设置IAP写命令
  96.     IAP_ADDRL = addr;  // 设置低地址
  97.     IAP_ADDRH = addr >> 8;  // 设置高地址
  98.     IAP_DATA = dat;    // 写入数据
  99.     EEPROM_Trig();     // 触发EEPROM操作
  100.     IAP_DISABLE();     // 关闭IAP功能
  101. }

  102. // 从EEPROM读取数据
  103. unsigned char EEPROM_Read(unsigned int addr) {
  104.     unsigned char dat;
  105.     IAP_ENABLE();
  106.     IAP_READ();        // 设置IAP读命令
  107.     IAP_ADDRL = addr;  // 设置低地址
  108.     IAP_ADDRH = addr >> 8;  // 设置高地址
  109.     EEPROM_Trig();     // 触发EEPROM操作
  110.     dat = IAP_DATA;    // 读取数据
  111.     IAP_DISABLE();     // 关闭IAP功能
  112.     return dat;
  113. }

  114. // 把指定地址的EEPROM扇区擦除
  115. void EEPROM_SectorErase(unsigned int addr)
  116. {
  117.     IAP_ENABLE();                       //设置等待时间,允许IAP操作,送一次就够
  118.     IAP_ERASE();                        //宏调用, 送扇区擦除命令,命令不需改变时,不需重新送命令
  119.                                           //只有扇区擦除,没有字节擦除,512字节/扇区。
  120.                                           //扇区中任意一个字节地址都是扇区地址。
  121.     IAP_ADDRH = addr / 256;             //送扇区地址高字节(地址需要改变时才需重新送地址)
  122.     IAP_ADDRL = addr % 256;             //送扇区地址低字节
  123.     EEPROM_Trig();                      //触发EEPROM操作
  124.     IAP_DISABLE();                      //禁止EEPROM操作
  125. }

  126. // 写入32位long类型数据到EEPROM
  127. void Write_Led_Keep_Time(unsigned long dat) {
  128.     unsigned char byte0, byte1, byte2, byte3;

  129.     // 将32位long类型数据分解为4个字节
  130.     byte0 = (dat >> 24) & 0xFF;  // 最高字节
  131.     byte1 = (dat >> 16) & 0xFF;  // 高次字节                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
  132.     byte2 = (dat >> 8) & 0xFF;   // 低次字节
  133.     byte3 = dat & 0xFF;          // 最低字节

  134.     // 写入EEPROM
  135.     EEPROM_Write(EEPROM_ADDR, byte0);    // 写入最高字节
  136.     EEPROM_Write(EEPROM_ADDR + 1, byte1); // 写入高次字节
  137.     EEPROM_Write(EEPROM_ADDR + 2, byte2); // 写入低次字节
  138.     EEPROM_Write(EEPROM_ADDR + 3, byte3); // 写入最低字节
  139. }

  140. // 从EEPROM读取32位long类型数据
  141. int Read_Led_Keep_Time() {
  142.     unsigned char byte0, byte1, byte2, byte3;

  143.     // 从EEPROM读取4个字节
  144.     byte0 = EEPROM_Read(EEPROM_ADDR);    // 读取最高字节
  145.     byte1 = EEPROM_Read(EEPROM_ADDR + 1); // 读取高次字节
  146.     byte2 = EEPROM_Read(EEPROM_ADDR + 2); // 读取低次字节
  147.     byte3 = EEPROM_Read(EEPROM_ADDR + 3); // 读取最低字节

  148.     // 将4个字节组合成一个32位long类型数据
  149.     return ((long)byte0 << 24) | ((long)byte1 << 16) | ((long)byte2 << 8) | byte3;
  150. }

  151. // 主流程
  152. void main() {
  153.     InitPorts();    // 初始化I/O端口
  154.     Timer0_Init();  // 初始化定时器0
  155.     Int0_Init();    // 初始化外部中断0
  156.     InitGlobals();  // 初始化全局变量
  157.     Flash_LED(2,1000); // 开机完成提示
  158.    
  159.     // 从EEPROM读取保持时长数据并赋值给led_keep_time,否则保存默认值到EEPROM
  160.     led_keep_time = Read_Led_Keep_Time();
  161.     if (led_keep_time == 0xFFFFFFFF || led_keep_time < 2000L || led_keep_time > 300000L) { // 如果EEPROM未初始化或值不合规
  162.         led_keep_time = KEEP_TIME; // 使用默认值
  163.         Write_Led_Keep_Time(led_keep_time);  // 将定时默认值写入EEPROM
  164.         Flash_LED(6,1000);   //慢闪,保存成功      
  165.     }

  166.     while (1) {
  167.         if (key_released && key_pressed) { // 检测按键按下
  168.             key_pressed = 0; // 清除按键标志
  169.             if (erase_mode) { //重置模式
  170.                 erase_mode = 0; // 清除重置模式标志
  171.                 EEPROM_SectorErase(EEPROM_ADDR);
  172.                 Flash_LED(10,150);   //执行完成后闪烁提示         
  173.                 led_keep_time = KEEP_TIME;
  174.             }
  175.             else if (key_long_press) { // 长按按键
  176.                 key_long_press = 0;
  177.                 if (!setting_mode) { // 进入设置模式
  178.                     setting_mode = 1;
  179.                     led_state = 1; // 点亮LED
  180.                     LED_PIN = 0;
  181.                     led_on_time = msTicks; // 重置点亮时间
  182.                 }
  183.             }
  184.             else { // 短按按键
  185.                 if (setting_mode) { // 在设置模式中
  186.                     setting_mode = 0; // 退出设置模式
  187.                     led_state = 0; // 熄灭LED
  188.                     LED_PIN = 1;
  189.                     led_keep_time = msTicks - led_on_time;        // 保存点亮时长
  190.                     Write_Led_Keep_Time(led_keep_time);           // 将new_keep_time写入EEPROM
  191.                     Flash_LED(6,1000);   //慢闪,保存成功
  192.                 } else { // 不在设置模式
  193.                     if (led_state) { // 如果LED已点亮
  194.                         led_state = 0; // 熄灭LED
  195.                         LED_PIN = 1;
  196.                     } else { // 如果LED未点亮
  197.                         led_state = 1; // 点亮LED
  198.                         LED_PIN = 0;
  199.                         led_on_time = msTicks; // 重置点亮时间
  200.                     }
  201.                 }
  202.             }
  203.             idle_time = msTicks; // 重置空闲时间
  204.         }
  205.         
  206.         if (!setting_mode){ // 不在设置模式
  207.             if (led_state) { // LED点亮
  208.                 if (msTicks - led_on_time >= led_keep_time) { // 达到保持时长
  209.                     led_state = 0; // 熄灭LED            
  210.                     LED_PIN = 1;
  211.                     Flash_LED(6,200);   //执行完成后闪烁提示
  212.                 }
  213.             }else{ // LED熄灭
  214.                 if (msTicks - idle_time >= IDLE_TIME) { // 空闲时间超过30秒
  215.                     Flash_LED(10,100);   //执行完成后闪烁提示            
  216.                     PowerDownMode(); // 进入掉电模式
  217.                 }
  218.             }
  219.         }else if (led_state && (msTicks - led_on_time > 120000)){
  220.             setting_mode = 0; // 超时退出设置模式
  221.             led_state = 0;    // 熄灭LED
  222.             LED_PIN = 1;
  223.             Flash_LED(6,500);   //半慢闪2次,超时
  224.         }
  225.     }
  226. }

  227. // 定时器0初始化
  228. void Timer0_Init(void)      //1毫秒@24.000MHz
  229. {
  230.     AUXR |= 0x80;           //定时器时钟1T模式
  231.     TMOD &= 0xF0;           //设置定时器模式 16位自动重载
  232.     TL0 = 0x40;             //设置定时初始值
  233.     TH0 = 0xA2;             //设置定时初始值
  234.     TF0 = 0;                //清除TF0标志
  235.     TR0 = 1;                //定时器0开始计时
  236.     ET0 = 1;                //使能定时器0中断
  237. }

  238. // 定时器0中断服务程序
  239. void Timer0_Isr(void) interrupt 1 {
  240.     static unsigned long key_press_time = 0;   // 按键按下时间 毫秒
  241.     msTicks++;
  242.    
  243.     if (!KEY_PIN) { // 按键按下
  244.         key_press_time++;
  245.         CTR_PIN = 0;
  246.         key_released = 0;  // 设置按键状态为按下
  247.     } else { // 按键松开
  248.         if (key_press_time >= RESET_TIME) { // 长按 10秒及以上
  249.             erase_mode = 1;
  250.         }
  251.         else if (key_press_time >= LONG_PRESS_TIME && key_press_time < RESET_TIME) { // 长按 3秒 ~ 10秒
  252.             key_long_press = 1;
  253.             key_pressed = 1;
  254.         }
  255.         else if (key_press_time >= SHORT_PRESS_TIME && key_press_time < LONG_PRESS_TIME) { // 短按少于3秒
  256.             key_pressed = 1;
  257.         }
  258.         else {
  259.             key_long_press = 0;
  260.             key_pressed = 0;
  261.         }
  262.         if (!key_released) {  // 按键曾按下
  263.             CTR_PIN = 1;
  264.         }
  265.         key_released = 1;   // 重置按键状态为释放
  266.         key_press_time = 0; // 重置按键计时
  267.     }
  268. }

  269. // 外部中断0初始化
  270. void Int0_Init(void) {
  271.     IT0 = 1;         // 使能INT0下降沿中断
  272.     EX0 = 1;         // 使能INT0中断0
  273.     EA = 1;          // 使能中断总开关
  274. }


  275. // 掉电模式
  276. void PowerDownMode(void) {
  277.     LED_PIN = 1; // 确保输出高电平
  278.     CTR_PIN = 1;
  279.     KEY_PIN = 1;
  280.     wakeup_status = 0;
  281.     _nop_();
  282.     _nop_();
  283.     MCU_POWER_DOWN();  // MCU进入掉电模式(STC8G系列)
  284.     _nop_();
  285.     _nop_();
  286.     _nop_();
  287.     _nop_();
  288. }

  289. // 从掉电模式唤醒
  290. void WakeUpFromPowerDown(void) {
  291.     PCON &= 0xFD; // 清除掉电标志
  292.     msTicks = 0;
  293.     idle_time = msTicks; // 重置空闲时间
  294. }

  295. // 外部中断0服务程序(按键唤醒)
  296. void INT0_ISR(void) interrupt 0 {
  297.     if (!wakeup_status){       // 如果在休眠状态,就执行唤醒操作
  298.         wakeup_status = 1;
  299.         WakeUpFromPowerDown(); // 从掉电模式唤醒
  300.     }
  301. }
复制代码


打赏

参与人数 1家元 +12 收起 理由
vip2128 + 12

查看全部打赏

回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-2-9 11:25:02 | 显示全部楼层
pxhxkij 发表于 2025-2-8 09:07
看的我一愣一愣的就是不懂。

提问前,先跟ds或者kimi说"我是小学生",然后再提问,它就会用最通俗易懂的说法告诉你了。
回复 支持 反对

使用道具 举报

发表于 2025-2-8 09:07:49 | 显示全部楼层
kindzhon 发表于 2025-2-6 09:42
代码中,好像没有把定时的时间长度保存到寄存器中,也缺少进入省电(掉电)模式的代码,长期在while循环中 ...

看的我一愣一愣的就是不懂
回复 支持 反对

使用道具 举报

发表于 2025-2-6 09:42:46 | 显示全部楼层
代码中,好像没有把定时的时间长度保存到寄存器中,也缺少进入省电(掉电)模式的代码,长期在while循环中,会很耗
好的,我需要仔细分析用户的问题以及之前提供的代码,找出其中存在的不足,并根据用户的新反馈进行改进。用户提到两点问题:一是没有将定时时间保存到寄存器中,二是缺少进入省电模式的代码,导致长期在while循环中耗电。

首先,关于保存定时时间到寄存器的问题。STC8G1K08单片机内部有EEPROM(或称为数据Flash),可以用来存储非易失性数据。之前的代码中,current_level变量在设置模式中调整,但并未保存到EEPROM中,因此每次上电后都会重置为默认值。用户希望将当前档位保存,以便下次上电时恢复。因此,需要添加EEPROM读写功能,当退出设置模式时,将current_level写入EEPROM,而在初始化时从EEPROM读取。

其次,省电模式的问题。用户指出代码主循环中一直运行while(1)循环,导致CPU持续运行,增加功耗。为了降低功耗,当系统处于非抽水状态且非设置模式时,可以进入掉电模式(Power Down Mode),此时CPU停止,只有外部中断或复位可以唤醒。需要配置相应的唤醒源,比如按键中断,当按键按下时唤醒单片机。

接下来,我需要考虑如何修改代码来实现这两个功能。对于EEPROM存储,需要参考STC8G1K08的数据手册,了解其EEPROM操作的具体步骤,包括擦除和写入的流程。通常,STC的EEPROM操作需要先擦除扇区,然后写入数据。同时,需要注意写入次数限制,避免频繁写入导致EEPROM寿命缩短。因此,只有在退出设置模式时才进行保存操作,而不是每次调整档位都保存。

对于省电模式,需要配置单片机进入掉电模式,并通过外部中断(如按键)唤醒。STC8G1K08的掉电模式可以通过设置PCON寄存器的PD位来进入。同时,需要将按键引脚配置为外部中断源,比如INT0或INT1,并设置中断服务程序来处理唤醒后的操作。进入掉电模式前,需要确保所有中断配置正确,并且唤醒后能正常恢复系统运行。

另外,需要检查现有代码中的功耗问题。主循环中的while(1)在没有任务时确实会持续运行,导致CPU一直工作。通过进入掉电模式,可以显著降低功耗。但需注意,在抽水过程中或设置模式下,不应进入省电模式,只有在空闲状态时才进入。

现在,我需要整合这些思考,修改原有代码,添加EEPROM存储和省电模式功能。同时,确保代码结构清晰,功能正常,避免引入新的错误。例如,在EEPROM操作时,需要处理可能的失败情况;在进入掉电模式前,关闭不必要的模块以进一步降低功耗。

最后,测试和验证修改后的代码,确保保存和恢复功能正常,省电模式有效,且按键唤醒可靠。可能需要使用示波器或电流表测量功耗变化,确认改进效果。

为了实现保存档位到EEPROM和进入省电模式的功能,以下是改进后的代码和详细说明:

改进点说明
EEPROM存储:每次退出设置模式时,将当前档位current_level保存到EEPROM,开机时自动读取。

省电模式:在非设置模式且未抽水时,进入掉电模式(需配置外部中断唤醒)。

降低CPU占用:优化主循环逻辑,减少空转。

代码改进
c

  1. #include <stc8.h>
  2. #include <intrins.h>

  3. #define KEY_PIN P32        // 按键引脚
  4. #define PUMP_PIN P33       // 抽水控制引脚
  5. #define EEPROM_ADDR 0x0000 // EEPROM存储地址(STC8G1K08的EEPROM起始地址)

  6. // 全局变量
  7. bit setting_mode = 0;       // 设置模式标志
  8. bit pump_running = 0;       // 抽水状态标志
  9. unsigned char current_level = 0; // 当前档位
  10. unsigned int no_operation_time = 0; // 无操作计时

  11. // 函数声明
  12. void Timer0_Init(void);
  13. void GPIO_Init(void);
  14. void Enter_PowerDown(void);
  15. void EEPROM_Write(unsigned int addr, unsigned char dat);
  16. unsigned char EEPROM_Read(unsigned int addr);
  17. void Delay_ms(unsigned int ms);

  18. //------------------------------------------------------------------
  19. // 主函数
  20. //------------------------------------------------------------------
  21. void main(void) {
  22.     GPIO_Init();
  23.     Timer0_Init();
  24.    
  25.     // 开机时从EEPROM读取保存的档位
  26.     current_level = EEPROM_Read(EEPROM_ADDR);
  27.     if (current_level > 10) current_level = 0; // 首次使用默认值
  28.    
  29.     while (1) {
  30.         if (setting_mode) {
  31.             // 设置模式:短按调整档位,3秒无操作退出
  32.             if (KEY_PIN == 0) {
  33.                 Delay_ms(20);
  34.                 if (KEY_PIN == 0) {
  35.                     current_level++;          // 每次短按增加0.2L档
  36.                     if (current_level > 10) current_level = 0; // 限制最大档位
  37.                     no_operation_time = 0;    // 重置无操作计时
  38.                     while (KEY_PIN == 0);     // 等待按键释放
  39.                 }
  40.             }
  41.             // 3秒无操作保存并退出
  42.             if (no_operation_time >= 3000) {
  43.                 EEPROM_Write(EEPROM_ADDR, current_level); // 保存到EEPROM
  44.                 setting_mode = 0;
  45.                 no_operation_time = 0;
  46.             }
  47.         } else {
  48.             // 非设置模式:短按启停抽水
  49.             if (KEY_PIN == 0) {
  50.                 Delay_ms(20);
  51.                 if (KEY_PIN == 0) {
  52.                     pump_running = !pump_running;
  53.                     PUMP_PIN = !pump_running; // 抽水机低电平有效
  54.                     while (KEY_PIN == 0);     // 等待释放
  55.                 }
  56.             }
  57.             
  58.             // 空闲时进入掉电模式
  59.             if (!pump_running && !setting_mode) {
  60.                 Enter_PowerDown(); // 进入省电模式(按键中断唤醒)
  61.             }
  62.         }
  63.     }
  64. }

  65. //------------------------------------------------------------------
  66. // 定时器0初始化(1ms中断)
  67. //------------------------------------------------------------------
  68. void Timer0_Init(void) {
  69.     AUXR |= 0x80;        // 1T模式
  70.     TMOD &= 0xF0;        // 定时器0模式1
  71.     TH0 = 0xFC; TL0 = 0x18; // 1ms定时
  72.     ET0 = 1;             // 使能中断
  73.     EA = 1;
  74.     TR0 = 1;
  75. }

  76. //------------------------------------------------------------------
  77. // 定时器0中断服务函数
  78. //------------------------------------------------------------------
  79. void Timer0_ISR(void) interrupt 1 {
  80.     static unsigned int key_press_time = 0;
  81.     static bit key_last_state = 1;

  82.     TH0 = 0xFC; TL0 = 0x18; // 重载初值

  83.     // 按键长按检测
  84.     if (KEY_PIN == 0) {
  85.         if (key_last_state) { // 首次按下
  86.             key_last_state = 0;
  87.             key_press_time = 0;
  88.         } else {
  89.             key_press_time++;
  90.             if (key_press_time >= 1000) { // 长按1秒进入设置
  91.                 setting_mode = 1;
  92.                 key_press_time = 0;
  93.             }
  94.         }
  95.     } else {
  96.         if (!key_last_state) { // 按键释放
  97.             key_last_state = 1;
  98.         }
  99.     }

  100.     // 设置模式无操作计时
  101.     if (setting_mode) no_operation_time++;
  102. }

  103. //------------------------------------------------------------------
  104. // 进入掉电模式(通过外部中断唤醒)
  105. //------------------------------------------------------------------
  106. void Enter_PowerDown(void) {
  107.     // 配置INT0下降沿唤醒
  108.     IT0 = 1;    // 边沿触发
  109.     EX0 = 1;    // 使能INT0中断
  110.     EA = 1;
  111.    
  112.     PCON |= 0x02; // 进入掉电模式(执行后CPU停止)
  113.     _nop_();
  114.     _nop_();
  115.    
  116.     // 唤醒后继续执行
  117.     EX0 = 0; // 关闭INT0中断
  118. }

  119. //------------------------------------------------------------------
  120. // EEPROM写入函数
  121. //------------------------------------------------------------------
  122. void EEPROM_Write(unsigned int addr, unsigned char dat) {
  123.     IAP_CONTR = 0x80;    // 使能IAP
  124.     IAP_CMD = 0x02;      // 写模式
  125.     IAP_ADDRH = addr >> 8;
  126.     IAP_ADDRL = addr & 0xFF;
  127.     IAP_DATA = dat;
  128.     IAP_TRIG = 0x5A;
  129.     IAP_TRIG = 0xA5;
  130.     _nop_();
  131.     IAP_CONTR = 0x00;    // 关闭IAP
  132. }

  133. //------------------------------------------------------------------
  134. // EEPROM读取函数
  135. //------------------------------------------------------------------
  136. unsigned char EEPROM_Read(unsigned int addr) {
  137.     unsigned char dat;
  138.     IAP_CONTR = 0x80;    // 使能IAP
  139.     IAP_CMD = 0x01;      // 读模式
  140.     IAP_ADDRH = addr >> 8;
  141.     IAP_ADDRL = addr & 0xFF;
  142.     IAP_TRIG = 0x5A;
  143.     IAP_TRIG = 0xA5;
  144.     _nop_();
  145.     dat = IAP_DATA;
  146.     IAP_CONTR = 0x00;    // 关闭IAP
  147.     return dat;
  148. }

  149. //------------------------------------------------------------------
  150. // GPIO初始化
  151. //------------------------------------------------------------------
  152. void GPIO_Init(void) {
  153.     P3M0 = 0x00; P3M1 = 0x00; // P3准双向模式
  154.     PUMP_PIN = 1; // 默认关闭抽水机
  155. }

  156. //------------------------------------------------------------------
  157. // 简单延时函数
  158. //------------------------------------------------------------------
  159. void Delay_ms(unsigned int ms) {
  160.     unsigned int i, j;
  161.     for (i = ms; i > 0; i--)
  162.         for (j = 110; j > 0; j--);
  163. }
复制代码

关键改进说明
EEPROM存储功能

写入:在退出设置模式时调用EEPROM_Write,将current_level保存到指定地址。

读取:开机时从EEPROM读取保存的档位,避免断电丢失。

注意事项:STC8G的EEPROM实为Data Flash,写入前需按扇区擦除。上述代码假设每次只写入一个字节,实际使用时需注意扇区对齐(本例未处理擦除,需根据具体型号调整)。

省电模式

进入掉电模式:在非设置模式且未抽水时,调用Enter_PowerDown()进入省电模式。

唤醒方式:通过INT0(P3.2按键)下降沿中断唤醒,唤醒后继续执行主循环。

功耗优化

在空闲时进入掉电模式,CPU停止运行,功耗降至微安级。

抽水期间和设置模式下保持正常运行。

验证步骤
EEPROM测试:设置档位后断电重启,确认档位是否保留。

省电模式测试:用万用表测量电流,观察进入掉电模式后功耗是否显著下降。

唤醒测试:在省电模式下按下按键,确认能否正常唤醒并抽水。

注意事项
EEPROM寿命:STC8G的Data Flash擦写次数约10万次,避免频繁写入。

中断冲突:确保定时器中断和外部中断(INT0)无冲突。

硬件设计:抽水机控制引脚需根据实际电路设计(如使用三极管或继电器驱动)。

通过以上改进,系统既能保存用户设置,又能在空闲时大幅降低功耗,满足实际应用需求。

打赏

参与人数 2家元 +18 收起 理由
aming_ou + 10 謝謝分享
pxhxkij + 8 精彩回帖

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2025-2-6 09:36:06 | 显示全部楼层
aming_ou 发表于 2025-2-3 21:48
现在代码都用ds了,谢谢,能编译下载,但长按短按后的计时(挡位)好像没有相关代码,无法知晓几秒钟。
感 ...

因为你的要求里没有说要省电啊,AI就是这样,忠实的执行你的要求。你可以把代码发给它,让它继续优化的。
回复 支持 0 反对 0

使用道具 举报

 楼主| 发表于 2025-2-5 21:22:20 | 显示全部楼层
银河小铁骑 发表于 2025-2-5 15:54
STC8G1K08 免费+包邮送样,可以加我微信 :19952583740

已有这个芯片和最小化系统板,满足上述需求的实验硬件条件
回复 支持 反对

使用道具 举报

发表于 2025-2-5 15:54:14 | 显示全部楼层
STC8G1K08 免费+包邮送样,可以加我微信 :19952583740
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-2-3 21:48:39 | 显示全部楼层
本帖最后由 aming_ou 于 2025-2-3 22:06 编辑
kindzhon 发表于 2025-2-3 10:04
deepseek-v3给的答案:
要实现这个功能,可以使用STC8G1K08单片机的GPIO、定时器和中断功能。以下是实现该 ...

现在代码都用ds了,谢谢,能编译下载,但长按短按后的计时(挡位)好像没有相关代码,无法知晓几秒钟。
感觉用楼上的方法,长按进入设置模式并开始抽水,短按停止抽水,并保存当次抽水的时间,下次短按时直接开始定时抽水,如定时结束前短按就提前结束抽水,这样的方案更好实现。
另外,ds代码中,好像没有把定时的时间长度保存到寄存器中,也缺少进入省电(掉电)模式的代码,长期在while循环中,会很耗点。
回复 支持 反对

使用道具 举报

发表于 2025-2-3 10:04:02 | 显示全部楼层
deepseek-v3给的答案:
要实现这个功能,可以使用STC8G1K08单片机的GPIO、定时器和中断功能。以下是实现该功能的步骤和代码示例:

1. 硬件连接
按键连接到单片机的某个GPIO引脚(例如P3.2),并配置为上拉输入。

抽水控制信号连接到另一个GPIO引脚(例如P3.3),用于控制抽水机的启停。

2. 软件设计
按键检测:使用定时器中断来检测按键的按下和释放,判断是短按还是长按。

设置模式:长按进入设置模式,短按切换档位,3秒无操作保存并退出设置模式。

抽水控制:在非设置模式下,短按开始或停止抽水。

3. 代码实现


  1. #include <stc8.h>

  2. #define KEY_PIN P32
  3. #define PUMP_PIN P33

  4. bit setting_mode = 0;  // 设置模式标志
  5. bit pump_running = 0;  // 抽水状态标志
  6. unsigned char current_level = 0;  // 当前档位
  7. unsigned int no_operation_time = 0;  // 无操作时间计数

  8. void Timer0_Init(void);  // 定时器0初始化
  9. void GPIO_Init(void);    // GPIO初始化
  10. void Delay_ms(unsigned int ms);  // 延时函数

  11. void main(void) {
  12.     GPIO_Init();
  13.     Timer0_Init();

  14.     while (1) {
  15.         if (setting_mode) {
  16.             // 设置模式下,检测按键短按次数
  17.             if (KEY_PIN == 0) {  // 按键按下
  18.                 Delay_ms(20);  // 消抖
  19.                 if (KEY_PIN == 0) {
  20.                     current_level++;  // 每次短按增加档位
  21.                     no_operation_time = 0;  // 重置无操作时间
  22.                     while (KEY_PIN == 0);  // 等待按键释放
  23.                 }
  24.             }
  25.             // 3秒无操作退出设置模式
  26.             if (no_operation_time >= 3000) {
  27.                 setting_mode = 0;
  28.                 no_operation_time = 0;
  29.             }
  30.         } else {
  31.             // 非设置模式下,短按控制抽水
  32.             if (KEY_PIN == 0) {  // 按键按下
  33.                 Delay_ms(20);  // 消抖
  34.                 if (KEY_PIN == 0) {
  35.                     if (pump_running) {
  36.                         pump_running = 0;
  37.                         PUMP_PIN = 1;  // 停止抽水
  38.                     } else {
  39.                         pump_running = 1;
  40.                         PUMP_PIN = 0;  // 开始抽水
  41.                     }
  42.                     while (KEY_PIN == 0);  // 等待按键释放
  43.                 }
  44.             }
  45.         }
  46.     }
  47. }

  48. void Timer0_Init(void) {
  49.     AUXR |= 0x80;  // 定时器0为1T模式
  50.     TMOD &= 0xF0;  // 清除定时器0模式位
  51.     TMOD |= 0x01;  // 定时器0模式1
  52.     TH0 = 0xFC;    // 定时1ms
  53.     TL0 = 0x18;
  54.     ET0 = 1;       // 使能定时器0中断
  55.     EA = 1;        // 使能总中断
  56.     TR0 = 1;       // 启动定时器0
  57. }

  58. void Timer0_ISR(void) interrupt 1 {
  59.     TH0 = 0xFC;  // 重装定时器初值
  60.     TL0 = 0x18;

  61.     static unsigned int key_press_time = 0;
  62.     static bit key_state = 1;

  63.     if (KEY_PIN == 0) {  // 按键按下
  64.         if (key_state) {
  65.             key_state = 0;
  66.             key_press_time = 0;
  67.         } else {
  68.             key_press_time++;
  69.             if (key_press_time >= 1000) {  // 长按1秒进入设置模式
  70.                 setting_mode = 1;
  71.                 current_level = 0;
  72.                 key_press_time = 0;
  73.             }
  74.         }
  75.     } else {  // 按键释放
  76.         if (!key_state) {
  77.             key_state = 1;
  78.             if (key_press_time < 1000 && key_press_time > 20) {  // 短按
  79.                 // 在设置模式下,短按次数已经在主循环中处理
  80.             }
  81.         }
  82.     }

  83.     if (setting_mode) {
  84.         no_operation_time++;
  85.     }
  86. }

  87. void GPIO_Init(void) {
  88.     P3M0 = 0x00;  // P3口为准双向口
  89.     P3M1 = 0x00;
  90.     PUMP_PIN = 1;  // 初始状态关闭抽水机
  91. }

  92. void Delay_ms(unsigned int ms) {
  93.     unsigned int i, j;
  94.     for (i = ms; i > 0; i--)
  95.         for (j = 110; j > 0; j--);
  96. }
复制代码

4. 代码说明
Timer0_ISR:定时器0中断服务程序,用于检测按键的长按和短按,并计时无操作时间。

main函数:主循环中根据设置模式和非设置模式分别处理按键事件和抽水控制。

GPIO_Init:初始化GPIO引脚,配置按键和抽水控制引脚。

Timer0_Init:初始化定时器0,用于定时检测按键状态和无操作时间。

5. 注意事项
按键消抖:通过延时函数消除按键抖动。

定时器中断:定时器0每1ms中断一次,用于检测按键状态和无操作时间。

抽水控制:通过控制GPIO引脚的高低电平来控制抽水机的启停。

通过以上代码和设计,可以实现所需的功能。

打赏

参与人数 1家元 +30 收起 理由
数码家园 + 30

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2025-1-30 22:25:38 | 显示全部楼层
现在外地,手机上查看
回复 支持 反对

使用道具 举报

发表于 2025-1-30 21:35:47 | 显示全部楼层
  1. #include <STC8G.H>

  2. #define KEY_PIN P3_2  // 假设按键连接到P3.2引脚

  3. // 设置档位
  4. #define LEVEL_0_8L  0
  5. #define LEVEL_1_0L  1
  6. #define LEVEL_1_2L  2
  7. #define LEVEL_1_4L  3
  8. #define LEVEL_1_6L  4
  9. #define LEVEL_1_8L  5
  10. #define LEVEL_2_0L  6

  11. unsigned char water_level = LEVEL_1_0L;  // 初始档位为1.0L
  12. unsigned int key_press_time = 0;         // 按键按下的时长计时
  13. unsigned char key_press_count = 0;       // 短按计数器
  14. unsigned char setting_mode = 0;          // 设置模式标志

  15. // 延时函数
  16. void delay_ms(unsigned int ms) {
  17.     unsigned int i, j;
  18.     for(i = ms; i > 0; i--) {
  19.         for(j = 120; j > 0; j--);
  20.     }
  21. }

  22. // 检测按键是否被按下
  23. bit key_pressed() {
  24.     return (KEY_PIN == 0);  // 假设按键低电平有效
  25. }

  26. // 进入设置模式
  27. void enter_setting_mode() {
  28.     setting_mode = 1;
  29.     key_press_count = 0;  // 重置短按计数
  30.     // 可以在此设置LED等提示设置模式
  31. }

  32. // 保存设置
  33. void save_setting() {
  34.     // 在此保存档位选择
  35.     // 比如可以通过EEPROM存储当前档位
  36. }

  37. // 切换档位
  38. void switch_level() {
  39.     water_level = (water_level + 1) % 7;  // 增加档位,最多6档
  40. }

  41. // 抽水函数
  42. void start_water_pumping() {
  43.     // 开始抽水操作
  44.     // 根据档位进行控制水泵运行
  45. }

  46. void stop_water_pumping() {
  47.     // 停止抽水操作
  48.     // 停止水泵
  49. }

  50. void main() {
  51.     unsigned char key_state = 0;  // 记录按键状态
  52.     unsigned char last_key_state = 1;  // 上一个按键状态

  53.     while(1) {
  54.         if(key_pressed()) {
  55.             if(last_key_state == 1) {  // 按键刚按下
  56.                 key_press_time = 0;     // 计时清零
  57.                 last_key_state = 0;     // 更新按键状态
  58.             }
  59.             key_press_time++;  // 按键计时

  60.             if(key_press_time > 1000) {  // 长按超过1000ms(大约1秒)
  61.                 if(!setting_mode) {  // 如果不是在设置模式中
  62.                     enter_setting_mode();  // 进入设置模式
  63.                 }
  64.             }
  65.         } else {
  66.             if(last_key_state == 0) {  // 按键刚释放
  67.                 if(key_press_time < 1000) {  // 短按(小于1秒)
  68.                     if(setting_mode) {
  69.                         key_press_count++;
  70.                         if(key_press_count > 3) {
  71.                             key_press_count = 0;  // 每次超过3次就回到0
  72.                         }
  73.                         switch_level();  // 切换档位
  74.                     } else {
  75.                         start_water_pumping();  // 开始抽水
  76.                     }
  77.                 }
  78.                 last_key_state = 1;  // 更新按键状态
  79.                 key_press_time = 0;  // 重置计时
  80.             }
  81.         }

  82.         if(setting_mode && key_press_time > 3000) {  // 如果设置模式下,超过3秒无操作
  83.             save_setting();  // 保存设置
  84.             setting_mode = 0;  // 退出设置模式
  85.         }

  86.         if(!setting_mode) {
  87.             // 非设置模式下检查是否要停止抽水
  88.             if(key_pressed()) {
  89.                 stop_water_pumping();  // 停止抽水
  90.             }
  91.         }

  92.         delay_ms(10);  // 延时避免按键检测过快
  93.     }
  94. }
复制代码


AI的回复
回复 支持 1 反对 0

使用道具 举报

发表于 2025-1-30 17:45:44 | 显示全部楼层
一路按键长按进设置,一路按键短按设置增加升数循环并设置记录每个档升数的电机运行时间,此键兼开电机和关电机,3路最好增加3led显示各升数,
还有1️路驱动电机
回复 支持 反对

使用道具 举报

发表于 2025-1-30 11:29:19 | 显示全部楼层
这些主要是需要pin2pin的才方便,程序都是小问题
回复 支持 反对

使用道具 举报

发表于 2025-1-30 08:49:16 | 显示全部楼层
应该可以实现吧,问题是代码比较复杂
回复 支持 反对

使用道具 举报

发表于 2025-1-30 05:52:32 | 显示全部楼层
本帖最后由 devcang 于 2025-1-30 05:57 编辑

进行设置,按1开始,直到再按停止,这是之后按抽水的开关的一次的量。
其它设置,如保温温度,等等


一般操作时
1键加水,按预设量加,中途随时可以停止。
然后加热,
完成加热后保温
……

打赏

参与人数 1家元 +7 收起 理由
aming_ou + 7 有STC8G1K08可用的代码吗?谢谢

查看全部打赏

回复 支持 反对

使用道具 举报

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

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-6-27 09:24 , Processed in 0.561601 second(s), 19 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

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