风雨行 发表于 2026-7-7 22:56:02

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

设备因为计时间,为此必须使用高精度温漂低的外部有源晶振做时钟。又因为设备电源断电自动保存数据,所以使用比较器做掉电检测,掉电时比较器在下降沿产生中断,即检测电源低于11.2V时产生下降沿比较器中断。
在中断里保存数据,中断优先级设置最高级别。


上图是有源晶振25MHz的方波输出到STC32G12K-LQPF48的P1.7脚。U2为3.3V稳压器。

上图是比较器采样电压输入到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就无此问题。
求助大神指教指教!




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


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





























神农鼎 发表于 2026-7-8 09:09:51





神农鼎 发表于 2026-7-8 09:10:20

<p>好的,信息已经齐全了。下面我来做完整的分析。</p>
<hr />
<h2>掉电检测比较器电路分析</h2>
<h3>一、电路工作原理</h3>
<p>您设计的电路是一个<strong>经典的掉电检测电路</strong>,其工作原理如下:</p>
<p><strong>分压网络计算:</strong></p>
<ul>
<li>上拉电阻 R4 = 200kΩ</li>
<li>下拉电阻 R6 = 24kΩ</li>
<li>分压比 = R6 / (R4 + R6) = 24 / (200 + 24) ≈ <strong>0.1071</strong></li>
</ul>
<p><strong>掉电阈值计算:</strong></p>
<ul>
<li>比较器负极(-)为内部 1.19V 参考电压(BandGap)</li>
<li>当 P5.1 电压 &lt; 1.19V 时,比较器翻转,产生中断</li>
<li>对应的电源电压阈值 = 1.19V / 0.1071 ≈ <strong>11.1V</strong></li>
<li>这与您图中标注的 <strong>11.2V</strong> 基本吻合</li>
</ul>
<p><strong>工作逻辑:</strong></p>
<ul>
<li>当 24V 电源正常(&gt;11.2V)→ P5.1 电压 &gt; 1.19V → 比较器输出高电平</li>
<li>当 24V 电源跌落(&lt;11.2V)→ P5.1 电压 &lt; 1.19V → 比较器输出翻转,产生中断</li>
<li>在中断服务程序中保存关键数据到 EEPROM</li>
</ul>
<h3>二、关键配置分析(基于您的代码)</h3>
<p>对照手册(STC32G 第20章 比较器),您的配置是正确的:</p>
<table>
<thead>
<tr>
<th align="left">配置项</th>
<th align="left">代码</th>
<th align="left">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">比较器正极(+)选择</td>
<td align="left"><code>CMPEXCFG |= 0x02</code></td>
<td align="left">CMPPS = 10,选择<strong>P5.1</strong> 为 CMP+</td>
</tr>
<tr>
<td align="left">比较器负极(-)选择</td>
<td align="left"><code>CMPEXCFG |= 0x04</code></td>
<td align="left">CMPNVS = 1,选择<strong>内部 1.19V 参考电压</strong> 为 CMP-</td>
</tr>
<tr>
<td align="left">P5.1 模式</td>
<td align="left"><code>P5M0 &amp;= ~0x02; P5M1 |= 0x02</code></td>
<td align="left">配置为<strong>高阻输入模式</strong>(重要!)</td>
</tr>
<tr>
<td align="left">迟滞设置</td>
<td align="left"><code>CMPEXCFG |= 0x40</code></td>
<td align="left">10mV 迟滞,防止电源噪声引起误触发</td>
</tr>
<tr>
<td align="left">中断优先级</td>
<td align="left"><code>PCMPH=1; PCMP=1;</code></td>
<td align="left">最高优先级,确保掉电时能及时响应</td>
</tr>
</tbody>
</table>
<h3>三、电路设计的优点</h3>
<ol>
<li><strong>D2(4.3V 稳压管)的钳位保护作用</strong>:当 P5.1 电压异常升高时(如 24V 上电瞬间),D2 将 P5.1 电压钳位在约 4.3V + 0.7V ≈ 5.0V,保护单片机 I/O 口。手册规定 I/O 口对地电压不得超过 VDD+0.3V(最大 5.5V),此保护非常必要。</li>
<li><strong>R5(1kΩ)的限流作用</strong>:在 D2 导通时限制电流,防止损坏稳压管和 I/O 口。</li>
<li><strong>分压电阻取值合理</strong>:200kΩ + 24kΩ 的总阻抗约 224kΩ,静态电流仅约 11.2V / 224kΩ ≈ <strong>50μA</strong>,功耗极低。</li>
</ol>
<h3>四、注意事项与潜在问题</h3>
<h4>⚠️ 问题1:P3.6 必须空置(您图中已标注)</h4>
<p>手册明确指出,当比较器负极选择内部 1.19V 参考电压时,<strong>P3.6 引脚必须悬空,不能做其他用途</strong>。您的代码中已将其配置为准双向口模式,但建议改为高阻输入且悬空:</p>
<pre><code class="language-c">P3M0 &amp;= ~0x40;P3M1 |= 0x40;   // P3.6 高阻输入
// 且外部不要连接任何电路
</code></pre>
<h4>⚠️ 问题2:有源晶振与比较器中断的潜在冲突</h4>
<p>您在论坛帖子中提到&quot;使用外部有源晶振作系统时钟后,比较器中断出现问题&quot;。这可能是由于:</p>
<ul>
<li><strong>时钟切换时序问题</strong>:从内部 IRC 切换到外部有源晶振时,如果比较器正在工作,时钟切换瞬间可能导致比较器误触发</li>
<li><strong>建议</strong>:先完成比较器初始化并稳定后,再切换系统时钟源;或者在时钟切换前暂时屏蔽比较器中断</li>
</ul>
<h4>⚠️ 问题3:掉电检测的响应时间</h4>
<ul>
<li>比较器从输入变化到输出翻转有约 <strong>0.1μs ~ 0.5μs</strong> 的响应时间(取决于配置的迟滞)</li>
<li>加上中断响应和 EEPROM 写入时间(约 5<del>10μs/字节),建议在掉电检测电路中增加 <strong>储能电容</strong>(如 100μF</del>470μF 电解电容在 24V 输入端),确保掉电后有足够时间完成数据保存</li>
</ul>
<h3>五、完整的比较器初始化参考代码</h3>
<pre><code class="language-c">// 比较器初始化函数 - 掉电检测
// 正极(P5.1)接外部电阻分压,负极接内部1.19V参考电压
void CMP_Init(void)
{
    P_SW2 |= 0x80;                     // 使能访问XFR

    // --- 引脚配置 ---
    P5M0 &amp;= ~0x02;P5M1 |= 0x02;   // P5.1 高阻输入(比较器正极输入)
    P3M0 &amp;= ~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 &amp; 0x40)                  // 判断是否比较器中断
    {
      CMPCR1 &amp;= ~0x40;               // 清除中断标志
      
      // 保存关键数据到EEPROM
      // IAP_EraseSector(...);
      // IAP_Program(...);
      
      // 掉电前准备
      // 关闭不必要的外设...
    }
}
</code></pre>
<h3>六、总结</h3>
<table>
<thead>
<tr>
<th align="left">项目</th>
<th align="left">结论</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">电路设计</td>
<td align="left">✅<strong>正确</strong>,分压比合理,阈值约 11.1V</td>
</tr>
<tr>
<td align="left">D2 保护</td>
<td align="left">✅<strong>必要且正确</strong>,4.3V 稳压管保护 I/O 口</td>
</tr>
<tr>
<td align="left">比较器配置</td>
<td align="left">✅<strong>正确</strong>,P5.1 正极 + 内部 1.19V 负极</td>
</tr>
<tr>
<td align="left">P3.6 处理</td>
<td align="left">⚠️<strong>必须悬空</strong>,不能接任何电路</td>
</tr>
<tr>
<td align="left">有源晶振冲突</td>
<td align="left">⚠️ 注意时钟切换时序,建议先稳定比较器再切换时钟</td>
</tr>
</tbody>
</table>
<p>如果您能具体描述一下&quot;比较器中断出现问题&quot;的现象(如误触发、不触发、程序跑飞等),我可以进一步帮您排查。</p>
<hr />
<p><a href="https://www.stcaimcu.com/plugin.php?id=twpx_xunsearch&amp;mod=forum&amp;q=%E6%AF%94%E8%BE%83%E5%99%A8%20%E9%87%87%E6%A0%B7%E7%94%B5%E5%8E%8B%20P5.1%204.3V%E7%A8%B3%E5%8E%8B%E7%AE%A1%201.19VREF&amp;f=_all&amp;m=yes&amp;syn=yes&amp;s=relevance&amp;fid=0">🔍 搜索 &quot;比较器 采样电压 P5.1 4.3V稳压管 1.19VREF&quot;</a></p>
<p>⏱️ 查找用时 7.9s | 总回答用时 28.0s</p>

神农鼎 发表于 2026-7-8 09:12:53

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

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



风雨行 发表于 2026-7-8 10:52:26

感谢指教回复!我就情况继续说明一些,请帮我看看

上面红色提示我了一下,我的初始化顺序可能有矛盾!请看看如下图


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

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

风雨行 发表于 2026-7-8 11:04:48

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


梁工 发表于 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,不会出现您所说不正常的情况。
类似的应用我是大量使用的。





程序拷贝如下:

#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所选择的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;                //中断指示
}



风雨行 发表于 2026-7-8 14:42:41

梁工 发表于 2026-7-8 13:56
之前电话里也跟您说,不会有问题的。
专门按你的要求写了一个程序测试好给你:



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

风雨行 发表于 2026-7-8 16:16:11

不好意思哈,刚才有人喊我有事情,出去一下了,现在才回复您。
刚才我测试梁工发的程序在我的电路板上,应是没问题。二个指示灯正常亮起!
我由于时间短,再继续测试哈。


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

   

风雨行 发表于 2026-7-8 16:52:04



找到问题所在了!!




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

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

一切正常了!

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

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

再次谢谢大家!

可以结题、收贴了哈 。











页: [1] 2
查看完整版本: 求助!使用外部有源晶振作系统时钟后,比较器中断出现问题 | 已清楚