第3 节 汇编工程文件组织与第一个汇编入门程序
推荐给好友
打印
加入收藏
更新于2008-07-25 09:42:31

基于汇编语言编程给人的第一感觉是难,相对于其它的高级语言编程,汇编语言在编程的直观性、编程效率等方面有所欠缺,但针对资源相对较少的单片机以及时序要求严格的硬件接口编程,掌握汇编语言还是必不可少的。使用汇编语言编程是基本功,学习和掌握汇编语言编程可以增加编程者的“内力”,为以后使用高级语言编程打下坚实的基础。
在本书教学资料中提供的MT-IDE开发环境中,汇编程序是通过工程的方式组织起来的。汇编工程通常包含芯片相关的程序文件、硬件对象控制文件、通用程序文件和主程序文件等。下面将结合第一个08汇编工程实例“PrgFrame.prj”,讲解上述的文件概念,并详细分析08汇编工程的组成、汇编程序文件的编写规范、软硬件模块的合理划分等。读者若能认真分析与实践第一个汇编实例程序,可以达到由此入门的目的。
本书所有实例源程序在配套的提供教学资料中。第一个汇编工程实例的源程序在教学资料的“MC08Ex2007\GP32\GP32S\S01_简单IO及程序框架”文件夹中。
图4-5 PrgFrame.prj工程文件组织图
第一个08汇编工程实例“PrgFrame.prj”的功能是通过通用I/O口控制发光二极管闪烁。图4-5给出了这个工程的文件组织图。

 1.芯片相关的程序文件

芯片相关的程序文件是依赖于具体的芯片型号的,在实际编程过程中,这些文件内容较为固定,一般不作改动,或只对个别条目进行修改。
PrgFrame.prj工程中芯片相关的程序文件包括:GP32映像寄存器名定义头文件(GP32ASM.h)、芯片初始化文件(MCUinit.s)和中断处理子程序与中断向量表文件(Vectors08.s)。

(1) 芯片头文件GP32ASM.h
本文件为MC908GP32 MCU映象寄存器名定义头文件,它是GP32的汇编工程的映像寄存器名与其地址的对应表,具体内容参见附录C.1。本文件和具体MCU相关,同时为了程序的通用性,本文件内容不宜更改。实际程序使用.include "GP32ASM.h"语句将"GP32ASM.h"文件包含到源程序中。例如,有了这个头文件,对“D口数据寄存器”读出操作,可用“LDA PTD”取代“LDA $0003”,这样更容易理解。

(2) 芯片初始化文件MCUinit.s
芯片初始化文件,主要包含与芯片有关的设置,如总线频率、看门狗模块的使用等。
本实例程序中包含的MCUinit,是GP32系统初始化程序,它将对CONFIG1、CONFIG2进行设置,并对PLL进行编程,由外部晶振f=32.768KHz,得到内部总线时钟fBUS=2.4576MHz。这部分内容对初学者较难,请直接使用,本文将在第14章详细阐述。

(3) 中断处理子程序与中断向量表文件Vectors08.s
该文件包含了中断处理程序和中断矢量表。中断处理程序是当中断发生后执行的子程序,中断子程序在返回时需要使用RTI指令。中断矢量表实质是在MCU的矢量区写入一些常量值,这些常量值就是相应的中断处理程序的入口地址。当系统发生中断时,从中断向量表获得相应的中断处理子程序的入口地址,使程序流转向中断服务程序,执行相应的中断处理动作,处理结束后,从中断返回。本工程中只定义了一个复位中断向量。其他中断向量都为isrDummy子程序的入口地址,isrDummy子程序是一个空程序,这表明当发生相应中断时,不作任何处理。如果工程中还需要其他中断,先要在Vectors08.s中定义相应的中断处理子程序,再将该程序的入口地址置于中断向量表的对应位置即可。

//[Vectors08.s]中断处理子程序与中断向量表---------------*
//功能: *
// (1)定义中断处理子程序 *
// (2)放置中断向量表 *
//说明:该文件与芯片具体型号有关 *
// (1)芯片型号MC68HC908GP32 *
// (2)注意本文件内容的顺序不能变动 *
//------------------------------------------------------*

//此处为用户中断处理子程序的存放处

//

//未定义的中断处理子程序,本子程序不能删除
isrDummy::
RTI

//中断矢量表,用户若需开放某中断,可修改下表中的相应项目
//(interrupt service routine,isr 中断处理程序)
.area memory(abs)
.org 0xffdc //中断向量表起始地址(注意:与芯片型号有关)
vectab::
.word isrDummy //时基中断
.word isrDummy //AD转换中断
.word isrDummy //键盘中断
.word isrDummy //SCI发送中断
.word isrDummy //SCI接收中断
.word isrDummy //SCI错误中断
.word isrDummy //SPI发送中断
.word isrDummy //SPI错误中断
.word isrDummy //TIM2溢出中断
.word isrDummy //TIM2通道1输入捕捉/输出比较中断
.word isrDummy //TIM2通道0输入捕捉/输出比较中断
.word isrDummy //TIM1溢出中断
.word isrDummy //TIM1通道1输入捕捉/输出比较中断
.word isrDummy //TIM1通道0输入捕捉/输出比较中断
.word isrDummy //CGM的PLL锁相状态变化中断
.word isrDummy //IRQ引脚中断
.word isrDummy //SWI指令中断
.word MainInit //RESET(见主程序定位处)复位被认为是一种特殊中断

程序中的“.area memory(abs)”是伪指令,用于告诉汇编编译器:interrupt这块区域是不需要重新定位,接下来的“.org 0xffdc”语句指定了interrupt的起始地址为0xffdc,“.word isrDummy”语句是获取标号为isrDummy的程序地址。

2.硬件对象控制文件

MCU 工程编程是面向硬件对象的。PrgFrame.prj工程用通用I/O口控制小灯(LED)闪烁。该工程有针对性的创建了LED.s的源程序文件来专门控制小灯的亮、暗。相应的,也创建了一个同名头文件LED.h,用于小灯控制引脚定义和相关宏定义。这便体现了MCU编程的“面向硬件对象”的特性。而LED.h和LED.s就是硬件对象控制文件。

(1) 小灯头文件LED.h
文件“LED.h”中包含小灯控制引脚宏定义。
//[LED.h]小灯驱动头文件----------------------------------

//小灯控制引脚宏定义
Light_P = PTA //灯(Light)接在PTA口
Light_D = DDRA //相应的方向寄存器
Light_Pin = 1 //所在的引脚

(2) 小灯头文件驱动文件LED.s
文件“LED.s”中包含:
①文件描述:文件中的子程序的索引、硬件对象及其与MCU的连接描述
②头文件:小灯控制所需用到的头文件和小灯控制引脚定义及小灯控制子程序中用到的其他常量声明可全部存放在“LED.h”中,“LED.s”只要包含该头文件即可。
③小灯驱动子程序定义:每个子程序前要有相应的程序描述,包括子程序名,子程序的功能、入口和出口,必要时加上相关说明。
//[LED.s]小灯驱动---------------------------------------*
//本文件包含: *
// (1)LEDinit:定义控制小灯的MCU的I/O引脚为输出 *
// (2)LED_L_A:驱动小灯"亮","暗" *
//硬件连接: *
// (1)本处的小灯是一个发光二极管,由MCU的I/O引脚控制 *
// (2)控制引脚为高电平时,小灯"暗";反之,小灯"亮" *
//------------------------------------------------------*

//小灯驱动头文件
.include "LED.h"

//LEDinit:定义控制小灯的MCU引脚为输出-------------------*
//功能:定义控制小灯的MCU引脚为输出,并使小灯初始为暗 *
//入口:无 *
//出口:无 *
//堆栈深度:2 *
//------------------------------------------------------*
LEDinit::
BSET #Light_Pin,Light_D //令小灯引脚为输出
BSET #Light_Pin,Light_P //初始时,小灯"暗"
RTS

//LED_L_A:驱动小灯"亮","暗"-----------------------------*
//功能:根据A的值控制小灯的亮和暗 *
//入口:A(A = 'L',小灯亮;A = 'A',小灯暗) *
//出口:无 *
//堆栈深度:2 *
//------------------------------------------------------*
LED_L_A::
CMP #'A'
BNE LED_L_A_1
BSET #Light_Pin,Light_P //小灯"暗"
BRA LED_L_A_Exit
LED_L_A_1:
CMP #'L'
BNE LED_L_A_Exit //入口非'L'/'A',程序无响应
BCLR #Light_Pin,Light_P //小灯"亮"
LED_L_A_Exit:
RTS

3.通用程序文件
通用程序文件中可以定义一些与硬件无关、可以在不同工程项目中直接引用的子程序,如内存数据移动、延时以及数据转换子程序等。它还可以是与这些通用子程序文件相对应的头文件。在工程PrgFrame.prj中,有通用子程序文件GeneralFun.s,它定义了延时子程序DelayHX,这个程序不依赖于任何硬件模块,只是完成一个延时动作。如果在另一个工程中需要延时操作时,只要包含GeneralFun.s,就可以直接使用已经定义好的延时子程序了。如果通用子程序文件中还需要包含一些头文件或者宏定义,就可以创建一个通用程序头文件GeneralFun.h,把这些内容放到这个头文件中(本工程并没有用到GeneralFun.h)。
工程PrgFrame.prj中,还有一个比较特殊的文件——总头文件(Includes.h),它也属于通用程序文件。Includes.h用于声明全局变量和包含主程序文件中需要的头文件,宏定义等。它使得主函数文件能够尽量避免改动,结构更加清晰。在Includes.h中声明内存变量,通常称为“开辟内存变量”,内存变量的初始化在主程序开始部分完成。第一个内存变量需用“.ORG”语句定位,随后,按地址从小到大顺序存放。每个内存变量都有固定的内存地址。借用C语言术语,这里所开辟的所有内存变量都是“全局变量”。在Includes.h中也可以进行宏定义。例如,实际应用中,D口是接一个液晶显示器LCD的数据端口,若在总头文件中使用常量定义“LCDData = PTD”,之后LCDData就与PTD完全等同,在编程时可以用LCDData代表LCD的数据端口。这样处理不仅符合嵌入式编程的面向对象思想,而且更有利于程序的移植。如,想改用A口接LCD的数据端口,只要在Includes.h中改变相应的常量定义为“LCDData = PTA”就行了,而程序的其他部分无须任何改动。

(1) 总头文件Includes.h
//[Includes.h]总头文件----------------------------------*
//本文件包含: *
// (1)工程中用到的头文件 *
// (2)有关常量命名 *
//------------------------------------------------------*
.include "GP32ASM.h" //GP32 MCU映像寄存器名定义

RAMstartAddr = $0040 //RAM的起始地址
RAMendAddr = $023F //RAM的结束地址
FlashStartAddr = $8000 //程序开始地址

(2) 通用函数文件GeneralFun.s
//[GeneralFun.s]通用子程序------------------------------*
//本文件包含: *
// (1)DelayHX:延时约为HX*1000时钟周期(T) *
//------------------------------------------------------*

//DelayHX:延时子程序------------------------------------*
//功能:用程序的方法,延时约为HX*1000时钟周期(T) *
//入口:HX(0-65535) *
//出口: 无 *
//堆栈深度:2 + 1 = 3 *
//说明: *
// (1)忽略进入与退出部分指令的执行时间 *
// (2)这种延时方法,实际延时的长短与总线周期有关 *
//------------------------------------------------------*
DelayHX::
PSHA //A进栈(保护寄存器A)
CPHX #0
BEQ DelayHX_Exit
//延时约HX * 1000(T)=========
DelayHX_1:
//延时约200 * 5 = 1000(T)----
LDA #200
DelayHX_2:
NOP //(1T)
NOP //(1T)
DBNZA DelayHX_2 //(3T)
//---------------------------
AIX #-1
CPHX #0
BNE DelayHX_1
//===========================
DelayHX_Exit:
PULA //A出栈(恢复寄存器A)
RTS

以上所涉及到的三类程序文件中,所有的“.s”文件都属于子程序文件。从程序清晰化、分割合理角度出发,把功能相近或者基于某个硬件模块的子程序存储到一个子程序文件中,它们被主程序调用来完成主控流程。

由上述工程实例可以看到,汇编子程序通常由4个部分组成,分别描述如下。

①子程序文件描述:给出子程序文件的功能说明、外部调用的子程序列表及调用时的参数说明、子程序在调用时的注意事项。子程序文件描述应该让调用者很清晰地看到本文件中子程序的调用方法,在不阅读子程序时也能正确地调用子程序。

②头文件:有些子程序文件和某个硬件模块相关,这意味着需要定义一些寄存器或I/O端口的映射。如果这种映射较少,少于5个,可以直接在子程序文件中定义;如果这种映射较多,在 5个或5个以上时,建议新建一个头文件保存这些映射,子程序文件中通过“.include 头文件”语句将其包含进来,这样做有利于保持文件的清晰性。
除了在头文件里进行相关的宏定义,在这个头文件中还可以统一放置子程序文件中需要的所有头文件。

③子程序:子程序必须首先给出详尽的说明,子程序名、功能、入口参数、出口参数、堆栈深度、调用的内部子程序(包含在子程序文件的其它子程序)、备注等。编写子程序时,要注意各个子程序在文件中的位置,外部调用的子程序在文件的前面,内部调用的子程序放置在文件的尾部。

④注释:子程序文件中应该包含合理的注释。添加注释的原则是:注释要适度,不能一个子程序没有任何注释,也不要给一些非常容易理解的语句添加注释。子程序中关键或难以理解的语句后面应该给出行注释,子程序中某段独立任务应该给出段注释(某一段程序代码的开始处)。

4.主程序文件
一个汇编工程中包含一个汇编主程序文件,文件名固定为main.s。汇编主程序的主体是程序的主干流程,要尽可能简洁、清晰,流程中的各个环节由子程序去完成,主程序仅仅是完成对子程序的调用。
主程序文件Main.s,包含:

①工程描述:工程名、硬件连接索引、程序描述、目的、说明、注意、日期等。若调试过程有新的体会,也可在此添加。目的是为将来自己使用,或同组开发提供必要的备忘信息。
工程名中每个意义单词(或单词缩写)的首字母大写,后缀为.prj。
硬件连接索引是工程所要控制的硬件对象索引,详细描述在相应的硬件对象控制文件中给出。

②总头文件

③主程序:主程序一般包括初始化与主循环两大部分。初始化包括堆栈初始化、系统初始化、内存变量初始化、I/O端口初始化、中断初始化等。主循环是程序的工作循环,根据实际需要安排程序段,但一般不宜过长,建议不要超过200行,具体功能可通过调用子程序来实现,或由中断程序实现。不带操作系统的MCU程序总有一个主循环,表示程序周而复始地执行

④内部直接调用子程序:若有不单独存盘的子程序,建议放在此处。这样在主程序总循环的最后一个语句就可以看到这些子程序。建议不要超过3个,每个子程序不要超过200行。若有更多的子程序请单独存盘,单独测试。

⑤外部子程序:若程序使用独立存盘的子程序,可在此处使用“.include 子程序文件名”将其包含。注意,独立存盘的子程序必须与主程序在同一个目录中。
//------------------------------------------------------*
//工 程 名:PrgFrame.prj *
//硬件连接: *
// (1)MCU的I/O口引脚接小灯(见"LED.s"文件中的说明) *
//程序描述:用I/O口控制小灯闪烁 *
//目 的:第1个freescale HC08系列MCU 汇编语言程序框架 *
//说 明:提供Motorola MCU的编程框架,供教学入门使用 *
//注 意:如果延时不够长的话,会发觉灯不会闪烁,而是一直 *
// 亮,这是由于人的视觉引起的 *
//--------清华2007版《嵌入式技术基础与实践》实例--------*

//总头文件
.include "Includes.h"

//主程序
//以下两条语句的详细解释见"工程说明.txt"
.area flash(abs)
.org FlashStartAddr
MainInit:: //复位后从此处执行(见Vectors08.s文件末尾处)
SEI //关总中断
//1. 堆栈初始化为RAM最高端
LDHX #RAMendAddr + 1 //HX = #RAMEndAddr + 1
TXS //HX - 1 -> SP
//2. 系统初始化
JSR MCUinit //初学时跳过此处
//3. 模块初始化
JSR LEDinit //(1) I/O口小灯控制引脚初始化
//程序总循环入口
MainLoop:
LDA #'L' //小灯亮
JSR LED_L_A
LDHX #$02FF //延时
JSR DelayHX
LDA #'A' //小灯暗
JSR LED_L_A
LDHX #$02FF //延时
JSR DelayHX
JMP MainLoop


//包含本工程的其他文件-----------------------------------
.include "GeneralFun.s" //通用子程序
.include "MCUinit.s" //芯片初始化
.include "LED.s" //小灯驱动

//包含中断处理子程序与中断向量表文件"Vectors08.s"--------
//注意:这个包含语句的存放位置必须放在最后
.include "Vectors08.s" //中断处理子程序与中断向量表
第一个汇编工程讲述了08汇编工程文件组织方式、汇编程序文件的编写规范、软硬件模块的合理划分等内容,在以后的编程实践中,要按照这种方式和规范组织工程、编写程序,养成良好的编程习惯。

<<上一节 下一节>>

相关链接


 
关于我们 | 诚邀加盟 | 客户服务 | 相关法律 | 网站地图 | 友情链接 | 服务信箱:service@eefocus.com
© 2006 与非门科技信息咨询(北京)有限公司 All Rights Reserved.