dong889 发表于 2021-5-3 14:36:47

求助,STC8A单片机ADC重复调用导致程序假死的现象

单片机型号:STC8A8K64S4A12@24MHz
BUG描述:
多个ADC通道全部放在main函数while(1)主循环内轮询采样不会出问题,但是如果主循环内有ADC采样,定时器中断也调用ADC,则程序很容易出现假死现象。
我的程序有多个ADC通道没有时序要求放在主循环内采样,有另外两通道用于AC有效值检测的放在500uS的定时器中断内调用ADC采样。自己调试假死都停在ADC的while (!(ADC_CONTR & ADC_FLAG));语句上,就是ADC转换完成标识硬件不置1了?!而定时器中断任然能正常并不是真正跑飞,只是程序回不到main函数。
五一只放假1天早已经开始搬砖,这个问题折腾了好两天还是没解决,欸,大家帮忙看看吧,谢了!{:handshake:} ,具体看代码吧,注释<---箭头指的部分是调试记录,,,大家能提供一些程序调试方法也好的,比如变量内存、堆栈溢出之类的问题如何debug

以下程序运行正常:
void main()
{
        //...初始化过程...
       
        while(1)
        {
                if(T1ms>=100)                                                                //T1定时中断1ms加1
                {
                        T1ms = 0;
                        P77 = !P77;                                                        //main函数心跳指示灯,(示波器观察)100ms电平翻转正常        <---
                       
                        ADC3 = GetADCResult(3);
                        ADC4 = GetADCResult(4);
                        ADC5 = GetADCResult(5);
                        ADC6 = GetADCResult(6);
                        //...其它程序...
                }
               
                if(1==T2_Flag)                                                                //T2定时中断500us 标识符
                {
                        T2_Flag = 0;
                       
                        AC1_Buffer = GetADCResult(1);        //UAC1
                        AC2_Buffer = GetADCResult(2);        //UAC2
                        if(++AC_Samples>=40)                                        //采样数满40个(一个周期)
                        {
                                AC_Samples = 0;
                                AC_Volt_Sampling();                                        //计算均方根值+平均滤波
                                P43 = !P43;                                                //(示波器观察)电平翻转正常,但是翻转时间不能保证每次都是(500*40)us        <---
                        }
                }
        }
}


void TMR2_Isr() interrupt 12                                                        //TMR2定时中断 500uS
{
        AUXINTIF &= ~T2IF;                                                                //清T2中断标志
        T2_Flag = 1;
}


unsigned int GetADCResult(unsigned char ch)                                //读取ADC结果,这个采样函数应该没问题
{
        unsigned int ADCValue;
       
        ADC_CONTR = ADC_POWER | ADC_START | ch;
        while (!(ADC_CONTR & ADC_FLAG));                                        //等待ADC转换完成
        ADC_CONTR &= ~ADC_FLAG;                                                //清完成标志
        ADCValue = (ADC_RES<<4)+(ADC_RESL>>4);                        //
    return ADCValue;                                                                        //返回ADC结果
}


以下程序运行容易假死,而且把其它函数的调用时间间隔改越短越容易死机,比如if(T1ms>=100)调成10几秒钟就跑停了:
void main()
{
        //...初始化过程...
       
        while(1)
        {
                if(T1ms>=100)                                                                //T1定时中断1ms加1
                {
                        T1ms = 0;
                        P77 = !P77;                                                        //main函数心跳指示灯,(示波器观察)假死后电平不翻转!        <---
                       
                        ADC3 = GetADCResult(3);
                        ADC4 = GetADCResult(4);
                        ADC5 = GetADCResult(5);
                        ADC6 = GetADCResult(6);
                        //...其它程序...
                }
               
                if(1==T2_Flag)                                                                //T2定时中断500us
                {
                        T2_Flag = 0;
                        AC_Volt_Sampling();                                                //有效值计算+平均值滤波
                }
               
        }
}


void TMR2_Isr() interrupt 12                                                        //TMR2定时中断 500uS
{
        AUXINTIF &= ~T2IF;                                                                //清T2中断标志
       
        //AC采样
        AC1_Buffer = GetADCResult(1);                        //UAC1
        AC2_Buffer = GetADCResult(2);                        //UAC2
       
        //采样数+1
        if(++AC_Samples>=40)                                                        //采样数满40个,一个周期
        {
                AC_Samples = 0;
                T2_Flag = 1;
                P43 = !P43;                                                                //(示波器观察)假死后(500*40)us电平翻转正常        <---
        }
}


unsigned int GetADCResult(unsigned char ch)                                //读取ADC结果,这个采样函数应该没问题
{
        unsigned int ADCValue;
       
        ADC_CONTR = ADC_POWER | ADC_START | ch;
        while (!(ADC_CONTR & ADC_FLAG))                                        //等待ADC转换完成
        {
                P77 = 0;                                                                        //(示波器观察)假死后引脚低电平,用硬件debug调试运行也是一直停在这里        <--- 程序死在这里!
        }
        P77 = 1;
        ADC_CONTR &= ~ADC_FLAG;                                                //清完成标志
        ADCValue = (ADC_RES<<4)+(ADC_RESL>>4);                        //
    return ADCValue;                                                                        //返回ADC结果
}

电子喵星人 发表于 2021-5-3 14:46:57

定时器中断内不要做耗时大的应用(如采样等),这是第一个要点。

第二个,主程序里采样了,定时器中断里也采样了,这样很容易在主程序采样过程中发生定时器中断又重复操作采样时序,不乱套就怪了。

MYQQ2021 发表于 2021-5-3 15:47:56

你这种操作本身就很容易出问题呀,纯软的,还可以考虑使用可重入关键字,这个涉及到硬件的只能想办法去避免这种操作

dong889 发表于 2021-5-3 17:20:54

电子喵星人 发表于 2021-5-3 14:46
定时器中断内不要做耗时大的应用(如采样等),这是第一个要点。

第二个,主程序里采样了,定时器中断里也 ...

第一点,中断时间500us,ADC采样按最快速度配置的理论时间才1.333us,按理说完全没问题啊
第二点,重复ADC函数调用这个我也想到了,我再尝试改改…谢谢

zqf441775525 发表于 2021-5-3 21:59:08

重复ADC调用这个地方绝对有问题,不能这么写

gongxd 发表于 2021-5-3 23:11:16

while 加计数,超过多少计数退出while

youkebing 发表于 2021-5-4 00:04:12

如果你一定要在中断,和主循环中同时执行,那么在主循环中先关闭中断,在调用
比如 在主循环中{
EA= 0;
d=GetADCResult()
EA= 1;
//process 数据d
}
中断代码不用改

kundi 发表于 2021-5-4 10:59:12

大循环和中断涉及同一个外设操作时,大循环里涉及的这个外设操作要关闭中断请求,操作完了才打开。另外,中断函数越短越好,避免过长延时。之前我做串口透传上位机收发数据,大循环里串口发送数据时,如果这个时间段不禁止中断请求,就会出现死机的问题。

dong889 发表于 2021-5-4 15:37:33

好的,感谢楼上几位的回复,我继续修改程序。

warrenyan7251 发表于 2021-5-5 10:08:48

中断不应调用GetADCResult

小李非刀 发表于 2021-5-5 21:38:29

明显的资源竞争,ADC操作是不可重入的,某个任务启动ADC,没有结束前,别的任务不可以再使用ADC,必须互斥,否则会出现楼主说的“假死”, 比如,主程序启动ADC,等待其完成,此时进入中断,又触发ADC等待其完成,完成后清除ADC完成标志,退出中断,继续执行主程序中的等待ADC完成,但是此时ADC已经停止了,就等不来完成标志了。
在项目中,任何死等都要设置超时,避免意外。

dong889 发表于 2021-5-6 13:55:25

小李非刀 发表于 2021-5-5 21:38
明显的资源竞争,ADC操作是不可重入的,某个任务启动ADC,没有结束前,别的任务不可以再使用ADC,必须互斥 ...

感谢!你的回答解释了为什么while死等ADC完成标识出不来的原因。

dadatou 发表于 2021-5-6 16:22:33

楼主了解一下函数的重入。
页: [1]
查看完整版本: 求助,STC8A单片机ADC重复调用导致程序假死的现象