ooyobcqk 发表于 2025-4-18 13:30:15

请教一下,8G1K08A-8pin单片机测量PWM频率值偶现不准问题

我使用8G1K08A-8pin的单片机实现精准的PWM频率值测量(0-300Hz)的功能,目前无论是用高低电平计数,定时器计时,以及中断检测,在调节信号发生器的PWM频率值时用该单片机检测,都会出现频率偶发性的不精准测量,下面的代码是用高低电平持续时间计数的方法测量PWM 的周期,进而求得PWM频率值的方法。使用的是P33引脚进行高低电平检测计数,信号发生器不断增大频率,如图中所示,频率值缓慢上升,频率值本应该是58的位置,出现了53这个值,这是什么原因?应该怎么解决这种问题


#include <stc8.H>
#include <stdio.h>
#include <INTRINS.H>
#include <tm1651.h>

#define FOSC 11059200UL
#define BRT (65536 - FOSC / 115200 / 4)

#define ADC_Power 0x80      //使能ADC模块                打开ADC                将ADC引脚设置到
#define ADC_Start 0x40      //开始ADC转换
#define ADC_Flag 0x20      //转换完成标志位
#define ADC_SYS 0x2f      //将ADC系统时钟设定为SYSclk/2/16


//******************************************
//延时ms函数,参数:ms
//使用条件:晶振/系统频率30.000MHz
//******************************************
void Delay1000ms()                //@11.0592MHz
{
      unsigned char i, j, k;

      i = 57;
      j = 27;
      k = 112;
      do
      {
                do
                {
                        while (--k);
                } while (--j);
      } while (--i);
}

void Delay5us()                //@11.0592MHz
{
      unsigned char i;

      _nop_();
      i = 5;
      while (--i);
}

void Delay101us()                //@11.0592MHz
{
      unsigned char i, j;

      _nop_();
      _nop_();
      i = 2;
      j = 112;
      do
      {
                while (--j);
      } while (--i);
}

//******************************************
//函数说明:串口初始化
//******************************************
void UartInit(void)                //9600bps@11.0592MHz
{
      SCON = 0x50;                //8位数据,可变波特率
      AUXR |= 0x40;                //定时器时钟1T模式
      AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
      TMOD &= 0x0F;                //设置定时器模式
      TL1 = 0xE0;                        //设置定时初始值
      TH1 = 0xFE;                        //设置定时初始值
      ET1 = 0;                        //禁止定时器中断
      TR1 = 1;                        //定时器1开始计时
}
void Sendbyte(unsigned char dat){//发送8位数据到串口
SBUF = dat;
while(!TI);
TI=0;
}
char putchar(char c){//重定义printf()函数,用于串口打印信息
Sendbyte(c);
return c;
}


//******************************************
//函数说明:读取指定ADC引脚值。参数:ADC引脚编号(编号见上单片机引脚图)
//******************************************
//unsigned int analogRead(unsigned char ADC_Pin){
//
//ADC_CONTR = ADC_Power + ADC_Pin;//使能ADC,向寄存器写入将要读的ADC引脚
//ADCCFG = ADC_SYS;                                        //将ADC系统时钟设定为SYSclk/2/16
//ADC_CONTR |= ADC_Start;                        //开始ADC转换
//_nop_();
//_nop_();
//while(!(ADC_CONTR & ADC_Flag));      //等待完成
//ADC_CONTR &=~ ADC_Flag;                        //清除完成标志位
//return (ADC_RES<<8) | ADC_RESL;         //合并高8位低8位,返回合并后结果
//}


//******************************************
//主程序
//******************************************
void main(){
                unsigned long a=0,b=0;
          unsigned int c,d,e,f,sx;
                unsigned long temp;
    unsigned int period;
    unsigned int frequency;
                unsigned int low_time = 0; // 用于检测P30长时间低电平的计数器
    bit pwm_detected = 0; // 标志位,用于标记是否检测到PWM信号
                P1M0=0X00;
    P1M1=0X00;
                P3M0=0X00;
    P3M1=0X00;
                P5M0=0X00;
                P5M1=0X00;
    P3PU |= (1 << 0);   // 启用P30的内部上拉电阻
          UartInit();//初始化串口
                a=0;
    b=0;
    ES = 1;
    EA = 1;
               
                e=30;
      while(1){

                c=P33;
      if((c!=P33)&(P33==1))
      {
            a = 0; // 重置计数器
            b = 0;
            for(sx=4;sx>0;sx--)
            {
                while(P33)
                {
                  a++;
                  Delay5us();
                  if(a>500000)
                        break;
                }
                while(!P33)
                {
                  b++;
                  Delay5us();
                  if(b>500000)
                        break;
                }
            }
            period = a/10 + b/10; // 计算周期
            if(period > 0) {
                        P55=0;
                        Delay1000ms();
                        P55=1;
                        Delay1000ms();
                        frequency = 70000 / period; // 计算频率
                        printf("频率 = %u\n",frequency);
                if(e<frequency) {
                  P54 = 0; // 输出高电平
                  d=0;
                } else {
                  P54 = 1; // 输出低电平
                  d=1;
                }
            }
                  printf("继电器状态 = %u\n",d);
            
                        }
}
}
使用的是P33引脚进行高低电平检测计数,信号发生器不断增大频率,如图中所示,频率值缓慢上升,频率值本应该是58的位置,出现了53这个值,这是什么原因?应该怎么解决这种问题


国芯人工智能 发表于 2025-4-18 13:33:00

在STC8G1K08A-8pin单片机中测量PWM频率时出现偶发性不精准的问题,可能由多种因素引起。以下是一些可能的原因及相应的解决方案:

1. 信号抖动与噪声干扰
原因:PWM信号在传输过程中可能会受到噪声干扰,导致信号抖动,尤其是在高频段。这种抖动会导致单片机在检测高低电平时出现误判,从而影响频率测量的准确性。
解决方案:
硬件滤波:在PWM信号输入引脚前添加RC低通滤波器,可以有效滤除高频噪声,减少信号抖动。
软件滤波:在代码中增加去抖动算法,例如多次采样取平均值或中值滤波,以提高测量的稳定性。

2. 定时器精度与中断响应时间
原因:STC8G1K08A的定时器精度和中断响应时间可能会影响频率测量的准确性。特别是在高频测量时,定时器的计数误差和中断延迟会导致测量结果出现偏差。
解决方案:
提高定时器精度:使用更高精度的定时器或增加定时器的时钟源频率,以减少计数误差。
优化中断处理:尽量减少中断服务程序中的代码量,确保中断响应时间尽可能短。可以考虑使用硬件捕获功能来替代软件中断检测。

3. 代码逻辑与测量方法
原因:代码中使用的测量方法可能存在逻辑缺陷,例如在高低电平计数时未考虑到信号的边沿抖动或未正确处理信号的周期变化。
解决方案:
改进测量方法:使用定时器的捕获功能来直接测量PWM信号的周期,而不是通过高低电平计数。捕获功能可以更精确地记录信号的边沿时间,减少测量误差。
增加校验机制:在测量过程中增加校验机制,例如多次测量取平均值,或对测量结果进行合理性判断,以排除异常值。

4. 系统时钟与晶振稳定性
原因:系统时钟的稳定性直接影响定时器的精度。如果晶振频率不稳定或存在漂移,会导致定时器计数不准确,从而影响频率测量。
解决方案:
使用高精度晶振:选择高精度、低漂移的晶振作为系统时钟源,以提高定时器的精度。
校准系统时钟:定期对系统时钟进行校准,确保时钟频率的稳定性。

5. 电源稳定性
原因:电源电压的波动会影响单片机的正常工作,尤其是在高频测量时,电源噪声可能导致测量结果不准确。
解决方案:
电源滤波:在电源输入端添加滤波电容,减少电源噪声。
使用稳压电源:确保单片机工作在稳定的电源电压下,避免电压波动对测量结果的影响。

6. 代码优化与调试
原因:代码中可能存在未优化的部分,例如延时函数的使用可能导致测量过程中出现时间误差。
解决方案:
优化延时函数:使用更精确的延时方法,例如利用定时器实现精确延时,减少延时误差。
调试与测试:通过调试工具对代码进行逐步调试,观察测量过程中的变量变化,找出可能的问题点并进行优化。

7. 硬件设计
原因:硬件设计中的布局和布线可能会引入噪声,影响PWM信号的测量。
解决方案:
优化PCB布局:合理布局PCB,减少信号线的长度,避免信号线与噪声源靠近。
使用屏蔽措施:对敏感信号线进行屏蔽,减少外部干扰。

总结
针对STC8G1K08A-8pin单片机测量PWM频率时出现的偶发性不精准问题,可以从信号滤波、定时器精度、代码逻辑、系统时钟、电源稳定性、代码优化和硬件设计等多个方面进行排查和优化。通过综合应用上述解决方案,可以有效提高频率测量的准确性和稳定性。

希望这些建议能帮助您解决当前的问题。如果问题仍然存在,建议进一步深入分析具体现象,结合调试工具进行详细排查。

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

DebugLab 发表于 2025-4-18 14:06:21

程序多种方式都可复现,检查硬件,示波器测一下看看
页: [1]
查看完整版本: 请教一下,8G1K08A-8pin单片机测量PWM频率值偶现不准问题