文章目录
1. 准备工作(理论知识的学习+源代码编辑软件keil+仿真软件proteus)
- 理论知识的学习(推荐爱课程的张毅刚老师的视频)
http://www.icourses.cn/sCourse/course_5981.html- 源代码编辑软件keil的下载安装及基础教程
下载:http://www.ddooo.com/softdown/8567.htm#dltab
简易教程:http://www.51hei.com/mcuteach/189.html - 仿真软件proteus的下载安装及基础教程
下载:http://www.onlinedown.net/soft/578407.htm
安装:https://jingyan.baidu.com/article/656db918f8590de381249cbf.html
教程:http://www.elecfans.com/emb/581557.html
- 源代码编辑软件keil的下载安装及基础教程
2. 用proteus结合keil的联合电路仿真设置
(注意:ISIS是proteus里的电路仿真软件,ARES是PCB制版软件)
3. 十字路口交通灯实验题目的分析与构思
题目:假设一个十字路口为东西南北走向。开始为四个路口的红灯全部亮之后,东西路口的绿灯亮,南北路口的红灯亮,东西路口方向通车,延时一段时间后(20秒),东西路口的绿灯,闪烁若干次后(3秒),东西路口的绿灯熄灭,同时东西路口的黄灯亮,延时一段时间后(2秒),东西路口的红灯亮,南北路口的绿灯亮,南北路口方向通车,延时一段时间后(20秒),南北路口的绿灯闪烁若干次后(3秒),南北路口的绿灯熄灭,同时南北路口的黄灯亮,延时一段时间后(2秒),再切换到东西路口的绿灯亮,南北路口的红灯亮,之后重复以上过程。
画出示意图:
各个部分的实现构思:
1. 元件选择:大体看来,我们需要89C51单片机和红绿灯两种元件。
* 89C51单片机最小系统的搭建
http://www.51hei.com/bbs/dpj-86330-1.html
https://blog.csdn.net/mini92/article/details/71191718
* 红绿灯的驱动原理
https://zhidao.baidu.com/question/514751848.html
2. 初始状态:主程序main初始化时实现(设置相应引脚的电位)
3. 起始开关:可用本例中51单片机用不到的某个P口的某个引脚(本例中用P2.0)
4. 红绿灯状态:
* 用P1.0-P1.2三个引脚连接和控制东西路口红绿灯
* 用P1.3-P1.5三个引脚连接和控制南北路口红绿灯
5. 两组红绿灯状态的判断与转移:
一共四种状态,我们用PSW寄存器中留给用户的标志位PSW.1(F0)与PSW.4(F1)来标记两组红绿灯的当前状态。每次延时结束时进行循环:检查上一个状态–>跳转到下一状态–>根据已经跳转的状态选择对应的处理程序。
红绿灯状态转移关系表:
交通灯状态转移实现逻辑:
* 当F0F1=11时,F0’=0,F1’=0
* 当F0F1=其他值时,F0’=F0 U F1,F1’=F1*(F1求反)
6. 延时5s实现:用计时器T0方式2计数 + 寄存器保存的初值自减(子程序DELAY5)
7. 延时2s实现:用计时器T0方式2计数 + 寄存器保存的初值自减(子程序DELAY2)
8. 状态1到状态2的闪烁:用计时器T1计数 + 寄存器保存的初值自减 + P2.1按条件取反(子程序BLINK1)
9. 状态3到状态4的闪烁:用计时器T1计数 + 寄存器保存的初值自减 + P2.1按条件取反(子程序BLINK2)
4. 各部分代码的实现
-
main的初始化与起始开关:
F1 BIT PSW.1 ORG 0000H LJMP MAIN ORG 000BH LJMP T0P ORG 001BH LJMP T1P ORG 0100H MAIN: MOV P1,#09H ;装入初值,使两路口等同时为红 CLICK: JB P2.0,CLICK ;交通灯开始工作开关(不停查询开关状态)
-
状态判断与状态转移:
CLRFLAG: ;清零标志位F0F1 CLR F0 CLR F1 JNB F0,OneTwo ;判断F0F1并跳到指定状态的处理程序 JB F1,Four LJMP Three ;以上是第一次及当F0F1为11时的状态转移与程序选择处理 ;下面的LOOP是当F0F1为其他值时的处理 LOOP: MOV C,F0 ANL C,F1 JC CLRFLAG ;当F0F1为11时,跳到CLRFLAG清零并处理 MOV C,F0 ;当F0F1为其他情况时,各灯的处理 ORL C,F1 ;按照F0’=F0UF1、F1’=F1*进行位操作 MOV F0,C CPL F1 JNB F0,OneTwo ;判断F0F1并跳到指定状态的处理程序 JB F1,Four LJMP Three
-
四个点亮灯并延时/闪烁的子程序One、Two、Three、Four
OneTwo: JB F1,Two LJMP One One: ;状态1的处理程序 MOV P1,#0CH ;装状态1值并点亮对应灯 ACALL DELAY5 ;调用延时5S的子程序 ACALL BLINK1 ;调用闪烁3S的子程序1 LJMP LOOP ;跳回LOOP进行状态转移 Two: ;状态2的处理程序 MOV P1,#0AH ;装状态2值并点亮对应灯 ACALL DELAY2 ;调用延时2S的子程序 LJMP LOOP ;跳回LOOP进行状态转移 Three: ;状态3的处理程序 MOV P1,#21H ;装状态3值并点亮对应灯 ACALL DELAY5 ;调用延时5S的子程序 ACALL BLINK2 ;调用闪烁3S的子程序2 LJMP LOOP ;跳回LOOP进行状态转移 Four: ;状态4的处理程序 MOV P1,#11H ;装状态4值并点亮对应灯 ACALL DELAY2 ;调用延时2S的子程序 LJMP LOOP ;跳回LOOP进行状态转移
-
5s延时子程序DELAY5
DELAY5: ;延时5秒程序 MOV R0,#05H ;秒数计数初值 ACALL T0ORIGIN ;定时器T0初始化 LOOP5: CJNE R0,#0H,LOOP5 ;不断查询R0值是否在中断子程序中被减为0,非零则循环 CLR EA ;减为0,则关中断 CLR ET0 RET
-
2s延时子程序DELAY2
DELAY2: ;延时2秒程序(同5秒延时程序) MOV R0,#02H ACALL T0ORIGIN LOOP2: CJNE R0,#0H,LOOP2 CLR EA CLR ET0 RET
-
闪烁子程序BLINK1
BLINK1: ;闪烁程序1 MOV R0,#06H ;延时3秒(定时器计时0.5秒) ACALL T1ORIGIN ;T1初始化 LOOPB1: MOV C,P2.1 ;不断将P2.1位的值赋给P1.1位,引起闪烁 MOV P1.1,C CJNE R0,#0H,LOOPB1 ;不断查询中断子程序是否将R0的值减为0,非0则循环 CLR EA ;减为0,则关中断 CLR ET1 RET
-
闪烁子程序BLINK2
BLINK2: ;闪烁程序2(同闪烁程序1,但是将P2.1的值赋给P1.4) MOV R0,#06H ACALL T1ORIGIN LOOPB2: MOV C,P2.1 MOV P1.4,C CJNE R0,#0H,LOOPB2 CLR EA CLR ET1 RET
-
计时器T0初始化程序
T0ORIGIN: ;T0初始化程序 MOV TMOD,#02H ;方式2 SETB EA ;开中断 SETB ET0 MOV TH0,#9CH ;初值156D MOV TL0,#9CH MOV R1,#27H ;中断次数计数器(10000D) MOV R2,#10H SETB TR0 ;开始计时 RET
-
计时器T0中断服务子程序
T0P: ;T0中断服务子程序 CLR C MOV A,R2 ;每次中断,中断计数器减1 SUBB A,#01H MOV R2,A MOV A,R1 SUBB A,#00H MOV R1,A JC RST0 ;判断中断计数器是否减为0 RETI RST0: ;减为0,则R0自减1,中断次数计数器重装初值(10000D) DEC R0 MOV R1,#27H MOV R2,#10H RETI
-
计时器T1初始化子程序
T1ORIGIN: ;T1初始化程序 MOV TMOD,#20H ;方式2 SETB EA ;开中断 SETB ET1 MOV TH1,#9CH ;初值156D MOV TL1,#9CH MOV R3,#13H ;中断次数计数器(5000D) MOV R4,#88H SETB TR1 ;开始计时 RET
-
计时器T1中断服务子程序
T1P: ;T1中断服务子程序 CLR C MOV A,R4 ;每次中断,中断计数器减1 SUBB A,#01H MOV R4,A MOV A,R3 SUBB A,#00H MOV R3,A JC RST1 ;判断中断计数器是否减为0 RETI RST1: ;减为0,则R0自减1,中断次数计数器重装初值(5000D) DEC R0 CPL P2.1 MOV R3,#13H MOV R4,#88H RETI
-
仿真电路图搭建
5. 代码实现的难点解析
-
计时器T0与T1中断服务子程序中的带借位的16位2进制数的减法
-
计数原理:因为单片机晶振为11.0592MHZ/12MHZ,即使16位全用上,初值0值也不能计时1s钟,所以先用T0/T1方式2进行第一级的计时,再用通用寄存器组中的R1R2与R3R4,这两个16位二进制数来进行第二级的计数(就是看T0/T1中断了多少次)来完成计时1s的任务。流程图如下(计数初值均为156–计数100次晶振脉冲):
-
带借位的二进制减法SUBB是否可靠地完成了任务的测试
(以T0为例)CLR C MOV A,R2 ;每次中断,中断计数器减1 SUBB A,#01H MOV R2,A MOV A,R1 SUBB A,#00H MOV R1,A
要想我们的代码设计可靠,必须满足以下条件:
1. 若R2>=1时,对R2的SUBB结束后–>Cy=0,R2=R2-1
2. 若R2=0时,对R2的SUBB结束后–>Cy=1,R2=255(#FFH)
3. 若R1>=1时,对R1的SUBB结束后–>Cy=0,R1=R1-Cy
4. 若R1=0时,对R1的SUBB结束后–>R1=R1-Cy,Cy与R2的SUBB结束后的值保持一致
接下来我们用keil测试代码+ISIS仿真的89C51最小系统来测试上述条件是否满足:
当R2=#01H时ORG 0000H LJMP MAIN ORG 0100H MAIN: CLR C MOV R2,#01H MOV A,R2 SUBB A,#01H MOV R2,A MOV P3.0,C ;cy位的值在P3.0引脚显示 MOV P2,R2 ;R2的值在P2口显示 HERE: SJMP HERE END
-
联合proteus的调试结果:(R2=0#,C=0,满足条件)
当R2=#0H时,结果为R2=255,Cy=1,满足:
当Cy=0,R1=1时:
ORG 0000H
LJMP MAIN
ORG 0100H
MAIN:
CLR C
MOV R1,#01H
MOV A,R1
SUBB A,#00H
MOV R1,A
MOV P3.0,C ;cy位的值在P3.0引脚显示
MOV P2,R1 ;R1的值在P2口显示
HERE:
SJMP HERE
END
仿真结果:
当Cy=1,R1=1时:
当Cy=0,R1=0时:
当Cy=1,R1=0时:
综上所述:我们设计的16位二进制数的自减代码完全可靠
-
T1的中断服务子程序中的P2.1取反的理解
T1P: ;T1中断服务子程序 CLR C MOV A,R4 ;每次中断,中断计数器减1 SUBB A,#01H MOV R4,A MOV A,R3 SUBB A,#00H MOV R3,A JC RST1 ;判断中断计数器是否减为0 RETI RST1: ;减为0,则R0自减1,中断次数计数器重装初值(5000D) DEC R0 CPL P2.1 MOV R3,#13H MOV R4,#88H RETI
当16位二进制数自减为0后,用RST1代码块进行返回,其中会对P2.1取反。这是因为,T1中断服务子程序是服务于两个闪烁子程序BLINK1、BLINK2的,要闪烁(电平变化)的引脚有两个(P1.1、P1.4),而T1的中断服务程序只能有一个,所以不能在中断服务子程序内直接对这两个引脚取反。只能对某个我们用不到的引脚P2.1取反,然后在BLINK1内,将P2.1赋给P1.1;在BLINK2内,将P2.1赋给P1.4。
本文为原创内容,如需转载请注明出处。