PIC16系列单片机常用伪指令汇编Word文档格式.docx
- 文档编号:22434809
- 上传时间:2023-02-04
- 格式:DOCX
- 页数:23
- 大小:28.61KB
PIC16系列单片机常用伪指令汇编Word文档格式.docx
《PIC16系列单片机常用伪指令汇编Word文档格式.docx》由会员分享,可在线阅读,更多相关《PIC16系列单片机常用伪指令汇编Word文档格式.docx(23页珍藏版)》请在冰豆网上搜索。
中指定的全程文件路径下寻找该文件。
例3-01中”math.asm”没有指定路径,即意味着在
当前项目路径下寻找math.asm文件。
如果编译器找不到被包含的文件,将会有错误信息告
知。
请在你的源程序中尽量用MPLAB标准头文件定义的寄存器符号。
一来这些被定义的寄
存器符号和芯片数据手册上的描述一一对应,理解起来即直观又容易;
二来如果用你自己定
义符号就缺乏一个大家能一起交流的标准平台,其他人要解读你的代码时将费时费力。
故例
3-01中的首行#include包含引用伪指令可以说是PIC单片机程序编写时的标准必备。
list
list伪指令可以设定程序编译时的一些信息,例如所选单片机的型号,编译时选择的缺
省数制等。
例如:
listp=16f877a,r=DEC;
单片机型号为PIC16F877A,无特别指明的数字为十进制数
例3-02
如果程序开发时使用项目管理的模式,则所有list伪指令可以描述的参数项都可以在项
目的设定选项中通过对话框的形式设定并保存。
在此只需对list伪指令稍作了解即可。
__config
此伪指令的重要作用是把芯片的配置字设定在源程序中,请参阅2.5节的详细说明。
建
议大家尽量用此伪指令把芯片的配置字写在程序中。
__idlocs
PIC单片机中有一处非常特殊的标记单元。
它独立于任何其它存储器,唯一的作用就是
作为一个标记。
此标记值无法用软件读到,读取和写入的方法只有通过编程器实现。
此标记
值没有读保护,你可以利用它存放程序的版本或日期等信息。
如果需要,则可以用伪指令
__idloc在程序中定义具体的值。
__idloc0x1234;
设定芯片的标记值为0x1234,注意前面有两个下划线符
例3-03
和__config伪指令定义的配置字一样,用__idloc定义的芯片标记值在最后也会存放在
HEX文件中,这就要求编程器能够解析它。
errorlevel
errorlevel的用途是控制编译信息的输出显示。
编译器在编译你的源程序时会提供很多
信息,有些信息是你必须要处理的,例如错误信息(Error),只要有错误信息存在,你的程
序将永远无法完成编译;
有些可能只需要关注,例如警告信息(Warning);
也有一些可能你
根本就不感兴趣,它们只是一些提示信息(Message)而已。
注意出现警告和提示信息时将
不会中止编译器的编译工作,你的程序将被编译并最终产生HEX文件。
图3-14中显示了一
个程序编译后的各种信息实例,其中既有错误信息,也有警告和提示信息。
我们可以用
errorlevel伪指令来控制输出信息的级别,或刻意关闭/打开一些提示信息。
编译信息的输出显示级别有三种,分别是0、1和2。
级别0代表显示所有信息,包括
各种错误、警告和提示信息,如图3-14所示;
级别1代表显示错误和警告信息,忽略提示
信息;
级别3代表只显示错误信息而忽略警告和提示信息。
在任何一个大的级别上还可以对
某些信息单独设定显示或关闭。
每个信息都有一个识别标号,见图3-14中信息项“[]”中的
数字,打开或关闭某类信息只需在errorlevel伪指令中引用信息识别标号,并在其前面用“+”
或“-”号,即代表打开或关闭这一类信息,例如:
errorlevel0,-302,-305;
显示所有信息,但不需要302和305这两类提示信息
errorlevel1,+305;
显示错误和警告信息,但同时还要关注305类的提示信息
图3-14
例3-04
#define/#undefine
#define的作用是定义常数符号,即用一个符号变量替换另一个符号串或变量。
被替换
的可以是任意字母数字组成的符号但替换者本身不能是一个纯数字。
#defineDELAY_TIME1000;
定义常数符号,即用DELAY_TIME符号代替1000
#defineKEY1PORTB,7;
用KEY1符号代替端口PORTB的第7引脚
例3-05
用#define伪指令定义符号后,可使程序中的变量或指令变得更具实际意义,也使程序
变得更易维护。
指令“btfssPORTB,7”和“btfssKEY1”在事先用了例3-05中的#define后
编译的结果是一样的,但明显地后者看起来更容易理解,一看就知道这是在测试编号为
KEY1的一个按键。
而且如果你的硬件设计改动了KEY1所接的单片机引脚,只要改动这一
处#define重新定义引脚位置,程序的其它部分无需任何修改,再编译一次即可得到更新后
的软件代码。
一个好的编程习惯是事先把一些代表实际意义的变量、单片机的输入输出引脚
在硬件电路中的实际功能等用#define伪指令定义成简单直观的符号名字,然后在程序中直
接用其符号名字而不用简单机械的数字形式。
替换的工作由编译器在编译时自动完成。
它会
先扫描你的源程序代码,把事先#define的符号名改回成被替换的字符串,然后再继续编译
生产机器码。
equ
equ顾名思义是“等于”的意思,其作用和#define伪指令有点类似,也是用一个符号名
字替换其它数字变量,但它只能替换立即数。
如果要替换一个符号名字,则此符号名必须事
先用#define或equ伪指令已经定义替换了一个立即数。
#defineMyCount0x70;
定义MyCount符号替换立即数0x70
w_tempequ0x20;
符号名w_temp等于0x20
count1equMyCount;
符号名count1等同于MyCount
;
如果MyCount没有事先定义则会产生一个错误
例3-06
在绝对定位的编程模式中equ被经常用于定义用户自己的变量,即用一个符号名代替一
个固定的存储单元地址,上例3-06中的w_temp定义即属于此类。
用equ方式定义的符号在
汇编后可以生成相关的调试信息,可以通过各种变量观察的方式显示此符号所代表的内存地
址处的数据内容,但用#define方式定义的符号则不能产生调试信息。
要注意equ伪指令本
身并没有限定所定义的一定是一个变量地址,它只是一个简单的符号和数字替换而已,其意
义必须和具体的指令结合才能确定,如下例3-07中对符号w_temp的理解。
movlw0x55;
W=0x55
movwfw_temp;
把W的值送给变量w_temp,(0x20单元内容=0x55)
movfw_temp,w;
把w_temp单元内容送W,(W=0x55)
movwfFSR;
把W的内容送FSR,(FSR=0x55)
movlww_temp;
把w_temp所代表的立即数即地址值送给W,(W=0x20)
让FSR指针指向w_temp,(FSR=0x20而不是0x55)
例3-07
cblock/endc
用equ伪指令可以给一个符号变量分配一个地址。
但在一个程序设计过程中往往需要定
义很多变量,你当然可以给每一个变量逐个用equ的方法分配一个地址空间。
但如果变量很
多,这样做就显得非常麻烦,你必须自己安排每个变量的地址,小心不能出现地址重叠;
若
要在已定义分配好的变量间插入新的变量,那就必须重新逐个安排随后变量的地址等等。
cblock/endc伪指令可以轻松解决有很多变量定义的场合出现的这些问题,我们把它叫作变
量块连续定义。
具体用法如下:
cblock伪指令声明变量块的起始地址,endc伪指令声明变量块定义结束,cblock/endc
中间可以插入任意多的变量声明。
其地址编排由编译器自动计算:
第一个变量地址分配从起
始地址开始,然后按所声明变量保留的字节数自动分配后面变量的地址,变量所需保留的字
节数用“:
”加后面的数字表示,如果只有一个字节“:
1”可以省略不写。
以例3-08来说明:
cblock0x20;
变量定义起始地址为0x20
w_temp;
w_temp地址为0x20,占一个字节
status_temp;
status_temp地址为0x21,占一个字节
buffer:
8;
buffer的起始地址为0x22,并保留8个字节单元
var1;
var1的地址为0x2a,占一个字节
var2;
var2的地址为0x2b,占一个字节
endc;
结束变量连续定义
例3-08
用cblock方式定义的变量和用equ方式定义的变量一样在汇编后可以生成相关的调试
信息,可以通过各种变量观察的方式显示此符号所代表的内存地址和其中的数据内容,所以
实际编程时一般无需关心计算每个变量的具体地址。
程序员要注意的用这种方式连续定义很
多变量时不要让变量块跨越所处bank的边界。
你可以在cblock中随意插入新定义的变量,
或通过改变起始地址的方式使变量块整个挪到其它内存地址处,地址的更新由编译器代劳。
org
org用以定义程序代码的起始地址,通过此伪指令你可以把程序定位到任何可用的程序
空间,它实现的是程序代码绝对定位,如例3-09:
org0x0000;
定义复位入口地址,以下指令从地址0x0000开始
gotomain;
org0x0004;
定义中断入口地址,以下指令从地址0x0004开始
保存w
...;
其它中断服务代码
org0x0800;
定义page1的起始地址,以下指令代码放在page1
Sub1return
例3-09
只要你认为代码需要确定放在某一特定地址处,在程序的任何地方都可以用org伪指令
重新定义存放的起始地址,且地址顺序可以任意编排。
但要注意的是若干个确定起始地址的
代码块不能相互重叠,否则编译器会报错,无法得到正确结果。
若用可重定位方式开发指令
代码时一般不能用org伪指令绝对定位代码。
dt
dt的作用是定义表格数据。
在第一章介绍基本汇编指令时已经提到,PIC单片机实现表
格定义的最基本指令是“retlwxx”,表格中的每一个字节数据都以指令“retlw”的形式出现。
若表格较大,就需要很多“retlw”指令,比较麻烦,可读性也差。
这时我们可以用此“dt”
伪指令替代“retlw”实现很多数据的表格定义。
如例3-10:
TableaddwfPCL,f;
PC相对寻址查表
dt0;
retlw0
dt1,2,’3’;
retlw1
retlw2
retlw0x33(’3’的ASCII码)
dt”ABC”;
retlw’A’
retlw’B’
retlw’C’
例3-10
de
de伪指令可以让你在源程序中定义片内EEPROM的初值。
毫无疑问,该条伪指令只适
用于那些内含EEPROM数据存储器的单片机,例如:
PIC16F87x、PIC16F62x等等。
在中档
PIC单片机中,除了PIC16F7x系列外,其它Flash型的单片机都有片上EEPROM,只是字
节数多少的问题。
你可以编写代码在程序运行时来设定片内EEPROM数据区的初值,但此
EEPROM区还可以在芯片编程烧写时通过编程器对其设定初值。
对编程器而言EEPROM数
据区是程序空间的延伸,它有个特别的编程起始地址0x2100。
基于这一前提,我们可以在
源程序中利用“org”和“de”伪指令定义片内EEPROM数据的初值,这样最后得到的HEX
文件被烧入到单片机内后,EEPROM区就同时被特定数据所初始化。
看例3-11的实例
org0x2100;
特殊的程序空间起始地址
编程器能识别此地址作为EEPROM数据区的起始地址
de0,1,2,3;
EEPROM地址单元[0]=0,[1]=1,[2]=2,[3]=3
de”ABCD”;
[4]=0x41,[5]=0x42,[6]=0x43,[7]=0x44
例3-11
按例3-11所示的定义,芯片完成编程烧入后,其内部EEPROM区从0x00单元开始被
分别初始化成0x00、0x01、0x02、0x03、0x41、0x42、0x43、0x44。
其它未被初始化的EEPROM
单元全部是0xff。
要注意并不是所有的编程工具都能支持此法定义的EEPROM初始值烧入。
能直接挂接
在MPLAB环境下的Microchip原厂或兼容的编程工具都可以支持“de”伪指令定义的
EEPROM初值烧入,但其它第三方生产的编程工具就不一定,使用前请咨询编程器的生产
厂商。
fill
fill伪指令可以实现对程序空间连续自动填充某一特定的指令数据,被填充的可以是一
个立即数(实际肯定代表某一条指令),也可以是一条形象的汇编指令。
基本上在一个设计
中都有一些程序空间没有写上具体的指令编码(空白处),在单片机正常运行时这些地方的
指令是不会被执行到的。
但在有干扰的情况下程序跑飞正好落在这些非法指令处时,就有必
要设置软件陷阱捕捉这些非法跳转,让程序恢复正常运行。
如果要程序员一个一个地址去分
析哪里有空的指令单元然后又用特殊指令一条一条填入,这是根本行不通的。
fill伪指令在
这时就派上用场了。
fill0x0000,5;
从当前地址处连续5个程序字填成0x0000(NOP指令)
fill(goto$),NEXT_BLOCK-$;
从当前地址开始到标号NEXT_BLOCK前所有程序空间填上
goto$(死循环)指令
org0x0800
NEXT_BLOCK
例3-12
请大家特别注意上例3-12中第二行fill伪指令的用法。
在你自己的程序中也可以用同样
的方法把所有未用到的程序空间填上“goto$”这样一条死循环的指令。
一旦单片机执行过
程中非法跳到这些指令处时指令运行就将被“俘获”,停在那里直到看门狗复位,然后程序
从头开始。
这是软件陷阱的最基本处理方法。
若填充指令“goto0x0000”直接跳转到复位地
址处可能会有问题,因为goto指令执行时必须和PCLATH寄存器配合(跨页跳转的问题),
若PCLATH[4:
3]不为00就不能跳到复位地址0x0000处。
在程序跑飞非法跳转到设定的陷阱
处时你又怎能保证PCLATH中的页面设定为正好指向第0页?
end
end伪指令告诉汇编编译器编译工作到此为止,end后面所有的信息,不管正确与否,
一概不管。
绝大多数情形下你的程序的最后一行应该是“end”。
无论如何,end必须出现在
程序中,不然编译器会报错,无法进行编译工作。
3.2.4MPASM内的直接运算符
为了使所编的程序理解更直观,维护更方便,MPASM汇编器允许你在程序的编写过程
中直接以数学表达式的形式在指令中实现一些数字运算的功能。
千万不要误解成MPASM可
以替你生成数学运算的指令,那可是其它编译器(例如C编译器)才能完成的工作。
这里
讲的数字运算前提是所有参与运算的操作数全部是明明白白的立即数,如果是符号名字则必
须事先用#define或equ伪指令明确定义了的(如果是变量使用了这些符号则是地址运算,原先有明
确的地址)。
整个运算过程是由编译器在扫描你的源程序
时进行的,运算结果也只能是一个确定的立即数。
我们将在这里介绍几种非常有用的运算符。
取当前指令的地址值:
$
你可以在写程序时给一条指令前加上一个标号,然后直接引用该标而得到此程序字的地
址。
如果你的程序经常需要用到指令的当前地址或附近的地址值,这样的标号就需要写很多
且不能重复。
用“$”运算符让汇编器替你计算当前指令所处的位置将有效地减轻你的这份
工作量。
见例3-12和3-13。
用语句标号得到指令地址
HeregotoHere;
跳转到当前地址,程序进入死循环
Delaydecfszcount,f;
计数器减1并判0
gotoDelay;
跳转到上一行重复循环
用$运算符得到指令地址而无需定义任何语句标号
goto$;
decfszcount,f;
goto$-1;
跳转到(当前地址-1)处,即上一行,重复循环
例3-13
取16位立即数的高低字节:
high和low
一个16位的立即数在8位单片机中必须被拆解成高8位一个字节(高字节)和低8位
一个字节(低字节)才能用指令一条条处理,类似的处理在对两字节变量赋立即数初值和基
于PC相对跳转查表前设定PCLATH寄存器时经常碰到。
MPASM提供了high和low两个运
算符分别计算一个立即数的高字节和低字节。
我们看例3-14的代码实例:
两字节变量赋立即数初值
#defineDELAY_TIME.1000;
定义一个常数立即数
movlwlow(DELAY_TIME);
取立即数的低字节值,经编译器计算将得到0xe8
movwfcount;
赋给变量的低字节
movlwhigh(DELAY_TIME);
取立即数的高字节值,经编译器计算将得到0x03
movwfcount+1;
赋给变量的高字节(count已用EQU命令了,地址确定了,count+1表示count的下一个内存地址.)
查表前设定PCLATH寄存器。
关于PC相对跳转的概念详见1.5.2节
movlwhigh(Table);
取查找表入口地址的高字节值
movwfPCLATH;
设定PCLATH寄存器
movfindex,w;
取查表索引值
callTable;
调用查表子程序
例3-14
加减乘除:
+-*/
实际上前面的很多代码范例中都已经说明了“+”、“-”运算符的使用方法。
“*”和“/”
的运算也类似。
看下面例3-15计算异步串行通讯波特率常数的方法。
高速异步通信波特率BPS=Fosc/(16*(X+1))
故,波特率常数X=Fosc/(BPS*16)–1
#defineBPS.9600;
定义工作波特率
#defineFosc.4000000;
定义单片机工作振荡频率4MHz
其它代码
movlwFosc/(BPS*.16)–1;
编译器计算得到.25(10进制25)
movwfSPBRG;
设定波特率定时寄存器
例3-15
程序中用了统一的计算公式后,在调试时只要简单地改变前面的#define语句定义新的
波特率或振荡频率值,然后重新编译一次程序即实现了波特率设定代码的更新,非常方便。
移位运算:
和<
<
“>
”运算符把一个立即数算术右移若干位(高位补0),“<
”运算符把一个立即数算
术左移若干位(低位补0)。
#definexxx0x55
movlwxxx>
1;
W=0x2a
movlwxxx<
2;
W=0x54
movlw1<
7;
W=0x80
例3-16
立即数逻辑运算:
&
|^
“&
”运算符把一个立即数和另外一个立即数相“与”;
“|”运算符把一个立即数和另外
一个立即数相“或”;
“^”运算符把一个立即数和另外一个立即数相“异或”。
例3-17的代
码利用异或运算符“^”实现类似于C语言“switch-case”功能的汇编代码指令,注意例中
的VAL1、VAL2、VAL3等判别值都是事先已经定义的立即数而不是RAM中的变量。
利用异或运算实现类似于C语言的switch-case语句
movfswitchVal,w;
取分支判断值.switch(W)
xorlwVAL1;
W=W^VAL1
btfscSTATUS,Z;
判0标志
gotoCase_VAL1;
caseVAL1:
(原始W=VAL1)
xorlwVAL1^VAL2;
W=(W^VAL1)^(VAL1^VAL2)=W^VAL2
gotoCase_VAL2;
caseVAL2:
(原始W=VAL2)
xorlwVAL2^VAL3;
W=(W^VAL2)^(VAL2^VAL3)=W^VAL3
gotoCase_VAL3;
caseVAL3:
(原始W=VAL3)
其它case情况判别
例3-17
3.2.5MPASM的宏指令
引入宏指令的目的也是为了增强程序的可读性和易维护性。
和伪指令不同的是,伪指令
所起的只是辅助性的作用,其本身不会直接产生真正的机器码;
但宏指令是真正的指令,它
实际上是若干条基本汇编指令的集合。
为了编程方便,MPASM已经内含了一些非常好用的
宏指令,用户也可以自己编写任意形式的宏指令。
3.
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- PIC16 系列 单片机 常用 指令 汇编