EDA365电子工程师网

标题: PIC单片机汇编语言编程基础 [打印本页]

作者: Dedy01    时间: 2016-5-30 17:10
标题: PIC单片机汇编语言编程基础
1、程序的基本格式
- O: ^# M" I3 I6 t  先介绍二条伪指令:/ p5 c8 x( t! N- X
  EQU ——标号赋值伪指令
/ {! M+ |6 q. w1 |  x7 U  ORG ——地址定义伪指令
" M  H6 A6 J& Y( k& F1 S! c  PIC16C5X在RESET后指令计算器PC被置为全“1”,所以PIC16C5X几种型号芯片的复位地址为:: }' z$ l% O$ h. r" R
   PIC16C54/55:1FFH) [- L; K  @3 s6 f0 J2 f
   PIC16C56:3FFH
7 v: u3 y7 V. f( f   PIC16C57/58:7FFH
* w9 h  j1 q! m  一般来说,PIC的源程序并没有要求统一的格式,大家可以根据自己的风格来编写。但这里我们推荐一种清晰明了的格式供参考。
1 L7 T) `; P1 ^8 l$ A) b  TITLE This is …… ;程序标题
7 L2 ]0 H- d' k+ t  ;--------------------------------------& g: m* B: f' a4 R
  ;名称定义和变量定义
/ n: m2 e+ H3 ~* ?0 l7 _2 J  ;--------------------------------------
1 V0 A# P. j! m  F0    EQU  0
' w8 Y! y- q6 F( ~8 Y3 B: R2 @  RTCC   EQU  13 |5 l/ K% o) {
  PC    EQU  2
8 d& {9 F) Z- L- f1 ~  STATUS  EQU  3
7 t. o% r- ?$ X4 O& S' |  FSR   EQU  4! p5 {+ ?: l! g1 m
  RA    EQU  5
8 I$ [  K  K$ g1 C  RB    EQU  6
. c) h+ V  Y7 o% v2 I  RC    EQU  7  & ~, J$ d1 O" b
       ┋5 n. w3 Y5 x0 x% `% R' L) a7 a+ i# @( ]
  PIC16C54 EQU 1FFH ;芯片复位地址: W7 C5 I/ M, l/ s9 S8 m( E! M  X
  PIC16C56 EQU 3FFH( M2 C+ c5 v4 X3 X- b
  PIC16C57 EQU 7FFH
( v5 b" P, E! z3 I  ;-----------------------------------------
' c! B2 T: }" j  ORG PIC16C54 GOTO MAIN   ;在复位地址处转入主程序
% v0 M) H) r. r8 c3 g  ORG   0          ;在0000H开始存放程序
  R7 E: H* x$ L5 n" J  ;-----------------------------------------
) x  V: N+ i0 D- n! t, L% Y  ;子程序区
3 w4 r& e$ |) S, n# i  ;-----------------------------------------( h# E1 D5 O6 w8 |, `( _
  DELAY MOVLW 255. @7 M$ K  Q' o# A  `# U% \
      ┋
" g  {% m7 U$ k      RETLW 07 |! h: \! h  |/ s9 a+ L0 e4 v$ d
  ;------------------------------------------
: l' n9 k( o5 T* ]& \' T2 ]  ;主程序区$ o& n: ~0 Q! S: e; T7 k: r9 U
  ;------------------------------------------& C/ m5 P4 o3 J/ U( N3 F* m
  MAIN
2 X9 P, E9 A3 b0 k0 N      MOVLW B‘00000000’
9 F! g8 X% a5 O' v! y8 y% t      TRIS RB       ;RB已由伪指令定义为6,即B口
" h. }7 b1 a9 ?: G       ┋0 n% Z. l# Q$ U4 x7 e
  LOOP, }+ o0 E( p. O. y+ I! p
      BSF RB,7 CALL DELAY        
2 u( b- X' ?$ s* z$ `& d      BCF RB,7 CALL DELAY
4 L$ E% q* Y. D: Y2 c/ K2 I        ┋
- S' u4 }6 Y% d# A# w9 T      GOTO LOOP
2 A6 Z! }* b. `4 F. W  ;-------------------------------------------
- Y' m& G5 [, H. A, ~8 c' n( }/ z       END       ;程序结束/ E- l( Z( G$ h; T3 a. b
   注:MAIN标号一定要处在0页面内。6 i0 `! j  \% S/ C2 Y
2、程序设计基础( A& L3 [* b/ y" ?8 C; G4 _
  1) 设置 I/O 口的输入/输出方向
  O. ~9 N% @% e4 A8 K# Y. T9 C  PIC16C5X的I/O 口皆为双向可编程,即每一根I/O 端线都可分别单独地由程序设置为输入或输出。这个过程由写I/O控制寄存器TRIS f来实现,写入值为“1”,则为输入;写入值为“0”,则为输出。
: w9 u* [: |: J      MOVLW 0FH  ;0000 1111(0FH)
* n8 f4 y# Y. v            输入 输出  {# M4 E: T2 S- Y* w3 J$ [$ M
      TRIS 6    ;将W中的0FH写入B口控制器,9 B& l4 f$ U3 H! K" I9 B
             ;B口高4位为输出,低4位为输入。
9 ?3 Y9 R/ N# [  u3 R* V' {! @7 Y      MOVLW 0C0H ; 11 000000(0C0H)$ M, p7 f) W2 L8 ?
              RB4,RB5输出0 RB6,RB7输出1) a3 v. g  d0 F# D
  2) 检查寄存器是否为零
: i9 G, B. a8 ?6 q& y  如果要判断一个寄存器内容是否为零,很简单,现以寄存器F10为例:
; f' U. C; i8 F7 B      MOVF 10,1      ;F10→F10,结果影响零标记状态位Z
5 L& L2 o! i  a& `( ~# J! D      BTFSS STATUS,Z    ;F10为零则跳5 Q  C$ w3 L" ^
      GOTO NZ        ;Z=0即F10不为零转入标号NZ处程序
8 O( r+ Y/ r4 ^9 _- t' i/ h  X       ┋          ;Z=1即F10=0处理程序
1 x  L) N: _: F0 \  3) 比较二个寄存器的大小
! p0 X/ m- {8 e  要比较二个寄存器的大小,可以将它们做减法运算,然后根据状态位C来判断。注意,相减的结果放入W,则不会影响二寄存器原有的值。, \: b. H5 ^; k: w$ \
  例如F8和F9二个寄存器要比较大小:' `/ o1 t+ K2 k  m" K+ Q
       MOVF 8,0       ;F8→W& T0 p/ \* a/ I! |. t7 {
       SUBWF 9,0      ;F9—W(F8)→W
, P$ t: }/ ?4 t$ |# W$ j) ^       BTFSC STATUS,Z    ;判断F8=F9否. `7 }/ y8 b6 F) S- d
       GOTO F8=F98 C' R" D8 a: B4 c1 [
       BTFSC STATUS,C    ;C=0则跳
6 u1 ^; u( q( `3 v       GOTO F9>F8       ;C=1相减结果为正,F9>F8
4 q; b: i  T3 @9 W& t       GOTO F9<F8       ;C=0相减结果为负,F9<F8
- o. x. L9 H1 N) L         ┋3 y0 I3 k8 H9 |& A
   4) 循环n次的程序
1 J0 t  P2 y" Z  如果要使某段程序循环执行n次,可以用一个寄存器作计数器。下例以F10做计数器,使程序循环8次。
( S" M# o( X5 h- m  l0 C: R       COUNT EQU 10     ;定义F10名称为COUNT(计数器)
" i$ S9 Q' V) A          ┋$ G& {7 O% H& K% g
       MOVLW 8
1 M7 ~9 s: t/ L- q( i* R6 B/ a       MOVWF COUNT LOOP   ;循环体5 V! i0 |  q8 i6 i) Y4 y2 K
   LOOP
* }/ M; Y  w7 v0 L           ┋. J" _  D. M+ [, f9 s% s- o
       DECFSZ COUNT,1    ;COUNT减1,结果为零则跳, }! U+ q  v. [; z) c
       GOTO LOOP       ;结果不为零,继续循环! F* O$ l, g( W# C
           ┋       ;结果为零,跳出循环
8 Y% m' b; U9 R! n6 q   5)“IF……THEN……”格式的程序7 F$ d8 w/ ^0 D- r, E2 M" D: {" l
  下面以“IF X=Y THEN GOTO NEXT”格式为例。
& j; O( R  ?+ F- e9 ?/ v       MOVF X,0      ;X→W
5 ]0 R8 Y' [' k2 P9 U+ N       SUBWF Y,0     ;Y—W(X)→W+ i( i. E9 t7 U7 A9 o4 I
       BTFSC STATUS,Z   ;X=Y 否; J/ d8 _1 A5 a" p
       GOTO NEXT      ;X=Y,跳到NEXT去执行。; b) ~" s+ [  i' {2 Z: `
          ┋       ;X≠Y
. f5 z" a& A/ u5 L- y  P' |   6)“FOR……NEXT”格式的程序
. z$ C7 [$ ^) E% y% f/ X" `  s& u  “FOR……NEXT”程序使循环在某个范围内进行。下例是“FOR X=0 TO 5”格式的程序。F10放X的初值,F11放X的终值。
4 A- E& [( k2 Z1 y: n' c      START  EQU  10
! e- n5 T8 \' H      DAEND  EQU  11
4 h# G; @7 N9 |$ k- X$ h           ┋
! L6 H0 q0 q# l# T# T0 i      MOVLW 0) M% z2 Q7 `! G, g  g
      MOVWF START     ; 0→START(F10)% G! {5 W4 e' O: L1 b* I' p
      MOVLW 5
- q, N. x/ ^2 d) h( j      MOVWF DAEND     ;5→DAEND(F11)
2 m2 z- i$ V7 F. R# M   LOOP
9 k" C  P& k& |0 ?0 J3 j$ h3 B0 N           ┋# h% l, G$ ^$ t3 ]( T. d: s1 [
      INCF START,1     ;START值加1* t% P* z$ P7 o! E- N8 w% M
      MOVF START,0; ~2 v* W3 [; l+ r0 K5 a$ x
      SUBWF DAEND,0     ;START=DAEND ?(X=5否)
/ p- \/ @- j1 }+ {0 \+ J+ W9 m      BTFSS STATUS,Z
. |$ o7 e7 N9 E, h5 r8 |1 u. G7 K      GOTO LOOP        ;X<5,继续循环
; D4 w) s" `3 w1 F* v0 h) B( g$ a# s: D* @           ┋       ;X=5,结束循环
! Q1 x4 ^; [3 Y   7)“DO WHILE……END”格式的程序
. O( {, G5 I1 r1 \/ a" X0 A  “DO WHILE……END”程序是在符合条件下执行循环。下例是“DO WHILE X=1”格式的程序。F10放X的值。" r1 g( d* j$ D  N, |! \
      X  EQU  10$ j5 u3 S! q8 `# f$ M" W
        ┋7 L$ I- t- e- n; i  G3 X1 X
      MOVLW  15 O; J  d2 x1 B3 ^. s# Y* s, K
      MOVWF  X     ;1→X(F10),作为初值0 {" s0 W9 O  [2 O- r
   LOOP
$ O: p( q3 ?- d7 f# s: ]( ^        ┋* [1 I0 O) z) R( F. W' N) m7 i
      MOVLW 1. S  Z8 v) o9 `* F& X1 U
      SUBWF X,0
2 X6 @( _4 }; J( r& e      BTFSS STATUS,Z   ;X=1否?
* V8 D- r6 p. T$ z8 c& C) A      GOTO LOOP      ;X=1继续循环- h, g4 v2 S. X  G
        ┋        ;X≠1跳出循环4 o3 I" A% _% \: e% r6 `/ O) G
   8) 查表程序
- E" y$ P( N( c' |  查表是程序中经常用到的一种操作。下例是将十进制0~9转换成7段LED数字显示值。若以B口的RB0~RB6来驱动LED的a~g线段,则有如下关系:
& S/ k* E4 d3 v8 I& S; {9 U% H5 W8 ?9 i! u+ w- C- m
      8 [% H; K' @' f. |1 h9 e5 D7 }" }

/ S+ ?8 A0 e) m! B# y" M5 c  设LED为共阳,则0~9数字对应的线段值如下表: 十进数 线段值 十进数 线段值7 N) @7 ?2 _6 N2 {
0 C0H 5 92H
6 e' |* c6 L; V6 A- L% _1 X1 C9H 6 82H% Y4 {  W6 a; @. U8 R8 P
2 A4H 7 F8H0 |4 `5 a) Y* z; _/ R( c! W6 R
3 B0H 8 80H) O/ T+ F* ^  ]$ C( }
4 99H 9 90H
8 {! y, O  |0 v9 g
' |* W( E2 b6 Y" s: o* z/ `  PIC的查表程序可以利用子程序带值返回的特点来实现。具体是在主程序中先取表数据地址放入W,接着调用子程序,子程序的第一条指令将W置入PC,则程序跳到数据地址的地方,再由“RETLW”指令将数据放入W返回到主程序。下面程序以F10放表头地址。* C, p8 m$ z; Y& }" ^2 u1 N
      MOVLW  TABLE     ;表头地址→F10  , V; e- ?3 W0 E7 V) E
      MOVWF  10
7 j% `; U1 g7 N( U! p# u+ z          ┋
: `: E$ k9 ?" d; H3 Q! Q* d3 J5 v      MOVLW  1        ;1→W,准备取“1”的线段值
: ]! m! ~4 a, m, x# N7 `+ t% r4 O/ w4 j      ADDWF  10,1      ;F10 W =“1”的数据地址
( y/ D. h0 P" g7 ]& i# d      CALL  CONVERT* O3 `, f% ~/ i$ x) |
      MOVWF  6        ;线段值置到B口,点亮LED
8 ?5 h+ m% ~5 T" f2 ^          ┋
4 Q! V/ _- ^, v  a' Y  CONVERT MOVWF  2        ;W→PC TABLE
! W( N: @+ q. N' ]3 I      RETLW  0C0H      ;“0”线段值9 x9 I2 A3 I& o! W" s
      RETLW  0F9H      ;“1”线段值
/ h1 r/ i, w  O+ j          ┋
$ C# J& U1 _8 I) h/ W) l      RETLW  90H       ;“9”线段值1 W: c7 J7 ~' {* M* k+ s! ~
   9)“READ……DATA,RESTORE”格式程序! V1 g! n+ M4 W
  “READ……DATA”程序是每次读取数据表的一个数据,然后将数据指针加1,准备取下一个数据。下例程序中以F10为数据表起始地址,F11做数据指针。( W$ d' X/ ?# h6 U( ^: e9 ^
      POINTER  EQU  11   ;定义F11名称为POINTER
7 r) Y; W6 e' B6 |( o9 m" q          ┋
( l' m+ t& C1 g/ x% ]      MOVLW   DATA
) T* ]+ @. `: [# R      MOVWF   10     ;数据表头地址→F10
+ ]  v' W4 n! A      CLRF   POINTER   ;数据指针清零
# d7 j& H+ F4 H' D2 `, V          ┋/ c$ i9 c: {5 V  s& _
      MOVF   POINTER,0  5 Y/ G8 Y( }4 Q/ E% k9 D( R- n, J
      ADDWF 10,0      ;W =F10 POINTER
; u$ m4 J1 _- g          ┋
$ A3 h4 S# a) S$ r      INCF    POINTER,1  ;指针加1
6 Q) _7 B  p0 e8 B  u# m  k/ R- V5 I* |      CALL CONVERT      ;调子程序,取表格数据
; p1 r6 H1 o- V$ P& J( e2 Q          ┋: Z( V2 ]/ x- @
  CONVERT MOVWF   2    ;数据地址→PC
4 w# G- M' W; C( [+ B6 v: W  DATA  RETLW   20H    ;数据! _  m+ N+ D# G
          ┋
4 V& _+ A6 G5 y8 {! l      RETLW 15H      ;数据
' `# v% d& L+ f! I: v  如果要执行“RESTORE”,只要执行一条“CLRF POINTER”即可。) b2 O" Y4 l; ?; ~0 h4 F% j5 `
  10) 延时程序" Q3 V  Y6 {" \) Y
  如果延时时间较短,可以让程序简单地连续执行几条空操作指令“NOP”。如果延时时间长,可以用循环来实现。下例以F10计算,使循环重复执行100次。0 U2 n" K6 O) U9 [
      MOVLW D‘100’
& X- r" p/ K1 Q# d# c, h& Y" @) Q      MOVWF 10
6 [5 ?- s3 C5 ?6 D+ \3 |  LOOP  DECFSZ 10,1   ;F10—1→F10,结果为零则跳: ~  m# k/ O  V4 S
      GOTO LOOP$ s; _1 s2 Z: s8 B3 t/ R: Y
       ┋
4 b' Z) B2 ]- W+ q5 {' n4 \3 P1 c  延时程序中计算指令执行的时间和即为延时时间。如果使用4MHz振荡,则每个指令周期为1μS。所以单周期指令时间为1μS,双周期指令时间为2μS。在上例的LOOP循环延时时间即为:(1 2)*100 2=302(μS)。在循环中插入空操作指令即可延长延时时间:5 ^2 N1 s1 i5 M# K( ^
      MOVLW  D‘100’$ r, R0 J8 f$ L7 n" U
      MOVWF  108 U/ M: {7 `" J" c/ P3 F4 }
  LOOP   NOP
* k/ g3 B% s& w4 x, d0 o       NOP5 k$ ?+ [: \2 N& D4 f( K2 h
       NOP1 _7 N: c7 r: p7 l
      DECFSZ 10,1# X3 T! d+ p+ a) I5 C; B
      GOTO LOOP
1 u5 k- S5 r9 U  E        ┋
2 Y3 r$ p* P3 ^  延时时间=(1 1 1 1 2)*100 2=602(μS)。; W: B- i+ z. Q5 j
  用几个循环嵌套的方式可以大大延长延时时间。下例用2个循环来做延时:
: Y( `' m( O% m( n( _$ X* R      MOVLW   D‘100’+ P, b5 Y2 z7 p1 {; ^" e; t8 A6 t  K
      MOVWF   10
" g, ^/ I! a/ h) {  LOOP  MOVLW   D‘16’
+ i( v2 j# I5 r      MOVWF   11# X) T+ c' [: V& r  p5 S
  LOOP1  DECFSZ   11,1/ [8 J" M; c/ ?6 X
      GOTO    LOOP1( E; i" K5 G/ ~% B- S; c
      DECFSZ   10,1
8 c  \4 J8 D0 ?5 n$ }3 M      GOTO LOOP( W7 N) t0 p7 Q
       ┋
) s; ?; q- \( X: q+ r  延时时间=1 1 [1 1 (1 2)*16-1 1 2]*100-1=5201(μS)6 F( M8 ]( C" D# s; t7 ]
  11) RTCC计数器的使用
% @2 D9 U+ V0 v9 ~9 G  V  RTCC是一个脉冲计数器,它的计数脉冲有二个来源,一个是从RTCC引脚输入的外部信号,一个是内部的指令时钟信号。可以用程序来选择其中一个信号源作为输入。RTCC可被程序用作计时之用;程序读取RTCC寄存器值以计算时间。当RTCC作为内部计时器使用时需将RTCC管脚接VDD或VSS,以减少干扰和耗电流。下例程序以RTCC做延时:
  t+ }( |' ?0 N/ s+ R6 Y; j      RTCC  EQU  14 `( v) S2 I7 J9 S3 ^0 ^4 q7 B
       ┋
7 o9 _6 n0 K6 @7 d! x, m! n      CLRF  RTCC    ;RTCC清0) w/ y  z6 U, u' e4 @
      MOVLW  07H
; M+ D: U: {2 d+ q      OPTION    ;选择预设倍数1:256→RTCC( x: P1 M0 S& h+ {3 J
   LOOP  MOVLW  255   ;RTCC计数终值7 X6 Y9 b3 |$ T5 l- S1 k+ y
      SUBWF  RTCC,0
5 u; `1 t7 Q) m      BTFSS STATUS,Z   ;RTCC=255?1 N3 U2 `! I- u, R  n7 `+ W+ H. b
      GOTO LOOP8 k* h* u4 N  A: L% i
       ┋( Y$ L% J+ b) a
  这个延时程序中,每过256个指令周期RTCC寄存器增1(分频比=1:256),设芯片使用4MHz振荡,则:
" {8 n2 w% x/ O+ f( M/ g  延时时间=256*256=65536(μS)
( ?  @) M% L% z+ @* E5 Y/ m  RTCC是自振式的,在它计数时,程序可以去做别的事情,只要隔一段时间去读取它,检测它的计数值即可。* O2 Y: @+ x: Q/ W& ^' }
  12) 寄存器体(BANK)的寻址+ F$ _+ L% n) ~& j, x
  对于PIC16C54/55/56,寄存器有32个,只有一个体(BANK),故不存在体寻址问题,对于PIC16C57/58来说,寄存器则有80 个,分为4个体(BANK0-BANK3)。在对F4(FSR)的说明中可知,F4的bit6和bit5是寄存器体寻址位,其对应关系如下:, i$ [( `1 G* w
3 P# P! D3 `$ c" P2 t( v6 i. L
Bit6  Bit5 BANK 物理地址
: z7 E* x( D5 f 0    0BANK0 10H~1FH0 V- H  t. I( E! d5 W+ I3 W
 0    1BANK1 30H~3FH! V' W& Z" ^( K
 1    0BANK2 50H~5FH) t% t, ?( b. \- O. W( F* C
 1    1BANK3 70H~7FH1 N0 x% C% ?" [/ B1 d

. j# Z" B! X1 q7 i% L$ ?; Y- C  当芯片上电RESET后,F4的bit6,bit5是随机的,非上电的RESET则保持原先状态不变。& K$ y) s6 I- K( N1 X/ U; {
  下面的例子对BANK1和BANK2的30H及50H寄存器写入数据。' A7 P0 K9 ~9 V% G
  例1.(设目前体选为BANK0)* }. g5 M+ J( W" G4 |2 a
      BSF   4,5    ;置位bit5=1,选择BANK1! B: N+ O& K6 D% I' M$ ^
      MOVLW  DATA
0 L9 g  r* b* K7 V" ~      MOVWF  10H    ; DATA→30H
) p  {' A! Q# P# a- _; F      BCF   4,5
- j1 I, _7 E3 u6 ^      BSF   4,6   ;bit6=1,bit5=0选择BANK2/ D2 R+ }8 |8 L6 e# o
      MOVWF  10H    ;DATA→50H
9 t) p# h/ ]2 w: c, L% t$ f  从上例中我们看到,对某一体(BANK)中的寄存器进行读写,首先要先对F4中的体寻址位进行操作。实际应用中一般上电复位后先清F4的bit6和bit5为0,使之指向BANK0,以后再根据需要使其指向相应的体。
: c8 f& _) @8 y  K  ^  注意,在例子中对30H寄存器(BANK1)和50H寄存器(BANK2)写数时,用的指令“MOVWF 10H”中寄存器地址写的都是“10H”,而不是读者预期的“MOVWF 30H”和“MOVWF 50H”,为什么?
- _6 ~# D& |$ k7 m" Z  让我们回顾一下指令表。在PIC16C5X的所有有关寄存器的指令码中,寄存寻址位都只占5个位:fffff,只能寻址32个(00H—1FH)寄存器。所以要选址80个寄存器,还要再用二位体选址位PA1和PA0。当我们设置好体寻址位PA1和PA0,使之指向一个BANK,那么指令“MOVWF 10H”就是将W内容置入这个BANK中的相应寄存器内(10H,30H,50H,或70H)。  Z+ t* G5 e# A/ e1 C2 C5 k' z( T
  有些设计者第一次接触体选址的概念,难免理解上有出入,下面是一个例子:# b7 p" D" k3 n8 K
  例2:(设目前体选为BANK0)& j8 ^3 H, s4 I/ G+ Y4 v- l3 O, w5 R
      MOVLW  55H . C& y) V/ ~( D. y  E
      MOVWF  30H   ;欲把55H→30H寄存器
$ X. `$ C7 Y* I7 Z* P  {      MOVLW  66H
4 N% l( s, o- b% |9 ~& B      MOVWF  50H   ;欲把66H→50H寄存器
6 O0 e4 m% D" P  以为“MOVWF 30H”一定能把W置入30H,“MOVWF 50H”一定能把W置入50H,这是错误的。因为这两条指令的实际效果是“MOVWF 10H”,原因上面已经说明过了。所以例2这段程序最后结果是F10H=66H,而真正的F30H和F50H并没有被操作到。
5 x) f" f5 i7 h1 P" \( L  建议:为使体选址的程序清晰明了,建议多用名称定义符来写程序,则不易混淆。   例3:假设在程序中用到BANK0,BANK1,BANK2的几个寄存器如下:9 |9 ]  G3 K# j" k, p4 |

: h/ v' b9 V" _- J% i7 ^, @BANK0 地址 BANK1 地址 BANK2 地址 BANK3 地址" U) W/ v. y! L5 @4 k
A 10H B 30H C 50H · 70H
# S- K6 l& F, v% A6 p2 Y· · · · · · · ·
/ Z! R  I$ ^" c; Y% p· · · · · · · ·3 t& G5 h3 h! e. t- R  p2 v
8 K, b; c; y; o% V8 w: I& ?
       A   EQU  10H   ;BANK0' a& _- b8 n3 ?- R. W( ^% Q
       B   EQU  10H   ;BANK1) ~4 s4 C) d# o$ J9 P
       C   EQU  10H   ;BANK2
) W" H; C) R6 O- `/ c. [) m          ┋$ R7 r3 S4 ]2 X
       FSR  EQU  4
& P+ A& U% h/ w+ ~& H) P       Bit6  EQU  64 W, q( f  l! l4 ]9 s9 t
       Bit5  EQU  5; r: i& a$ C# X. P6 Z
       DATA  EQU  55H
& I5 L: y8 _' T          ┋3 _" ]4 R( r- o: \- |
       MOVLW  DATA4 T0 p) a: z9 q9 ^
       MOVWF  A  
8 j9 P* A8 t  b3 _# @       BSF   FSR,Bit5
. p0 _5 Y4 g; m# {' o3 v4 e       MOVWF  B     ;DATA→F30H
- h( o  ]" o9 E% e4 M       BCF   FSR,Bit5
/ t% z9 d2 Y: c" C7 `       BSF   FSR,Bit6& V2 n4 h' w& M$ d
       MOVWF  C     ;DATA→F50H
% @) g+ U6 a7 E' u          ┋
3 ?: q! @' i8 J4 C& j& M3 D. n8 y  程序这样书写,相信体选址就不容易错了。: E3 [# H: h, ]+ D% B# C4 y1 @6 i# Q
  13) 程序跨页面跳转和调用- z6 s/ l% g9 s* c( C
  下面介绍PIC16C5X的程序存储区的页面概念和F3寄存器中的页面选址位PA1和PA0两位应用的实例。
" c* P) V+ V- u1 y  (1)“GOTO”跨页面
5 }+ x9 `" M' ?; T" e1 h" {3 o; y   例:设目前程序在0页面(PAGE0),欲用“GOTO”跳转到1页面的某个地方7 K- z( {! ?! W* S
KEY(PAGE1)。3 D7 @+ j1 {0 g3 J
       STATUS  EQU  3+ |; H0 H: b/ h
       PA1   EQU  6
: i" k! n0 v( x* I( r       PA0   EQU  5
. R1 ^/ b* `6 v/ n! S           ┋- {1 i  S" Z6 a- G
       BSF  STATUS,PA0  ;PA0=1,选择PAGE页面
( \7 Y8 C- [: A       GOTO  KEY      ;跨页跳转到1页面的KEY" d4 B& T/ T( m6 Q6 H) \  j, K9 Y
           ┋3 {9 d1 y# X9 m' j: U
       KEY   NOP     ;1页面的程序
9 Q) }4 D# M: |2 ^& E           ┋
0 w! ~9 f" f  a+ q* W' S  (2)“CALL”跨页面& Z4 C% B1 o- @$ o, O
  例:设目前程序在0页面(PAGE0),现在要调用——放在1页面(PAGE1)的子程序DELAY。% o( c( o' ?, _) t- n: J
           ┋0 E* ~3 j9 J6 @/ O" G2 G
       BSF  STATUS,PA0   ;PA0=1,选择PAGE1页面
( N; M& Q! ~8 G  }* `       CALL  DELAY      ;跨页调用
" b/ L# [: b9 U+ U6 }& D) B/ @* H       BCF  STATUS,PA0   ;恢复0页面地址1 m+ W: k$ V6 p+ c% L  Y
           ┋
8 z' F  s' P. p+ [7 S; H       DELAY NOP       ;1页面的子程序# V) [5 W1 X3 L3 Q
           ┋
6 t7 H+ U! y' l6 K: r5 _  注意:程序为跨页CALL而设了页面地址,从子程序返回后一定要恢复原来的页面地址。4 N0 I: N0 U5 L( _( @7 E
  (3)程序跨页跳转和调用的编写8 e: X6 m$ [5 x: z, P
  读者看到这里,一定要问:我写源程序(.ASM)时,并不去注意每条指令的存放地址,我怎么知道这个GOTO是要跨页面的,那个CALL是需跨页面的?的确,开始写源程序时并知道何时会发生跨页面跳转或调用,不过当你将源程序汇编时,就会自动给出。当汇编结果显示出:
( z5 D( H- g6 k/ |+ n7 X4 _9 w9 p2 z       X X X(地址)“GOTO out of Range"
$ L1 f( v+ R2 u9 a       X X X(地址)“CALL out of Range"( Q; b3 n6 N. l
  这表明你的程序发生了跨页面的跳转和调用,而你的程序中在这些跨页GOTO和CALL之前还未设置好相应的页面地址。这时应该查看汇编生成的.LST文件,找到这些GOTO和CALL,并查看它们要跳转去的地址处在什么页面,然后再回到源程序(.ASM)做必要的修改。一直到你的源程序汇编通过(0 Errors and Warnnings)。
) _# }2 K, E4 A* l& f   (4)程序页面的连接
, p! I# B6 [. j0 V# j& m0 g  程序4个页面连接处应该做一些处理。一般建议采用下面的格式: 即在进入另一个页面后,马上设置相应的页面地址位(PA1,PA0)。 页面处理是PIC16C5X编程中最麻烦的部分,不过并不难。只要做了一次实际的编程练习后,就能掌握了。
' x3 ?2 {+ S$ V4 M5 b' h 各大类单片机的指令系统是没有通用性的,它是由单片机生产厂家规定的,所以用户必须遵循厂家规定的标准,才能达到应用单片机的目的。
0 Y0 m2 m& `  |  PIC 8位单片机共有三个级别,有相对应的指令集。基本级PIC系列芯片共有指令33条,每条指令是12位字长;中级PIC系列芯片共有指令35条,每条指令是14位字长;高级PIC系列芯片共有指令58条,每条指令是16位字长。其指令向下兼容。
+ n! M" Q& B$ u4 @0 n
9 D  ^9 }# _0 w# F2 C# i( w  在这里笔者介绍PIC 8位单片机汇编语言指令的组成及指令中符号的功能,以供初学者阅读相关书籍和资料时快速入门。1 z3 x. r( J. |, \: ?, i- g/ u

, ]; t! M& J6 b7 ^9 G  一、PIC汇编语言指令格式0 u1 p; M, R. ^  S. j) j4 b* Q
  PIC系列微控制器汇编语言指令与MCS-51系列单片机汇编语言一样,每条汇编语言指令由4个部分组成,其书写格式如下:
1 a9 o5 h$ G0 g+ R: ~8 N  标号 操作码助记符 操作数1,操作数2;注释
; K8 E, D% h- ~$ I( @' M5 f. `" n  指令格式说明如下:指令的4个部分之间由空格作隔离符,空格可以是1格或多格,以保证交叉汇编时,PC机能识别指令。6 Z4 ?& T! N* w
  1?北旰拧∮隡CS-51系列单片机功能相同,标号代表指令的符号地址。在程序汇编时,已赋以指令存储器地址的具体数值。汇编语言中采用符号地址(即标号)是便于查看、修改,尤其是便于指令转移地址的表示。标号是指令格式中的可选项,只有在被其它语句引用时才需派上标号。在无标号的情况下,指令助记符前面必须保留一个或一个以上的空格再写指令助记符。指令助记符不能占用标号的位置,否则该助记符会被汇编程序作标号误处理。9 e3 ~5 H" T6 e* D& Z2 X6 E6 Y, ^
  书写标号时,规定第一字符必须是字母或半角下划线“—”,它后面可以跟英文和数字字符、冒号(:)制符表等,并可任意组合。再有标号不能用操作码助记符和寄存器的代号表示。标号也可以单独占一行。
8 K% r# g* W$ I4 a- n% m) f6 t: s  2?辈僮髀胫?记符 该字段是指令的必选项。该项可以是指令助记符,也可以由伪指令及宏命令组成,其作用是在交叉汇编时,“指令操作码助记符”与“操作码表”进行逐一比较,找出其相应的机器码一一代之。. ]! K; \" _9 q& A/ C) @! _
  3?辈僮魇? 由操作数的数据值或以符号表示的数据或地址值组成。若操作数有两个,则两个操作数之间用逗号(,)分开。当操作数是常数时,常数可以是二进制、八进制、十进制或十六进制数。还可以是被定义过的标号、字符串和ASCⅡ码等。具体表示时,规定在二进制数前冠以字母“B”,例如 B10011100;八进制数前冠以字母“O”,例如O257;十进制数前冠以字母“D”,例如D122;十六进制数前冠以“H”,例如H2F。在这里 PIC 8位单片机默认进制是十六进制,在十六进制数之前加上Ox,如H2F可以写成Ox2F。
9 d2 ^( l( `) g  e4 U  指令的操作数项也是可选项。$ K1 d$ g9 q, Z, C- l1 ~
   PIC系列与MCS-51系列8位单片机一样,存在寻址方法,即操作数的来源或去向问题。因PIC系列微控制器采用了精简指令集(RISC)结构体系,其寻址方式和指令都既少而又简单。其寻址方式根据操作数来源的不同,可分为立即数寻址、直接寻址、寄存器间接寻址和位寻址四种。所以PIC系列单片机指令中的操作数常常出现有关寄存器符号。有关的寻址实例,均可在本文的后面找到。
: Y; c+ C3 |; `  4?弊⑹汀∮美炊猿绦蜃餍┧得鳎?便于人们阅读程序。注释开始之前用分号(;)与其它部分相隔。当汇编程序检测到分号时,其后面的字符不再处理。值得注意:在用到子程序时应说明程序的入口条件、出口条件以及该程序应完成的功能和作用。  X/ ]+ O% h' `* E+ z. p$ L, {; j! K
. L  G7 A( v- U
  二、清零指令(共4条)
; R1 I8 ~2 q4 H6 I  1?奔拇嫫髑辶阒噶?
; ]( W6 u0 g+ c/ K! s1 n  实例:CLRW;寄存器W被清零9 c  u# |% y8 |8 y0 V# H2 T; q2 H
  说明:该条指令很简单,其中W为PIC单片机的工作寄存器,相当于MCS-51系列单片机中的累加器A,CLR是英语Clear的缩写字母。& [- w+ u" k: S5 W. P
  2?笨疵殴范ㄊ逼髑辶阒噶睢?8 Q9 z% x2 v2 r' H4 }3 y
  实例:CLRWDT;看门狗定时器清零(若已赋值,同时清预分频器)
7 p9 `, ^. r1 e' p2 d+ i  说明:WDT是英语Watchdog Timer的缩写字母。CLR见上述说明。注意该两条指令无操作数。! @0 g/ g4 k  w/ }  n0 N* j0 W
  3?奔拇嫫鱢清零指令。指令格式:CLRF f
1 E! i& Q/ q( y( R% d, W# W  实例:CLRF TMRO;对TMRO清零: i; u6 f  x' ^( ?
  说明:在PIC系列8位单片机中,常用符号F(或f)代表片内的各种寄程器和F的序号地址。F取值按PIC系列不同型号而不同,一般为 Ox00~Ox1F/7F/FF。TMRO代表定时器/计数器TMRO,所以CLRF对寄程器清零,采用了直接寻址方式直接给出要访问的寄存器TMRO。
; u' Y, C0 N5 Z4 r$ j6 |5 z  4?蔽磺辶阒噶睢V噶罡袷? BCF f,b5 K8 T" l  W3 n0 G3 O# P
  实例:BCF REG1,2;把寄存器REG1的D2位清零
! l/ u! c8 s: R1 m* H' e2 L  说明:BCF是英语Bit Clear F的缩写。指令格式中的F,同上说明;符号b是表示PIC片内某个8位数据寄存器F的位号(或位地址),所以b的取值为0~7或D0~D7。实例中REG 是Register的缩写。实例中的2代表指令格式中的b=2即寄存器REG1的D2位。, Z- F" B3 O1 o1 @0 p
  通过上述四条清零指令格式和实例,可以说明,学习PIC系列8位单片机的指令时应首先了解指令的助记符意义(功能),再有就是它的表达方式。初学者没有必要死记指令,重要是理解和实践。: z: F5 X( \, C% T. N0 `5 H/ Q, l! C
8 q, A# [  y6 D" S0 d0 c% v
    三、面向字节、常数与控制操作的指令  j' e) R0 _6 v/ ]
  1?贝?送立即数至工作寄存器W指令2 ]7 u/ P5 h$ Q3 _
  指令格式:MOVLW k;k表示常数、立即数和标号
/ ^# i( F, @* C. X# q" i  说明:MOVLW是Move Literal to w的缩写# p0 {' B- s/ d* D: K3 e
  实例:MOVL 0x1E;常数30送W
: p( a, w) g+ x: O  [  2?盗/O口控制寄存器TRIS设置指令
8 J' [# x) ?* c7 A  指令格式;TRIS f6 {' x5 D9 o2 m8 W9 R
  说明;TRIS f是Load TRIS Register的缩写。其功能是把工作寄存器W的内容送入I/O口控制寄存器f。当W=0时,置对应I/O口为输出;W=1,置I/O口为输入。
2 n3 A( x- h! z/ V  L; e+ e  实例:MOVLW 0x00 ;把00H送入W
9 ]) g0 _% n% n) Q4 n7 e) x' M     TRIS RA ;置PIC RA口为输出
) L; l8 W9 x7 C0 e3 \% y. P1 @) p     MOVLW 0xFF ;把FFH送入W
  W" N% X) b3 M6 t8 `7 U     TRIS RB ;置PIC RB口为输入
7 f& ?' e  Z5 m) y4 J3 M2 ?; y  说明:这是PIC汇编语言中常用的几条指令,即设置某个I/O口(这里是RA口和RB口)为输入或输出的语句。可见,识读指令时,一应充分理解语句格式的功能,二应前后联系阅读。
. {6 s3 T! P+ ~! S/ w7 Y( E  3?盬寄存器内容送寄存器f(W内容保持不变)指令* @- I' f. O, N+ e; f5 u! g
  指令格式:MOVWF f, g$ l1 T+ m9 Z0 I( d
  说明:MOVWF是Move W to f的缩写; ]( ?' L9 Y% T& Y$ @" Y
  实例:MOVLW 0x0B;送0BH送W1 u* Z: T9 l$ f7 R0 |8 @
     MOVWF 6 ;送W内容到RB口7 j7 D: r4 e" e
  说明:第一条指令0x0B(常数11)送工作寄存器W,第二条指令,把W内容常数11送到寄存器F6中,查表F6即为RB口,所以PORT_B(B口)=0BH=D11+ e/ s, m% h( f4 Z! b
  4?奔拇嫫鱢传送指令
7 d' a1 S" u5 X9 \  指令格式:MOVF f,d
( v1 H2 e0 i, F3 L8 N; E  说明:MOVF是Move f的缩写。F代表PIC中的某个寄存器。指令中的d规定:d=0时,f内容送W;d=1时,f内容送寄存器。
  t& o! L% ~  ?( T" P2 M! [/ I5 ^) v  实例:MOVF 6,0 ;RB口内容送W3 z6 U, [: B8 n2 w" j
     MOVWF 8  ;RB口内容送f8  S/ R! Q0 ~( R2 p# Q, x' l
  说明:第一条指令中的6代表寄存器f=6,查寄存器表f=6为RB口;0代表d=0,代表选择的目标为寄存器W。第二条指令中的8代表寄存器f=8。所以两条指令结果是把RB口的内容送f8。至于f8内容是多少?还应在汇编语言开始时附加指令,这里从略。
& R/ l1 A8 g& f) {  5?笨詹僮髦噶?/ j( T. M) R4 U3 \0 |! o
  指令格式:NOP6 Z8 l# F' X7 k, t! z' A
  说明:NOP是英语No Operation的缩写。NOP无操作数,所以称为空操作。执行NOP指令只使程序计数器PC加1,所以占用一个机器周期。& V- |5 X0 [2 n: @8 o8 g
  实例:MOVLW 0xOF   ;送OFH到W
/ z0 ?* @) h5 \5 E) s     MOVWF PORT_B ;W内容写入B口
2 {, B% h( `9 P1 x9 a( u     NOP      ;空操作7 h3 L) p# ~& o% K6 B- _
     MOVF PORT_B,W ;读操作
: B9 Y3 ^- w+ \4 }; r" T  说明:该三条指令是一种对I/O口的B口连续操作的实例,其目的达到写入B口的内容要读出时,应保证写、读之间有个稳定时间,因此加入了空操作指令NOP。
1 v/ m9 q6 q% l+ L7 t7 E  6?蔽尢跫?跳转指令
8 O. G! C2 L: `  指令格式:GOTO k
+ D" C. @! _' V! Y  说明:执行该条指令时,将指令转移到指定的地址(跳转)。指令中的k,常与程序中的标号联系起来。0 J  Z: H& R% k2 {* j; s
  实例:见第9条指令中( y3 r7 F$ ^- M+ W6 M  z8 m
  7?奔拇嫫髂谌菁?1,结果为零的间跳指令
4 l, m3 E4 D' P  指令格式:DECFSZ f,d
' Z/ P4 [. ^' K6 `, b: K+ V. l  说明:DECFSZ是英语Decrement f,Skip of not 0的缩写。符号f,d代表的意义,前述已作说明。该条指令是指寄存器的内容减1存入W(d=0)或f(d=1)中。若指令执行结果减1不为零,指令顺序执行;为零时,就间跳下一条指令后再执行(等效顺序执行一条空指令NOP),实际指令中,当d=1时,该项常被略去。# ^, r) l5 Z1 O! K; y% G
  8?奔拇嫫髂谌菁?1,结果为零间跳指令- v# y9 S- y; p) @9 v
  指令格式:INCFSZ f,d
, {# f3 |, V* W) y  l+ T  说明:INCFSZ是英语Increment f,Skip of 0的缩写。该条指令与上一条(7)指令差别仅在于“1”上,即执行这条指令时,寄存器f内容加1,若结果不为零,则指令顺序执行;为零则指令间跳执行。执行这条指令的其它逻辑关系与上条相同。' G2 p1 r$ S- l4 O) [1 q+ \" f
  9?弊映绦蚍祷刂噶?
: U; v2 ]4 n0 {5 j( q3 I4 v  {  指令格式:RETLW k
" O! f8 Z' |4 [: f  说明:RETLW是Return Literal to W的缩写。该指令代表子程序返回,返回前先把8位立即数送W。7 F4 h( M  c7 _4 \' y1 _" t
  实例:PIC某个汇编语言的延时子程序(摘要):, T% s2 V6 {5 o
     (1)BELY MOVLW 0xC5 ;送延时常数0C5H入W
" t, I1 R6 _; p' f9 @     (2)   MOVWF COUNT2;0C5H送入计数器20 J" z6 z$ F) R
     (3)   CLRF COUNT1;对计数器1清零2 j0 _  \$ @$ d6 C0 s+ n- t
     (4)LOOP INCFSZ COUNT1;计数器1加1计数器1加' j. N$ e5 M# b0 T
            1结果不为零,跳转循环
6 \5 }7 b3 W& P- F     (5)   GOTO LOOP ;
+ [2 L, [# Y/ Z     (6)   DECTSZ CPUNT2 ;计数2减1计数器2减1
/ J. j0 M% u$ w+ r8 N) M0 [9 s            结果不为零,跳转循环重# K5 w  B4 ]3 f: O4 R4 T
            复执行第4条指令' _& |  b. x  ]. L/ R, D4 o9 `
     (7)   GOTO LOOP ;7 r" c" h9 M$ Z; L' |% D; ?# V
     (8)   RETLW 0   ;子程序执行结束返回$ z- I0 K* f8 ]! h; y' A  S, m
  说明:程序中的注释已分别对每条指令的功能作了说明,补充说明1?钡敝葱械?(4)条加1指令结果为零时,就间跳转到执行第(6)条指令。2?钡敝葱械? (6)条减1指令结果为零时,就间跳转到第(8)条子程序返回,整个延时指令才算完成。3?奔剖?器1或2代表PIC中某个寄存器,该寄存器由程序开始的伪指令赋值决定(关于伪指令今后将作专门介绍)。本文关于指令的注释将与前述指令中的略有不同。前述指令注释时是对指令具体完成的功能给以说明,这种注释方法对初学者确实易于接受和理解,但是实际应用中的PIC产品汇编语言的注释通常是以程序要做什么(或指令的作用)而不是说指令的直接功能。鉴于上述原因,下述的指令注释将改变过去的注释方法,用程序应起的作用作注释。4 D" e% h4 {8 O( d1 o, F
  10?奔某唐靼胱纸诮换恢噶?
( h' f" T* ?2 ^* V, N" A  指令格式:SWAPF f,d
4 j$ c& U& t: B. n5 t" z- n  说明:SWAPF是Swap f的合写。符号f、d的意义与前述的相同。该条指令的功能是寄存器f的高4位与低4位交换,即指令执行前,若寄存器f的8位状态为D7、D6、D5、 D4、D3、D2、D1、D0,执行后的8位状态变为D3、D2、D1、D0、D7、D6、D5、D4,其结果存入W(d=0)或f(d=1)中。
  B* t1 X& }# p; C  实例:中断现场保护是中断技术中重要部分。由于PIC16C××指令系统中没有进栈PUSH和出栈POP指令,所以只能用其它指令来实现。因为在主程序中常常用到工作寄存器W和状态寄存器STATUS,所以中断现场保护常要保护寄存器W和STATUS。
$ w1 w. z" Z- z% V1 e: D' z/ R! _  下面是对PIC16C7×系列芯片中断现场保护的实例程序。
+ g3 B+ z$ ~. Z4 _6 j6 V8 t  nMOVWF W_TEMP   ;将W内容存入到临时寄存器
1 T5 [+ ~; }6 \  B- P             W_TEMP中
% m. V4 `3 x4 h0 i4 TSWAPF STATUS,W  ;交换STATUS与W内容1 y8 j0 y. h) r% B
MOVWF STATUS_TEMP ;将STATUS的内容存入到临
+ B$ T- T2 v6 b9 Y) m…           时寄存器STATUS_TEMP中* d$ k( y, u) d( H% _/ Z0 U
中断服务程序
5 T2 m# y  [" u/ o. K3 u& _6 [" }% {' m( z, C: J
SWAPF STATUS_TEMP,W;交换STATUS_TEMP与W0 @% B" a& z" r& ?& J+ i
            的内容- }$ F  }5 s, v0 S+ ]: H9 f
MOVWF STATUS    ;STATUS复原成原来的状态
; L6 E" `* z" P4 x% {& ySWAPF W_TEMP,F   ;交换内容2 O/ c9 f2 `6 r9 Y9 d+ o: k
SWAPF W_TEMP,W  ;W复原成原来的状态
- b6 G3 d! N1 o  说明:上述程序中各条指令的注释基本上都是以程序应达到的目的而注释的,对每条指令的功能几乎未涉及。这是初学者应特别注意的。, w- U: q4 K# j) b8 y. R% ^
  11?弊映绦虻饔弥噶?(Subroutine Call)
% w; Y/ p& Y8 M0 X2 W  指令格式:CALL k;k为立即地址' d" v; w7 V7 j8 L* B& y& d
  说明:子程序调用,不同型号芯片的实现方法不尽相同,其共同点是首先将返回地址((PC)+1)压栈保护,再转入所调用的子程序入口地址执行(与MCS-51指令功能相似)。
9 v2 d0 H; Y/ B指令格式模式:HERE CALL DELAY;调用延时子程序
- P7 E3 T* v. Q* ^        …
! a* K0 _$ F- H$ F: Z1 }       DELAY MOVLW 0x80 ;延时子程序
5 Q  U( h0 P) z2 V: F" j          RETLW 0& Y. ]" V" Q6 `% ~( o# e+ H
  说明:调用指令执行前,PC=地址HERE
& U0 v3 q  S6 c" D  y+ b9 R: F$ u# q  调用指令执行后,PC=地址DELAY(标号),堆栈指针TOS=HERE+1(返回地址)。
  I! ?3 f6 P" V7 l, B* G  实例:见下条指令的实例$ i; T5 H. |0 _" h, q
  12?奔拇嫫髂谌萑》粗噶?
; j, F: z9 g7 M, s* J  指令格式:COMF f,d: G- {3 K* E, Y& v
  说明:COMF是Complement f的缩写。其中d=1时,操作(f)→f;d=0时,操作(f)→w。
0 y# P( ?9 I0 b, _9 {  功能:寄存器f内容取反后送入W(d=0)或f自身(d=1)。
2 C: T. C# Y( \) p5 V& K  实例:  ORG   0x1FF
- C6 ?+ [1 e: j6 M, R# n       GOTO MAIN+ c; r1 P6 i* N- G, k: ~% y. w
       ORG   0" S6 z* R( a- p- `
   DELAY …9 I& t- c% J: k+ N, h7 h
   MAIN MOVLW 0 ;主程序开始
) c9 T6 C' j. V1 N* l8 k       TRTS 5 ;设置RA口为输出( ?! h. d4 _$ ^. @5 h* a
    BCF 5,0  ;置RA口0位为09 V$ r+ S" U* I7 ~/ k2 n7 X5 K, J; Y
   LOOP   CALL DELAY;闪动延时
2 P+ V2 H4 y& H% G+ U! s5 r       COMF 5?? ;RA口求反(亮—灭—
; A; N) x: g8 x               亮……控制). a( J7 C( ^, a4 e6 y0 K5 N0 W
       GOTO LOOP ;循环/ R) l! w/ {! ]2 q' n# U
       …3 i, K- n, C* z# w* x/ C3 \
  说明:上述指令是一种PIC16C54 LED发光控制实验部分程序。其中延时子程序DELY未列出,但不影响本条指令的识读。程序中的主程序开始的三条指令,均已介绍过,紧跟着的CALL指令是调用执行子程序,其入口地址为标号DELAY。子程序执行结束后,又执行COMF 5的LED发光亮—灭…亮—灭……控制指令。后面一条GOTO LOOP指令是达到LED循环点亮目的。
9 g7 I: a$ n4 J3 j: t+ a  13?泵嫦蛭坏牟僮髦噶?(共4条,PIC高级产品多增一条)! N2 E/ r- n' y8 n
  该类指令除一条位清零外,另有一条寄存器f位b的置1指令和另外两条位跳步指令(PIC高级产品多增一条f的b位触发转换指令)。
7 q5 C: Y* r5 o2 z/ n  (1)位置1指令。指令格式 BSF f,b( M% y& [& y" w! u' J' b
  说明:BSF是Bit Set f的缩写。F和b的意义与前述相同,该条指令的功能是将寄存器f的b位置1。
7 \5 J% M& B5 e  (2)位测试、为零间跳指令。指令格式 BTFSC f,b
* r. W) _6 y+ d  说明:BTFSC是Bit Test,Skip if Clear的缩写。指令功能是测试寄存器f位“b”,如为0,跳过下一条指令;为1顺序执行,即当f(b)=0时,就不执行当前指令而执行下一条指令(间跳),即用一条空指令NOP代替它,所以该条指令占用2个指令周期。# N! o6 O$ z* r$ a2 S- w
  (3)位测试、为1间跳指令。指令格式 BTFSS f,b5 ^4 `/ I# z. i3 q/ R8 B+ J/ `
  说明:BTFSS是Bit Test,Skip if Set的缩写。其指令的逻辑功能与上条相反,位测试f(b)=1就间跳执行,f(b)=0顺序执行。: T4 m# `7 v% X
  上面介绍的PIC 8位单片机汇编语言指令仅是部分指令,此外还有循环左、右移指令;W和寄存器f相“加”、相“与”指令和进入睡眠方式等指令。
作者: ygcgsa    时间: 2016-5-31 14:56
楼主好用心!
作者: cyxs    时间: 2016-6-7 17:41
学习中,谢谢分享# d& c- b( b+ ~2 c( ~* c$ q

作者: Gegu    时间: 2017-1-5 13:56
楼主好用心!
作者: xuzwg    时间: 2017-1-5 13:56





欢迎光临 EDA365电子工程师网 (http://bbs.elecnest.cn/) Powered by Discuz! X3.2