《51单片机应用开发从入门到精通》——2.5 用取表方式实现灯移动

本节书摘来异步社区《51单片机应用开发从入门到精通》一书中的第2章,第2.5节,作者:张华杰 ,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.5 用取表方式实现灯移动

51单片机应用开发从入门到精通
功能说明:单片机端口接8只LED,编程时利用取表的方法,使端口P1的LED先一次点亮1只跳跃左移,点亮顺序为P1.0、P1.2、P1.4、P1.6;接着一次点亮2只,从左向右移动2次;一次点亮3只,左右移3次;然后4只灯P1.0、P1.2、P1.4、P1.6与P1.1、P1.3、P1.5、P1.7交互点亮4次;最后8只灯闪烁6次,不断循环。

2.5.1 程序设计

1.流程图
程序设计流程如图2-3所示。

《51单片机应用开发从入门到精通》——2.5 用取表方式实现灯移动

2.程序
汇编语言编写的用取表方式实现灯移动源程序代码如下:

01:    START:   MOV       DPTR,  #TABLE      ; 存表
02:    LOOP:    CLR       A                   ; A清0 
03:             MOVC      A, @A + DPTR        ; 取表代码
04:             CJNE      A, #01H, LOOP1      ; 不是01H,循环
05:             JMP       START               ; 重新设定
06:    LOOP1:   MOV       P1, A               ; 将A值送入P1输出
07:             ACALL     DELAY               ; 调用延时子程序
08:             INC       DPTR                ; 数据指针加1
09:             JMP       LOOP                ; 跳转到LOOP处  
10:    DELAY:   MOV      R5, #25             ; 延时0.5s   
11:    DLY1:    MOV       R6, #100        
12:    DLY2:    MOV       R7, #100        
13:             DJNZ      R7, $       
14:             DJNZ      R6, DLY2         
15:             DJNZ      R5, DLY1        
16:             RET                             ; 延时子程序返回       
17:  ; 单灯向左跳移1次控制码
18:    TABLE:   DB       0FEH,0FBH,0EFH,0BFH  
19:  ; 2灯向右移动2次控制码
20:              DB        3FH,9FH,0CFH,0E7H   
21:              DB        0F3H,0F9H,0FCH
22:              DB        3FH,9FH,0CFH,0E7H   
23:              DB        0F3H,0F9H,0FCH  
24:  ; 3灯左右移动3次控制码
25:              DB        0F8H,1FH,0F8H,1FH   
26:  ; 4灯交互移动4次控制码
27:              DB        0AAH,55H,0AAH,55H  
28:  ; 8灯闪烁6次控制码
29:              DB        00H,0FFH,00H,0FFH    
30:              DB        00H,0FFH,00H,0FFH    
31:              DB        00H,0FFH,00H,0FFH   
32:              DB        01H                   ; 结束码     
33:              END                             ; 程序结束

2.5.2 代码详解

1.标号说明
START:起始程序的进入点。

LOOP:循环操作的进入点。

LOOP1:输出循环的进入点。

DELAY:延时子程序的进入点。

DEL1:延时重复递减判断的进入点1。

DEL2:延时重复递减判断的进入点2。

TABLE:代码表的进入点。

2.寄存器使用分配情况
程序中A、P、R5、R6、R7、DPTR为寄存器,其中A、P、R5、R6、R7寄存器的作用与前节相同。

DPTR是新用到的寄存器,也是唯一的16位专用寄存器,它是由两个8位寄存器DPH和DPL拼装而成,其中DPH为DPTR的高8位,DPL为DPTR的低8位。它既可作为一个16位寄存器来使用,也可以作为两个独立的8位寄存器(DPH和DPL)来使用。

我们把代码表存入DPTR,确切地说是将代码表的首地址TABLE送到DPTR中作基地址,A作为变址寄存器,基址寄存器和变址寄存器的内容相加(@A + DPTR)形成16位地址,该地址既是操作数的地址,也是存入地址指针(也叫数据指针)。知道了地址指针,按照地址指针所指,自然就能取到代码表中的控制码了。

3.程序分析解释
本程序利用取表方式实现亮灯的移动。

利用取表的方式将控制码建成一个表,利用“MOV DPTR, #TABLE”语句存表,再利用“MOVC A, @A + DPTR”语句从表中读取出控制码,这种方法可方便地处理一些复杂的控制动作,下面重点介绍这一部分。

第01~09行代码的作用是存表、读取表并输出,是程序的主要部分。

01:存入TABLE表,用来控制灯的一组组十六进制数控制码建成一个表,取表名为TABLE。将TABLE表存放的地址存入特殊功能寄存器DPTR中,以便查找。

所谓存表是指存入表的首地址,即表的地址指针或数据指针。地址指针指向第1行第1位控制码的位置。总之,地址指针指向哪个位置就到哪个位置读取控制码。

02:将寄存器A中的内容清除为0。

03:查表读取控制码。“MOVC A, @A + DPTR”是一条很重要的查表语句,它将A + DPTR指定的表地址的数据送入寄存器A中。当程序第一次运行时,地址指针指向的位置是表中首位放置的控制码0FEH,将0FEH取出并存入寄存器A中。

04:判断读取出的控制码是否为结束码01H,如果不是则跳转到LOOP1处。CJNE指令的功能是比较两个操作数,若不相等就跳转,只有相等时才继续向下运行。01H是结束标志码,放在表中的最后位置。

如果寄存器A中的数不是01H,即不相等,说明表还没有查完,程序跳转到LOOP1处继续查表读取出控制码并输出。

若寄存器A中的数是01H,即两个操作数相等了,说明表已经查完一遍,程序执行下一条语句。

05:程序跳转到标号START处,即跳转到程序开始处,重新设定显示值。

06:将取来的控制码送入P1并输出。

07:调用0.5s延时子程序。

08:数据指针加1,指向下一个操作码。INC指令的功能是使寄存器A中的数据加1,即地址指针向下移动一个位置,指向第二个控制码,为下一次取码做准备。

09:跳转到LOOP处,重新开始,不断循环。

10~16:0.5s延时子程序。

第18~32行代码为控制码编制表,表名为TABLE。

18:单灯跳跃左移控制码。

20~23:两只灯从左向右移动控制码。

25:3只灯左右移控制码。

27:4只灯交互点亮控制码。

亮灯的移动是由控制码控制的,控制码采用十六进制数,如果将十六进制码换成二进制码就可以清楚地看出灯是怎样被控制的。

29~31:闪烁6次控制码。控制码00H表示8个灯都亮(0表示亮);控制码0FFH表示8个灯都灭(1表示灭)。一亮一灭形成一次闪烁,共6对控制码,闪烁6次。

32:结束控制码。结束码可以是任意的数,存放的位置要在表的最后,标志控制码已经结束。此时,由于第04行语句的作用,当寄存器A中的数是01H,即两个操作数相等,程序将执行下一条语句,即跳转到标号LOOP处,程序重新开始。

如果没有结束码,当取表码结束后,程序运行下一行END指令,使整个程序结束。

此外,制表时要注意,每一行控制码的前边都要加伪指令DB,DB是定义字节的指令。

33:程序结束。

4.边用边学指令
本节使用新的指令有:MOVC、INC、CLR、CJNE和DB。

MOVC:MOVC和前边经常用到的MOV指令都属于数据传送类指令,基本功能是传送数据,但使用上有所区别。MOVC的功能是将表中查到的一组数据送到寄存器A。常用形式如“MOVC A, @A + DPTR”,意思是将A + DPTR指定的内存地址的数据传送到寄存器A,所以也称查表指令MOVC。
INC:是算术运算类指令。算术运算类指令包括有加、减、乘和除法指令,其中INC指令的功能是使累加器加1。
CLR:属于位移类指令。CLR的功能是将累加器A清0。
CJNE:属于控制转移类指令。CJNE与前边介绍的DJNZ指令都是有条件转移指令,DJNZ的条件是寄存器减1不为0转移,一般是与寄存器配合使用,用在控制已知循环次数的循环结构程序中。而CJNE的条件是对两数进行比较,不相等则转移,多用在分支结构的程序中。
DB:是伪指令。其功能是从指定的地址单元开始,定义若干个字节作为内存单元的内容。
5.本章用过的指令归类
数据传送类指令:MOV、MOVC。
算术运算类指令:INC。
逻辑运算及位移类指令:RL、RR、CLR。
控制转移类指令:JMP、DJNZ、ACALL、RET、CJNE。
位操作类指令:CPL。
伪指令:END、DB。

2.5.3 模拟仿真

本节模拟仿真时主要注意观察取控制码的过程。

运行程序第1行语句时,把表TABLE的首地址存入到DPTR寄存器。

注意,在特殊功能寄存器窗口中看不到DPTR,而是看到两个独立的8位寄存器DPH和DPL,DPTR寄存器是由DPH和DPL组成的。此时DPL = 1CH,此地址是表的开头第1个控制码的地址,也称地址指针或数据指针,即该指针指向表中的第1个控制码0FEH。

运行程序第2行语句时将寄存器A清0。

运行程序第3行语句时,由于在第3行语句中是将A和DPTR两个寄存器内容相加,此时A为0,相加后仍为原DPTR的地址,此地址指针指向的是第1个控制码0FEH,所以,送入寄存器A的控制码是0FEH。

接下来再运行第6行语句时将控制码0FEH送入P1并输出。

运行到第8行语句“INC DPTR”时,地址指针加1即1CH + 01H = 1DH,所以DPL = 1DH,此地址指针指向的是第2个控制码0FBH,依次不断循环。

2.5.4 实例测试

将写入程序的单片机插入实验板接通电源后,会看到单灯向左跳移1次,双灯向右移动2次,3只灯左右移动3次,4只灯交互点亮,8只灯闪烁6次,不断循环。

如果将程序改动一下,把表TABLE中的结束码取消,再在实验板上运行程序,会看到亮灯在进行闪烁后,程序马上停止,不再循环。

这是因为当表查完后,程序要执行下一个语句“END”,END是程序结束指令,所以程序就此结束。

如果想查完一遍表后程序不立即结束,需要在表中最后一行设置一个结束标志码,当程序取到该结束标志码时,通过判断语句(如“CJNE A,#01H,LOOP1”)的作用,使程序不运行结束语句END,而跳转执行其他程序段。

2.5.5 经验总结

利用查表方法编写程序,可以很方便地完成一些复杂的控制功能。

首先通过DB伪指令把控制码制成一个表TABLE,然后再通过查表的指令查表取码。用于查表的指令有两条:

MOVC  A,@A + DPTR
MOVC  A,@A + PC```
其中使用DPTR作为基地址来查表比较简单,可以通过两步操作来完成。

(1)利用“MOV DPTR,#TABLE”语句,将所查表的首地址存入DPTR数据指针寄存器。
上一篇:巧用Zabbix自定义监控Mysql性能状态


下一篇:《51单片机应用开发从入门到精通》——2.4 延时时间计算实例