这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » 为何我的STC8A单片机ADC重复调用会导致程序异常?

共7条 1/1 1 跳转至

为何我的STC8A单片机ADC重复调用会导致程序异常?

工程师
2021-05-05 22:18:18     打赏

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

                        AC2_Buffer[AC_Samples] = 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[AC_Samples] = GetADCResult(1);                        //UAC1

        AC2_Buffer[AC_Samples] = 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结果

}





关键词: STC8A     ADC     A/D     异常    

工程师
2021-05-05 22:32:36     打赏
2楼

定时器中断内不要做耗时大的应用(如采样等),这是第一个要点。
第二个,主程序里采样了,定时器中断里也采样了,这样很容易在主程序采样过程中发生定时器中断又重复操作采样时序,不乱套就怪了。


工程师
2021-05-05 22:37:03     打赏
3楼

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


工程师
2021-05-05 22:43:10     打赏
4楼
如果你一定要在中断,和主循环中同时执行,那么在主循环中先关闭中断,在调用
比如 在主循环中{
EA= 0;
d=GetADCResult()
EA= 1;
//process 数据d
}
中断代码不用改



工程师
2021-05-05 23:56:44     打赏
5楼

代码写的非常干练


专家
2021-05-06 00:02:41     打赏
6楼

感谢楼主的分享,很实用了。


工程师
2021-05-06 00:11:27     打赏
7楼

感谢楼主的分享,很实用了。


共7条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]