找回密码
 立即注册
查看: 154|回复: 11

求助!使用外部有源晶振作系统时钟后,比较器中断出现问题 | 已清楚

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 最近打卡:2026-07-09 10:30:54
已绑定手机

1

主题

7

回帖

33

积分

新手上路

积分
33
发表于 2026-7-7 22:56:02 | 显示全部楼层 |阅读模式
设备因为计时间,为此必须使用高精度温漂低的外部有源晶振做时钟。又因为设备电源断电自动保存数据,所以使用比较器做掉电检测,掉电时比较器在下降沿产生中断,即检测电源低于11.2V时产生下降沿比较器中断。
在中断里保存数据,中断优先级设置最高级别。

有源晶振到STC32G12K-LQPF48的P1.7

有源晶振到STC32G12K-LQPF48的P1.7


上图是有源晶振25MHz的方波输出到STC32G12K-LQPF48的P1.7脚。U2为3.3V稳压器。
截图202607072220352176.jpg
上图是比较器采样电压输入到P51脚,D2是4.3V稳压管。P51设置为比较器(+)电压,去比较单片机内部1.19VREF电压(-)。

比较器初始化函数如下:


void CMP_int(void)      //比较器初始化函数  
{               
    PCMPH =1; PCMP =1;                          //比较器中断优先级最高级 -----------------         
    CMPEXCFG = 0x00;                            //比较器扩展配置寄存器  
                CMPEXCFG |= 0x40;                           //比较器DC迟滞输入选择,0:0mV; 0x40:10mV; 0x80:20mV; 0xC0:30mV
   //-------------------------------------  
//   CMPEXCFG &= ~0x04;                          //P3.6为CMP-输入脚
    CMPEXCFG |= 0x04;                           //内部1.19V参考电压为CMP-输入脚   
   //---------------------------------------------------------               
//      P3M0 &= ~0x80; P3M1 |= 0x80;                //P37 配置成无上下拉的高阻模式
       P3M0 &= ~0x80; P3M1 &= ~0x80;               //P37 配置成准双向口模式
//      P37 =0;
      //-------------------------------------  
//      P3M0 &= ~0x40; P3M1 |= 0x40;                     //P36 配置成无上下拉的高阻模式         
       P3M0 &= ~0x40; P3M1 &= ~0x40;               //P36 配置成准双向口模式  -------重要!---------                 
//     P36 =0;                                   //P36 =0   -------重要!---------         
     //---------------------------------------------------------               
      P5M0 &= ~0x02; P5M1 |= 0x02;                //P51 配置成无上下拉的高阻模式  -------重要!---------   
      CMPEXCFG |= 0x02;                           //P5.1为CMP+输入脚
     //-------------------------------------         
//    CMPEXCFG |= 0x01;                           //P5.0为CMP+输入脚  
//    CMPEXCFG &= ~0x03;                          //P3.7为CMP+输入脚        
//    CMPEXCFG |= 0x03;                           //ADC输入脚为CMP+输入脚
        //---------------------------------------------------------        
     CMPCR2 = 0x00;                              //比较器控制器2  
     INVCMPO = 0;                                //比较器正向输出
//    INVCMPO = 1;                                //比较器反向输出
    //-------------------------------------  
     DISFLT = 0;                                   //0=使能0.1us滤波
//    CMPCR2 &= ~0x3f;                                //比较器结果直接输出
     CMPCR2 |= 0x10;                             //比较器结果经过16个去抖时钟后输出   
    //---------------------------------------------------------        
     CMPCR1 = 0x00;                                    //比较器控制器1  
      PIE = 0;                                    //1=使能比较器上降沿中断  
    //-------------------------------------  
     NIE = 1;                                    //1=使能比较器下降沿中断  
    //-------------------------------------                  
      CMPOE = 0;                                  //1=使能比较器输出
//     CMPO_S = 0;                                 //选择P3.4作为比较器输出脚  
      CMPO_S = 1;                                 //选择P4.1作为比较器输出脚  
    //---------------------------------------------------------        
     CMPIF = 0;                                  //清中断标志                  
      CMPEN = 0;                                  //1=使能比较器模块   
     //---------------------------------------------------------        
}

比较器中断函数如下

/******************* 比较器中断函数 ********************/
void CMP_Isr() interrupt 21         //掉电时刻 保存累计流量  
{                //CMPRES=1,比较器结果为1=CMP+大于CMP-
     //---------------------------------------------------------        
       CMPIF = 0;                        //清中断标志  
     //---------------------------------------------------------                        
           TSP_V = 0;                        //0=关 显示屏电源控制脚         
           Q1    = 0;                        //0=关 报警继电器                 
           NIE   = 0;                        //禁止比较器下降沿中断         
           CMPEN = 0;                        //1=使能比较器模块   
           LED2 = 0;      
//0=亮,LED指示灯 测试用
          //---------------------------------------------------------            
    if( !CMPRES )  //比较器结果,0= CMP+小于CMP-   
    {
                //-----------------------------------------------------        
//               LED2 = 0;             //0=亮,LED指示灯                                
                //-----------------------------------------------------               
                if( !WO_Busy==0 )  //WO_Busy=1=数据写入忙标志 防止重入! ---------------------   
                {
                       //-----------------------------------------------------                                                
                        WO_Busy=1;      //WO_Busy=1=数据写入忙标志                                                         
                       //-----------------------------------------------------        
                        FQ  = labs(FQ);          // FQ --------
                       //-----------------------------------------------------               
                           tmp1[ 25 ] =  FQ >> 24;              //累计流量值 FQ -------------  
                           tmp1[ 26 ] = (FQ<<8 ) >> 24;            //
                           tmp1[ 27 ] = (FQ<<16) >> 24;            //(32位long)               
                           tmp1[ 28 ] = (FQ<<24) >> 24;            //                                                         
                       //-----------------------------------------------------                                                                                   
//                      EEPROM_WO_DRR( 70 );    //写入断电保存的寄存器的队列  70  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                       //下载程序时千万去掉(下次擦出用户EEPROM 的勾) !!!                                
                       //-----------------------------------------------------                  
                       WO_Busy =0;            //WO_Busy=1=数据写入忙标志                 
                       CMP_Busy =0;           //1=比较器忙标志  
                      //-----------------------------------------------------                                       
                }        
                //-----------------------------------------------------        
     }
          //---------------------------------------------------------        
            NIE = 1;                        //1=使能比较器下降沿中断                                 
            CMPEN = 1;                        //1=使能比较器模块
            TSP_V = 1;                        //0=关 显示屏电源控制脚                          
          //---------------------------------------------------------               
            delay_ms( 10 );   //替换储存操作测试                                 
            LED2 = 1;                //0=亮,LED指示灯 测试用        

          //---------------------------------------------------------        
}




上述程序在使用STC内部IRC做时钟就没有问题,但是使用外部有源晶振就出现比较器中断问题!中断大概0.8秒自动间断持续发生!但是系统与外部485通信波特率正常,通信正常,应该不是晶振问题!
奇怪现象:比较器初始化设置与P36和P37无关连了,但是现象却是与P36、P37有关联!示波器测试P17脚是0.5V电3V峰值的25M方波,P36、P37脚为悬空。
问题结果:使用外部有源晶振做时钟,无法正常使用比较器下降沿中断!使用内部IRC就无此问题。
求助大神指教指教!


截图202607072254566297.jpg

图1中有源晶振的1脚YEN,悬空就是使能开启使用晶振状态,特此说明一下。


图1中U2是南京微盟出的3.3V稳压器 ME6206A33XG,SOT23-3贴片小小封装,不占PCB空间,所以选择了它。

截图202607081130318699.jpg



























回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 2026-7-8 09:09:51 | 显示全部楼层
截图202607080908584172.jpg


截图202607080909486842.jpg
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 2026-7-8 09:10:20 | 显示全部楼层

好的,信息已经齐全了。下面我来做完整的分析。


掉电检测比较器电路分析

一、电路工作原理

您设计的电路是一个经典的掉电检测电路,其工作原理如下:

分压网络计算:

  • 上拉电阻 R4 = 200kΩ
  • 下拉电阻 R6 = 24kΩ
  • 分压比 = R6 / (R4 + R6) = 24 / (200 + 24) ≈ 0.1071

掉电阈值计算:

  • 比较器负极(-)为内部 1.19V 参考电压(BandGap)
  • 当 P5.1 电压 < 1.19V 时,比较器翻转,产生中断
  • 对应的电源电压阈值 = 1.19V / 0.1071 ≈ 11.1V
  • 这与您图中标注的 11.2V 基本吻合

工作逻辑:

  • 当 24V 电源正常(>11.2V)→ P5.1 电压 > 1.19V → 比较器输出高电平
  • 当 24V 电源跌落(<11.2V)→ P5.1 电压 < 1.19V → 比较器输出翻转,产生中断
  • 在中断服务程序中保存关键数据到 EEPROM

二、关键配置分析(基于您的代码)

对照手册(STC32G 第20章 比较器),您的配置是正确的:

配置项 代码 说明
比较器正极(+)选择 CMPEXCFG |= 0x02 CMPPS[1:0] = 10,选择P5.1 为 CMP+
比较器负极(-)选择 CMPEXCFG |= 0x04 CMPNVS = 1,选择内部 1.19V 参考电压 为 CMP-
P5.1 模式 P5M0 &= ~0x02; P5M1 |= 0x02 配置为高阻输入模式(重要!)
迟滞设置 CMPEXCFG |= 0x40 10mV 迟滞,防止电源噪声引起误触发
中断优先级 PCMPH=1; PCMP=1; 最高优先级,确保掉电时能及时响应

三、电路设计的优点

  1. D2(4.3V 稳压管)的钳位保护作用:当 P5.1 电压异常升高时(如 24V 上电瞬间),D2 将 P5.1 电压钳位在约 4.3V + 0.7V ≈ 5.0V,保护单片机 I/O 口。手册规定 I/O 口对地电压不得超过 VDD+0.3V(最大 5.5V),此保护非常必要。
  2. R5(1kΩ)的限流作用:在 D2 导通时限制电流,防止损坏稳压管和 I/O 口。
  3. 分压电阻取值合理:200kΩ + 24kΩ 的总阻抗约 224kΩ,静态电流仅约 11.2V / 224kΩ ≈ 50μA,功耗极低。

四、注意事项与潜在问题

⚠️ 问题1:P3.6 必须空置(您图中已标注)

手册明确指出,当比较器负极选择内部 1.19V 参考电压时,P3.6 引脚必须悬空,不能做其他用途。您的代码中已将其配置为准双向口模式,但建议改为高阻输入且悬空:

P3M0 &= ~0x40;  P3M1 |= 0x40;   // P3.6 高阻输入
// 且外部不要连接任何电路

⚠️ 问题2:有源晶振与比较器中断的潜在冲突

您在论坛帖子中提到"使用外部有源晶振作系统时钟后,比较器中断出现问题"。这可能是由于:

  • 时钟切换时序问题:从内部 IRC 切换到外部有源晶振时,如果比较器正在工作,时钟切换瞬间可能导致比较器误触发
  • 建议:先完成比较器初始化并稳定后,再切换系统时钟源;或者在时钟切换前暂时屏蔽比较器中断

⚠️ 问题3:掉电检测的响应时间

  • 比较器从输入变化到输出翻转有约 0.1μs ~ 0.5μs 的响应时间(取决于配置的迟滞)
  • 加上中断响应和 EEPROM 写入时间(约 510μs/字节),建议在掉电检测电路中增加 储能电容(如 100μF470μF 电解电容在 24V 输入端),确保掉电后有足够时间完成数据保存

五、完整的比较器初始化参考代码

// 比较器初始化函数 - 掉电检测
// 正极(P5.1)接外部电阻分压,负极接内部1.19V参考电压
void CMP_Init(void)
{
    P_SW2 |= 0x80;                     // 使能访问XFR
  
    // --- 引脚配置 ---
    P5M0 &= ~0x02;  P5M1 |= 0x02;     // P5.1 高阻输入(比较器正极输入)
    P3M0 &= ~0x40;  P3M1 |= 0x40;     // P3.6 高阻输入(必须悬空!)
  
    // --- 比较器扩展配置 ---
    CMPEXCFG = 0x00;
    CMPEXCFG |= 0x40;                  // 10mV迟滞,防噪声误触发
    CMPEXCFG |= 0x04;                  // 负极选择内部1.19V参考电压
    CMPEXCFG |= 0x02;                  // 正极选择P5.1
  
    // --- 比较器控制寄存器 ---
    CMPCR1 = 0x00;                     // 先清零
    CMPCR1 |= 0x80;                    // 使能比较器
    CMPCR1 |= 0x40;                    // 使能比较器输出
    CMPCR1 |= 0x10;                    // 下降沿中断(掉电时电压下降)
    CMPCR1 |= 0x08;                    // 使能比较器中断
  
    // --- 中断优先级(最高级) ---
    PCMPH = 1;                         // 比较器中断优先级高位
    PCMP  = 1;                         // 比较器中断优先级低位
  
    EA = 1;                            // 开启总中断
}

// 比较器中断服务函数
void CMP_ISR(void) interrupt 21        // 比较器中断号21
{
    if(CMPCR1 & 0x40)                  // 判断是否比较器中断
    {
        CMPCR1 &= ~0x40;               // 清除中断标志
      
        // 保存关键数据到EEPROM
        // IAP_EraseSector(...);
        // IAP_Program(...);
      
        // 掉电前准备
        // 关闭不必要的外设...
    }
}

六、总结

项目 结论
电路设计 正确,分压比合理,阈值约 11.1V
D2 保护 必要且正确,4.3V 稳压管保护 I/O 口
比较器配置 正确,P5.1 正极 + 内部 1.19V 负极
P3.6 处理 ⚠️必须悬空,不能接任何电路
有源晶振冲突 ⚠️ 注意时钟切换时序,建议先稳定比较器再切换时钟

如果您能具体描述一下"比较器中断出现问题"的现象(如误触发、不触发、程序跑飞等),我可以进一步帮您排查。


🔍 搜索 "比较器 采样电压 P5.1 4.3V稳压管 1.19VREF"

⏱️ 查找用时 7.9s | 总回答用时 28.0s

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 2026-7-8 09:12:53 | 显示全部楼层
手册明确指出,当比较器负极选择内部 1.19V 参考电压时,
P3.6 引脚必须悬空,不能做其他用途
您的代码中已将其配置为准双向口模式,但建议改为高阻输入且悬空:

===这回事,可以当 普通 I/O 使用


截图202607080912506602.jpg

回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 最近打卡:2026-07-09 10:30:54
已绑定手机

1

主题

7

回帖

33

积分

新手上路

积分
33
发表于 2026-7-8 10:52:26 | 显示全部楼层
感谢指教回复!我就情况继续说明一些,请帮我看看
截图202607081041562013.jpg
上面红色提示我了一下,我的初始化顺序可能有矛盾!请看看如下图
截图202607081042162504.jpg

上图显示我是先初始化系统时钟,后比较器。

另外P36我是悬空的,并且配置高阻模式、双向口模式各参数测试了一番,都是不正常。配置高阻模式时,比较器中断没有规律的发生,不是下降沿发生,LED2指示灯快速闪烁。
P36配置成双向模式并置=0时,情况说起来好一些,LED2变成有规则的缓慢闪烁。但是不表示是下降沿中断,因为比较器+输入电压一直恒压不变,就是说不是下降沿产生那个突变电压状态。
特此解释一下现场哈

回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 最近打卡:2026-07-09 10:30:54
已绑定手机

1

主题

7

回帖

33

积分

新手上路

积分
33
发表于 2026-7-8 11:04:48 | 显示全部楼层
另外还有个现场,不过已经解决了。昨天电话过梁工并且说明这个。
这个情况就是:使用外部25M有源晶振做时钟时,定时器T0配置24位计数(100ms一次中断)做系统任务定时器,就出错100ms变成几个ms级的定时器。
后来改成16位自动重载模式12个脉冲周期,定时器20ms计数5次=100ms,100ms一次任务执行。
上述只是说一下现场而已,已经改20ms的16位定时器模式,解决问题了,不用讨论哈。
截图202607081104383296.jpg

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:410
  • 最近打卡:2026-07-21 09:39:09

83

主题

7972

回帖

1万

积分

超级版主

积分
17025
发表于 2026-7-8 13:56:14 | 显示全部楼层
之前电话里也跟您说,不会有问题的。
专门按你的要求写了一个程序测试好给你:

/*************        功能说明        **************
请先别修改程序, 直接下载"01-比较器基本测试-C语言"里面的"compare.hex"测试.

本程序用于测试STC32G系列的模拟比较器。
比较器负输入端切换到内部1.19V:
比较器正输入端切换到P5.1:
比较结果从P3.4取反输出(方便示波器观察或接成震荡方式).
比较器配置为 下降沿中断, 中断函数取反P2.0输出方波。
主循环取反P2.1输出方波指示。
******************************************/

下图为实测示波器波形照片,黄色正弦波为输入到P5.1---CMP+_3的信号,CMP-接内部的1.19V,比较器中断里取反P2.0输出方波,用于指示中断。
实测输入1000Hz正弦波,均在波形下降沿至1.19V处产生中断,方波信号为500Hz,不会出现您所说不正常的情况。
类似的应用我是大量使用的。

截图202607081351173561.jpg

01-比较器基本测试.rar (49.38 KB, 下载次数: 1)

程序拷贝如下:

#define MAIN_Fosc                24000000L        //定义主时钟

#include        "..\STC32G.h"

/************************ 比较器配置 ****************************/
void        Compare_Config(void)        //比较器初始化
{
        u8        i;

        CMPCR1 = 0;
        CMPCR2 = 20;                //比较结果变化延时周期数, 0~63

        CMPEN = 1;        //1: 允许比较器,     0:关闭比较器
        PIE   = 0;        //1: 允许上升沿中断, 0: 禁止
        NIE   = 1;        //1: 允许下降沿中断, 0: 禁止
        DISFLT  = 0;        //0: 允许内部0.1uF滤波,    1: 关闭

        i = 0;
        i |= (3<<6);        //比较器迟滞输入选择: 0: 0mV,  1: 10mV, 2: 20mV, 3: 30mV
        i |= (1<<2);        //输入负极性选择, 0: 选择P3.6做输入,   1: 选择内部BandGap电压BGv做负输入.
        i |=  2;                //输入正极性选择, 0: 选择P3.7做输入,   1: 选择P5.0做输入,  2: 选择P5.1做输入,  3: 选择ADC输入(由ADC_CHS[3:0]所选择的ADC输入端做正输入, ADC电源要打开).
        CMPEXCFG = i;
        P5n_HighZ(Pin1);                        //P5.1做COM+_3输入

        CMPOE  = 1;                                                        //1: 允许比较结果输出到IO(P3.4或P4.1),  0: 比较结果禁止输出到IO
        INVCMPO = 0;                                                //1: 比较器结果输出IO取反, 0: 不取反
        CMPO_S = 0;        P3n_push_pull(Pin4);        //CMPO_S=0: 比较结果输出到P3.4,1:比较结果输出到P4.1。
//        CMPO_S = 1;        P4n_push_pull(Pin1);        //CMPO_S=1: 比较结果输出到P4.1,P4.1设置为推挽输出
}



/******************** task A **************************/
void main(void)
{
        WTST  = 0;
        CKCON = 0;
        EAXFR = 1;        //SFR enable, 允许访问扩展寄存器


        P1n_HighZ(Pin7+Pin6);        //P1.7 P1.6设置为高阻输入
        InputDisable(P1IE, Pin7+Pin6);        //(PxIE, ie), 关闭P1.7 P1.6的数字输入,省电

        //外部有源时钟24.576MHz
        XOSCCR = 0x80+(0<<6)+(0<<5)+(0<<2);                        //启动外部晶振, bit6=0:外部时钟,=1:外部无源晶振,bit5=0:低增益,=1:高增益;抗干扰bit3 bit2=0:48MHz以下, =1:24M以下,=2:12M以下,
        while (!(XOSCCR & 1));        //等待时钟稳定
        CLKDIV = 1;                         //主时钟分频系数, 1~255,  122.88/3=40.96.8MHz
        CLKSEL = 1;                                //主时钟源选择, 0: 内部高精度IRC(默认), 1: 外部高速晶振, 2: 外部32K晶振, 3: 内部32K低速IRC

        Compare_Config();
        EA = 1;                //允许总中断

        P2n_push_pull(Pin1+Pin0);;        //P2.1 P2.0用于测试指示

        while (1)
        {
                NOP(5);
                P21 = ~P21;        //主循环指示
        }
}


void CMP_ISR(void) interrupt CMP_VECTOR                //比较器中断函数
{
        CMPIF   =  0;        //比较器中断标志, 包括上升沿或下降沿中断, 软件清0
        P20 = ~P20;                //中断指示
}



回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 最近打卡:2026-07-09 10:30:54
已绑定手机

1

主题

7

回帖

33

积分

新手上路

积分
33
发表于 2026-7-8 14:42:41 | 显示全部楼层
梁*** 发表于 2026-7-8 13:56
之前电话里也跟您说,不会有问题的。
专门按你的要求写了一个程序测试好给你:

哦哦!谢谢梁工。我下载测试测试
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 最近打卡:2026-07-09 10:30:54
已绑定手机

1

主题

7

回帖

33

积分

新手上路

积分
33
发表于 2026-7-8 16:16:11 | 显示全部楼层
不好意思哈,刚才有人喊我有事情,出去一下了,现在才回复您。
刚才我测试梁工发的程序在我的电路板上,应是没问题。二个指示灯正常亮起!
我由于时间短,再继续测试哈。
截图202607081615307725.jpg

添加电路板上二个指示灯上去测试了。
测试为正常!!先回复您一下

截图202607081617536263.jpg     截图202607081626121649.jpg

回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 最近打卡:2026-07-09 10:30:54
已绑定手机

1

主题

7

回帖

33

积分

新手上路

积分
33
发表于 2026-7-8 16:52:04 | 显示全部楼层


找到问题所在了!!


截图202607082317028572.jpg

去掉上图外部有源晶振初始化函数的1和2处语句,问题解决!!!

比较器下降沿中断和有源晶振都恢复工作正常!

一切正常了!

比较器下降沿中断,外部低温漂有源时钟等全部正常工作!

感谢姚大大和梁工大的帮助!!!感谢各位看客的支持!
也感谢我自己脑壳,由于没有解决,几天了脑壳头发像刺猬一样竖起,乱糟糟头大了,这下子大脑松弛了!

再次谢谢大家!

可以结题、收贴了哈 。

  









回复

使用道具 举报 送花

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

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-7-22 10:31 , Processed in 0.091969 second(s), 87 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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