51系列单片机的程序设计.docx
《51系列单片机的程序设计.docx》由会员分享,可在线阅读,更多相关《51系列单片机的程序设计.docx(118页珍藏版)》请在冰豆网上搜索。
![51系列单片机的程序设计.docx](https://file1.bdocx.com/fileroot1/2023-5/16/5f9f27a3-af54-4f6a-866b-03cca318370d/5f9f27a3-af54-4f6a-866b-03cca318370d1.gif)
51系列单片机的程序设计
程序设计及试验
汇编语言是一种面相机器的程序设计语言,单片机内核不一样,汇编的语言和指令格式也不相同.下面我们以51单片机的汇编语言为基础,详细讲解一下51单片机的程序设计,中间穿插了一些试验例子,以便于大家好掌握.
第一课51单片机汇编程序流程图和设计格式
1.1序流程图:
在设计程序之前我们都会理一理设计思路,这就是原始的程序流程图,在实际设计中,特别是比较大的项目中,就必须将程序的设计思路先开始定下来,这个思路先开始是一种理想状态,我们认为它是对的,只有在设计和调试中我们才会去纠正某些出错的地方,也就是说,程序流程图只起方向指导的作用,在设计时有可能会一步一步纠正他,有些书上将一些规则讲得很详细,我在此不再累述,我要讲的就是,如果程序只是为自己设计的,你可以自己定义一个熟悉的规则,将程序的设计思路表达出来就行了,没有必要像书上写得那幺正规,但是一定要注意,你的表达方式必须要在你日后能够看得懂它,其中最重要的是在程序语句后面加上注释,小的程序可以不写程序流程图,有一些算法最好是写写程序流程图并最好注释,有必要时可以将入口和出口参数标示出来,以便在今后调用是直接输入入口参数,再取出口参数即可,这里的入口和出口参数分别指的是开始参数和结果参数,比如说:
你有一个2个字节乘法程序算法,乘数放在40H,41H,被乘数放在43H,44H单元中,经过此算法后,结果放在放在50H,51H,52H,53H,那幺入口就是40H,41H和43H,44H,在计算开始时,我们将数据放在这些单元中,调用算法后,我们就可以在50H,51H,52H,53H取结果就行,50H,51H,52H,53H就是出口。
1.2
汇编语言通常是典型的四分段格式,如下:
标号段
操作码段
操作数段
注释段
LABLE
OPCODE
OPRAND
COMMENT
标号段和操作码段之间要用”:
”相隔,操作码和操作数段间要用空格分隔;双操作数之间要用”,”分隔,操作数段和注释间要用”;分隔”,操作码段势必选项,其它是任选项:
例如:
ORG0080H
START:
MOVA,#0
MOVR1,#01H
MOVR2,#08H
LOOP:
ADDA,R1;将累加器A加1
DJNZR2,LOOP
NOP
LJMPSTART
END
上例中的START,LOOP就是标号字段,MOV,ADD,NOP就是操作码段,08H,01H就是操作数段,”将累加器A加1”就是注释;END是整个程序的结束标识.
下面我简单的描述一下指示性语句的用法:
1.ORGXXXXH(16位)
表示程序执行的起始地址;
2.ENG:
表示整个主程序的结束;
3.EQU:
汇编语言的赋值指令,相当于C语言的“=”;
4.DATA:
汇编语言的数据地址赋值指令;
5.DB:
汇编语言的字节定义指令(8位),常常应用于查表程序中;
6.DW:
汇编语言的字定义指令(16位),常常应用于查表程序中;
7.BIT:
汇编语言的位赋值指令,相当于C语言的“SBIT”;
8.DS:
汇编语言的地址预留指令,从它的标号地址开始预留一定数量的单元,已备源程序执行过程中使用,例如:
ORG0400H
START:
MOVA,#0
……
LP:
DS09H
DB25H
END
上述源程序汇编时,当碰到DS语句时便会自动的从LP地址开始预留9格内存单元;
第二课I/O口操作程序的设计
I/O口顾名思义就是中文的”输入输出”的意思,51单片机的I/O口操作较简单,不像其它的单片机在程序初始化时要定义一下I/O口的输入输出方向,然后才能操作,51单片机的I/O口操作较随意,在不同的情况下可任意输入输出,下面我们来学习I/O口操作程序的设计的方法.
2.1用I/O口点亮LED发光二极管
2.1.1用P1.0点亮一个LED发光二极管
ORG0000H;程序执行的起始地址
LJMPSTART
ORG0080H
START:
SETBP1.0;关LED
LOOP:
NOP
CLRP1.0;开LED
LJMPLOOP;让LED一直被点亮
END
从以上程序我们可以知道,51单片机控制如此容易,下一步我们让LED眨眼睛
2.1.2用P1.0点亮一个LED发光二极管,并且LED亮一秒,灭一秒
在编程序前,我们来认识”延时”的真正涵义:
单片机的延时就是然单片机停在某个地方等一段时间的意思,不要简单认为此时的单片机没有执行操作,只不过是我们让单片机在一个循环程序中执行加减操作,一至运行到此程序的最后一条指令为止,根据单片机机器周期的执行时间,我们可以进行不太精确的延时,下面我们来看一看延时子程序的编写方法.
DELAY:
MOVR6,#0FFH
TN1:
MOVR5,#0FFH;1个机器周期
TN0:
DJNZR5,TNO;R5减1,如果R5等于0,从新为R5赋值0FFH;2个机器周期
DJNZR6,TN1;R6减1,如果R5等于0,退出程序
;2个机器周期
RET
假设我们用的振荡器频率为12.000M,那幺一个机器周期的时间为:
Tx=12/12MS=0.000001S=1uS
那幺上面的延时程序的时间为:
256x(256+1)x1uS=65.8ms
如果我们要做1秒中的延时,就必须将以上程序执行1000/65.8=15次
在实际应用中,我们不可能调用15次以上程序,因而我们会再加上15次循环即可,程序如下:
DELAY1mS:
MOVR7,#0FH
DELAY:
MOVR6,#0FFH
TN1:
MOVR5,#0FFH;1个机器周期
TN0:
DJNZR5,TNO;R5减1,如果R5等于0,从新为R5赋值0FFH;2个机器周期
DJNZR6,TN1;R6减1,如果R5等于0,退出程序
;2个机器周期
DJNZR7,DELAY;15到了码?
没有,返回DELAY
RET
有时候,主程序调用延时程序之前,会用到R6,R5,R7积存器,如果调用延时程序后,这些寄存器的值会改变,为了避免这种情况的出现,修改如下:
DELAY1Ms:
PUSH05H;保存R5值
PUSH06H;保存R6值
PUSH07H;保存R7值
MOVR7,#0FH
DELAY:
MOVR6,#0FFH
TN1:
MOVR5,#0FFH;1个机器周期
TN0:
DJNZR5,TNO;R5减1,如果R5等于0,从新为R5赋值0FFH;2个机器周期
DJNZR6,TN1;R6减1,如果R5等于0,退出程序
;2个机器周期
DJNZR7,DELAY;15到了码?
没有,返回DELAY
POP07H;先弹出R7,注意先进后出的顺序
POP06H;然后弹出R6
POP05H;最后弹出R5
RET
好了,下面我们再来完成让LED亮一秒,灭一秒的程序:
ORG0000H;程序执行的起始地址
LJMPSTART
ORG0080H
START:
SETBP1.0;关LED
LOOP:
NOP
CLRP1.0;开LED
CALLDELAY1Ms;延时1S
SETBP1.0
CALLDELAY1Ms;延时1S
LJMPLOOP;让LED一直被点亮
END
以上程序,我们也可以让其它的LED灯闪烁,只是变化一下P1口的设置即可,如果我们想要所有的P1口都运作起来,程序怎样编写呢?
不要急,我们下一步就来实现它.
2.1.3将P1口的LED逐个点亮,每个亮一秒,灭一秒.
未了方便初学者了解,我先用一个较为”笨”的办法设计一个程序演示给大家看:
ORG0000H;程序执行的起始地址
LJMPSTART
ORG0080H
START:
MOVA,#0FFH;关所有的LED
MOVP1,A
LOOP:
CLRP1.0;开P10LED
CALLDELAY1Ms;延时1S
SETBP1.0
CALLDELAY1Ms;延时1S
CLRP1.1;开P11LED
CALLDELAY1Ms;延时1S
SETBP1.1
CALLDELAY1Ms;延时1S
CLRP1.2;开P12LED
CALLDELAY1Ms;延时1S
SETBP1.2
CALLDELAY1Ms;延时1S
CLRP1.3;开P13LED
CALLDELAY1Ms;延时1S
SETBP1.3
CALLDELAY1Ms;延时1S
CLRP1.4;开P14LED
CALLDELAY1Ms;延时1S
SETBP1.4
CALLDELAY1Ms;延时1S
CLRP1.5;开P15LED
CALLDELAY1Ms;延时1S
SETBP1.5
CALLDELAY1Ms;延时1S
CLRP1.6;开P16LED
CALLDELAY1Ms;延时1S
SETBP1.6
CALLDELAY1Ms;延时1S
CLRP1.7;开P17LED
CALLDELAY1Ms;延时1S
SETBP1.7
CALLDELAY1Ms;延时1S
LJMPLOOP;让LED一直被点亮
END
下面我再用一种通用的程序设计方法设计此程序演示给大家看:
ORG0000H;程序执行的起始地址
LJMPSTART
ORG0080H
START:
MOVA,#0FFH;关所有的LED
MOVP1,A
MOVA,#0FEH
LOOP:
MOVP1,A;开LED
CALLDELAY1mS;延时1S
MOVP1,#0FFH;关LED
CALLDELAY1mS;延时1S
RLA
LJMPLOOP
END
大家注意一下,上面两个程序有啥区别?
第1个程序通俗易懂,第二个程序比较简明,但是不好懂,里面有一个简单的算法,至于其它的跑马灯花样,希望大家自己设计一下程序,我在这里不再累述.
第三课按键程序的设计
按键的硬件通常有两种形式:
1.独立式:
独立式键盘较为单一,通过判断键盘接入口的状态就能判断有无键按下;
2.矩阵式也叫行列式:
所谓矩阵式就是说每一个按键不是单独的,键与键之间有一定的硬件联系,它的判断是通过先设置某行或某列的状态再来读某列或某行的状态来实现的.
相比之下,两种方式各有千秋,如果单片机资源充足的情况下,用独立式键盘较方便,如果单片机资源紧缺,当然要用矩阵式键盘,下面我们来简单的讲一下独立式键盘的程序编写原理:
试验板上P1为按键输入口,当有按键按下时,对应的P3.2,P3.3的LED会闪动,我们这样做的目的在于让初学者知道散转指令的一些用法,知道按键的实际应用意义,因程序中有延时,有时候按键会没有效,后面的讲课我们会教你如何去克服这种现象,程序设计如下:
…
89c51
程序编号为:
ORG0000H
LJMPSTART0
ORG0050H
START0:
MOVP1,#0FFH
MOVA,P1
START:
JNBACC.0,PG0;是P1.0被按下吗?
是的,转PG0
JNBACC.1,PG1;是P1.1被按下吗?
是的,转PG1
JNBACC.2,PG2;是P1.2被按下吗?
是的,转PG2
JNBACC.3,PG3;是P1.3被按下吗?
是的,转PG3
JNBACC.4,PG4;是P1.4被按下吗?
是的,转PG4
JNBACC.5,PG5;是P1.5被按下吗?
是的,转PG5
JNBACC.6,PG6;是P1.6被按下吗?
是的,转PG6
JNBACC.7,PG7;是P1.7被按下吗?
是的,转PG7
SJMPSTART
PG0:
MOVP3,#0FFH
CLRP3.2;点亮D7
SJMPSTART
PG1:
MOVP3,#0FFH
CLRP3.3;点亮D8
SJMPSTART
PG2:
MOVP3,#0FFH
CLRP3.2;点亮D7
CLRP3.3;点亮D8
SJMPSTART
PG3:
MOVP3,#0FFH
MOVA,#8
NEXT0:
CLRP3.2;;点亮D7
CALLDELAY1Ms;延时1S
SETBP3.2;灭D7
DECA
CJNEA,#00,NEXT0;循环8次
SJMPSTART
PG4:
MOVP3,#0FFH
CLRP3.3
SJMPSTART
PG5:
MOVP3,#0FFH
CLRP3.2
SJMPSTART
PG6:
MOVP3,#0FFH
CLRP3.2
CLRP3.3
SJMPSTART
PG7:
MOVP3,#0FFH
SJMPSTART
END
那幺矩阵式键盘的程序又是怎样设计的呢?
通常有三种方法来设计按键扫描程序:
⒈计算法;⒉查表法;⒊查寻法。
下面我用计算法来设计一下按键扫描程序:
程序标号为:
KEY01.ASM
P1.4P1.5P1.6P1.7
P1.0
P1.1
P1.2
P1.3
89C51
ORG0000H
LJMPSTART
ORG0050H
START:
MOVP1,#0FH;按键初始化
LCALLKEYIN;调按键输入子程序
JNZLKOUT;有按键吗?
LJMPSTART
LKOUT:
MOVR2,#0EFH;首列扫描码送入R2
MOVR4,#00H;首列偏移值送R4
CONU:
MOVP1,R2;列扫描码送P1
MOVA,P1;读按键值
JBACC.0,L0NE;检查按键是否在第0行.
MOVA,#00H;此行有键按下,将列号放入A
AJMPKCODE;求出键号
LONE:
JBACC.1,LTWO;检查按键是否在第1行.
MOVA,#04H;此行有键按下,将列号放入A
AJMPKCODE;求出键号
LTWO:
JBACC.2,LTHREE;检查按键是否在第2行.
MOVA,#08H;此行有键按下,将列号放入A
AJMPKCODE;求出键号
LTHREE:
JBACC.3,LFOUR;检查按键是否在第2行.
MOVA,#0CH;此行有键按下,将列号放入A
LKP:
;求出键号
ADDA,R4;行号加列号进行编码
PUSHA;将A中的值保存起来
LCALLKEYIN;等待按键释放
JNZWKFE;按键松开了码?
POPA;按键松开,键值放在A中
LJMPKJMP;根据键值处理相关事项
LFOUR:
INCR4;此列没有键按下,继续检查下一列
MOVA,R2;
JNBACC.7,KND;4列检查完了码?
RLA;没有,继续检查.
MOVR2,A;
LJMPCONU;
KND:
;4列检查完,返回
RET
KEYIN:
MOVP1,#0FH;想将按键初始化
MOVA,P1;读按键
CPLA;取反
ANLA,#0FH;屏蔽高四位,在这里其实是检查有无按键按
;下,如A不等于0,表示有按键按下.
RET
KJMP:
……;后面的显示程序中我们会将此转散程序加
;上的
从上面的程序我们知道,按键的编码其实就是按照我们预先定义的键号来做的,至于键号是多少,我们不要去理会,因为我们可以根据自己想要的一个键号值去处理相关的事务.光有按键程序,我们看不到按键执行的效果,下面我重点讲一下用查表法来设计按键扫描程序的设计:
程序编号为:
ORG0000H
LJMPSTART
ORG0050H
START:
MOVA,#0FFH;请按键
MOVP1,A
MOVP2,A;关显示
START1:
MOVR3,#0F7H;按键扫描码送R3
MOVR1,#00H;先将按键值置0
START2:
MOVA,R3;开始扫描按键
MOVP1,A
MOVA,P1;读按键
MOVR4,A;按键存入R4
SETBC
MOVR5,#04H;按键扫描4次
START3:
RLCA;看有没有按键按下
JNCKEYIN
INCR1;没有,指针加一
DJNZR5,START3;扫描四列了吗?
MOVA,R3;扫描值送入A
SETBC
RRCA
MOVR3,A
JCSTART2;4行扫描完了吗?
LJMPSTART1;是
KEYIN:
MOVR7,#80;消抖延时程序
KEYIN1:
MOVR6,#200
DJNZR6,$
DJNZR7,KEYIN1
KEYIN2:
MOVA,P1;在次读按键
XRLA,R4;与前一次按键值比较
JZKEYIN2;按键松开了吗?
MOVA,R1;是
MOVDPTR,#TAB1;查表
MOVCA,@A+DPTR
MOV30H,A;键值存在30H中
LJMPSTART1
TAB1:
DB0DH,0EH,00H,0FH
DB0CH,07H,08H,09H
DB0BH,04H,05H,06H
DB0AH,01H,02H,03H
END
从上面的两个程序我们可以分析得出,查表法设计程序较为简洁,比较好懂,用查寻法设计程序,逻辑较差,程序似乎不紧奏,无论用那种方法,结果都一样,因此,初学者最好定好一种格式,将自己的风格加进去,学会用自己的个性来设计程序。
第四课LED段码管的显示程序的编写
关于段码显示器的显示原理,其实就是将8个LED集成在一起组成一个8字型的器件,从而显示出我们想要的数字,现将编码表列出如下:
显示字符
共阴极段码
共阳极段码
显示字符
共阴极段码
共阳极段码
0
3FH
C0H
9
6FH
90H
1
06H
F9H
A
77H
88H
2
5BH
A4H
B
7CH
83H
3
4FH
B0H
C
39H
C6H
4
66H
99H
D
5EH
A1H
5
6DH
92H
E
79H
86H
6
7DH
82H
F
71H
8EH
7
07H
F8H
8.
FFH
00H
8
7FH
80H
灭
00H
FFH
在试验板上我用的是HCF4056或CD4056,这种LED驱动器好处就是能直接将数据显示出来,比如:
我们要讲“6”显示出来,我们可将ABCD=6,再开通片璇即可,缺点是不能显示BCDEF,列表如下:
显示字符
ABCD
显示字符
ABCD
0
0
9
9
1
1
L
A
2
2
H
B
3
3
P
C
4
4
A
D
5
5
-
E
6
6
灭
F
7
7
8
8
下面我们来学习段码显示器的显示程序的编写方法.
4.1.1点亮一个段码显示器
试验板的原理图中,我们下面讲的是直接I/O口操作,显示数据从P2口输出,让第一个显示器显示“6”,程序编写如下:
ORG0000H
LJMPSTART
ORG0050H
START:
MOV50H,#6
MOVA,#0FFH;关显示
MOVP2,A
START1:
MOVA,#0FH;清第1个显示器
ADDA,#10H
MOVP2,A
MOVA,50H;显示当前按键值
ADDA,#10H
MOVP2,A
LJMPSTART1
DELAY:
PUSH07H
PUSH06H
MOVR7,#08H
DELAY1:
MOVR6,#200
DJNZR6,$
DJNZR7,DELAY1
POP06H
POP07H
RET
END
上面的程序设计只是教我们如何去点亮一个段码显示器,在实际应用中这是毫无使用价值的,下面我们来讲试验板上的4位段码显示器全部显示为“6“.
4.1.2点亮4为段码显示器,让他们显示”6666”.
注:
今后如果没有特别说明,显存地址均为:
50----57H
ORG0000H
LJMPSTART
ORG0050H
START:
MOV50H,#6
MOVA,#0FFH;请按键
MOVP2,A
START1:
MOVA,#0FH;清第1个显示器
ADDA,#10H
MOVP2,A
MOVA,50H;显示”6”
ADDA,#10H
MOVP2,A
MOVA,#0FH;清第2个显示器
ADD