PIC系列单片机程序设计基础(转)

[复制链接]
 楼主| paul1983 发表于 2008-12-17 23:09 | 显示全部楼层 |阅读模式
1、程序的基本格式<br />  先介绍二条伪指令:<br />  EQU&nbsp;——标号赋值伪指令<br />  ORG&nbsp;——地址定义伪指令<br />  PIC16C5X在RESET后指令计算器PC被置为全“1”,所以PIC16C5X几种型号芯片的复位地址为:<br />  &nbsp;PIC16C54/55:1FFH<br />  &nbsp;PIC16C56:3FFH<br />  &nbsp;PIC16C57/58:7FFH<br />  一般来说,PIC的源程序并没有要求统一的格式,大家可以根据自己的风格来编写。但这里我们推荐一种清晰明了的格式供参考。<br />  TITLE&nbsp;This&nbsp;is&nbsp;……&nbsp;;程序标题<br />  ;--------------------------------------<br />  ;名称定义和变量定义<br />  ;--------------------------------------<br />  F0&nbsp;   EQU&nbsp; 0<br />  RTCC&nbsp;  EQU&nbsp; 1<br />  PC&nbsp;   EQU&nbsp; 2<br />  STATUS&nbsp; EQU&nbsp; 3<br />  FSR   EQU&nbsp; 4<br />  RA&nbsp;   EQU&nbsp; 5<br />  RB&nbsp;   EQU&nbsp; 6<br />  RC&nbsp;   EQU&nbsp; 7&nbsp; <br />      &nbsp;┋<br />  PIC16C54&nbsp;EQU&nbsp;1FFH&nbsp;;芯片复位地址<br />  PIC16C56&nbsp;EQU&nbsp;3FFH<br />  PIC16C57&nbsp;EQU&nbsp;7FFH<br />  ;-----------------------------------------<br />  ORG&nbsp;PIC16C54&nbsp;GOTO&nbsp;MAIN&nbsp;  ;在复位地址处转入主程序<br />  ORG&nbsp; &nbsp;0&nbsp;         ;在0000H开始存放程序<br />  ;-----------------------------------------<br />  ;子程序区<br />  ;-----------------------------------------<br />  DELAY&nbsp;MOVLW&nbsp;255<br />     &nbsp;┋<br />    &nbsp; RETLW&nbsp;0<br />  ;------------------------------------------<br />  ;主程序区<br />  ;------------------------------------------<br />  MAIN<br />     &nbsp;MOVLW&nbsp;B‘00000000’<br />     &nbsp;TRIS&nbsp;RB&nbsp;      ;RB已由伪指令定义为6,即B口<br />      &nbsp;┋<br />  LOOP<br />      BSF&nbsp;RB,7&nbsp;CALL&nbsp;DELAY&nbsp;       <br />      BCF&nbsp;RB,7&nbsp;CALL&nbsp;DELAY<br />       &nbsp;┋<br />      GOTO&nbsp;LOOP<br />  ;-------------------------------------------<br />      &nbsp;END&nbsp;      ;程序结束<br />  &nbsp;注:MAIN标号一定要处在0页面内。
 楼主| paul1983 发表于 2008-12-17 23:09 | 显示全部楼层

程序设计基础

  2、程序设计基础<br />  1)&nbsp;设置&nbsp;I/O&nbsp;口的输入/输出方向<br />  PIC16C5X的I/O&nbsp;口皆为双向可编程,即每一根I/O&nbsp;端线都可分别单独地由程序设置为输入或输出。这个过程由写I/O控制寄存器TRIS&nbsp;f来实现,写入值为“1”,则为输入;写入值为“0”,则为输出。<br />      MOVLW&nbsp;0FH&nbsp; ;0000&nbsp;1111(0FH)<br />            输入&nbsp;输出<br />      TRIS&nbsp;6&nbsp;   ;将W中的0FH写入B口控制器,<br />            &nbsp;;B口高4位为输出,低4位为输入。<br />      MOVLW&nbsp;0C0H&nbsp;;&nbsp;11&nbsp;000000(0C0H)<br />             &nbsp;RB4,RB5输出0&nbsp;RB6,RB7输出1<br />  2)&nbsp;检查寄存器是否为零<br />  如果要判断一个寄存器内容是否为零,很简单,现以寄存器F10为例:<br />      MOVF&nbsp;10,1&nbsp;     ;F10→F10,结果影响零标记状态位Z<br />      BTFSS&nbsp;STATUS,Z&nbsp;   ;F10为零则跳<br />      GOTO&nbsp;NZ&nbsp;       ;Z=0即F10不为零转入标号NZ处程序<br />      &nbsp;┋&nbsp;         ;Z=1即F10=0处理程序<br />  3)&nbsp;比较二个寄存器的大小<br />  要比较二个寄存器的大小,可以将它们做减法运算,然后根据状态位C来判断。注意,相减的结果放入W,则不会影响二寄存器原有的值。<br />  例如F8和F9二个寄存器要比较大小:<br />      &nbsp;MOVF&nbsp;8,0&nbsp;      ;F8→W<br />      &nbsp;SUBWF&nbsp;9,0&nbsp;     ;F9—W(F8)→W<br />      &nbsp;BTFSC&nbsp;STATUS,Z   &nbsp;;判断F8=F9否<br />      &nbsp;GOTO&nbsp;F8=F9<br />      &nbsp;BTFSC&nbsp;STATUS,C&nbsp;   ;C=0则跳<br />      &nbsp;GOTO&nbsp;F9&gtF8&nbsp;      ;C=1相减结果为正,F9&gtF8<br />      &nbsp;GOTO&nbsp;F9&ltF8&nbsp;      ;C=0相减结果为负,F9&ltF8<br />        &nbsp;┋<br />  &nbsp;4)&nbsp;循环n次的程序<br />  如果要使某段程序循环执行n次,可以用一个寄存器作计数器。下例以F10做计数器,使程序循环8次。<br />      &nbsp;COUNT&nbsp;EQU&nbsp;10&nbsp;    ;定义F10名称为COUNT(计数器)<br />         &nbsp;┋<br />      &nbsp;MOVLW&nbsp;8<br />      &nbsp;MOVWF&nbsp;COUNT&nbsp;LOOP&nbsp;  ;循环体<br />   LOOP<br />          &nbsp;┋<br />      &nbsp;DECFSZ&nbsp;COUNT,1&nbsp;   ;COUNT减1,结果为零则跳<br />      &nbsp;GOTO&nbsp;LOOP&nbsp;      ;结果不为零,继续循环<br />          &nbsp;┋&nbsp;      ;结果为零,跳出循环<br />  &nbsp;5)“IF……THEN……”格式的程序<br />  下面以“IF&nbsp;X=Y&nbsp;THEN&nbsp;GOTO&nbsp;NEXT”格式为例。<br />      &nbsp;MOVF&nbsp;X,0&nbsp;     ;X→W<br />      &nbsp;SUBWF&nbsp;Y,0&nbsp;    ;Y—W(X)→W<br />      &nbsp;BTFSC&nbsp;STATUS,Z&nbsp;  ;X=Y&nbsp;否<br />      &nbsp;GOTO&nbsp;NEXT&nbsp;     ;X=Y,跳到NEXT去执行。<br />         &nbsp;┋&nbsp;      ;X≠Y<br />  &nbsp;6)“FOR……NEXT”格式的程序<br />  “FOR……NEXT”程序使循环在某个范围内进行。下例是“FOR&nbsp;X=0&nbsp;TO&nbsp;5”格式的程序。F10放X的初值,F11放X的终值。<br />      START&nbsp; EQU&nbsp; 10<br />      DAEND&nbsp; EQU&nbsp; 11<br />          &nbsp;┋<br />      MOVLW&nbsp;0<br />      MOVWF&nbsp;START&nbsp;    ; 0→START(F10)<br />      MOVLW&nbsp;5<br />      MOVWF&nbsp;DAEND&nbsp;    ;5→DAEND(F11)<br />  &nbsp;LOOP<br />          &nbsp;┋<br />     &nbsp;INCF&nbsp;START,1&nbsp;    ;START值加1<br />     &nbsp;MOVF&nbsp;START,0<br />     &nbsp;SUBWF&nbsp;DAEND,0&nbsp;    ;START=DAEND&nbsp;?(X=5否)<br />     &nbsp;BTFSS&nbsp;STATUS,Z<br />     &nbsp;GOTO&nbsp;LOOP       &nbsp;;X<5,继续循环<br />          &nbsp;┋&nbsp;      ;X=5,结束循环<br />  &nbsp;7)“DO&nbsp;WHILE……END”格式的程序<br />  “DO&nbsp;WHILE……END”程序是在符合条件下执行循环。下例是“DO&nbsp;WHILE&nbsp;X=1”格式的程序。F10放X的值。<br />     &nbsp;X&nbsp; EQU&nbsp; 10<br />       &nbsp;┋<br />     &nbsp;MOVLW&nbsp; 1<br />     &nbsp;MOVWF&nbsp; X&nbsp;    ;1→X(F10),作为初值<br />  &nbsp;LOOP<br />       &nbsp;┋<br />     &nbsp;MOVLW&nbsp;1<br />     &nbsp;SUBWF&nbsp;X,0<br />     &nbsp;BTFSS&nbsp;STATUS,Z&nbsp;  ;X=1否?<br />     &nbsp;GOTO&nbsp;LOOP&nbsp;     ;X=1继续循环<br />       &nbsp;┋&nbsp;       ;X≠1跳出循环<br />  &nbsp;8)&nbsp;查表程序<br />  查表是程序中经常用到的一种操作。下例是将十进制0~9转换成7段LED数字显示值。若以B口的RB0~RB6来驱动LED的a~g线段,则有如下关系:<br /><br /> &nbsp;    <br /><br />  设LED为共阳,则0~9数字对应的线段值如下表:&nbsp;十进数&nbsp;线段值&nbsp;十进数&nbsp;线段值<br />0&nbsp;C0H&nbsp;5&nbsp;92H<br />1&nbsp;C9H&nbsp;6&nbsp;82H<br />2&nbsp;A4H&nbsp;7&nbsp;F8H<br />3&nbsp;B0H&nbsp;8&nbsp;80H<br />4&nbsp;99H&nbsp;9&nbsp;90H<br /><br />  PIC的查表程序可以利用子程序带值返回的特点来实现。具体是在主程序中先取表数据地址放入W,接着调用子程序,子程序的第一条指令将W置入PC,则程序跳到数据地址的地方,再由“RETLW”指令将数据放入W返回到主程序。下面程序以F10放表头地址。<br />      MOVLW&nbsp; TABLE&nbsp;    ;表头地址→F10&nbsp; <br />      MOVWF&nbsp; 10<br />         &nbsp;┋<br />      MOVLW &nbsp;1&nbsp;       ;1→W,准备取“1”的线段值<br />      ADDWF&nbsp; 10,1&nbsp;     ;F10&nbsp;W&nbsp;=“1”的数据地址<br />      CALL &nbsp;CONVERT<br />      MOVWF&nbsp; 6&nbsp;       ;线段值置到B口,点亮LED<br />         &nbsp;┋<br />  CONVERT&nbsp;MOVWF &nbsp;2       &nbsp;;W→PC&nbsp;TABLE<br />      RETLW&nbsp; 0C0H&nbsp;     ;“0”线段值<br />      RETLW&nbsp; 0F9H&nbsp;     ;“1”线段值<br />         &nbsp;┋<br />      RETLW&nbsp; 90H&nbsp;      ;“9”线段值<br />  &nbsp;9)“READ……DATA,RESTORE”格式程序<br />  “READ……DATA”程序是每次读取数据表的一个数据,然后将数据指针加1,准备取下一个数据。下例程序中以F10为数据表起始地址,F11做数据指针。<br />      POINTER&nbsp; EQU&nbsp; 11&nbsp;  ;定义F11名称为POINTER<br />         &nbsp;┋<br />      MOVLW&nbsp;  DATA<br />      MOVWF&nbsp;  10&nbsp;    ;数据表头地址→F10<br />      CLRF&nbsp;  POINTER&nbsp;  ;数据指针清零<br />         &nbsp;┋<br />      MOVF&nbsp;  POINTER,0&nbsp; <br />      ADDWF&nbsp;10,0&nbsp;     ;W&nbsp;=F10&nbsp;POINTER<br />         &nbsp;┋<br />     &nbsp;INCF&nbsp;   POINTER,1&nbsp; ;指针加1<br />     &nbsp;CALL&nbsp;CONVERT&nbsp;     ;调子程序,取表格数据<br />         &nbsp;┋<br />  CONVERT&nbsp;MOVWF  &nbsp;2&nbsp;   ;数据地址→PC<br />  DATA&nbsp; RETLW&nbsp;  20H&nbsp;   ;数据<br />         &nbsp;┋<br />     &nbsp;RETLW&nbsp;15H&nbsp;     ;数据<br />  如果要执行“RESTORE”,只要执行一条“CLRF&nbsp;POINTER”即可。<br />  10)&nbsp;延时程序<br />  如果延时时间较短,可以让程序简单地连续执行几条空操作指令“NOP”。如果延时时间长,可以用循环来实现。下例以F10计算,使循环重复执行100次。<br />     &nbsp;MOVLW&nbsp;D‘100’<br />     &nbsp;MOVWF&nbsp;10<br />  LOOP&nbsp; DECFSZ&nbsp;10,1&nbsp;  ;F10—1→F10,结果为零则跳<br />     &nbsp;GOTO&nbsp;LOOP<br />      &nbsp;┋<br />  延时程序中计算指令执行的时间和即为延时时间。如果使用4MHz振荡,则每个指令周期为1μS。所以单周期指令时间为1μS,双周期指令时间为2μS。在上例的LOOP循环延时时间即为:(1&nbsp;2)*100&nbsp;2=302(μS)。在循环中插入空操作指令即可延长延时时间:<br />      MOVLW&nbsp; D‘100’<br />      MOVWF&nbsp; 10<br />  LOOP&nbsp;  NOP<br />      &nbsp;NOP<br />      &nbsp;NOP<br />      DECFSZ&nbsp;10,1<br />      GOTO&nbsp;LOOP<br />       &nbsp;┋<br />  延时时间=(1&nbsp;1&nbsp;1&nbsp;1&nbsp;2)*100&nbsp;2=602(μS)。<br />  用几个循环嵌套的方式可以大大延长延时时间。下例用2个循环来做延时:<br />      MOVLW&nbsp;  D‘100’<br />      MOVWF&nbsp;  10<br />  LOOP  MOVLW&nbsp;  D‘16’<br />      MOVWF&nbsp;  11<br />  LOOP1&nbsp; DECFSZ&nbsp;  11,1<br />      GOTO&nbsp;   LOOP1<br />      DECFSZ&nbsp;  10,1<br />      GOTO&nbsp;LOOP<br />      &nbsp;┋<br />  延时时间=1&nbsp;1&nbsp;[1&nbsp;1&nbsp;(1&nbsp;2)*16-1&nbsp;1&nbsp;2]*100-1=5201(μS)<br />  11)&nbsp;RTCC计数器的使用<br />  RTCC是一个脉冲计数器,它的计数脉冲有二个来源,一个是从RTCC引脚输入的外部信号,一个是内部的指令时钟信号。可以用程序来选择其中一个信号源作为输入。RTCC可被程序用作计时之用;程序读取RTCC寄存器值以计算时间。当RTCC作为内部计时器使用时需将RTCC管脚接VDD或VSS,以减少干扰和耗电流。下例程序以RTCC做延时:<br />      RTCC&nbsp; EQU&nbsp; 1<br />      &nbsp;┋<br />      CLRF&nbsp; RTCC&nbsp;   ;RTCC清0<br />      MOVLW&nbsp; 07H<br />      OPTION&nbsp;   ;选择预设倍数1:256→RTCC<br />  &nbsp;LOOP&nbsp; MOVLW&nbsp; 255&nbsp;  ;RTCC计数终值<br />      SUBWF&nbsp; RTCC,0<br />      BTFSS&nbsp;STATUS,Z&nbsp;  ;RTCC=255?<br />      GOTO&nbsp;LOOP<br />       ┋<br />  这个延时程序中,每过256个指令周期RTCC寄存器增1(分频比=1:256),设芯片使用4MHz振荡,则:<br />  延时时间=256*256=65536(μS)<br />  RTCC是自振式的,在它计数时,程序可以去做别的事情,只要隔一段时间去读取它,检测它的计数值即可。<br />  12)&nbsp;寄存器体(BANK)的寻址<br />  对于PIC16C54/55/56,寄存器有32个,只有一个体(BANK),故不存在体寻址问题,对于PIC16C57/58来说,寄存器则有80&nbsp;个,分为4个体(BANK0-BANK3)。在对F4(FSR)的说明中可知,F4的bit6和bit5是寄存器体寻址位,其对应关系如下:<br /><br />Bit6&nbsp; Bit5&nbsp;BANK&nbsp;物理地址<br /> 0 &nbsp;  0BANK0&nbsp;10H~1FH<br /> 0&nbsp;   1BANK1&nbsp;30H~3FH<br /> 1&nbsp;   0BANK2&nbsp;50H~5FH<br /> 1   &nbsp;1BANK3&nbsp;70H~7FH<br /><br />  当芯片上电RESET后,F4的bit6,bit5是随机的,非上电的RESET则保持原先状态不变。<br />  下面的例子对BANK1和BANK2的30H及50H寄存器写入数据。<br />  例1.(设目前体选为BANK0)<br />      BSF  &nbsp;4,5   &nbsp;;置位bit5=1,选择BANK1<br />      MOVLW &nbsp;DATA<br />      MOVWF&nbsp; 10H&nbsp;   ;&nbsp;DATA→30H<br />      BCF  &nbsp;4,5<br />      BSF&nbsp;  4,6&nbsp;  ;bit6=1,bit5=0选择BANK2<br />      MOVWF&nbsp; 10H&nbsp;   ;DATA→50H<br />  从上例中我们看到,对某一体(BANK)中的寄存器进行读写,首先要先对F4中的体寻址位进行操作。实际应用中一般上电复位后先清F4的bit6和bit5为0,使之指向BANK0,以后再根据需要使其指向相应的体。<br />  注意,在例子中对30H寄存器(BANK1)和50H寄存器(BANK2)写数时,用的指令“MOVWF&nbsp;10H”中寄存器地址写的都是“10H”,而不是读者预期的“MOVWF&nbsp;30H”和“MOVWF&nbsp;50H”,为什么?<br />  让我们回顾一下指令表。在PIC16C5X的所有有关寄存器的指令码中,寄存寻址位都只占5个位:fffff,只能寻址32个(00H—1FH)寄存器。所以要选址80个寄存器,还要再用二位体选址位PA1和PA0。当我们设置好体寻址位PA1和PA0,使之指向一个BANK,那么指令“MOVWF&nbsp;10H”就是将W内容置入这个BANK中的相应寄存器内(10H,30H,50H,或70H)。<br />  有些设计者第一次接触体选址的概念,难免理解上有出入,下面是一个例子:<br />  例2:(设目前体选为BANK0)<br />      MOVLW&nbsp; 55H <br />      MOVWF&nbsp; 30H&nbsp;  ;欲把55H→30H寄存器<br />      MOVLW&nbsp; 66H<br />      MOVWF&nbsp; 50H&nbsp;  ;欲把66H→50H寄存器<br />  以为“MOVWF&nbsp;30H”一定能把W置入30H,“MOVWF&nbsp;50H”一定能把W置入50H,这是错误的。因为这两条指令的实际效果是“MOVWF&nbsp;10H”,原因上面已经说明过了。所以例2这段程序最后结果是F10H=66H,而真正的F30H和F50H并没有被操作到。<br />  建议:为使体选址的程序清晰明了,建议多用名称定义符来写程序,则不易混淆。&nbsp;  例3:假设在程序中用到BANK0,BANK1,BANK2的几个寄存器如下:<br /><br />BANK0&nbsp;地址&nbsp;BANK1&nbsp;地址&nbsp;BANK2&nbsp;地址&nbsp;BANK3&nbsp;地址<br />A&nbsp;10H&nbsp;B&nbsp;30H&nbsp;C&nbsp;50H&nbsp;·&nbsp;70H<br />·&nbsp;·&nbsp;·&nbsp;·&nbsp;·&nbsp;·&nbsp;·&nbsp;·<br />·&nbsp;·&nbsp;·&nbsp;·&nbsp;·&nbsp;·&nbsp;·&nbsp;·<br /><br />       A  &nbsp;EQU&nbsp; 10H&nbsp;  ;BANK0<br />       B&nbsp;  EQU&nbsp; 10H&nbsp;  ;BANK1<br />       C&nbsp;  EQU&nbsp; 10H&nbsp;  ;BANK2<br />         &nbsp;┋<br />       FSR  EQU&nbsp; 4<br />       Bit6&nbsp; EQU&nbsp; 6<br />       Bit5 &nbsp;EQU&nbsp; 5<br />       DATA&nbsp; EQU&nbsp; 55H<br />         &nbsp;┋<br />       MOVLW&nbsp; DATA<br />       MOVWF&nbsp; A&nbsp; <br />       BSF&nbsp;  FSR,Bit5<br />       MOVWF&nbsp; B&nbsp;    ;DATA→F30H<br />       BCF&nbsp;  FSR,Bit5<br />       BSF&nbsp;  FSR,Bit6<br />       MOVWF&nbsp; C&nbsp;    ;DATA→F50H<br />         &nbsp;┋<br />  程序这样书写,相信体选址就不容易错了。<br />  13)&nbsp;程序跨页面跳转和调用<br />  下面介绍PIC16C5X的程序存储区的页面概念和F3寄存器中的页面选址位PA1和PA0两位应用的实例。<br />  (1)“GOTO”跨页面<br />  &nbsp;例:设目前程序在0页面(PAGE0),欲用“GOTO”跳转到1页面的某个地方<br />KEY(PAGE1)。<br />      &nbsp;STATUS &nbsp;EQU&nbsp; 3<br />      &nbsp;PA1&nbsp;  EQU&nbsp; 6<br />      &nbsp;PA0&nbsp;  EQU&nbsp; 5<br />          &nbsp;┋<br />      &nbsp;BSF&nbsp; STATUS,PA0&nbsp; ;PA0=1,选择PAGE页面<br />      &nbsp;GOTO&nbsp; KEY&nbsp;     ;跨页跳转到1页面的KEY<br />          &nbsp;┋<br />      &nbsp;KEY&nbsp;  NOP&nbsp;    ;1页面的程序<br />          &nbsp;┋<br />  (2)“CALL”跨页面<br />  例:设目前程序在0页面(PAGE0),现在要调用——放在1页面(PAGE1)的子程序DELAY。<br />          &nbsp;┋<br />      &nbsp;BSF&nbsp; STATUS,PA0&nbsp;  ;PA0=1,选择PAGE1页面<br />      &nbsp;CALL&nbsp; DELAY&nbsp;     ;跨页调用<br />      &nbsp;BCF&nbsp; STATUS,PA0&nbsp;  ;恢复0页面地址<br />          &nbsp;┋<br />      &nbsp;DELAY&nbsp;NOP&nbsp;      ;1页面的子程序<br />          &nbsp;┋<br />  注意:程序为跨页CALL而设了页面地址,从子程序返回后一定要恢复原来的页面地址。<br />  (3)程序跨页跳转和调用的编写<br />  读者看到这里,一定要问:我写源程序(.ASM)时,并不去注意每条指令的存放地址,我怎么知道这个GOTO是要跨页面的,那个CALL是需跨页面的?的确,开始写源程序时并知道何时会发生跨页面跳转或调用,不过当你将源程序汇编时,就会自动给出。当汇编结果显示出:<br />      &nbsp;X&nbsp;X&nbsp;X(地址)“GOTO&nbsp;out&nbsp;of&nbsp;Range&quot;<br />      &nbsp;X&nbsp;X&nbsp;X(地址)“CALL&nbsp;out&nbsp;of&nbsp;Range&quot;<br />  这表明你的程序发生了跨页面的跳转和调用,而你的程序中在这些跨页GOTO和CALL之前还未设置好相应的页面地址。这时应该查看汇编生成的.LST文件,找到这些GOTO和CALL,并查看它们要跳转去的地址处在什么页面,然后再回到源程序(.ASM)做必要的修改。一直到你的源程序汇编通过(0&nbsp;Errors&nbsp;and&nbsp;Warnnings)。<br />  &nbsp;(4)程序页面的连接<br />  程序4个页面连接处应该做一些处理。一般建议采用下面的格式:&nbsp;即在进入另一个页面后,马上设置相应的页面地址位(PA1,PA0)。&nbsp;页面处理是PIC16C5X编程中最麻烦的部分,不过并不难。只要做了一次实际的编程练习后,就能掌握了。

评分

参与人数 1威望 +1 收起 理由
weimao228 + 1

查看全部评分

 楼主| paul1983 发表于 2008-12-17 23:10 | 显示全部楼层

PIC 8位单片机汇编语言常用指令

&nbsp;&nbsp;&nbsp;各大类单片机的指令系统是没有通用性的,它是由单片机生产厂家规定的,所以用户必须遵循厂家规定的标准,才能达到应用单片机的目的。<br />  PIC&nbsp;8位单片机共有三个级别,有相对应的指令集。基本级PIC系列芯片共有指令33条,每条指令是12位字长;中级PIC系列芯片共有指令35条,每条指令是14位字长;高级PIC系列芯片共有指令58条,每条指令是16位字长。其指令向下兼容。<br /><br />  在这里笔者介绍PIC&nbsp;8位单片机汇编语言指令的组成及指令中符号的功能,以供初学者阅读相关书籍和资料时快速入门。<br /><br />  一、PIC汇编语言指令格式<br />  PIC系列微控制器汇编语言指令与MCS-51系列单片机汇编语言一样,每条汇编语言指令由4个部分组成,其书写格式如下:<br />  标号 操作码助记符 操作数1,操作数2;注释<br />  指令格式说明如下:指令的4个部分之间由空格作隔离符,空格可以是1格或多格,以保证交叉汇编时,PC机能识别指令。<br />  1?北旰拧∮隡CS-51系列单片机功能相同,标号代表指令的符号地址。在程序汇编时,已赋以指令存储器地址的具体数值。汇编语言中采用符号地址(即标号)是便于查看、修改,尤其是便于指令转移地址的表示。标号是指令格式中的可选项,只有在被其它语句引用时才需派上标号。在无标号的情况下,指令助记符前面必须保留一个或一个以上的空格再写指令助记符。指令助记符不能占用标号的位置,否则该助记符会被汇编程序作标号误处理。<br />  书写标号时,规定第一字符必须是字母或半角下划线“—”,它后面可以跟英文和数字字符、冒号(:)制符表等,并可任意组合。再有标号不能用操作码助记符和寄存器的代号表示。标号也可以单独占一行。<br />  2?辈僮髀胫?记符 该字段是指令的必选项。该项可以是指令助记符,也可以由伪指令及宏命令组成,其作用是在交叉汇编时,“指令操作码助记符”与“操作码表”进行逐一比较,找出其相应的机器码一一代之。<br />  3?辈僮魇? 由操作数的数据值或以符号表示的数据或地址值组成。若操作数有两个,则两个操作数之间用逗号(,)分开。当操作数是常数时,常数可以是二进制、八进制、十进制或十六进制数。还可以是被定义过的标号、字符串和ASCⅡ码等。具体表示时,规定在二进制数前冠以字母“B”,例如&nbsp;B10011100;八进制数前冠以字母“O”,例如O257;十进制数前冠以字母“D”,例如D122;十六进制数前冠以“H”,例如H2F。在这里&nbsp;PIC&nbsp;8位单片机默认进制是十六进制,在十六进制数之前加上Ox,如H2F可以写成Ox2F。<br />  指令的操作数项也是可选项。<br />  &nbsp;PIC系列与MCS-51系列8位单片机一样,存在寻址方法,即操作数的来源或去向问题。因PIC系列微控制器采用了精简指令集(RISC)结构体系,其寻址方式和指令都既少而又简单。其寻址方式根据操作数来源的不同,可分为立即数寻址、直接寻址、寄存器间接寻址和位寻址四种。所以PIC系列单片机指令中的操作数常常出现有关寄存器符号。有关的寻址实例,均可在本文的后面找到。<br />  4?弊⑹汀∮美炊猿绦蜃餍┧得鳎?便于人们阅读程序。注释开始之前用分号(;)与其它部分相隔。当汇编程序检测到分号时,其后面的字符不再处理。值得注意:在用到子程序时应说明程序的入口条件、出口条件以及该程序应完成的功能和作用。<br /><br />  二、清零指令(共4条)<br />  1?奔拇嫫髑辶阒噶?<br />  实例:CLRW;寄存器W被清零<br />  说明:该条指令很简单,其中W为PIC单片机的工作寄存器,相当于MCS-51系列单片机中的累加器A,CLR是英语Clear的缩写字母。<br />  2?笨疵殴范ㄊ逼髑辶阒噶睢?<br />  实例:CLRWDT;看门狗定时器清零(若已赋值,同时清预分频器)<br />  说明:WDT是英语Watchdog&nbsp;Timer的缩写字母。CLR见上述说明。注意该两条指令无操作数。<br />  3?奔拇嫫鱢清零指令。指令格式:CLRF&nbsp;f<br />  实例:CLRF&nbsp;TMRO;对TMRO清零<br />  说明:在PIC系列8位单片机中,常用符号F(或f)代表片内的各种寄程器和F的序号地址。F取值按PIC系列不同型号而不同,一般为&nbsp;Ox00~Ox1F/7F/FF。TMRO代表定时器/计数器TMRO,所以CLRF对寄程器清零,采用了直接寻址方式直接给出要访问的寄存器TMRO。<br />  4?蔽磺辶阒噶睢V噶罡袷?&nbsp;BCF&nbsp;f,b<br />  实例:BCF&nbsp;REG1,2;把寄存器REG1的D2位清零<br />  说明:BCF是英语Bit&nbsp;Clear&nbsp;F的缩写。指令格式中的F,同上说明;符号b是表示PIC片内某个8位数据寄存器F的位号(或位地址),所以b的取值为0~7或D0~D7。实例中REG&nbsp;是Register的缩写。实例中的2代表指令格式中的b=2即寄存器REG1的D2位。<br />  通过上述四条清零指令格式和实例,可以说明,学习PIC系列8位单片机的指令时应首先了解指令的助记符意义(功能),再有就是它的表达方式。初学者没有必要死记指令,重要是理解和实践。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;三、面向字节、常数与控制操作的指令<br />  1?贝?送立即数至工作寄存器W指令<br />  指令格式:MOVLW&nbsp;k;k表示常数、立即数和标号<br />  说明:MOVLW是Move&nbsp;Literal&nbsp;to&nbsp;w的缩写<br />  实例:MOVL&nbsp;0x1E;常数30送W<br />  2?盗/O口控制寄存器TRIS设置指令<br />  指令格式;TRIS f<br />  说明;TRIS&nbsp;f是Load&nbsp;TRIS&nbsp;Register的缩写。其功能是把工作寄存器W的内容送入I/O口控制寄存器f。当W=0时,置对应I/O口为输出;W=1,置I/O口为输入。<br />  实例:MOVLW&nbsp;0x00&nbsp;;把00H送入W<br />    &nbsp;TRIS&nbsp;RA&nbsp;;置PIC&nbsp;RA口为输出<br />    &nbsp;MOVLW&nbsp;0xFF&nbsp;;把FFH送入W<br />    &nbsp;TRIS&nbsp;RB&nbsp;;置PIC&nbsp;RB口为输入<br />  说明:这是PIC汇编语言中常用的几条指令,即设置某个I/O口(这里是RA口和RB口)为输入或输出的语句。可见,识读指令时,一应充分理解语句格式的功能,二应前后联系阅读。<br />  3?盬寄存器内容送寄存器f(W内容保持不变)指令<br />  指令格式:MOVWF&nbsp;f<br />  说明:MOVWF是Move&nbsp;W&nbsp;to&nbsp;f的缩写<br />  实例:MOVLW&nbsp;0x0B;送0BH送W<br />    &nbsp;MOVWF&nbsp;6&nbsp;;送W内容到RB口<br />  说明:第一条指令0x0B(常数11)送工作寄存器W,第二条指令,把W内容常数11送到寄存器F6中,查表F6即为RB口,所以PORT_B(B口)=0BH=D11<br />  4?奔拇嫫鱢传送指令<br />  指令格式:MOVF&nbsp;f,d<br />  说明:MOVF是Move&nbsp;f的缩写。F代表PIC中的某个寄存器。指令中的d规定:d=0时,f内容送W;d=1时,f内容送寄存器。<br />  实例:MOVF&nbsp;6,0&nbsp;;RB口内容送W<br />    &nbsp;MOVWF&nbsp;8 &nbsp;;RB口内容送f8<br />  说明:第一条指令中的6代表寄存器f=6,查寄存器表f=6为RB口;0代表d=0,代表选择的目标为寄存器W。第二条指令中的8代表寄存器f=8。所以两条指令结果是把RB口的内容送f8。至于f8内容是多少?还应在汇编语言开始时附加指令,这里从略。<br />  5?笨詹僮髦噶?<br />  指令格式:NOP<br />  说明:NOP是英语No&nbsp;Operation的缩写。NOP无操作数,所以称为空操作。执行NOP指令只使程序计数器PC加1,所以占用一个机器周期。<br />  实例:MOVLW&nbsp;0xOF  &nbsp;;送OFH到W<br />    &nbsp;MOVWF&nbsp;PORT_B&nbsp;;W内容写入B口<br />    &nbsp;NOP     &nbsp;;空操作<br />    &nbsp;MOVF&nbsp;PORT_B,W&nbsp;;读操作<br />  说明:该三条指令是一种对I/O口的B口连续操作的实例,其目的达到写入B口的内容要读出时,应保证写、读之间有个稳定时间,因此加入了空操作指令NOP。<br />  6?蔽尢跫?跳转指令<br />  指令格式:GOTO&nbsp;k<br />  说明:执行该条指令时,将指令转移到指定的地址(跳转)。指令中的k,常与程序中的标号联系起来。<br />  实例:见第9条指令中<br />  7?奔拇嫫髂谌菁?1,结果为零的间跳指令<br />  指令格式:DECFSZ&nbsp;f,d<br />  说明:DECFSZ是英语Decrement&nbsp;f,Skip&nbsp;of&nbsp;not&nbsp;0的缩写。符号f,d代表的意义,前述已作说明。该条指令是指寄存器的内容减1存入W(d=0)或f(d=1)中。若指令执行结果减1不为零,指令顺序执行;为零时,就间跳下一条指令后再执行(等效顺序执行一条空指令NOP),实际指令中,当d=1时,该项常被略去。<br />  8?奔拇嫫髂谌菁?1,结果为零间跳指令<br />  指令格式:INCFSZ&nbsp;f,d<br />  说明:INCFSZ是英语Increment&nbsp;f,Skip&nbsp;of&nbsp;0的缩写。该条指令与上一条(7)指令差别仅在于“1”上,即执行这条指令时,寄存器f内容加1,若结果不为零,则指令顺序执行;为零则指令间跳执行。执行这条指令的其它逻辑关系与上条相同。<br />  9?弊映绦蚍祷刂噶?<br />  指令格式:RETLW&nbsp;k<br />  说明:RETLW是Return&nbsp;Literal&nbsp;to&nbsp;W的缩写。该指令代表子程序返回,返回前先把8位立即数送W。<br />  实例:PIC某个汇编语言的延时子程序(摘要):<br />     (1)BELY&nbsp;MOVLW&nbsp;0xC5&nbsp;;送延时常数0C5H入W<br />     (2)  &nbsp;MOVWF&nbsp;COUNT2;0C5H送入计数器2<br />     (3)  &nbsp;CLRF&nbsp;COUNT1;对计数器1清零<br />     (4)LOOP&nbsp;INCFSZ&nbsp;COUNT1;计数器1加1计数器1加<br />           &nbsp;1结果不为零,跳转循环<br />     (5)  &nbsp;GOTO&nbsp;LOOP ;<br />     (6)  &nbsp;DECTSZ&nbsp;CPUNT2&nbsp;;计数2减1计数器2减1<br />           &nbsp;结果不为零,跳转循环重<br />           &nbsp;复执行第4条指令<br />     (7)  &nbsp;GOTO&nbsp;LOOP ;<br />     (8)  &nbsp;RETLW&nbsp;0   ;子程序执行结束返回<br />  说明:程序中的注释已分别对每条指令的功能作了说明,补充说明1?钡敝葱械?(4)条加1指令结果为零时,就间跳转到执行第(6)条指令。2?钡敝葱械?&nbsp;(6)条减1指令结果为零时,就间跳转到第(8)条子程序返回,整个延时指令才算完成。3?奔剖?器1或2代表PIC中某个寄存器,该寄存器由程序开始的伪指令赋值决定(关于伪指令今后将作专门介绍)。本文关于指令的注释将与前述指令中的略有不同。前述指令注释时是对指令具体完成的功能给以说明,这种注释方法对初学者确实易于接受和理解,但是实际应用中的PIC产品汇编语言的注释通常是以程序要做什么(或指令的作用)而不是说指令的直接功能。鉴于上述原因,下述的指令注释将改变过去的注释方法,用程序应起的作用作注释。<br />  10?奔某唐靼胱纸诮换恢噶?<br />  指令格式:SWAPF f,d<br />  说明:SWAPF是Swap&nbsp;f的合写。符号f、d的意义与前述的相同。该条指令的功能是寄存器f的高4位与低4位交换,即指令执行前,若寄存器f的8位状态为D7、D6、D5、&nbsp;D4、D3、D2、D1、D0,执行后的8位状态变为D3、D2、D1、D0、D7、D6、D5、D4,其结果存入W(d=0)或f(d=1)中。<br />  实例:中断现场保护是中断技术中重要部分。由于PIC16C××指令系统中没有进栈PUSH和出栈POP指令,所以只能用其它指令来实现。因为在主程序中常常用到工作寄存器W和状态寄存器STATUS,所以中断现场保护常要保护寄存器W和STATUS。<br />  下面是对PIC16C7×系列芯片中断现场保护的实例程序。<br />MOVWF&nbsp;W_TEMP&nbsp; &nbsp;;将W内容存入到临时寄存器<br />            &nbsp;W_TEMP中<br />SWAPF&nbsp;STATUS,W &nbsp;;交换STATUS与W内容<br />MOVWF&nbsp;STATUS_TEMP&nbsp;;将STATUS的内容存入到临<br />…          &nbsp;时寄存器STATUS_TEMP中<br />中断服务程序<br />…<br />SWAPF&nbsp;STATUS_TEMP,W;交换STATUS_TEMP与W<br />           &nbsp;的内容<br />MOVWF&nbsp;STATUS   &nbsp;;STATUS复原成原来的状态<br />SWAPF&nbsp;W_TEMP,F  &nbsp;;交换内容<br />SWAPF&nbsp;W_TEMP,W  ;W复原成原来的状态<br />  说明:上述程序中各条指令的注释基本上都是以程序应达到的目的而注释的,对每条指令的功能几乎未涉及。这是初学者应特别注意的。<br />  11?弊映绦虻饔弥噶?(Subroutine&nbsp;Call)<br />  指令格式:CALL&nbsp;k;k为立即地址<br />  说明:子程序调用,不同型号芯片的实现方法不尽相同,其共同点是首先将返回地址((PC)+1)压栈保护,再转入所调用的子程序入口地址执行(与MCS-51指令功能相似)。<br />指令格式模式:HERE&nbsp;CALL&nbsp;DELAY;调用延时子程序<br />       &nbsp;…<br />      &nbsp;DELAY&nbsp;MOVLW&nbsp;0x80 ;延时子程序<br />         &nbsp;RETLW&nbsp;0<br />  说明:调用指令执行前,PC=地址HERE<br />  调用指令执行后,PC=地址DELAY(标号),堆栈指针TOS=HERE+1(返回地址)。<br />  实例:见下条指令的实例<br />  12?奔拇嫫髂谌萑》粗噶?<br />  指令格式:COMF f,d<br />  说明:COMF是Complement&nbsp;f的缩写。其中d=1时,操作(f)→f;d=0时,操作(f)→w。<br />  功能:寄存器f内容取反后送入W(d=0)或f自身(d=1)。<br />  实例: &nbsp;ORG&nbsp; &nbsp;0x1FF<br />      &nbsp;GOTO&nbsp;MAIN<br />      &nbsp;ORG&nbsp; &nbsp;0<br />  &nbsp;DELAY&nbsp;…<br />  &nbsp;MAIN&nbsp;MOVLW&nbsp;0&nbsp;;主程序开始<br />  &nbsp;   &nbsp;TRTS&nbsp;5&nbsp;;设置RA口为输出<br />   &nbsp;BCF&nbsp;5,0 &nbsp;;置RA口0位为0<br />  &nbsp;LOOP&nbsp; &nbsp;CALL&nbsp;DELAY;闪动延时<br />      &nbsp;COMF&nbsp;5??&nbsp;;RA口求反(亮—灭—<br />              &nbsp;亮……控制)<br />      &nbsp;GOTO&nbsp;LOOP&nbsp;;循环<br />       …<br />  说明:上述指令是一种PIC16C54&nbsp;LED发光控制实验部分程序。其中延时子程序DELY未列出,但不影响本条指令的识读。程序中的主程序开始的三条指令,均已介绍过,紧跟着的CALL指令是调用执行子程序,其入口地址为标号DELAY。子程序执行结束后,又执行COMF&nbsp;5的LED发光亮—灭…亮—灭……控制指令。后面一条GOTO&nbsp;LOOP指令是达到LED循环点亮目的。<br />  13?泵嫦蛭坏牟僮髦噶?(共4条,PIC高级产品多增一条)<br />  该类指令除一条位清零外,另有一条寄存器f位b的置1指令和另外两条位跳步指令(PIC高级产品多增一条f的b位触发转换指令)。<br />  (1)位置1指令。指令格式&nbsp;BSF f,b<br />  说明:BSF是Bit&nbsp;Set&nbsp;f的缩写。F和b的意义与前述相同,该条指令的功能是将寄存器f的b位置1。<br />  (2)位测试、为零间跳指令。指令格式&nbsp;BTFSC&nbsp;f,b<br />  说明:BTFSC是Bit&nbsp;Test,Skip&nbsp;if&nbsp;Clear的缩写。指令功能是测试寄存器f位“b”,如为0,跳过下一条指令;为1顺序执行,即当f(b)=0时,就不执行当前指令而执行下一条指令(间跳),即用一条空指令NOP代替它,所以该条指令占用2个指令周期。<br />  (3)位测试、为1间跳指令。指令格式&nbsp;BTFSS f,b<br />  说明:BTFSS是Bit&nbsp;Test,Skip&nbsp;if&nbsp;Set的缩写。其指令的逻辑功能与上条相反,位测试f(b)=1就间跳执行,f(b)=0顺序执行。<br />  上面介绍的PIC&nbsp;8位单片机汇编语言指令仅是部分指令,此外还有循环左、右移指令;W和寄存器f相“加”、相“与”指令和进入睡眠方式等指令。
hotpower 发表于 2008-12-17 23:46 | 显示全部楼层

俺也懂些~~~以前做的PIC的宏的效果和C几乎一样~~~

  <br /> 相关链接:<a href='http://www.21icbbs.com/club/bbs/ShowAnnounce.asp?v=&ID=2731712'>http://www.21icbbs.com/club/bbs/ShowAnnounce.asp?v=&ID=2731712</a>
weimao228 发表于 2011-4-3 13:41 | 显示全部楼层
Wa 太感谢了,不知怎么说了。就是解决了一个不钻牛角尖的问题。:lol
shizaigaole 发表于 2011-4-4 08:07 | 显示全部楼层
对于跨页,跨段问题用pagesel和banksel等伪指令比较方便
沧海一笑 发表于 2011-5-29 08:49 | 显示全部楼层
复习了...感谢...
chongren 发表于 2011-5-30 10:30 | 显示全部楼层
loversweet 发表于 2011-6-26 16:57 | 显示全部楼层
收藏了 太好了
吴永慧521 发表于 2011-6-30 09:54 | 显示全部楼层
收藏了很好
can_all 发表于 2011-10-14 09:11 | 显示全部楼层
、、、、、、很好!
fangfei036 发表于 2011-11-24 13:08 | 显示全部楼层
汇编,心中的痛
fangfei036 发表于 2011-11-24 13:08 | 显示全部楼层
资料非常不错
z.c.xu 发表于 2011-12-6 09:03 | 显示全部楼层
收藏先!
baimandong01 发表于 2011-12-26 14:33 | 显示全部楼层
:lol非常好的资料,学习了
luochangqing112 发表于 2012-2-15 22:21 | 显示全部楼层
ninghaijun 发表于 2012-3-9 12:37 | 显示全部楼层
受学
xfgood3872 发表于 2012-3-15 09:14 | 显示全部楼层
苦苦在学汇编
570957790 发表于 2012-7-11 17:42 | 显示全部楼层
求PICc语言教程啊。。
978462113 发表于 2012-7-18 11:54 | 显示全部楼层
学习进行中,感觉用C些还不错。。。。。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

29

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部