c51中断函数如何返回值
c51中断函数如何返回值
c51中断函数如何返回值
c51中断函数如何返回值
匿名提问
2009-04-20 16:14:40 发布
电脑软件
2个回答
BiG299792458 | 2009-09-25 08:19:33
有0人认为这个回答不错 | 有0人认为这个回答没有帮助
主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。
也可以这样理解,重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),这样的函数就是purecode(纯代码)可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。
如果确实需要访问全局变量(包括static),一定要注意实施互斥手段。
可重入函数在并行运行环境中非常重要,但是一般要为访问全局变量付出一些性能代价。
编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护。
说明:若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。
示例:假设Exam是int型全局变量,函数Squre_Exam返回Exam 平方值。
那么如下函数不具有可重入性。
unsigned int example( int para ) {
unsigned int temp;
Exam = para; // (**)
temp = Square_Exam( );
return temp;
}
此函数若被多个进程调用的话,其结果可能是未知的,因为当(**)语句刚执行完后,另外一个使用本函数的进程可能正好被激活,那么当新激活的进程执行到此函数时,将使Exam赋与另一个不同的para值,所以当控制重新回到“temp = Square_Exam( )”后,计算出的temp很可能不是预想中的结果。
此函数应如下改进。
unsigned int example( int para ) {
unsigned int temp;
[申请信号量操作] //(1)
Exam = para;
temp = Square_Exam( );
[释放信号量操作]
return temp;
}
(1)若申请不到“信号量”,说明另外的进程正处于给Exam赋值并计算其平方过程中(即正在使用此信号),本进程必须等待其释放信号后,才可继续执行。
若申请到信号,则可继续执行,但其它进程必须等待本进程释放信号量后,才能再使用本信号。
保证函数的可重入性的方法:
在写函数时候尽量使用局部变量(例如寄存器、堆栈中的变量),对于要使用的全局变量要加以保护(如采取关中断、信号量等方法),这样构成的函数就一定是一个可重入的函数。
VxWorks中采取的可重入的技术有:
* 动态堆栈变量(各子函数有自己独立的堆栈空间)
* 受保护的全局变量和静态变量
* 任务变量
在实时系统的设计中,经常会出现多个任务调用同一个函数的情况。
如果这个函数不幸被设计成为不可重入的函数的话,那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据,从而导致不可预料的后果。
那么什么是可重入函数呢?所谓可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。
不可重入函数在实时系统设计中被视为不安全函数。
满足下列条件的函数多数是不可重入的:
1) 函数体内使用了静态的数据结构;
2) 函数体内调用了malloc()或者free()函数;
3) 函数体内调用了标准I/O函数。
下面举例加以说明。
A. 可重入函数
void strcpy(char *lpszDest, char *lpszSrc) {
while(*lpszDest++=*lpszSrc++);
*dest=0;
}
B. 不可重入函数1
charcTemp;//全局变量
void SwapChar1(char *lpcX, char *lpcY) {
cTemp=*lpcX;
*lpcX=*lpcY;
lpcY=cTemp;//访问了全局变量
}
C. 不可重入函数2
void SwapChar2(char *lpcX,char *lpcY) {
static char cTemp;//静态局部变量
cTemp=*lpcX;
*lpcX=*lpcY;
lpcY=cTemp;//使用了静态局部变量
}
问题1,如何编写可重入的函数?
答:在函数体内不访问那些全局变量,不使用静态局部变量,坚持只使用局部变量,写出的函数就将是可重入的。
如果必须访问全局变量,记住利用互斥信号量来保护全局变量。
问题2,如何将一个不可重入的函数改写成可重入的函数?
答:把一个不可重入函数变成可重入的唯一方法是用可重入规则来重写它。
其实很简单,只要遵守了几条很容易理解的规则,那么写出来的函数就是可重入的。
1) 不要使用全局变量。
因为别的代码很可能覆盖这些变量值。
2) 在和硬件发生交互的时候,切记执行类似disinterrupt()之类的操作,就是关闭硬件中断。
完成交互记得打开中断,在有些系列上,这叫做“进入/退出核心”。
3) 不能调用其它任何不可重入的函数。
4) 谨慎使用堆栈。
最好先在使用前先OS_ENTER_KERNAL。
堆栈操作涉及内存分配,稍不留神就会造成益出导致覆盖其他任务的数据,所以,请谨慎使用堆栈!最好别用!很多黑客程序就利用了这一点以便系统执行非法代码从而轻松获得系统控制权。
还有一些规则,总之,时刻记住一句话:保证中断是安全的!
实例问题:曾经设计过如下一个函数,在代码检视的时候被提醒有bug,因为这个函数是不可重入的,为什么?
unsigned int sum_int( unsigned int base ) {
unsigned int index;
static unsigned int sum = 0; // 注意,是static类型
for (index = 1; index <= base; index++)
sum += index;
return sum;
}
分析:所谓的函数是可重入的(也可以说是可预测的),即只要输入数据相同就应产生相同的输出。
这个函数之所以是不可预测的,就是因为函数中使用了static变量,因为static变量的特征,这样的函数被称为:带“内部存储器”功能的的函数。
因此如果需要一个可重入的函数,一定要避免函数中使用static变量,这种函数中的
static变量,使用原则是,能不用尽量不用。
将上面的函数修改为可重入的函数,只要将声明sum变量中的static关键字去掉,变量sum即变为一个auto类型的变量,函数即变为一个可重入的函数。
当然,有些时候,在函数中是必须要使用static变量的,比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。
keil中断返回到主函数的特定位置
keil中断返回到主函数的特定位置在嵌入式系统开发中,中断是一种重要的机制,用于处理实时性要求较高的任务。
Keil是一款常用的嵌入式开发工具,它提供了丰富的功能和库,方便开发人员进行嵌入式软件开发。
在Keil中,中断的处理是通过中断服务函数来完成的。
当中断发生时,程序会跳转到相应的中断服务函数中执行,然后再返回到主函数继续执行。
然而,在某些情况下,我们可能希望中断处理完毕后能够返回到主函数的特定位置,而不是从中断服务函数的末尾返回。
这种需求在一些特定的应用场景中非常常见,比如在处理完中断后需要继续执行某个特定的任务。
为了实现这个目标,我们可以借助Keil提供的一些特性和技巧。
首先,我们需要在主函数中定义一个全局变量,用于标记中断处理完毕后需要跳转到的位置。
这个全局变量可以是一个枚举类型,用于表示不同的跳转位置。
然后,在中断服务函数中,我们可以根据具体的需求设置这个全局变量的值。
接下来,我们需要在主函数中添加一个循环,用于检测这个全局变量的值。
当检测到全局变量的值与我们预期的跳转位置相同时,我们可以跳出循环,继续执行后续的任务。
为了避免循环过于频繁地检测全局变量的值,我们可以在循环中添加一些延时操作,以降低CPU的占用率。
下面是一个示例代码,演示了如何在Keil中实现中断返回到主函数的特定位置:```c#include <reg51.h>// 定义全局变量,用于标记跳转位置enum JumpPosition {POSITION_A,POSITION_B,POSITION_C};enum JumpPosition jumpPosition = POSITION_A; // 中断服务函数void interruptServiceRoutine() interrupt 0 {// 中断处理代码...// 设置跳转位置jumpPosition = POSITION_B;}void main() {// 初始化...// 启用中断EA = 1;// 主循环while (1) {// 检测跳转位置if (jumpPosition == POSITION_B) {// 跳转到特定位置// ...// 清除跳转位置标记jumpPosition = POSITION_A;}// 延时操作,降低CPU占用率// ...}}```在上面的示例代码中,中断服务函数中设置了跳转位置为POSITION_B。
MCS-51单片机中断函数注意事项
学习单片机的快速方法先介绍下单片机。
把单片机说成是电子信息类专业最重要的一门课程一点也不为过,你在街上看到的广告彩灯、烟花表演、红绿灯、自动开关门这些都可以用单片机实现。
有了单片机,你就可以写程序进单片机,控制单片机管脚输出的高低电平,从而你可以控制其他模块。
这就实现了自动控制。
我需要怎样的控制,我就写怎样的程序就可以了。
接下来介绍单片机的学习流程。
有的人学了两年才学会。
有的人一个月就学的很不错了。
告诉大家我学51单片机只用了两个星期。
普通的应用差不多都会了,可以自己设计一个国旗升降系统、温度自动控制系统之类的电路和程序。
当然想要这么短的时间内精通是不可能的。
学习单片机是有捷径可走的。
首先你得知道你要学习什么样的单片机,我建议从80C51核心学起,80C51是MCS-51系列中的一个典型品种;其它厂商以8051为基核开发出的CMOS工艺单片机产品统称为80C51系列。
当前常用的80C51系列单片机主要产品有:Intel 的80C31、80C51、87C51,80C32、80C52、87C52 ATMEL的89C51、89C52、89C2051 Philips、Dallas、华邦、STC Siemens等公司的产品。
之所以要从80C51学起,是因为目前很多公司都是用51核心的单片机。
而且51的资料很多,懂的人也比较多。
其实你学好了一种单片机,再学习其他的单片机会很轻松。
我就从C51说起,首先得准备一本单片机教材,只要是C51核心的都可以。
打开书看单片机的管脚各有什么功能,内部都有什么东西,用来干嘛。
这本书你当成小说来看个一天就可以了。
不要求你全部看懂。
知道都有些什么东西就行。
因为从我的学习过程来看,光看理论等于没学。
单片机必须是理论加实践,而且要大量实践,在实践的过程中寻找理论。
这才是最快捷的学习方法。
这好比你学习汉语,你不可能什么字都会写,遇到不懂的,查查字典就可以了。
1 / 2然后我们谈实践,花100来块钱买块单片机学习板,有做板经验的也可以自己做一块,原理图网上多得是。
c51 return用法
c51 return用法C51 Return用法是指使用C51系列单片机中的Return语句。
Return语句在程序中起到跳转到函数调用点的作用,同时将函数的返回值传递给调用函数。
在C51编程中,我们经常需要在函数中使用Return语句。
Return语句可以用于函数的正常返回和异常返回。
首先,我们来看一下Return语句的正常返回用法。
当一个函数执行完了所有的语句,或者遇到了Return语句时,函数将被终止并返回到调用函数的位置。
在函数定义时,我们可以使用函数类型来指定Return语句返回的数据类型,如果函数没有返回值,则可以使用void关键字作为返回类型。
示例代码如下:```cint add(int a, int b) {int sum = a + b;return sum;}void main() {int result = 0;result = add(5, 3);// 此处result将会被赋值为8}```在上面的示例中,我们定义了一个add函数,它接受两个整数参数并返回它们的和。
在主函数中,我们调用add函数,并将返回值赋给result变量。
下面,我们再来看一下Return语句的异常返回用法。
在函数执行过程中,如果遇到特定的条件或错误,我们可以使用Return语句提前结束函数执行,并返回一个特定的错误码或标志。
示例代码如下:```cint divide(int a, int b) {if (b == 0) {return -1; // 返回错误标志}return a / b; // 返回正常结果}void main() {int dividend = 10;int divisor = 0;int result = divide(dividend, divisor);if (result == -1) {// 执行错误处理代码}}```在上面的示例中,我们定义了一个divide函数,它接受两个整数参数a和b,并计算它们的商。
中断处理函数参数传递和返回值
中断处理函数参数传递和返回值首先,中断处理函数通常有几个标准的参数:1.中断号/向量号:中断号用于唯一标识不同的中断类型,系统会将中断号作为参数传递给中断处理函数,以便函数可以区分不同的中断类型,并采取相应的处理措施。
2.寄存器的值:中断处理函数通常需要保存和恢复寄存器的值以确保中断过程对程序的影响最小化。
这些寄存器的值可能会传递给中断处理函数作为参数,以便函数在处理中断时能够获取之前的状态信息。
3.上下文信息:中断处理函数还可能需要访问一些上下文信息,例如正在执行的进程的上下文或特定任务的上下文。
这些上下文信息可能会作为参数传递给中断处理函数,以便函数可以在中断处理期间使用它们。
此外,中断处理函数还可以接收其他特定于中断类型的参数,这取决于实际应用中的需求。
在处理中断时,中断处理函数可能会进行一些操作,并根据具体需求返回一些结果。
以下是几种可能的返回值:1.状态码:中断处理函数可以返回一个状态码,用于指示中断处理的结果。
例如,返回一个成功或失败的状态码,以便其他代码可以根据返回值采取相应的措施。
2.错误信息:中断处理函数可能需要返回一些错误信息,以便其他代码可以根据错误信息来进行相应的处理。
例如,当中断处理函数遇到无法处理的错误时,可以返回一个错误信息,以便其他代码能够采取适当的措施。
3.数据结果:有些中断处理函数可能需要返回一些计算结果,例如在中断处理过程中对数据进行处理或计算得到其中一种结果。
这些结果可以返回给调用者,在需要时可以使用。
需要注意的是,中断处理函数通常被认为是一段短小精悍的代码,应尽可能快速地执行完毕,以避免影响正常的程序运行。
因此,在设计中断处理函数时,应考虑到处理时间的限制,并尽量避免过多的参数传递和复杂的结果返回。
总之,中断处理函数是处理硬件中断或软件中断时自动执行的代码。
它需要接收一些参数来完成相应的处理,并且可能会返回一些结果。
通过合理设计中断处理函数的参数传递和结果返回,可以有效地完成中断处理的任务。
中断函数
中断函数类型void,说明中断函数是不能有返回值的,这点要明确,所以不要企图让中断函数给你个返回值。还有,函数名后面括弧内是没有参数的。。。这点也需要注意,中断函数不能传递参数。interrupt 后面的点是中断号。重点我给你解释下using。这是C51编译器扩展的关键字。用来选择80c51单片机中不同的寄存器组,你也知道R0-R7总共有4组,那么using 后面的数字也就是0-3范围里的一个数了。如果不需要using的话,编译器会自动选择一组寄存器作为绝对寄存器访问的。using对代码是有一定的影响的。给你说个简单的例子,你可以想下,如果定义 void time0() interrupt 1 using 1假设,我们又在中断里调用了一个delay();这个delay是起延时作用。那么,我们就需要确保我们使用的寄存器组是同一组,否则,就会出现混乱。
void INT0()interrupt 0 using 1
{....
.....
}
interrupt 0 指明是外部中断0;
interrupt 1 指明是定时器中断0;
interrupt 2 指明是外部中断1;
interrupt 3 指明是定时器中断1;
interrupt 4 指明是串行口中断;
using 3时设置 RS1=1,RS0 =1,用第3组寄存器,R0--R7的在数据存储区里的实际地址是18H-1FH。R0(18H)....R7(1Fቤተ መጻሕፍቲ ባይዱ)
using 1时设置 RS1=0,RS0 =1,用第1组寄存器,R0--R7的在数据存储区里的实际地址是08H-0FH。R0(08H)....R7(0FH)
中断处理函数参数传递和返回值
中断处理函数参数传递和返回值
中断处理函数参数的传递和返回值的使用通常有以下几种方式:
1.使用全局变量:
中断处理函数可以访问全局变量,因此可以使用全局变量作为参数的
传递方式。
同时,可以在中断处理函数中修改全局变量的值,作为返回值
的使用。
2.使用指针:
可以使用指针作为参数的传递方式,将需要传递的参数作为指针参数
传递给中断处理函数。
同时,中断处理函数可以通过修改指针指向的内存
地址,将结果返回给调用者。
3.使用寄存器:
有些处理器架构允许使用寄存器作为参数的传递方式。
此时,需要根
据处理器的规范,将参数存储在指定的寄存器中,在中断处理函数中使用
该寄存器中存储的值。
4.使用函数参数和返回值:
有些处理器架构支持使用函数参数和返回值传递参数和返回值。
此时,需要按照该处理器的规范,将参数和返回值存储在指定的寄存器或栈中。
需要注意的是,在中断处理函数中使用传递的参数时,需要确保参数
的正确性和有效性,特别是在多次调用中断处理函数时,需要保证参数的
一致性和可靠性。
C51的中断函数
C51的中断函数C51的中断函数的格式为:void FuncIr(void) interrupt x [using y]以下是一些分析:一、中断函数是一个特殊的函数,没有参数,也没有返回值;但是程序中允不允许使用return呢?答案是允许的,不过只能用"return;",不能用"return(z);";用在一些需要快速返回的地方,对应的汇编会有多个ret语句,相对效率会高一些。
二、using的用法,using可以修饰任何函数,不过个人建议只用来修饰中断函数;简单的说,“using”会指定工作寄存器组,由于中断函数一般都是比较紧急的事情,有时一条语句都会斤斤计较,所以使用using切换寄存器组可以省去一些压栈的动作,由于51只有两级中断,同级中断不能被打断,因此,我们可以同级中断设成同样的寄存器组,从某种意义上来说,有一组寄存器是多余的。
同时个人建议中断函数应该使用using这个关键字。
三、中断中调用函数,首先要讨论中断函数中调用函数的必要性,前天在论坛上我和别人争论过这个问题,现在我还是这个观点:有些情况中断中调用函数还是必要的,这个时候是不是该调用函数,其实和普通函数差不多,首先是这个函数如果调用多次,或者要带一些参数什么的就更加必要的;前天有人跟我叫劲,说假如只调用一次且无参数无返回的函数要直接写,因为如果用函数,至少会增加CALL和RET两条语句,我不敢苟同,我是实际调试发现的,当你程序比较复杂时,你将那部分单独拉出来做成函数,可能代码和时间都会更好。
四、中断中调用的函数最好不要被中断外的其它函数调用,因为会出现“重复调用”的警告,有时这种调用是很致命的,有人说这个函数可以用reentrant来修饰,是的,的确可以这样解决,不过个人不建议这么做,也许这样会跟你减少很多堆栈空间,并且整个程序的优化要差很多,个人建议出现这种情况就把这个函数写两遍,分成两个函数分别调用。
c51中断函数的介绍
c51中断函数的介绍C51编译器允许用c51创建中断服务程序,大家仅仅需要关心中断号和寄存器组的选择就可以了。
编译器自动产生中断向量和程序的入栈及出栈代码。
在函数声明时包括interrupt,将把所声明的函数定义为一个中断服务程序。
另外,可以用using定义此中断服务程序所使用的寄存器组。
一、中断函数的定义1、中断函数定义的格式为:函数类型函数名interrupt n using n其中:Interrupt后面的n是中断号。
关键字using后面的n是所选择的寄存器组,取值范围是0-3.定义中断函数时,using是一个选项,可以省略不用。
如果不用则由编译器选择一个寄存器组作为绝对寄存器组。
2、8051的中断过程通过使用interrupt关键字和中断号来实现,中断号告诉编译器中断程序的入口地址。
中断号对应着IE寄存器中的使能位,换句话说,IE 寄存器中的0位对应着外部中断0,相应的外部中断0的中断号是0.IE寄存器中的使能位与外部中断对应关系:中断号中断源0 外部中断01 定时器02 外部中断13 定时器1中断4 串行口中断5 定时器2中断二、使用中断函数时要注意的问题:1. 在设计中断时,要注意的是哪些功能应该放在中断程序中,哪些功能应该放在主程序中。
一般来说中断服务程序应该做最少量的工作,这样做有很多好处。
首先系统对中断的反应面更宽了,有些系统如果丢失中断或对中断反应太慢将产生十分严重的后果,这时有充足的时间等待中断是十分重要的。
其次它可使中断服务程序的结构简单,不容易出错。
中断程序中放入的东西越多,他们之间越容易起冲突。
简化中断服务程序意味着软件中将有更多的代码段,但可把这些都放入主程序中。
中断服务程序的设计对系统的成败有至关重要的作用,要仔细考虑各中断之间的关系和每个中断执行的时间,特别要注意那些对同一个数据进行操作的ISR.2. 中断函数不能传递参数。
3. 中断函数没有返回值。
4. 中断函数调用其他函数,则要保证使用相同的寄存器组,否则出错。
单片机中断处理过程:中断响应 中断处理 中断返回详解
单片机中断处理过程:中断响应中断处理中断返回详
解
中断处理过程可分为中断响应、中断处理和中断返回三个阶段。
中断响应
中断响应是CPU 对中断源中断请求的响应,包括保护断点和将程序转向中断服务程序的入口地址(通常称矢量地址)。
中断响应过程
中断响应过程包括保护断点和将程序转向中断服务程序的入口地址。
首先,中断系统通过硬件自动生成长调用指令(LACLL),该指令将自动把断点地址压入堆栈保护(不保护累加器A、状态寄存器PSW 和其它寄存器的内容),然后,将对应的中断入口地址装入程序计数器PC(由硬件自动执行),使程序转向该中断入口地址,执行中断服务程序。
MCS-51 系列单片机各中断源的入口地址由硬件事先设定,分配如下:
中断源入口地址
外部中断00003H
定时器T0 中断000BH
外部中断10013H
定时器T1 中断001BH
串行口中断0023H
使用时,通常在这些中断入口地址处存放一条绝对跳转指令,使程序跳转到用户安排的中断服务程序的起始地址上去。
中断返回
中断返回是指中断服务完后,计算机返回原来断开的位置(即断点),继续执。
c51语言处理单片机的中断是由专门的中断函数来处理的
c51语言处理单片机的中断是由专门的中断函数来处理的C51语言处理单片机的中断是由专门的中断函数来处理的。
中断是一种在程序执行过程中被外部事件触发的事件,它可以打断程序的正常执行,使得程序能够及时响应外部设备的请求。
C51语言中,中断函数是由程序员自定义的函数,用来处理中断事件。
当中断事件发生时,单片机会自动跳转到对应的中断函数进行处理。
中断函数通常包括以下几个部分:1.中断向量表:中断向量表是存储中断向量地址的表格。
每个中断向量对应一个中断类型,当中断事件发生时,单片机会根据中断类型找到对应的中断向量并跳转到相应的中断函数。
在C51语言中,中断向量表是通过设置中断向量地址的方式来定义的。
2.中断优先级:C51语言支持多级中断优先级,用来确定一些中断是否可以打断另一个中断的执行。
中断优先级可以通过设置特定的寄存器来实现,具体优先级的设置需要根据实际应用场景来确定。
3.中断服务程序:中断服务程序是中断函数的核心部分,用来处理中断事件。
在中断服务程序中,通常会进行以下几个步骤:a.保存现场:在进入中断服务程序之前,需要保存当前程序的执行状态,包括各个寄存器的值、堆栈指针等。
这样可以保证在中断处理完成后,程序能够正确地返回到中断发生前的执行状态。
b.清除中断标志:在进入中断服务程序之前,需要将中断标志位清除,以防止重复触发中断。
中断标志位通常是由硬件自动设置的,在中断服务程序中需要手动清除。
c.执行中断处理逻辑:在中断服务程序中,可以编写相应的逻辑代码来处理中断事件。
这可以包括读取外部设备的数据、进行数据处理、发送数据等。
中断服务程序中的代码需要尽量简洁高效,以确保及时响应外部事件。
d.恢复现场:在中断服务程序执行完成后,需要恢复之前保存的执行状态,包括恢复各个寄存器的值、堆栈指针等。
这样可以保证程序能够正确地返回到中断发生前的执行状态。
4.中断返回指令:在中断服务程序执行完成后,需要使用特定的指令来返回到主程序的执行位置。
