




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第5章汇编语言程序设计
5.1STVD开发环境与STM8汇编语言程序结构5.2STM8汇编程序结构5.3程序基本结构5.4并行多任务程序结构及实现5.5程序仿真与调试 5.1STVD开发环境与STM8汇编语言程序结构5.1.1STVD开发环境中创建工作站文件工作站文件的创建过程如下:
(1)执行“File”菜单下的“NewWorkspace…”,选择“createworkspaceandproject”,创建新的ST工作站文件(扩展名为 .STW)。
(2)在图5-1所示的“Workspace”文本框内指定工作站文件的存放目录路径;在“Workspacefilename”文本框内输入工作站文件名(不用扩展名)。图5-1创建工作站文件
(3)在图5-2所示的“Projectfilename”文本框内输入“项目文件名”;根据选定的开发语言(ST汇编还是某特定的C语言),在“Toolchain”文本框内选定相应的连接程序,如“STAssemblerLinker”。
(4)在“MCU”窗口内选择相应的MCU型号,如STM8S208MB等,单击“OK”按钮后,就可以观察到如图5-3所示的文件夹。5.1.2STVD自动创建项目文件内容利用STVD创建STM8S汇编项目文件时,在“SourceFiles”文件夹下自动生成了mapping.asm源文件(段定义汇编文件)、main.asm文件(用户应用程序主模块框架汇编文件);在“IncludeFiles”包含文件夹内自动生成了mapping.inc文件(段定义汇编文件中涉及的符号常量定义说明文件)。其中,mapping.asm、mapping.inc的内容简单明了,容易理解。下面主要分析main.asm文件组成和关键指令功能。
stm8/ ; [注1]指定CPU类型汇编格式的伪指令,不能缺省且顶格书写
#include"mapping.inc" ; [注2]包含文件说明伪指令,凡指令、伪指令行至少退一个以上字符位
segment'rom' ;在rom段内存放指令码main.l ; [注3]主程序开始标号,凡标号一律顶格书写
; initializeSP
ldwX,#stack_end ; [注4]初始化堆栈指针SPldwSP,X#ifdefRAM0 ; [注5]RAM存储区00H~FFH单元清0; clearRAM0ram0_start.bEQU$ram0_segment_startram0_end.bEQU$ram0_segment_endldwX,#ram0_startclear_ram0.l
clr(X) incwX cpwX,#ram0_end
jruleclear_ram0 #endif #ifdefRAM1 ; [注6]RAM存储区0100H及以上单元清0 ; clearRAM1ram1_start.wEQU$ram1_segment_startram1_end.wEQU$ram1_segment_end ldwX,#ram1_startclear_ram1.l clr(X) incwX cpwX,#ram1_end jruleclear_ram1
#endif ;
clearstack ; [注7]堆栈区单元清0stack_start.wEQU$stack_segment_startstack_end.wEQU$stack_segment_end ldwX,#stack_startclear_stack.l clr(X) incwX cpwX,#stack_end jruleclear_stackinfinite_loop.l ;虚拟的主程序
jrainfinite_loop interruptNonHandledInterrupt ; [注8]中断服务程序定义伪指令NonHandledInterrupt.l iret;中断入口地址表
segment'vectit' dc.l{$82000000+main} ; reset[注9]中断入口地址表
dc.l{$82000000+NonHandledInterrupt} ; trap dc.l{$82000000+NonHandledInterrupt} ; irq0 dc.l{$82000000+NonHandledInterrupt} ; irq1 dc.l{$82000000+NonHandledInterrupt} ; irq2 dc.l{$82000000+NonHandledInterrupt} ; irq3 dc.l{$82000000+NonHandledInterrupt} ; irq4 dc.l{$82000000+NonHandledInterrupt} ; irq5 dc.l{$82000000+NonHandledInterrupt} ; irq6 dc.l{$82000000+NonHandledInterrupt} ; irq7 dc.l{$82000000+NonHandledInterrupt} ; irq8
dc.l{$82000000+NonHandledInterrupt} ; irq9dc.l{$82000000+NonHandledInterrupt} ; irq10dc.l{$82000000+NonHandledInterrupt} ; irq11dc.l{$82000000+NonHandledInterrupt} ; irq12dc.l{$82000000+NonHandledInterrupt} ; irq13dc.l{$82000000+NonHandledInterrupt} ; irq14dc.l{$82000000+NonHandledInterrupt} ; irq15dc.l{$82000000+NonHandledInterrupt} ; irq16dc.l{$82000000+NonHandledInterrupt} ; irq17dc.l{$82000000+NonHandledInterrupt} ; irq18dc.l{$82000000+NonHandledInterrupt} ; irq19dc.l{$82000000+NonHandledInterrupt} ; irq20dc.l{$82000000+NonHandledInterrupt} ; irq21dc.l{$82000000+NonHandledInterrupt} ; irq22dc.l{$82000000+NonHandledInterrupt} ; irq23dc.l{$82000000+NonHandledInterrupt} ; irq24dc.l{$82000000+NonHandledInterrupt} ; irq25dc.l{$82000000+NonHandledInterrupt} ; irq26dc.l{$82000000+NonHandledInterrupt} ; irq27dc.l{$82000000+NonHandledInterrupt} ; irq28dc.l{$82000000+NonHandledInterrupt} ; irq29end ;汇编程序结束伪指令,不能缺省
[注1]在ST汇编中,汇编程序源文件开始处必须用“st7/(或ST7/)”或“stm8(或STM8/)”伪指令指定随后的指令、伪指令按哪一种类MCU芯片指令格式进行汇编,不可缺省且必须顶格书写。
[注2]由于自动创建的段定义文件使用了符号常量作为段起始地址、终了地址,因此STVD自动创建了mapping.inc文件。该文件对段定义中涉及的符号常量进行了定义,插入了#includemapping.inc伪指令。
[注3]在ROM段中开始定义了一个main.l标号,标号必须顶格书写。这样,只要在中断入口地址表中用“dc.l{$82000000+main}”伪指令填充复位中断向量存储单元,就可以保证将复位中断逻辑指向复位后要执行的第一条指令所在存储单元的地址,如[注9]所示。在STM8S中,复位后将从复位中断逻辑指示的地址单元(可以是ROM、EEPROM,甚至是RAM)取出并执行第一条指令。第一条指令在ROM存储区中的存放位置并没有限制,将第一条指令所在存储单元的地址填入复位中断入口地址表中。
[注4]复位后初始化堆栈指针。尽管复位后堆栈指针SP也指向RAM的最后一个单元,但复位后,用指令初始化有关寄存器是一个良好的习惯,避免了用缺省值造成的不确定性。
[注5]~[注7]分别清除了RAM存储区、堆栈区。如果复位后没有保留RAM单元信息的必要,则复位后对RAM单元进行集中清0非常必要。
[注8]在ST汇编中,必须通过“interrupt”伪指令定义相应中断服务程序入口地址标号,然后在中断入口地址表中填入对应的中断服务程序入口地址标号。
[注9]中断入口地址表。例如,PortA外部中断EXTI0的中断号为IRQ03,因此PortA口外中断服务程序结构如下:
InterruptEXTI0
EXTI0.L ;外中断EXTI0服务程序指令系列
IRET然后将中断向量表内的IRQ03改为
dc.l{$82000000+NonHandledInterrupt} ;
irq2,未定义中断入口地址表依然保留
dc.l{$82000000+EXTI0}
; irq3,即PortA口外中断入口地址表
dc.l{$82000000+NonHandledInterrupt} ; irq4可见在STM8S中,中断服务程序入口地址不固定,只需将对应中断号服务程序第一条指令所在存储单元地址,实际上是把82000000H与中断服务程序第一条指令前标号相加,填入对应中断向量表内。5.1.3完善STVD自动创建的项目文件内容由STVD创建的项目汇编文件尚不十分完善,没有把相应型号MCU的外设寄存器定义文件加入到“SourceFiles”(源文件夹)中,在用户程序中尚不能直接引用外设寄存器名,如“LDPA_DDR,A”等;也没有定义外设控制寄存器与外设状态寄存器中的位,如“FLASH_IAPSR”的DUL位。为此须按下列步骤完善STVD创建的项目文件。
1.加入相应MCU的外设寄存器定义汇编文件(.asm)及其外部标号说明文件(.inc)将光标移到“SourceFiles”源程序文件夹上,单击右键,选择“AddFilestoForlder…”将ST_toolset\asm\include文件夹中对应型号芯片的外设寄存器定义汇编文件,如STM8S208MB.asm,加入到源程序文件中。将光标移到“IncludeFiles”包含文件夹上,单击右键,选择“AddFilestoForlder…”将ST_toolset\asm\include文件夹中对应芯片的外设端口寄存器标号说明文件,如STM8S208MB.inc,加入到该文件夹中。
2.在main.asm文件中插入相应MCU的.inc文件在main.asm文件中插入#include
"×××××××.inc",其中×××××××代表芯片型号,即上一步添加的包含文件名,以便能在应用程序模块中直接引用外设寄存器名,如PA_DDR、PA_CR1、FLASH_IAPSR等。到此基本完成了汇编环境的创建过程,可以在main.asm文件内插入用户指令系列,并进行编译、模拟仿真、联机调试等操作。
3.在main.asm文件头插入通用变量定义伪指令为便于模块化应用程序的编写、调试,可将00H~3FH之间的RAM存储单元划分为四个区,其中,00H~0FH作为主程序通用变量区,10H~1FH作为优先级为1的中断服务程序的变量区,20H~2FH作为优先级为2的中断服务程序的变量区,30H~3FH作为优先级为3的中断服务程序的变量区;分别用R00~R3F变量名对这64字节RAM单元进行命名,具体如下: segment'ram0' BYTES ; ram0段内标号为字节标号。如果不用BYTES伪指令或后缀.B指定为字
;节标号,将默认为字标号,采用16位地址格式; 00H~0FH单元定义为字节变量,供主程序使用.R00ds.b1 ;最好定义为公共变量,即用前缀“.”进行声明.R01ds.b1.R0Fds.b1; 10H~1FH单元定义为字节变量,供优先级1中断服务程序使用.R10ds.b1.R11ds.b1.R1Fds.b1; 20H~2FH单元定义为字节变量,供优先级2中断服务程序使用.R20ds.b1.R21ds.b1.R2Fds.b1; 30H~3FH单元定义为字节变量,供优先级3中断服务程序使用.R30ds.b1.R31ds.b1.R3Fds.b1
segment'ram1'WORDS ; ram1段后定义的标号为字标号。当在ram0段中,把变量标号定义为bytes时,
;该语句不能少在模块化程序结构中,最好将R00~R3F公共变量定义伪指令放在一个特定的源文件中,如User_register.asm,同时创建相应外部变量说明文件,如User_register.inc。然后分别添加到“SourceFiles”、“IncludeFiles”文件夹内,供不同的模块引用。这两个文件的内容如下:
; User_register.asm文件内容:stm8/
;用户定义公共变量
segment'ram0'
; 00H~0FH单元定义为字节变量(供主程序使用)
.R00.Bds.b1
.R01.Bds.b1
.R02.Bds.b1;省略R03~R0F变量定义伪指令行; 10H~1FH单元定义为字节变量,供优先级为1中断服务程序使用.R10.Bds.b1.R11.Bds.b1.R12.Bds.b1;省略R13~R1F变量定义伪指令行; 20H~2FH单元定义为字节变量,供优先级为2中断服务程序使用.R20.Bds.b1.R21.Bds.b1.R22.Bds.b1;省略R23~R2F变量定义伪指令行; 30H~3FH单元定义为字节变量,供优先级为3中断服务程序使用.R30.Bds.b1.R31.Bds.b1.R32.Bds.b1;省略R33~R3F变量定义伪指令行BYTESend; User_register.inc文件内容:;用户定义公共变量属性说明; 00H~0FH单元定义为字节变量,供主程序使用
EXTERNR00.B ;用户定义的变量
EXTERNR01.B ;用户定义的变量
EXTERNR02.B ;用户定义的变量;省略R03~R0F变量定义属性说明伪指令行
EXTERNR10.B ;用户定义的变量
EXTERNR11.B ;用户定义的变量
EXTERNR12.B ;用户定义的变量
;省略R13~R1F变量定义属性说明伪指令行
EXTERNR20.B ;用户定义的变量
EXTERNR21.B ;用户定义的变量
EXTERNR22.B ;用户定义的变量;省略R23~R2F变量定义属性说明伪指令行
EXTERNR30.B ;用户定义的变量
EXTERNR31.B ;用户定义的变量
EXTERNR32.B ;用户定义的变量;省略R33~R3F变量定义属性说明伪指令行在具体应用程序中,可根据需要灵活裁剪,如某应用系统的中断源只有两个优先级,则无须保留30H~3FH单元作为中断优先级3的通用变量区。需要注意的是:非屏蔽中断TRAP、顶级中断TLI(PD7引脚外中断)可中断优先级为3的可屏蔽中断,这两类中断服务程序不宜共用R30~R3F变量,否则可能出现资源冲突现象。
4.更换汇编语言数制表示方式如果程序员熟悉Intel格式汇编语言数制表示方式,则可按下列步骤改造STVD自动创建的main.asm文件:
(1)在虚拟主程序段前插入“Intel”伪指令。
(2)在segment'vectit'(中断向量段)前插入“Motorola”伪指令;在segment'vectit'(中断向量段)后插入“Intel”伪指令。当然也可以将堆栈段内中断入口地址表常数中全部的“$82000000”修改为“82000000H”,但远不如直接插入“Motorola”、“Intel”等伪指令方便。
5.创建外设寄存器位定义说明文件(.inc)创建外设寄存器位定义说明文件不是必需的,只是为了增加源程序的可读性。未用 #Define伪指令定义外设寄存器位前,ST汇编语言源程序中的位操作指令只能采用“寄存器名,#位编号”形式作为位操作数。例如:
BTJTFLASH_IAPSR,#3,EEPROM_Write_Next1 ;DUL(EEPROM写保护标志位)为1,则跳转该指令如果不加注释,则指令的可读性很差,需要查阅用户指南才能确定FLASH_IAPSR[3]位是DUL标志。此外,当程序中多处出现“FLASH_IAPSR,#3”时,容易出错。例如,将“#3”误写成“#2”,则编译时不给出任何提示信息。为此最好创建一个通用的外设寄存器位定义说明文件(periph_bit_define.INC),以便在程序中用“寄存器名_位名”形式作为位操作数,以提高源程序的可读性。该文件格式如下:;------FLASH_IAPSR寄存器位定义------; 位名称 所在位编号 读写特性说明#defineFLASH_IAPSR_HVOFF FLASH_IAPSR,#6 ;r#defineFLASH_IAPSR_DUL FLASH_IAPSR,#3 ;rc_w0#defineFLASH_IAPSR_EOP FLASH_IAPSR,#2 ;rc_r#defineFLASH_IAPSR_PUL FLASH_IAPSR,#1 ;rc_w0#defineFLASH_IAPSR_WR_PG_DIS FLASH_IAPSR,#0 ;rc_r将periph_bit_define.inc文件添加到IncludeFiles文件夹内,并在应用程序文件头部分插入 #include“periph_bit_define.inc” 伪指令,如图5-4所示。图5-4插入并引用外设寄存器位定义名为防止编译时联接程序找不到指定的文件,一个简单的办法是将User_register.asm、User_register.inc、periph_bit_define.inc,甚至相应型号MCU外设寄存器定义文件,如STM8S207R8.asm、STM8S207R8.inc等,复制到指定工作站目录下,然后再分类逐一添加到SourceFiles、IncludeFiles文件夹内。这样,在汇编语言源程序中就可以直接使用位定义名代替“寄存器名,#位编号”形式位操作数,原因是“BTJTFLASH_IAPSR_DUL,EEPROM_Write_Next1”与“BTJTFLASH_IAPSR,#3,EEPROM_Write_Next1”等效。显然,用寄存器位定义名后提高了源程序的可读性,且只要保证位定义文件中寄存器名、位编号正确,就不会出错。5.1.4在项目文件中添加其他文件
STVD开发环境支持多模块汇编。因此,可创建多个源程序,并将它添加到“SourceFiles”文件夹内,汇编后将根据模块内段定义特征连接成一个完整的应用程序。在项目内添加程序源文件时,需要注意以下几点:
(1)任意一个汇编源程序文件的第一条指令必须是“ST7/(或st7/)”或“STM8/(或stm8/)”伪指令;最后一条指令一般为“END(或end)”伪指令,且每一指令行必须带有“回车符”。
(2)对于变量定义伪指令,必须通过“Segment”指定变量存放在哪一段内;对于代码,也必须通过“Segment”指定存放汇编后代码存放在哪一段内。
(3)在多模块结构程序中,模块内的公共变量、标号必须指定为Public类型,或用前缀“.”定义,否则汇编时将视为局部变量、标号,仅在本模块内有效。模块内引用来自其他模块定义的变量、标号时必须用“EXTERN”伪指令说明。
(4)在main.asm主程序中无须加入“#include汇编源程序名.asm”指令。对于包含文件(.inc),则必须通过“#include文件名.inc”语句声明。
(5)模块顺序决定了连接后的定位顺序。在STVD中,“SourceFiles”汇编程序源文件顺序依次为
Mapping.asm ;段定义源文件应排在最前面
STM8S208MB.asm ;外设寄存器定义源程序文件,具体文件名与MCU型号有关
Main.asm ;用户主程序或其他模块程序5.2STM8汇编程序结构5.2.1子程序与中断服务程序在主模块内
采用子程序与中断服务程序在主模块内的结构时,工作站文件夹中除了相应型号芯片的头文件外,几乎没有其他模块文件,形成了单一主模块程序结构,如图5-5所示。该结构中所有的子程序、中断服务程序均位于主应用程序main.asm模块内,变量、子程序入口地址标号、中断服务入口地址等属于局部标号与局部变量,无须指定标号类型,也无须用EXTERN伪指令声明其来源,但其缺点是程序结构不够清晰,查找某一个子程序时效率较低。
图5-5单一主模块程序结构在这种结构程序中,主应用程序main.asm模块大致包含了如下内容:
;按st7还是stm8代码格式汇编源程序
stm8/ ;代码格式伪指令
;程序头(由#define、equ、cequ定义的符号常量、标号)#defineVAR1$50
;主程序引用的外部标号(变量)说明区(EXTERN)
segment'rom' ;指定了代码存放在哪一段中
mani.l ;主程序开始标号
;初始化I/O引脚的输入/输出方式,并用#define指令对I/O引脚重定义
;初始化堆栈指针
; RAM0段存储单元清0
; RAM1段存储单元清0
;堆栈段存储单元清0
;初始化主时钟及CPU时钟频率
;硬件初始化(设置外设部件工作方式)
;复位中断优先级(开中断);主程序实体指令系列;子程序;中断服务程序;常数表(由dc.b、dc.w、dc.l定义的常数表);中断向量表由于STVD开发环境不支持过程汇编,位于同一个汇编文件内不同子程序中的标号必须唯一。因此,在STVD开发环境中最好取长标号,可按“模块名_模块内标号”形式给标号取名。例如,在“EEPROM_Write.L”模块中可用“EEPROM_Write_Lab1”、“EEPROM_Write_Next1”、“EEPROM_Write_Last1”、“EEPROM_Write_Loop1”等作为该模块的标号。尽管在理论上,子程序与中断服务程序可存放在FlashROM存储区中的任意位置,但在STVD开发环境中,中断服务程序必须位于子程序后,否则在仿真调试时,调试速度会很慢,单步执行一条指令(包括子程序调用指令)所需的时间可能很长,甚至不能接受。5.2.2子程序与中断服务程序在各自模块内把子程序,尤其指令较多的子程序、中断服务程序安排在各自模块内,形成多模块结构程序,如图5-6所示。这种程序结构清晰,除了指定为“Public”的公共变量、标号外,均属于局部变量、局部标号,这意味着不同模块内的局部标号可重复使用。这有利于程序的维护,以及多人协作完成同一项目控制程序的设计。图5-6多模块程序结构为便于程序的维护和升级,在程序中应尽量避免直接使用存储单元地址。因此良好的程序习惯如下:
(1)对于常数,可在程序头中用#define、EQU伪指令定义。例如:
#definePlus_width50H或
Plus_widthEQU50H
(2)对于RAM、EEPROM中的存储单元,最好用ds.b、ds.w、ds.l伪指令定义,使变量对应的存储单元地址处于浮动状态,变量实际地址待编译后才能确定。例如:
Segment'ram0'
ds.bTRK11
ds.bTRK21
(3)位于FlashROM中的常数表,用dc.B、dc.W、dc.L伪指令定义。5.2.3子程序结构所谓子程序就是供其他程序模块通过CALL或CALLF指令调用的指令系列。当子程序中存在改写CPU内某一寄存器(包括索引寄存器X、Y,累加器A,条件码寄存器CC)时,如果返回后需要用到调用前该寄存器的值,则必须将其压入堆栈保护,这容易理解。但容易忽略的是寄存器CC中的标志位,由于STM8内核CPU许多指令均影响标志位状态,因此返回后还需使用调用前的标志位状态,为防止错误,在子程序中一律将CC寄存器压入堆栈将是一个良好的习惯。其实,在模块化程序设计中,把子程序中改写的CPU寄存器一律压入堆栈也是一个良好策略,这是因为STM8内核MCU堆栈深度较大,如果子程序嵌套层数不太多,则遇到堆栈溢出的可能性很小。在STM8系统中,子程序入口地址标号可以是Word类型,即16位地址形式,对应的返回指令为RET;也可以是Long类型,即24位地址形式,对应的返回指令为RETF。采用L类型地址标号还是W类型地址标号,与子程序存放的位置有关。
(1)当子程序位于00段内时,可定义为W类型,也可以定义为L类型。当定义为W类型时,调用(CALL指令)与返回(RET指令)代码短、执行速度快,子程序中所有标号均定义为W类型。为方便程序维护,最好在地址标号后加“:”(冒号)。这样当需要将该子程序放到01段以上时,只要将“:”(冒号)用“.L”替换;将JP绝对跳转指令中的操作码助记符“JP”用“JPF”替换;将返回指令RET用“RETF”替换即可。因此,00段内入口地址为W类型的子程序结构如下:
Sub_xxx: ;子程序入口地址标号Sub_xxx定义为W(Word)类型
PUSHWX ;保护索引寄存器X(子程序用到寄存器X) PUSHWY ;保护索引寄存器Y(子程序用到寄存器Y) PUSHCC ;保护寄存器CC PUSHA ;保护累加器A ;子程序实体
JPSub_xxx_NEXT1 ;子程序实体Sub_xxx_NEXT1: ;子程序内的地址标号定义为W类型
POPA POPCC POPWY POPWX RET ;子程序返回指令
RET ;子程序返回冗余指令数目为1~4条,参阅第11章这种结构子程序,只能在00段内通过CALL指令调用。为此可将00段内子程序入口地址定义为L类型,以便在任何位置都可以通过CALLF指令调用。因此,00段内推荐的子程序结构如下:
Sub_xxx.L ;子程序入口地址标号Sub_xxx定义为L(Long)类型
PUSHWX ;保护索引寄存器X(子程序用到寄存器X)
PUSHWY ;保护索引寄存器Y(子程序用到寄存器Y) PUSHCC ;保护寄存器CC PUSHA ;保护累加器A ;子程序实体
JPSub_xxx_NEXT1 ;为减少指令码长度、提高运行速度,仍采用JP绝对跳转指令
;子程序实体Sub_xxx_NEXT1: ;子程序内的地址标号定义为W类型
POPA POPCC POPWY POPWX RETF ;子程序返回指令
RETF ;子程序返回冗余指令
(2)当子程序位于01段以上时,入口地址标号必须定义为L类型,同时,子程序内所有地址标号均定义为L类型,如下所示:
Sub_xxx.L ;子程序入口地址标号Sub_xxx定义为L(Lord)类型
PUSHWX ;保护索引寄存器X(子程序用到寄存器X) PUSHWY ;保护索引寄存器Y(子程序用到寄存器Y) ;子程序实体
JPFSub_xxx_NEXT1 ;采用远跳转指令JPF ;子程序实体Sub_xxx_NEXT1.L ;子程序内所有地址标号必须定义为L类型
POPWY POPWX RETF ;子程序返回指令
RETF ;子程序返回冗余指令值得注意的是:中断服务程序入口地址标号一定为L类型,原因是STM8内核CPU响应中断请求时,将入口地址标号对应的三个字节压入堆栈;在中断服务程序中,无须将CPU内核寄存器压入堆栈,这是因为STM8内核CPU响应中断请求时已自动将CPU内各寄存器压入堆栈。5.3程序基本结构5.3.1顺序结构所谓顺序程序结构,是指程序段中没有转移指令,执行时CPU逐条执行。例5-1查表程序。假设共阳LED数码管数码0~F的笔段码存放在以LED_Data为标号的存储单元中,如下所示:
LED_Data:
;
0,
1,2,
3,4,
5,
6,7,
8,9,
A,B,
C,D,
E,
F
dc.B
0C0H,0F9H,0A4H,0B0H,099H,092H,082H,0F8H,080H,090H,088H,083H,0C6H,0A1H,086H,08EH显示数据在累加器A中,试编写一个程序段将显示数据对应的笔段码取出。
参考程序段如下:
CLRWX ;寄存器X清0 LDXL,A ;显示数码送寄存器XL ADDWX,#LED_Data ;加上数据首地址LDA,(X) ;取出对应的笔段码或
CLRWX ;寄存器X清0 LDXL,A ;显示数码送寄存器XL LDA,(LED_Data,X) ;以“基址 + 变址寻址”方式取出对应的笔段码例5-2
将存放在R01单元中压缩形式的BCD码转换为二进制数。参考程序如下:
;功能:把存放在R01单元中压缩形式的BCD码转换为二进制数
;算法:
;入口参数:待转换的BCD码存放在R01单元中
;出口参数:结果回送R01单元,假定R01物理地址在RAM存储区中
;使用资源:寄存器A、X及R01单元S_BCD_BI.W ;单字节BCD码转二进制
LDA,R01 ;取BCD码
ANDA,#0F0H ;保留高4位(十位) SWAPA LDXL,A LDA,#10 MULX,A ; X←XL × A,十位乘10,最大为90,高8位XH为0 LDA,R01 ;取BCD码
ANDA,#0FH ;保留BCD码个位
LDR01,A ;回送R01暂存
LDA,XL ADDA,R01 LDR01,A RET例5-3把存放在R02、R03单元中压缩形式的BCD码转换为二进制数,结果回送到R02、R03单元中。参考程序如下:
;功能:把存放在R02、R03单元中压缩形式的BCD码转换为二进制数
;算法:a3 × 103 + a2 × 102 + a1 × 10 + a0 = (a3 × 10 + a2) × 100 + (a1 × 10 + a0)
;入口参数:待转换的BCD码存放在寄存器R02、R03单元中
;出口参数:结果回送R02、R03单元,假定两单元相邻,可按字节访问,也可以按字访问
;使用资源:寄存器A、X以及R01单元D_BCD_BI.W MOVR01,R03 ;十位、个位送R01 CALLS_BCD_BI ;调用单字节BCD码转二进制子程序,计算a1 × 10 + a0 MOVR03,R01 ;结果暂时存放在R03中
MOVR01,R02 ;千位、百位送R01 CALLS_BCD_BI ;调用单字节BCD码转二进制子程序,计算a3 × 10 + a2 LDA,R01 LDXL,A ;结果送寄存器XL中
LDA,#100 MULX,A ; (a3 × 10 + a2) × 100,结果在寄存器X中
CLRR02 ;清除R02单元
ADDWX,R02 ;按字相加
LDWR02,X ;结果回送R02、R03单元
RET例5-4将存放在R01单元中的二进制数转换为压缩形式的BCD码,结果存放在R02(百位)、R03(十位及个位)单元。参考程序如下:
;算法:待转换的二进制数除以100,所得的商是百位,余数再除以10所得的商是十位,余数为个位
;入口参数:待转换的二进制存放在R01单元中
;出口参数:百位存放在R02单元中,十位及个位存放在R03单元中
;使用资源:寄存器A、X及R02、R03单元
S_BI_BCD: ;单字节二进制转化为压缩形式的BCD码
CLRWX LDA,R01 LDXL,A LDA,#100 DIVX,A ;商(百位码)在XL中,余数在A中
EXGA,XL ;商与余数交换
LDR02,A ;保存百位码
LDA,#10 DIVX,A ;商(十位码)在XL中,余数(个位码)在A中
LDR03,A ;个位码先放R03LDA,XL ;取十位码SWAPA ;十位码转移到高4位ORA,R03 ;合并压缩形式的BCD码LDR03,ARET5.3.2循环结构循环程序结构由初始化、循环体、包含条件跳转指令的循环控制等三部分组成。例5-5
将ram0段(即00H~FFH)存储单元清0。由于需要清除256个单元,因此,不能再用例5-1~例5-4所示的顺序程序结构,否则程序会很长,须用循环结构程序实现。参考程序如下:;初始化LDWX,#ram0_start ;初始化索引寄存器X ;循环体Ram0_CLR_LOOP: CLR(X) ;把寄存器X内容对应单元清0 INCWX ; X加1,指向下一个存储单元
;循环控制指令CPWX,#ram0_end ;与ram0段的终了地址比较JRULERam0_CLR_LOOP ;当X不大于终了地址时,跳转到标号为LOOP处继续执行5.3.3分支程序结构分支程序也是一种常见的程序结构,常需要根据运算结果、某一个输入引脚的状态,决定是否执行相应的操作。根据分支多少,可将分支程序结构分为简单分支(即两分支)结构和多路分支结构。
1.简单分支简单分支常用条件转移指令实现,例如位测试转移指令,如下所示:
BTJTR00,#3,NEXT1
NEXT1:
2.多路分支在STM8指令系统中,00段内的多路分支可用无条件跳转指令JP(TAB_ADR,X)实现,为菜单、并行多任务程序结构中任务与作业切换操作等提供了方便。由于变址寄存器X为16位,因此可以实现超过256分支的散转。例5-6
编写一段程序完成32位除以16位的运算。假设32位被除数存放在R04、R05、R06、R07单元中,16位除数存放在R00、R01单元中。
STM8内核CPU没有32位除以16位运算指令,只能通过类似多项式除法完成。根据运算规则可知商为32位,余数为16位,因此需要把除数扩展为16 + 32 = 48位,然后通过移位相减获得对应位的商。参考程序如下:
DIV3216: ; 32位除以16位运算子程序(没有检查除数为0)
; 32bit/16bit运算程序
;入口参数:32位被除数存放在R04~R07单元中,高位放在低地址
; :16位除数存放在R00~R01单元中,高位放在低地址
;出口参数:32位商放在R04、R05、R06、R07单元中
; :余数放在R02、R03寄存器中
;算法:通过移位相减类似多项式除法实现,先将被除数扩展为32 + 16位,即48位
;执行时间约为584个机器周期;使用资源:X,ACLRR02CLRR03 ;扩展被除数为(32 + 16)位LDA,#32 ;定义移位相减次数DIV3216_LOOP1: SLLR07 ;左逻辑移位(C←b7,b7←b6,b0←0)RLCR06RLCR05RLCR04RLCR03RLCR02BCCMR07,#0 ; C移到R07的b0位LDWX,R02 ;取扩展被除数最高位SUBWX,R00JRNCDIV3216_NEXT1;
C
=
1,即有借位BTJFR07,#0,DIV3216_NEXT2 ;商已为0,无须再设置;移出位为1,说明也够减DIV3216_NEXT1:;够减,用差替换被减数BSETR07,#0 ; b0位置1,用差替换被减数LDWR02,XDIV3216_NEXT2:DECAJRNEDIV3216_LOOP1RET可见,实现32位除以16位运算程序段指令代码不长,执行时间大约为584个机器周期。用多项式除法运算规则实现除法运算时,一般情况下需要扩展被除数,但在特殊情况下,也可以不用扩展被除数。例如,当除数不小于8000H,即除数最高位b15为1,可不用扩展被除数,而是直接用被除数高位减除数,完成时间将极大地缩短,仅需300个机器周期,如例5-7所示。例5-7已知32位被除数存放在R04~R07单元中,不小于8000H的16位除数存放在R00~R01单元中,编写出相应的除法计算指令系列。参考程序如下:
DIV3216A: ;特殊的32位除以16位运算子程序
; 32 bit / 16 bit运算程序
;入口参数:32位被除数存放在R04~R07中,高位放在低地址
;不小于8000H的16位除数存放在R00~R01中,高位放在低地址
;出口参数:32位商放在R05(只有b0,即商的b16位有效)、R06、R07中;余数放在寄存器X中;算法:通过移位相减实现,由于除数不小于8000H,因此无须扩展被除数为48位;执行时间为300个机器周期;使用资源:寄存器X,ALDA,#16BRESR02,#0 ;开始时移出位为0LOOP1:LDWX,R04 ;取被除数最高位SUBWX,R00 ;减除数JRNCNEXT1;有借位BTJTR02,#0,NEXT1;有借位,且移出位为0,不够减,应保留原来的被减数 RCF ;商为0 JRTNEXT2NEXT1: LDWR04,X ;用差替换被除数高16位
SCF ;商为1NEXT2: RLCR07 ;移位
RLCR06 RLCR05 RLCR04 BCCMR02,#0 ; C送R02的b0位
DECA ;循环次数减1 JRNELOOP1 ;最后一次减
LDWX,R04 ;取被除数最高位
SUBWX,R00 JRNCNEXT3
BTJTR02,#0,NEXT3 ;有借位,且移出位为0,说明不够减,保留原来的差
LDWX,R04 RCF ;商为0 JRTNEXT4NEXT3: ;最后一次移位,形成商
SCF ;商为1NEXT4:
CLRR05 ;清除商的最高位
RLCR07 ;把最后运算结果移到商字节
RLCR06 RLCR05 RET在单片机应用系统中,有时用查表方式代替分支程序结构可能更简单,不仅代码短、运行速度快,程序维护、修改也非常方便,如例5-8所示。例5-8在某工程设计中,3个触发器Q2、Q1、Q0状态及其6个输出量y5~y0变化规律如表5-1所示,表中未列出的状态均属无效态。表5-1转换表如果采用条件跳转指令的分支程序结构完成状态判别及计算输出量y5~y0的值将很复杂,而采用查表方式完成就非常简单。根据表5-1状态转换对应的输出量,不难得出二维数表,即b5~b0对应y5~y0,b6未使用规定为0,b7为“1”时表示无效态,如下所示:Y5_Y0_TAB:;列编号为新状态(n+1),行编号为当前状态(n);01234567 dc.b01H,10H,02H,05H,05H,01H,06H,80H ;当前状态(0) dc.b80H,00H,20H,80H,25H,21H,24H,80H ;当前状态(1) dc.b80H,10H,00H,80H,05H,01H,04H,80H ;当前状态(2) dc.b09H,80H,80H,01H,80H,80H,80H,80H ;当前状态(3) dc.b80H,10H,0AH,80H,00H,08H,02H,80H ;当前状态(4) dc.b80H,10H,02H,80H,04H,00H,06H,80H ;当前状态(5) dc.b80H,10H,08H,80H,01H,09H,00H,80H ;当前状态(6) dc.b80H,80H,80H,80H,80H,80H,80H,80H ;当前状态(7)参考程序段如下:LDA,stu_Q2_0 ;取Q2~Q0当前状态ANDA,#07HSLLASLLASLLA ;每个旧状态对应8个新状态,因此要乘以8ADDA,R00 ;假设新状态存放在R00单元中CLRWXLDXL,A ;旧—新状态编号存放在寄存器X中LDA,(Y5_Y0_TAB,X) ;查表获得旧—新状态对应的输出量CPA,#80HJRNENEXT1;遇到非法状态,可根据需要强制系统复位或不处理dc.b05Hdc.b05Hdc.b05Hdc.b05Hdc.b05H ;本例强制执行非法指令使系统复位NEXT1:MOVstu_Q2_0,R00 ;更新状态变量stu_Q2_0LDR00,A ;把输出量送R00单元,以便通过位测试判别BTJFR00,#0,NEXT21; R00的b0位即y0为1
;执行y0为1的指令系列NEXT21:BTJFR00,#1,NEXT22;
R00的b1位即y1为1 ;执行y1为1的指令系列NEXT22: ;省略5.4并行多任务程序结构及实现5.4.1串行多任务程序结构与并行多任务程序结构在串行多任务程序结构中,按预先设定的顺序执行各任务(即模块),任何时候只执行其中的一个任务,如图5-7所示。
串行多任务程序结构简单、清晰,编写、调试比较容易,是单片机应用系统中最常用的程序结构之一。但在串行多任务程序结构中,只能通过查询(如果满足条件,则通过CALL及CALLF指令)和中断方式执行某些需要实时处理的事件,不适应于具有多个需要实时处理事件的应用系统。例如,在无线防盗报警器中,某防区报警时,第一要通过电话线将报警信息以DTMF方式发送到接警中心,或以语音方式通知用户;第二要监控其他防区有无被触发,即无线接收、解码不能停顿;第三要监视电话线状态,如忙音、回铃音、被叫方提机、断线等;第四是控制内置警笛的音量及音调。为此,在单片机应用系统中,有时需要用“实时(或称为并行)多任务”程序结构。由于单片机系统内嵌RAM存储器容量小,如STM8系列芯片的只有几KB,没有更多空间存放任务切换时需要保护的数据——断点(即PC指针)、现场(CPU内寄存器,如累加器A、通用寄存器、标志寄存器等)和中间结果,因此决定了单片机应用系统并行多任务程序结构与一般微机、小型机并行多任务操作系统程序结构有所不同。图5-7串行多任务程序结构5.4.2并行多任务程序结构并行多任务程序结构如图5-8所示。图5-8并行多任务程序结构把需要实时处理的多个任务排成一个队列,通过队列指针(也称为任务号),借助散转指令,如STM8的“JP(TAB_ADR,X)”指令(或条件转移指令)实现任务间的切换。每个任务执行时间长短不同,需将每一个任务细分为若干作业(或称为子过程),不同任务的作业量不尽相同,即作业量与任务本身的复杂程度有关。例如,在图5-8中的A任务,就分成A0、A1、A2、…、An,即n个作业。为此还需给每一个任务设置一个作业指针(或称为作业号),切换到某一个任务后,执行其中的哪一个作业由任务内的作业指针确定。在并行多任务程序结构中,各任务地位相同,每个任务内的作业地位也相同。并行多任务程序结构模块清晰,能方便地增减其中任一个模块。任务调度也很灵活,可根据当前作业的执行结果选择下一步将要执行的任务号,非常适合于需要实时处理的多任务控制系统,实用价值较高。在单片机并行多任务程序结构设计中,需要考虑的问题及注意事项如下:
(1)作业划分原则。为减少任务、作业切换时需要保护的数据量,任务内的每一个作业必须是一个完整的子过程。对于执行时间较长的任务,通过设置若干标志后,细分为多个作业,使每个作业执行时间不超出系统基本定时器的溢出时间。按上述原则划分作业后,在作业处理过程中,除中断外不被其他任务所中断,作业执行结束后,只需将处理结果保存到相应的RAM存储单元中,对于初始化处理类作业根本无须保存结果;无须保护现场,即CPU各寄存器的值,如STM8的寄存器CC、A、X、Y等。
(2)任务切换方式。在微机、小型机实时多任务操作系统中,一般按设定顺序执行各任务,即每一个作业执行结束后任务指针加1,当执行到最后一个任务时将指针切换到第一个任务。在单片机控制系统中,一般不宜采用“定时时间到切换”规则,如果系统中没有需要精确定时的事件,根本不需要定时器。原因是单片机系统时钟频率低,CPU响应速度慢,内嵌RAM容量有限,没有更多空间存放任务切换时需要保存的大量数据。另外,在单片机应用系统中,控制对象属性、控制对象所要执行的操作又非常明确,完全可根据当前作业的执行结果和系统的当前状态,直接切换到某个任务,以提高系统的实时性。这与十字路口交通灯切换时间最好由当前的车流量决定问题类似。例如,在报警器设计中,报警器上传接警中心信息可分为三大类:布防、撤防及报警信息(包括旁路信息及系统状态信息)。为减轻用户话费负担,这三类信息须分别拨打不同的电话号码。对于布防、撤防信息来说,一般只需了解该信息来自哪一个用户及其时间,完全可借助“来电显示”功能感知信息来源与发生的时间(用FSK来电信息帧内嵌时间或接警中心内部时钟),无须提机。对于布防、撤防以外的信息,如报警、旁路信息等,还需要进一步了解是哪个防区报警、旁路,什么样的警情等,需要提机接收报警器上传的全部信息。为此,在拨号前除了先根据缓冲区内信息类型确定拨打的电话号码外,在拨号过程中还必须根据缓冲区内出现的新信息,决定是继续拨号,还是挂机后拨打另一个号码。为进一步提高系统的即时处理能力,除了在每个作业执行结束后根据当前作业的执行结果、系统当前状态设置任务号外,还可在中断服务程序中重新设置任务号。
(3)通过中断方式响应需要实时处理的事件。在作业处理过程中,只能通过中断方式(包括定时器溢出中断、外部中断等)响应需要实时处理的事件。为避免中断服务程序执行时间过长,降低系统对同级中断请求响应的实时性,需要在中断服务程序中引入事件驱动方式:响应某一个中断请求后,仅设置事件发生标志或进行简单处理后就退出,而事件执行由事件处理程序完成,也就是说把中断响应与事件处理过程分离,以提高系统的实时响应速度。例如,DTMF解码芯片8870解码有效DV信号接STM8SMCU的外中断PD口的某一个引脚,则PD口中断服务程序如下:InterruptPD_Interrupt_procPD_Interrupt_proc.L BSETPA_ODR,#6 ;解码输出允许OE接PA6引脚
LDA,PI_IDR ;解码输出引脚D3~D0分别接MCU的PI3~PI0 ANDA,#0FH ;屏蔽与解码输入无关位
ORA,#80H ;将DTMF输入寄存器b7位置1,作为解码输入有效标志
LDDTMF_IN,A ;输入数据放入DTMF_IN变量后就退出,至于输入什么数据
;由解码输入处理程序判别和处理
BRESPA_ODR,#6 ;将8870解码输出置为高阻态
IRET系统中由主定时器控制的各定时时间必须呈整数倍关系,主定时器的溢出时间就是最小的定时时间。对于需要精确定时的事件,可放在系统主定时器中断服务程序中计时,定时时间到,相应标志位置1,处理程序检查相应标志来确定是否需要处理相应事件。例如,当主定时器溢出时间为10ms,而键盘定时扫描间隔为20ms时,那么,主定时器中断服务程序内与键盘扫描定时有关的指令为
DECKEY_TIMEJRNEEXIT_K_T
;键盘扫描定时器减1,不为0跳转BSETR_S_KEY,#0
;置位键盘扫描执行标志R_S_KEY[0]MOVKEY_TIME,#2
;重置键盘扫描定时时间EXIT_K_T:
(4)子程序调用原则。各任务内任意一个作业均可调用同一个子程序,但不允许中断服务程序调用,以免产生混乱。主定时器中断服务程序应尽量短小,当遇到处理时间较长的事件时,可通过设置执行标志后返回,在主程序任务调度处理过程中执行。下面是具有4个任务的“实时多任务”程序结构:Segment'ram0'BytesTASKPds.b1 ;任务指针TASK0P ds.b1 ;任务0作业指针;TASK0P_TIMEds.b1 ;任务0执行控制时间。对于有时间限制的任务,可设置
;任务执行时间TASK1Pds.b1 ;任务1作业指针;TASK1P_TIMEds.b1 ;任务1执行控制时间TASK2P ds.b1 ;任务2作业指针;TASK2P_TIMEds.b1 ;任务2执行控制时间TASK3P ds.b1 ;任务3作业指针;TASK3P_TIMEds.b1 ;任务3执行控制时间WordsSegment'rom'main.L;初始化部分MOVTASKP,#0 ;从任务0开始执行MOVTASK0P,#0 ;任务0从作业0开始MOVTASK1P,#0 ;任务1从作业0开始
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 墙地砖合同范本
- 全书定制合同范本
- 单位电脑采购合同范本
- 单位与保安合同范本
- 个人投资合伙合同范本
- 修路转让合同范例
- 入围合同范本
- 做线上合同范本
- 麻城外包型钢加固施工方案
- 乙方多个公司合同范本
- 河南省公安基础知识真题汇编1
- 内陆常规货物物流运输代理协议三篇
- 2024年江苏常州市教育基本建设与装备管理中心招聘3人历年高频难、易错点500题模拟试题附带答案详解
- 《护理交接班规范》课件
- 2022年新高考I卷读后续写David's run公开课课件-高三英语一轮复习
- 《语感与语言习得一:积累与探索》教案- 2023-2024学年高教版(2023)中职语文基础模块上册
- 粮油食材配送投标方案(大米食用油食材配送服务投标方案)(技术方案)
- 祭扫烈士实施方案
- 2024年中国电动助力汽车转向系统(EPS)行业市场现状、前景分析研究报告
- 2023年吉林省中考满分作文《成功源于勤奋》
- 2024-2030年中国后量子密码学行业运营动态及投资策略分析报告
评论
0/150
提交评论