interrupt和using在C51中断中的使用

2015-10-13来源: eefocus关键字:interrupt  using  C51中断
C51的中断函数的格式为:void FuncIr(void) interrupt x [using y]

interrupt和using都是C51的关键字。C51中断过程通过使用interrupt关键字和中断号(0到3)来实现,中断号指明了中断程序的入口地址。有了这一声明,编译器不需理会寄存器组参数的使用和对累加器A、状态寄存器、寄存器B、数据指针和默认的寄存器的保护。只要在中断程序中用到,编译器会把它们压栈,在中断程序结束时将他们出栈。C51支持所有5个8051标准中断(从0到4)和在8051系列(增强型)中多达27个的中断源。

using关键字用来指定中断服务程序使用的寄存器组。用法是:using后跟一个0到3的数,对应着 4组工作寄存器。一旦指定工作寄存器组,默认的工作寄存器组就不会被压栈,这将节省32个处理周期,因为入栈和出栈都需要2个处理周期。这一做法的缺点是所有调用中断的过程都必须使用指定的同一个寄存器组,否则参数传递会发生错误。因此对于using,在使用中需灵活取舍。

如果在ISR(中断服务函数)中使用寄存器,那么必须处理好using的使用问题:

1、中断服务函数使用using指定与主函数不同的寄存器组(主函数一般使用 Register bank 0)。

2、中断优先级相同的ISR可用using指定相同的寄存器组,但优先级不同的ISR必须使用不同的寄存器组,在ISR中被调用的函数也要使用using指定与中断函数相同的寄存器组。

3、如果不用using指定,在ISR的入口,C51 默认选择寄存器组0,这相当于中断服务程序的入口首先执行指令:MOV PSW #0

这点保证了,没使用using指定的高优先级中断。可以中断使用不同的寄存器组的低优先级中断。

4、使用using关键字给中断指定寄存器组,这样直接切换寄存器组而不必进行大量的 PUSH和POP操作,可以节省RAM空间,加速 MCU 执行时间。寄存器组的切换,总的来说比较容易出错,要对内存的使用情况有比较清晰的认识,其正确性要由你自己来保证。特别在程序中有直接地址访问的时候,一定要小心谨慎!至于“什么时候要用到寄存器组切换”,一种情况是:当你试图让两个(或以上)作业同时运行,而且它们的现场需要一些隔离的时候,就会用上了。在ISR或使用实时操作系统RTOS中,寄存器非常有用。

 

寄存器组使用的原则:

1、8051的最低32个字节分成4组8寄存器。分别为寄存器R0到R7。寄存器组由PSW 的RS1(PSW^4)、RS0(PSW^3)两位选择。在ISR中,MCU可以切换到一个不同的寄存器组。对寄存器的访问不可位寻址,C51编译器规定使用using或禁止中断的函数(#pragma disable)均不能返回bit类型的值。

2、主程序(main函数)使用一组,如 bank 0;低中断优先级的所有中断均使用第二组,如bank 1;高中断优先级的所有中断均使用另外一组,如 bank 2。显然,同级别的中断使用同一组寄存器不会有问题,因为不会发生中断嵌套;而高优先级的中断则要使用与低优先级中断不同的一组,因为有可能出现在低优先级中断中发生高优先级中断的情况。编译器会自动判断何时可使用绝对寄存器存取。

3、在ISR中调用其它函数,必须和中断使用相同的寄存器组。当没用NOAREGS命令做明确的声明,编译器将使用绝对寄存器寻址方式访问函数选定(即用using或REGISTERBANK指定)的寄存器组,当函数假定的和实际所选的寄存器组不同时,将产生不可预知的结果,从而可能出现参数传递错误,返回值可能会在错误的寄存器组中。

举一例子:当需要在中断内和中断外调用同一个函数,假定按照程序的流程控制,不会出现函数的递归调用现象,这样的调用会不会出现问题?若确定不会发生重入情况,则有以下两种情况:

1、如果ISR和主程序使用同一寄存器组(主程序缺省使用BANK 0,若ISR没有使用using为其指定寄存器区,则缺省也使用BANK 0),则不需其他设置。

2、如果 ISR 和主程序使用不同的寄存器组(主程序缺省使用BANK 0,ISR使用using指定了其他BANK),则被调用函数必须放在:#pragma NOAREGS和#pragma AREGS控制参数对中,指定编译器不要对该函数使用绝对寄存器寻址方式;或者也可在 Options->C51,选中“Don't use absolute register accesses”,使所有代码均不使用绝对寄存器寻址方式(这样,执行效率将稍有降低)。不论以上的哪一种情况,编译器均会给出重入警告,需手工更改OVERLAY参数,做重入说明。

3、还有一种办法:如果被调用函数的代码不是很长,还是将该函数复制一份,用不同的函数名代替,这种情况适合ROM有足够多余的空间。

因此,对using关键字的使用,如果没把握,宁可不用,交给编译系统自己去处理好了。

————————————————————————————————

以下是梦游的一些分析:

一、中断函数是一个特殊的函数,没有参数,也没有返回值;但是程序中允不允许使用return呢?答案是允许的,不过只能用"return;",不能用"return(z);";用在一些需要快速返回的地方,对应的汇编会有多个reti语句,相对效率会高一些。

二、using的用法。using可以修饰任何函数,不过个人建议只用来修饰中断函数;简单的说,“using”会指定工作寄存器组,由于中断函数一般都是比较紧急的事情,有时一条语句都会斤斤计较,所以使用using切换寄存器组可以省去一些压栈的动作,由于51只有两级中断,同级中断不能被打断,因此,我们可以同级中断设成同样的寄存器组,从某种意义上来说,有一组寄存器是多余的。同时个人建议中断函数应该使用using这个关键字。

三、中断中调用函数,首先要讨论中断函数中调用函数的必要性,前天在论坛上我和别人争论过这个问题,现在我还是这个观点:有些情况中断中调用函数还是必要的,这个时候是不是该调用函数,其实和普通函数差不多,首先是这个函数如果调用多次,或者要带一些参数什么的就更加必要的;前天有人跟我叫劲,说假如只调用一次且无参数无返回的函数要直接写,因为如果用函数,至少会增加CALL和RET两条语句,我不敢苟同,我是实际调试发现的,当你程序比较复杂时,你将那部单独拉出来做成函数,可能代码和时间都会更好。

四、中断中调用的函数最好不要被中断外的其它函数调用,因为会出现“重复调用”的警告,有时这种调用是很致命的,有人说这个函数可以用reentrant来修饰,是的,的确可以这样解决,不过个人不建议这么做,因为这样会减少很多堆栈空间,并且整个程序的优化要差很多,个人建议出现这种情况就把这个函数写两遍,分成两个函数分别调用。

五,中断调用了函数,会出现一些莫名其妙的问题,一些数据不对。其实一般是因为汇编中使用了绝对寄存器引起的,有人说中断函数使用哪个寄存器组,被中断调用的函数就使用哪个寄存器组,我认为这样不好:

这样会增加额外的消耗,使用using会增加一下语句:

   PUSH PSW

   MOV PSW, #XX

    ....

   POP PSW

更重要的是,使用using的函数不能有返回值,这是致命伤。

个人推荐的方法有两种:

1、使用“#pragma NOAREGS”禁止使用绝对寄存器

2、使用“#pragma RB(x)”来指定本文件的工作寄存器组

六、一般说来,要求中断函数尽可能的短,但也有特殊情况,有些前/后台的系统中,就会把很多相对重要的事情放到定时中断(这个定时中断类似实时操作系统中的时钟节拍)去做,而且程序很长。我单独提出来这点是想告诉大家,中断函数也是一个函数而已,只要系统有必要,可以做一些看似不合理的事情,该出手时就出手,就像goto语句一样。

关键字:interrupt  using  C51中断 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/2015/1013/article_22947.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:STARTUP.A51注解
下一篇:基于proteus的步进电机控制

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

STVD 编译代码提示stm8_interrupt_vector.o: section 0 expected (.text:0000)
今天编译前同事留下的代码,发现代码报错:#error clnk Debugcsc_code.lkf:93 Debugstm8_interrupt_vector.o: section 0 expected (.text:0000)  The command: "clnk -l"D:Program Files (x86)COSMICCXSTM8Lib" -l"D:Program Files (x86)COSMICCXSTM8Lib"  -o Debugcsc_code.sm8 -mDebugcsc_code.map Debugcsc_code.lkf
发表于 2020-04-18
STVD 编译代码提示stm8_interrupt_vector.o: section 0 expected (.text:0000)
msp430 timerA interrupt
; CCR2 = 25000 ;        _EINT();    while(1)  {    show(0,i);    delayms(10);    show(1,j);    delayms(10);  }    } #pragma vector = TIMERA1_VECTOR __interrupt void timer(void){     switch
发表于 2020-03-09
stm8s_STVD生成interrupt_vector.c问题
在使用STVD软件开发stm8sMCU时,打开STVD软件,系统自动生成中断向量文件vector.c。其中可以看到如下的代码参考stm8s mcu的官方文档PM0044 program manual,可以了解到mcu有32个4字节的中断向量入口,其中操作码为0x82,紧跟的是24位的地址。而上图中代码就是中断向量的入口地址并且NonHandledInterrupt刚好时@far类型(@far是代表3字节指针)。程序中我使用的是TIM4溢出中断,刚开始只是将/*irq23*/一行中的NonHandledInterrupt更换为TIM4中断函数,然后出现了error:invalid pointer ...。查阅后发现
发表于 2020-02-21
stm8s_STVD生成interrupt_vector.c问题
STM8的wait for interrupt
如果我用disable interrupt和enable interrupt包裹wait forinterrupt(WFI)。你说WFI还能被唤醒么?有思考过么?昨晚拿STM8L151K4的开发板,实际测试下来。这样的用法是完全没问题的。由此我猜测芯片内部,core应该会专门有一根信号线唤醒sleep状态的core,这根信号不受interrupt disable影响。这个实验对控制睡眠唤醒sleep/resume是很有用的,因为你会期望睡眠唤醒sleep/resume按照单线程处理,否则你将没有机会拦截resume过程,因为resume时,core会把等待已久的WFI指令运行完,然后就跳到了中断ISR去了,你是完全没机会拦截
发表于 2020-01-18
STM8的wait for interrupt
PIC 16f877a PORTB State Change Interrupt仿真与源码
PORT Change Interrupt PIC 16f877A单片机源程序如下:#define      LED_1     PORTd.b0#define      LED_2     PORTd.b1void interrupt(){if(INTCON.RBIF)  {  INTCON.RBIF = 0;  if(!PORTB.B4)   {    LED_1 = 0;    LED_2 = 1;   } 
发表于 2019-11-09
PIC 16f877a PORTB State Change Interrupt仿真与源码
Keil C51对C语言的关键词扩展之十四: interrupt
;  入口地址     00003h1000Bh20013h3001Bh40023h5002Bh60033h7003Bh80043h9004Bh100053h11005Bh120063h13006Bh140073h15007Bh 中断号    入口地址    160083h17008Bh180093h19009Bh2000A3h2100ABh2200B3h2300BBh2400C3h2500CBh2600D3h2700DBh2800E3h2900EBh3000F3h3100FBh函数声明时使用interrupt关键词,该函数将被编译器
发表于 2018-06-21
小广播
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2020 EEWORLD.com.cn, Inc. All rights reserved