求高手举例说明一下51中断的using使用注意事项

2020-01-15 19:27发布

看了51的通用寄存器,不明白里面的工作原理,以下是我在网上看到的例子51单片机中断函数using问题  

2012-04-24 00:24:15|  分类: 单片机 |  标签:4组工作寄存器   |举报|字号 订阅
看郭天祥的第4课,一开始有个例子,就是发光二极管每1秒(循环加1消耗时间)亮一次,每二秒(定时器0中断)所有数码管转一次,从0转到F  我的源程序如下:

#include<reg52.h>
#include<intrins.h>

unsigned int i;
unsigned int icount;
void delay(unsigned int ) ;
unsigned char code  table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};

sbit wei=P2^7;
sbit duan=P2^6;
sbit beep=P2^3;

void main ()
{  
icount=0;
i=0;
  P1=0xfe;

wei=1;
P0=0xc0;
wei=0;
    duan=1;
P0=0x00;
duan=0;

  //定时器1,方式1
TMOD=0x01;
EA=1;
ET0=1;
TH0=15536/256;
TL0=15536%256;
TR0=1;

    while(1)
    {
    delay(1000);
     P1=_cror_(P1,1);
  beep=~beep;





   }

  
}

void t0() interrupt 1 using 2
{
TH0=15536/256;
TL0=15536%256;
icount++;
  /*
if(icount%20==0)
{  beep=~beep;
   P1=_cror_(P1,1);
}
*/
if(icount%40==0)
{

if(i==16) i=0;
duan=1;
P0=table[i];
duan=0;
i++;

}


}

void delay(unsigned int z)
{
unsigned int x,y;
for(x=z;x>0;x--)
  for(y=110;y>0;y--);
}



以上程序经调试时,2秒的数码管是正常的,但是跑马灯去在不停的闪,而不是1秒,无意中将  中断函数 using 0  改为了using 1就正常了,去掉 using 0,也是正常的,之前的程序也有用过using 0,不知是否因为  RAM 段0中有什么指令有叠加,导致存入寄存器数据有误!!!


如果我仍然用 using 0,但是把dealy 函数中的内容直接入到while(1)中去,也是正常的!
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
9条回答
wildone
2020-01-15 20:24
jswd0810 发表于 2014-4-7 08:08
这儿有个解释,可以参考一下http://www.51hei.com/mcu/766.html

赞,比其它人详细解释了这项


刚才看到一位很牛的师兄写的一篇日志中提到了Keil C51中using这个关键字的用法,粗心的我本来一直都没有留意它是用来干嘛的(因为我一般看见它都是在中断服务函数的定义开头处,好像没有了它也可以中断呀,所以才没怎么管),然而在日志中有看到这个关键字,所以也考究了一下,突然发现,原来这个东东和我最近在帮一个同学调的一个程序的时候突然遇到一个很怪的问题是有关系的,而且就是因为它才搞得程序莫明奇妙的出错(因为编译通过了,看起来也没什么错误,按C语言的逻辑分析也分析不出个什么所以然来,所以才怪)。

        后来调试了好久,甚至到http://www.51hei.com/keil%CF%C2%D4%D8.html 这里下载了好几个版本的keil,反汇编代码看了N次,基本各个版本出来的结果都差不多,不知情的情况下,推断出是Keil C51的编译器问题,以为Keil C51的编译器的变量空间分配出问题了,R7本来是用来作参数传递的,却也被编译器分配用来在主函数中被用来作循环变量,结果就是循环变量的汇编判断指令CJNE指令执行前,R7的值(也就是循环变量)被改变了,导致判断R7总是不能达到循环设置的值,就不断地在循环,也就致使按键扫描去抖动这个延时循环无法完成,就导致延时后的第二次判断无法进行,自然按键下来也就没反应了。后来尝试着关了定时器中断,又可以了,那就可以肯定是在执行CJNE前,R7的值被改变了,就是因为跳到了中断,然后中断函数执行的过程中,R7值被改变了(因为中断函数中还调用了其它的函数,而R7后来被分析反汇编代码时发现是被用作参数传递的),中断执行完后R7的值当然不正确啦!

得出推断后我的解决办法就是:让C51编译好之后手动修改汇编的指令,不要直接使用寄存器来作循环变量,而是使用能实现间接寻地址的R1进行间接寻址(指向0x77)的方式,终于把问题解决了,因为0x77在执行中断函数的过程中没被修改,CJNE语句能正常判断了,循环能完成了,按键也能扫下来了,问题也可以解决了。后来我又想到用_at_关键字直接指定循环变量的分配地址,这样的话就不用手动改汇编指令了,编译器直接就是编译成使用间接寻址的,这也算解决了。但还是有个疑问,为什么编译器会不够聪明地把R7的空间分配错了呢?一直在疑问。。。。。。奇怪,怎么编译器会出这种问题?

        直到我刚才考究过了using的用法之后,才发现原来自己的推断是错的,结论的总方向还是对的,只是问题并不在编译器身上,真正的问题根源如下:

1.我的同学写的那个程序,用到了定时器0,它的中断服务函数定义是这样写的:

void t0(void) interrupt 1 using 0

{

    ........//一大串大代码,其中包括调用了其他函数,用到了R7作参数传递

}

这里就是加了个using 0,而问题就出在加了这个using 0,为什么?且听我的理解:

using关键字的作用就是指定某个函数在执行时切换寄存器组的:

  51中有四个寄存器组,每个组有R0-R7这8个寄存器,用于CPU的数据处理,一般主函数main()默认使用第0组,因为PSW寄存器的初始值的第3、4位是00嘛,即默认指定了第0组。using 0就是指定在执行函数时切换为使用第0组,不加using关键字的话,一般都默认使用第0组,但这样的话在调用其他函数(包括中断服务函数)的时候,就会加入一些压栈指令以保护原来的R0-R7寄存器的(这样的话,程序执行会效率会低一点,因为会产生很多个指令是用来压栈出栈的),照这样说,只是加了using 0,使用的也是第0组又不是其它的组,程序就不应该有问题了吧,但是就是因为加了using 0,编译了一次,才发现在定时器中断函数t0()的入口中并没有发现把R0-R7的代码压入栈呀,就是说没有保护好R7呀,那当然就是在执行完之后回来R7不能回复原来的值啦,接下来的事情。。。。我就不说啦,这就是问题的根源,去掉using 0就可以了,编译器就自动帮你将R0-R7压栈,手动加了using 0,就是让编译器以为之前用的并不是第0组,而现在执行这个中断函数时就切换到第0组,而省去了将R0-R7这8个寄存器压入栈的指令了,这样虽然看起来是快了,然而对于这个程序来说却是致命的问题!!!因为根本没有保护好R0-R7,而没有保护R7并不是我预期发生的!!!这不是编译器的问题,是自己没有了解好、用好using的问题啊!还有,其实也可以在编译器选项里有选项来硬性规定编译器统一不直接使用寄存器而是使用间接寻址的办法来改变循环变量分配的地址的,或者使用

#pragma NOAREGS

定义函数
#pragma AREGS

来规定某个函数的是这样子。

  当然,这样的规定和使用using本身并不冲突,只是使用了这个关键字后就可能会间接地产生一系列的问题,让人郁闷了这么久,其实本来如果有详细地看整个程序的反汇编代码,或许当时就会发现发现少了那段压栈指令了,而不是说推断为编译器分配空间的问题了。。。。。。。。希望我的这次教训对各位有所帮助!!!

  另外,using的用法,其实就是手动指定函数使用的寄存器组,用得不好,如果在中断里还有调用其它函数,用得不好会出现函数传递出错的,不信可以反汇编看看,建议如果对这个关键字用法和C51的结构及汇编不熟的话,请还是让C51编译器帮你好了,不要胡乱使用,因为会比较容易出错的,要切记哦!!!其实用using关键到底对在编译后会造成什么影响,建议自己亲自去查看汇编程序。。。

对不起,编译器,我错怪你了!!!

一周热门 更多>