关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)

关于51单片机的仿真栈(模拟栈/可重入栈) 51单片机的仿真栈(又叫模拟栈、或者可重入栈)。 首先来看,51的系统栈(又叫系统栈,或者硬件栈),就是SP所指向的栈,他是一个满增栈(注释1),位于片内RAM的128 bytes之中,上电之后系统堆栈指针SP的初值等于多少呢?这个要从51的启动文件来分析,启动文件中有这样的汇编代码: ?STACK          SEGMENT   IDATA ;定义一个片内数据段,段名:?STACK 阅读详情

作者:xzp21st 邮箱: tyter1223@163.com 撰文辛苦,转载请注明作者及出处

关键字:keilc51,模拟堆栈,可重入函数调用,参数传递,C?XBP,C?ADDXBP

摘要:本文较详细的介绍了keilc51可再入函数和模拟堆栈的一些概念和实现原理,通过一个简单的程序来剖析keilc51在大存储模式下可重入函数的调用过程,希望能为keilc51和在51系列单片机上移植嵌入式实时操作系统的初学者提供一些帮助。

 

关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)

“可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候都可以被中断,一段时间以后又可以运行,而相应的数据不会丢失。”(摘自嵌入式实时操作系统uC/OS-II)

在理解上述概念之前,必须先说一下keilc51的“覆盖技术”。(采用该技术的原因请看附录中一网友的解释)

(1)局部变量存储在全局RAM空间(不考虑扩展外部存储器的情况);

(2)在编译链接时,即已经完成局部变量的定位;

(3)如果各函数之间没有直接或间接的调用关系,则其局部变量空间便可覆盖。

正是由于以上的原因,在Keil C51环境下,纯粹的函数如果不加处理(如增加一个模拟栈),是无法重入的。举个例子:

void TaskA(void* pd)
{
        int a;
        //其他一些变量定义

        do{
        //实际的用户任务处理代码
        }while(1);
}


void TaskB(void* pd)
{
        int b;
        //其他一些变量定义

        do{
        func();
        //其他实际的用户任务处理代码
        }while(1);
}


void func()
{
        int c;
        //其他变量的定义

        //函数的处理代码
}

 

在上面的代码中,TaskA与TaskB并不存在直接或间接的调用关系,因而它们的局部变量a与b便是可以被互相覆盖的,即它们可能都被定位于某一个相同的RAM空间。这样,当TaskA运行一段时间,改变了a后,TaskB取得CPU控制权并运行时,便可能会改变b。由于a和b指向相同的RAM空间,导致TaskA重新取得CPU控制权时,a的值已经改变,从而导致程序运行不正确,反过来亦然。另一方面,func()与TaskB有直接的调用关系,因而其局部变量b与c不会被互相覆盖,但也不能保证func的局部变量c不会与TaskA或其他任务的局部变量形成可覆盖关系。

 

根据上述分析我们很容易就能够判断出TaskA和TaskB这两个函数是不可重入的(当然,func也不可重入)。那么如何让函数成为可重入函数呢?C51编译器采用了一个扩展关键字reentrant作为定义函数时的选项,需要将一个函数定义为可重入函数时,只要在函数后面加上关键字reentrant即可。

 

与非可重入函数的参数传递和局部变量的存储分配方法不同,C51编译器为可重入函数生成一个模拟栈(相对于系统堆栈或是硬件堆栈来说),通过这个模拟栈来完成参数传递和存放局部变量。模拟栈以全局变量?C_IBP、?C_PBP和?C_XBP作为栈指针(系统堆栈栈顶指针为SP),这些变量定义在DATA地址空间,并且可在文件startup.a51中进行初始化。根据编译时采用的存储器模式,模拟栈区可位于内部(IDATA)或外部(PDATA或XDATA)存储器中。如表1所示:

 

存储模式

栈指针

栈区域

Small

?C_IBP(1字节)

间接访问的内部数据存储器(IDATA),栈区最大为256字节

Compact

?C_PBP(1字节)

分页寻址的外部数据存储器(PDATA),栈区最大为256字节

Large

?C_XBP(2字节)

外部数据存储器(XDATA),栈区最大为64K

                                                                                                 表1

注意:51系列单片机的系统堆栈(也叫硬件堆栈或常规栈)总是位于内部数据存储器中(SP为 8位寄存器,只能指向内部),而且是“向上生长”型的(从低地址向高地址),而模拟栈是“向下生长”型的。

 

2、 可重入函数参数传递过程剖析

在进入剖析之前,先简单讲讲c51函数调用时参数是如何传递的。简单来说,参数主要是通过寄存器R1~R7来传递的,如果在调用时,参数无寄存器可用或是采用了编译控制指令“NOREGPARMS”,则参数的传递将发生在固定的存储器区域,该存储器区域称为参数传递段,其地址空间取决于编译时所选择的存储器模式。利用51单片机的工作寄存器最多传递3个参数,如表2所示。

 

传递的参数

char、1字节指针

int、2字节指针

long、float

一般指针

第一个参数

R7

R6,R7

R4~R7

R1,R2,R3

第二个参数

R5

R4,R5

R4~R7

R1,R2,R3

第三个参数

R3

R2,R3

无

R1,R2,R3

                                                                                             表2

举两个例子:

func1(int a):“a”是第一个参数,在R6,R7中传递;

func2(int b,int c, int *d):“b”在R6,R7中传递,“c”在R4,R5中传递,“*d”则在R1,R2,R3中传递。

至于函数的返回值通过哪些寄存器或是什么方法传递这里就不说了,大家可以看看c51的相关文档或是书籍。

 

好了,接下来我们开始剖析一个简单的程序,代码如下:

int fun(char a, char b, char c, char d ) reentrant //为了分析简单,参数都是char型;
{
        int j1,j2;

        j1 = a + b + c +d;
        j2 = j1 + 10;

        return j2;
}

main()
{
        int i;

        i = fun(1,2,3,4);
}

 

程序很简单,废话少说,下面跟我一起看看c51翻译成的汇编语言是什么样子的。

main()
{
        int i;
        i = fun(1,2,3,4);
        MOV DPTR,#0xFFFF              ; 模拟栈指针C?XBP最初指向0xFFFF+1
        LCALL C?ADDXBP(C:00A6)        ;调用C?ADDXBP子程序,调整模拟栈指针C?XBP指向0xFFFF
                                      ;(C?ADDXBP子程序:C?XBP+DPTR. //加上0xFFFF相当于减一)
        MOV A,#0x04                   ;无寄存器可用,第四个参数直接压入模拟栈
        MOVX @DPTR,A ;
        MOV R3,#0x03                  ;参数3通过R3传递,见表2
        MOV R5,#0x02                  ;参数2过R5传递,见表2
        MOV R7,#0x01                  ;参数1通过R7传递,见表2
        LCALL fun(C:0003)             ;调用fun函数

        MOV DPTR,#C_STARTUP(0x0000)   ; fun函数返回值(int型)通过R6,R7传递回来。并存储在
        MOV A,R6                      ;外部数据存储器0x0000和0x0001处(int型为两个字节)
        MOVX @DPTR,A
        INC DPTR
        MOV A,R7
        MOVX @DPTR,A
}
RET                                   ;main返回

说明:模拟栈指针最初在startup.a51中初始化为0xFFFF+1;由以上汇编代码可以看出参数是从右往左扫描的。

 

接下来看看fun的汇编代码:(很长,大家耐心看吧,有些可以跳过的)

C:0003
MOV DPTR,#0xFFFF
LCALL C?ADDXBP(C:00A6)         ;调整模拟栈指针C?XBP=C?XBP-1
MOV A,R3
MOVX @DPTR,A                   ;R3中的值(参数3)压入模拟栈
MOV DPTR,#0xFFFF
LCALL C?ADDXBP(C:00A6)         ;调整模拟栈指针C?XBP=C?XBP-1
MOV A,R5
MOVX @DPTR,A                   ;R5中的值(参数2)压入模拟栈
MOV DPTR,#0xFFFF
LCALL C?ADDXBP(C:00A6)         ;调整模拟栈指针C?XBP=C?XBP-1
MOV A,R7
MOVX @DPTR,A                   ;R7中的值(参数1)压入模拟栈
MOV DPTR,#0xFFFC
LCALL C?ADDXBP(C:00A6)         ;继续调整模拟栈指针C?XBP=C?XBP-4,为放两个局部int变量做准备

j1 = a + b + c +d;
MOV DPTR,#0x0005
LCALL C?XBPOFF(C:00CA)         ;通过C?XBP的值调整DPTR使其指向模拟栈中第一个
                               ;参数,此时DPTR=0xFFFF注意:C?XBPOFF不改变C?XBP的值

MOVX A,@DPTR
MOV R7,A                       ;取出参数1
MOV A,R7
。。。。。。
。。。。。。                    ;省略,完成取参数2,取参数3,取参数4并相加
。。。。。。
MOV DPH(0x83),?C_XBP(0x08)
MOV DPL(0x82),0x09             ;0x09就是?C_XBP+1
MOV A, R6
MOVX @DPTR,A
INC DPTR
MOV A,R7
MOVX @DPTR,A                   ;计算结果j1压入模拟栈

j2 = j1 + 10;
。。。。。。
。。。。。。
。。。。。。                    ;省略,完成j2=j1+10,并把计算结果j2压入模拟栈

return j2;
MOV DPH(0x83),?C_XBP(0x08)
MOV DPL(0x82),0x09
INC DPTR
INC DPTR
MOVX A,@DPTR
MOV R6,A
INC DPTR
MOVX A,@DPTR
MOV R7,A                       ;从模拟栈取出j2送入R6,R7
}

MOV DPTR,#?C_XBP(0x0008)
LCALL C?ADDXBP(C:00A6)         ;fun要返回,释放模拟栈,使C_XBP指向0xffff
RET

 

说明:模拟栈结构如下

参数4

参数3

参数2

参数1

j1低字节

j1高字节

J2低字节

J2高字节

接下来说明两个重点子函数C_ADDXBP和C_XBPOFF

C?ADDXBP:                     ;####    该函数实现了C?XBP+DPTR    ####
MOV A,0x09                    ;0x09即为C_XBP
ADD A,DPL(0x82)               ;以下到第一个RET之前即完成:C_XBP+DPTR
MOV DPL(0x82),A
MOV A,?C_XBP(0x08)
ADDC A,DPH(0x83)
MOV DPH(0x83),A
CJNE A,?C_XBP(0x08),C:00B9
MOV 0x09,DPL(0x82)
RET


C:00B9
JBC EA(0xA8.7),C:00C2         ;中断开着吗?开着就把它关了(清0),然后跳到C:00C2
MOV 0x09,DPL(0x82)            ;中断本来就关着,安全,下面的行动不会被打断,把新
                              ;的模拟栈指针赋给C_XBP
MOV ?C_XBP(0x08),A
RET


C:00C2
MOV 0x09,DPL(0x82)
MOV ?C_XBP(0x08),A
SETB EA(0xA8.7)               ;开中断
RET

C?XBPOFF:                     ;此函数的功能一看就明白,即完成DPTR=C_XBP+DPTR
MOV A,0x09
ADD A,DPL(0x82)
MOV DPL(0x82),A
MOV A,?C_XBP(0x08)
ADDC A,DPH(0x83)
MOV DPH(0x83),A
RET

终于到尾声了,最后重点说明啦~~~

模拟堆栈是向下生长的,C_XBP最初等于0xffff+1,那么请看下面这句

即C_XBP -1;

MOV DPTR,#0xFFFF
LCALL C?ADDXBP(C:00A6)
(0xffff+1)+0xffff = 0xffff

再看

MOV DPTR,#0xFFFE
LCALL C?ADDXBP(C:00A6)

即C_XBP-2

再看

MOV DPTR,#0xFFFE
LCALL C?ADDXBP(C:00A6)

即C_XBP-3

。。。

其实是这样:加0xffff相当与减1,加0xfffe相当与减2,加0xfffd相当于减4。。。。。。为啥,就不用说了吧:)

 

结束语:

经过了几天的研究,终于写了个总结报告,算是自己的一点小小成就吧,错误之处在所难免,希望能够同大家一起讨论问题,共同进步。

 

 

参考文献:

1、徐爱钧,彭秀华 《单片机高级语言C51windows环境编程与应用》电子工业出版社 2001

2、彭光红,构造一个51单片机的实时操作系统。

 

附录:

在其它环境下(比如PC,比如ARM),函数重入的问题一般不是要特别注意的问题.只要你没有使用static变量,或者指向static变量的指针,一般情况下,函数自然而然地就是可重入的.

但C51不一样,如果你不特别设计你的函数,它就是不可重入的.

引起这个差别的原因在于:一般的C编译器(或者更确切点地说:基于一般的处理器上的C编译器),其函数的局部变量是存放于堆栈中的,而C51是存放于一个可覆盖的(数据)段中的.

至于C51这样做的原因,不是象有些人说的那样,为了节约内存.事实上,这样做根本节约不了内存.理由如下:

1) 如果一个函数func1调用另一个函数func2,那么func1,func2的局部变量根本就不能是同一块内存.C51还是要为他们分配不同的RAM.这跟使用堆栈相比,节约不了内存.

2) 如果func1,func2不是在一个调用链上,那么C51可以通过覆盖分析,让它们的局部变量共享相同的内存地址.但这样也不会比使用堆栈节约内存.因为既然它们是在不同的调用链上,那么当其中一个函数运行时,那么另外一个函数必然不在其生命期内,它所占用的堆栈也已释放,归还给系统.

真实的原因(C51使用覆盖段作为局部变量的存放地的原因)是:

51的指令系统没有一个有效的相对寻址(变址寻址)的指令,这使得使用堆栈作为变量的代价太过昂贵.

使用堆栈存放变量的一般做法是:

进入函数时,保留一段堆栈空间,作为变量的存放空间,用一个可作为基址寻址的寄存器指向这个空间,通过加上一个偏移量,就可以访问不同的变量了.

例如: MOV EAX, [EBP + 14]        ;X86指令

LDR R0, [R12, #14]        ;ARM指令

都可以很好的解决这个问题.

但51缺少这样的指令.

*其实,51中还是有2个可变址寻址的指令的,但不适合访问堆栈的局部变量这样的场合.

MOVC A, @A+DPTR

MOVC A, @A+PC

所以,C51有个特别的关键字: reentrant 用来解决函数重入的问题

keil5编译链设置 从Keil(MDK-ARM)5.37开始,官方只默认安装最新的ARM Compiler6(简称AC6),不再默认安装旧的ARM Compiler5(简称AC5)。ARM Compiler是ARM的嵌入式C/C++编译工具链。ARM Compiler5以及更早的版本使用的是ARMCC编译器,ARM Compiler6使用的是ARMCLANG编译器。下载AC5按要求选着自己要下载的版本导入编译链。 阅读详情

相关推荐

C51中函数调用时参数是如何传递的?

调用者将参数依次压入栈中,被调用函数在栈中为局部变量分配空间,并通过栈指针来访问参数和局部变量。函数执行完毕后,将返回值存放在特定的寄存器中,并通过栈中保存的返回地址返回到调用者的代码处。由于栈的大小有限,同时8位单片机在处理32位以上的数据时需要进行额外的操作,因此在C51中,通常会将较大的数据类型(如结构体或数组)通过指针来传递,以减少栈空间的消耗。函数执行完毕后,会将返回值存放在特定的寄存器中(如C51中的ACC寄存器),然后通过栈中保存的返回地址,返回到调用者的代码处。的值会被依次压入栈中。

JfdCoding的博客 539

C51子函数

水一篇文章

Erosyaya的博客 418

芯片设计新思路:如何用SystemC+TLM2.0快速搭建ESL性能模型(附GEM5实战案例)

本文探讨了在芯片设计早期利用SystemC和TLM2.0标准快速构建电子系统级(ESL)性能模型的方法。通过对比不同抽象层次,详细介绍了如何搭建事务级模型,并与GEM5仿真平台协同,实现高效的架构探索和性能评估。文章最后通过一个片上网络(NoC)带宽优化的实战案例,展示了该方法如何驱动数据驱动的设计决策,从而在流片前规避风险、优化芯片性能。

fire9的博客 370

Keil调用跟踪

Keil调试中跟踪函数被什么调用

injre! injre~ 2631

用blender为模型贴图并在gazebo中显示

用blender为模型贴图并在gazebo中显示 首先我用的是最新的blender2.92版本,附上链接https://www.blender.org/download/。并设置好简体中文。语言可以在编辑->偏好设置->界面里面设置。 首先界面右上角的摄像头,方块,灯光三个场景全都删除。 接着导入stl格式的模型文件,下面以tianbot_mini上面的激光雷达的模型为例。 导入激光雷达模型后,点击进入上方的UV Editing按钮进入UV编辑界面 此时我面看见网格化的激光雷达模型

qq_48427527的博客 8001

autodl云服务器使用中的conda源报错

通过find path -name filename找到.condarc文件,在autodl服务器中,一般在/root/.condarc。为了利用jypyter打开conda虚拟环境运行,需要下载ipykenel。这个报错是源的问题,在通过源获取包元数据时出现了HTTP 429错误。通过conda换源解决。修改.condarc文件,命令: vim .condarc。但这样还不能解决问题,得把报错中有问题的源注释掉。通过以下命令添加清华镜像源。把报错的链接源注释如下。

Ty_kilig的博客 1584

Java重入函数_关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)

MOVDPH(0x83),?C_XBP(0x08)MOVDPL(0x82),0x09;0x09就是?C_XBP+1MOVA, R6MOVX@DPTR,AINCDPTRMOVA,R7MOVX@DPTR,A;计算结果j1压入模拟栈j2 = j1 + 10;。。。。。。。。。。。。。。。。。。;省略,完成j2=j1+10,并把计算结果j1压入模拟栈return j2;MOVDPH(0x83),?C_XB...

weixin_42491590的博客 207

VMware vSphere运维管理手册

ESXi 7.0 引入了一种系统存储布局,可对大型模块和第三方组件进行灵活的分区管理和支持,同时简化了调试操作。ESXi 7.0 系统存储更改在 ESXi 7.0 之前,分区大小固定不变,除 /scratch 分区和可选 VMFS 数据存储之外,且分区号是静态的,这些限制了分区管理。在 ESXi 7.0 中,分区整合为更少且更大的可扩展分区,具体取决于所使用的引导介质及其容量。ESXi 7.0 系统存储布局包含四个分区:分区适用情况类型系统引导存储引导加载程序和 EFI 模块。FAT16。

Avenjan优快云博客 3624

keil C51 模拟堆栈,可重入函数调用,参数传递

作者:xzp21st   撰文辛苦,转载请注明作者及出处 关键字:keilc51,模拟堆栈,可重入函数调用,参数传递,C?XBP,C?ADDXBP 摘要:本文较详细的介绍了keilc51可再入函数和模拟堆栈的一些概念和实现原理,通过一个简单的程序来剖析keilc51在大存储模式下可重入函数的调用过程,希望能为keilc51和在51系列单片机上移植嵌入式实时操作系统的初学者提供一些帮助。  

phenixyf的专栏 2623

【Transformer】小白入门指南

Transformer是起源于NLP领域,然后在图像、视频、声音等领域都得到了广泛应用的基础架构,是继MLP、CNN、RNN’后的公认的第四大基础模型结构。Transformer技术凭借其卓越的表现和广泛的应用前景,已经成为了机器学习领域的一个关键技术。自从2017年Google提出》一文以来,Transformer架构的出现彻底改变了机器学习和深度学习的格局,尤其是在处理语言模型、生成模型以及多模态学习等领域,Transformer几乎成为了不可或缺的技术基石。

图挖掘领域,新晋砖家 ☞ 未来可期,欢迎和静静一起学习交流吖 2522

S32DS S32K312 LPSPI + DMA配置异步SPI

配置DMA->Specific Configuration->Dma Logic Channel->Configuration->Global。SpiPhyUnitMapping:将该配置Mapping到某一路Spi,此工程配置为Spi1,遂选择LPSPI_1。配置的该路SPI的片选ID,此处ID对应配置Pin脚时lpspi_pcs后的下表(本工程此处为PCS3)配置该路SPI对应的SPI硬件单元ID,本工程配置为Lpspi1,因此配置为CSIB1。首先配置SPI的Pin脚,此处使用的是LPSPI_1。

希望我的博客,能帮上你解决学习中工作中所遇到的问题 2363

KEILC51可重入函数及模拟栈浅析

摘要:本文较详细的介绍了keilc51可再入函数和模拟堆栈的一些概念和实现原理,通过一个简单的程序来剖析keilc51在大存储模式下可重入函数的调用过程,希望能为keilc51和在51系列单片机上移植嵌入式实时操作系统的初学者提供一些帮助。  1、关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)  “可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候都可以被中断

学海无涯,回头是岸........ 2025

研制过程评审活动(三)工程研制阶段

工程研制阶段主要任务、及评审活动

Abel的博客 2388

C51的再入函数 reentrant RECURSIVE CALL TO SEGMENT

重入函数,又叫再入函数,是一种可以在函数体内不直接或间接调用其自身的一种函 数。 再入函数可被递归调用,无论何时,包括中断函数在内的任何函数都可以调入。再入 函数在 C51编译时使用的是模拟栈。 函数说明: 函数名(形式参数表) reentrant 注意事项: 1、再入函数不能传递bit类型参数。 2、与PL/M51兼容的函数不能具有reentrant,这样也不能调用再入函数。

husion01的专栏 6450

NEU-DET 冷轧钢缺陷检测:基于 YOLOv8 的目标检测与可视化 UI 实现

冷轧钢的生产过程中,由于设备故障、工艺问题或者人为因素,钢板表面可能会出现多种缺陷,如划痕、裂纹、孔洞等,这些缺陷会严重影响钢材的质量。它比之前的版本(如 YOLOv5、YOLOv4)在速度和精度上都有显著提升,并且对硬件要求较低,适合进行工业界面和嵌入式系统的应用。这些版本相较于更大型的模型(如 YOLOv8x)具有更快的推理速度和较低的资源占用,适合工业生产线中的实时应用。通过将训练好的模型部署到工业生产线摄像头中,我们可以实现实时的缺陷检测,极大地提升生产效率和产品质量。

YOLO 737

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

51可重入堆栈的实现(转)

摘要:本文较详细的介绍了keilc51可再入函数和模拟堆栈的一些概念和实现原理,通过一个简单的程序来剖析keilc51在大存储模式下可重入函数的调用过程,希望能为keilc51和在51系列单片机上移植嵌入式实时操作系统的初学者提供一些帮助。   1、  关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈) “可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时

1207

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

51单片机的仿真栈(模拟栈/可重入栈)

51单片机的仿真栈(又叫模拟栈、或者可重入栈)。 首先来看,51的系统栈(又叫系统栈,或者硬件栈),就是SP所指向的栈,他是一个满增栈(注释1),位于片内RAM的128 bytes之中,上电之后系统堆栈指针SP的初值等于多少呢?这个要从51的启动文件来分析,启动文件中有这样的汇编代码: ?STACK SEGMENT IDATA ;定义一个片内数据段,段名:?STACK RSEG ?...

weixin_30389003的博客 578

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

KEILC51可重入函数及模拟栈浅析(转)

<br />摘要:本文较详细的介绍了keilc51可再入函数 和模拟 堆栈的一些概念和实现原理 ,通过一个简单的程序 来剖析keilc51在大存储模式 下可重入函数的调用过程,希望能为keilc51和 在51系列单片机 上移植嵌入式 实时操作系统 的初学者提供一些帮助。<br />1、关于可重入函数(可再入函数)和模拟堆栈(仿真 堆栈)<br />   “可重入函数可以被一个以上的任务调用,而不必担心数据 被破坏。可重入函数任何时候都可以被中断,一段时间 以后又可以运行,而相应的数

xqhj7的专栏 671

keilc51可重入函数及模拟栈浅析 (1)

1、关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)   “可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候都可以被中断,一段时间以后又可以运行,而相应的数据不会丢失。”(摘自嵌入式实时操作系统uC/OS-II)   在理解上述概念之前,必须先说一下keilc51的“覆盖技术”。(采用该技术的原因请看附录中一网友的解释)   (1)局部变量存储在全局RAM空间(...

Embest028的专栏 989

模拟栈

摘要:本文较详细的介绍了keilc51可再入函数和模拟堆栈的一些概念和实现原理,通过一个简单的程序来剖析keilc51在大存储模式下可重入函数的调用过程,希望能为keilc51和在51系列单片机上移植嵌入式实时操作系统的初学者提供一些帮助。 1、关于可重入函数(可再入函数)和模

kerwinash的专栏 721

keil c51 可重如问题

 查看文章  KEIL C51可重入函数及模拟栈浅析(转)2009-10-19 21:56原文:http://blog.163.com/shuqianyan/blog/static/74740421200911594540643/摘要:本文较详细的介绍了keilc51可再入函数和模拟堆栈的一些概念和

ly56086566的专栏 1975

移植uCOS-II到C51 - 5

这两天又测试了几个不同的移植版本,包括最先看到的SW和杨屹的版本,总算能在我的系统上跑起来了。虽然还有些问题没搞明白,但心里还是挺高兴的。毕竟,自己的努力看到了些许成果。做到现在才真的感觉到,其实在C51上跑uCOS,能做的东西也很有限。对我来说,只不过是个体验而已,而我也不希望这是失败的、让人受挫的体验。我也发现自己的硬件电路也有很多局限性,比如我把P3.6和P3.7用作了按键输入。这在不使

Tuhuo的专栏 2111
上一篇: 51单片机寄存器功能一览表
下一篇: MATLAB仿真PID控制器与模糊控制器
ACanswer
博客等级 码龄12年 280粉丝 42原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值