利用全局及外部变量实现C51无参数化调用A51函数

合集下载

C51的函数

C51的函数
/* ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
此程序用于说明函数的简单调用 ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ */
void delay(unsigned int time) {
while(time­­); }
d++;
6
return d; }
unsigned char get_value2(unsigned char *pd)*pd; }
void main() {
unsigned char a=100,b; b=get_value1(a); /*a 为参数,通过 get_value1 函数的形式参数传入函数中 这里要指出的是 get_value1 函数的形式参数 d,是定义在 函数内的,变量 a 传进来时是将变量 a 的值赋给了 d,实际 参与运算的是 d,可以说 d 是 a 的一个副本,对 d 的操作与 a 无关。所以 b 的值为 101,而 a 的值仍然为 100,这种传递方 式称为参数的值传递。 */式称为值传递。 b=get_value2(&a); /*get_value2 函数的参数为指针型,&a 作为参数传入,是
/* ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
此程序用于说明通过函数指针的用法 ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ */
void delay(unsigned int time) {
while(time­­); }
函数类型 函数名(数据类型 形式参数,数据类型 形式参数........) {

利用全局及外部变量实现C51无参数化调用A51函数

利用全局及外部变量实现C51无参数化调用A51函数

利用全局及外部变量实现C51无参数化调用A51函数摘要: 利用C51全局及外部变量,可实现无参数化调用A51函数,不但避开了传统C51调用A51时繁琐的接口约定,而且把在A51中所用到的变量全部放至C51程序中而不必考虑变量在内存中的位置,使编程更加简洁。

用实例验证了该方法的优越性和有效性。

关键词: C51 A51 汇编语言无参数化调用《电子技术应用》2001年第7期发表苟帅的文章“ASM51无参数化调用C51函数的实现”,阐述了ASM51无参数化调用C51函数的实现原理并给出实例来验证该方法的优越性和可行性。

作者在文章中表明这样一种观点:“利用汇编语言对I/O接口、中断向量及程序空间分配的巨大优势,让程序员对MCS-51内的每一个字节甚至是每一比特(可位寻址空间)全部进行统筹安排,设计好各个程序模块,包括I/O口地址和中断向量地址的处理;同时在具体数据处理、通信等不需要过多与硬件打交道的程序模块中,充分利用C51语言强大高效的编程能力”。

上述观点并没有错,但是对I/O接口、中断向量及程序空间分配及MCS-51内的每一个字节甚至是每一比特(可位寻址空间)全部进行统筹安排,对于大多数程序员来讲恰恰是件不容易的事。

地址空间、栈的起始地址和中断向量地址的分配等,经常顾此失彼,这边可行,那边又出问题。

其实完全可以把上述繁重的工作交给C51去完成,由C51定义各个变量并自动分配各个变量的空间,而集中精力完成所需功能的A51程序设计。

笔者在研制基于C51、插拔式FLASH存储器的无纸记录仪中,就是利用全局及外部变量实现C51无参数化调用A51函数的,取得了很好的效果。

1 C51无参数化调用A51函数的实现原理为了说明其原理,必须理解局部变量、全局变量和外部变量的意义。

局部变量:是在函数内部定义的变量,只在定义它的函数内部有效,仅在使用它时,才为它分配内存单元。

全局变量:又称外部变量,是在函数外部定义的变量,可以为多个函数共同使用,其有效作用范围是从它定义的位置开始直到整个程序文件结束。

C51启动代码详解

C51启动代码详解

$NOMOD51 ;使编译器不使能预定义的8051符号,避免产生重复定义的错误。

;------------------------------------------------------------------------------; This file is part of the C51 Compiler package; Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc.;------------------------------------------------------------------------------; STARTUP.A51: This code is executed after processor reset.;; To translate this file use A51 with the following invocation:;; A51 STARTUP.A51;; To link the modified STARTUP.OBJ file to your application use the following ; BL51 invocation:;; BL51 <your object file list>, STARTUP.OBJ <controls>; BL51是Keil使用的链接器(Linker),这是命令行的使用格式,一般不用,使用IDE环境,用project管理,有相应的按钮可以实现该功能.;------------------------------------------------------------------------------;; User-defined Power-On Initialization of Memory --- 初始化RAM单元;; With the following EQU statements the initialization of memory; at processor reset can be defined:;; ; the absolute start-address of IDATA memory is always 0 IDATALEN EQU 80H ; the length of IDATA memory in bytes.---根据你选用的芯片可以适当的修改这些值;XDATASTART EQU 0H ; the absolute start-address of XDATA memory--以下两项根据目标系统的外设配置和连接自己修改XDATALEN EQU 0H ; the length of XDATA memory in bytes.;PDATASTART EQU 0H ; the absolute start-address of PDATA memoryPDATALEN EQU 0H ; the length of PDATA memory in bytes.;; Notes: The IDATA space overlaps physically the DATA and BIT areas of the ; 8051 CPU. At minimum the memory space occupied from the C51; run-time routines must be set to zero.;------------------------------------------------------------------------------;; Reentrant Stack Initilization --注意的再入堆栈的方向区别的芯片自带的堆栈的生长方式,自顶向下生长的!而SP是是自底向上的!; --且再入堆栈是由编译器自己管理的,一般不必去关心,只是在有再入函数的时候,根据函数的存储器模式使用相应的RAM空间做为再入堆栈。

C51和A51函数互调

C51和A51函数互调
4、在"xx.c"中加入汇编代码#pragma ASM
;Assembler Code Here
#pragma ENDASM
C51和A51函数互调.files
2根据选择的编译模式把相应的库文件象加xxc一样加入工程中并放在xxc下面如smail模式下选择keilc51libc51slib加入工程中如果要进行浮点运算把keilc51libc51fpllib也加入工程中
1、把"xx.c"加入工程中,右击"xx.c"选择“options for file"xx.c"选择“Generate Assembler SRC File”和“Assemble SRC File”打上黑勾有效;
2、根据选择的编译模式,把相应的库文件象加"xx.c"一样加入工程中并放在"xx.c"下面,如smail模式下选择"keil\c51\lib\c51s.lib"加入工程中,如果要进行浮点运算把&##34;也加入工程中。
3、在"xx.c"头文件中加入优化:比如#pragma OT(4,speed)

浅谈KEIL-C51

浅谈KEIL-C51

学习C51有一段时间了,是时候做个小结。

一存储类型code:相当const 用来存储常量/程序(16位);data:片内低128字节用来存储变量速度最快(8位);idata:片内高128字节用来存储变量(8位);xdata:片外512字节用来存储变量(16位);pdata:片外低256字节用来存储变量(16位);bdata:内部位地址空间用来存储位变量。

(个人理解:既然data是idata的子集,那两者执行速度应该是一样,同理pdata与xdata也是一样)二寄存器变量(register)一般将使用频率较高的变量设置为register类型,作用是给编译器推荐几个register变量,被推荐的变量是否能真正成为寄存器变量,最终还是由编译器决定,貌似这个变量定义并无实际意义。

三自动(auto)变量定义时必须赋初值,否则会是一个不确定值。

四 C51如何节省空间1 尽量不调用C的函数库;2 尽量少用浮点数和算法;3 尽量减少重复;4 优化程序的结构与逻辑,减少多余的代码。

(一般这种情况是对于小型程序而言,1、2就不必考虑了,毕竟个人精力有限。

查找3、4就能省下不少空间,能解决问题就行了,再不行就换IC了,遇到这种问题不必纠结。

)五中断定义中断函数如下void timer1() interrupt 3 using 1{…………}注:在上述中,建议不要加using选项。

因为using是指定寄存器的组数。

C51中断程序编写要求:1 中断函数不能进行参数传递,否则,将导致编译出错2 中断中,不能包含任何参数声明,否则,将导致编译出错。

3 中断函数没有返回值,如果企图定义一个返回值将得到不正确的结果,因些建议在定义中断函数的时将其定义为void类型,明确说明没有返回值。

4 任何情况下都不能直接调用中断函数,否则会主生编译出错。

5 如果中断函数中用到了浮点运算,必须保存浮点寄存器的状态。

当没有其它的程序执行浮点运算时(即只有中断中用到浮点运算),可以不用保存。

汇编中调用C51函数

汇编中调用C51函数

汇编中调用C51函数摘要本文讨论如何在Franklin C51和A51的编程过程中,实现C函数和汇编子程序的互相调用。

内容涉及C51函数及其相关段的命名规则,调用中两种参数的传递规则,最后,用两个具体的编程实例,说明C函数和汇编子程序相互调用方法。

关键词函数名段参数传递1 引言C语言是一种编译型程序设计语言,它兼顾了多种高级语言的特点,并可以调用汇编语言的子程序。

用C语言设计开发微控制器程序已成为一种必然的趋势。

Franklin C51是一种专门针对Intel 8051系列微处理器的C开发工具,它提供了丰富的库函数,具有很强的数据处理能力,编程中对8051寄存器和存储器的分配均由编译器自动管理,因而,通常用C51来编写主程序。

然而,有时也需要在C程序中调用一些用汇编A51编写的子程序。

例如,以前用汇编语表1 C51中函数名的转换言编写的子程序、要求较高的处理速度而必须用更简练的汇编语言编写的特殊函数或因时序要求严格而不得不使用灵活性更强的汇编语言编写的某些接口程序。

另一方面,在以汇编语言为主体的程序开发过程中,如果涉及到复杂的数学运算,往往需要借助C语言工具所提供的运算库函数和强大的数据处理能力,这就要求在汇编中调用C函数。

本文所涉及的内容,正是讨论如何在Franklin C51和A51的编程过程中,实现C函数和汇编子程序的互相调用。

2 Franklin C51和A51接口所涉及的几个主要问题2.1 C51函数名的转换及其命名规则C51程序模块编译成目标文件后,其中的函数名依据其定义的性质不同会转换为不同的函数名,因此,在C和汇编程序的相互调用中,要求汇编程序必须服从这种函数名的转换规则,否则,将无法调用到所需的函数或出现错误。

C51中函数名的转换规则如表1所列。

2.2 C51函数及其相关段的命名规则一个C51源程序模块被编译后,其中的每一个函数以“?PR?函数名?模块名”为名的命名规则被分配到一个独立的CODE段。

A51与C51变量转换

A51与C51变量转换

***********A51与C51变量转换************* ?PR?int0?0001 SEGMENT CODE?PR?main?0001 SEGMENT CODE?C_INITSEG SEGMENT CODE?DT?0001 SEGMENT DA TA EXTRN CODE (?C_STARTUP)PUBLIC intcountPUBLIC mainPUBLIC int0RSEG ?DT?0001intcount: DS 2RSEG ?C_INITSEGDB 002HDB intcountDW 00000H; #include<reg52.h>; #define LED P2; #define count 50000; #define TH_M1 (65536-count)/256; #define TL_M1 (65536-count)%256; int intcount=0;CSEG AT 0000BHLJMP int0; void int0(void) interrupt 1 using 0RSEG ?PR?int0?0001USING 0int0: PUSH ACC; SOURCE LINE # 7; { ; TH0=TH_M1;; SOURCE LINE # 9MOV TH0,#03CH; TL0=TL_M1;; SOURCE LINE # 10MOV TL0,#0B0H; if(++intcount==5); SOURCE LINE # 11INC intcount+01HMOV A,intcount+01HJNZ ?C0006INC intcount?C0006: XRL A,#05HORL A,intcountJNZ ?C0002; { ; SOURCE LINE # 12; intcount=0; ; SOURCE LINE # 13 MOV intcount,AMOV intcount+01H,A; LED^=0XFF; ; SOURCE LINE # 14 XRL P2,#0FFH; } ; SOURCE LINE # 15; } ; SOURCE LINE # 16?C0002: POP ACCRETI; END OF int0; void main(void)RSEG ?PR?main?0001main: ; SOURCE LINE # 18; { ; SOURCE LINE # 19; IE=0X82; ; SOURCE LINE # 20MOV IE,#082H; TMOD=0X01; ; SOURCE LINE # 21 MOV TMOD,#01H; TH0=TH_M1; ; SOURCE LINE # 22 MOV TH0,#03CH; TL0=TL_M1; ; SOURCE LINE # 23 MOV TL0,#0B0H; TR0=1; ; SOURCE LINE # 24SETB TR0; LED=0XF0; ; SOURCE LINE # 25MOV P2,#0F0H?C0003:; while(1) ; SOURCE LINE # 26; { ; SOURCE LINE # 27; } ; SOURCE LINE # 29SJMP ?C0003; END OF main***********************?PR?int0?0001 SEGMENT CODE ******* ?PR?main?0001 SEGMENT CODE?C_INITSEG SEGMENT CODE?DT?0001 SEGMENT DA TAEXTRN CODE (?C_STARTUP) PUBLIC intcount PUBLIC main PUBLIC int0RSEG ?DT?0001intcount: DS 1RSEG ?C_INITSEGDB 001HDB intcountDB 000H; #include<reg52.h>; #define LED P2; #define count 50000; #define TH_M1 (65536-count)/256; #define TL_M1 (65536-count)%256; char intcount=0;CSEG AT 0000BHLJMP int0; void int0(void) interrupt 1 using 0RSEG ?PR?int0?0001USING 0int0: PUSH ACCPUSH PSW ; SOURCE LINE # 7 ; {; TH0=TH_M1; ; SOURCE LINE # 9MOV TH0,#03CH; TL0=TL_M1; ; SOURCE LINE # 10MOV TL0,#0B0H; if(++intcount==5) ; SOURCE LINE # 11 INC intcountMOV A,intcountCJNE A,#05H,?C0002; { ; SOURCE LINE # 12; intcount=0; ; SOURCE LINE # 13 MOV intcount,#00H; LED^=0XFF; ; SOURCE LINE # 14 XRL P2,#0FFH; } ; SOURCE LINE # 15; } ; SOURCE LINE # 16?C0002: POP PSWPOP ACCRETI; END OF int0 *********************?PR?int0?0001 SEGMENT CODE ***** ?PR?main?0001 SEGMENT CODEEXTRN CODE (?C_STARTUP)PUBLIC mainPUBLIC int0; #include<reg52.h>; #define LED P2; #define count 50000; #define TH_M1 (65536-count)/256; #define TL_M1 (65536-count)%256CSEG AT 0000BHLJMP int0; void int0(void) interrupt 1 using 0RSEG ?PR?int0?0001USING 0int0: PUSH ACCPUSH PSWUSING 0MOV PSW,#00H; SOURCE LINE # 7; {; char intcount=0;; SOURCE LINE # 9 ;---- Variable 'intcount?040' assigned to Register 'R7' ---- CLR AMOV R7,A; TH0=TH_M1; ; SOURCE LINE # 10 MOV TH0,#03CH; TL0=TL_M1; ; SOURCE LINE # 11 MOV TL0,#0B0H; if(++intcount==5) ; SOURCE LINE # 12 INC R7MOV R6,#01HINC ACJNE A,#05H,?C0002; { ; SOURCE LINE # 13; intcount=0; ; SOURCE LINE # 14 DEC R7; LED^=0XFF; ; SOURCE LINE # 15 XRL P2,#0FFH; } ; SOURCE LINE # 16; } ; SOURCE LINE # 17?C0002: POP PSWPOP ACCRETI; END OF int0 ***************?PR?int0?0001 SEGMENT CODE *****?DT?int0?0001 SEGMENT DATA OVERLAY ABLE ?PR?main?0001 SEGMENT CODE EXTRN CODE (?C_STARTUP) PUBLIC mainPUBLIC int0RSEG ?DT?int0?0001?int0?BYTE:intcount?040: DS 1; #include<reg52.h>; #define LED P2; #define count 50000; #define TH_M1 (65536-count)/256 ; #define TL_M1 (65536-count)%256 CSEG AT 0000BHLJMP int0; void int0(void) interrupt 1 using 0 RSEG ?PR?int0?0001USING 0int0: PUSH ACCPUSH PSW; SOURCE LINE # 7 ; {; char intcount;; TH0=TH_M1;; SOURCE LINE # 10MOV TH0,#03CH; TL0=TL_M1; ; SOURCE LINE # 11MOV TL0,#0B0H; if(++intcount==5) ; SOURCE LINE # 12 INC intcount?040MOV A,intcount?040CJNE A,#05H,?C0002; { ; SOURCE LINE # 13; intcount=0; ; SOURCE LINE # 14MOV intcount?040,#00H; LED^=0XFF; ; SOURCE LINE # 15 XRL P2,#0FFH; } ; SOURCE LINE # 16; } ; SOURCE LINE # 17?C0002: POP PSWPOP ACCRETI; END OF int0 ***************************?PR?int0?0001 SEGMENT CODE ?PR?main?0001 SEGMENT CODE ?DT?0001 SEGMENT DA TA EXTRN CODE (?C_STARTUP)PUBLIC intcountPUBLIC mainPUBLIC int0RSEG ?DT?0001intcount: DS 1; #include<reg52.h>; #define LED P2; #define count 50000; #define TH_M1 (65536-count)/256; #define TL_M1 (65536-count)%256; char intcount;CSEG AT 0000BHLJMP int0; void int0(void) interrupt 1 using 0RSEG ?PR?int0?0001USING 0int0: PUSH ACCPUSH PSW; SOURCE LINE # 7; {; TH0=TH_M1; ; SOURCE LINE # 10 MOV TH0,#03CH; TL0=TL_M1; ; SOURCE LINE # 11 MOV TL0,#0B0H; if(++intcount==5); SOURCE LINE # 12INC intcountMOV A,intcountCJNE A,#05H,?C0002; { ; SOURCE LINE # 13; intcount=0; ; SOURCE LINE # 14 MOV intcount,#00H; LED^=0XFF; ; SOURCE LINE # 15 XRL P2,#0FFH; } ; SOURCE LINE # 16; } ; SOURCE LINE # 17?C0002: POP PSWPOP ACCRETI; END OF int0 *****************?PR?int0?0001 SEGMENT CODE?PR?main?0001 SEGMENT CODE?DT?main?0001 SEGMENT DA TA OVERLAY ABLE ?DT?0001 SEGMENT DATA EXTRN CODE (?C_STARTUP)PUBLIC intcountPUBLIC mainPUBLIC int0RSEG ?DT?main?0001?main?BYTE:j?141: DS 1RSEG ?DT?0001intcount: DS 1; #include<reg52.h>; #define LED P2; #define count 50000; #define TH_M1 (65536-count)/256; #define TL_M1 (65536-count)%256; char intcount;CSEG AT 0000BHLJMP int0; void int0(void) interrupt 1 using 0RSEG ?PR?int0?0001USING 0int0: PUSH ACCPUSH PSW; SOURCE LINE # 7; { ; TH0=TH_M1;; SOURCE LINE # 10 MOV TH0,#03CH; TL0=TL_M1; ; SOURCE LINE # 11 MOV TL0,#0B0H; if(++intcount==5); SOURCE LINE # 12 INC intcountMOV A,intcountCJNE A,#05H,?C0002; { ; SOURCE LINE # 13; intcount=0; ; SOURCE LINE # 14 MOV intcount,#00H; LED^=0XFF;; SOURCE LINE # 15 XRL P2,#0FFH; } ; SOURCE LINE # 16; } ; SOURCE LINE # 17?C0002:POP PSWPOP ACCRETI; END OF int0; void main(void)RSEG ?PR?main?0001main:USING 0; SOURCE LINE # 19; { ; SOURCE LINE # 20; char i,j;; IE=0X82; ; SOURCE LINE # 22 MOV IE,#082H; TMOD=0X01; ; SOURCE LINE # 23 MOV TMOD,#01H; TH0=TH_M1; ; SOURCE LINE # 24 MOV TH0,#03CH; TL0=TL_M1;; SOURCE LINE # 25MOV TL0,#0B0H; TR0=1; ; SOURCE LINE # 26SETB TR0; LED=0XF0;; SOURCE LINE # 27MOV P2,#0F0H?C0003:; while(1) ; SOURCE LINE # 28; { ; SOURCE LINE # 29; i=0; ; SOURCE LINE # 30;---- Variable 'i?140' assigned to Register 'R7' ---- CLR AMOV R7,A; P1=i+j*2;; SOURCE LINE # 31MOV A,j?141ADD A,ACCADD A,R7MOV P1,A; } ; SOURCE LINE # 33SJMP ?C0003; END OF mainEND。

C51中变量和函数的绝对地址定位问题解读

C51中变量和函数的绝对地址定位问题解读

C51中变量和函数的绝对地址定位问题: 1. 变量绝对地址定位1) 在定义变量时使用 _at_ 关键字加上地址就可.e.g.unsigned char idata myvar _at_ 0x40;把变量 myvar 定义在 idata 的 0x40 处, 在 M51 文件中可以找到这麽一行IDATA 0040H 0001H ABSOLUTE表示有变量在 idata 的 0x0040 处绝对地址定位.2) 使用 KeilC 编译器定义绝对地址的变量, 方法待查.2. 函数绝对地址定位1) 在程序中编写一函数 myTestvoid myTest(void){// Add your code here}2) 使用 KeilC 编译器定位绝对地址的函数, 打开 Project -> Options for Target 菜单,选中 BL51 Locate 选项卡, 在 Code: 中输入:?PR?myTest?MAIN(0x4000)把函数 myTest 定位到程序区的 0x4000 处,再次编译就可以了.3) 一次定位多个函数的方法同样地, 在程序中再编写另外一个函数 myTest1void myTest1(void){// Add your code here}在 Options for Target 菜单的 BL51 Locate 选项卡的 Code: 中输入: ?PR?myTest1?MAIN(0x3900), ?PR?myTest?MAIN(0x4000)把函数 myTest1 定位在程序区的 0x3900 处, 把函数 myTest 定义在程序区的 0x4000处,重新编译就可以了.在 M51 文件中可以找到下面的内容:>> 3.obj TO Reader RAMSIZE (256) CODE (?PR?MYTEST1?MAIN (0X3900), ?PR?MYTEST?MAIN (0X4000))3665H 029BH *** GAP ***CODE 3900H 0014H UNIT ?PR?MYTEST1?MAIN3914H 06ECH *** GAP ***CODE 4000H 0014H UNIT ?PR?MYTEST?MAIN4) 函数的调用:程序中直接调用函数的方式就不说明了, 这里重点讲使用函数指针调用绝对地址处的函数的方法.(1) 定义调用的函数原形typedef void (*CALL_MYTEST)(void);这是一个回调函数的原形, 参数为空.(2) 定义相应的函数指针变量CALL_MYTEST myTestCall = NULL;(3) 函数指针变量赋值, 指向我们定位的绝对地址的函数myTestCall = 0x3900;指向函数 myTest1(4) 函数指针调用if (myTestCall != NULL){myTestCall(); // 调用函数指针处的函数 myTest1, 置 PC 指针为 0x3900}检查编译生成的 bin 文件, 到 0x3900 处可以看到 myTest1 的内容, 在 0x4000 处可以看到 myTest 的内容,(5) 其它说明:如果在 0x3000 到 0x3900 的程序空间没有内容时, 把 myTestCall 的地址指针指到0x3800(在 0x3000 到 0x3900 之间) 时, 会从 0x3900 处开始执行.至於在 Load 中调用 AP 中的函数的方法与此类似, 但是相应的参数传递可能要另寻方法.************************************keil C51绝对地址访问在利用keil进行8051单片机编程的时,常常需要进行绝对地址进行访问.特别是对硬件操作,如DA AD 采样 ,LCD 液晶操作,打印操作.等等.C51提供了三种访问绝对地址的方法:1. 绝对宏:在程序中,用“#include<absacc.h>”即可使用其中定义的宏来访问绝对地址,包括:CBYTE、XBYTE、PWORD、DBYTE、CWORD、XWORD、PBYTE、DWORD 具体使用可看一看absacc.h便知例如:rval=CBYTE[0x0002];指向程序存贮器的0002h地址rval=XWORD [0x0002];指向外RAM的0004h地址2. _at_关键字直接在数据定义后加上_at_ const即可,但是注意:(1)绝对变量不能被初使化;(2)bit型函数及变量不能用_at_指定。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

利用全局及外部变量实现C51无参数化调用A51函数摘要: 利用C51全局及外部变量,可实现无参数化调用A51函数,不但避开了传统C51调用A51时繁琐的接口约定,而且把在A51中所用到的变量全部放至C51程序中而不必考虑变量在内存中的位置,使编程更加简洁。

用实例验证了该方法的优越性和有效性。

关键词: C51 A51 汇编语言无参数化调用《电子技术应用》2001年第7期发表苟帅的文章“ASM51无参数化调用C51函数的实现”,阐述了ASM51无参数化调用C51函数的实现原理并给出实例来验证该方法的优越性和可行性。

作者在文章中表明这样一种观点:“利用汇编语言对I/O接口、中断向量及程序空间分配的巨大优势,让程序员对MCS-51内的每一个字节甚至是每一比特(可位寻址空间)全部进行统筹安排,设计好各个程序模块,包括I/O口地址和中断向量地址的处理;同时在具体数据处理、通信等不需要过多与硬件打交道的程序模块中,充分利用C51语言强大高效的编程能力”。

上述观点并没有错,但是对I/O接口、中断向量及程序空间分配及MCS-51内的每一个字节甚至是每一比特(可位寻址空间)全部进行统筹安排,对于大多数程序员来讲恰恰是件不容易的事。

地址空间、栈的起始地址和中断向量地址的分配等,经常顾此失彼,这边可行,那边又出问题。

其实完全可以把上述繁重的工作交给C51去完成,由C51定义各个变量并自动分配各个变量的空间,而集中精力完成所需功能的A51程序设计。

笔者在研制基于C51、插拔式FLASH存储器的无纸记录仪中,就是利用全局及外部变量实现C51无参数化调用A51函数的,取得了很好的效果。

1 C51无参数化调用A51函数的实现原理为了说明其原理,必须理解局部变量、全局变量和外部变量的意义。

局部变量:是在函数内部定义的变量,只在定义它的函数内部有效,仅在使用它时,才为它分配内存单元。

全局变量:又称外部变量,是在函数外部定义的变量,可以为多个函数共同使用,其有效作用范围是从它定义的位置开始直到整个程序文件结束。

若一个全局变量不是在程序文件开始处定义的,但又希望在它的定义点之前的函数中引用该变量,这时应在引用该变量的函数中用关键字EXTERN将其说明为外部变量。

此外如果一个程序模块文件中应用另一个程序模块中定义的变量时,也必须用EXTERN进行说明。

全局变量在整个程序的执行过程中都要占用内存单元。

外部变量(EXTERN):使用存储种类说明符EXTERN定义的变量称为外部变量。

按照缺省规则,凡在所有函数之前,在函数外部定义的变量都是外部变量,定义时可以不写EXTERN说明符。

但是,在一个函数体内说明一个在该函数体外或别的程序模块文件中定义过的外部变量时,则必须使用EXTERN说明符。

一个外部变量被定义之后,它就被分配了固定的内存空间。

外部变量的生存期为程序的整个执行时间。

C语言允许将大型的程序分解为若干个独立的程序模块,各个模块可分别进行编译,然后将它们连接在一起。

如果某个变量需要在所有程序模块文件中使用,只要在一个程序模块中将该变量定义成全局变量,而在其它程序模块中用EXTERN说明该变量是已被定义过的外部变量就可以了。

在C51中定义函数时如果冠以关键字EXTERN即将其明确定义为一个外部函数。

具体方法是:在C51用unsigned char 定义的变量,则在A51中必须对应声明该变量在DATA区;在C51用bit 定义位变量,则在A51中必须对应声明该变量在可位选区。

A51 中用关键字EXTRN表示外部变量,对应关系如下:C51 定义A51 声明unsigned char varible_nameEXTRN DATA(varible_name)bit bit_name EXTRN BIT (bit_name)此外,C51编译器支持在C语言源程序中直接编写8051单片机的中断服务程序,从而减轻采用汇编语言编写中断服务程序的繁琐程度。

C51编译器对函数的定义进行了扩展,增加了一个扩展关键字interrupt。

定义中断服务函数的一般格式为:函数类型函数名(形式参数表) [interrupt n] [using n]关键字interrupt后面的n是中断号,取值为0~31,编译器从8n+3处产生中断向量。

using 后的n取值为0~3,表示选择那一组寄存器。

2 C51无参数化调用A51函数的实例研制的无纸记录仪实现的功能是:记录仪有4个通道,每秒采样1次,每个通道每次采样需记录两个字节,根据各个通道的转储速率(从1~255可设定),把采样的数据写入外部数据存储器62256,一旦满512字节,就把512字节一次性写入FLASH MEMORY中。

主程序用C51编程,每秒中断采样程序由A51实现。

主程序的主要工作是:把采集到的数据按曲线、棒图和数据送到液晶显示器显示;根据一定的条件查询数据等。

中断程序就是采样4个通道,把在A51采样所写入的单元及所需要的一些位变量在C51定义为全局变量,在A51中这些变量声明为外部变量,在C51调用A51的中断函数必须声明为外部函数。

这样就实现了C51无参数化调用A51。

C51主程序如下:程序名为 MYMAIN.C#include#include#include#define uchar unsigned char//以下3个函数在采样程序模块ZTDJLY.A51中,名称就是标号extern void sample(); //中断采样程序extern void initial(); //AD7715初始化extern void res_ram(); //外部存储器62256地址指针复位//以下变量为采样程序模块ZTDJLY.A51中所用到的变量bit write_alw,vz_bit,fs_bit; //write_alw=1,写入FLASH允许,vz_bit,fs_bit 是零标定及满刻度标定的标志uchar sample_w[12]; //4路连零点采样值,每路2个字节共10个字节,另外2个字节作写入62256前把采样值添加通道号处理所需单元uchar xdata *ram_point; //62256的指针uchar sa_p,sa_vzt[8]; //sa_p为采样当前路,sa_vzt[8]为4路的零点uchar sa_timer[8]={1,1,1,1,1,1,1,1}; //4路的转储速率,前为子,后为母本 ......void disp_int0() interrupt 0 using 2 //用中断0,使用寄存器2组{sample();//C51无参数化调用A51函数}main(){...initial();while(1) //主循环{……}}A51程序如下:NAME ztdjly.a51ram_cs bit 090h.2 ;p1.2 ;62256 片选b373 bit 090h.1 ;p1.1bit_dog bit 0b0h.4 ;p3.4;以下3个是AD7715的引脚定义data_bit bit 0a0h.6 ;P2.6adsclk_bit bit 0b0h.5 ;P3.5drdy_bit bit 0b0h.3 ;p3.3?PR?sample?SAMPLE SEGMENT CODE ;定义程序代码段EXTRN DATA (sample_w) ;声明外部变量,在C51程序定义分配在DATA区EXTRN DATA (sa_p)EXTRN DATA (ram_point)EXTRN DATA (sa_timer)EXTRN DATA (sa_vzt)EXTRN BIT (write_alw) ;声明外部变量,在C51程序定义分配在可位寻址区EXTRN BIT (vz_bit)EXTRN BIT (fs_bit)PUBLIC sample ;公用符号说明PUBLIC initial,res_ramRSEG ?PR?sample?SAMPLEusing 0initial:;AD7715初始化,该标号即是C51定义外部函数;initial()的入口地址……ret;sample: ;采样程序,该标号即是C51定义外部函数;sample()的入口地址……ret……res_ram: ;62256指针复位,该标号即是C51定义外部函数res_ram()的入口地址……retend在研制无纸记录仪之前,作者用MASM51开发了其他仪表,采样部分有些相同,因此这次A51采样程序并不是重新编写,而是把以前的MASM51程序按A51的规则来修改。

以前ASM 的程序(数据段部分)如下所示:bseg;write_alw bit 00h;vz_bit bit 01hfs_bit bit 02hce_bit bit p1.6ram_cs bit p1.2b373 bit p1.1bit_dog bit p1.4data_bit bit P2.6sclk_bit bit P3.4drdy_bit bit p3.3ends;dsegorg 08horg 28hsample_w: ds 12sa_p: ds 1 ;当前通道号ram_point: ds 2sa_timer: ds 8 ;0,1,2,3 每通道两字节sa_vzt: ds 8 ;0,1,2,3 vzends;csegorg 0hbegin: retiorg 03hlcall sampleretisample:……ends从上述程序可以看出,在采样程序中所用到的变量并不少,定义在数据存储区28H单元起始的空间中。

这样安排,对汇编程序没什么问题,但与C51主程序连接的时候,并不一定好,很多时候碰到地址空间溢出的问题(C51主程序编译连接后将近16K字节,CPU采用ATMEL公司的AT89C55WD)。

如何调整这些变量的分配,并非容易。

另外,C51随时要用到采样单元sample_w[12]的值,如果采用一般的参数传递,将是相当复杂而繁琐的;而采用全局及外部变量实现C51无参数化调用A51函数,极大简化了这些工作,大大提高了工作效率。

本文第2部分的程序,采用Keil 公司的Keil C V6.10集成开发工具包完成。

所研制的无纸记录仪已经通过福建省科技厅的鉴定。

相关文档
最新文档