ucos中任务切换函数都是汇编写的,属于“需移植”文件,
这个汇编文件名一般叫做:OS_CPU_A.ASM
要想看懂任务切换的原理,首先遇到的第一个难点,就是OS_CPU_A.ASM这个汇编文件里的一大堆不常见的汇编伪指令,搞懂这些指令是搞懂程序原理的第一步。
这篇文章先只分析这些汇编指令。
这个文件为ucos操作系统提供了4个API函数,分别是:
PUBLIC OSStartHighRdy;函数功能:切换到已就绪的任务横纵优先级最高的那个任务中去
PUBLIC OSCtxSw ;函数功能:一般的上下文切换,ContextSwitch,上下文切换又叫任务切换
PUBLIC OSIntCtxSw ;函数功能:在中断中进行上下文切换
PUBLIC OSTickISR ;函数功能:系统滴答
PULIC是汇编伪代码,表明所声明的函数可以被其他文件调用
首先来学习一个知识点,如何在汇编代码中写一个函数,才能使得这个函数能够被其他文件调用?不仅是加PUBLIC关键字这么简单,另外,我们还必须遵守一定的规范,可参考这篇文章,链接:点击打开链接
如果链接挂了,自行搜索关键字即可:《汇编函数与C函数的相互调用》
这个文件OS_CPU_A.ASM除了供外部文件引用自己的函数外,也需要引用别的文件的函数和变量,例如:
EXTRN IDATA (OSRunning) ;声明引用IDATA 区的变量OSRunning
MOV R0,#LOW (OSRunning) ;在汇编中使用外部变量
EXTRN CODE (_?OSTaskSwHook) ;声明引用外部函数(代码),OSTaskSwHook()
LCALL _?OSTaskSwHook ;在汇编中调用C语言函数OSTaskSwHook()
解释:函数OSTaskSwHook是用C语言写的,名字为OSTaskSwHook,但是在汇编中引用它的话,必须在前面加前缀才行,由前面链接里的文章我们知道,如果我们在汇编中引用的是可重入函数,那么必须在函数名前面加_?前缀才能被汇编文件识别到。为什么要加前缀?因为C51的C语言函数转换为汇编的时候,keil编译器会自动把C语言的函数名给改掉,当然keil所做的改动是有规律的,例如,我们声明的可重入的C函数,keil转成汇编后,会自动在原先的函数名前加前缀“_?” 。
keil会自动添加什么前缀,添加的前缀有什么规范?这些问题可以参考keil的帮助文件,依次点击菜单栏->help->uVision help,在打开的帮助文件中搜索“Segment Naming Conventions(段命名惯例)”可查阅相关信息。
在汇编中写一个供C语言调用的函数的标准格式如下:
?PR?OSStartHighRdy?OS_CPU_A SEGMENT CODE;先声明一个可重定位的代码段(这个语句的用法与解释可参考本博客的另一篇文章)
RSEG ?PR?OSStartHighRdy?OS_CPU_A;进行重定位,下面的代码都将被链接到在RSEG指令所指定的段中
OSStartHighRdy: ;地址标号,作为函数名
·········;汇编函数的函数体,直到遇到CSEG/DSEG/RSEG 等段分配指令
上述代码解释:?PR?OSStartHighRdy?OS_CPU_A SEGMENT CODE这一句不仅是声明了一个可重定位段的段名,这个段名的前缀为?PR?,这个前缀的意义是:该段是一个函数段(Executable program code)
再来看一个汇编调用C语言函数的例子:
在main.c文件中,我们定义了这样一个函数:
char add_two(char a1, char a2) REENTRANT
{
return a1+a2;
}
这个函数在keil编译之后如下:
85: char add_two(char a1, char a2) REENTRANT
86: {
C:0x269B 90FFFF MOV DPTR,#0xFFFF
C:0x269E 120436 LCALL C?ADDXBP(C:0436)
C:0x26A1 ED MOV A,R5
C:0x26A2 F0 MOVX @DPTR,A
C:0x26A3 90FFFF MOV DPTR,#0xFFFF
C:0x26A6 120436 LCALL C?ADDXBP(C:0436)
C:0x26A9 EF MOV A,R7
C:0x26AA F0 MOVX @DPTR,A
87: return a1+a2;
C:0x26AB 850883 MOV DPH(0x83),?C_XBP(0x08)
C:0x26AE 850982 MOV DPL(0x82),OutTxBuf(0x09)
C:0x26B1 A3 INC DPTR
C:0x26B2 E0 MOVX A,@DPTR
C:0x26B3 FF MOV R7,A
C:0x26B4 850883 MOV DPH(0x83),?C_XBP(0x08)
C:0x26B7 850982 MOV DPL(0x82),OutTxBuf(0x09)
C:0x26BA E0 MOVX A,@DPTR
C:0x26BB 2F ADD A,R7
C:0x26BC FF MOV R7,A
88: }
C:0x26BD 900002 MOV DPTR,#0x0002
C:0x26C0 020436 LJMP C?ADDXBP(C:0436)
由汇编代码我们可以看到,该函数被放在了0x269B地址处,继续观察keil生成的.m51文件(即map文件),搜索add_two,发现相关内容如下:
(1)CODE 269BH 0028H UNIT ?PR?_?ADD_TWO?MAIN
(2)C:269BH PUBLIC _?add_two
(3)------- PROC _?ADD_TWO
x:0000H SYMBOL a1
x:0001H SYMBOL a2
C:269BH LINE# 85
C:26ABH LINE# 87
C:26BDH LINE# 88
------- ENDPROC _?ADD_TWO
有上述查到的内容发现,keil在编译add_two()函数的过程中,做了3个工作:
(1)为add_two()函数声明了一个段,位置从269BH开始,大小为0028H ,这个段中只含有add_two这个函数的代码段,不含其他函数,也不不含任何数据段,段名为:?PR?_?ADD_TWO?MAIN(段名由3部分组成:一是固定前缀?PR?,二是函数名的大写ADD_TWO,并且keil在为函数生成汇编时,自动为可重入函数的函数名前加前缀_?,三是模块名,默认的模块名即该函数所在的文件名)。
(2)keil对“为add_two()生成的汇编函数”进行了PUBLIC声明,以供其他文件调用该函数,但是声明函数名的时候,加了_?前缀:_?add_two,如果有汇编文件打算调用add_two()函数,就得这样:
EXTRN CODE (_?add_two) ;声明引用外部函数(代码):add_two()
LCALL _?add_two ;在汇编中调用C语言函数add_two()
按照keil的命名惯例,
_?前缀的函数是可重入函数。
拓展资料:其他类型的函数的命名惯例如下:
无参函数: ?PR?函数名?文件名
有参函数: ?PR?_函数名?文件名
可重入函数: ?PR?_?函数名?文件名
(3)这是add_two()函数的符号表,也就是指出了add_two()函数中的形参和局部变量所在的存储位置。那么这里有个疑问,为什么a1、a2被放在了00h和01H地址处呢,这两个地址不是寄存器R0和R1吗?这就涉及到了keil的编译规则,形参的类型、数量不同时,传参的方法都是不一样的,形参往哪里放有专门的文章介绍,一般来说,形参和局部变量较少时,全部都用寄存器Rn来传递和存储;数量较多时,不可重入函数的形参和局部变量在Rn不够用时,其余的存放到固定的内存地址中,可重入函数的形参和局部变量在Rn不够用时,其余的入仿真栈。
上一篇:51单片机之IO口扩展
下一篇:基于51单片机的贪吃蛇游戏
推荐阅读最新更新时间:2024-10-30 10:05
设计资源 培训 开发板 精华推荐
- AM30EW-2405.1SZ 5.1V 单路输出 DC/DC 转换器的典型应用
- 使用 Analog Devices 的 LT1021BMH-5 的参考设计
- 【智能车竞赛】 C车电机驱动板
- LTC1430ACS8 多相开关稳压器在低电压、高电流应用中提供高效率
- m1_mh743_ada_v4
- AD9754-EB,用于 AD9754、14 位、125 MSPS 高性能 D/A 转换器的评估板
- 基于单对以太网技术SPE的水下传感器节点的开源项目(从教程到实战,英文)
- IMX50EVK: i.MX50评估套件
- AOZ1037 5A 同步降压稳压器的典型应用
- TAR5SB20 点稳压器(低压差稳压器)的典型应用