您好,欢迎来电子发烧友网! ,新用户?[免费注册]

当前位置:电子发烧友网 > 图书频道 > 电子 > 《单片机原理与应用》 > 第4章 汇编语言程序设计

第3节 单片机源程序常用结构

  一个应用系统的汇编语言源程序,无论其系统工能的要求简单还是复杂,其程序结构的组成基础总是由简单程序、分支程序、循环程序、子程序、查表程序等结构化程序块组合而成。这是汇编语言源程序的设计基础。

  (1) 简单结构程序

  简单结构程序又称顺序结构程序,是汇编语言程序设计中最基本、最单纯的程序,在整个程序设计所占比例最大,是程序设计的基础。这里所说的简单结构程序是指一种无分支的直接程序,是按照逻辑操作顺序,从第一条指令开始逐步条顺序执行,直到最后一条指令为止。可见,简单结构程序是指其组织成结构简单、程序的逻辑流向是一维的,程序的具体内为止。可见简单结构程序是指其组成结构简单,程序逻辑的逻辑流向是一维的,程序的具体内容不一定简单,在实际编程中,如何正确选择指令,合理使用工作寄存器、节省存储单元等,是编写好程序的基本功。现举例说明。

  (2) 分支结构程序

  很多复杂的实际问题,总是伴随着逻辑判断,从而选择不同的处理路径,即程序的走向,从而使计算机能根据肯定某种智能基础。

  分支程序的主要特点是程序的流向从一个入口、两个或两个以上的出口,根据给定的条件进行选择确定。编程的关键是如何确定供判断或选择的条件以及选择的条件以及选择合适的分支指令AT89S52的指令集提供了极为丰富、功臣极强的多种分支指令,特别是比较转移和位判跳指令,给复杂问题,尤其是测控系统的程序设计提供了方便。

  分支程序又称复杂程序。因为一个源程序如果包含有无数个分支,每个分支均有不同的处理程序段,分支中又包含分支,这就使程序的流向十分复杂。因此 ,程序设计时必须借助程序流程图,把复杂的程序流向展现在平面图上,使之一目了然。为减少程序的复杂性,应尽力少用分支结构程序。

  ① 单分支结构程序

  单分支结构在程序设计中应用最广,拥有的分支指令了也多,其结构一般为一个入口两个出口。常用的流程图图形如图4.3所示。

  单分支结构程序的选择条件一般由运算或检测的状态标志提供,选用对应的条件判跳指令来实现。

  ② 多分支结构程序

  在实际应用中,常常需要从两个以上的流向(出口)中选一。例如,两个数相比较,必然存在大于、等于、小于三种情况,这时就需从三个分支中选一。再如多分支跳转(又称散转)将根据运算结果值在多分支中选一。这就形成了多分支结构。其流程图形如图4.4 所示。

  

  MCS-51单片机指令集设有散转指令:

  JMP @A+DPTR

  其中数据指针DPTR为存放转移指令串(S0~Sn)的首地址,由累加器A的内

  容动态选择对应的转 指令。因此,可多达256(n=1~256)个分支程序中选一。

  ③ 分支结构程序的形式

  单分支选择结构程序有以下三种典型形式:

  图4.3:条件成立则执行分支程序(1),否则执行分支程序(2),两者选一。图4.5a:条件成立则跳过程序段(2),执行程序段(3),否则顺序执行。图4.5b:条件成立则顺序往下执行,否则重复执行程序段(1),直至条件成立,程序顺序往下执行。

   

                                                    (a)                      

                                                                  图4.5单分支结构程序的典型形式

  对于第三种形式,可以程序段(1)重复执行次数作为判别条件,当重复次数达到条件满足时停止重复,程序顺序往下执行,这是分支程序结构的一种特殊形式。其中判别条件也可能是一个定量或状态标志,而重复次数是不定的,只要条件一满足,立即停止重复。也可能条件不满足时直接转向判跳指令本身,一旦条件成立,立即结束。这种方式常用于状态检测。例如: LOOP:JB P1.X,LOOP;

  本例是等待P1.X引脚上的电平出现“1”→“0”的急跳变而结束循环等待,程序顺序往下执行。

  分支结构程序允许嵌套,即一个分支接着一个分支,形成树根式多级分支程序结构。汇编语言程序本身并不限止这种嵌套层次数,但过多的嵌套层次将使程序结构变得十分复杂和雍肿,以致造成逻辑上的混乱和错误,因而应尽力避免。

  

 

  图4.6循环程序的典型形式

  一个较复杂的程序,总是包含多个分支程序段,为防止分支流向的混乱,应采用程序流程图具体标明每个分支的确切条件和走向。

  (3) 循环结构程序

  循环结构程序是控制主机多次、重复执行同一个程序段的一种基本程序结构。从本质上讲,它是分支结构程序中的一个特殊形式。由于它在程序设计中的重要性,故而配以专用指令,单独作为一种程序结构的形式进行设计。

  在某些情况下采用循环程序结构,可使原程序大大缩短和简化。例如,利用软件延时lms,若采用NOP指令来实现,当fosc=12MHz时,需近1000条NOP指令组成。如果采用循环结构程序,只需少数几条指令即可完成。可见其程序的简化效率。

  ① 循环结构程序的组成

  循环结构程序如图4.6所示,它由下述4个主要部分组成。

  A、初始化部分

  程序在进入循环处理程序段部分之前需设备初值,如循环次数、有关单元清0、变量设置、地址指针等。

  B、循环处理部分

  循环处理部分又称循环程序主体。是循环结构程序的核心,是循环执行需完成某种功能的主体。

  C、循环控制部分

  在重复执行循环体的过程中,不断修改和判断循环控制变量,直到符合结束循环条件。循环控制变量可以是循环递减计数或条件控制。前者是每循环执行一次,控制变量减1,并判是否减为0,若不为0,继续执行循环体程序;若控制变量减1后结果值为0,则结束循环程序的执行,进入结束处理。这些工作均由循环指令自动完成。后者判别结束条件上是否成立,例如计算结果达到给定精度要求或达到某一给定条件时就结束循环,这时的循环次数是不固定的。常用条件判跳指令来完成。

  D、结束处理部分

  这是对循环程序全部执行结束后的结果进行处理和存储。

  ② 循环结构程序举例

  有两条功能极强的循环转移指令:

  DJNZ Rn,rel ;以工作寄存器Rn为控制寄存器

  DJNZ direct,rel ;以直接寻址单元作控制寄存器

  这两条基本指令可派生出很多条不同控制计数器的循环转移指令,大大扩展了应用范围和多重循环的层次。

  控制计数器的计数方式一般均为不断减1计数(递减方式),即每循环一次,计数器自动减1计数,并判控制计数器是否为0,若不为0,继续执行循环;若为0,则结束循环程序的执行,脱离循环程序顺序往下执行。循环次数在初始化时预置,循环次数范围1-255,如超过此范围,则要采用多重循环方式。多重层次原则上不受限。

  ③ 多重循环结构程序

  某些复杂问题或者循环数超过256,则需采用多重循环的程序结构,即循环程序中包含循环程序或一个大循环中包含多个小循环程序,称多重循环程序结构,又称循环嵌套。循环的重数不限,但必须每循环的层次分明,不能有相互交叉!

  例如:双重软件延时。

  START:MOV R3,#DATA1 ;外层循环计数初值

  LOOP1:MOV R2, ;内层循环计数初值

  LOOP2:NOP

  NOP

  NOP

  DJNZ R2,LOOP2 ;(R2)-1≠0,转LOOP2

  DJNZ R3,LOOP1 ;(R3)-1≠0,转LOOP1

  END ;结束

  此例为典型的二重循环程序结构 ,可根据实际需要设计任意重循规蹈矩环。其执行过程是由内向外逐层展开。内层循环全部执行完后,外层则执行一次循环,依此类推。如内层循环次数为M,外层循规蹈矩环次数为N,则总的循规蹈矩环次数为NM次。

  (4) 子程序结构程序

  在实际应用中常会遇到带有通用性的问题,例如,数制转换、浮点运算等,且在同一个源程序中可能需多次用到。这就应该把它单独设计成通用子程序供随时调用,这样可使程序紧凑,缩短程序长度,调试方便。从执行时间看,每调用一次需附加断点保护、参量进栈、出栈等开销。

  ① 子程序结构

  能供调用的子程序,其结构应具备:

  A、必须标明子程序的入口地址,又称首地址,以便再教育程序调用;

  B、必须以返回指令RET结束子程序。

  在汇编语言源程序中调节器用子程序时,一般应注意两个问题:参数传递和现场保护。

  在使用调用指令不附带任何参数时,参数的互相传递要由设计者通过程序安排。一般可采用以下方法:

  A、传递数据。将需传递的参数通过工作寄存器Rn或累加器A传递给子程序。即在主程序调用子程序前将参数差额主选定的工作寄存器组R0~R7、累加器A中,供子程序读取或者将参数在调用前先压入堆栈,进入子程序后再从堆栈中读出。

  B、传递地址。将要传递的参数存放在数据存储器中,将其地址通过工作寄存器R0、R1或数据指针DPTR传递,供子程序读取参数。

  主程序的现场保护和恢复。子程序(包括中断服务程序)是个独立的程序段,在子程序执行过程中常需用到通用单元,例如:工作寄存器R0~R7、累加器A、数据指针DPTR,以及有关标志、状态位等。而这个单元中的原内容在调用子程序结束之后 的主程序中仍有用,需进行保护,称现场保护。在执行完子程序并返回继续执行主程序前应恢复其原内容,称现场恢复。一般有两种现场保护/恢复方式:

  A、调用前保护、返回后恢复。这种方式是在主程序逻辑的调节器用指令前进行现场保护,在调用指令之后,即返回原断点处进行恢复现场。其程序结构如下所示:

  主程序

  PUSH PSW ;将PSW、A、DPTR等入栈保护

  PUSH ACC

  PUSH B

  PUSH DPL

  PUSH DPH

  PUSH PSW,#10H ;选用工作寄存器组2,将0组保护

  LCALL addr16 ;调用子程序addr16

  POP DPH ;出栈

  POP DPL

  POP B

  POP ACC

  POP PSW

  这种结构灵活,可根据实际需要实现现场保护/恢复。

  B、调用后保护、返回前恢复。这种结构是在子程序的开始部分进行现场保护,而在子程序的结束部分、返回指令前恢复。其程序结构如下所示:

  ADDR:PUSH PSW ;子程序现场保护

  PUSH ACC

  PUSH B

  PUSH DPL

  PUSH DPH

  MOV PSW,#08H ;选用工作寄存器组1,0组保护

  …………… ;子程序主体

  POP DPH ;现场恢复

  POP DPL

  POP B

  POP ACC

  POP PSW

  RET ;返回

  这是子程序标准格式,现场保护/恢复内容固定,但程序规范、清晰。

  上述两种方式可由设计者任选。

  ② 子程序特性

  随着汇编语言程序设计技术的发展,子程序的应用越显重要。因此,对子程序的设计具有较高要求,除通常在程序设计中应遵循的原则外,还应具备以下特性:

  A、通用性

  严格讲,子程序有通用和专用两种。前者如数制转换、浮点运算等子程序可广泛应用于同系列单片机的任何应用系统,后者仅限用于同一个应用系统中。特别是前者,都应注意子程序的通用性。

  子程序中某些可变的量称为参量,这些参量在子程序的定义中是“哑变量”,占用一定的变量单元,每次调用均由实际变量或数据赋值。因此,一个子程序可以对不同的变量或参数进行处理。为了使子程序具有通用性,在设计中要解决的一个重要问题,就是确定哪些变量作为参量以及何传递参量。

  B、可浮动性

  可浮动性是指子程序段可安置在程序存储器的任何区域。为此,在子程序中应避免选用绝对转移地址。

  C、可递归和可重入性

  子程序能自己调用自己和同时能被多个任务(或多个用户程序)调用的特性,分别称之为子程序的可递归性和可重入性。这类子程序常在庞大而复杂的程序中应用,在单片机应用程序设计中较少用到。

  D、子程序说明文件

  对于通用子程序,为便于各种应用程序的选用,要求在子程序编制、调试完成后应提供一个说明文件。其内容应包含以下内容。

  Ø ·子程序名。标明子程序功能的名称。

  Ø ·子程序功能。简要说明子程序能完成的主要功能,包括重要算法、参量要求及有关存储单元配置等。

  Ø ·子程序调用。指明本子程序还需调用哪些子程序。

  Ø ·附子程序流程图及程序清单。

  由于子程序结构在程序设计中应用极为普遍,因此,一般在指令集中均设有子程序调用指令。AT89S系列的指令集中,考虑到程序存储器空间的限制和节省,特设置了绝对调用和长调两条指令,供实际应用时选择。

  ③ 子程序举例

  子程序的设计除它本身的特殊性外,其余完全同典型程序设计要求,只是其功能单一、程序量小、结构简单、易于编制与调试。通常它总是只完成整个任务中的某一个单一而独立的,又需多次调用的部分功能。如任务中的 DELAY 程序段为延时子程序。

  (5) 查表结构程序

  在很多情况下,直接通过查表方式求得的值(变量的值)比通过计算解决要简单、方便得多,而且速度快,实时性强。有些数值转换,例如八段显示编码与显示数值必须经过转换,都需通过查表程序来实现。为此,AT89S系列的指令集专门提供了如下查表指令:

  MOVC A,@A+DPTR和MOVC A,@A+PC

  待查的表数一般是一串有规律、按顺序排列的固定常量。因此,常把它固化在程序存储器的数据区域,所以MOVC指令是专访程序存储器表格类数据的指令。编程时可很方便地通过DB伪指令对指定的存储单元设置不同:选用DPTR为首地址指针时表格参量可存放在64KB范围内的任何区段,可供无限次查表,选用PC当前值为首地址指针时表硌参量必须设置在紧跟查表指令(MOVC)之后,基本上只能一次性查表,编程时应根据实际情况进行选择,一般以选择DPTR为基址指针的查表指令灵活、方便,可省去一些麻烦。

  选用DPTR作为基地址的查表指令时,其操作可分3步进行:

  A、将待查表格的首地址置入DPTR基址寄存器;

  B、将待查的表格具体项数值置入变址寄存器A中;

  C、执行指令

  MOVC A,@A+DPTR

  将查表结果值读入累加器A中。

  选用PC当前值作为基地址的查表指令时,由于PC的当前值正是查表指令的下一条指令的第一个字节的地址值,亦即表格数据串的首地址,当执行完查表指令,读取到表格数据后仍需从这个PC的当前值继续往下执行,显然两者发生重叠,为解决这个矛盾,势必将表格下移若干个字节,以便设置一条跳转指令跳过表格串继续安排顺序往下执行的程序。把表格串下移的字节数加到变址寄存器A中,即在查表指令前加一条指令。

  ADD A,#data

  A中为经动态运算得到的待查表格数的项数。Data为表格串下移的字节数,亦即PC的当前值到下移后的表格串首地址的距离数。这个距离数显然不宜太大,因为它将影响表格串的长度,两者之和不能超过565B,这种表格一般属一次性查找,因为PC的当前值随程序的执行而改变。

  如任务中查找字型码,就是采用查表程序实现的。它先建立了TABLE表格,利用

  MOV DPTR,#TABLE 确定表格首址,再用MOV A,@A+DPTR指令查表。

  以上介绍了几种组成源程序设计的基要结构,一般单片机应用程序不管如何复杂,都是这几种基本程序结构的有机组合。在充分掌握这几种基本程序结构的基础上,结合具体算法,施展程序设计技巧,就能设计出符合要求的、正确可靠、具有较高水平的优秀程序。

  (6) 单片机源程序的基本格式

  由于不同类型的单片机系列因有关资源的地址分配不同,故而其对应的源程序格式也各不相同。对于51系列单片机,因其中断矢量被设置在程序存储器的0003H~0033H地址区域、而源程序的起始地址又必须从0000H单元开始,因而源程序的首条指令设置在0000H~0002H单元,而且必是跳转指令,以跳过中断矢量地址区域,开始主程序的顺序执行。这是由51系列单片机的硬件结构所决定的。

  在单片机的指令集中,无程序启动运行指令,系统复位后立即启动并开始执行应用系统源程序,由于复位后的程序存储器指针PC值为0000H,所以程序必定从0000H单元开始执行。为此,在000H~0002H三个单元专门用于设置一条跳转指令(长跳转LJMP属三字节指令),从而满足源程序既从0000H存储单元开始启动并执行,又跳过中断矢量地址区域段,跳转到应用系统主程序顺次执行。

  在中断矢量地址区段,由于分配给每个中断服务程序的地址空间只有8个存储单元,一般是不够用的。实际应用中也常安排一条跳转指令,从中断矢量处跳转到对应的中断服务程序去执行。这样,一般中断服务程序总是安排在主程序的高地址方向空余的存储器区域。此外,一个应用系统软件,总有若干个子程序供主程序调用,一般也都安排在程序存储器高地址方向,主程序地址空间之外空余的地址单元区段。

  有些应用系统软件,常有一些固定的表格参数存放在程序存储器中,例如LED显示编码等,以供主程序或子程序等查用,一般也安排在主程序之后的高地址方向区段。

  因此,任一个51系列单片机的应用系统源程序,其基本地址空间可划分为:中断矢量地址空间段、主程序地址空间段、中断服务程序和了程序地址空间段、固定表格参数地址空间段四部分。除中断矢量地址实间是固定的外,其作均应视程序量和执行方便等任意分配地址空间,其顺序也不受此限制。

  1) 单片机应用系统源程序的基本格式及其部分地址分配如下例所示:

  ORG 0000H

  LJMP START ;转向主程序

  ORG 0003H

  LJMP INTE0 ;转向外中断0服务子程序

  ORG 000BH

  LJMP INTTFO ;转向定时/计数器0服务子程序

  …… ;可按实际需要设置服务子程序

  ORG 0030H

  START:MOV A,#00H ;主程序从0030H单元开始

  MOV R1,#data

  MOV R0,#00H

  DJNZ R1,LOOP0

  …… ;初始化程序段

  …… ;主程序主体区段

  ORG 3100H

  A0: … ;子程序A0

  RET

  A1 … ;子程序A1

  RET

  ……

  ORG 3100h

  INTIE0: ;外部中断0中断服务程序

  RETI

  ORG 4800H

  INTTFO: ;定时/计数器0中断服务程序

  RETI

  …… ;其他中断服务程序段

  ORG 5500H

  DBL0:DB 43,56, ;}固定表格参量区段

  END ;结束

  上例中的地址分配是为了便于示例。在实际应用中应根据具体情况而定。在主程序段应包含有关单元清0、方式选择、参数设置以及有关部分的初始化程序等。主程序是整个源程序的核心、主体、其他程序段均从属于它,必须切实设计好主程序。其他程序段的具体格格不入与要求,已于前述。这里就不再述/

  由于单片机特定的应用环境,其源程序设计定型付诸实际应用后,一般不再改变,而且多为周面复始运行。因此,它一般均固化于程序存储器(ROM)中。这是与典型微型计算机的主要区别之一。