c51 reentrant 关键字详解

/*
单片机分 端口映射 和 内存映射
51 为端口映射 但亦可以扩展为内存映射 那样就是混合映射
大部分的非51 cpu为内存映射
端口映射的CPU需要对c语言进行扩展,扩展端口映射语句,sfr Register = addr;
内存映射的CPU直接用标准c就可。 与硬件相关时可用 #define Register (*(volatile uint8*)addr)
073182527395


usb初始化{
准备好 三个描述(设备描述符,配置描述符,字符串描述符,接口描述符,端点描述符)
设置usb状态信息
设置配置信息
设置回调函数
打开上拉电阻,与主机建立连接
开usb中断

//初始化usb RAM;(不是必须的)
//关闭除端点0外所有端点; (不是必须的)
}
开总中断{};
等待{};


usb中断函数{


}
*/
//小实验
//目的:了解关键字 reentrant 的用法
//实验代码1
//int fun(char a, char b, char c, char d )
//{  
//int j1,j2;   
//j1 = a + b + c +d; 
//j2 = j1 + 10;  
// return j2; 
// }   
// 
//void  main()
//  {  
//  int i; 
//  i = fun(1,2,3,4);  
//  }   
//
//  void exit_interrupt() interrupt 0
//  {
//   fun(6,7,8,9);
//  }
//编译结果:
//Build target 'Target 1'
//assembling STARTUP.A51...
//compiling a.c...
//linking...
//*** WARNING L15: MULTIPLE CALL TO SEGMENT
//    SEGMENT: ?PR?_FUN?A
//    CALLER1: ?C_C51STARTUP
//    CALLER2: ?PR?EXIT_INTERRUPT?A
//Program Size: data=19.0 xdata=0 code=129
//creating hex file from "aaa"...
//"aaa" - 0 Error(s), 1 Warning(s).


//实验代码2
//int fun(char a, char b, char c, char d ) reentrant  
//{  
//int j1,j2;   
//j1 = a + b + c +d; 
//j2 = j1 + 10;  
// return j2; 
// }   
// 
//void  main()
//  {  
//  int i; 
//  i = fun(1,2,3,4);  
//  }   
//
//  void exit_interrupt() interrupt 0
//  {
//   fun(6,7,8,9);
//  }
//编译结果:
//Build target 'Target 1'
//assembling STARTUP.A51...
//compiling a.c...
//linking...
//Program Size: data=12.0 xdata=0 code=212
//creating hex file from "aaa"...
//"aaa" - 0 Error(s), 0 Warning(s).


//实验结果:
//reentrant 关键字解决单个函数多次并行调用的数据安全问题
//说明:如果函数使用在并行任务中,想函数的多个调用能产生预期的结果,那就在后面加上 关键字 reentrant
//注意:加了reetrant关键字的函数,就是内存消费机,加太或调用太多多会使内存不够用。

转自:https://blog.csdn.net/helezh/article/details/39225925

------------------------------------------------------------

Keil C51对C语言的关键词扩展之十五: reentrant

reentrant声明的函数为可重入函数。可重入的函数能够被多个进程同时调用。可重入函数在执行时,另外的进程可以中断当前执行的函数,并且调用同一个函数。正常情况下,C51程序中的函数不能被递归地调用,这是由于函数的参数和局部变量都被保存在固定的地址,在递归调用时操作了相同存储位置,导致数据被覆盖。

使用reentrant声明函数为可递归调用的可重入函数:

int calc (char i, int b) reentrant  {
  int  x;
  x = table [i];
  return (x * b);
}

可重入函数,能够被递归调用,也能被两个以上的进程同时调用。可重入函数通常在实时应用或者中断与非中断程序共享相同函数这两种情况下被使用。

每个可重入函数都有一个位于内部ram或外部ram的模拟堆栈:

1)SMALL内存模型下,可重入函数模拟堆栈位于idata区;

2)COMPACT内存模型下,可重入函数模拟堆栈位于pdata区;

3)LARGE内存模型下,可重入函数模拟堆栈位于xdata区;

使用reentrant声明可重入函数须遵循的规则:

1)可重入函数不支持位寻址变量,比如bit类型的参数;

2)可重入函数不能被alien函数调用;

3)可重入函数不能被声明为alien属性(alien用于使能PL/M-51参数传递约定);

4)可重入函数可以同时拥有其他属性,比如using、interrupt、small、compact、large;

5)返回地址被保存在硬件堆栈中;

6)使用不同存储模型的可重入函数能够混合,但是各自函数声明时必须指定存储模型;

7)三种内存模型的可重入函数都有自己的对战区和栈指针。比如在相同模块中,定义了small和large类型的可重入函数,则small和large类型的堆栈及其堆栈指针都被创建;

可重入堆栈模拟体系,效率比较低下,但是由于8051自身缺乏适当寻址方法的硬件特性,所以推出这种堆栈模拟体系来满足我们的可重入需求,在应用中 ,我们应该尽量不用或少用可冲入函数。

可重入函数使用的模拟堆栈拥有独立于8051硬件堆栈的栈指针。堆栈和堆栈指针在STARTUP.A51文件中被定义和初始化。

下表列出了三种内存模型下的模拟堆栈指针名称、大小、数据区域:

内存模型        指针   堆栈信息
SMALL ?C_IBP        
(1 字节) 
    堆栈位于间接寻址的内部ram(idata). 最大可重入堆栈容量256字节. 访问该模拟堆栈,须要使用 R0 或 R1装载?C_IBP 的值,然后使用       MOV A, @R0/@R1 或 MOV @R0/@R1, A 指令。
COMPACT      ?C_PBP
(1 字节)
    堆栈位于可页寻址的外部ram。 最大容量256 字节。 访问该模拟堆栈,须要使用 R0 或 R1装载?C_PBP 的值,然后使用MOVX A, @R0/@R1 或 MOVX @R0/@R1, A 指令。
LARGE ?C_XBP
(2 字节)
    堆栈位于外部ram。 T最大堆栈容量64KB。 访问该模拟堆栈,须要使用DPTR 装载?C_XBP 的值,然后使用MOVX A, @DPTR 或 MOVX @DPTR, A 指令。

8051硬件堆栈向上增长,堆栈指针先加再压栈,可重入模拟堆栈正好相反,其指针先减再压栈。

STARTUP.A51启动代码声明并初始化了模拟堆栈及其堆栈指针,如果使用可重入函数就必须修改启动代码指出哪个模拟堆栈须要初始化。可以在启动代码中修改模拟堆栈的起始地址。

可重入函数的参数传递,通过模拟堆栈的压栈、出栈完成。

可重入函数的局部变量,也保存在模拟堆栈中,通过模拟堆栈指针访问。

转自:http://news.eeworld.com.cn/mcu/article_2018062139896.html

发布了106 篇原创文章 · 获赞 204 · 访问量 128万+

猜你喜欢

转载自blog.csdn.net/ab6326795/article/details/99821093