【系列专栏】:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来!
《QT开发实战》 《嵌入式通用开发实战》 《从0到1学习嵌入式Linux开发》 《Android开发实战》 《实用硬件方案设计》
长期持续带来更多案例与技术文章分享;
欢迎商业项目咨询,10年 软硬全栈内功,助力解决您的尊贵需求。
——————————————————————————————————
目录
0 引言
1 功能需求
2 方案设计
2.1 硬件设计
2.1.1 STC12C5A60S2单片机最小系统
2.1.2 步进电机
2.1.3 按键/显示接口电路
2.1.4 传感器
2.2 软件设计
2.2.1 主程序软件设计
2.2.2 步进电机子程序设计
2.2.3 显示子程序设计
2.2.4 按键子程序设计
2.2.5 定时子程序设计
3 总结
附1:作品实拍
附2:汇编语言代码
0 引言
整理资料时,发现了学生时代一些好的电子设计,这些设计在当时也是比较不错的,到现在看来已经年代久远了,不过放在今天自己DIY下给小朋友,培养兴趣爱好,也是很好的。
今天分享的是基于STC单片机设计的自动窗帘控制升降系统,根据其不同控制模式,实现半自动控制、自动控制、定时控制和无线控制的相互转换控制窗帘机。该设计由光电开关电路、按键控制模块、无线遥控模块等组成,可实现窗帘自动升降。这个小电子设计当时得了校电子设计大赛一等奖,翻看了下,连编程语言都是用汇编写的,现在想想,那时候真是初生牛犊不怕虎啊。
1 功能需求
自动窗帘主要有以下几大功能:
- 1)手动控制:电动窗帘具有手动正转、手动反转 和手动停止的功能;
- 2)工作状态指示:电机工作在正转、反转和停止状态的时候,数码管均有不同工作状态指示;
- 3)半自动控制:在需要关闭或打开窗帘的时候,只需要人工按一下“正转”或“反转”按键后,窗帘到位自动停止;
- 4)无线控制:通过远程遥控器,控制窗帘的正转、反转 和停止;
- 5)环境亮度控制:通过自动感知环境亮度完成窗帘的开启或关闭操作控制,“天黑关闭,天亮打开”具有智能管理,不产生误动作;
- 6)定时控制:根据设置输入的开启或关闭时间,来控制窗帘的关闭和打开。
2 方案设计
基于以上需求,使用基于单片机的方案实现,单片机应用系统由硬件和软件组成。硬件由单片机,输入/出设备以及各种实现单片机系统控制要求的接口电路和有关的外围电路芯片或部件组成;软件由单片机应用系统实现其特定控制功能的各种工作程序和管理程序组成。
本自动窗帘控制升降系统采用STC12C5A60S2单片机作为主控器,窗帘的正转、反转和停止功能可由单片机输出电平来控制步进电机的运转实现;环境亮度的控制通过光敏电阻和运放组成的电路来控制单片机输出电平继而控制电机的正转和反转;时间自动控制可以由定时器来控制;无线控制由无线收发模块完成。由光电传感器来探测外界的光强,后的信号由单片机控制器,来实现电机的运行与停止。显示部件用来显示电动窗帘控制器的各种状态信息。键盘是主要的输入设备,控制单片机的各种参量。
窗帘控制器可以使用无线遥控器进行远程手动开、手动关和手动停控制;可以执行事先输入的开启时间和关闭时间进行时间控制。三种工作方式可以方便地进行选择,当选择时间控制的方案时,数码管还能显示当时小时和分钟时间。
2.1 硬件设计
当时是使用Protues仿真软件设计的电路图,该软件可以进行仿真,验证设计的功能是否正确。系统总体的硬件接线图如下:
总体硬件电路图包括单片机外围电路、检测电路、键盘/显示接口电路、步进电机控制电路等模块。单片机外围电路提供各模块所需的5V电源和时钟模块;单片机的P2口控制步进电机的运行从而控制窗帘的升降。显示和键盘让人机交换变得更容易。以上各模块组成了电动窗帘控制器的总体电路。因当时proteus器件库中不含STC12C5A60S2,仿真电路中单片机芯片采用AT89C51。
2.1.1 STC12C5A60S2单片机最小系统
(1)STC12C5A60S2单片机
89C51是一种带4K字节闪烁可编程可擦除只读存储器(FPEROM—Falsh Programmable and Erasable Read Only Memory)的低电压,高性能CMOS8位微处理器,俗称单片机。STC12C5A60S2/AD/PWM系列单片机是宏晶科技生产的单时钟/机器周期(1T)的单片机,是高速/低功耗/超强抗干扰的新一代8051单片机,指令代码完全兼容传统8051,但速度快8-12倍。内部集成MAX810专用复位电路,2路PWM,8路高速10位A/D转换(250K/S),针对电机控制,强干扰场合。STC12C5A60S2单片机为很多嵌入式控制系统提供了一种灵活性高且价廉的方案。
(2)晶振电路
电路中的晶振即石英晶体震荡器。由于石英晶体震荡器具有非常好的频率稳定性和抗外界干扰的能力,所以,石英晶体震荡器是用来产生基准频率的。通过基准频率来控制电路中的频率的准确性。同时,它还可以产生振荡电流,向单片机发出时钟信号。
下图是单片机的晶振电路。电路中的电容C1和C2的典型值通常选择为30PF左右,该电容的大小会影响振荡电路频率的高低、振荡器的稳定性和起振的快速性。晶体振荡频率的范围通常在1.2~12MHz。晶体的频率越高,系统的时钟频率越快,单片机的运行速度越快。但反过来,运行速度对于存储器的速度要求就越高,对印刷电路板的工艺要求也就越高,即要求线间的寄生电容要小。晶体和电容应该尽可能安装得与单片机芯片靠近,以减少寄生电容,更好地保证振荡器稳定、可靠地工作。
(3)复位电路
复位是单片机的初始化操作,只需要给STC12C5A60S2的复位引脚RST加上大于2个机器周期(即24个时钟振荡周期)的高电平就可以使STC12C5A60S2复位。复位时,单片机初始化为0000H,从0000H单元开始执行程序。除了进入系统的正常初始化之外,当程序运行错误(如程序跑飞)或操作错误使系统处于锁死状态时,也需要复位键使RST脚为高电平,使STC12C5A60S2摆脱“跑飞”或“死锁”状态而重新启动。
2.1.2 步进电机
步进电机是一种将电脉冲转化为角位移的执行机构。通俗一点讲:当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度(及步进角)。可以通过控制脉冲个数来控制角位移量,从而达到准确定位的目的;同时也可以通过控制脉冲频率来控制电机转动的速度和加速度,从而达到调速的目的。 步进电机28BYJ48型四相八拍电机,电压为DC5V—DC12V。当对步进电机施加一系列连续不断的控制脉冲时,它可以连续不断地转动。每一个脉冲信号对应步进电机的某一相或两相绕组的通电状态改变一次,也就对应转子转过一定的角度(一个步距角)。当通电状态的改变完成一个循环时,转子转过一个齿距。四相步进电机可以在不同的通电方式下运行,常见的通电方式有单(单相绕组通电)四拍(A-B-C-D-A。。。),双(双相绕组通电)四拍(AB-BC-CD-DA-AB-。。。),八拍(A-AB-B-BC-C-CD-D-DA-A。。。)
步进电机控制系统的方框图如下:
选用的步进电机接线方式:
红线接电源5V,其他四根线接单片机IO口,由于单片机接口驱动能力不足,需要使用ULN2003进行电机驱动:
2.1.3 按键/显示接口电路
按键在单片机应用系统中能实现向单片机输入数据,传送命令等功能,是人工干预单片机的主要手段。本设计中的功能按键有:当前时间设定键(调整时间 分);闹铃时间设定键(调整时间时);自动窗帘开和关的定时控制键;确定键,时间设定完成后确定输入;复位键,在程序出错或者有误操作的时候;正转键,使步进电机正转,窗帘打开;反转键,使步进电机反转,窗帘关闭;停止键,步进电机停止运转。
按键的触点在闭合和断开时均会产生抖动,这时触点的逻辑电平是不稳定的,如果不妥善处理,将会引起按键命令的错误执行或重复执行。一般消除抖动采用软件方法来解决,将在软件部分介绍这点。
显示部分则主要显示时间,用于设置时间。采用LED数码管进行显示是一种经济实用的方法。每位数码管由7个笔画加上小数点共8个发光二极管组成;有共阴极和共阳极两种类型,公共端用来进行位控制,笔画端用来进行字符控制;数码管显示有静态显示和动态显示两种方法。
在数码管显示中,有2个技术问题需要解决,这就是整数高位和闪烁显示问题。虽然某些新型LED驱动芯片本身具有闪烁控制和熄灭控制功能,但通过合理的软件设计,采用廉价芯片组成的驱动电路同样可以实现整数高位灭零和闪烁显示功能,达到降低系统硬件成本的目的。本设计采用的就是4位LED数码管的串行驱动电路来达到显示时间和消除闪烁显示的目的。驱动器采用74LS245,显示电路图如下:
2.1.4 传感器
电动窗帘要根据光照的情况而自动开关窗帘,因而需要使用到光电传感器。这里使用光敏电阻,光敏电阻是用光电导体制成的光电器件,又称光导管,他是基于半导体光电效应工作的。光敏电阻没有极性,纯粹是一个电阻器件,使用时可以加直流偏压,也可以加交流电压。当无光照时,光敏电阻值(暗电阻)很大,电路中电流很小。当光敏电阻受到一定波长范围的光照时,它的阻值急剧减少,因此电路中电流迅速增加。光敏电阻具有很高的灵敏度,很好的光谱特性,光谱响应从紫外区一直到红外区。而且体积小、重量轻、性能稳定。因此在自动化技术中得到广泛的应用。光敏电阻器一般用于光的测量、光的控制和光电转换(将光的变化转换为电的变化)。通常,光敏电阻器都制成薄片结构,以便吸收更多的光能。当它受到光的照射时,半导体片(光敏层)内就激发出电子—空穴对,参与导电,使电路中电流增强。
根据光敏电阻的光谱特性,可分为三种光敏电阻器:
- 1)紫外光敏电阻器:对紫外线较灵敏,包括硫化镉、硒化镉光敏电阻器等,用于探测紫外线。
- 2)红外光敏电阻器:主要有硫化铅、碲化铅、硒化铅。锑化铟等光敏电阻器,广泛用于导弹制导、天文探测、非接触测量、人体病变探测、红外光谱,红外通信等国防、科学研究和工农业生产中。
- 3)可见光光敏电阻器:包括硒、硫化镉、硒化镉、碲化镉、砷化镓、硅、锗、硫化锌光敏电阻器等。主要用于各种光电控制系统,如光电自动开关门户,航标灯、路灯和其他照明系统的自动亮灭,自动给水和自动停水装置,机械上的自动保护装置和“位置检测器”,极薄零件的厚度检测器,照相机自动曝光装置,光电计数器,烟雾报警器,光电跟踪系统等方面。这里选用可见光光敏电阻器。
应用光控原理工作,天亮窗帘自动打开,天黑窗帘自动关闭。上图中使用LM358做的比较电路,同相输入端有两个电阻分压得到一个电压值,作为基准电压进行比较,而反相输入端用一个光敏电阻对外部环境的光线进行采集,利用光敏电阻暗时电阻大,亮时电阻小的特点,来确定反向输入端的电压值。再两者进行比较,比较后的信号再送入单片机89C2051的IO口,输出高/低电平,从而通过单片机来控制电机的正反转。来实现天亮窗帘自动打开,天黑窗帘自动关闭这一自动控制功能。另外反相输入端可以通过可变电阻改变分压值,进而调节感光的灵敏度。
2.2 软件设计
系统软件设计主要包括显示子程序,键盘子程序,时钟程序,步进电机控制程序设计及部分构成。本章节系统的介绍了电动窗帘的主程序和各主要功能子程序的设计流程,具体的程序代码见附录。
2.2.1 主程序软件设计
主程序大循环,主要完成单片机初始化,关中断,菜单显示内容初始化,按键扫描,电机运行,计时等功能。
启动主程序,先关中断并且设置堆栈,接着初始化寄存器,初始化显示内容;然后执行按键查询,执行相应的操作。如果是设定键,则设定时间,开始计时;到时间后步进电机开始相应的工作,工作完成后停机。如果是电机控制键,则也执行相应的工作。如果都不是,则是复位键,采取复位操作。
2.2.2 步进电机子程序设计
步进电机程序设计的主要任务有三点:
- 1)判断旋转方向;
- 2)按顺序传送控制脉冲;
- 3)判断所要求的控制步数是否传送完毕。
2.2.3 显示子程序设计
显示程序开始后,起始地址60H发送到R0,01H发送至显示位代码R2,再将位代码发送到单片机A口,单片机取显示数据查表转换成显示代码发送至单片机B口,延时2ms,指针R0加1,然后判断6显示是否完成。如果完成则返回,没完成则位代码R2左移一位,继续显示查表,一直到6位显示完成后返回。
2.2.4 按键子程序设计
按键的触点在闭合和断开时均会产生抖动,这时触点的逻辑电平是不稳定的,如不妥善处理,将会引起按键命令的错误执行或者重复执行。现在用软件延时的方法来避开抖动阶段。在判断是否有键闭合后都延时5ms,按下键后再延时12ms就可以避免键盘的抖动。然后键码分析,执行相应的模块,显示后返回。
2.2.5 定时子程序设计
定时的主要功能是在设定时间单片机能够得到中断信号,从而控制窗帘的开关。时钟发出50ms的信号给单片机,计数器计数,当计数到20,则过了1秒,秒单元数值加1,当秒单元到达60,分单元数值加1,秒单元清零。当时单元到达24,时单元清零。标志1天时间计满,把他们的值放到存储单元的指定单元。
3 总结
博主在整理此篇文章时,充满了对学生时代满满的回忆,因为这个电子设计是博主从事工程师生涯的第一个作品,当时得了校级电子设计大赛一等奖,怀念当时一起组队的小伙伴。这个作品现在看来可能很简单,但在当时刚刚入门的阶段,完成它也算是经历了千辛万苦的,清晰的记着因为一点硬件问题去电子城买器件的契机请教老师傅,去学校实验室请教高年级学长,到网上查资料,到图书馆恶补专业知识,大学时代真的是太美好了。
作于202204281410,已归档
———————————————————————————————————
本文为博主原创文章,未经博主允许不得转载!
若本文对您有帮助,轻抬您发财的小手,关注/评论/点赞/收藏,就是对我最大的支持!
祝君升职加薪,鹏程万里!
附1:作品实拍
当时都是手工焊接的,看起来有些简陋吧。现在有些器件已经丢了,运行不起来了,这么多年过去了,板子一直留在身边:)
附2:汇编语言代码
当时才大二,C语言刚刚学习,汇编语言学的较多,所以当时就勇者无畏的使用了汇编语言编程:)
代码语言:javascript复制主程序:
ALB EQU 20H.0 ;闹铃标志位
K5 EQU P0.0 ;设定P0.0以K5表示 正
K6 EQU P0.1 ;设定P0.1以K6表示 反
K7 EQU P0.2 ;设定P0.2以K7表示 停
K8 EQU P0.3 ;设定P0.3以K8表示 转换
K9 EQU P3.7 ;光敏信号
K10 EQU P3.6 ;复位调节键
K2 EQU P0.5 ;小时控制位
K1 EQU P0.4 ;分钟控制位
K3 EQU P0.6 ;时间确认按键
K4 EQU P0.7 ;电机正反辨认
SEC EQU 38H ;存放秒钟变量
MIN EQU 37H ;存放分钟变量
HOUR EQU 36H ;存放时钟变量
TCNT EQU 39H ;存放50ms计数
HH EQU 40H ;闹钟设置小时变量
MM EQU 41H ;闹钟设置分钟变量
SS EQU 42H ;闹钟设置秒钟变量
;程序开始
ORG 00H
SJMP START
ORG 0BH
LJMP INT_T0
START:
;初始化
MOV R1,#1 ;用于计电机转数
MOV R3,#0 ;用于计电机转数
MOV HOUR,#0
MOV MIN,#0
MOV SEC,#0
MOV TCNT,#0
MOV TMOD,#01H
MOV TH0,#3CH ;定时50毫秒
MOV TL0,#0B0H
MOV IE,#82H
SETB TR0
;****************************************************
;无穷循环,判断是否有控制键按下,是哪一个键按下
LOOP: ACALL CONV
ACALL TIME_PRO ;检查闹铃时间
JB K8,L12 ;开关转换光控制
JNB K5, ZM_2 ;是否按K5,是则正转
JNB K6, FM_2 ;是否按K6,是则反转
JNB K7, STOP1 ;是否按K7,是则停止
JB K10,TJ ;是否按K10,是则调节反转
JNB K10,$
LCALL F_MTJ
TJ: JB K1,M1
ACALL SET_TIME ;设置目前时间
JMP LOOP
;设置闹铃时间
M1:JB K2,M2 ;未按下K2,则继续扫描
ACALL SET_ATIME ;设置闹铃时间
SETB ALB
JMP LOOP ;跳到LOOP处执行
M2:JB K1,M3 ;未按下K1,则继续扫描
JMP LOOP ;跳到LOOP处执行
M3:JB K3,M4 ;未按下K3,则继续扫描
JMP LOOP ;跳到LOOP处执行
M4:JMP LOOP ;跳到LOOP处执行
L12: JNB K9,FM_3
JB K9,ZM_3
;***********************************************
STOP: MOV P3,#0F0H ;步进电机停止工作
JMP LOOP
;开关控制步进电机正反转程序
ZM_3:LCALL DELAY1
CJNE R3,#40, Z_M ; 检测正转是否满
JMP LOOP ;转至LOOP
FM_3:LCALL DELAY1
CJNE R3,#0, F_M ; 检测反转是否满
JMP LOOP ;转至LOOP
FM_2: LCALL DELAY1 ;按K6键消抖
JNB K6,$ ;K6是否放开
LCALL DELAY1 ;放开消抖
CJNE R3,#0, F_M ;是否反转满
JMP LOOP ;转至LOOP
ZM_2: LCALL DELAY1 ;按K5键消抖
JNB K5,$ ;K5是否放开
LCALL DELAY1 ;放开消抖
CJNE R3,#40, Z_M ; 检测正转是否满
JMP LOOP ;转至LOOP
;***********************************************
STOP1:LCALL DELAY1 ;按K7键消抖
JNB K7,$ ;K7是否放开
LCALL DELAY1 ;放开消抖
JMP STOP ;电机停止
;执行闹铃处理
TIME_OUT:
JB K4,Z
JMP F
Z: ACALL ZM_3
CLR ALB
LJMP LOOP
F:ACALL FM_3
CLR ALB
LJMP LOOP
RET
Z_M: MOV R0,#00H ;正转到TABLE取码指针初值
Z_M1: MOV P3,#00H
MOV A,R0
MOV DPTR,#TABLE1 ;存表
MOVC A,@A DPTR ;取表代码
JZ Z_M ;是否取到结束码
MOV P3,A ;输出
JNB K7,STOP1 ;若按K7键,停止转动
JNB K6,FM_2 ;若按K6键,反转动
LCALL CONV
INC R0 ;取下一码
INC R1 ;计数
CJNE R1,#255, Z_M1 ;循环
MOV R1,#00H
INC R3 ;计数
CJNE R3,#40, Z_M1 ;循环
MOV P3,#0F0H ;计数满停止工作
LJMP LOOP
RET
;反转子程序
F_M : MOV R0,#09H ;反转到TABLE取码指针初值
F_M1: MOV P3,#00H
MOV A,R0
MOV DPTR,#TABLE1 ;存表
MOVC A,@A DPTR ;取表代码
JZ F_M ;是否取到结束码
MOV P3,A ;输出
JNB K7,STOP1 ;若按K7键,停止转动
JNB K5,ZM_2 ;若按K6键,反转动
LCALL CONV
INC R0 ;取下一码
DJNZ R1,F_M1 ;循环
MOV R1,#255
DJNZ R3,F_M1 ;循环
MOV P3,#0F0H ;计数满停止工作
LJMP LOOP
RET
;***********************************************
;定时器中断服务程序,对秒,分钟和小时的计数
INT_T0: MOV TH0,#3CH ;重加载初始值
MOV TL0,#0B0H
INC TCNT ;加1
MOV A,TCNT
CJNE A,#20,RETUNE ;是否到了1s
INC SEC
MOV TCNT,#0 ;计数值清零
MOV A,SEC
CJNE A,#60,RETUNE ;是否到了1min
INC MIN
MOV SEC,#0
MOV A,MIN
CJNE A,#60,RETUNE ;是否到了1h
INC HOUR
MOV MIN,#0
MOV A,HOUR
CJNE A,#24,RETUNE ;是否到了24h
MOV HOUR,#0
MOV MIN,#0
MOV SEC,#0
MOV TCNT,#0
RETUNE: RETI
;实现显示功能的子程序
CONV:MOV DPTR ,#TABLE
MOV A,SEC ;显示秒
MOV B,#10
DIV AB
CLR P2.6
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.6
MOV A,B
CLR P2.7
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.7
CLR P2.5
MOV P1,#40H ;显示分隔符
LCALL DELAY
SETB P2.5
MOV A,MIN ;显示分钟
MOV B,#10
DIV AB
CLR P2.3
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.3
MOV A,B
CLR P2.4
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.4
CLR P2.2
MOV P1,#40H ;显示分隔符
LCALL DELAY
SETB P2.2
MOV A,HOUR ;显示小时
MOV B,#10
DIV AB
CLR P2.0
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.0
MOV A,B
CLR P2.1
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.1
RET
TABLE:
DB 3FH,06H,5BH,4FH,66H
DB 6DH,7DH,07H,7FH,6FH
;***********************************************
;实现调整时间子程序
;设置目前时间
SET_TIME:
CLR TR0 ;定时器暂时停止工作
MOV SEC,#0 ;秒钟清零
L00: ACALL CONV ;扫描显示
JNB K1,L00
L0: ACALL CONV ;扫描显示
JMP L1
L3: JB K2,L2 ;未按下K2,则继续扫描
JNB K2,$ ;按下,则等待放开
; K2小时调整
INC HOUR ;小时值加1
MOV A,HOUR
CJNE A,#24,L11 ;判断是否加到24小时
MOV HOUR,#0
L11: ACALL CONV ;转换显示数据
JMP L0 ;继续程序执行
L1: JB K1,L3 ;未按下K1,则继续扫描
JNB K1,$ ;按下,则等待放开
; K1分钟调整
INC MIN ; 分钟值加1
MOV A,MIN
CJNE A,#60,L21 ;判断是否加到60min
MOV MIN,#0
L21: ACALL CONV ;转换显示数据
JMP L0 ;继续程序执行
L2: JB K3,L0 ;未按下K3,则继续扫描
JNB K3,$ ;按下,则等待放开
SETB TR0 ;启动定时器
RET
;***********************************************
;设置闹铃时间
SET_ATIME:
N00: ACALL CONVA ; 加载闹铃时间数据
JNB K2,N00 ;按下,则等待放开
; K2小时调整
N0: ACALL CONVA ; 加载闹铃时间数据
JB K2,N1
JNB K2,$
INC HH ;小时值加1
MOV A,HH
CJNE A,#24,N11 ;判断是否加到24小时
MOV HH,#0
N11: ACALL CONVA ;加载闹铃时间数据
JMP N0 ;跳转到N0执行
N1: JB K1,N2 ; 未按下K1,则继续扫描
JNB K1,$ ;按下,则等待放开
; K1分钟调整
INC MM ; 分钟值加1
MOV A,MM
CJNE A,#60,N21 ;判断是否加到60min
MOV MM,#0
N21: ACALL CONVA ;加载闹铃时间数据
JMP N0 ;跳转到N0执行
N2: JB K3,N0 ;未按下K3,则继续扫描
JNB K3,$ ;按下,则等待放开
ACALL CONV ;加载现在时间数据
RET
;***********************************************
; 加载闹铃时间数据
CONVA:
MOV DPTR,#TABLE
MOV A,SS ;显示秒
MOV B,#10
DIV AB
CLR P2.6
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.6
MOV A,B
CLR P2.7
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.7
CLR P2.5
MOV P1,#40H ;显示分隔符
LCALL DELAY
SETB P2.5
MOV A,MM ;显示分钟
MOV B,#10
DIV AB
CLR P2.3
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.3
MOV A,B
CLR P2.4
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.4
CLR P2.2
MOV P1,#40H ;显示分隔符
LCALL DELAY
SETB P2.2
MOV A,HH ;显示小时
MOV B,#10
DIV AB
CLR P2.0
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.0
MOV A,B
CLR P2.1
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.1
RET
;***********************************************
;检查闹钟时间处理
TIME_PRO:
JB ALB ,TI3
RET
;检查闹铃是否到
TI3:
MOV A,HOUR
MOV B,HH
CJNE A,B,BX ;检查闹铃小时时间
MOV A,MIN
MOV B,MM
CJNE A,B,BX ;检查闹铃分钟时间
MOV A,SEC
MOV B,SS
CJNE A,B,BX ;检查闹铃分钟时间
ACALL TIME_OUT ;时间到,进行处理
BX: RET
DELAY: MOV R6,#10
D1: MOV R7,#50
DJNZ R7,$
DJNZ R6,D1
RET
DELAY1 :MOV R6,#20 ;延时20ms
D2 :MOV R7,#248
DJNZ R7,$
DJNZ R6,D2
RET
TABLE1: ALB EQU 20H.0 ;闹铃标志位
K5 EQU P0.0 ;设定P0.0以K5表示 正
K6 EQU P0.1 ;设定P0.1以K6表示 反
K7 EQU P0.2 ;设定P0.2以K7表示 停
K8 EQU P0.3 ;设定P0.3以K8表示 转换
K9 EQU P3.7 ;光敏信号
K10 EQU P3.6 ;复位调节键
K2 EQU P0.5 ;小时控制位
K1 EQU P0.4 ;分钟控制位
K3 EQU P0.6 ;时间确认按键
K4 EQU P0.7 ;电机正反辨认
SEC EQU 38H ;存放秒钟变量
MIN EQU 37H ;存放分钟变量
HOUR EQU 36H ;存放时钟变量
TCNT EQU 39H ;存放50ms计数
HH EQU 40H ;闹钟设置小时变量
MM EQU 41H ;闹钟设置分钟变量
SS EQU 42H ;闹钟设置秒钟变量
;程序开始
ORG 00H
SJMP START
ORG 0BH
LJMP INT_T0
START:
;初始化
MOV R1,#1 ;用于计电机转数
MOV R3,#0 ;用于计电机转数
MOV HOUR,#0
MOV MIN,#0
MOV SEC,#0
MOV TCNT,#0
MOV TMOD,#01H
MOV TH0,#3CH ;定时50毫秒
MOV TL0,#0B0H
MOV IE,#82H
SETB TR0
;****************************************************
;无穷循环,判断是否有控制键按下,是哪一个键按下
LOOP: ACALL CONV
ACALL TIME_PRO ;检查闹铃时间
JB K8,L12 ;开关转换光控制
JNB K5, ZM_2 ;是否按K5,是则正转
JNB K6, FM_2 ;是否按K6,是则反转
JNB K7, STOP1 ;是否按K7,是则停止
JB K10,TJ ;是否按K10,是则调节反转
JNB K10,$
LCALL F_MTJ
TJ: JB K1,M1
ACALL SET_TIME ;设置目前时间
JMP LOOP
;设置闹铃时间
M1:JB K2,M2 ;未按下K2,则继续扫描
ACALL SET_ATIME ;设置闹铃时间
SETB ALB
JMP LOOP ;跳到LOOP处执行
M2:JB K1,M3 ;未按下K1,则继续扫描
JMP LOOP ;跳到LOOP处执行
M3:JB K3,M4 ;未按下K3,则继续扫描
JMP LOOP ;跳到LOOP处执行
M4:JMP LOOP ;跳到LOOP处执行
L12: JNB K9,FM_3
JB K9,ZM_3
;***********************************************
STOP: MOV P3,#0F0H ;步进电机停止工作
JMP LOOP
;开关控制步进电机正反转程序
ZM_3:LCALL DELAY1
CJNE R3,#40, Z_M ; 检测正转是否满
JMP LOOP ;转至LOOP
FM_3:LCALL DELAY1
CJNE R3,#0, F_M ; 检测反转是否满
JMP LOOP ;转至LOOP
FM_2: LCALL DELAY1 ;按K6键消抖
JNB K6,$ ;K6是否放开
LCALL DELAY1 ;放开消抖
CJNE R3,#0, F_M ;是否反转满
JMP LOOP ;转至LOOP
ZM_2: LCALL DELAY1 ;按K5键消抖
JNB K5,$ ;K5是否放开
LCALL DELAY1 ;放开消抖
CJNE R3,#40, Z_M ; 检测正转是否满
JMP LOOP ;转至LOOP
;***********************************************
STOP1:LCALL DELAY1 ;按K7键消抖
JNB K7,$ ;K7是否放开
LCALL DELAY1 ;放开消抖
JMP STOP ;电机停止
;执行闹铃处理
TIME_OUT:
JB K4,Z
JMP F
Z: ACALL ZM_3
CLR ALB
LJMP LOOP
F:ACALL FM_3
CLR ALB
LJMP LOOP
RET
Z_M: MOV R0,#00H ;正转到TABLE取码指针初值
Z_M1: MOV P3,#00H
MOV A,R0
MOV DPTR,#TABLE1 ;存表
MOVC A,@A DPTR ;取表代码
JZ Z_M ;是否取到结束码
MOV P3,A ;输出
JNB K7,STOP1 ;若按K7键,停止转动
JNB K6,FM_2 ;若按K6键,反转动
LCALL CONV
INC R0 ;取下一码
INC R1 ;计数
CJNE R1,#255, Z_M1 ;循环
MOV R1,#00H
INC R3 ;计数
CJNE R3,#40, Z_M1 ;循环
MOV P3,#0F0H ;计数满停止工作
LJMP LOOP
RET
;反转子程序
F_M : MOV R0,#09H ;反转到TABLE取码指针初值
F_M1: MOV P3,#00H
MOV A,R0
MOV DPTR,#TABLE1 ;存表
MOVC A,@A DPTR ;取表代码
JZ F_M ;是否取到结束码
MOV P3,A ;输出
JNB K7,STOP1 ;若按K7键,停止转动
JNB K5,ZM_2 ;若按K6键,反转动
LCALL CONV
INC R0 ;取下一码
DJNZ R1,F_M1 ;循环
MOV R1,#255
DJNZ R3,F_M1 ;循环
MOV P3,#0F0H ;计数满停止工作
LJMP LOOP
RET
;***********************************************
;定时器中断服务程序,对秒,分钟和小时的计数
INT_T0: MOV TH0,#3CH ;重加载初始值
MOV TL0,#0B0H
INC TCNT ;加1
MOV A,TCNT
CJNE A,#20,RETUNE ;是否到了1s
INC SEC
MOV TCNT,#0 ;计数值清零
MOV A,SEC
CJNE A,#60,RETUNE ;是否到了1min
INC MIN
MOV SEC,#0
MOV A,MIN
CJNE A,#60,RETUNE ;是否到了1h
INC HOUR
MOV MIN,#0
MOV A,HOUR
CJNE A,#24,RETUNE ;是否到了24h
MOV HOUR,#0
MOV MIN,#0
MOV SEC,#0
MOV TCNT,#0
RETUNE: RETI
;实现显示功能的子程序
CONV:MOV DPTR ,#TABLE
MOV A,SEC ;显示秒
MOV B,#10
DIV AB
CLR P2.6
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.6
MOV A,B
CLR P2.7
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.7
CLR P2.5
MOV P1,#40H ;显示分隔符
LCALL DELAY
SETB P2.5
MOV A,MIN ;显示分钟
MOV B,#10
DIV AB
CLR P2.3
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.3
MOV A,B
CLR P2.4
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.4
CLR P2.2
MOV P1,#40H ;显示分隔符
LCALL DELAY
SETB P2.2
MOV A,HOUR ;显示小时
MOV B,#10
DIV AB
CLR P2.0
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.0
MOV A,B
CLR P2.1
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.1
RET
TABLE:
DB 3FH,06H,5BH,4FH,66H
DB 6DH,7DH,07H,7FH,6FH
;***********************************************
;实现调整时间子程序
;设置目前时间
SET_TIME:
CLR TR0 ;定时器暂时停止工作
MOV SEC,#0 ;秒钟清零
L00: ACALL CONV ;扫描显示
JNB K1,L00
L0: ACALL CONV ;扫描显示
JMP L1
L3: JB K2,L2 ;未按下K2,则继续扫描
JNB K2,$
LCALL DELAY ;按下,则等待放开
; K2小时调整
INC HOUR ;小时值加1
MOV A,HOUR
CJNE A,#24,L11 ;判断是否加到24小时
MOV HOUR,#0
L11: ACALL CONV ;转换显示数据
JMP L0 ;继续程序执行
L1: JB K1,L3 ;未按下K1,则继续扫描
JNB K1,$
LCALL DELAY ;按下,则等待放开
; K1分钟调整
INC MIN ; 分钟值加1
MOV A,MIN
CJNE A,#60,L21 ;判断是否加到60min
MOV MIN,#0
L21: ACALL CONV ;转换显示数据
JMP L0 ;继续程序执行
L2: JB K3,L0 ;未按下K3,则继续扫描
JNB K3,$ ;按下,则等待放开
SETB TR0 ;启动定时器
RET
;***********************************************
;设置闹铃时间
SET_ATIME:
N00: ACALL CONVA ; 加载闹铃时间数据
JNB K2,N00 ;按下,则等待放开
; K2小时调整
N0: ACALL CONVA ; 加载闹铃时间数据
JB K2,N1
JNB K2,$
LCALL DELAY
INC HH ;小时值加1
MOV A,HH
CJNE A,#24,N11 ;判断是否加到24小时
MOV HH,#0
N11: ACALL CONVA ;加载闹铃时间数据
JMP N0 ;跳转到N0执行
N1: JB K1,N2 ; 未按下K1,则继续扫描
JNB K1,$
LCALL DELAY ;按下,则等待放开
; K1分钟调整
INC MM ; 分钟值加1
MOV A,MM
CJNE A,#60,N21 ;判断是否加到60min
MOV MM,#0
N21: ACALL CONVA ;加载闹铃时间数据
JMP N0 ;跳转到N0执行
N2: JB K3,N0 ;未按下K3,则继续扫描
JNB K3,$ ;按下,则等待放开
ACALL CONV ;加载现在时间数据
RET
;***********************************************
; 加载闹铃时间数据
CONVA:
MOV DPTR,#TABLE
MOV A,SS ;显示秒
MOV B,#10
DIV AB
CLR P2.6
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.6
MOV A,B
CLR P2.7
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.7
CLR P2.5
MOV P1,#40H ;显示分隔符
LCALL DELAY
SETB P2.5
MOV A,MM ;显示分钟
MOV B,#10
DIV AB
CLR P2.3
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.3
MOV A,B
CLR P2.4
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.4
CLR P2.2
MOV P1,#40H ;显示分隔符
LCALL DELAY
SETB P2.2
MOV A,HH ;显示小时
MOV B,#10
DIV AB
CLR P2.0
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.0
MOV A,B
CLR P2.1
MOVC A,@A DPTR
MOV P1,A
LCALL DELAY
SETB P2.1
RET
;***********************************************
;检查闹钟时间处理
TIME_PRO:
JB ALB ,TI3
RET
;检查闹铃是否到
TI3:
MOV A,HOUR
MOV B,HH
CJNE A,B,BX ;检查闹铃小时时间
MOV A,MIN
MOV B,MM
CJNE A,B,BX ;检查闹铃分钟时间
MOV A,SEC
MOV B,SS
CJNE A,B,BX ;检查闹铃分钟时间
ACALL TIME_OUT ;时间到,进行处理
BX: RET
DELAY: MOV R6,#10
D1: MOV R7,#50
DJNZ R7,$
DJNZ R6,D1
RET
DELAY1 :MOV R6,#20 ;延时20ms
D2 :MOV R7,#248
DJNZ R7,$
DJNZ R6,D2
RET
TABLE1:
DB 09H,01H,03H,02H,06H,04H,0CH,08H ;正转表
DB 00 ;正转结束
DB 08H,0CH,04H,06H,02H,03H,01H,09H ;反转表
DB 00 ;反转结束
;反转子程序
F_MTJ :
MOV R1,#1 ;用于计电机转数
MOV R3,#0 ;用于计电机转数
F_MTJ1 :
MOV R0,#09H ;反转到TABLE取码指针初值
F_M1TJ:
MOV P3,#00H
MOV A,R0
MOV DPTR,#TABLE1 ;存表
MOVC A,@A DPTR ;取表代码
JZ F_MTJ1 ;是否取到结束码
MOV P3,A ;输出
LCALL CONV
INC R0 ;取下一码
INC R1 ;计数
CJNE R1,#255, F_M1TJ ;循环
MOV R1,#00H
INC R3 ;计数
CJNE R3,#2, F_M1TJ ;循环
MOV P3,#0F0H ;计数满停止工作
LJMP LOOP
RET
END
DB 09H,01H,03H,02H,06H,04H,0CH,08H ;正转表
DB 00 ;正转结束
DB 08H,0CH,04H,06H,02H,03H,01H,09H ;反转表
DB 00 ;反转结束
;反转子程序
F_MTJ :
MOV R1,#1 ;用于计电机转数
MOV R3,#0 ;用于计电机转数
F_MTJ1 :
MOV R0,#09H ;反转到TABLE取码指针初值
F_M1TJ:
MOV P3,#00H
MOV A,R0
MOV DPTR,#TABLE1 ;存表
MOVC A,@A DPTR ;取表代码
JZ F_MTJ1 ;是否取到结束码
MOV P3,A ;输出
LCALL CONV
INC R0 ;取下一码
INC R1 ;计数
CJNE R1,#255, F_M1TJ ;循环
MOV R1,#00H
INC R3 ;计数
CJNE R3,#2, F_M1TJ ;循环
MOV P3,#0F0H ;计数满停止工作
LJMP LOOP
RET
END