wuzhengmin 发表于 2025-9-10 11:47:32

比较器实验求救 | 已解决

我跟着冲哥学到了二十二集《比较器》

课程好像听懂了(实际是七窍通了六窍),程序也乖乖的跟着冲哥写好了
顺利的通过了编译(有一个警告),下载后,K33按键可以正常执行,数码管也能正常显示2位
但是,实验最重要的掉电保存,不能实现!
我把源程序放在附件中,哪位大侠帮我看看,哪里出了问题? 我折腾了10几天啦,挫败感严重,跪求指点.............

国芯人工智能 发表于 2025-9-10 11:48:00

你好,感谢你在学习比较器实验过程中遇到问题时积极寻求帮助,也感谢你对课程的坚持和努力。作为一名国芯人工智能,我将尽力为你分析你所遇到的问题,并给出一些排查建议,希望能帮你解决掉电保存功能无法实现的问题。

一、问题背景分析

根据你的描述:
你已完成比较器实验(第22集),程序已编写完成并通过编译;
编译时有一个警告,但程序仍能下载运行;
K33按键可以正常执行,数码管能正常显示两位;
核心问题:掉电保存功能无法实现;
你已附上源代码,希望有人协助分析。

从这些信息来看,硬件基本功能(如按键、数码管)是正常的,说明主程序逻辑、GPIO、定时器等模块工作正常。而掉电保存功能未能实现,说明问题可能出在以下几点:

二、掉电保存机制原理

掉电保存的核心在于:在系统断电前将关键数据写入非易失性存储器(如Flash或EEPROM)中,以便系统上电后读取并恢复状态。

在嵌入式系统中,常用的实现方式包括:

1. 使用内部Flash模拟EEPROM(适用于无外置EEPROM芯片的系统);
2. 使用外部EEPROM芯片(如AT24C02);
3. 使用RTC芯片自带的掉电保存寄存器;
4. 通过检测电源电压下降,触发中断进行保存操作。

你使用的是哪种方式?请确认以下几点:
是否使用了Flash或EEPROM进行数据保存?
是否有掉电检测电路或中断?
是否有保存数据的函数调用?
是否有上电初始化时读取数据的代码?

三、可能的故障点排查

1. Flash/EEPROM操作逻辑错误

常见问题包括:
未正确初始化Flash/EEPROM模块;
未解锁Flash写保护;
未等待写操作完成;
地址越界或覆盖了其他数据区域;
未对数据进行校验或处理断电中断时的异常情况。

建议你检查:
Flash擦写函数是否调用成功;
是否使用了正确的页地址和偏移;
是否在写入前进行了擦除;
是否设置了标志位用于判断是否已有保存数据。

2. 掉电检测机制缺失或不灵敏

如果你的系统没有掉电检测电路,那么掉电保存就只能依赖系统正常关机时主动调用保存函数。但若系统是突然断电,而没有触发保存函数,那么自然无法保存。

建议你:
检查是否有电源电压检测引脚(如ADC采样);
是否配置了看门狗或低电压中断(LVD);
是否在主循环或中断中调用了保存函数。

3. 数据保存函数未被调用

即使函数写好了,也有可能因为以下原因未被调用:
掉电触发条件设置不正确;
保存函数被放在主循环末尾,但在断电前未执行到;
函数调用顺序或条件判断错误;
编译优化导致函数被优化掉(可通过volatile关键字解决)。

建议你在保存函数中加入调试信息,例如点亮LED、串口打印、或使用调试器断点,验证函数是否执行。

4. 编译警告可能影响功能

你说有一个编译警告,请务必查看具体内容。常见警告如:
变量未使用;
函数返回类型不匹配;
指针类型不匹配;
Flash地址超出范围;
未初始化变量。

这些警告虽然不影响编译通过,但有可能导致运行时行为异常。请务必检查警告信息,并逐一排除。

四、建议的排查步骤

1. 确认保存函数是否被调用
在保存函数中加入LED点亮或串口打印语句;
使用调试器设置断点,观察执行流程。

2. 检查Flash/EEPROM读写是否成功
写入后立即读取并打印验证;
检查是否返回错误码或状态位。

3. 验证掉电触发条件
是否有掉电中断或ADC检测;
是否在掉电前预留足够时间进行保存。

4. 检查上电读取逻辑
是否有初始化时读取Flash/EEPROM的代码;
是否判断了标志位以确认数据有效性。

5. 查看编译警告内容
逐条分析,排除潜在问题。

五、代码分析建议

由于你已附上源代码,但由于目前无法查看附件,建议你:
将关键函数(如保存函数、Flash操作函数、掉电检测函数)粘贴出来;
说明你使用的MCU型号、开发环境(如Keil、IAR、STM32CubeIDE等);
提供编译警告的具体内容。

这样我可以更准确地帮你分析问题所在。

六、联系方式说明

你提供的电话和微信联系方式,我们理解你希望尽快解决问题的迫切心情。但出于安全和隐私考虑,建议你通过以下方式继续交流:
在学习论坛或课程平台中继续提问;
将代码上传到GitHub或网盘,分享链接;
使用QQ群、微信群等课程群组交流。

七、结语

嵌入式开发中,掉电保存功能确实是一个容易出问题的环节,尤其是涉及Flash操作和中断响应时。你已经完成了大部分功能,说明你已经具备了良好的基础,不要因为一时的困难而气馁。

只要你耐心排查、逐步验证,问题一定能解决。如果你愿意继续分享代码片段或具体现象,我会非常乐意进一步协助你定位问题。

祝你早日调试成功!

—— 国芯人工智能

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

wuzhengmin 发表于 2025-9-10 11:52:32

比较器中断函数如下:/******************* 比较器中断函数 ********************/
void CMP_Isr() interrupt 21
{


       
    CMPIF = 0;          //清中断标志
    P42 = CMPRES;       //中断方式读取比较器比较结果
   
    if(CMPRES)
    {
      if(LowVolFlag)
      {
            LowVolFlag = 0;    //清除低电压标志
            if(Test_cnt != Temp_cnt)
            {
                EEPROM_read_n(EE_ADDRESS,tmp,2);      //读出2字节
                Test_cnt = ((u16)tmp << 8) + tmp; //秒计数
                if(Test_cnt > 10000)    Test_cnt = 0;   //秒计数范围为0~10000
                Temp_cnt = Test_cnt;
                                EEPROM_SectorErase(EE_ADDRESS); //擦除扇区
                               
            }
      }
    }
    else
    {
      
/*下面8行关闭数码管
                HC595_SER = 0;
      for(i=0; i<16; i++)   //先关闭显示,省电
      {
            HC595_SCK = 1;
            HC595_SCK = 0;
      }
               
      HC595_RCK = 1;
      HC595_RCK = 0;       //锁存输出数据
*/               
                Display_Seg(0,0);//实际这个函数入口参数直接写0,就是上面8行关闭数码管
               
               

      if(!LowVolFlag)
      {
            LowVolFlag = 1;    //设置低电压标志
            if(Test_cnt != Temp_cnt)
            {
                Temp_cnt = Test_cnt;
               
                tmp = (u8)(Temp_cnt >> 8);
                tmp = (u8)Temp_cnt;
                EEPROM_write_n(EE_ADDRESS,tmp,2);
            }
      }
    }
}

这个是严格按照冲哥的步骤来写的

wuzhengmin 发表于 2025-9-10 11:54:17

主程序和定时器0中断函数如下:void main(void)
{
        Sys_init();                //系统初始化
       
        #if USB_STATE == 1
                usb_init();                                     //USB CDC 接口配置
                IE2 |= 0x80;                                    //使能USB中断
        #endif
       
        CMP_config();
       
    EA = 1;                                                                                        //IE |= 0X80;
       
        #if USB_STATE == 1
                while (DeviceState != DEVSTATE_CONFIGURED);   //等待USB完成配置
        #endif
       
        Parm_Read();//读取上电次数
       
       
        Timer0_Init();                                                                        //定时器初始化
        Init_595();
       
        while(1)
        {
               
                #if USB_STATE == 1               
                                if (bUsbOutReady)                                                        //如果接收到了数据
                                {
                                        //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
                                       
                                        usb_OUT_done();                                                        //
                                }
                #endif
                               
                if (LowVolFlag == 0)//没有低电压发生(就是正常工作的时候)
                {
                        Task_Pro_Handler_Callback();                                //执行功能函数
                       
                        SEG_Show_U32( Test_cnt );                        //显示操作次数
                }
        }
}



void Timer0_Isr(void) interrupt 1                //1MS执行一次
{

        if (LowVolFlag == 0)//没有低电压发生(就是正常工作的时候)
        {
                Task_Marks_Handler_Callback();                                        //系统计时
        }
}
已经把USB关闭啦

wuzhengmin 发表于 2025-9-10 11:56:09

数码管显示,考虑节约电:只要2位:

void SEG_Show_U32(u32 num)
{
        u8 i;
        for(i=0;i<2;i++)
        {
                passward = num%10;
                num /= 10;
        }
}



wuzhengmin 发表于 2025-9-10 11:57:43

同时, Parm_Read(void)函数中也限定Test_cnt 不能大于100void Parm_Read(void)
{
        EEPROM_read_n(EE_ADDRESS,tmp,2);      //读出2字节
    Test_cnt = ((u16)tmp << 8) + tmp; //秒计数
    if(Test_cnt > 100)    Test_cnt = 0;   //秒计数范围为0~10000,数码管显示2位的话,最大数值是100
        EEPROM_SectorErase(EE_ADDRESS); //擦除扇区
}       


wuzhengmin 发表于 2025-9-10 11:59:40

焦点聚集在以下代码:

Display_Seg(0,0);//实际这个函数入口参数直接写0,就是上面8行关闭数码管
               
               

      if(!LowVolFlag)
      {
            LowVolFlag = 1;    //设置低电压标志
            if(Test_cnt != Temp_cnt)
            {
                Temp_cnt = Test_cnt;

                tmp = (u8)(Temp_cnt >> 8);
                tmp = (u8)Temp_cnt;
                EEPROM_write_n(EE_ADDRESS,tmp,2);
            }
      }

为什么没有执行?难道是不够电?真要换电容?但是人家冲哥是可以的哦?

wuzhengmin 发表于 2025-9-10 12:07:17

实际上冲哥的例子是真的掉电保存,我们STC试验箱的程序22,是调整电位器让电压下降,触发比较器动作,比起冲哥的例子,掉电不够干脆,所以很想把冲哥的例子真正搞懂,也要在试验箱上真的实现才行!

DebugLab 发表于 2025-9-10 12:13:38

调电位器设置比较点略低于电源电压,让电源电压刚开始下降时就触发比较器,才能实现掉电保存

wuzhengmin 发表于 2025-9-10 12:20:38

DebugLab 发表于 2025-9-10 12:13
调电位器设置比较点略低于电源电压,让电源电压刚开始下降时就触发比较器,才能实现掉电保存 ...

冲哥的实验,和我们STC公司的实验22不同,冲哥是直接按Power_sw来验证,我们按K33的次数是否保存成功
页: [1] 2 3 4
查看完整版本: 比较器实验求救 | 已解决