关于中断服务函数里面是否可以调用其他函数的问题
- 格式:docx
- 大小:16.84 KB
- 文档页数:3
中断服务函数嵌入式-回复什么是中断服务函数?在嵌入式系统中,中断是指在CPU执行程序的过程中,遇到某个特定的事件或条件,会打断正常的程序执行流程,转而去执行一个特定的处理函数。
而中断服务函数(Interrupt Service Routine,ISR)就是这个特定的处理函数。
中断服务函数是一段预编写的代码,在系统接收到中断信号时执行。
它的作用是快速响应中断,从而在时间上能够及时处理特定事件。
中断服务函数通常用来保存和恢复CPU的寄存器状态,并进行特定的处理逻辑,例如读取输入设备的数据、写入输出数据、更新系统状态等。
中断服务函数的执行方式是通过向CPU发送中断请求,然后由CPU 根据中断请求的类型选择相应的中断服务函数来执行。
每个中断请求都有一个对应的中断服务函数,通过这种方式,系统能够优先处理紧急或重要的事件,提高系统的实时性能。
中断服务函数的编写需要根据具体的硬件平台和中断源的特点而定。
一般来说,编写一个中断服务函数需要经历以下步骤:1. 确定中断源:首先要确定引发中断的具体事件或条件。
这可以是外部硬件设备触发的中断信号,如按键、定时器溢出等;也可以是内部软件事件触发的中断信号,如软件定时器超时、异常事件等。
2. 注册中断服务函数:将中断服务函数注册到相应的中断控制器或向量表中,以便在产生中断时能够被调用。
不同的嵌入式系统平台可能有不同的注册方法和接口,需要根据具体平台的文档进行操作。
3. 实现中断服务函数:编写中断服务函数的具体代码。
中断服务函数通常需要保存和恢复CPU的寄存器状态,这是因为在进入中断服务函数之前,CPU需要将当前的执行状态保存下来,以便在执行完中断服务函数后能够继续执行中断之前的程序。
此外,中断服务函数还需要根据具体需求进行特定的处理逻辑,比如读取输入设备的数据、更新系统状态等。
4. 中断优先级管理:在有多个中断源的情况下,需要对中断信号进行优先级排序和管理。
通过设置中断的优先级,可以确保高优先级的中断优先得到处理,避免低优先级的中断长时间得不到响应。
文章标题:深度解析:51单片机汇编中断程序调用子程序一、介绍在51单片机的汇编编程中,中断程序和子程序的调用是非常重要的内容。
本文将深入讨论51单片机汇编中断程序如何调用子程序的相关知识,帮助读者更加深入地理解这一主题。
二、51单片机汇编中断程序调用子程序的基本原理在51单片机中,中断是指在程序运行过程中,由硬件或者软件主动触发的一种事件,当中断发生时,CPU会立即暂停正在执行的程序,转而去执行与该中断相关的处理程序,当处理完毕后再返回原程序继续执行。
子程序则是一段独立的代码,可以被主程序或其他子程序调用执行。
中断程序调用子程序的基本原理是,当中断发生时,CPU会跳转到中断服务程序进行处理,在中断服务程序中可以调用需要的子程序进行处理,处理完毕后再返回中断服务程序,最终返回到原来的程序中继续执行。
三、中断程序调用子程序的具体实现方法1. 中断程序的编写首先需要编写中断程序,并向51单片机的中断向量表中注册相应的中断号。
在中断程序中,可以调用需要的子程序进行处理。
2. 子程序的编写编写需要被调用的子程序,并保证其能够正确地处理需要的任务。
子程序的调用和返回是通过特定的指令来实现的。
3. 调用和返回在中断程序中,通过特定的指令调用需要的子程序,等待子程序执行完成后再进行返回。
这里需要特别注意子程序调用的参数传递和返回值的处理。
四、中断程序调用子程序的实际应用中断程序调用子程序在实际应用中有着广泛的用途,比如在实时系统中,可以利用中断程序调用子程序来实现即时响应;在通信系统中,可以利用中断程序调用子程序来实现数据处理和通信协议的处理等。
五、个人观点和总结中断程序调用子程序是51单片机汇编编程中的重要内容,掌握了这一技术可以让我们更加灵活地进行程序设计和开发。
通过本文的深度解析,希望读者能够更加深入地理解和掌握这一知识,并在实际应用中发挥其作用。
完整的文章已经写好并按照知识的文章格式进行了排版,总字数超过3000字。
中断服务函数的使用技巧中断服务函数(Interrupt Service Routine,简称ISR)是在计算机系统中,由硬件中断或异常触发的一段程序代码,用于处理中断或异常情况。
在嵌入式系统中,中断是实现实时响应的重要机制之一。
下面将介绍一些中断服务函数的使用技巧。
首先,编写中断服务函数时,要保证其简洁和高效。
中断服务函数需要在最短的时间内完成任务,因此应尽量减少不必要的代码和延迟。
可以选择使用汇编语言编写ISR,因为汇编语言直接操作硬件,执行效率高。
同时,要注意避免在ISR中进行复杂的计算或调用其他函数,以免浪费时间导致系统性能下降。
其次,中断服务函数需要考虑数据的同步和保护。
由于中断可以随时发生并打断正在执行的代码,所以在中断服务函数中访问的全局数据可能会与主程序产生冲突。
为了避免数据竞争和死锁,可以使用临界区(critical section)来保护共享的变量或资源。
在进入中断服务函数之前,禁止其他中断的发生;在离开中断服务函数之后,再开放其他中断。
这样可以确保在修改共享资源期间,不会被其他中断干扰。
另外,中断服务函数需要注意对中断状态的处理。
根据不同的应用场景,可能需要在中断服务函数中禁用或开放中断。
禁用中断可以防止其他更高优先级的中断打断当前中断服务函数的执行,从而实现嵌套中断的控制。
开放中断则可以保证系统具有实时响应能力,及时响应更高优先级的中断。
在使用中断服务函数时,要根据具体的需求合理调整中断的优先级和开放的时间点。
最后,中断服务函数需要测试和验证。
在开发和调试中断服务函数时,可以使用模拟器或硬件调试器进行单步调试和变量监视,以确保中断服务函数的正确性和稳定性。
同时,还可以编写一些自动化测试代码,对中断服务函数进行性能和可靠性的评估。
通过充分的测试和验证,可以发现潜在的问题和bug,并及时进行修复和优化。
总之,中断服务函数的使用技巧涉及到代码的简洁高效、数据的同步保护、中断状态的处理以及测试和验证等方面。
中断服务函数带传参
中断服务函数是嵌入式系统中常用的一种机制,它可以在系统发生特定事件时,立即中断当前的执行流程,转而执行预先定义好的中断服务函数。
而带传参的中断服务函数则是在中断发生时,除了执行特定的处理代码外,还可以传递一些数据给中断服务函数来进一步处理。
中断服务函数带传参的设计主要有两个方面的考虑:传递参数的方式和参数的类型。
传递参数的方式可以通过寄存器或者堆栈来实现。
在使用寄存器传递参数时,需要事先规定好寄存器的使用规则,以免在中断发生时出现寄存器冲突的问题。
而使用堆栈传递参数则需要在中断服务函数中手动保存和恢复堆栈指针,确保参数的正确传递和处理。
参数的类型可以根据具体的需求来选择。
可以是简单的基本类型,如整数、字符等,也可以是复杂的结构体或指针类型。
在传递结构体时,需要注意结构体的大小和对齐方式,以及在中断服务函数中如何正确地访问结构体的成员变量。
而传递指针类型时,则需要确保指针指向的内存空间是有效的,并且在中断服务函数中正确地解引用指针。
在实际应用中,中断服务函数带传参的设计可以提高系统的灵活性和可扩展性。
例如,在一个实时控制系统中,可以通过传递参数来
实现不同的控制策略;在一个通信系统中,可以通过传递参数来处理不同类型的数据包。
中断服务函数带传参是嵌入式系统中一种重要的设计方式,它可以在中断发生时,通过传递参数来实现更加灵活和可定制的中断处理。
通过合理的参数传递方式和类型选择,可以提高系统的性能和可维护性。
在实际应用中,需要根据具体的需求来设计和实现中断服务函数的参数传递机制,以满足系统的要求。
原文地址:C语言中关于中断的问题(嵌入式)前提:中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。
具代表事实是,产生了一个新的关键字__interrupt。
下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。
__interrupt double compute_area (double radius){double area = PI * radius * radius;printf(" Area = %f", area);return area;}这个函数有太多的错误了,以至让人不知从何说起了:1). ISR 不能返回一个值。
2). ISR 不能传递参数。
3). 在许多的处理器/编译器中,浮点一般都是不可重入的。
有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。
此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。
4). 与第三点一脉相承,printf()经常有重入和性能上的问题。
对于printf()经常有重入和性能上的问题的理解:由于存在任务的调度,它实时系统,可剥夺型内核中是危险的,如同一个安静的水雷。
可能会被触发,也可能安然无恙。
由于它运行结果的不可预期性,会使系统带来隐患。
下面引用一段别人的解释:这主要在多任务环境中使用,一个可重入的函数简单来说,就是:可以被中断的函数。
就是说,你可以在这个函数执行的任何时候中断他的运行,在OS的调度下去执行另外一段代码而不会出现什么错误。
而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等等,所以他如果被中断的话,可能出现问题,所以这类函数是不能运行在多任务环境下的。
把一个不可重入函数变成可重入的唯一方法是用可重入规则来重写他。
其实很简单,只要遵守了几条很容易理解的规则,那么写出来的函数就是可重入的。
关中断是否禁⽌任务调度?关中断能作为互斥吗?今天再看《嵌⼊式软件系统教程》((美)著,等译),⾥⾯讲到关中断会关了任务调度,作者没说原因,我也不知道为什么,所以查了查⽹络。
===============================================================在这个⽹址***上有⼀个讨论,我觉得很有道理。
很明显观点是关了中断,只是禁⽌了抢占,但并没有禁⽌调度。
这样看来,那本书上的观点是不正确的??把⼀些⾃认为很有道理的解释摘抄下来:⾸先说明⼀下,引起调度(任务切换)的原因有两个:1、任务主动进⼊了阻塞状态,就像2楼说的,调⽤了所谓的切换函数。
2、中断,中断服务程序(isr)改变了任务状态,使某个⽐当前任务优先级⾼的任务进⼊了准备好(ready)状态,于是内核调⽤了切换函数。
这两个原因可以称为“同步⽅式调度”和“异步⽅式调度”。
抢占的意义在于:如果⾼优先级任务进⼊准备好状态后,操作系统可以⽴即调度(任务切换),将⾼优先级任务投⼊运⾏。
⽽这些动作对低优先级任务来说是完全透明的。
但是,谁?!使⾼优先级任务进⼊准备好状态,⽽⼜对低优先级任务透明呢?就是“中断”。
所以说,“抢占”是系统进⾏了“异步⽅式调度”的结果。
既然关闭了中断,也就禁⽌了抢占。
楼主,你想明⽩了吗?在这⾥也要纠正⼀下3楼和5楼:“关了中断,就禁⽌了调度”这种说法不妥。
关了中断,只是禁⽌了抢占,但并没有禁⽌调度啊。
正在运⾏的任务是所有就绪任务中优先级最⾼的,如果要运⾏其它优先级更⾼的任务,就只有发⽣了某些事件使得更⾼优先级的任务进⼊了就绪状态,⽽这些事件只有在两种情况下发⽣:1. 中断改变了任务状态2. 运⾏中的任务主动使优先级更⾼的任务进⼊就绪状态2 相当于是主动放弃CPU占有权,所以说抢占是由中断引起的,是异步调度的结果(??这⾥逻辑有点问题。
作者的意思可能是:2是⾮抢占的,主动的,1是抢占的,被动的,所以说抢占是由中断引起的,是异步调度的结果。
中断服务子函数中断服务子函数是嵌入式系统中的一种特殊函数,用于处理硬件中断信号。
在嵌入式系统中,硬件设备会产生各种中断信号,如定时器中断、外部中断等。
当这些中断信号发生时,中断服务子函数会被调用来处理相应的中断事件。
本文将详细介绍中断服务子函数的作用、原理和实现方法。
一、中断服务子函数的作用中断服务子函数主要用于及时响应硬件中断,处理中断事件,并返回到原来的执行点。
它可以在不影响主程序运行的情况下,对中断事件进行快速响应和处理。
中断服务子函数可以完成一些与硬件相关的任务,如读取传感器数据、控制外设等。
通过中断服务子函数的调用,可以提高系统的实时性和可靠性。
二、中断服务子函数的原理中断服务子函数的原理是通过中断向量表来实现的。
中断向量表是一个保存中断服务子函数入口地址的表格,其中每个中断信号对应一个中断向量。
当一个中断信号发生时,CPU会根据中断信号的编号在中断向量表中查找对应的中断服务子函数入口地址,并跳转到该地址开始执行中断服务子函数。
三、中断服务子函数的实现方法中断服务子函数的实现方法与具体的嵌入式系统有关,下面以ARMCortex-M系列为例进行介绍。
1. 配置中断向量表:在Cortex-M系列中,中断向量表位于内存的起始地址,需要将中断服务子函数的入口地址存放在中断向量表中。
可以使用汇编语言或特定的编译器指令来配置中断向量表。
2. 定义中断服务子函数:在C语言中,可以使用特殊的中断处理函数来定义中断服务子函数。
函数名通常以“IRQ”或“ISR”开头,后接中断信号的编号。
中断服务子函数可以包含一系列的处理操作,如读取寄存器、调用其他函数等。
3. 注册中断服务子函数:在系统初始化时,需要将中断服务子函数注册到中断控制器中。
中断控制器负责管理和分发中断信号,将中断信号传递给对应的中断服务子函数。
4. 中断服务子函数的执行:当一个中断信号发生时,中断控制器会通过硬件机制将中断信号传递给CPU。
CPU会根据中断信号的编号在中断向量表中查找对应的中断服务子函数入口地址,并跳转到该地址开始执行中断服务子函数。
中断中调用函数与函数可重入问题一、中断函数是一个特殊的函数,没有参数,也没有返回值;但是程序中允不允许使用return呢?答案是允许的,不过只能用"return;",不能用"return(z);";用在一些需要快速返回的地方,对应的汇编会有多个ret语句,相对效率会高一些。
1 g& j; ^; r! @' ` 二、using的用法,using可以修饰任何函数,不过个人建议只用来修饰中断函数;简单的说,“using”会指定工作寄存器组,由于中断函数一般都是比较紧急的事情,有时一条语句都会斤斤计较,所以使用using切换寄存器组可以省去一些压栈7 g/ O F. @% A! k7 _9 的动作,由于51只有两级中断,同级中断不能被打断,因此,我们可以同级中断设成同样的寄存器组,从某种意义上来说,有一组寄存器是多余的。
同时个人建议中断函数应该使用using这个关键字。
9 V8 [! t0 y( l三、中断中调用函数,首先要讨论中断函数中调用函数的必要性,前天在论坛上我和别人争论过这个问题,现在我还是这个观点:有些情况中断中调用函数还是必要的,这个时候是不是该调用函数,其实和普通函数差不多,首先是这个函数如r5 X8 C; ^7 s2 g+ ]: }1 u$ `( {厦门E城论坛果调用多次,或者要带一些参数什么的就更加必要的;前天有人跟我叫劲,说假如只调用一次且无参数无返回的函数要直接写,因为如果用函数,至少会增加CALL和RET两条语句,我不敢苟同,我是实际调试发现的,当你程序比较复杂时,你将那部单独拉出来做成函数,可能代码和时间都会更好。
4 C4 h( A/ M8 [. u& Z& x. A. M6 四、中断中调用的函数最好不要被中断外的其它函数调用,因为会出现“重复调用”的警告,有时这种调用是很致命的,有人说这个函数可以用reentrant来修饰,是的,的确可以这样解决,不过个人不建议这么做,也许这样会跟你减少很多堆栈空间,并且整个程序的优化要差很多,个人建议出现这种情况就把这个函数写两遍,分成两个函数分别调用。
中断处理函数与异常处理函数的关系一、引言在计算机系统中,中断和异常是两种常见的事件,它们在系统运行过程中起着重要的作用。
中断是指外部设备发送的信号,请求CPU进行处理;异常是指程序运行过程中发生的错误或意外情况。
在处理中断和异常时,计算机系统会调用相应的中断处理函数和异常处理函数来进行处理。
本文将探讨中断处理函数与异常处理函数的关系,并分析它们在系统运行中的作用和联系。
二、中断处理函数的概念和作用1.中断的概念中断是指由外部设备发送的信号,请求CPU进行处理的事件。
在计算机系统中,中断可以分为硬件中断和软件中断两种类型。
硬件中断由外部设备产生,软件中断由CPU指令产生。
2.中断处理函数的作用中断处理函数是系统预先定义的处理中断事件的函数,当系统接收到中断信号时,会调用相应的中断处理函数来处理中断。
中断处理函数的作用是保存当前运行环境的状态,处理中断事件,并在处理完毕后恢复之前的运行环境。
三、异常处理函数的概念和作用1.异常的概念异常是指程序运行过程中发生的错误或意外情况,例如除零操作、访问非法内存等。
异常的发生会导致程序无法继续执行,需要系统进行相应的处理。
2.异常处理函数的作用异常处理函数是系统预先定义的处理异常事件的函数,当程序发生异常时,系统会调用相应的异常处理函数来进行处理。
异常处理函数的作用是终止当前程序的执行,处理异常情况,并采取相应的措施来保护系统的稳定运行。
四、中断处理函数与异常处理函数的关系1.联系中断处理函数和异常处理函数都是系统预先定义的用于处理特定事件的函数,它们都起着保护系统稳定运行的作用。
在系统接收到中断信号或程序发生异常时,都会调用相应的处理函数来进行处理。
2.区别虽然中断处理函数和异常处理函数都是用于处理特定事件的函数,但它们的处理对象和触发条件有所不同。
中断处理函数处理的是外部设备发送的中断信号,而异常处理函数处理的是程序运行过程中发生的错误或意外情况。
五、中断处理函数与异常处理函数的设计原则1.灵活性中断处理函数和异常处理函数应该具有一定的灵活性,在设计时需要考虑到系统的实际情况和需求,确保它们能够适应不同的中断或异常情况。
51单片机汇编中断程序调用子程序(原创实用版)目录1.51 单片机汇编中断程序概述2.中断程序的调用方式3.子程序的定义与调用4.中断程序调用子程序的实例分析5.总结正文一、51 单片机汇编中断程序概述在 51 单片机汇编语言编程中,中断是一种常见的编程方式,可以实现在特定条件下程序的跳转和执行。
通过中断程序,可以实现对硬件设备的实时控制,提高程序的执行效率。
二、中断程序的调用方式中断程序的调用方式主要有两种:1.通过外部中断引脚(如 P1.0、P2.0 等)触发中断。
这种方式下,当外部中断引脚的状态发生改变时,单片机会立即跳转到中断程序的入口地址执行。
2.通过软件中断实现中断程序的调用。
这种方式下,程序员可以通过设置特定的寄存器值来触发中断,使程序跳转到中断程序的入口地址执行。
三、子程序的定义与调用子程序,也称为子例程,是程序中一段可独立执行的代码段。
子程序可以通过以下方式定义和调用:1.使用“SUB”伪指令定义子程序。
在需要调用子程序的地方,编写“CALL 子程序名”,即可实现子程序的调用。
2.使用“PROG”伪指令定义子程序。
在需要调用子程序的地方,直接编写子程序名,即可实现子程序的调用。
四、中断程序调用子程序的实例分析假设我们有一个 51 单片机汇编语言程序,当外部中断引脚 P1.0 触发时,需要执行一个子程序以完成特定功能。
程序如下:```ORG 00HMOV P1, #00HMOV R4, #0FFHSTART: NOPINT0: MOV R3, #0FFHCALL INT_SUBROUTINESJMP STARTINT_SUBROUTINE: MOV R5, R3// 子程序执行的内容MOV R3, R5SJMP RETURNRETURN: MOV R4, R3SJMP RETURN_SUBROUTINERETURN_SUBROUTINE: MOV R3, #00HSJMP START```在上述程序中,当 P1.0 引脚触发中断时,程序会跳转到“INT0”标签所在的位置,执行子程序“INT_SUBROUTINE”。
的值已经改变,从而导致程序运行不正确,反过来亦然。
另一方面,func()与TaskB有直接的调用关系,因而其局部变量b与c不会被互相覆盖,但也不能保证func的局部变量c不会与TaskA或其他任务的局部变量形成可覆盖关系。
根据上述分析我们很容易就能够判断出TaskA和TaskB这两个函数是不可重入的(当然,func也不可重入)。
那么如何让函数成为可重入函数呢?C51编译器采用了一个扩展关键字reentrant作为定义函数时的选项,需要将一个函数定义为可重入函数时,只要在函数后面加上关键字reentrant即可。
厦门城论坛z' V- \0 L/ Z2 A. G) j
与非可重入函数的参数传递和局部变量的存储分配方法不同,C51编译器为可重入函数生成一个模拟栈(相对于系统堆栈或是硬件堆栈来说),通过这个模拟栈来完成参数传递和存放局部变量。
模拟栈以全局变量?C_IBP、?C_PBP和?C_XBP作为栈指针(系统堆栈栈顶指针为SP),这些变量定义在DATA地址空间,并且可在文件startup.a51中进行初始化。
根据编译时采用的存储器模式,模拟栈区可位于内部(IDATA)或外部(PDATA或XDATA)存储器中。
如表1所示:
表1
s. h; j5 s) j- _4 @
厦门电子城厦门城电子零件单片机电子,xmecity,电子制作,DIY,电脑元件,9 A/ c6 `2 `6 M4 |
注意:51系列单片机的系统堆栈(也叫硬件堆栈或常规栈)总是位于内部数据存储器中(SP为 8位寄存器,
只能指向内部),而且是“向上生长”型的(从低地址向高地址),而模拟栈是“向下生长”型的。
, g! c) @% u( z) M/ r: x7 w
1、可重入函数参数传递过程剖析 T0 T$ T3 J. R# e
在进入剖析之前,先简单讲讲c51函数调用时参数是如何传递的。
简单来说,参数主要是通过寄存器R1~R7来传递的,如果在调用时,参数无寄存器可用或是采用了编译控制指令“NOREGPARMS”,则参数的传递将发生在固定的存储器区域,该存储器区域称为参数传递段,其地址空间取决于编译时所选择的存储器模式。
利用51单片机的工作寄存器最多传递3个参数,如表2所示。
表二
+ D0 N+ f2 J2 X' ]. j
举两个例子:厦门城论坛6 }0 W( O: |- V
func1(int a):“a”是第一个参数,在R6,R7中传递; func2(int b,int c, int *d):“b”在R6,R7中传递,“c”在R4,R5中传递,“*d”则在R1,R2,R3中传递。
厦门电子城厦门城电子零件单片机电子xmecity,
电子制作,DIY,电脑元件,2 t, B$ L$ b9 D+ z7 U0 P x! G0 r
至于函数的返回值通过哪些寄存器或是什么方法传递这里就不说了,大家可以看看c51的相关文档或是书籍。
好了,接下来我们开始剖析一个简单的程序,代码如下:
! A1 @6 W* W- L `# b4 V0 \0 V
程序很简单,废话少说,下面跟我一起看看c51翻译成的汇编语言是什么样子的(大存储模式下large XDA TA)。
说明:模拟栈指针最初在startup.a51中初始化为0xFFFF+1;由以上汇编代码可以看出参数是从右往左扫描的。
厦门电子城厦门城电子零件单片机电子,xmecity,电子制作,DIY,电脑元件,/ c! P" A! ~: R' A- d. }
接下来看看fun的汇编代码:(很长,大家耐心看吧,有些可以跳过的)
说明:模拟栈结构如下
接下来说明两个重点子函数C_ADDXBP和C_XBPOFF
终于到尾声了,最后重点说明啦~~~
模拟堆栈是向下生长的,C_XBP最初等于0xffff+1,那么请看下面这句4 n3 `3 G* `. [( n; s
其实是这样:加0xffff相当与减1,加0xfffe相当与减2,加0xfffd相当于减4。
为啥,就不用说了吧:)
厦门城论坛: O2 v# ~8 }# e1 j者
的论坛互相交流,提高自己的知识( Q9 @2 M3 I- b) o6 K4 O
附录:
在其它环境下(比如PC,比如ARM),函数重入的问题一般不是要特别注意的问题.只要你没有使用static变量,或者指向static变量的指针,一般情况下,函数自然而然地就是可重入的.厦门城论坛; U" u9 A2 [ e+ y3 B$ ?8 X
但C51不一样,如果你不特别设计你的函数,它就是不可重入的( T: H* m+ D& N% h
引起这个差别的原因在于:一般的C编译器(或者更确切点地说:基于一般的处理器上的C编译器),其函数的局部变量是存放于堆栈中的,而C51是存放于一个可覆盖的(数据)段中的./ o/ _3 r3 X" X# }+ ! A! s
至于C51这样做的原因,不是象有些人说的那样,为了节约内存.事实上,这样做根本节约不了内存.理由如下:
1) 如果一个函数func1调用另一个函数func2,那么func1,func2的局部变量根本就不能是同一块内存.C51还是要为他们分配不同的RAM.这跟使用堆栈相比,节约不了内存.* f3 {" }6 M( b
2) 如果func1,func2不是在一个调用链上,那么C51可以通过覆盖分析,让它们的局部变量共享相同的内存地址.但这样也不会比使用堆栈节约内存.因为既然它们是在不同的调用链上,那么当其中一个函数运行时,那么另外一个函数必然不在其生命期内,它所占用的堆栈也已释放,归还给系统.
真实的原因(C51使用覆盖段作为局部变量的存放地的原因)是:
51的指令系统没有一个有效的相对寻址(变址寻址)的指令,这使得使用堆栈作为变量的代价太过昂贵. 使用堆栈存放变量的一般做法是:厦门城论坛+ L! v& @, z! t' B$ W% d
进入函数时,保留一段堆栈空间,作为变量的存放空间,用一个可作为基址寻址的寄存器指向这个空间,通过加上一个偏移量,就可以访问不同的变量了.
例如: MOV EAX, [EBP + 14];X86指令8 l5 m5 g( G( A0 v9 G$ b
LDR R0, [R12, #14];ARM指令; {* |7 x& ?3 H8 c" w# j# s
都可以很好的解决这个问题. 但51缺少这样的指令.- j# E/ Z+ j- \% |- R
*其实,51中还是有2个可变址寻址的指令的,但不适合访问堆栈的局部变量这样的场合. MOVC A, @A+* T: }0 g% b* @) a0 K2 L L9 o
MOVC A, @A+PC集合厦门地区电子爱好者的论坛互相交流,提高自己的知识7 t: S! x% s' C
所以,C51有个特别的关键字: reentrant 用来解决函数重入的问题.。