数字钟[★]
1. 实验任务
(1. 开机时,显示12:00:00的时间开始计时;
(2. P0.0/AD0控制“秒”的调整,每按一次加1秒;
(3. P0.1/AD1控制“分”的调整,每按一次加1分;
(4. P0.2/AD2控制“时”的调整,每按一次加1个小时;
2. 电路原理图
图4.20.1
3. 系统板上硬件连线
(1. 把“单片机系统”区域中的P1.0-P1.7端口用8芯排线连接到“动态数码显示”区域中的A-H端口上;
(2. 把“单片机系统:区域中的P3.0-P3.7端口用8芯排线连接到“动态数码显示”区域中的S1-S8端口上;
(3. 把“单片机系统”区域中的P0.0/AD0、P0.1/AD1、P0.2/AD2端口分别用导线连接到“独立式键盘”区域中的SP3、SP2、SP1端口上;
4. 相关基本知识
(1. 动态数码显示的方法
(2. 独立式按键识别过程
(3. “时”,“分”,“秒”数据送出显示处理方法
5. 程序框图
6. 汇编源程序
SECOND EQU 30H
MINITEEQU 31H
HOUR EQU 32H
HOURKBIT P0.0
MINITEK BIT P0.1
SECONDK BIT P0.2
DISPBUF EQU 40H
DISPBIT EQU 48H
T2SCNTA EQU 49H
T2SCNTB EQU 4AH
TEMP EQU 4BH
ORG 00H
LJMP START
ORG 0BH
LJMP INT_T0
START: MOV SECOND,#00H
MOV MINITE,#00H
MOV HOUR,#12
MOV DISPBIT,#00H
MOV T2SCNTA,#00H
MOV T2SCNTB,#00H
MOV TEMP,#0FEH
LCALL DISP
MOV TMOD,#01H
MOV TH0,#(65536-2000) / 256
MOV TL0,#(65536-2000) MOD 256
SETB TR0
SETB ET0
SETB EA
WT: JB SECONDK,NK1
LCALL DELY10MS
JB SECONDK,NK1
INC SECOND
MOV A,SECOND
CJNE A,#60,NS60
MOV SECOND,#00H
NS60: LCALL DISP
JNB SECONDK,$
NK1:JB MINITEK,NK2
LCALL DELY10MS
JB MINITEK,NK2
INC MINITE
MOV A,MINITE
CJNE A,#60,NM60
MOV MINITE,#00H
NM60: LCALL DISP
JNB MINITEK,$
NK2:JB HOURK,NK3
LCALL DELY10MS
JB HOURK,NK3
INC HOUR
MOV A,HOUR
CJNE A,#24,NH24
MOV HOUR,#00H
NH24: LCALL DISP
JNB HOURK,$
NK3:LJMP WT
DELY10MS:
MOV R6,#10
D1: MOV R7,#248
DJNZ R7,$
DJNZ R6,D1
RET
DISP:
MOV A,#DISPBUF
ADD A,#8
DEC A
MOV R1,A
MOV A,HOUR
MOV B,#10
DIV AB
MOV @R1,A
DEC R1
MOV A,B
MOV @R1,A
DEC R1
MOV A,#10
MOV@R1,A
DEC R1
MOV A,MINITE
MOV B,#10
DIV AB
MOV @R1,A
DEC R1
MOV A,B
MOV @R1,A
DEC R1
MOV A,#10
MOV@R1,A
DEC R1
MOV A,SECOND
MOV B,#10
DIV AB
MOV @R1,A
DEC R1
MOV A,B
MOV @R1,A
DEC R1
RET
INT_T0:
MOV TH0,#(65536-2000) / 256
MOV TL0,#(65536-2000) MOD 256
MOV A,#DISPBUF
ADD A,DISPBIT
MOV R0,A
MOV A,@R0
MOV DPTR,#TABLE
MOVC A,@A+DPTR
MOV P1,A
MOV A,DISPBIT
MOV DPTR,#TAB
MOVC A,@A+DPTR
MOV P3,A
INC DISPBIT
MOV A,DISPBIT
CJNE A,#08H,KNA
MOV DISPBIT,#00H
KNA:INC T2SCNTA
MOV A,T2SCNTA
CJNE A,#100,DONE
MOV T2SCNTA,#00H
INC T2SCNTB
MOV A,T2SCNTB
CJNE A,#05H,DONE
MOV T2SCNTB,#00H
INC SECOND
MOV A,SECOND
CJNE A,#60,NEXT
MOV SECOND,#00H
INC MINITE
MOV A,MINITE
CJNE A,#60,NEXT
MOV MINITE,#00H
INC HOUR
MOV A,HOUR
CJNE A,#24,NEXT
MOV HOUR,#00H
NEXT: LCALL DISP
DONE: RETI
TABLE: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH,40H
TAB:DB 0FEH,0FDH,0FBH,0F7H,0EFH,0DFH,0BFH,07FH
END
7. C语言源程序
#include <AT89X51.H>
unsigned char code dispcode[]={0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71,0x00}
unsigned char dispbitcode[]={0xfe,0xfd,0xfb,0xf7,
0xef,0xdf,0xbf,0x7f}
unsigned char dispbuf[8]={0,0,16,0,0,16,0,0}
unsigned char dispbitcnt
unsigned char second
unsigned char minite
unsigned char hour
unsigned int tcnt
unsigned char mstcnt
unsigned char i,j
void main(void)
{
TMOD=0x02
TH0=0x06
TL0=0x06
TR0=1
ET0=1
EA=1
while(1)
{
if(P0_0==0)
{
for(i=5i>0i--)
for(j=248j>0j--)
if(P0_0==0)
{
second++
if(second==60)
{
second=0
}
dispbuf[0]=second%10
dispbuf[1]=second/10
while(P0_0==0)
}
}
if(P0_1==0)
{
for(i=5i>0i--)
for(j=248j>0j--)
if(P0_1==0)
{
minite++
if(minite==60)
{
minite=0
}
dispbuf[3]=minite%10
dispbuf[4]=minite/10
while(P0_1==0)
}
}
if(P0_2==0)
{
for(i=5i>0i--)
for(j=248j>0j--)
if(P0_2==0)
{
hour++
if(hour==24)
{
hour=0
}
dispbuf[6]=hour%10
dispbuf[7]=hour/10
while(P0_2==0)
}
}
}
}
void t0(void) interrupt 1 using 0
{
mstcnt++
if(mstcnt==8)
{
mstcnt=0
P1=dispcode[dispbuf[dispbitcnt]]
P3=dispbitcode[dispbitcnt]
dispbitcnt++
if(dispbitcnt==8)
{
dispbitcnt=0
}
}
tcnt++
if(tcnt==4000)
{
tcnt=0
second++
if(second==60)
{
second=0
minite++
if(minite==60)
{
minite=0
hour++
if(hour==24)
{
hour=0
}
}
}
dispbuf[0]=second%10
dispbuf[1]=second/10
dispbuf[3]=minite%10
dispbuf[4]=minite/10
dispbuf[6]=hour%10
dispbuf[7]=hour/10
}
}
基于DS1302的日历时钟#include<reg51.h>//包含单片机寄存器的头文件
#include<intrins.h> //包含_nop_()函数定义的头文件
/***********************************************************************
以下是DS1302芯片的 *** 作程序
************************************************************************/
unsigned char code digit[10]={"0123456789"} //定义字符数组显示数字
sbit DATA=P1^1 //位定义1302芯片的接口,数据输出端定义在P1.1引脚
sbit RST=P1^2 //位定义1302芯片的接口,复位端口定义在P1.1引脚
sbit SCLK=P1^0 //位定义1302芯片的接口,时钟输出端口定义在P1.1引脚
/*****************************************************
函数功能:延时若干微秒
入口参数:n
***************************************************/
void delaynus(unsigned char n)
{
unsigned char i
for(i=0i<ni++)
}
/*****************************************************
函数功能:向1302写一个字节数据
入口参数:x
***************************************************/
void Write1302(unsigned char dat)
{
unsigned char i
SCLK=0 //拉低SCLK,为脉冲上升沿写入数据做好准备
delaynus(2) //稍微等待,使硬件做好准备
for(i=0i<8i++) //连续写8个二进制位数据
{
DATA=dat&0x01 //取出dat的第0位数据写入1302
delaynus(2) //稍微等待,使硬件做好准备
SCLK=1 //上升沿写入数据
delaynus(2) //稍微等待,使硬件做好准备
SCLK=0 //重新拉低SCLK,形成脉冲
dat>>=1 //将dat的各数据位右移1位,准备写入下一个数据位
}
}
/*****************************************************
函数功能:根据命令字,向1302写一个字节数据
入口参数:Cmd,储存命令字;dat,储存待写的数据
***************************************************/
void WriteSet1302(unsigned char Cmd,unsigned char dat)
{
RST=0 //禁止数据传递
SCLK=0 //确保写数居前SCLK被拉低
RST=1 //启动数据传输
delaynus(2)//稍微等待,使硬件做好准备
Write1302(Cmd) //写入命令字
Write1302(dat) //写数据
SCLK=1 //将时钟电平置于已知状态
RST=0 //禁止数据传递
}
/*****************************************************
函数功能:从1302读一个字节数据
入口参数:x
***************************************************/
unsigned char Read1302(void)
{
unsigned char i,dat
delaynus(2) //稍微等待,使硬件做好准备
for(i=0i<8i++) //连续读8个二进制位数据
{
dat>>=1 //将dat的各数据位右移1位,因为先读出的是字节的最低位
if(DATA==1)//如果读出的数据是1
dat|=0x80 //将1取出,写在dat的最高位
SCLK=1 //将SCLK置于高电平,为下降沿读出
delaynus(2) //稍微等待
SCLK=0 //拉低SCLK,形成脉冲下降沿
delaynus(2) //稍微等待
}
return dat //将读出的数据返回
}
/*****************************************************
函数功能:根据命令字,从1302读取一个字节数据
入口参数:Cmd
***************************************************/
unsigned char ReadSet1302(unsigned char Cmd)
{
unsigned char dat
RST=0//拉低RST
SCLK=0 //确保写数居前SCLK被拉低
RST=1//启动数据传输
Write1302(Cmd) //写入命令字
dat=Read1302() //读出数据
SCLK=1 //将时钟电平置于已知状态
RST=0 //禁止数据传递
return dat //将读出的数据返回
}
/*****************************************************
函数功能: 1302进行初始化设置
***************************************************/
void Init_DS1302(void)
{
WriteSet1302(0x8E,0x00)//根据写状态寄存器命令字,写入不保护指令
WriteSet1302(0x80,((0/10)<<4|(0%10))) //根据写秒寄存器命令字,写入秒的初始值
WriteSet1302(0x82,((0/10)<<4|(0%10))) //根据写分寄存器命令字,写入分的初始值
WriteSet1302(0x84,((12/10)<<4|(12%10)))//根据写小时寄存器命令字,写入小时的初始值
WriteSet1302(0x86,((16/10)<<4|(16%10)))//根据写日寄存器命令字,写入日的初始值
WriteSet1302(0x88,((11/10)<<4|(11%10)))//根据写月寄存器命令字,写入月的初始值
WriteSet1302(0x8c,((8/10)<<4|(8%10))) //根据写小时寄存器命令字,写入小时的初始值
}
/*******************************************************************************
以下是对液晶模块的 *** 作程序
*******************************************************************************/
sbit RS=P2^0 //寄存器选择位,将RS位定义为P2.0引脚
sbit RW=P2^1 //读写选择位,将RW位定义为P2.1引脚
sbit E=P2^2 //使能信号位,将E位定义为P2.2引脚
sbit BF=P0^7 //忙碌标志位,,将BF位定义为P0.7引脚
/*****************************************************
函数功能:延时1ms
(3j+2)*i=(3×33+2)×10=1010(微秒),可以认为是1毫秒
***************************************************/
void delay1ms()
{
unsigned char i,j
for(i=0i<10i++)
for(j=0j<33j++)
}
/*****************************************************
函数功能:延时若干毫秒
入口参数:n
***************************************************/
void delaynms(unsigned char n)
{
unsigned char i
for(i=0i<ni++)
delay1ms()
}
/*****************************************************
函数功能:判断液晶模块的忙碌状态
返回值:result。result=1,忙碌result=0,不忙
***************************************************/
bit BusyTest(void)
{
bit result
RS=0 //根据规定,RS为低电平,RW为高电平时,可以读状态
RW=1
E=1 //E=1,才允许读写
_nop_() //空 *** 作
_nop_()
_nop_()
_nop_() //空 *** 作四个机器周期,给硬件反应时间
result=BF //将忙碌标志电平赋给result
E=0//将E恢复低电平
return result
}
/*****************************************************
函数功能:将模式设置指令或显示地址写入液晶模块
入口参数:dictate
***************************************************/
void WriteInstruction (unsigned char dictate)
{
while(BusyTest()==1) //如果忙就等待
RS=0 //根据规定,RS和R/W同时为低电平时,可以写入指令
RW=0
E=0 //E置低电平(根据表8-6,写指令时,E为高脉冲,
// 就是让E从0到1发生正跳变,所以应先置"0"
_nop_()
_nop_() //空 *** 作两个机器周期,给硬件反应时间
P0=dictate //将数据送入P0口,即写入指令或地址
_nop_()
_nop_()
_nop_()
_nop_() //空 *** 作四个机器周期,给硬件反应时间
E=1 //E置高电平
_nop_()
_nop_()
_nop_()
_nop_() //空 *** 作四个机器周期,给硬件反应时间
E=0 //当E由高电平跳变成低电平时,液晶模块开始执行命令
}
/*****************************************************
函数功能:指定字符显示的实际地址
入口参数:x
***************************************************/
void WriteAddress(unsigned char x)
{
WriteInstruction(x|0x80)//显示位置的确定方法规定为"80H+地址码x"
}
/*****************************************************
函数功能:将数据(字符的标准ASCII码)写入液晶模块
入口参数:y(为字符常量)
***************************************************/
void WriteData(unsigned char y)
{
while(BusyTest()==1)
RS=1 //RS为高电平,RW为低电平时,可以写入数据
RW=0
E=0 //E置低电平(根据表8-6,写指令时,E为高脉冲,
// 就是让E从0到1发生正跳变,所以应先置"0"
P0=y //将数据送入P0口,即将数据写入液晶模块
_nop_()
_nop_()
_nop_()
_nop_() //空 *** 作四个机器周期,给硬件反应时间
E=1 //E置高电平
_nop_()
_nop_()
_nop_()
_nop_() //空 *** 作四个机器周期,给硬件反应时间
E=0 //当E由高电平跳变成低电平时,液晶模块开始执行命令
}
/*****************************************************
函数功能:对LCD的显示模式进行初始化设置
***************************************************/
void LcdInitiate(void)
{
delaynms(15) //延时15ms,首次写指令时应给LCD一段较长的反应时间
WriteInstruction(0x38)//显示模式设置:16×2显示,5×7点阵,8位数据接口
delaynms(5) //延时5ms ,给硬件一点反应时间
WriteInstruction(0x38)
delaynms(5) //延时5ms ,给硬件一点反应时间
WriteInstruction(0x38)//连续三次,确保初始化成功
delaynms(5) //延时5ms ,给硬件一点反应时间
WriteInstruction(0x0c)//显示模式设置:显示开,无光标,光标不闪烁
delaynms(5) //延时5ms ,给硬件一点反应时间
WriteInstruction(0x06)//显示模式设置:光标右移,字符不移
delaynms(5) //延时5ms ,给硬件一点反应时间
WriteInstruction(0x01)//清屏幕指令,将以前的显示内容清除
delaynms(5)//延时5ms ,给硬件一点反应时间
}
/**************************************************************
以下是1302数据的显示程序
**************************************************************/
/*****************************************************
函数功能:显示秒
入口参数:x
***************************************************/
void DisplaySecond(unsigned char x)
{
unsigned char i,j//j,k,l分别储存温度的百位、十位和个位
i=x/10//取十位
j=x%10//取个位
WriteAddress(0x49) //写显示地址,将在第2行第7列开始显示
WriteData(digit[i]) //将百位数字的字符常量写入LCD
WriteData(digit[j]) //将十位数字的字符常量写入LCD
delaynms(50)//延时1ms给硬件一点反应时间
}
/*****************************************************
函数功能:显示分钟
入口参数:x
***************************************************/
void DisplayMinute(unsigned char x)
{
unsigned char i,j//j,k,l分别储存温度的百位、十位和个位
i=x/10//取十位
j=x%10//取个位
WriteAddress(0x46) //写显示地址,将在第2行第7列开始显示
WriteData(digit[i]) //将百位数字的字符常量写入LCD
WriteData(digit[j]) //将十位数字的字符常量写入LCD
delaynms(50)//延时1ms给硬件一点反应时间
}
/*****************************************************
函数功能:显示小时
入口参数:x
***************************************************/
void DisplayHour(unsigned char x)
{
unsigned char i,j//j,k,l分别储存温度的百位、十位和个位
i=x/10//取十位
j=x%10//取个位
WriteAddress(0x43) //写显示地址,将在第2行第7列开始显示
WriteData(digit[i]) //将百位数字的字符常量写入LCD
WriteData(digit[j]) //将十位数字的字符常量写入LCD
delaynms(50)//延时1ms给硬件一点反应时间
}
/*****************************************************
函数功能:显示日
入口参数:x
***************************************************/
void DisplayDay(unsigned char x)
{
unsigned char i,j//j,k,l分别储存温度的百位、十位和个位
i=x/10//取十位
j=x%10//取个位
WriteAddress(0x0c) //写显示地址,将在第2行第7列开始显示
WriteData(digit[i]) //将百位数字的字符常量写入LCD
WriteData(digit[j]) //将十位数字的字符常量写入LCD
delaynms(50)//延时1ms给硬件一点反应时间
}
/*****************************************************
函数功能:显示月
入口参数:x
***************************************************/
void DisplayMonth(unsigned char x)
{
unsigned char i,j//j,k,l分别储存温度的百位、十位和个位
i=x/10//取十位
j=x%10//取个位
WriteAddress(0x09) //写显示地址,将在第2行第7列开始显示
WriteData(digit[i]) //将百位数字的字符常量写入LCD
WriteData(digit[j]) //将十位数字的字符常量写入LCD
delaynms(50)//延时1ms给硬件一点反应时间
}
/*****************************************************
函数功能:显示年
入口参数:x
***************************************************/
void DisplayYear(unsigned char x)
{
unsigned char i,j//j,k,l分别储存温度的百位、十位和个位
i=x/10//取十位
j=x%10//取个位
WriteAddress(0x06) //写显示地址,将在第2行第7列开始显示
WriteData(digit[i]) //将百位数字的字符常量写入LCD
WriteData(digit[j]) //将十位数字的字符常量写入LCD
delaynms(50)//延时1ms给硬件一点反应时间
}
/*****************************************************
函数功能:主函数
***************************************************/
void main(void)
{
unsigned char second,minute,hour,day,month,year //分别储存苗、分、小时,日,月,年
unsigned char ReadValue //储存从1302读取的数据
LcdInitiate()//将液晶初始化
WriteAddress(0x01) //写Date的显示地址,将在第1行第2列开始显示
WriteData('D') //将字符常量写入LCD
WriteData('a') //将字符常量写入LCD
WriteData('t') //将字符常量写入LCD
WriteData('e') //将字符常量写入LCD
WriteData(':') //将字符常量写入LCD
WriteAddress(0x08) //写年月分隔符的显示地址, 显示在第1行第9列
WriteData('-') //将字符常量写入LCD
WriteAddress(0x0b) //写月日分隔符的显示地址, 显示在第1行第12列
WriteData('-') //将字符常量写入LCD
WriteAddress(0x45) //写小时与分钟分隔符的显示地址, 显示在第2行第6列
WriteData(':') //将字符常量写入LCD
WriteAddress(0x48) //写分钟与秒分隔符的显示地址, 显示在第2行第9列
WriteData(':') //将字符常量写入LCD
Init_DS1302() //将1302初始化
while(1)
{
ReadValue = ReadSet1302(0x81) //从秒寄存器读数据
second=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F)//将读出数据转化
DisplaySecond(second) //显示秒
ReadValue = ReadSet1302(0x83) //从分寄存器读
minute=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F)//将读出数据转化
DisplayMinute(minute) //显示分
ReadValue = ReadSet1302(0x85) //从分寄存器读
hour=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F)//将读出数据转化
DisplayHour(hour) //显示小时
ReadValue = ReadSet1302(0x87) //从分寄存器读
day=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F)//将读出数据转化
DisplayDay(day) //显示日
ReadValue = ReadSet1302(0x89) //从分寄存器读
month=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F)//将读出数据转化
DisplayMonth(month) //显示月
ReadValue = ReadSet1302(0x8d) //从分寄存器读
year=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F)//将读出数据转化
DisplayYear(year) //显示年
}
}
改改 就是你的了
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)