在单片机应用系统设计中,过去主要采用汇编语言开发程序。 汇编语言编写的程序对单片机硬件操作很方便,编写的程序代码短,效率高,但系统设计的周期长,可读性和可移植性都很差。C语言程序开发是近年来单片机系统开发应用所采用的主要开发方式之一,C 语言功能丰富、表达能力强、使用灵活方便、开发周期短、可读性强、可移植性好。但是,采用C 语言编程还是存在着如对硬件没有汇编方便、效率没有汇编高、编写延时程序精确度不高等缺点,因而现在单片机系统开发中经常用到C 语言与汇编语言混合编程技术。混合编程技术可以把C 语言和汇编语言的优点结合起来,编写出性能优良的程序。单片机混合编程技术通常是,程序的框架或主体部分用C 语言编写,对那些使用频率高、要求执行效率高、延时精确的部分用汇编语言编写,这样既保证了整个程序的可读性,又保证了单片机应用系统的性能。

  1、混合编程的基本方式
  C 语言与汇编语言混合编程通常有两种基本方法:在C 语言中嵌入汇编程序和在C 语言中调用汇编程序
  1.1 在C51 中嵌入汇编程序
  在C51 中嵌入汇编程序主要用于实现延时或中断处理,以便生成精练的代码,减少运行时间。嵌入式汇编通常用在当汇编函数不大,且内部没有复杂的跳转的时候。在单片机C 语言程序中嵌入汇编程序是通过C51 中的预处理指令# pragma asm/endasm 语句实现,格式如下:

# pragma ASM
;汇编程序代码
# pragma ENDASM

  通过# pragma asm 和# pragma endasm 告诉C51 编译器它们之间的语句行不用编译成汇编程序代码。

  1.2  在C51 中调用汇编程序
  在C51 中调用汇编程序的方法应用较多,C 模块与汇编模块的接口较简单,分别用C51 与A51 对源程序进行编译,然后用L51 将obj 文件连接即可,关键问题在于C 函数与汇编函数之间的参数传递和得到正确返回值,以保证模块间的数据交换。
  2 C51 与汇编程序的参数传递
  在C51 中嵌入汇编程序或调用汇编程序,其参数传递的过程是不一样的。
  2.1 在C51 中嵌入汇编程序的参数传递
  对于在C 语言程序中通过# pragma asm 和#pragma endasm 嵌入的汇编程序,C51 编译器在编译时只是将当中的汇编程序不编译,而不做其他任何处理,因此不存在函数调用时的参数传递和返回值问题。如果要在C 程序中和汇编程序中实现数据传递,可以通过变量或特殊功能寄存器来实现,例如,在C 程序的变量定义部分定义Z 变量,在C 语言程序和汇编程序中共同访问Z 变量,这样,C 语言程序可以通过Z 变量把参数传递给汇编程序,汇编程序可以通过Z 变量把参数返回给C语言程序。
  2.2 在C51 中调用汇编程序的参数传递
  在C51 中调用汇编程序是通过函数调用的形式来实现的。由于C51 程序函数有明确的参数和返回值约定,因此在C51 中调用汇编程序进行参数传递时都必须严格遵守C51 函数的参数和返回值相关约定。
  在C51 中调用汇编程序进行参数传递关键在于要弄清C51 函数的参数传递规则。在C51 中调用汇编程序进行参数传递的方式有两种:一种是通过寄存器传递参数;一种是通过固定存储区传递
  2.2.1 通过寄存器传递参数。
  Franklin C51 规定调用函数最多可通过51 单片机的工作寄存器传递3 个参数,余下的通过固定存储区传递。 可以用“NOREGPARMS”命令取消用寄存器传递参数,如果用寄存器传递参数取消或参数太多,参数通过固定存储区传递。用寄存器传递参数的函数,在生成代码时被Cx51 编译器在函数名前加上一个下划线“_”的前缀,在固定存储区传递参数的函数则没有下划线。不同的参数用到的寄存器不一样,不同的数据类型用到的寄存器也不同。 通过寄存器传递的参数如表1 所示。

 表1 中,int 型和long 型数据传递时高位数据在低位寄存器中,低位数据在高位寄存器中;float型数据满足32 位的IEEE 格式,指数和符号位在R7 中;通用指针存储类型在R3 中,高位在R2 中。函数参数传递举例情况如表2 所示。

  2.2.2  通过固定存储区传递。
  用固定存储区传递参数给汇编程序,参数段首地址用段名“ ? function-name ? BYTE”和“ ?function-name ? BIT”保存,function-name 为函数的名称,其中“, ? function-name ? BIT”保存位参数段首地址,“ ? function-name ? BYTE”保存别的参数段首地址,即使通过寄存器传递参数,参数也将在这些段中分配空间,参数按声明的先后在每个段中顺序保存。
  用做参数传递的固定存储区可在内部数据区或外部数据区,这由存储模式决定。 Small 模式的参数段用内部数据区,Compact 和Large 模式用外部数据区。
  2.2.3  函数返回值。
  函数返回值通常用寄存器传递,表3 列出了可能的返回值和所用的寄存器。

  3 C51 中嵌入汇编程序的实现方法
  通常,在C51 程序中嵌入汇编程序的处理方法如下:
第一步,在C 文件中以如下方式嵌入汇编程序。

# pragma ASM
;汇编程序
# pragma ENDASM

第二步,在keil C51 软件的Project 窗口右键单击嵌入汇编程序的C 文件,选择“Options for ?”,点击右边的“Generate Assembler SRC File”和“Assemble SRC File”,使检查框由灰色变成黑色(有效) 状态。
第三步,根据选择的编译模式,把相应的库文件(如Small 模式时, 是Keil \ C51 \ Lib \ C51S。Lib) 加入工程中, 该文件必须作为工程的最后文件。 库文件与编译模式的关系如下:
C51S.LIB - 没有浮点运算的Small model
C51C.LIB - 没有浮点运算的Compact model
C51L.LIB - 没有浮点运算的Large model
C51FPS.LIB - 带浮点运算的Small model
C51FPC.LIB - 带浮点运算的Compact model
C51FPL.LIB - 带浮点运算的Large model
第四步,编译,即可生成目标代码。

  4  C51 中调用汇编程序的实现方法
  为了能够在C 语言中调用汇编程序,要求汇编程序的编写必须符合C 语言的相关命名规则。
  C51 程序在调用汇编程序时,除了前面参数传递的相关规则外,函数及其相关段也需要满足一定的规则。
  一个C51 源程序模块被编译后,其中的每一个函数以“ ? PR ? 函数名? 模块名”为名的命名规则被分配到一个独立的CODE 段。 例如,如果模块“FUNC51”内包含一个名为“func”的函数, 则其CODE 段的名字是“ ? PR ? FUNC ? FUNC51”,如果函数中还包含有data 和bit 对象的局部变量,编译器将按“ ? 函数名? BYTE 和? 函数名? BIT”命令规则建立一个data 和bit 段,它们代表所要传递参数的起始位置,其偏移值为零。 段内代码与数据定义也遵循一定的规则。这些段是公开的,它们的地址可被其他模块访问。另外,这些段被编译器赋予“OVERLAYABLE”标志,其可被L51 连接P定位器做覆盖分析。
  下面是一个简单的C51 程序编译时形成的汇编程序。
  C 语言源程序如下:

#define uchar unsigned char
uchar max(uchar x ,uchar y)
{
    uchar z ;
    z = (x > = y) ? x :y ;
    return(z) ;
}

  汇编后形成的SRC 文件(只须扩展名改为.a51就变成汇编程序) 如下:

NAME  A1              ;定义模块名称
?PR?_max?  A1  SEGMENT CODE   ;定义程序代码
  PUBLIC  _max          ;定义公共符号
; #define uchar unsigned char
;uchar max(uchar x ,uchar y)
  RSEG    ?PR?_max?  A1      ;程序代码段
_max:                 ;起始地址
  USING  0
    ;SOURCE LINE # 2
; ??Variable ’y ? 041’assigned to Register ’R5’??
; ??Variable ’x ? 040’assigned to Register ’R7’??
; {
    ; SOURCE LINE # 3
; uchar z ;
; z = (x > = y) ? x :y ;
    ; SOURCE LINE # 5
  MOV  A, R7          ;R7 中为第二个字节参数
  CLR  C
  SUBB  A, R5          ;R5 中为第一个字节参数
  JC   ?C0001
  SJMP  ?C0002
?C0001:
  MOV  R7, AR5         ;R7 中为返回值
?C0002:
; ??Variable ’z ? 042’assigned to Register ’R7’??
; return(z) ;
    ; SOURCE LINE # 6
    ; SOURCE LINE # 7
; }
?C0003:
  RET
; END OF - max
  END

  可以看出,要编写为C51 调用的汇编程序,除了参数必须按前面规定的寄存器或存储器传送外,程序格式也有相应的规则。 这些规则比较繁琐,在实际处理中往往按下面方式处理:
第一步,先用C 语言程序编写出程序框架,如文件名为a1.c (注意参数) 。
第二步,在keil C51 的Project 窗口中用右键单击该C 语言文件,在右键菜单中选择“Options for?”, 点击右边的“Generate Assembler SRCFile”和“Assemble SRC File”,使检查框由灰色变成黑色(有效) 状态。
第三步,根据选择的编译模式,把相应的库文件(如Small 模式时,是Keil \ C51 \ Lib \ C51S.Lib) 加入工程中,该文件必须作为工程的最后文件。 库文件与编译模式的关系如前面所述。
第四步,编译后将会产生一个SRC 的文件,将这个文件扩展名改为ASM。 这样就形成了可供C51程序调用的汇编程序。 随后可在该文件的代码段中加入所需指令代码。
第五步,将该汇编程序与调用它的主程序一起加到工程文件中,这时工程文件中不再需要原来的C 语言文件和库文件,主程序只需要在程序开始处用EXTERN 对所调用的汇编程序中的函数作声明,主程序中可调用汇编程序中的函数。

  5 总结
  在单片机C 语言与汇编语言混合编程中,主要应注意相应的使用方法和参数传递。在单片机应用系统设计中,采用单片机C 语言与汇编语言混合编程的方法,既可提高程序开发的效率,又可以很方便的操作单片机硬件;既能保证整个程序的可读性,又能保证对硬件访问的精确性。适当的用好C 语言与汇编语言混合编程技术,就可以开发出性能较好的单片机应用程序。

单片机C 语言与汇编语言混合编程的更多相关文章

  1. keil C语言与汇编语言混合编程

    C与汇编混合编程主要有以下几种:(1)C语言中嵌入汇编(2)无参数传递的函数调用(3)有参数传递的函数调用 一.C语言中嵌入汇编 1.在 C 文件中要嵌入汇编代码片以如下方式加入汇编代码: #prag ...

  2. C语言与汇编语言混合编程实验

    混合编程方法: 模块链接法 汇编指令嵌入法 1: 模块链接法则 模块链接法是指分别用汇编语言和C语言实现独立的模块(或子程序),再用链接程序把各模块生成的obj文件连接成一个可执行程序. 1:C语言调 ...

  3. C51与汇编语言混合编程

    函数内部混合编程 若想在C语言函数内部使用汇编语言,应使用以下Cx51编译器控制命令: #pragma asm ; Assembly code #pragma endasm  功能作用:asm和end ...

  4. c语言环境初始化&c语言和汇编混合编程

    bootloader通常会分为两个阶段:第一阶段采用汇编语言来编写,主要是一些核心的初始化工作(内存,时钟的初始化),第二阶段使用C语言来编写,主要是它会完成一些板载硬件的初始化(串口,网口)然后其启 ...

  5. arm:c语言和汇编混合编程

    仅作演示. 1.C和汇编可相互调用,汇编子函数格式参考 汇编:普通的函数调用的汇编代码解析 http://www.cnblogs.com/mylinux/p/4139972.html 本文演示了 : ...

  6. C语言调用Python 混合编程

    导语 Python有很多库,Qt用来编写界面,自然产生C++调用Python的需求.一路摸索,充满艰辛 添加头文件搜索路径,导入静态库 我的python头文件搜索路径:C:\Python27amd64 ...

  7. 【转】VxWorks中高精度实时时钟的实现及C语言汇编混合编程

    最近一个项目中需要在VxWorks下使用一个高精度实时时钟,要求精度为1ms,溢 出时间大于5小时.VxWorks提供系统时钟,该时钟在操作系统启动后开始计数,精度为1个tick,可以通过tickGe ...

  8. Java语言与C语言混合编程(2)--在Java中调用C语言本地库

    在上一篇文章中介绍了Java语言中的native关键字,以及Java语言调用C语言的编译生成本地动态链接库(DLL)实现加法运算的小例子,本文通过一个更加详细的例子,深入讲解Java语言调用C语言的函 ...

  9. 批处理与python代码混合编程的实现方法

    批处理可以很方便地和其它各种语言混合编程,除了好玩,还有相当的实用价值, 比如windows版的ruby gem包管理器就是运用了批处理和ruby的混合编写, bathome出品的命令工具包管理器bc ...

随机推荐

  1. 复制文件时,如何显示进度条(使用TFileStream一点一点读,或者使用BlockRead)

    procedure mycopyfile(sourcef,targetf:string;i:integer); var FromF,ToF:file; NumRead,NumWritten:Integ ...

  2. C++11 in Qt5

    本文转载自:http://woboq.com/blog/cpp11-in-qt5.html   C++11 in Qt5 Posted by Olivier Goffart on 11 June 20 ...

  3. SQL SERVER 自带系统存储过程分类

    目录存储过程 用于实现 ODBC 数据字典功能,并隔离 ODBC 应用程序以使其不受基础系统表更改的影响. 变更数据捕获存储过程 用于启用.禁用.或报告变更数据捕获对象. 游标存储过程 用于实现游标变 ...

  4. iOS 9之Advanced Touch Input(高级触摸输入)

    金田 今天要讲的主题是iOS 9高级触摸输入,更准确地讲,是在iOS9上如何减少触摸输入到屏幕显示的延迟程度,此次将分 低延迟渲染(iOS9 渲染性能优化)和 触摸点方案改进 两个方面来介绍. 低延迟 ...

  5. wireshark保存部分报文的方法

    抓包时采用下列两种命令: tcpdump –s 0 –i eth0 host IP1 and IP2 and port 5060 and 5080 –v –w file1.pcap 与 tcpdump ...

  6. redis 学习笔记三(队列功能)

    Redis队列功能介绍 List 常用命令: Blpop删除,并获得该列表中的第一元素,或阻塞,直到有一个可用 Brpop删除,并获得该列表中的最后一个元素,或阻塞,直到有一个可用 Brpoplpus ...

  7. Ubuntu 无线连接能上网,但是有线连接不能上

    这两天装Ubuntu,遇到小问题.最头疼的还是上网,过去我装了Ubuntu时,都是插上网线就能直接上网,这次就不行了. 我刚点开一个网页,接下来点就不能上了,但是无线连接就可以正常上网. 我在一个论坛 ...

  8. (转)js正则表达式之中文验证

    今天做表单提交的输入框条件验证,验证是否包含中文:网上搜了一圈基于js正则表达式的验证基本不好用,而且大多都是出自一两篇原文的转帖!到底什么才是拿来主义呢.根据搜索结果,本文取精华,告诉大家一个好用的 ...

  9. Asp.Net中JSON的序列化和反序列化-----JavaScriptSerializer ,加上自己工作心得

    在工作中和手机通信用到web服务和javascriptSerializer,返回json数据,供手机端调用,一开始返回的数据是一大堆,比如 [{"word_picture9":&q ...

  10. 转载 Silverlight实用窍门系列:1.Silverlight读取外部XML加载配置---(使用WebClient读取XAP包同目录下的XML文件))

    转载:程兴亮文章,地址;http://www.cnblogs.com/chengxingliang/archive/2011/02/07/1949579.html 使用WebClient读取XAP包同 ...