• 从零开始--单片机十字路口交通灯控制实验


    1. 准备工作(理论知识的学习+源代码编辑软件keil+仿真软件proteus)

    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. 各部分代码的实现

    1. 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	;交通灯开始工作开关(不停查询开关状态)   
      
    2. 状态判断与状态转移:

       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
      
    3. 四个点亮灯并延时/闪烁的子程序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进行状态转移
      
    4. 5s延时子程序DELAY5

       DELAY5:  ;延时5秒程序
       MOV R0,#05H     ;秒数计数初值
       ACALL T0ORIGIN   ;定时器T0初始化
       LOOP5:
       	CJNE R0,#0H,LOOP5  ;不断查询R0值是否在中断子程序中被减为0,非零则循环
       CLR EA           ;减为0,则关中断
       CLR ET0
       RET
      
    5. 2s延时子程序DELAY2

       DELAY2:  ;延时2秒程序(同5秒延时程序)
       MOV R0,#02H     
       ACALL T0ORIGIN
       LOOP2:
       	CJNE R0,#0H,LOOP2
       CLR EA
       CLR ET0
       RET
      
    6. 闪烁子程序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
      
    7. 闪烁子程序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
      
    8. 计时器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
      
    9. 计时器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
      
    10. 计时器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
      
    11. 计时器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
      
    12. 仿真电路图搭建
      在这里插入图片描述

    5. 代码实现的难点解析

    1. 计时器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,满足条件)
    调试结果1
    当R2=#0H时,结果为R2=255,Cy=1,满足:
    调试结果2
    当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   
    

    仿真结果:
    调试结果3
    当Cy=1,R1=1时:
    在这里插入图片描述
    当Cy=0,R1=0时:
    调试结果5
    当Cy=1,R1=0时:
    调试结果6
    综上所述:我们设计的16位二进制数的自减代码完全可靠

    1. 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。

    本文为原创内容,如需转载请注明出处。

  • 相关阅读:
    解决html中刷新页面后checkbox还选中的问题
    初始化spring容器的几种方法
    在web.xml中配置spring配置文件的路径
    查找算法
    排序算法
    ORACLE TO_CHAR,TO_DATE函数格式说明
    ORACLE TO_DATE函数
    ORACLE SUBSTR函数
    ORACLE学习笔记
    Linux 查看端口占用情况
  • 原文地址:https://www.cnblogs.com/peterzhangsnail/p/12521669.html
Copyright © 2020-2023  润新知