A. 用C语言编写AT89C51单片机程序,设计一个智能数字钟。
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit QB1=P1^0;
sbit QB2=P1^1; //数码管段选
sbit QB3=P1^2;
sbit QB4=P1^3;
sbit QB5=P1^4;
sbit QB6=P1^5;
sbit fm=P1^6; //蜂鸣器
sbit s1=P2^4; //s5按键,切换显示
sbit s2=P2^3; //s2按键,设置调时
sbit s3=P2^2; //s3按键,加1
sbit s4=P2^1; //s4按键,减1
sbit led1=P0^0;
sbit led2=P0^1;
sbit led3=P0^2;
uchar count;
uchar sec,minu,hour,day,week,mon;
uchar n_sec,n_minu,n_hour;
uint year;
uchar set_2=1,set_1=1;
uchar hs,hg,mis,mig,ss,sg;
uchar nhs,nhg,nms,nmg,nss=0,nsg=0;
uchar ms,mg,ds,dg,w;
uchar code table[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,
0X90,0X88,0X83,0XC6,0XA1,0X8E,0X86,0xbf}; //0~F,-,共阳
//uchar code tableyi[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,
//0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40};//0-F,-,共阴
uchar code table_d[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,
0x87,0xff,0xef}; //0~9数组,带小数点
uchar table1[]={31,31,29,31,30,31,30,31,31,30,31,30,31}; //闰年
uchar table2[]={31,31,28,31,30,31,30,31,31,30,31,30,31}; //非闰年
void delay(uint); //延时函数
void timer0(); //走时中断函数
void jishi(); //计时函数
void key_change(); //切换显示按键函数
void key_set(); //设置时间按键函数
void disp(uchar,uchar,uchar,uchar,uchar,uchar); //显示函数
void zd_clock(); //整点报时函数
void nz_clock(); //闹钟函数
uchar incone(uchar); //加1函数
uchar decone(uchar); //减1函数
void set_time(); //设置时间函数
void set_clock(); //设置闹钟函数
void set_mdw(); //设置月日星期函数
void main() //主函数
{
EA=1;
ET0=1;
TR0=1;
TMOD=0x01;
TH0=0x4c; //50ms初值 晶振11.0592
TL0=0x00;
hour=23;minu=59;sec=49; //赋初值:11点59分0秒
n_hour=12;n_minu=56;n_sec=0; //闹钟赋初值12点1分0秒
year=2008;mon=5;day=14;week=3;//年月日星期赋初值2008年5月11日星期天;祝天下所有母亲节日快乐
while(1)
{
hs=hour/10; //时分秒HH.MM.SS
hg=hour%10;
mis=minu/10;
mig=minu%10;
ss=sec/10;
sg=sec%10;
ms=mon/10; //月日-星期MM.DD.-W
mg=mon%10;
ds=day/10;
dg=day%10;
w=week;
nhs=n_hour/10; //闹钟定时HH.MM.SS
nhg=n_hour%10;
nms=n_minu/10;
nmg=n_minu%10;
nss=n_sec/10;
nsg=n_sec%10;
key_change(); //s4按键扫描
key_set(); //s2按键扫描
set_time(); //设置时间
set_mdw(); //设置月日星期
set_clock(); //设置闹钟
if(set_1==1) //正常走时显示
{
disp(hs,hg,mis,mig,ss,sg);
}
if(set_1==2) //设置时间,LED1闪亮
{
disp(hs,hg,mis,mig,ss,sg);
if(sec%2==0)
{led2=1;led3=1;led1=~led1;}
// else
// {led1=1;}
}
if(set_1==3) //正常显示月日-星期
{
disp(ms,mg,ds,dg,16,w);
}
if(set_1==4) //设置月日-星期,LED2闪亮
{
disp(ms,mg,ds,dg,16,w);
if(sec%2==0)
{led1=1;led3=1;led2=~led2;}
// else
// {led2=1;}
}
if(set_1==5) //正常显示定时
{
disp(nhs,nhg,nms,nmg,nss,nsg);
}
if(set_1==6) //设置闹钟定时,LED3闪亮
{
disp(nhs,nhg,nms,nmg,nss,nsg);
if(sec%2==0)
{led1=1;led2=1;led3=~led3;}
// else
// {led3=1;}
}
zd_clock(); //整点报时
nz_clock(); //闹钟
}
}
void timer0() interrupt 1 //50ms中断函数
{
TMOD=0x01;
TH0=0x4c; //50ms初值 晶振11.0592
TL0=0x00;
count++;
if(count==20)
{
count=0;
sec++;
jishi(); //调计时函数
}
}
void jishi() //计时函数
{
if(sec==60)
{
sec=0;
minu++;
if(minu==60)
{
minu=0;
hour++;
if(hour==24)
{ hour=0;
day++;
week++;
if(week==8)
{week=0;}
if(year%4==0&&year%100!=0||year%400==0) //闰年
{
if(day==table1[mon]+1)
{
day=0;
mon++;
if(mon==13)
{mon=0;year++;}
}
}
else //非闰年
{
if(day==table2[mon]+1)
{
day=0;
mon++;
if(mon==13)
{mon=0;year++;}
}
}
}
}
}
}
void key_change() //s1按键扫描
{
if(s1==0)
{
delay(200);
if(s1==0)
{
set_1++;
while(!s1);
if(set_1==7)
{set_1=1;}
}
}
}
void key_set() //s2按键扫描
{
if(s2==0)
{
delay(10);
if(s2==0)
{
set_2++;
while(!s2);
if(set_2==4)
{set_2=1;}
}
}
}
void disp(uchar a1,uchar a2,uchar a3,uchar a4,uchar a5,uchar a6) //显示函数
{
QB1=1;
QB2=0;
QB3=0;
QB4=0;
QB5=0;
QB6=0;
P3=table[a1]; //段码送P0口
delay(10); //延时一小会
QB1=0;
QB2=1;
QB3=0;
QB4=0;
QB5=0;
QB6=0;
P3=table[a2]; //第2个数码管显示,带小数点
delay(10);
QB1=0;
QB2=0;
QB3=1;
QB4=0;
QB5=0;
QB6=0;
P3=table[a3]; //第3个数码管显示
delay(10);
QB1=0;
QB2=0;
QB3=0;
QB4=1;
QB5=0;
QB6=0;
P3=table[a4]; //第4个数码管显示,带小数点
delay(10);
QB1=0;
QB2=0;
QB3=0;
QB4=0;
QB5=1;
QB6=0;
//第5个数码管显示
P3=table[a5];
delay(10);
QB1=0;
QB2=0;
QB3=0;
QB4=0;
QB5=0;
QB6=1;
P3=table[a6]; //第6个数码管显示
delay(10);
QB1=0;
QB2=0;
QB3=0;
QB4=0;
QB5=0;
QB6=0;
}
void zd_clock() //整点报时函数
{
if(minu==59&&(sec==53||sec==55||sec==57))
{
fm=0;
delay(5);
fm=1;
delay(5);
}
fm=0;
if(minu==59&&sec==59)
{
fm=0;
delay(5);
fm=1;
delay(5);
fm=0;
}
}
void nz_clock() //闹钟函数
{
if(hour==n_hour&&minu==n_minu&&sec==n_sec)
//if((sec%2==0)&&sec<30)
{
fm=0;
delay(1);
fm=1;
delay(1);
}
}
void set_time() //设置时间函数
{
if(set_1==2)
{
if(set_2==1)
{
hour=incone(hour);
if(hour==24)
{hour=0;}
// if(hour<0)
// {hour=23;}
hour=decone(hour);
}
if(set_2==2)
{
minu=incone(minu);
if(minu==60)
{minu=0;}
// if(minu<0)
// {minu=59;}
minu=decone(minu);
}
}
}
void set_mdw() //设置月日星期函数
{
if(set_1==4)
{
if(set_2==1)
{
mon=incone(mon);
if(mon==13)
{mon=1;}
mon=decone(mon);
// if(mon==0)
// {mon=12;}
}
if(set_2==2)
{
day=incone(day);
if(day==32)
{day=0;}
day=decone(day);
// if(day==0)
// {day=0;}
}
if(set_2==3)
{
week=incone(week);
if(week==8)
{week=0;}
week=decone(week);
// if(week==0)
// {week=7;}
}
}
}
void set_clock() //设置闹钟函数
{
if(set_1==6)
{
if(set_2==1)
{
n_hour=incone(n_hour);
if(n_hour==24)
{n_hour=0;}
n_hour=decone(n_hour);
if(n_hour==0)
{n_hour=0;}
}
if(set_2==2)
{
n_minu=incone(n_minu);
if(n_minu==60)
{n_minu=0;}
n_minu=decone(n_minu);
if(n_minu==0)
{n_minu=0;}
}
}
}
uchar incone(uchar n) //加1函数
{
if(s3==0)
{ delay(200);
if(s3==0)
{
n++;
while(!s3);
}
}
return(n);
}
uchar decone(uchar m) //减1函数
{
if(s4==0)
{
delay(200);
if(s4==0)
{
m--;
while(!s4);
if(m<0)
{m=0;}
}
}
return(m);
}
void delay(uint k) //延时函数
{
uint i,j;
for(i=k;i>0;i--)
for(j=80;j>0;j--);
}
B. 跪求!基于单片机的数字时钟设计
#include<reg52.h>
#include<absacc.h>
#include<intrins.h>
#define unit unsigned int
#define uchar unsigned char
//#define HZ 12
sbit key0=P0^0; // 分钟调整
sbit key1=P0^1; // 小时调整
sbit P2_0=P2^7; //秒 指示灯
sbit MN_RXD=P3^6;
sbit MN_TXD=P3^7;
uchar data CLOCK[4]={0,0,0,12};//存放时钟时间(百分秒,秒,分,和时位)
//数码管显示表0-f 灭
uchar code TABLE[]={0xBE,0x06,0xEA,0x6E,0x56,0x7C,0xFC,0x0E,0xFE,0x7E,0x00};
//**********************************
//模拟串口发送一个字节数据 函数
//**********************************
void SendData(unsigned char senddata)
{
unsigned char i;
for(i=0;i<8;i++)
{
if((senddata&0x01)==0)
MN_RXD=0;
else
MN_RXD=1;
_nop_();
MN_TXD=0;
_nop_();
MN_TXD=1;
senddata=senddata>>1;
}
}
//**********************************
//显示程序函数
//**********************************
void display(void)
{
// unsigned int n;
uchar temp;
temp=CLOCK[1]; temp=temp%10; SendData(TABLE[temp]);
temp=CLOCK[1]; temp=temp/10; SendData(TABLE[temp]);
temp=CLOCK[2]; temp=temp%10; SendData(TABLE[temp]);
temp=CLOCK[2]; temp=temp/10; SendData(TABLE[temp]);
temp=CLOCK[3]; temp=temp%10; SendData(TABLE[temp]);
temp=CLOCK[3]; temp=temp/10; SendData(TABLE[temp]);
/*
for(n=0;n<5000;n++);
for(n=0;n<6;n++)
{
SendData(TABLE[10]);
}
*/
}
//**********************************
//按键控制函数
//**********************************
void keycan()
{
unsigned int n;
EA=0;
if(key0==0) // 分钟调整
{
for(n=0;n<10000;n++); //延时去抖动
while(key0==0);
CLOCK[2]=CLOCK[2]+1;
if(CLOCK[2]==60) //到一时
{
CLOCK[2]=0;
}
display();
}
if(key1==0) // 小时调整
{
for(n=0;n<10000;n++); //延时去抖动
while(key1==0);
CLOCK[3]=CLOCK[3]+1;
if(CLOCK[3]==24)
{
CLOCK[3]=0;
}
display();
}
EA=1;
}
//**********************************
//T0中断服务函数
//**********************************
void time0() interrupt 1 //using 1
{
TH0=0xD8; TL0=0xF0; //重置初值
// TH0=0xB1; TL0=0xE0;
//时钟处理
CLOCK[0]=CLOCK[0]+1;
}
//**********************************
//主函数
//**********************************
void main()
{
EA=1;
ET0=1;
TMOD=0x01; //T0方式1定时
TH0=0xD8; TL0=0xF0; //D8F0 定时10ms
// TH0=0xB1; TL0=0xE0; //定时 20ms
TR0=1;
for(;;)
{
if(CLOCK[0]==100) //到一秒 10ms*100
{
CLOCK[0]=0;
P2_0=~P2_0;
CLOCK[1]=CLOCK[1]+1;
if(CLOCK[1]==60) //到一分
{
CLOCK[1]=0;
CLOCK[2]=CLOCK[2]+1;
if(CLOCK[2]==60) //到一时
{
CLOCK[2]=0;
CLOCK[3]=CLOCK[3]+1;
if(CLOCK[3]==24)
{
CLOCK[3]=0;
}
}
}
display();
}
keycan();
}
}
C. 基于MCS-51单片机的数字时钟系统设计
51单片机的PDF 89S52典型的51结构
主要性能
l 与MCS-51单片机产品兼容
l 8K字节在系统可编程Flash存储器
l 1000次擦写周期
l 全静态操作:0Hz~33Hz
l 三级加密程序存储器
l 32个可编程I/O口线
l 三个16位定时器/计数器
l 八个中断源
l 全双工UART串行通道
l 低功耗空闲和掉电模式
l 掉电后中断可唤醒
l 看门狗定时器
l 双数据指针
l 掉电标识符
功能特性描述
AT89S52是一种低功耗、高性能CMOS8位微控制器,具有
8K 在系统可编程Flash 存储器。使用Atmel 公司高密度非
易失性存储器技术制造,与工业80C51 产品指令和引脚完
全兼容。片上Flash允许程序存储器在系统可编程,亦适于
常规编程器。在单芯片上,拥有灵巧的8 位CPU 和在系统
可编程Flash,使得AT89S52为众多嵌入式控制应用系统提
供高灵活、超有效的解决方案。
AT89S52具有以下标准功能: 8k字节Flash,256字节RAM,
32 位I/O 口线,看门狗定时器,2 个数据指针,三个16 位
定时器/计数器,一个6向量2级中断结构,全双工串行口,
片内晶振及时钟电路。另外,AT89S52 可降至0Hz 静态逻
辑操作,支持2种软件可选择节电模式。空闲模式下,CPU
停止工作,允许RAM、定时器/计数器、串口、中断继续工
作。掉电保护方式下,RAM内容被保存,振荡器被冻结,
单片机一切工作停止,直到下一个中断或硬件复位为止。
R
8 位微控制器
8K 字节在系统可编程
Flash
AT89S52
Rev. 1919-07/01
AT89S52
2
引脚结构
AT89S52
3
方框图
引脚功能描述
AT89S52
4
VCC : 电源
GND: 地
P0 口:P0口是一个8位漏极开路的双向I/O口。作为输出口,每位能驱动8个TTL逻
辑电平。对P0端口写“1”时,引脚用作高阻抗输入。
当访问外部程序和数据存储器时,P0口也被作为低8位地址/数据复用。在这种模式下,
P0具有内部上拉电阻。
在flash编程时,P0口也用来接收指令字节;在程序校验时,输出指令字节。程序校验
时,需要外部上拉电阻。
P1 口:P1 口是一个具有内部上拉电阻的8 位双向I/O 口,p1 输出缓冲器能驱动4 个
TTL 逻辑电平。对P1 端口写“1”时,内部上拉电阻把端口拉高,此时可以作为输入
口使用。作为输入使用时,被外部拉低的引脚由于内部电阻的原因,将输出电流(IIL)。
此外,P1.0和P1.2分别作定时器/计数器2的外部计数输入(P1.0/T2)和时器/计数器2
的触发输入(P1.1/T2EX),具体如下表所示。
在flash编程和校验时,P1口接收低8位地址字节。
引脚号第二功能
P1.0 T2(定时器/计数器T2的外部计数输入),时钟输出
P1.1 T2EX(定时器/计数器T2的捕捉/重载触发信号和方向控制)
P1.5 MOSI(在系统编程用)
P1.6 MISO(在系统编程用)
P1.7 SCK(在系统编程用)
P2 口:P2 口是一个具有内部上拉电阻的8 位双向I/O 口,P2 输出缓冲器能驱动4 个
TTL 逻辑电平。对P2 端口写“1”时,内部上拉电阻把端口拉高,此时可以作为输入
口使用。作为输入使用时,被外部拉低的引脚由于内部电阻的原因,将输出电流(IIL)。
在访问外部程序存储器或用16位地址读取外部数据存储器(例如执行MOVX @DPTR)
时,P2 口送出高八位地址。在这种应用中,P2 口使用很强的内部上拉发送1。在使用
8位地址(如MOVX @RI)访问外部数据存储器时,P2口输出P2锁存器的内容。
在flash编程和校验时,P2口也接收高8位地址字节和一些控制信号。
P3 口:P3 口是一个具有内部上拉电阻的8 位双向I/O 口,p2 输出缓冲器能驱动4 个
TTL 逻辑电平。对P3 端口写“1”时,内部上拉电阻把端口拉高,此时可以作为输入
口使用。作为输入使用时,被外部拉低的引脚由于内部电阻的原因,将输出电流(IIL)。
P3口亦作为AT89S52特殊功能(第二功能)使用,如下表所示。
在flash编程和校验时,P3口也接收一些控制信号。
AT89S52
5
引脚号第二功能
P3.0 RXD(串行输入)
P3.1 TXD(串行输出)
P3.2 INT0(外部中断0)
P3.3 INT0(外部中断0)
P3.4 T0(定时器0外部输入)
P3.5 T1(定时器1外部输入)
P3.6 WR(外部数据存储器写选通)
P3.7 RD(外部数据存储器写选通)
RST: 复位输入。晶振工作时,RST脚持续2 个机器周期高电平将使单片机复位。看门
狗计时完成后,RST 脚输出96 个晶振周期的高电平。特殊寄存器AUXR(地址8EH)上
的DISRTO位可以使此功能无效。DISRTO默认状态下,复位高电平有效。
ALE/PROG:地址锁存控制信号(ALE)是访问外部程序存储器时,锁存低8 位地址
的输出脉冲。在flash编程时,此引脚(PROG)也用作编程输入脉冲。
在一般情况下,ALE 以晶振六分之一的固定频率输出脉冲,可用来作为外部定时器或
时钟使用。然而,特别强调,在每次访问外部数据存储器时,ALE脉冲将会跳过。
如果需要,通过将地址为8EH的SFR的第0位置“1”,ALE操作将无效。这一位置“1”,
ALE 仅在执行MOVX 或MOVC指令时有效。否则,ALE 将被微弱拉高。这个ALE 使
能标志位(地址为8EH的SFR的第0位)的设置对微控制器处于外部执行模式下无效。
PSEN:外部程序存储器选通信号(PSEN)是外部程序存储器选通信号。
当AT89S52从外部程序存储器执行外部代码时,PSEN在每个机器周期被激活两次,而
在访问外部数据存储器时,PSEN将不被激活。
EA/VPP:访问外部程序存储器控制信号。为使能从0000H 到FFFFH的外部程序存储器
读取指令,EA必须接GND。
为了执行内部程序指令,EA应该接VCC。
在flash编程期间,EA也接收12伏VPP电压。
XTAL1:振荡器反相放大器和内部时钟发生电路的输入端。
XTAL2:振荡器反相放大器的输出端。
AT89S52
6
表1 AT89S52 特殊寄存器映象及复位值
特殊功能寄存器
特殊功能寄存器(SFR)的地址空间映象如表1所示。
并不是所有的地址都被定义了。片上没有定义的地址是不能用的。读这些地址,一般将
得到一个随机数据;写入的数据将会无效。
用户不应该给这些未定义的地址写入数据“1”。由于这些寄存器在将来可能被赋予新的
功能,复位后,这些位都为“0”。
定时器2 寄存器:寄存器T2CON 和T2MOD 包含定时器2 的控制位和状态位(如表2
和表3所示),寄存器对RCAP2H和RCAP2L是定时器2的捕捉/自动重载寄存器。
中断寄存器:各中断允许位在IE寄存器中,六个中断源的两个优先级也可在IE中设置。
AT89S52
7
表2 T2CON:定时器/计数器2控制寄存器
T2CON 地址为0C8H 复位值:0000 0000B
位可寻址
TF2 EXF2 RLCLK TCLK EXEN2 TR2
7 6 5 4 3 2 1 0
符号功能
TF2 定时器2 溢出标志位。必须软件清“0”。RCLK=1 或TCLK=1 时,TF2
不用置位。
EXF2
定时器2 外部标志位。EXEN2=1 时,T2EX 上的负跳变而出现捕捉或重
载时,EXF2 会被硬件置位。定时器2 打开,EXF2=1 时,将引导CPU
执行定时器2 中断程序。EXF2 必须如见清“0”。在向下/向上技术模式
(DCEN=1)下EXF2不能引起中断。
RCLK
串行口接收数据时钟标志位。若RCLK=1,串行口将使用定时器2 溢出
脉冲作为串行口工作模式1 和3 的串口接收时钟;RCLK=0,将使用定
时器1计数溢出作为串口接收时钟。
TCLK
串行口发送数据时钟标志位。若TCLK=1,串行口将使用定时器2 溢出
脉冲作为串行口工作模式1 和3 的串口发送时钟;TCLK=0,将使用定
时器1计数溢出作为串口发送时钟。
EXEN2
定时器2外部允许标志位。当EXEN2=1时,如果定时器2没有用作串行
时钟,T2EX(P1.1)的负跳变见引起定时器2 捕捉和重载。若EXEN2
=0,定时器2将视T2EX端的信号无效
TR2 开始/停止控制定时器2。TR2=1,定时器2开始工作
定时器2 定时/计数选择标志位。=0,定时; =1,外部事
件计数(下降沿触发)
捕捉/重载选择标志位。当EXEN2=1时, =1,T2EX出现负脉冲,
会引起捕捉操作;当定时器2溢出或EXEN2=1时T2EX出现负跳变,都
会出现自动重载操作。=0 将引起T2EX 的负脉冲。当RCKL=1
或TCKL=1时,此标志位无效,定时器2溢出时,强制做自动重载操作。
双数据指针寄存器:为了更有利于访问内部和外部数据存储器,系统提供了两路16位
数据指针寄存器:位于SFR中82H~83H的DP0和位于84H~85。特殊寄存器AUXR1
中DPS=0 选择DP0;DPS=1 选择DP1。用户应该在访问数据指针寄存器前先初始化
AT89S52
8
DPS至合理的值。
表3a AUXR:辅助寄存器
AUXR 地址:8EH 复位值:XXX00XX0B
不可位寻址
- - - WDIDLE DISRTO - - DISALE
7 6 5 4 3 2 1 0
- 预留扩展用
DISALE ALE使能标志位
DISALE 操作方式
0 ALE 以1/6晶振频率输出信号
1 ALE 只有在执行MOVX 或MOVC指令时激活
DISRTO 复位输出标志位
DISRTO
0 看门狗(WDT)定时结束,Reset 输出高电平
1 Reset 只有输入
WDIDLE 空闲模式下WDT使能标志位
WDIDLE
0 空闲模式下,WDT继续计数
1 空闲模式下,WDT停止计数
掉电标志位:掉电标志位(POF)位于特殊寄存器PCON的第四位(PCON.4)。上电期
间POF置“1”。POF可以软件控制使用与否,但不受复位影响。
表3b AUXR1:辅助寄存器1
AUXR1 地址:A2H 复位值:XXXXXXX0B
不可位寻址
- - - - - - - DPS
7 6 5 4 3 2 1 0
- 预留扩展用
DPS 数据指针选择位
DPS
0 选择DPTR寄存器DP0L和DP0H
1 选择DPTR寄存器DP1L和DP1H
AT89S52
9
存储器结构
MCS-51器件有单独的程序存储器和数据存储器。外部程序存储器和数据存储器都可以
64K寻址。
程序存储器:如果EA引脚接地,程序读取只从外部存储器开始。
对于89S52,如果EA 接VCC,程序读写先从内部存储器(地址为0000H~1FFFH)开
始,接着从外部寻址,寻址地址为:2000H~FFFFH。
数据存储器:AT89S52 有256 字节片内数据存储器。高128 字节与特殊功能寄存器重
叠。也就是说高128字节与特殊功能寄存器有相同的地址,而物理上是分开的。
当一条指令访问高于7FH 的地址时,寻址方式决定CPU 访问高128 字节RAM 还是特
殊功能寄存器空间。直接寻址方式访问特殊功能寄存器(SFR)。
例如,下面的直接寻址指令访问0A0H(P2口)存储单元
MOV 0A0H , #data
使用间接寻址方式访问高128 字节RAM。例如,下面的间接寻址方式中,R0 内容为
0A0H,访问的是地址0A0H的寄存器,而不是P2口(它的地址也是0A0H)。
MOV @R0 , #data
堆栈操作也是简介寻址方式。因此,高128字节数据RAM也可用于堆栈空间。
看门狗定时器
WDT是一种需要软件控制的复位方式。WDT 由13位计数器和特殊功能寄存器中的看
门狗定时器复位存储器(WDTRST)构成。WDT 在默认情况下无法工作;为了激活
WDT,户用必须往WDTRST 寄存器(地址:0A6H)中依次写入01EH 和0E1H。当
WDT激活后,晶振工作,WDT在每个机器周期都会增加。WDT计时周期依赖于外部
时钟频率。除了复位(硬件复位或WDT溢出复位),没有办法停止WDT工作。当WDT
溢出,它将驱动RSR引脚一个高个电平输出。
WDT的使用
为了激活WDT,用户必须向WDTRST寄存器(地址为0A6H的SFR)依次写入0E1H
和0E1H。当WDT激活后,用户必须向WDTRST写入01EH和0E1H喂狗来避免WDT
溢出。当计数达到8191(1FFFH)时,13 位计数器将会溢出,这将会复位器件。晶振正
常工作、WDT激活后,每一个机器周期WDT 都会增加。为了复位WDT,用户必须向
WDTRST 写入01EH 和0E1H(WDTRST 是只读寄存器)。WDT 计数器不能读或写。
当WDT 计数器溢出时,将给RST 引脚产生一个复位脉冲输出,这个复位脉冲持续96
个晶振周期(TOSC),其中TOSC=1/FOSC。为了很好地使用WDT,应该在一定时间
内周期性写入那部分代码,以避免WDT复位。
掉电和空闲方式下的WDT
在掉电模式下,晶振停止工作,这意味这WDT也停止了工作。在这种方式下,用户不
必喂狗。有两种方式可以离开掉电模式:硬件复位或通过一个激活的外部中断。通过硬
件复位退出掉电模式后,用户就应该给WDT 喂狗,就如同通常AT89S52 复位一样。
通过中断退出掉电模式的情形有很大的不同。中断应持续拉低很长一段时间,使得晶振
AT89S52
10
稳定。当中断拉高后,执行中断服务程序。为了防止WDT在中断保持低电平的时候复
位器件,WDT 直到中断拉低后才开始工作。这就意味着WDT 应该在中断服务程序中
复位。
为了确保在离开掉电模式最初的几个状态WDT不被溢出,最好在进入掉电模式前就复
位WDT。
在进入待机模式前,特殊寄存器AUXR的WDIDLE位用来决定WDT是否继续计数。
默认状态下,在待机模式下,WDIDLE=0,WDT继续计数。为了防止WDT在待机模
式下复位AT89S52,用户应该建立一个定时器,定时离开待机模式,喂狗,再重新进
入待机模式。
UART
在AT89S52 中,UART 的操作与AT89C51 和AT89C52 一样。为了获得更深入的关于
UART 的信息,可参考ATMEL 网站(http://www.atmel.com)。从这个主页,选择
“Procts”,然后选择“8051-Architech Flash Microcontroller”,再选择“Proct
Overview”即可。
定时器0 和定时器1
在AT89S52 中,定时器0 和定时器1 的操作与AT89C51 和AT89C52 一样。为了获得
更深入的关于UART 的信息,可参考ATMEL 网站(http://www.atmel.com)。从这个主
页,选择“Procts”,然后选择“8051-Architech Flash Microcontroller”,再选择“Proct
Overview”即可。
定时器2
定时器2是一个16位定时/计数器,它既可以做定时器,又可以做事件计数器。其工作
方式由特殊寄存器T2CON中的C/T2位选择(如表2所示)。定时器2有三种工作模式:
捕捉方式、自动重载(向下或向上计数)和波特率发生器。如表3 所示,工作模式由
T2CON中的相关位选择。定时器2 有2 个8位寄存器:TH2和TL2。在定时工作方式
中,每个机器周期,TL2 寄存器都会加1。由于一个机器周期由12 个晶振周期构成,
因此,计数频率就是晶振频率的1/12。
表3 定时器2工作模式
RCLK+TCLK CP/RL2 TR2 MODE
0 0 1 16位自动重载
0 1 1 16位捕捉
1 × 1 波特率发生器
× × 0 (不用)
在计数工作方式下,寄存器在相关外部输入角T2 发生1 至0 的下降沿时增加1。在这
AT89S52
11
种方式下,每个机器周期的S5P2期间采样外部输入。一个机器周期采样到高电平,而
下一个周期采样到低电平,计数器将加1。在检测到跳变的这个周期的S3P1 期间,新
的计数值出现在寄存器中。因为识别1-0的跳变需要2个机器周期(24个晶振周期),
所以,最大的计数频率不高于晶振频率的1/24。为了确保给定的电平在改变前采样到
一次,电平应该至少在一个完整的机器周期内保持不变。
捕捉方式
在捕捉模式下,通过T2CON中的EXEN2来选择两种方式。如果EXEN2=0,定时器2
时一个16位定时/计数器,溢出时,对T2CON 的TF2标志置位,TF2引起中断。如果
EXEN2=1,定时器2做相同的操作。除上述功能外,外部输入T2EX引脚(P1.1)1至
0的下跳变也会使得TH2和TL2中的值分别捕捉到RCAP2H和RCAP2L中。除此之外,
T2EX 的跳变会引起T2CON 中的EXF2 置位。像TF2 一样,T2EX 也会引起中断。捕
捉模式如图5所示。
图5 定时器的捕捉模式
自动重载
当定时器2 工作于16 位自动重载模式,可对其编程实现向上计数或向下计数。这一功
能可以通过特殊寄存器T2MOD(见表4)中的DCEN(向下计数允许位)来实现。通
过复位,DCEN 被置为0,因此,定时器2 默认为向上计数。DCEN 设置后,定时器2
就可以取决于T2EX向上、向下计数。
如图6 所示,DCEN=0 时,定时器2 自动计数。通过T2CON 中的EXEN2 位可以选择
两种方式。如果EXEN2=0,定时器2计数,计到0FFFFH后置位TF2溢出标志。计数
溢出也使得定时器寄存器重新从RCAP2H 和RCAP2L 中加载16 位值。定时器工作于
捕捉模式,RCAP2H和RCAP2L的值可以由软件预设。如果EXEN2=1,计数溢出或在
外部T2EX(P1.1)引脚上的1到0的下跳变都会触发16位重载。这个跳变也置位EXF2
中断标志位。
如图6所示,置位DCEN,允许定时器2向上或向下计数。在这种模式下,T2EX引脚
控制着计数的方向。T2EX上的一个逻辑1使得定时器2向上计数。定时器计到0FFFFH
AT89S52
12
溢出,并置位TF2。定时器的溢出也使得RCAP2H和RCAP2L中的16位值分别加载到
定时器存储器TH2和TL2中。
T2EX 上的一个逻辑0 使得定时器2 向下计数。当TH2 和TL2 分别等于RCAP2H 和
RCAP2L中的值的时候,计数器下溢。计数器下溢,置位TF2,并将0FFFFH加载到定
时器存储器中。
定时器2上溢或下溢,外部中断标志位EXF2 被锁死。在这种工作模式下,EXF2不能
触发中断。
图6 定时器2重载模式(DCEN=0)
表4 T2MOD-定时器2控制寄存器
T2MOD 地址:0C9H 复位值:XXXXXX00B
不可位寻址
- - - - - - T2OE DCEN
7 6 5 4 3 2 1 0
符号功能
- 无定义,预留扩展
T2OE 定时器2输出允许位
DCEN 置1后,定时器2可配置成向上/向下计数
AT89S52
13
图7 定时器2自动重载(DCEN=1)
图8 定时器2 波特率发生器模式
AT89S52
14
波特率发生器
通过设置T2CON(见表2)中的TCLK或RCLK可选择定时器2 作为波特率发生器。
如果定时器2作为发送或接收波特率发生器,定时器1可用作它用,发送和接收的波特
率可以不同。如图8 所示,设置RCLK 和(或)TCLK 可以使定时器2 工作于波特率
产生模式。
波特率产生工作模式与自动重载模式相似,因此,TH2 的翻转使得定时器2 寄存器重
载被软件预置16位值的RCAP2H和RCAP2L中的值。
模式1和模式3的波特率由定时器2溢出速率决定,具体如下公式:
模式1和模式3波特率=
16
2溢出率定时器
定时器可设置成定时器,也可为计数器。在多数应用情况下,一般配置成定时方式
(CP/T2=0)。定时器2 用于定时器操作与波特率发生器有所不同,它在每一机器周期
(1/12晶振周期)都会增加;然而,作为波特率发生器,它在每一机器状态(1/2晶振
周期)都会增加。波特率计算公式如下:
模式1和模式3的波特率=
)] 2 , 2 ( 65536 [ 32 L RCAP H RCAP - ′
晶振频率*原文少半个括号“(”
其中,(RCAP2H,RCAP2L)是RCAP2H和RCAP2L组成的16位无符号整数。
定时器2 作为波特率发生器,如图8 所示。图中仅仅在T2CON 中RCLK 或TCLK=1
才有效。特别强调,TH2的翻转并不置位TF2,也不产生中断; EXEN2置位后,T2EX
引脚上1~0的下跳变不会使(RCAP2H,RCAP2L)重载到(TH2,TL2)中。因此,
定时器2作为波特率发生器,T2EX也还可以作为一个额外的外部中断。
定时器2处于波特率产生模式,TR2=1,定时器2正常工作。TH2或TL2不应该读写。
在这种模式下,定时器在每一状态都会增加,读或写就不会准确。寄存器RCAP2可以
读,但不能写,因为写可能和重载交迭,造成写和重载错误。在读写定时器2 或RCAP2
寄存器时,应该关闭定时器(TR2清0)。
可编程时钟输出
如图9 所示,可以通过编程在P1.0 引脚输出一个占空比为50%的时钟信号。这个引脚
除了常规的I/O 角外,还有两种可选择功能。它可以通过编程作为定时器/计数器2 的
外部时钟输入或占空比为50%的时钟输出。当工作频率为16MHZ时,时钟输出频率范
围为61HZ到4HZ。
为了把定时器2配置成时钟发生器,位C/T2(T2CON.1)必须清0,位T2OE(T2MOD.1)
必须置1。位TR2(T2CON.2)启动、停止定时器。时钟输出频率取决于晶振频率和定
时器2捕捉寄存器(RCAP2H,RCAP2L)的重载值,如公式所示:
时钟输出频率=
] 2 , 2 65536 [ 4 ) -(
晶振频率
L RCAP H RCAP ′
在时钟输出模式下,定时器2不会产生中断,这和定时器2用作波特率发生器一样。定
AT89S52
15
时器2也可以同时用作波特率发生器和时钟产生。不过,波特率和输出时钟频率相互并
不独立,它们都依赖于RCAP2H和RCAP2L。
图9 定时器2时钟输出模式
中断
AT89S52 有6个中断源:两个外部中断(INT0 和INT1),三个定时中断(定时器0、1、
2)和一个串行中断。这些中断如图10所示
每个中断源都可以通过置位或清除特殊寄存器IE 中的相关中断允许控制位分别使得中
断源有效或无效。IE还包括一个中断允许总控制位EA,它能一次禁止所有中断。
如表5所示,IE.6位是不可用的。对于AT89S52,IE.5位也是不能用的。用户软件不应
给这些位写1。它们为AT89系列新产品预留。
定时器2可以被寄存器T2CON中的TF2和EXF2的或逻辑触发。程序进入中断服务后,
这些标志位都可以由硬件清0。实际上,中断服务程序必须判定是否是TF2 或EXF2激
活中断,标志位也必须由软件清0。
定时器0和定时器1标志位TF0 和TF1在计数溢出的那个周期的S5P2被置位。它们的
值一直到下一个周期被电路捕捉下来。然而,定时器2 的标志位TF2 在计数溢出的那
个周期的S2P2被置位,在同一个周期被电路捕捉下来。
AT89S52
16
表4 中断允许控制寄存器(IE)
(MSB) (LSB)
EA - ET2 ES ET1 EX1 ET0 EX0
中断允许控制位=1,允许中断
中断允许控制位=0,禁止中断
符号位地址功能
EA IE.7 中断总允许控制位。EA=0,中断总禁止;EA=1,各中断由各
自的控制位设定
- IE.6 预留
ET2 IE.5 定时器2中断允许控制位
ES IE.4 串行口中断允许控制位
ET1 IE.3 定时器1中断允许控制位
EX1 IE.2 外部中断1允许控制位
ET0 IE.1 定时器0中断允许控制位
EX0 IE.0 外部中断1允许控制位
图10 中断源
AT89S52
17
晶振特性
如图10 所示,AT89S52 单片机有一个用于构成内部振荡器的反相放大器,XTAL1 和
XTAL2 分别是放大器的输入、输出端。石英晶体和陶瓷谐振器都可以用来一起构成自
激振荡器。从外部时钟源驱动器件的话,XTAL2 可以不接,而从XTAL1 接入,如图
12 所示。由于外部时钟信号经过二分频触发后作为外部时钟电路输入的,所以对外部
时钟信号的占空比没有其它要求,最长低电平持续时间和最少高电平持续时间等还是要
符合要求的。
图11 内部振荡电路连接图图12 外部振荡电路连接图
石英晶振C1,C2=30PF±10PF
陶瓷谐振器C1,C2=40PF±10PF
空闲模式
在空闲工作模式下,CPU 处于睡眠状态,而所有片上外部设备保持激活状态。这种状
态可以通过软件产生。在这种状态下,片上RAM和特殊功能寄存器的内容保持不变。
空闲模式可以被任一个中断或硬件复位终止。
由硬件复位终止空闲模式只需两个机器周期有效复位信号,在这种情况下,片上硬件禁
止访问内部RAM,而可以访问端口引脚。空闲模式被硬件复位终止后,为了防止预想
不到的写端口,激活空闲模式的那一条指令的下一条指令不应该是写端口或外部存储
器。
掉电模式
在掉电模式下,晶振停止工作,激活掉电模式的指令是最后一条执行指令。片上RAM
AT89S52
18
和特殊功能寄存器保持原值,直到掉电模式终止。掉电模式可以通过硬件复位和外部中
断退出。复位重新定义了SFR 的值,但不改变片上RAM 的值。在VCC未恢复到正常
工作电压时,硬件复位不能无效,并且应保持足够长的时间以使晶振重新工作和初始化。
表6 空闲模式和掉电模式下的外部引脚状态
模式程序存储器ALE PSEN PORT0 PORT1 PORT2 PORT3
空闲内部1 1 数据数据数据数据
空闲外部1 1 浮空数据地址数据
掉电内部0 0 数据数据数据数据
掉电外部0 0 浮空数据数据数据
程序存储器的加密位
AT89S52有三个加密位不可编程(U)和可编程获得下表所示的功能。
表7 加密位保护模式
加密位1(LB1)编程后,EA 引脚的逻辑值被采样,并在复位期间锁存。如果器件复
位,而没有复位,将锁存一个随机值,直到复位为止。为了器件功能正常,锁存到的
EA值必须和这个引脚的当前逻辑电平一致。
Flash编程―并行模式
AT89S52 带有用作编程的片上Flash 存储器阵列。编程接口需要一个高电压(12V)编
程使能信号,并且兼容常规的第三方*(原文:third-party,不知道对不对)Flash或EPROM
编程器。
AT89S52程序存储阵列采用字节式编程。
编程方法
对AT89S52编程之前,需根据Flash编程模式表和图13、图14对地址、数据和控制信
号设置。可采用下列步骤对AT89S52编程:
AT89S52
19
1.在地址线上输入编程单元地址信号
2.在数据线上输入正确的数据
3.激活相应的控制信号
4.把EA/Vpp升至12V
5.每给Flash写入一个字节或程序加密位时,都要给ALE/PROG一次脉冲。字节写周
期时自身定制的,典型值仅仅50us。改变地址、数据重复第1步到第5步,知道全
部文件结束。
Data Polling
AT89S52用Data Polling作为一个字节写周期结束的标志特征。
D. 利用单片机(STC89C52)设计倒计时数字钟
#include<reg51.h>
#define uchar unsigned char
uchar code ledtab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40};//0-9
unsigned char sec=0,min=0,hour=24,scanled;
unsigned char key,mode,time;
unsigned char disdat[8];
unsigned char alarm[3]={23,59,0},dly;
sbit keyhu=P1^0;
sbit keyhd=P1^1;
sbit keymu=P1^2;
sbit keymd=P1^3;
sbit keysu=P1^4;
sbit keysd=P1^5;
sbit keyst=P1^6;
sbit fmq=P3^0;
bit flag=0;
void delay(unsigned int x)
{
unsigned int i,j;
for(i=0;i<x;i++)
for(j=0;j<120;j++);
}
void dischg()
{
disdat[0]=sec%10;
disdat[1]=sec/10;
disdat[2]=min%10;
disdat[3]=min/10;
disdat[4]=hour%10;
disdat[5]=hour/10;
}
void t0isr() interrupt 1//秒计时
{
TH0=0x3c;
TL0=0xb0;
time++;
switch(mode)
{
case 0:
if(time==20)
{
time=0;
sec++;
if(sec>59)
{
sec=0;
min++;
if(min>59)
{
min=0;
hour++;
if(hour>23)hour=0;
}
}
}
break;
case 1:
if(time==20)
{
time=0;
if(sec>0 && flag==0)sec--;
else if(min>0 && flag==0){sec=59;min--;}
else if(hour>0 && flag==0){sec=59;min=59;hour--;}
if((hour == alarm[0]) && (min == alarm[1]) && (sec == alarm[2])){fmq=1;flag=1;dly++;}
}
break;
}
if(dly>=2){fmq=0;flag=0;TR0=0;dly=0;}
dischg();
}
void t1isr() interrupt 3//显示
{
TH1=0xec;
TL1=0x78;
switch(scanled)
{
case 0:
P2=0x20;
P0=~ledtab[disdat[5]];
break;
case 1:
P2=0x10;
P0=~ledtab[disdat[4]]&0x7f;
break;
case 2:
P2=0x08;
P0=~ledtab[disdat[3]];
break;
case 3:
P2=0x04;
P0=~ledtab[disdat[2]]&0x7f;
break;
case 4:
P2=0x02;
P0=~ledtab[disdat[1]];
break;
case 5:
P2=0x01;
P0=~ledtab[disdat[0]];
break;
default:break;
}
scanled++;
scanled%=6;
}
main()
{
TMOD=0x11;
TH0=0x3c;
TL0=0xb0;
TH1=0xec;
TL1=0x78;
TR1=1;
TR0=0;
ET0=1;
ET1=1;
EA=1;
fmq=0;
scanled=0;
time=0;
mode=1;
dischg();
while(1)
{
if(keyhu==0)
{
while(keyhu==0);
TR0=0;
hour++;
hour%=24;
}
if(keyhd==0)
{
while(keyhd==0);
TR0=0;
if(hour>0)hour--;
if(hour==0)hour=23;
}
if(keymu==0)
{
while(keymu==0);
TR0=0;
min++;
min%=60;
}
if(keymd==0)
{
while(keymd==0);
TR0=0;
if(min>0)min--;
if(min==0)min=59;
}
if(keysu==0)
{
while(keysu==0);
TR0=0;
sec++;
sec%=60;
}
if(keysd==0)
{
while(keysd==0);
TR0=0;
if(sec>0)sec--;
if(sec==0)sec=59;
}
if(keyst==0)
{
while(keyst==0);
TR0=~TR0;
}
dischg();
}
}
E. 基于8051单片机的数字钟设计
1602液晶显示
你的单片机原理图没发
程序很依赖硬件的哦
这个程序你查考下,有三个键按可以修改时间的。
#include
#define
uchar
unsigned
char
#define
uint
unsigned
int
sbit
la=P2^6;
sbit
wela=P2^7;
sbit
rs=P3^5;
sbit
lcden=P3^4;
sbit
s1=P3^0;
sbit
s2=P3^1;
sbit
s3=P3^2;
sbit
rd=P3^7;
uchar
count,s1num;
char
miao,shi,fen;
uchar
code
table[]="
2009-5-17
MON";
\\
uchar
code
table1[]="
00:00:00";
\\自己设下
void
delay(uint
z)
{
uint
x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void
write_com(uchar
com)
{
rs=0;
lcden=0;
P0=com;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void
write_date(uchar
date)
{
rs=1;
lcden=0;
P0=date;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void
init()
{
uchar
num;
la=0;
wela=0;
lcden=0;
//
fen=59;
//
miao=53;
//
shi=23;
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
write_com(0x80);
for(num=0;num<15;num++)
{
write_date(table[num]);
delay(5);
}
write_com(0x80+0x40);
for(num=0;num<12;num++)
{
write_date(table1[num]);
delay(5);
}
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;
ET0=1;
TR0=1;
}
void
write_sfm(uchar
add,uchar
date)
{
uchar
shi,ge;
shi=date/10;
ge=date%10;
write_com(0x80+0x40+add);
write_date(0x30+shi);
write_date(0x30+ge);
}
void
keyscan()
{
rd=0;
if(s1==0)
{
delay(5);
if(s1==0)
{
s1num++;
while(!s1);
if(s1num==1)
{
TR0=0;
write_com(0x80+0x40+10);
write_com(0x0f);
}
}
if(s1num==2)
{
write_com(0x80+0x40+7);
}
if(s1num==3)
{
write_com(0x80+0x40+4);
}
if(s1num==4)
{
s1num=0;
write_com(0x0c);
TR0=1;
}
}
if(s1num!=0)
{
if(s2==0)
{
delay(5);
if(s2==0)
{
while(!s2);
if(s1num==1)
{
miao++;
if(miao==60)
miao=0;
write_sfm(10,miao);
write_com(0x80+0x40+10);
}
if(s1num==2)
{
fen++;
if(fen==60)
fen=0;
write_sfm(7,fen);
write_com(0x80+0x40+7);
}
if(s1num==3)
{
shi++;
if(shi==24)
shi=0;
write_sfm(4,shi);
write_com(0x80+0x40+4);
}
}
}
if(s3==0)
{
delay(5);
if(s3==0)
{
while(!s3);
if(s1num==1)
{
/*
if(miao==0)
{
miao=59;
write_sfm(10,miao);
write_com(0x80+0x40+10);
}*/
miao--;
if(miao==-1)
miao=59;
write_sfm(10,miao);
write_com(0x80+0x40+10);
}
if(s1num==2)
{
fen--;
if(fen==-1)
fen=59;
write_sfm(7,fen);
write_com(0x80+0x40+7);
}
if(s1num==3)
{
shi--;
if(shi==-1)
shi=23;
write_sfm(4,shi);
write_com(0x80+0x40+4);
}
}
}
}
}
void
main()
{
init();
while(1)
{
keyscan();
}
//
while(1);
}
void
timer0()
interrupt
1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
count++;
if(count==18)
{
count=0;
miao++;
if(miao==60)
{
miao=0;
fen++;
if(fen==60)
{
fen=0;
shi++;
if(shi==24)
{
shi=0;
}
write_sfm(4,shi);
}
write_sfm(7,fen);
}
write_sfm(10,miao);
}
}
F. 基于单片机基础的指针式电子时钟设计
您好,方案一:静态显示。所谓静态显示,就是每个数码管的每一个段码都由一个单片机的I/O端口进行驱动。静态驱动的优点是编程简单,显示亮度高,缺点是占用I/O端口多,如驱动5个数码管静态显示则需要5×8=40根I/O端口来驱动。故实际应用时必须增加驱动器进行驱动,增加了硬体电路的复杂性。
方案二:动态显示。数码管动态显示接口是单片机中应用最为广泛的一种显示方式之一,动态驱动是将所有数码管的8个显示笔划"a,b,c,d,e,f,g,dp "的同名端连在一起,另外为每个数码管的公共极COM增加位元选通控制电路,位元选通由各自独立的I/O线控制,当单片机输出字形码时,所有数码管都接收到相同的字形码,但究竟是那个数码管会显示出字形,取决于单片机对位元选通COM端电路的控制,所以我们只要将需要显示的数码管的选通控制打开,该位元就显示出字形,没有选通的数码管就不会亮。
透过分时轮流控制各个LED数码管的COM端,就使各个数码管轮流受控显示,这就是动态驱动。在轮流显示过程中,每位元数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极体的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示资料,不会有闪烁感,动态显示的效果和静态显示是一样的,能够节省大量的I/O端口,而且功耗更低。
G. 基于单片机的数字钟 论文设计
ORG 0000H ;程序入口地址
LJMP START
ORG 000BH ;定时器0中断入口地址
LJMP TIMER_0
ORG 0300H
/*****程序开始,初始化*****/
START:
SETB 48H ;使用一个bit位用于调时闪烁标志
SETB 47H ;使用一个bit位用于产生脉冲用于调时快进时基
MOV R1,#0 ;调整选择键功能标志:0正常走时、1调时、2调分、3调秒
MOV 20H,#00H ;用于控制秒基准时钟源的产生
MOV 21H,#00H ;清零秒寄存器
MOV 22H,#00H ;清零分寄存器
MOV 23H,#00H ;清零时寄存器
MOV 24H,#00H ;用于控制调时闪烁的基准时钟的产生
MOV IP,#02H ;IP,IE初始化
MOV IE,#82H
MOV TMOD,#01H ;设定定时器0工作方式1
MOV TH0,#3CH
MOV TL0,#0B0H ;赋定时初值,定时50ms
SETB TR0 ;启动定时器0
MOV SP,#40H ;重设堆栈指针
/*****主程序*****/
MAIN:
LCALL DISPLAY ;调用显示子程序
LCALL KEY_SCAN ;调用按键检测子程序
JZ MAIN ;无键按下则返回重新循环
LCALL SET_KEY ;调用选择键处理子程序
JB 46H,MAIN ;如果已进行长按调整(调时快进),则不再执行下面的单步调整
LCALL ADD_KEY ;调用增加键处理子程序,加一
LCALL DEC_KEY ;调用减少键处理子程序,减一
LJMP MAIN ;重新循环
/*****定时器中断服务程序*****/
TIMER_0:
PUSH ACC
PUSH PSW ;保护现场
MOV TH0,#3CH
MOV TL0,#0B0H ;重新赋定时初值
CPL 47H ;产生脉冲用于调时快进时基
INC 24H
MOV A,24H
CJNE A,#10,ADD_TIME ;产生0.5秒基准时钟,用于调时闪烁
CPL 48H ;取反调时闪烁标志位
MOV 24H,#00H
ADD_TIME: ;走时
INC 20H
MOV A,20H
CJNE A,#20,RETI1 ;产生1秒基准时钟
MOV 20H,#00H ;一秒钟时间到,清零20H
MOV A,21H
ADD A,#01H
DA A ;作十进制调整
MOV 21H,A
CJNE A,#60H,RETI1
MOV 21H,#00H ;一分钟到
MOV A,22H
ADD A,#01H
DA A
MOV 22H,A
CJNE A,#60H,RETI1
MOV 22H,#00H ;一小时到
MOV A,23H
ADD A,#01H
DA A
MOV 23H,A
CJNE A,#24H,RETI1
MOV 23H,#00H ;到24点,清零小时
RETI1:
POP PSW
POP ACC ;恢复现场
RETI ;中断返回
/*****显示处理*****/
DISPLAY:
MOV A,21H ;秒
ANL A,#0FH
MOV 2FH,A ;转换出秒个位,存入2FH
MOV A,21H
ANL A,#0F0H
SWAP A
MOV 2EH,A ;转换出秒十位,存入2EH
JB 46H,MIN ;如果长按按键(调时快进),则跳过闪烁处理程序
CJNE R1,#3,MIN ;如果R1为3,闪烁秒位待调整
JB 48H,MIN
MOV 2FH,#0AH ;使该位为10,查表得到使该位不显示的输出
MOV 2EH,#0AH
MIN:
MOV A,22H ;分
ANL A,#0FH
MOV 2DH,A ;转换出分个位,存入2DH
MOV A,22H
ANL A,#0F0H
SWAP A
MOV 2CH,A ;转换出分十位,存入2CH
JB 46H,HOUR ;如果长按按键(调时快进),则跳过闪烁处理程序
CJNE R1,#2,HOUR ;如果R1为2,闪烁分位待调整
JB 48H,HOUR
MOV 2DH,#0AH ;使该位为10,查表得到使该位不显示的输出
MOV 2CH,#0AH
HOUR:
MOV A,23H ;时
ANL A,#0FH
MOV 2BH,A ;转换出时个位,存入2BH
MOV A,23H
ANL A,#0F0H
SWAP A
MOV 2AH,A ;转换出时十位,存入2AH
JB 46H,DISP ;如果长按按键(调时快进),则跳过闪烁处理程序
CJNE R1,#1,DISP ;如果R1为1,闪烁时位待调整
JB 48H,DISP
MOV 2BH,#0AH ;使该位为10,查表得到使该位不显示的输出
MOV 2AH,#0AH
/*****数码管动态扫描显示*****/
DISP:
MOV DPTR,#TABLE
MOV A,2FH
MOVC A,@A+DPTR
MOV P0,A
setb P2.7
LCALL DELAY
clr P2.7 ;显示秒个位
MOV A,2EH
MOVC A,@A+DPTR
MOV P0,A
setb P2.6
LCALL DELAY
clr P2.6 ;显示秒十位
MOV A,#0BFH
MOV P0,A
setb P2.5
LCALL DELAY
clr P2.5 ;显示"-"
MOV A,2DH
MOVC A,@A+DPTR
MOV P0,A
setb P2.4
LCALL DELAY
clr P2.4 ;显示分个位
MOV A,2CH
MOVC A,@A+DPTR
MOV P0,A
setb P2.3
LCALL DELAY
clr P2.3 ;显示分十位
MOV A,#0BFH
MOV P0,A
setb P2.2
LCALL DELAY
clr P2.2 ;显示"-"
MOV A,2BH
MOVC A,@A+DPTR
MOV P0,A
setb P2.1
LCALL DELAY
clr P2.1 ;显示时个位
MOV DPTR,#TABLE1 ;该位使用TABLE1以消除前置0
MOV A,2AH
MOVC A,@A+DPTR
MOV P0,A
setb P2.0
LCALL DELAY
clr P2.0 ;显示时十位
RET
/*****按键检测子程序*****/
KEY_SCAN:
CLR 46H ;关闭长按调整(调时快进)标志
MOV P1,#0FFH ;将P1口设置成输入状态
MOV A,P1
CPL A
ANL A,#07H ;P1口低3位连接3个按键,只判断该3位
JZ EXIT_KEY ;无键按下则返回
LCALL DELAY ;延时去抖动
MOV A,P1 ;重新判断
CPL A
ANL A,#07H
JZ EXIT_KEY ;键盘去抖动
MOV R5,A ;临时将键值存入R5
MOV R4,#00H ;用于控制调时快进速度
;设置为00H是为了在进入长按处理前加长延时区分用户的长按与短按,防止误快进
LOOP: ;进入长按处理
LCALL DISPLAY ;使长按时显示正常
MOV A,P1
CPL A
ANL A,#07H
JB 47H,LOOP1
INC R4 ;调时快进间隔时间基准加1
LOOP1:
CJNE R1,#03H,LOOP2 ;如果调秒时长按,则不处理
LJMP LOOP3
LOOP2:
CJNE R4,#99H,LOOP3
MOV R4,#70H ;确认用户长按后,重新设定起始值,加快调时快进速度
SETB 46H ;长按调整(调时快进)标志
LCALL ADD_KEY
LCALL DEC_KEY
LOOP3:
JNZ LOOP ;等待键释放
MOV A,R5 ;输出键值
RET
EXIT_KEY:
RET
/*****延时子程序*****/
DELAY:
MOV R7,#150
DJNZ R7,$
RET
/*****选择键处理子程序*****/
SET_KEY:
CJNE R5,#01H,EXIT ;选择键键值
INC R1 ;调整选择功能标志加一
CJNE R1,#4,EXIT
MOV R1,#0
MOV 24H,#00H ;调时闪烁基准清零
RET
/*****增加键处理子程序*****/
ADD_KEY:
CJNE R5,#02H,EXIT ;增加键键值
CJNE R1,#01H,NEXT1 ;选择键功能标志为1,调时,否则跳出
MOV A,23H
ADD A,#01H
DA A
MOV 23H,A
CJNE A,#24H,EXIT
MOV 23H,#00H
NEXT1:
CJNE R1,#02H,NEXT2 ;选择键功能标志为2,调分,否则跳出
MOV A,22H
ADD A,#01H
DA A
MOV 22H,A
CJNE A,#60H,EXIT
MOV 22H,#00H
NEXT2:
CJNE R1,#03H,EXIT ;选择键功能标志为3,调秒,否则跳出
MOV 21H,#00H ;如增加键按下直接清零秒
RET
/*****减少键处理子程序*****/
DEC_KEY:
CJNE R5,#04H,EXIT ;减少键键值
CJNE R1,#01H,NEXT3 ;选择键功能标志为1,调时,否则跳出
MOV A,23H
ADD A,#99H
DA A
MOV 23H,A
CJNE A,#99H,EXIT
MOV 23H,#23H
NEXT3:
CJNE R1,#02H,NEXT4 ;选择键功能标志为2,调分,否则跳出
MOV A,22H
ADD A,#99H
DA A
MOV 22H,A
CJNE A,#99H,EXIT
MOV 22H,#59H
NEXT4:
CJNE R1,#03H,EXIT ;选择键功能标志为3,调秒,否则跳出
MOV 21H,#00H ;如较少键按下直接清零秒
RET
/*****万用返回子程序*****/
EXIT:
RET
/*****数码管字形编码表*****/
TABLE:
DB 0C0H,0F9H,0A4H,0B0H,99H,92H,82H,0F8H,80H,90H,0FFH ;字形显示编码
TABLE1:
DB 0FFH,0F9H,0A4H,0B0H,99H,92H,82H,0F8H,80H,90H,0FFH ;小时位的十位数编码,该位如果为0则不显示
END ;程序结束
H. 单片机的数字钟设计 利用51单片机设计一个数字钟,至少实现基本的时分秒功能,要有必要的程序、程序流程图
这有一篇给你参考:基于51单片机的多功能数字钟系统设计(免费下载)
地址:http://www.mcudata.com/Webmaster/51danpianji/2010/0819/4435.html
第1章 绪 论
第2章 MCS-51单片机的结构
第3章 电路的硬件设计
第4章 电路的软件设计
第5章 结论与展望
5.1 结论
5.2 单片机的发展趋势
I. 单片机数字时钟课程设计
这个 我正在学单片机,也刚刚做过了这个实验没多久,不过我的是8098单片机,确实是汇编语言。不过我做的仅仅是个电子钟,你可以随时改变你输入的时间然后它就会按时分秒跳动,我做的是24小时制的。不过我没有弄闹钟额……不过也简单,可以弄一个中断申请就ok。话说你的闹钟要求是什么?我记得8098是不能响的,只有一个发光二极管可以亮一亮……
话说能请你把问题补充一下么?我的程序写在纸上,然后我们还要求是要把程序翻译出机器码然后在单片机上实验出结果的。所以我连机器码都翻译了的……实在不知道你们的要求。
原理可以先和你说一下:主程序先是一系列的初始化(中断悬挂的清零和寄存器的设置,堆栈的设置等),然后开启中断,写显示程序(显示程序前要弄好你显示的寄存器以及扫描子程序的地址,还要对十六进制数进行转换变成十进制数,只要做一个除法就行,用十六进制数除以A就能够得出相应的十进制数。)
然后就是你的中断程序了,比如你的中断申请是每10ms申请一次,那你就计数,如果到了100次中断了,那就秒加一,再查看秒是否到60,是则清零让分加一,否则跳到中断程序末端;然后再依次查分和时。最后中断程序的末端还要用一次计时器软件中断申请。再跳回主程序反复运行。可能比较麻烦,我记得我打的草稿就好多张纸呢,后来在16进制向10进制转换的时候还出了个寄存器的问题。
不知道和你程序的要求是否相同= =。
期待你能够补充一下你的问题。
J. 基于单片机的数字时钟怎么做
#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int
sbit la=P2^6;
sbit wela=P2^7;
sbit rs=P3^5;
sbit lcden=P3^4;
sbit s1=P3^0;
sbit s2=P3^1;
sbit s3=P3^2;
sbit rd=P3^7;
uchar count,s1num;
char miao,shi,fen;
uchar code table[]=" 2007-7-30 MON";
uchar code table1[]=" 00:00:00";
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void write_com(uchar com)
{
rs=0;
lcden=0;
P0=com;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void write_date(uchar date)
{
rs=1;
lcden=0;
P0=date;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void init()
{
uchar num;
la=0;
wela=0;
lcden=0;
// fen=59;
// miao=53;
// shi=23;
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
write_com(0x80);
for(num=0;num<15;num++)
{
write_date(table[num]);
delay(5);
}
write_com(0x80+0x40);
for(num=0;num<12;num++)
{
write_date(table1[num]);
delay(5);
}
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;
ET0=1;
TR0=1;
}
void write_sfm(uchar add,uchar date)
{
uchar shi,ge;
shi=date/10;
ge=date%10;
write_com(0x80+0x40+add);
write_date(0x30+shi);
write_date(0x30+ge);
}
void keyscan()
{
rd=0;
if(s1==0)
{
delay(5);
if(s1==0)
{ s1num++;
while(!s1);
if(s1num==1)
{
TR0=0;
write_com(0x80+0x40+10);
write_com(0x0f);
}
}
if(s1num==2)
{
write_com(0x80+0x40+7);
}
if(s1num==3)
{
write_com(0x80+0x40+4);
}
if(s1num==4)
{
s1num=0;
write_com(0x0c);
TR0=1;
}
}
if(s1num!=0)
{
if(s2==0)
{
delay(5);
if(s2==0)
{
while(!s2);
if(s1num==1)
{
miao++;
if(miao==60)
miao=0;
write_sfm(10,miao);
write_com(0x80+0x40+10);
}
if(s1num==2)
{
fen++;
if(fen==60)
fen=0;
write_sfm(7,fen);
write_com(0x80+0x40+7);
}
if(s1num==3)
{
shi++;
if(shi==24)
shi=0;
write_sfm(4,shi);
write_com(0x80+0x40+4);
}
}
}
if(s3==0)
{
delay(5);
if(s3==0)
{
while(!s3);
if(s1num==1)
{
/* if(miao==0)
{
miao=59;
write_sfm(10,miao);
write_com(0x80+0x40+10);
}*/
miao--;
if(miao==-1)
miao=59;
write_sfm(10,miao);
write_com(0x80+0x40+10);
}
if(s1num==2)
{
fen--;
if(fen==-1)
fen=59;
write_sfm(7,fen);
write_com(0x80+0x40+7);
}
if(s1num==3)
{
shi--;
if(shi==-1)
shi=23;
write_sfm(4,shi);
write_com(0x80+0x40+4);
}
}
}
}
}
void main()
{
init();
while(1)
{
keyscan();
}
// while(1);
}
void timer0() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
count++;
if(count==18)
{
count=0;
miao++;
if(miao==60)
{
miao=0;
fen++;
if(fen==60)
{
fen=0;
shi++;
if(shi==24)
{
shi=0;
}
write_sfm(4,shi);
}
write_sfm(7,fen);
}
write_sfm(10,miao);
}
}
这个是数字时钟的源程序,用12864夜晶显示。