第十四课 单片机的指令 四
一 控制转移类指令
控制转移类指令共有 17 条 分为无条件转移指令 条件转移指令和返回及调用指令三大类 下 面我们分别加以学习
1 无条件转移类指令
1无条件绝对转移指令 AJMP addr11
2无条件长转移指令 LJMP addr16
3无条件相对转移指令 SJMP rel
在讲解上面这三条指令之前先来认识一下三个符号 add11 add16 rel 其中 add11 和 add16 表 示外部 ROM 的 16 位和 11 位地址 前面我们已经讲过 单片机的外部 ROM 可以扩展到 64K add16 就表
示 64K 程序存储器的任何地址 换句话说 LJMP 指令可以跳转到程序的任何地方 而 add11 则表示下 一条指令的 2K 页面 也就是说 SJMP 指令只能跳转到程序的 2K 范围之内 rel 表示 8 位的偏移量 其 范围是下一条指令第一字节的前 128 到后 127 个字节(即-128-+127) 介绍完了三个符号 再看上面的 三条转移类指令 如果要仔细分析的话 它们之间其实区别很大 但在初学时 我们可以不理会这么多
统统把它们理解成 *JMP 标号比如 SJMP LOOP 就是跳转到有 LOOP 标号处 原则上 所有用 SJMP
或 AJMP 的地方都可以用 LJMP 来替代 因此在初学时 需要跳转时可以全不采用 LJMP 代替 下面再来 看第 4 条跳转指令
4无条件间接转移指令 JMP @A+DPTR
这条指令的用途也是跳转 跳转到什么地方去呢 这可不能由标号简单地决定了 让我们从一个 实际的例子入手吧
MOV DPTR #TAB 将 TAB 所代表的地址送入 DPTR
A 中的值乘 2 详见下面的说明
JMP A @A+DPTR 跳转
TAB: AJMP S1 跳转表格
AJMP S2
AJMP S3
应用背景介绍 在单片机开发中 经常要用到键盘 见下面的 9 个按键的键盘图 我们的要求是
当按下功能键 A G 时去完成不同的功能 这用程序设计语言来表达的话 就是 按下不同的键去执 行不同的程序段 以完成不同的功能 怎么样来实现这个功能呢
看图 前面的程序读入的是按键的值 如按下 A'键后获得的键值是 0按下 B'键后获得的 值是 1 等等 然后根据不同的值进行跳转 如键值为 0 就转到 S1 处执行 如键值为 1 就转到
S2 处执行 到底如何来实现这一功能呢
先从程序的下面看起 是若干条 AJMP 语句 这若干条 AJMP 语句最后在存储器中是这样存放的 见 图也就是每个 AJMP 语句都占用了两个存储器的空间 并且是连续存放的 而 AJMP S1 存放的地址是
47
----------------
TAB 到底 TAB 等于多少 我们不需要知道 把它留给汇编程序来算好了
下面我们来看这段程序的执行过程 第 1 条 MOV DPTR #TAB 执行完了之后 DPTR 中的值就是 TAB
第 2 条是 MOV A R0 我们假设 R0 是由按键处理程序获得的键值 比如按下 A 键 R0 中的值是 0
按下 B 键 R0 中的值是 1 以此类推 现在我们假设按下的是 B 键 则执行完第 2 条指令
后 A 中的值就是 1并且按照我们的分析 按下 B 后应当执行 S2 这段程序 让我们来看一看是 否是这样呢 第 3 条 第 4 条指令是将 A 中的值乘 2即执行完第 4 条指令后 A 中的值是 2下面
就执行 JMP @A+DPTR 了 现在 DPTR 中的值是 TAB而 A+DPTR 后就是 TAB+2因此 执行完这条 程序后 将会跳到 TAB+2 这个地址处继续执行 看一看在 TAB+2 这个地址里面放的是什么 就是 AJMP S2
这条指令 因此 马上又执行 AJMP S2 这条指令 程序将跳到 S2 处往下执行 这与我们的要求相符合
请大家自行分析按下键 ACD 之后的情况
这样我们用 JMP @A+DPTR 这条指令就实现了按下一个键跳转到相应程序段去执行的这样一个要
求 再提一个问题 为什么取得键值后要乘 2 呢 如果例程下面的所有指令换成 LJMP 即 LJMP S1,LJMP S2 这段程序还能正确地执行吗 如果不能 应该怎么改
条件转移类指令 条件转移类指令就是在满足一定的条件后进行相对转移
累加器为 0 转移指令 JZ rel
累加器非 0 转移指令 JNZ rel
第 条指令的功能是 如果(A)=0 则转移 否则顺序执行执行本指令的下一条指令转移到 什么地方去呢 如果按照传统的方法 就要算偏移量 很麻烦 好在现在我们可以借助于机器汇编了
因此这条指令我们可以这样理解 JZ 标号 即转移到标号处 下面举一个例子来加以说明
MOV A R0
JZ L1
MOV R1,#00H
AJMP L2
L1: MOV R1,#0FFH
L2: SJMP L2
END
在执行上面这段程序前如果 R0 中的值是 0 的话 就转移到 L1 标号处执行 因此最终的执行结 果是 R1 中的值为 0FFH 而如果 R0 中的值不等于 0则顺序执行 也就是执行 MOV R1 #00H 指令
48
----------------
最终的执行结果是 R1 中的值等于 0把这个例子中的 JZ 改成 JNZ 试试吧 看看程序执行的结果是什 么 这里注意一点 L1 MOV R1 #0FFH 这条指令的位置不能大于-128 到+127 为什么 我好象刚才
讲过了
比较转移指令
A CJNE A #data,rel
指令说明 第 1 条指令的功能是将 A 中的值和立即数 data 比较 如果两者相等 就顺序执行 如 果不相等 就转移 同样地 我们可以将 rel 理解成标号 即 CJNE A #data,标号 这样利用这条 指令 我们就可以判断两数是否相等 这在很多场合是非常有用的 但有时还想知道两数比较之后哪个
大 哪个小 本条指令也具有这样的功能 如果两数不相等 则 CPU 还会反映出哪个数大 哪个数小
这是用 CY进位位来实现的 如果前面的数A 中的大 则 CY=0 否则 CY=1 因此在程序转移后 再次利用 CY 就可判断出 A 中的数比 data 大还是小了
例如 MOV A,R0
CJNE A,#10H,L1
MOV R1,#0FFH
AJMP L3
L1: JC L2
MOV R1,#0AAH
AJMP L3
L2: MOV R1,#0FFH
L3: SJMP L3
上面的程序中有一条指令我们还没学过 即 JC 这条指令的原型是 JC rel,作用和上面的 JZ 类
似 但是它是判 CY 是 0 还是 1 进行转移 如果 CY=1 则转移到 JC 后面的标号处执行 如果 CY=0
则顺序执行
分析一下上面的程序 如果A=10H 则顺序执行 即 R1=0 如果A不等于 10H 则转到 L1
处继续执行 在 L1 处 再次进行判断 如果A>10H 则 CY=1 将顺序执行 即执行 MOV R1 #0AAH
指令而如果A<10H 则将转移到 L2 处指行 即执行 MOV R1 #0FFH 指令因此最终结果是
本程序执行前 如果R0=10H 则R1=00H 如果R0>10H 则R1=0AAH 如果R0<10H
则R1=0FFH
弄懂了这条指令 其它的几条就类似了 第 2 条是把 A 当中的值和直接地址中的值比较 第 3 条 则是将直接地址中的值和立即数比较 第 4 条是将间址寻址得到的数和立即数比较 这里就不解释了
请大家自行分析一下 下面给出几个相应的例子
CJNE A,10H 把 A 中的值和 10H 中的值比较注意和上题的区别
CJNE 10H #35H 把 10H 中的值和 35H 中的值比较
CJNE @R0,#35H 把 R0 中的值作为地址 从此地址中取数并和 35H 比较
循环转移指令
第 条指令在前面的实验中已经有详细的分析 这里就不解释了 第 条指令 只是将 Rn 改成
direct 其它的也一样
调用及返回指令
在前面的实验中 我们已用过了子程序 只是我们并没有明确地介绍子程序是干什么用的 为什么 要用子程序呢 举个例子 我们数学老师布置了 10 道算术题 经过观察 每一道题中都包含一个 5+2
49
----------------
*3 的运算 我们可以有两种选择 第一种选择 每做一道题 都把这个算式算一遍 第二种选择 我 们可以先把这个结果算出来也就是 21放在一边 然后要用到这个算式时就将 21 代进去 这两种
方法哪种更好呢 那就不必多言了吧 设计程序时也是这样 有时候一个功能会在程序的不同地方反复
使用 我们就可以把这个功能设计成一段程序 每次需要用到这个功能时就 调用 一下 这就是子程 序的用途所在 当然实际上子程序的作用还远不止这些 后面我们还会更详细地讲解子程序的用法
主程序调用了子程序 子程序执行完之后必须再返回到主程序继续执行 不能 一去不回头
那么回到什么地方呢 就是回到调用子程序的下面一条指令处继续执行 大家可以回去看一下前面的实
验 是不是这样做的
1调用指令
上面两条指令都是在主程序中调用子程序 两者的区别大家结合前面的知识可以自己分析一下
同样作为初学者 我们可以不必加以区分 也就是说可以用 LCALL 指令代替 ACALL
2返回指令
A 子程序返回 RET B 中断返回 RETI
RET 用于子程序返回 RETI 用于中断程序返回 这是有区别的 可不能用错了 至于什么是中断
我们讲到中断时再来解释 如何从子程序返回呢 很简单 就是执行 RET 指令 看一下前面的实验
4 空操作指令
所谓空操作 就是什么事也不干 停一个机器周期 一般用作短时间的延时 不要以为它没用
其实作用还是不小的 等你入门了就知道了
二 本课总结
本课的内容是控制转移类指令 这些指令在单片机中起着非常重要的作用 请大家务必搞清楚它 们的作用和功能
三 第 14 课习题
1 控制转移类指令分为几大类 AJMP LJMP SJMP 的区别是什么
2 什么是子程序
3 用实验仪编一段程序 要求按一个键盘时 LED1 闪一秒 按另一个按键后 LED1 闪 0.5 秒
50
----------------
第十五课 单片机的指令 五
一 位及位操作指令
位操作指令也叫布尔操作指令 什么是布尔指令 它有什么用呢 这个问题稍微有点复杂 我只 能给大家简单的介绍一下 在 MCS-51 系列单片机中 有一个功能很强的布尔处理器 它实际上是一个
独立的一位处理器 它有一套专门处理布尔变量布尔变量也叫开关变量 就是以位作为单位的运算和 操作
的指令子集 以完成对布尔变量的传送 运算 转移 控制等操作 这个子集的指令就是布尔操
作指令 那么为什么要有这样的一套的指令系统 它是如何操作的呢 大家接着往下看
1 位寻址的概念
为什么要位寻址呢 单片机不是可以有多种寻址方式吗 大家是否还记得 我们第十三课做的那 个流水灯实验 用的就是 位 操作 也就是对一盏灯的亮和灭进行控制 而之前我们学的指令却全都
是用 字节 来介绍的 字节的移动 加减法 逻辑运算 移位等等 用字节来处理一些数学问题比 如控制空调的温度 电视机的音量等等
非常直观 可以直接用数值来表示 可是如果用它来控制一个
开关的打开或者合上 一个灯的亮或者灭 就有些不直接了 比如我们前面课上的那个流水灯的实验
我们把数值送往 P1 口之后并不能马上知道是哪个 LED 灭了 而是要化成二进制后才能知道 在工业控 制中有很多场合需要处理这类单个的开关输出 比如一个继电器的吸合或者释放 一个指示灯的亮或者
灭 用字节来处理就显得有些麻烦了 所以在 51 系列单片机中就特意引入了一个位处理机制 那么位 处理器有多少地址空间 哪些特殊功能寄存器可以直接进行位寻址呢
2 可位寻址的特殊功能寄存器
在 89C51 单片机中 内部 RAM 的范围是 00H-7FH 之间 其中可位寻址的低 128 位处于内部 RAM 的
20H-2FH 字节单元 其位地址从 00H-7FH 看下面的表
字节地址 |
位地址 |
|||||||
2FH |
7FH |
7EH |
7DH |
7CH |
7BH |
7AH |
79H |
78H |
2EF |
77H |
76H |
75H |
74H |
73H |
72H |
71H |
70H |
2DH |
6FH |
6EH |
6DH |
6CH |
6BH |
6AH |
69H |
68H |
2CH |
67H |
66H |
65H |
64H |
63H |
62H |
61H |
60H |
2BH |
5FH |
5EH |
5DH |
5CH |
5BH |
5AH |
59H |
58H |
2AH |
57H |
56H |
55H |
54H |
53H |
52H |
51H |
50H |
29H |
4FH |
4EH |
4DH |
4CH |
4BH |
4AH |
49H |
48H |
28H |
47H |
46H |
45H |
44H |
43H |
42H |
41H |
40H |
27H |
3FH |
3EH |
3DH |
3CH |
3BH |
3AH |
39H |
38H |
26H |
37H |
36H |
35H |
34H |
33H |
32H |
31H |
30H |
25H |
2FH |
2EH |
2DH |
2CH |
2BH |
2AH |
29H |
28H |
24H |
27H |
26H |
25H |
24H |
23H |
22H |
21H |
20H |
23H |
1FH |
1EH |
1DH |
1CH |
1BH |
1AH |
19H |
18H |
22H |
17H |
16H |
15H |
14H |
13H |
12H |
11H |
10H |
21H |
0FH |
0EH |
0DH |
0CH |
0BH |
0AH |
09H |
08H |
20H |
07H |
06H |
05H |
04H |
03H |
02H |
01H |
00H |
在物理实体上它们与原来的以字节寻址的 RAM 及端口是完全一样的 换句话说这些 RAM 单元及端
口都可以有两种用法 比如用 28H.5 和 45H 是一样的
除此之外 从 80H 单元开始除了程序计数器 PC 和 4 个工作寄存器区外 每 8 个字节还安排了 21
个特殊功能寄存器89C52 有 26 个这些 SFR 都有一个共同的特点 就是其字节地址均可被 8 整除
大家回到前面看一下第九课的表格 这些 SFR 都是具有位寻址功能的 也就是说这些 RAM 单元的每一个 位都可以直接用这个地址来对其直接进行操作 了解了位操作的原理 再来看位操作的指令
51
----------------
4 位操作指令
1位传送指令
A MOV C bit
B MOV bit C
指令说明 这两条指令的功能是实现进位位和其它位地址之间的数据传递这里 bit 就是位的意 思例如 MOV P1.0,CY 将 CY 中的状态送到 P1.0 引脚上去如果是做算术运算 我们就可以通过观
察 P1.0 端口知道现在 CY 是多少了再如 MOV P1.0,CY 将 P1.0 的状态送给 CY
2位清零指令
A
CLR
C
B
CLR
bit
指令说明 第 1 条指令使 CY=0 第 2 条指令使指定的位地址等于 0例如 CLR P1.0 使 P1.0
为 0
3位置 1 指令
A
SETB
C
B
SETB
bit
指令说明 第 1 条使 CY=1 第 2 条使指定的位地址等于 1例如 SETB P1.0 使 P1.0 为 1
4取反指令
A
CPL
C
B
CPL
bit
指令说明 第 1 条使 CY 等于原来的相反的值 即由 1 变为 0由 0 变为 1第 2 条使 指定位的值等于原来相反的值 相当于做 非 运算例如 CPL P1.0 以我们做过的实验为例
如果原来灯是亮的 则执行本指令后灯就灭了 反之就是灯亮
5位逻辑 与 指令
指令说明 第 1 条 CY 位与指定的位地址的值相 与结果送回 CY 第 2 条先将指定的位地址中 的值取出后取反 再和 CY 相 与结果送回 CY 但需注意 指定的位地址中的值本身并不发生变
化 例如 ANL C,/P1.0 设 执行本指令前 CY=1 P1.0 等于 1 灯灭则执行完本指令后 CY=0
而 P1.0 仍等于 1可用下列程序进行验证
ORG 0000H
AJMP START
ORG 30H
START MOV SP #5FH
MOV P1 #0FFH
SETB C
ANL C /P1.0
MOV P1.1,C 将做完的结果送 P1.1,结果应当是 P1.1 上的灯亮 而 P1.0 上的灯还是
亮
6位逻辑 或 指令
A ORL C bit
B ORL bit C
这两条指令的功能大家自行分析吧 然后对照上面的例程 自己编一个验证程序 看看自己想得 对不对
7判 CY 条件转移指令
A JC rel
52
----------------
B JNC rel
指令说明 这两条指令叫做判 CY 转移指令 第 1 条指令的功能是如果 CY 等于 1 就转移 如果 不等于 1 就顺序执行 那么转移到什么地方去呢 我们可以这样理解 JC 标号 即如果等于 1
就转到标号处执行 第 2 条指令则和第 1 条指令正好相反 即如果 CY=0 就转移 不等于 0 则顺序执
行 转移到什么地方 我们同样可以这样理解 JNC 标号
8判位变量转移指令
A JB bit,rel
B JNB bit,rel
指令说明 第 1 条指令是如果指定的 bit 位中的值是 1则转移 否则就顺序执行 转移到什 么地方 同样我们可以这样理解 JB bit,标号 第 2 条指令请大家自行分析一下
9判位变量转移并将该位清零 JBC bit rel
指令说明 这条指令同 JB bit rel 的区别在于判 1 转移的同时清除该位 为什么要这样做
呢 以后我们会讲到
接下来我们做一个这方面的实验
二 位操作指令实验
1 实验程序
ORG 0000H ; LJMP START ; ORG 30H ; START:MOV SP,#5FH ;
MOV
P1,#0FFH
;
MOV
P3,#0FFH
;
L1:JNB P3.4,L2 ;P3.4 上接有一只按键 它按下时 P3.4=0
JNB P3.5,L3 ;P3.5 上接有一只按键 它按下时 P3.5=0
LJMP L1 ; L2:MOV P1,#00H ; LJMP L1 ; L3:MOV P1,#0FFH ; LJMP L1 ;
END.
2 程序分析
把上面的程序下载到实验板上 看看有什么现象 按下接在 P3.4 上的按键 P1 口的灯全亮
了 松开或再按 灯并不熄灭 然后按下接在 P3.5 上的按键 灯就全灭了 这像什么 这不就是工业 控制中经常用到的启动 停止功能吗 怎么做到的呢 一开始 将 0FFH 送入 P3 口 这样 P3 口所有
的引线都处于高电平 然后执行 L1 如果 P3.4 是高电平键没有按下则顺序执行 JNB P3.5,L3 语
句 同样 如果 P3.5 是高电平键没有按下则顺序执行 LJMP L1 语句 这样就不停地检测 P3.4 和
P3.5 如果有一次 P3.4 上的按键按下去了 则转移到 L2执行 MOV P1 #00H使灯全亮 然后又转
去 L1 再次循环 直到检测到 P3.5 为 0就转去 L3执行 MOV P1 #0FFH使灯全灭 再转去 L1
如此不断地循环就可以了 这里提一个问题 我们这个实验中控制的是一个字节既整个 P1 口如何 来实现一位比如 P1.0的控制呢 其实很简单 只要把程序改一下就可以了
程序如下
ORG 0000H ; LJMP START ; ORG 30H ; START:MOV SP,#5FH ;
53
----------------
MOV
P1,#0FFH
;
MOV
P3,#0FFH
;
L1:JNB P3.4,L2 ;P3.4 上接有一只按键 它按下时 P3.4=0
JNB P3.5,L3 ;P3.5 上接有一只按键 它按下时 P3.5=0
LJMP L1 ;
L2:CLR P1.0 ;亮 LED1
LJMP L1 ;
L3:SETB P1.0 ;暗 LED1
LJMP L1 ;
END
尽管实际的程序还要考虑按键的去抖动问题 但程序的基本结构和流程是实用的 这样的程序就 能完成我们对工业控制中一个继电器的控制目的 怎么样 如果现在让您用单片机控制一台电机的正反 转应该没有问题了吧 试试看 最后提个问题 能不能把本程序改用 JB 指令来写 如果行的话 该怎 么写呢
既然讲到了位操作指令 就一定要讲讲位操作指令的写法 在很多的程序中 常常这样写程序
CLR 20H.02 CLR PSW.7 什么意思 以 PSW.7 为例 大家还记得 PSW 是什么 对了是程序状态字寄 存器 那么 PSW.7 是什么意思呢 其实说穿了很简单就是把 PSW 的第 7 位清零 第 7 位是什么 忘了
也太没记性了 是 CY 位 回去翻翻 这样一来 CLR 20H.02 也应该懂了吧
三 本课总结
这一课我们讲述了位操作指令的编程方法 事实上正是由于单片机有了位操作机制 才使得其编程 变得十分的简单和方便 希望大家把这部分的内容搞清楚
到本课为止 单片机的指令已经全部讲完 也许有些您已经记住了 而有些可能还记不住 或者说 还不会使用 没关系 我们以后还会在具体的实验中再来讲解它们 下一课我将向大家讲解单片机程序 的设计方法 这可是蛮重要的哦
四 第 15 课习题
1 位寻址与字节寻址的区别在哪里
2 单片机中哪些 RAM 和 SFR 是可以位寻址的
3 写一段程序 使两个 LED 灯交替地亮或灭 类似于工业控制中的正反转功能
54
----------------