amoBBS 阿莫电子论坛

 找回密码
 注册
搜索
bottom↓
查看: 1371|回复: 65

一行比较两个整数大小的C语句运行结果竟然会偶尔出错!

[复制链接]
发表于 2019-5-7 16:04:57 | 显示全部楼层 |阅读模式
一个量产3年的产品,从来没有出过问题,代码没改过、板子没改过,最近进让出现了诡异的错误。
                       
if(usWaterCounter.n >= usDestLiters.n) { // 如果加水量达到目标体积
                       
        // 停止加水
        if (VALVE_ON == ucValveState) {
                StartStopValve(); // 关闭电磁阀
        }               
       
        // 启动电机
        if (!ucMotorState.Func.Run) {
                RunMotor(2);
        }               
       
        Beep(31);  // 蜂鸣
        ucStepN++; // 进入下一个工作状态
        bUpdateData = 1; // 刷LCD显示
}

就是上面这段代码,每天循环运行,售出的数百台设备运行了3年没有故障。这几天发现新生产的设备运行到上述if语句时,明明usWaterCounter的值是小于usDestLiters的值(LCD实时显示这两个变量的值),然后就是能偶尔的执行到if语句之内开始停水启动电机进入下一个状态(加水量还没有达到目标数)。
作为一个严谨的电工,肯定先要怀疑自己,但是找了一天也没找到问题所在。程序内部没有数组越界访问、没有指针,变量值明明也是对的,就是判断大小会出错?真是遇见鬼了。用的stc15f2k32s2,真的想怀疑批次问题,但是没办法验证,之前的货都出完了。
这条语句是在while(1)循环内部的,循环运行十几秒就会出现误判情况。加水大概需要1分多钟,结果10几秒就停水了。出错概率大概就是1 / (每秒循环次数 X 10几秒),每秒循环次数估计有几千上万次。
于是我把if判断多写了一遍就工作正常了,如下:
if(usWaterCounter.n >= usDestLiters.n) if(usWaterCounter.n >= usDestLiters.n) { // 如果加水量达到目标体积
万分之1 X 万分之1  就是数亿分之一的概率,我怕万一再出现错误,于是就又多谢了一遍if,测试一上午,没有再出错。现在的代码成了:
f(usWaterCounter.n >= usDestLiters.n) f(usWaterCounter.n >= usDestLiters.n) if(usWaterCounter.n >= usDestLiters.n) { // 如果加水量达到目标体积
这再要出错就特么都数以万亿分之一了,真可以去买彩票了。

变量没变,连续两次大小判断竟然会出现不一样的结果,真是见了鬼了。

差点忘了提到一个现象,系统DC 36V供电,使用34063DC-DC降压到5V,我如果把明纬36V电源的输出电压调低一点,那么出错的概率就会跟着降低一点。先出货,有时间再找找是不是36v降到5v占空比太低导致电源有问题,电源问题导致MCU跑错。

真他妈见鬼了,干了10几年电工,第一遇到这么诡异的问题。
发表于 2019-5-7 16:18:41 | 显示全部楼层
变量有在中断中使用吗,如果有,要加上volatile,修饰,否则有可能判读的时候被中断,数值被改变而不知道!
发表于 2019-5-7 16:19:35 | 显示全部楼层
stc的单片机不熟悉,如果是8位机,而usWaterCounter和usDestLiters是16或32位的,则加载他们的时候不是原子操作,需要关中断保护。
发表于 2019-5-7 16:20:25 来自手机 | 显示全部楼层
对变量进行强制类型转换。
发表于 2019-5-7 16:21:13 | 显示全部楼层
xiaomu 发表于 2019-5-7 16:18
变量有在中断中使用吗,如果有,要加上volatile,修饰,否则有可能判读的时候被中断,数值被改变而不知道! ...

如果中断中有修改,加volatile也没用,需要关中断保护。
volatile只是让程序每次都从内存取数,而不是用寄存器中暂存的。
发表于 2019-5-7 16:26:13 | 显示全部楼层
34063这颗料很LOW的,我现在还有一管LC买的料,质量不怎么滴!
我也不知道这个是买了哪个牌子的,反正输入12V这个料就会挂,换LC买的HTC的,就稳稳的!
发表于 2019-5-7 16:27:04 | 显示全部楼层
变量定义加volatile试试?
发表于 2019-5-7 16:34:13 | 显示全部楼层
正在打算用dcdc取代7805的我看的菊花一紧

原因找到前,可以拍20集走近你大爷了
 楼主| 发表于 2019-5-7 16:34:19 来自手机 | 显示全部楼层
vuo50z 发表于 2019-5-7 16:19
stc的单片机不熟悉,如果是8位机,而usWaterCounter和usDestLiters是16或32位的,则加载他们的时候不是原子 ...

关中断也不起作用,而且中断里只是++操作,即便不关中断也不改出问题啊。大学专业是计科,写过操作系统,对计算机的底层还是颇为了解的,这回把我弄懵了。
 楼主| 发表于 2019-5-7 16:38:43 来自手机 | 显示全部楼层
huike 发表于 2019-5-7 16:26
34063这颗料很LOW的,我现在还有一管LC买的料,质量不怎么滴!
我也不知道这个是买了哪个牌子的,反正输入1 ...

34063没有问题,便宜好用,用了很多的还没出过问题。一直用st的。
发表于 2019-5-7 16:41:45 | 显示全部楼层
会不会是数据越界?  
发表于 2019-5-7 16:44:24 | 显示全部楼层
本帖最后由 dadatou 于 2019-5-7 16:49 编辑

如果两个变量在中断中使用了,那么首先肯定是要用volatile修饰,让程序每次都到物理地址去读取,但楼主使用多几次判断就能减少异常现象,则不是volatile修饰的原因。
那肯定是原子操作的问题,如果参与比较的两个变量不是8位,又在中断中使用了这个变量,那么就会出问题:
举个例子:
变量A为16位的数据,即两个字节,在中断中,A会执行加一操作,即A++;
那么在主程序中需要用到这个变量时,A正好加到255(0x00ff)。
第一步:主程序读出高字节为0。
第二步:主程序被中断,A执行加一操作,A的值变成256(0x0100)。
第三步:中断回来,主程序继续读低字节为0。
第四步:主程序此时使用的变量A的数值为0。而实际上,此时A的值为256。

这就是为什么要使用原子操作的原因。要明白一点,在8位机上操作大于8位的变量,在C语言中的一句话,是由若干指令完成的,在这中间的任何位置都有可能被中断,所以必须使用原子操作,简单的方法主是在主程序中使用这些变量时,关闭中断,使用完再开启中断。
发表于 2019-5-7 16:47:12 | 显示全部楼层
感觉楼主没有真正找到问题的原因啊。
发表于 2019-5-7 16:48:30 | 显示全部楼层
这估计要调试器看栈数据才好推测了. 走进去就中断执行
发表于 2019-5-7 16:53:02 | 显示全部楼层

加上解决方法

本帖最后由 lianglee 于 2019-5-7 16:58 编辑

usWaterCounter.n >= usDestLiters.n

这两个变量如果是16位或32位,且其中一个在中断里自加1.
出现楼主所述的情况很正常。

假定usWaterCounter.n是16位且在中断中自加1,
那么分解成8位,就是:0xYX 和0xWT
当YX=02,WT=FF时,进入中断,而且程序中断的位置刚好就是在程序先读低WT后,且未读高位YX时,进入了中断。
在中断里加1后,usWaterCounter.n变成了0x0100,即YX=03,WT=00,
中断退出后,程序再读取高位YX,结果就变成了0x03FF与usDestLiters.n做比较,而不是0300。

少了256次。

唯一的觖决方法是,在中断里计数的变量,必须定义为8位。

如果数值较大,可以再在正常程序中再加变量进行自加。防止读取高低位时,刚好进入了中断,造成逻辑误差。

楼上所说的加什么修饰,都没有用。如果一定要16位,那么必须得关中断。

当然上面所说的是指8位机,16位或32位机另当别论。
 楼主| 发表于 2019-5-7 16:53:22 来自手机 | 显示全部楼层
albert_w 发表于 2019-5-7 16:48
这估计要调试器看栈数据才好推测了. 走进去就中断执行

设备上没办法调试了
 楼主| 发表于 2019-5-7 16:56:44 来自手机 | 显示全部楼层
lianglee 发表于 2019-5-7 16:53
usWaterCounter.n >= usDestLiters.n

这两个变量如果是16位或32位,且其中一个在中断里自加1.

你说的这个我能理解,上午关中断试过没疗效啊。
这个固件用了几年没问题,突然就出现了问题,而且有点过猛。
 楼主| 发表于 2019-5-7 17:02:33 来自手机 | 显示全部楼层
lianglee 发表于 2019-5-7 16:53
usWaterCounter.n >= usDestLiters.n

这两个变量如果是16位或32位,且其中一个在中断里自加1.

我再试试,看是不是上午中断关的不对,比较前关全局中断试试。
多谢
 楼主| 发表于 2019-5-7 17:03:56 来自手机 | 显示全部楼层
dadatou 发表于 2019-5-7 16:44
如果两个变量在中断中使用了,那么首先肯定是要用volatile修饰,让程序每次都到物理地址去读取,但楼主使用 ...

多谢回复,打了这么多字辛苦了
发表于 2019-5-7 17:04:44 | 显示全部楼层
lswood 发表于 2019-5-7 16:56
你说的这个我能理解,上午关中断试过没疗效啊。
这个固件用了几年没问题,突然就出现了问题,而且有点过 ...

这个能理解,我之前也是同样的情况,分析了好久才得出结论。

同一程序,之前好好的,但是加入了某段函数之前,时间次序总是不对,去掉那段程序又正常。

就是因为这个原因,程序加长了,使得程序跑动后,在那个点进入中断的机率剧增,才爆露出来。

之前是因为在那个点进入中断的几率小,所以一切正常。
发表于 2019-5-7 17:09:12 | 显示全部楼层
有启动电机等这么重要的动作,应该加上延时滤波啊,即加水量达到目标体积并保持100ms以上则执行
发表于 2019-5-7 17:11:32 | 显示全部楼层
lswood 发表于 2019-5-7 16:34
关中断也不起作用,而且中断里只是++操作,即便不关中断也不改出问题啊。大学专业是计科,写过操作系统, ...

如果cpu宽度比数据宽度窄,++操作也不是原子的。
如果你的关中断方法正确,那还真是见鬼了。
你可以把判断异常的值记录下来,看看能不能看出什么规律。
发表于 2019-5-7 17:14:03 | 显示全部楼层
除了关中断,还可以监视一下执行if(usWaterCounter.n >= usDestLiters.n) 过程中有没有进中断触发++操作,理论上这个概率比出错的概率还要大一个数量级
发表于 2019-5-7 17:15:02 | 显示全部楼层
不知道程序是不是带操作系统的
发表于 2019-5-7 17:21:19 | 显示全部楼层
对于全局变量,从来是“关中断->缓冲至局部变量->开中断”来实现读取的,
写入的话需要更谨慎
发表于 2019-5-7 17:31:14 | 显示全部楼层
可以判定100% 是中断其他地方读取这个变量地址引起引起的问题。 因为写两遍就没有问题,说明确实是非原子操作造成了影响
 楼主| 发表于 2019-5-7 17:47:21 来自手机 | 显示全部楼层
zf12862177 发表于 2019-5-7 17:31
可以判定100% 是中断其他地方读取这个变量地址引起引起的问题。 因为写两遍就没有问题,说明确实是非原子操 ...

现在可以100%判定不是中断引起的,因为这个变量比较小,最大400多,而且出错的时候变量的值只有不超过100,高位字节一直是0。
现在大概找到问题了,是电源烦扰导致的跑飞应该,之前从来还没遇到过mcu跑飞的情况,最多是变频器干扰到485通信不正常。
现在是把电磁阀线去掉,怎么都正常,一接电磁阀就出现这个故障。我看下这批电磁阀电流是否过大、还有pwm的频率是否过低(防止电磁阀过热)
 楼主| 发表于 2019-5-7 17:51:43 来自手机 | 显示全部楼层
lianglee 发表于 2019-5-7 17:04
这个能理解,我之前也是同样的情况,分析了好久才得出结论。

同一程序,之前好好的,但是加入了某段函数 ...

大概率是mcu的pc寄存器跑飞了,把电磁阀线断开就好了,一接电磁阀就出问题。而且刚才突然想起中断访问的变量值出错时从没超过255,高位字节一直是0。
现在极度怀疑电磁阀pwm驱动的频率有点低,这批电磁阀可能电流太大了,导致干扰严重。
发表于 2019-5-7 17:55:20 | 显示全部楼层
应该是中断里有改变usWaterCounter.n和usDestLiters.n的值,你应该在if(usWaterCounter.n >= usDestLiters.n)前加条EA=0这条语句,执行完再开总中断
发表于 2019-5-7 18:44:15 | 显示全部楼层
lswood 发表于 2019-5-7 17:51
大概率是mcu的pc寄存器跑飞了,把电磁阀线断开就好了,一接电磁阀就出问题。而且刚才突然想起中断访问的 ...

电磁阀 2端 有没有吸收电容或者电阻电容?
 楼主| 发表于 2019-5-7 18:49:13 来自手机 | 显示全部楼层
kebaojun305 发表于 2019-5-7 18:44
电磁阀 2端 有没有吸收电容或者电阻电容?

有续流回路,明天上午看看到底是哪块问题。
发表于 2019-5-7 18:53:28 | 显示全部楼层
哈哈哈,看到单片机型号,我就确信我遇到的问题原因了。
发表于 2019-5-7 19:05:10 | 显示全部楼层
lswood 发表于 2019-5-7 17:51
大概率是mcu的pc寄存器跑飞了,把电磁阀线断开就好了,一接电磁阀就出问题。而且刚才突然想起中断访问的 ...

软件上对付电磁阀干扰最有效的措施就是延时滤波
 楼主| 发表于 2019-5-7 19:18:08 来自手机 | 显示全部楼层
a136498491 发表于 2019-5-7 18:53
哈哈哈,看到单片机型号,我就确信我遇到的问题原因了。

你遇到什么问题了?
别总让stc背锅,stc抗干扰还是可以的,这次我要看看到底是谁的锅。
发表于 2019-5-7 19:26:18 来自手机 | 显示全部楼层
很大可能是电磁阀的锅,国内这些做电磁阀的,乱七八糟乱做
发表于 2019-5-7 19:36:31 | 显示全部楼层
看来楼主对软件比较拿手,对硬件有欠缺,电源变化会跟故障关联明显是硬件问题,可能是LAYOUT问题比较大,电感啥的吸收不良,这类问题的典型特征就是一阶段产品OK,稍有变化,比如电源或某个器件变化就出现问题
发表于 2019-5-7 19:43:39 | 显示全部楼层
楼主还是要好好查查硬件电路和器件。
发表于 2019-5-7 20:38:26 | 显示全部楼层
gyzzg2030 发表于 2019-5-7 19:36
看来楼主对软件比较拿手,对硬件有欠缺,电源变化会跟故障关联明显是硬件问题,可能是LAYOUT问题比较大,电 ...

赞同  大伙从软件分析故障都非常精辟   但是忽略了楼主描述的一个故障现象:电压调低之后故障明显发生率下降   这个是关键点    34063是哪家公司的   有些芯片最高电压是30v  请重新看看芯片手册  或者按照先简单后复杂的排除思路,重新换一颗34063(满足36v工作的),或者直接宽电压类似7805的DCDC电源芯片换上去,看看是不是硬件引起的故障
发表于 2019-5-7 21:05:44 | 显示全部楼层
感觉还是电源的锅
发表于 2019-5-7 22:30:01 | 显示全部楼层
lswood 发表于 2019-5-7 19:18
你遇到什么问题了?
别总让stc背锅,stc抗干扰还是可以的,这次我要看看到底是谁的锅。 ...

不能说完全是STC的锅,我这里问题不好描述,因为在办公室里从来没有重现过,类似跑飞又不是完全跑飞。而且换了单片机就好。或者有问题的机器,重新烧程序也行。
我估计是我电源没有做好,我是AC-DC12V然后 DC-DC到 5V,可能现场干扰大或者因为用的ACDC质量差吧。反正不管了,已经不做了
发表于 2019-5-7 22:39:40 | 显示全部楼层
在上家公司有一个产品,用的也是STC单片机,也会碰到跟楼主一样的问题,也是偶尔出现的,有时判断就出错,没查出什么原因,程序是别人写的,只是我发现的,后来做了些处理,判断错了也做某些补救动作就算了
发表于 2019-5-8 00:45:55 | 显示全部楼层
电源脉冲会导致数据改变,是真的。听说是一种破解技术
 楼主| 发表于 2019-5-8 07:29:04 | 显示全部楼层
a136498491 发表于 2019-5-7 22:30
不能说完全是STC的锅,我这里问题不好描述,因为在办公室里从来没有重现过,类似跑飞又不是完全跑飞。而 ...

有问题重新烧程序就好了,这种问题我也经常遇到,看来stc要背一点点锅了
发表于 2019-5-8 07:35:19 来自手机 | 显示全部楼层
qwe2231695 发表于 2019-5-8 00:45
电源脉冲会导致数据改变,是真的。听说是一种破解技术

电压跌落会造成半导体性能波动。估计lz跑的是最高频率。8位机用32位数确实很头疼,之前用avr兼容机跑程序,当程序达到10kb及以上时,各种莫名其妙的毛病全来了。最后搞了2天还搞不定,换riscv兼容机了。
 楼主| 发表于 2019-5-8 08:16:37 | 显示全部楼层
这要是mcu用在宇宙辐射环境中,各种问题得多棘手啊
发表于 2019-5-8 08:57:23 | 显示全部楼层
3楼正解,不是原子操作,所有单片机处理不当都会有这个问题
发表于 2019-5-8 08:59:51 | 显示全部楼层
lswood 发表于 2019-5-8 07:29
有问题重新烧程序就好了,这种问题我也经常遇到,看来stc要背一点点锅了 ...

就算是 STC的锅,老姚也不会陪你损失的吧 ,,,只能说你的电路原来虽然没问题、但离问题很近了
发表于 2019-5-8 09:03:43 | 显示全部楼层
变量莫名其妙被改,楼主查查整个程序有没有数组下标或者指针范围溢出,特别是长时间运行后偶尔出现这种
发表于 2019-5-8 09:14:07 | 显示全部楼层
程序不太严谨   我都是每次中断里 刷新数据后 某个FLAG置为1  主循环里检测到1之后进行处理  然后置0     
发表于 2019-5-8 10:03:08 | 显示全部楼层
判断前后要加上若干个NOP,否则CISC单片机出现跑飞后会出现指令不同步情况。同样在RET指令后面也要加上3条以上NOP指令。
 楼主| 发表于 2019-5-8 10:08:07 来自手机 | 显示全部楼层
norman33 发表于 2019-5-8 09:03
变量莫名其妙被改,楼主查查整个程序有没有数组下标或者指针范围溢出,特别是长时间运行后偶尔出现这种 ...

变量没有被改,一直是正确的,就是判断出错。
 楼主| 发表于 2019-5-8 10:09:04 来自手机 | 显示全部楼层
ackyee 发表于 2019-5-8 09:14
程序不太严谨   我都是每次中断里 刷新数据后 某个FLAG置为1  主循环里检测到1之后进行处理  然后置0      ...

关中断了,不是中断的锅
 楼主| 发表于 2019-5-8 10:16:33 来自手机 | 显示全部楼层
结题,不再深究了,没时间。问题总结:1.不是原语问题,判断前后关开中断没有效果,并且出措施变量高位字节没有改变一直是0;2.变量没有被篡改过,就是判断出错,应该是电磁阀干扰到了cpu寄存器的条件位,导致跳转错误;3.电磁阀是12v的,36vpwm驱动,并联的ss36作为续流二极管,之前没有问题现在有问题,可能是这一批电磁阀有问题,没有上批存货没法对比了;4.通过多加一条if解决问题,暂时先这样吧。
发表于 2019-5-8 10:35:43 | 显示全部楼层
lswood 发表于 2019-5-8 10:16
结题,不再深究了,没时间。问题总结:1.不是原语问题,判断前后关开中断没有效果,并且出措施变量高位字节 ...

如果是干扰导致 CPU 出错,那你这么改只是治标不治本吧 ? 干扰可不止只干扰这一个语句吧?
 楼主| 发表于 2019-5-8 12:28:54 | 显示全部楼层
EMC菜鸟 发表于 2019-5-8 10:35
如果是干扰导致 CPU 出错,那你这么改只是治标不治本吧 ? 干扰可不止只干扰这一个语句吧? ...

确实不治本,又在找电源部分的问题了,真不省心
发表于 2019-5-8 13:19:29 | 显示全部楼层
最简单的测试办法就是在“//停止加水”注释上面把usWaterCounter.n和usDestLiters.n的值存到另外两个变量中并监测之
 楼主| 发表于 2019-5-8 17:33:28 来自手机 | 显示全部楼层
这回真的结题。真是他妈的见鬼了,确实是电源出的问题,生产这批板子时,工人把34063上的电容给贴错了,贴成了104,频率低成狗了,波纹也大成狗了,导致cpu工作不正常。纳闷的是其它地方都不跑飞,就这里跑飞。还纳闷贴片机的料栈没动,怎么就会错呢?
 楼主| 发表于 2019-5-8 17:34:12 来自手机 | 显示全部楼层
看来要对生产流程严加管控了
 楼主| 发表于 2019-5-8 17:37:28 来自手机 | 显示全部楼层
谢谢大家的参与,一个人的失误害的几个人折腾了两天。
发表于 2019-5-9 08:48:32 | 显示全部楼层
34063后面用的和104一样封装的陶瓷电容??应该用大个铝电解电容吧?
发表于 2019-5-9 09:18:42 | 显示全部楼层
8位单片机,短整型需要分两次读取,当读取出低字节时产生了中断,你去++,这个时候正好发生了进位,结果就产生了错误
不管是不是这个个问题引起的你这个现象,这都是需要注意的问题。
多余的话就不说了
发表于 2019-5-9 11:08:14 | 显示全部楼层
确实是教训啊!
 楼主| 发表于 2019-5-9 12:15:14 来自手机 | 显示全部楼层
nibia 发表于 2019-5-9 08:48
34063后面用的和104一样封装的陶瓷电容??应该用大个铝电解电容吧?

是控制34063工作频率的电容,贴片0805电容
发表于 2019-5-9 16:44:14 | 显示全部楼层
dadatou 发表于 2019-5-7 16:44
如果两个变量在中断中使用了,那么首先肯定是要用volatile修饰,让程序每次都到物理地址去读取,但楼主使用 ...

我有一个疑问,之前在8051中,有大量的 关中断的操作。
在现在都是arm的时代,是不是在stm32上,如果是32位的数据,已经不需要这种关闭中断的操作了
发表于 2019-5-9 17:20:52 | 显示全部楼层
nibia 发表于 2019-5-9 16:44
我有一个疑问,之前在8051中,有大量的 关中断的操作。
在现在都是arm的时代,是不是在stm32上,如果是32 ...

32位有时也需要,要不就不会有要求原子操作一说了
发表于 2019-5-10 08:59:04 | 显示全部楼层
lswood 发表于 2019-5-8 17:33
这回真的结题。真是他妈的见鬼了,确实是电源出的问题,生产这批板子时,工人把34063上的电容给贴错了,贴 ...

其实你的案例挺有特点,感觉是电压低到MCU工作不正常区域了,复位电压怎么设的?
友情提示:标题不合格、重复发帖,将会被封锁ID。详情请参考:论坛通告:封锁ID、获得注册邀请码、恢复被封ID、投诉必读
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|阿莫电子论坛(原ourAVR/ourDEV) ( 公安备案:44190002001997(交互式论坛) 工信部备案:粤ICP备09047143号 )

GMT+8, 2019-7-21 19:54

阿莫电子论坛, 原"中国电子开发网"

© 2004-2018 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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