找回密码
 立即注册
查看: 111|回复: 12

求程序大神帮忙看看这个程序bug

[复制链接]
  • 打卡等级:常住居民I
  • 打卡总天数:62
  • 最近打卡:2025-10-11 16:53:16

4

主题

29

回帖

424

积分

中级会员

积分
424
发表于 2025-10-8 15:01:58 | 显示全部楼层 |阅读模式
1:主控芯片是STC8G1K08,电量芯片是HLW8032

检测一个负载功率,如果大于设定值,控制一个小马达转一下
如果小于设定值,也是控制一个马达转一下
现在的问题是明明功率没有达到设定值,为什么会误动作,纯待机情况下,几乎没有功耗,大约一天误触发一次
程序已经做了20次校验,持续20次满足才执行,为什么还是会误动作,(当然20改200这种情况可以好,但是反应太慢了)
看看有没有知道的大哥帮忙看看
截图202510081500271012.jpg

HLW8032.zip

184.3 KB, 下载次数: 3

回复

使用道具 举报 送花

3

主题

1880

回帖

608

积分

等待验证会员

积分
608
发表于 2025-10-8 15:03:00 | 显示全部楼层
你好,感谢分享你的问题。作为一名专注于嵌入式系统与电力监测的AI助手,我将从硬件、软件、算法和干扰等多个角度,为你分析这个问题的可能原因,并提供一些优化建议。

一、问题简述

你使用的是 STC8G1K08 作为主控芯片,HLW8032 作为电量检测芯片,实现的功能是:
检测负载功率;
如果功率大于设定值 → 控制马达动作;
如果功率小于设定值 → 控制马达动作;
当前问题:在待机状态下(几乎无功耗),仍偶尔误触发马达动作,约每天一次;
程序中已经做了 20 次连续检测校验,只有 20 次都满足条件才执行操作;
提高校验次数(如 200 次)可缓解误触发,但响应速度变慢。

二、可能原因分析

1. HLW8032 数据采集干扰或漂移

HLW8032 是一款高精度电量计量芯片,但其输入信号来自电流互感器和电压采样电路,极易受到以下因素影响:
电磁干扰(EMI):在待机状态下,虽然负载功率极低,但周围环境中的电磁噪声可能被误认为是有效信号;
采样电路设计不当:例如滤波电路不完善、输入信号未正确偏置;
芯片内部寄存器配置错误:例如增益设置、积分时间、参考电压配置不正确;
温漂或时间漂移:HLW8032 在长时间运行后,可能会出现数据漂移,尤其是在低功耗状态下,信号微弱,容易误判。

2. 软件滤波策略不足

虽然你做了“20次连续满足才触发”的判断,但该策略可能仍存在以下问题:
判断逻辑是“连续”满足条件,但如果某次采集出现异常值(如噪声干扰),就可能导致误判;
未对采集数据做中值滤波或滑动平均处理,直接使用原始值进行判断;
未设置死区(Deadband):即在设定值附近设置一个容差范围,防止在阈值附近来回波动;
未对采集数据做异常值剔除(如剔除最大/最小值)。

3. 马达控制逻辑设计问题

你提到“无论大于还是小于设定值都控制马达转一下”,这可能是一个逻辑设计问题:
如果是“只要状态变化就触发马达”,那么即使状态判断错误,也会导致马达误动作;
如果是“只要满足某一条件就触发”,但状态判断不稳,也会导致误触发;
建议增加“状态变化检测”和“防抖机制”。

4. 主控芯片的定时器/中断精度问题

STC8G1K08 是一款国产8位MCU,在高精度定时或中断处理上可能存在以下问题:
定时器精度不高,导致采样间隔不一致;
中断服务程序中处理数据时被其他中断打断,导致数据处理异常;
系统时钟源不稳定,影响采样频率。

5. 电源或地线噪声干扰
主控芯片和HLW8032共用电源,若电源不稳定或地线设计不合理,容易引入噪声;
在低功耗状态下,噪声更容易被放大。

三、优化建议

1. 硬件优化建议
检查采样电路:确认电流互感器与HLW8032之间的滤波电路是否合理,建议加入RC滤波(如10kΩ+10nF);
电源去耦:在HLW8032和MCU的VCC引脚加100nF陶瓷电容+10uF电解电容;
屏蔽干扰源:若马达或其他设备靠近检测电路,应做物理隔离或屏蔽;
确认HLW8032寄存器配置:特别是增益、积分时间、参考电压等参数,建议使用官方推荐配置;
考虑使用外部基准电压:提高低功耗状态下的测量精度。

2. 软件优化建议

(1) 增强数据滤波机制
  1. c
  2. // 示例:滑动平均 + 中值滤波
  3. define SAMPLESIZE 20
  4. uint16t samples[SAMPLESIZE];
  5. uint16t sortedsamples[SAMPLESIZE];
  6. void addsample(uint16t val) {
  7.     static uint8t idx = 0;
  8.     samples[idx++] = val;
  9.     if (idx >= SAMPLESIZE) idx = 0;
  10. }
  11. uint16t getmedian() {
  12.     memcpy(sortedsamples, samples, sizeof(samples));
  13.     qsort(sortedsamples, SAMPLESIZE, sizeof(uint16t), compare);
  14.     return sortedsamples[SAMPLESIZE / 2];
  15. }
复制代码

(2) 增加死区(Deadband)
  1. c
  2. define THRESHOLD 1000   // 设定值
  3. define DEADBAND 50      // 死区范围
  4. if (power > THRESHOLD + DEADBAND) {
  5.     // 触发动作
  6. } else if (power < THRESHOLD - DEADBAND) {
  7.     // 触发动作
  8. } else {
  9.     // 不动作,防止在阈值附近反复触发
  10. }
复制代码

(3) 改进触发逻辑
当前逻辑是“只要满足条件就触发”,建议改为“状态变化时才触发”;
可以记录上一次状态,仅在状态变化时触发一次。
  1. c
  2. static uint8t laststate = 0;
  3. if (currentstate != laststate) {
  4.     // 状态变化,触发马达动作
  5.     motoraction();
  6.     laststate = currentstate;
  7. }
复制代码

(4) 增加异常值剔除机制
对采集的数据进行最大值、最小值剔除,再取平均或中值。
  1. c
  2. // 剔除最大最小值后取平均
  3. uint16t filtered = (sum - maxval - minval) / (SAMPLESIZE - 2);
复制代码

四、其他建议
增加日志输出:可以在主循环中加入调试信息输出,记录每次采集的功率值、判断结果、是否触发动作,便于定位误触发发生时的数据;
使用看门狗机制:确保程序不跑飞;
降低采样频率:在低功耗状态下,可以适当降低采样频率,减少误触发机会;
考虑使用外部中断唤醒机制:仅在检测到明显变化时唤醒MCU进行处理。

五、总结

你当前的问题,很可能是由于 HLW8032 采集信号受到干扰 + 软件滤波策略不足 + 阈值判断逻辑过于敏感 所致。虽然你已经做了“20次连续判断”的策略,但在低功耗状态下,信号微弱且易受干扰,仍可能导致误触发。

建议采取以下组合策略:

| 优化方向 | 推荐措施 |
|----------|----------|
| 硬件抗干扰 | 增加滤波电容、电源去耦、屏蔽干扰源 |
| 数据采集 | 增加中值滤波、滑动平均、异常值剔除 |
| 判断逻辑 | 增加死区、状态变化检测 |
| 软件防抖 | 使用防抖机制、降低采样频率 |
| 日志调试 | 增加调试输出,辅助问题定位 |

通过以上方法,可以在不牺牲响应速度的前提下,显著降低误触发概率。

如果你愿意提供具体的代码片段(如HLW8032读取、判断逻辑、马达控制部分),我可以进一步帮你做代码级优化建议。

祝你项目顺利,早日解决问题!

—— 国芯人工智能助手

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

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:62
  • 最近打卡:2025-10-11 16:53:16

4

主题

29

回帖

424

积分

中级会员

积分
424
发表于 2025-10-8 15:07:49 | 显示全部楼层
国芯人*** 发表于 2025-10-8 15:03
你好,感谢分享你的问题。作为一名专注于嵌入式系统与电力监测的AI助手,我将从硬件、软件、算法和干扰等多 ...

void main(void)
{
        //IO口初始化
        P3M1=0x00; P3M0=0xF3;        //P3(推推推推双双推推)
        P5M1=0x00; P5M0=0xFF;        //P5(推推推推推推推推)
        P3=0xFF;                                //IO口输出高电平
        P5=0x00;                                //IO口输出低电平
       
        //串口中断初始化
        P_SW1=0x40;                //选择P3.2和P3.3引脚作为串口
        PCON &= 0x7F;        //波特率不倍速
        SCON = 0x50;        //8位数据,可变波特率
        AUXR &= 0xBF;        //定时器时钟12T模式
        AUXR &= 0xFE;        //串口1选择定时器1为波特率发生器
        TMOD &= 0x0F;        //设置定时器模式
        TMOD |= 0x20;        //设置定时器模式
        TL1 = 0xFA;                //设置定时初始值
        TH1 = 0xFA;                //设置定时重载值
        ET1 = 0;                //禁止定时器中断
        TR1 = 1;                //定时器1开始计
        ES=1;                   //使能串口中断
        EA=1;                        //打开总中断       
        //while循环
        while(1)
        {
                /*系统工作延时*/
                Delay10ms();//避免上电状态不稳定,所以加延时
               
                /*串口接收到数据*/
                if(JDE_UART==3)
                {
                        DAT_POWER=Data_Processing();                //电量数据处理
                        DAT_POWER=DAT_POWER*4;                                //采集的功率放大4倍
                        for(DI=0;DI<100;DI++){GET[DI]=0;}        //清除接收的数据               
                        CNT_UART=0;        //数据保存变量清0
                        JDE_UART=0;        //接收判断清0
                        JDE_WORK=1;        //电机工作
                }               
                if(JDE_WORK==1)        /*电机工作*/
                {
                        if(DAT_POWER>29)        /*功率大于设定值*/
                        {
                                CNT_GO=0;        //计数清0                               
                                CNT_BACK++;                //计数+1
                                if(CNT_BACK>20)        //连续检测到同样数据
                                {
                                        LOOP0:
                                        JDE_GO=0;        //计数清0
                                        P30=0;                //状态指示灯打开
                                        P31=1;            //红灯灭
                                       
                                        JDE_BACK++;                //时间计数+1
                                        if(JDE_BACK<10)        //停机0.1秒钟
                                        {
                                                P54=0;
                                                P55=0;
                                        }                                       
                                        else if(JDE_BACK<1100)        //运行10秒钟【开门】
                                        {
                                                P54=0;
                                                P55=1;
                                        }
                                       
                                        else
                                        {
                                                JDE_BACK=10000;        //停机
                                                CNT_BACK=0;                //计数清0
                                               
                                                P54=0;
                                                P55=0;
                                        }
                                }
                        }               
                        else if(DAT_POWER<24)                        /*功率小于20W*/
                        {
                                CNT_BACK=0;        //计数清0
                               
                                CNT_GO++;                //计数+1
                                if(CNT_GO>20)        //连续检测到同样数据
                                {
                                        LOOP1:
                                        JDE_BACK=0;        //计数清0
                                        P30=1;                //状态指示灯关闭
                                        P31=0;           //红灯开
                                       
                                        JDE_GO++;                //时间计数+1
                                        if(JDE_GO<10)        //停机0.1秒钟
                                        {
                                                P54=0;
                                                P55=0;
                                        }
                                       
                                        else if(JDE_GO<1100)        //运行10秒钟【关门】
                                        {
                                                P54=1;
                                                P55=0;
                                        }
                                       
                                        else
                                        {
                                                JDE_GO=10000;        //停机
                                                CNT_GO=0;                //计数清0
                                               
                                                P54=0;
                                                P55=0;
                                        }
                                }
                        }
                       
                        else        /*其它功率值,停机*/
                        {
                                /*增加稳定性程序*/
                                if((JDE_BACK>0)&&(JDE_BACK!=10000)){goto LOOP0;}        //继续走完
                                if((JDE_GO  >0)&&(JDE_GO  !=10000)){goto LOOP1;}        //继续走完
                               
                                CNT_GO=0;        //计数清0
                                CNT_BACK=0;        //计数清0
                               
                                P54=0;        //停机
                                P55=0;
                        }
                }
                else        /*电机不工作*/
                {
                        P54=0;
                        P55=0;
                }
        }
}


/***********************************************************************/
//======================================================================
//        串口中断
//======================================================================
/***********************************************************************/
void UART_Isr() interrupt 4
{
        //串口接收到数据
        if(TI){TI=0;}        //清中断标志
    if(RI)                        //接收到数据
    {
                RI=0;                        //清除接收标志位
                DAT_UART=SBUF;        //保存接收的数据
               
                //保存接收的数据
                //数据格式:55 5A 02 C5 60 00 05 AD 00 3C 64 00 2C 0A 4B B6 D0 00 81 A5 71 04 E9 04
                //数据格式:55 5A 02 C5 60 00 05 AD 00 3C 64 00 2C 0A 4B B6 D0 00 81 54 71 04 F2 BC
                if(JDE_UART==2)
                {
                        JDE_TIME=0;        //时间判断变量清0       
                        CNT_UART++;                                //计数+1
                        GET[CNT_UART]=DAT_UART;        //保存接收的数据
                       
                        //接收完整数据判断
                        if(CNT_UART==23)//接收完24个数据
                        {
                                CNT_UART=0;        //计数清0
                                DAT_UART=0;        //保存接收的数据
                               
                                //数据校验
                                CHK_UART=0;                                                //校验清0
                                for(DH=2;DH<23;DH++)                        //进行数据校验
                                {
                                        CHK_UART=CHK_UART+GET[DH];        //校验和计算
                                }
                               
                                CHK_UART%=256;                        //取256的余数
                               
                                if(CHK_UART==GET[23])        /*校验通过*/
                                {
                                        JDE_UART=3;                        //进行数据判断
                                }
                                else                                        /*校验不通过*/
                                {
                                        JDE_UART=0;                        //重新接收数据
                                }
                        }
                }       
                //数据头判断:0x55、0x5A
                if((JDE_UART==1)&&(DAT_UART==0x5A)){JDE_UART=2; GET[1]=0x5A; CNT_UART=1;}        //数据头2
                if((JDE_UART==0)&&(DAT_UART==0x55)){JDE_UART=1; GET[0]=0x55; CNT_UART=0;}        //数据头1
    }
}

点评

每次检测到功率大于或者小于后,需要延迟一个时间,不然20次很快就会读完 考虑你这个功率芯片可能真的存在干扰,但是干扰时间肯定不会很长,将这个判断时间拉长就可以避过去了 类似按键消抖的原理  详情 回复 发表于 2025-10-8 16:43
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:62
  • 最近打卡:2025-10-11 16:53:16

4

主题

29

回帖

424

积分

中级会员

积分
424
发表于 2025-10-8 15:08:51 | 显示全部楼层
国芯人*** 发表于 2025-10-8 15:03
你好,感谢分享你的问题。作为一名专注于嵌入式系统与电力监测的AI助手,我将从硬件、软件、算法和干扰等多 ...

void SC(UCHAR DAT)
{
        SBUF=DAT;        //赋值数据
        while(!TI);        //等特数据传送
        TI=0;                //清除数据传送标志
}
ULONG Data_Processing(void)
{
  long VP_REG=0;
        long V_REG=0;
        long CP_REG=0;
        long C_REG=0;
        long PP_REG=0;
        long P_REG=0;
        unsigned int PF=0;
        unsigned int PF_CNT_UART=0;       
        double DAT_V=0;                //电压保存
        double DAT_C=0;                //电流保存
        double DAT_P=0;                //功率保存
        double DAT_E=0;                //电量保存
        ULONG  DAT_OUT=0;        //用于输出保存
        if((GET[0]==0x55)&&(GET[1]==0x5A))
        {
                PP_REG=GET[14]*65536+GET[15]*256+GET[16];        //计算功率参数寄存
                P_REG=GET[17]*65536+GET[18]*256+GET[19];        //计算功率寄存器
                DAT_P=(PP_REG/P_REG)*Ue*Ce;                                        //计算有效功率
                DAT_OUT=DAT_P;                                                                //输出功率【1位小数】
        }
        return DAT_OUT;
}
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:208
  • 最近打卡:2025-10-15 11:03:15

787

主题

1万

回帖

1万

积分

管理员

积分
19770
发表于 2025-10-8 15:16:58 | 显示全部楼层
void main (void)
{
        P_SW2 |= 0x80;                                //允许访问扩展的特殊寄存器,XFR
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:62
  • 最近打卡:2025-10-11 16:53:16

4

主题

29

回帖

424

积分

中级会员

积分
424
发表于 2025-10-8 15:23:52 | 显示全部楼层
神*** 发表于 2025-10-8 15:16
void main (void)
{
        P_SW2 |= 0x80;                                //允许访问扩展的特殊寄存器,XFR

可以在明确一些吗 这个是什么意思 谢谢
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:208
  • 最近打卡:2025-10-15 11:03:15

787

主题

1万

回帖

1万

积分

管理员

积分
19770
发表于 2025-10-8 16:11:43 | 显示全部楼层
要首先加上这句
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:328
  • 最近打卡:2025-10-15 08:50:20
已绑定手机

85

主题

3256

回帖

7196

积分

荣誉版主

无情的代码机器

积分
7196
发表于 2025-10-8 16:12:32 | 显示全部楼层
“明明功率没有达到设定值”,“当然20改200这种情况可以好”

这不是自相矛盾吗,添加调试串口,触发动作时关键变量都打印出来看看。
另外不建议用goto语句。


上面提到的EAXFR看此贴:
新手必读!新手必读!新手必读!新手必读!新手必读!新手必读!新手必读!新手必读! - 老鸟反刍/吐槽,新手乐园,毕业设计 国芯人工智能技术交流网站 - AI32位8051交流社区
截图202510081611278629.jpg
三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:591
  • 最近打卡:2025-10-15 09:49:21
已绑定手机
已实名认证

116

主题

2889

回帖

7364

积分

版主

积分
7364
发表于 2025-10-8 16:43:11 | 显示全部楼层
l1649*** 发表于 2025-10-8 15:07
void main(void)
{
        //IO口初始化

每次检测到功率大于或者小于后,需要延迟一个时间,不然20次很快就会读完
考虑你这个功率芯片可能真的存在干扰,但是干扰时间肯定不会很长,将这个判断时间拉长就可以避过去了
类似按键消抖的原理
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:62
  • 最近打卡:2025-10-11 16:53:16

4

主题

29

回帖

424

积分

中级会员

积分
424
发表于 2025-10-8 16:59:16 | 显示全部楼层
王*** 发表于 2025-10-8 16:43
每次检测到功率大于或者小于后,需要延迟一个时间,不然20次很快就会读完
考虑你这个功率芯片可能真的存 ...

已经尝试修改100  一样会误触发 所以初步是怀疑单片机串口工作不正常

点评

考虑误触发可能由于非原子操作问题引起,在主循环执行中,使用EA=0保护,执行完后再EA=1放开。 https://www.stcaimcu.com/forum.php?mod=viewthread&tid=11928&highlight=%E5%8E%9F%E5%AD%90%E6%93%8D%E4%BD%9C&page  详情 回复 发表于 2025-10-8 17:38
那么有两种解决方向: 硬件方面,为了防止串口的错误干扰,可以将上拉电阻打开,使其在对面不工作的时候也有一个准确的信号。 还有一种误动作的情况是电源质量问题导致单片机重启,可以尝试给单片机电源脚位再加个电  详情 回复 发表于 2025-10-8 17:36
回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2025-10-16 05:31 , Processed in 0.137339 second(s), 116 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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