如何利用keilC实现单片机中断功能(interrupt,using关键字的用法)

更新时间:2023-05-12 13:15:34 阅读: 评论:0

如何利⽤keilC实现单⽚机中断功能(interrupt,using关键字的⽤法)
在8051单⽚机上的扩展(interrupt、using关键字的⽤法)
直接访问寄存器和端⼝
定义
sfr P0 0x80
sfr P1 0x81
sfr ADCON; 0xDE
sbit EA  0x9F
操作
ADCON = 0x08;
P1 = 0xFF;
io_status = P0 ;
EA = 1;
在使⽤了interrupt 1 关键字之后,会⾃动⽣成中断向量
在 ISR中不能 与其他 “后台循环代码”(the background loop code) 共享局部变量,因为连接器会复⽤ 在RAM中这些变量的位置,所以它们会有不同的意义,这取决于当前使⽤的不同的函数复⽤变量对RAM有限的51来讲很重要。所以,这些函数希望按照⼀定的顺序执⾏ ⽽不被中断。
void timer0_int(void) interrupt 1 using 2
{
unsigned char temp1;
unsigned char temp2;
executable C statements;
}
“interrupt”声明表向量⽣成在(8*n+3),这⾥,n就是interrupt参数后的那个数字这⾥,在08H的代码区域 ⽣成LJMP timer0_int 这样⼀条指令。
“using” tells the compiler to switch register banks on entry to an interrupt routine. This “context” switch is the fastest way of
providing a fresh registerbank for an interrupt routine’s local data and is to be preferred to stacking registers for very time-criticalroutines. Note that interrupts of the same priority can share a register bank, since there is no risk that they will interrupt each other.
“using” 告诉编译器 在进⼊51danpi中断处理器 去切换寄存器的bank。这个”contet”切换是为中断处理程序的局部变量提供⼀个新鲜的寄存器bank最快的⽅式。对时序要求严格的程序,是⾸选的stack寄存器(保存寄存器到stack)⽅式。
注意:同样优先级别的中断可以共享寄存器bank,因为他们每次将中断没有危险。
If a USING 1 is added to the timer1 interrupt function prototype, the pushing of registers is replaced by a simple MOV to PSW to switch registerbanks. Unfortunately, while the interrupt entry is speeded up, the direct register addressing ud on entry to sys_interp fails. This is becau C51 has not yet b
een told that the registerbank has been changed. If no working registers are ud and no other function is called, the optimizer eliminiates teh code to switch register banks.
如果在timer1的中断函数原型中使⽤using 1, 寄存器的pushing将被MOV to PSW切换寄存器bank 所替换。
不幸的是,当⼀个中断⼊⼝被加速时。⽤在⼊⼝的 直接寄存器寻址将失败。这是因为 C51没有告诉 寄存器bank已经改变。如果不⼯作的寄存器将被使⽤,如果没有其他函数被调⽤,优化器…..
Logically, with an interrupt routine, parameters cannot be pasd to it or returned. When the interrupt occurs,
compiler-inrted code is run which pushes the accumulator, B,DPTR and the PSW (program status word) onto the stack. Finally, on exiting the interrupt routine, the items previously stored on the stack are restored and the closing “}” caus a RETI to be ud rather than a normal RET.
逻辑上,⼀个中断服务程序,不能传递参数进去,也不可返回值。当中断发⽣时,编译器插⼊的代码被运⾏,它将累加器,B,DPTR 和PSW(程序状态字)⼊栈。最后,在退出中断程序时,预先存储在栈中 被恢复。最后的”}”结束符号将插⼊RETI到中断程序的最后,为了⽤ Keil C语⾔创建⼀个中断服
务程序(ISR),利⽤ interrupt 关键词和正确的中断号声明⼀个static void函数。Keil C编译器⾃动⽣成中断向量,以及中断程序的进⼝、出⼝代码。Interrupt 函数属性标志着该函数为ISR。可⽤using属性指定ISR使⽤哪⼀个寄存器区,这是可选的。有效的寄存器区范围为1到3。
中断源的⽮量位置
中断源              Keil中断编号          ⽮量地址
最⾼优先级                6                    0x0033
外部中断0                0                    0x0003
定时器0溢出              1                    0x000B
外部中断1                2                    0x0013
定时器1溢出              3                    0x001B
串⼝                          4                    0x0023
定时器2溢出              5                    0x002B
DMA                          7                    0x003B
硬件断点                    8                    0x0043
JTAG                        9                    0x004B
软件断点                  10                    0x0053
监视定时器              12                    0x0063
1.函数在调⽤前定义与在调⽤后定义产⽣的代码是有很⼤差别的(特别是在优化级别⼤于3级时)。(本⼈也不太清楚为什么,⼤概因为在调⽤前定义则调⽤函数已经知道被调⽤函数对寄存器的使⽤情况,则可对函数本⾝进⾏优化;⽽在调⽤后进⾏定义则函数不知被调⽤函数对寄存器的使⽤情况,它默认被调⽤函数对寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、, R6、 R7)都已经改变,因此不在这些寄存器中存⼊有效的数据)
2.函数调⽤函数时除在堆栈中存⼊返回地址之外,不在堆栈中保存其它任何寄存器。(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、
R2、 R3、R 4、 R5、, R6、 R7)的内容。(除⾮被调⽤函数使⽤了using特性)
3.中断函数是⼀个例外,它会计算⾃⾝及它所调⽤的函数对寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、
R5、, R6、 R7)的改变,并保存相应它认为被改变了的寄存器。
4.使⽤C写程序时,尽量少使⽤using n (n=0,1,2,3)特性。(这个特性在本⼈使⽤的过程中存在⼀些问题,不知算不算是⼀个⼩bug)
默认keil c51中的函数使⽤的是0寄存器组,当中断函数使⽤using n时,n = 1,2,3或许是对的,但n=0时,程序就已经存在了
bug(只有中断函数及其所调⽤的函数并没有改变R0 —- R7的值时,这个bug不会表现出来)
⼀个结论是,在中断函数中如果使⽤了using n,则中断不再保存R0—-R7的值。由此可以推论出,⼀个⾼优先级的中断函数及⼀个低优先级的中断函数同时使⽤了using n,(n = 0,1,2,3)当n相同时,这个存在的bug 是多么的隐蔽。(这恰是使⼈想象不到的)
使⽤不同寄存器组的函数(特殊情况外)不能相互调⽤”using”关键字告诉编译器切换register bank
如果中断程序不重要,using关键字能忽略。如果⼀个函数被从中断程序调⽤,⽽此中断强制使⽤using,当编译⼀个被调⽤的函数时,编译器必须告诉它
1)在函数前 必须⽤伪指令
#pragma NOAREGS
在进⼊函数
#pragma RESTORE
或者
#pragmas AREGS
这样就不会使⽤ “绝对地址定位”
2)#pragma REGISTERBANK(n)
⽤这个指定告诉当前使⽤的bank
⽤NOAREGS指令 移除 MOV R7,AR7
中断服务例程
void timer0_int(void) interrupt 1 using 1
{
unsigned char temp1 ;
unsigned char temp2 ;
}
被调⽤的函数
#pragma SAVE // Rember current registerbank
#pragma REGISTERBANK(1)    // Tel C51 ba address of current registerbank.
void func(char x)
{
// Called from interrupt routine
/
/ with “using1”
}
#pragma RESTORE // Put back to original registerbank
如果中断服务例程使⽤了using,被中断服务例程 调⽤的函数⼀定要REGISTERBANK(n),⼀个被ISR调⽤的函数也可能被后台程序调⽤为了函数”reentrant(可重⼊8051 系列MCU的基本结构包括:32 个I/O ⼝(4 组8 bit 端⼝);两个16 位定时计数器;全双⼯串⾏通信;6 个中断源(2 个外部中断、2 个定时/计数器中断、1 个串⼝输⼊/输出中断),两级中断优先级;128 字节内置RAM;独⽴的64K 字节可寻址数据和代码区。中断发⽣后,MCU 转到5个中断⼊⼝处之⼀,然后执⾏相应的中断服务处理程序。中断程序的⼊⼝地址被编译器放在中断向量中,中断向量位于程序代码段的最低地址处,注意这⾥的串⼝输⼊/输出中断共⽤⼀个中断向量。8051的中断向量表如下:
中断源 中断向量
—————————
上电复位          0000H
外部中断0        0003H
定时器0 溢出  000BH
外部中断1        0013H
定时器1 溢出  001BH
串⾏⼝中断      0023H
定时器2 溢出    002BH
interrupt 和 using 都是C5 的关键字。C51中断过程通过使⽤interrupt关键字和中断号(0 到 31)来实现。中断号指明编译器中断程序的⼊⼝地址中断序号对应着8051中断使能寄存器IE 中的使能位,对应关系如下:
IE寄存器 C51中的 8051的
的使能位 中断号 中断源
——————————–
IE.0 0 外部中断0
IE.1 1 定时器0 溢出
IE.2 2 外部中断1
IE.3 3 定时器1 溢出
IE.4 4 串⼝中断
IE.5 5 定时器2 溢出
有了这⼀声明,编译器不需理会寄存器组参数的使⽤和对累加器A、状态寄存器、寄存器B、数据指针和默认的寄存器的保护。只要在中断程序中⽤到,编译器会把它们压栈,在中断程序结束时将他们出栈。C51 ⽀持所有 5 个 8051 标准中断从 0 到 4 和在 8051 系列(增强型)中多达 27 个中断源。
using 关键字⽤来指定中断服务程序使⽤的寄存器组。⽤法是:using 后跟⼀个0 到3 的数,对应着 4 组⼯作寄存器。⼀旦指定⼯作寄存器组,默认的⼯作寄存器组就不会被压栈,这将节省 32 个处理周期,因为⼊栈和出栈都需要 2 个处理周期。这⼀做法的缺点是所有调⽤中断的过程都必须使⽤指定的同⼀个寄存器组,否则参数传递会发⽣错误。因此对于using,在使⽤中需灵活取舍。

本文发布于:2023-05-12 13:15:34,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/599839.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:中断   寄存器   函数   程序   编译器
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图