单片机c语言using,C语言在8051单片机上的扩展(interrupt、using关键字的用法)

C语言在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来将很重要。所以,这些函数希望按照一定的顺序执行而不被中断。

timer0_int() 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-critical routines. Note that interrupts of the same priority can share a register bank, since there is no risk that they will interrupt each other.

'using'告诉编译器在进入中断处理器去切换寄存器的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 used on entry to sys_interp fails. This is because C51 has not yet been told that the registerbank has been changed. If no working registers are used 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 passed to it or returned. When the interrupt occurs, compiler-inserted 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 "}" causes a RETI to be used 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

中断服务例程

timer0_int() interrupt 1 USING 1 {

unsigned char temp1 ;

unsigned char temp2 ;

}

被调用的函数

#pragma SAVE // Rember current registerbank

#pragma REGISTERBANK(1)    // Tel C51 base 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都是C51的关键字。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,在使用中需灵活取舍。

(2008-06-26 14:12:36)

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用单片机C语言编写使蜂鸣器间隔发出声音的代码,我们可以利用定时器和IO口控制蜂鸣器的开关。 首先,我们需要声明引脚和定时器的相关参数,例如预定义蜂鸣器的IO口和定时器的频率。 然后,在主函数中,我们需要设置相关的引脚为输出模式,并初始化定时器。 接下来,我们可以设计一个循环,在循环中控制蜂鸣器的开关,以实现间隔发出声音的效果。 在循环中,我们可以利用定时器的中断功能来产生周期性的中断,在中断服务函数中可以控制蜂鸣器的开关状态。 具体的代码如下所示: ```c #include <reg52.h> sbit Buzzer = P1^0; //蜂鸣器控制引脚 void timer_init(); //定时器初始化函数 void main() { timer_init(); //初始化定时器 Buzzer = 0; //初始状态关闭蜂鸣器 while(1) { Buzzer = ~Buzzer; //切换蜂鸣器状态 Delay_ms(500); //延时500ms } } void timer_init() { TMOD = 0x01; //设置定时器0为工作模式1(16位自动重装定时器) TH0 = 0xFF; //设置定时器的初始值(定时1ms) TL0 = 0xFE; ET0 = 1; //使能定时器0中断 EA = 1; //打开总中断 TR0 = 1; //启动定时器0 } void timer0_interrupt() interrupt 1 using 1 { TH0 = 0xFF; //重装定时器的初始值(定时1ms) TL0 = 0xFE; Buzzer = ~Buzzer; //切换蜂鸣器状态 } ``` 以上代码中,定时器中断服务函数用于周期性地改变蜂鸣器的状态,每当定时器中断一次,蜂鸣器的状态就会切换一次,从而发出间隔的声音。 这段代码只是实现了蜂鸣器间隔发出声音的一个简单示例,具体的间隔时间可以根据需求进行调整。同时,还可以根据具体的单片机型号和蜂鸣器接口来进行相应的引脚声明和定时器配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值