(1)学习AT89C51内部定时/计数器的原理及应用
(2)了解使用单片机处理复杂逻辑的方法
(3)掌握多位数码管动态显示的方法
(4)掌握DS18B20数字温度传感器的工作原理及使用方法
(5)掌握对DS18B20转换数据进行处理的方法
(6)学习用数码管显示复杂数据的方法
用AT89C51单片机的定时/计数器T0产生一秒的定时时间,作为秒计数时间,当一秒产生时,秒计数加1。开机时显示00-00-00的时间,开始计时:P1.0控制“秒“的调整,每按一次加1秒;P1.1控制“分“的调整,每按一次加1分;P1.2控制”时“的调整,没按一次加1小时。计时满23-59-59时,返回00-00-00重新计时。P1.3用做复位键,在计时过程中如果按下复位键,则返回00-00-00重新计时。
用AT89C51控制DS18B20,读取数据,并对DS18B20转换后的数据进行处理,最后在数码管上显示DS18B20测出的温度。要求使用6位数码管显示,最高位为符号位,如果温度值为正,不显示,如果温度为负,则显示负号;第2—4位显示温度值的整数部分,并在第4位数据上显示小数点;第5位显示一位小数,最低位显示摄氏度符号“C”。
a.用AT89C51单片机的定时/计数器T0产生一秒的定时时间,作为秒计数时间。
b.当一秒产生时,秒计数加1。
c.开机时,显示00-00-00,并开始连续计时。计时满23-59-59时,返回00-00-00重新开始计时。
d.用AT89C51控制DS18B20,读取数据
e.对DS18B20转换后的数据进行处理,转换成实际温度值
f. 将符号位,整数值和小数值分别存放在特定的存储单元中.
在以上设计基础上,在单片机的P1.0-P1.3口分别接入4个按键:
a.P1.0控制“秒”的调整,每按一次加1秒
b.P1.1控制“分”的调整,每按一次加1分
c.P1.2控制“时”的调整,每按一次加1时
d.P1.3用作复位键,在计时过程中如果按下复位键,则返回00-00-00重新计数。
e.使用6位数码管显示测得的温度
f.最高位为符号位,如果温度值为正,不显示,如果温度为负,则显示负号
g.第2—4位显示温度值的整数部分,并在第4位数据上显示小数点
h.第5位显示一位小数
i.最低位显示摄氏度符号“C”
项目工程及仿真资料(链接:https://pan.baidu.com/s/1ILLeTeofJFg4dU7A5gznVw 提取码:1234 )
本实验主要有单片机、七个按键、一个8位数码管、一个6位数码管以及温度传感器组成,实现电路如下:
a.各个变量与引脚的定义 本实验将温度传感器的输出口DQ输入到单片机的P1_4处。
#include
#define uchar unsigned char
#define uint unsigned int
unsigned char tab[]={ 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40,0x39};
uchar code led_7seg[11]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf};
uint hour=23,minute=59,second=580,month=12,day=31;
uint n=0,i=0;
uint count=0,year=2000,a,b;
sbit P2_0=P2^0;
sbit P2_1=P2^1;
sbit P2_2=P2^2;
sbit P2_3=P2^3;
sbit P2_4=P2^4;
sbit P2_5=P2^5;
sbit P2_6=P2^6;
sbit P2_7=P2^7;
//sbit DQ =P2^4 ;
sbit DQ =P1^4 ;
uint flag=0;
b.延迟函数定义
void delay(unsigned int j)
{
while(j--);
}
c.温度传感器的初始化 每隔一定时间对DQ口进行电平的拉高拉低。
void Init_DS18B20(void)
{
unsigned char x=0;
DQ = 1;
delay(8);
DQ = 0;
delay(80);
DQ = 1;
delay(14);
x=DQ;
delay(20);
}
d.传感器数据按字读取
ReadOneChar(void)
{
unsigned char i = 0;
unsigned char dat = 0;
for (i=8;i>0;i--)
{
DQ = 0;
dat>>=1;
DQ = 1;
if(DQ)
dat|=0x80;
delay(4);
}
return(dat);
}
e.传感器数据按字节写入dat中
void WriteOneChar(unsigned char dat)
{
unsigned char i=0;
for (i=8; i>0; i--)
{
DQ = 0;
DQ = dat&0x01;
delay(5);
DQ = 1;
dat>>=1;
}
delay(4);
}
f.将读取到的数据处理成可读的温度数据
ReadTemperature(void)
{
unsigned char a=0;
unsigned char b=0;
unsigned int t=0;
Init_DS18B20();
WriteOneChar(0xCC);
WriteOneChar(0x44);
Init_DS18B20();
WriteOneChar(0xCC);
WriteOneChar(0xBE);
a=ReadOneChar();
b=ReadOneChar();
t=b<<8;
t=t|a;
return(t);
}
g.将温度信息显示到6位数码管上
void display_tempmain(unsigned int i)
{
float temp ;
unsigned char xiaoshu;
unsigned int zhengshu;
if((0xf000&i)==0xf000)
{
i=~i+1;
P0=0x00;
P0=tab[10];
P3=0xfe;
delay(1000);
}
temp = (0x000f & i) * 10.0 * 0.0625;
xiaoshu = temp;
zhengshu= i>>4;
P0=0x00;
P0=tab[zhengshu/100];
P3=0xfd;
delay(500);
P0=0x00;
P0=tab[zhengshu%100/10];
P3=0xfb;
delay(500);
P0=0x00;
P0=tab[zhengshu%100%10] | ~0x7f;
P3=0xf7;
delay(1000);
P0=0x00;
P0=tab[xiaoshu];
P3=0xef;
delay(1000);
P0=0x00;
P0=tab[11];
P3=0xdf;
delay(1000);
P3=0xff;
}
h.对时钟进行初始化
void init_timer0(void)
{
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
ET0=1;
TR0=1;
EA=1;
}
i.当时间为23:59:59时可对其进行清零处理
void qinglin()
{
for(i=0;i<100;i++);
P2=0;
}
j.将时间数据显示在8位数码管上记为display1(void)
void display1(void)
{
// P3=led_7seg[year/1000];
P0=led_7seg[hour/10];
P2_0=1;
qinglin();
// P3=led_7seg[year/100%10];
P0=led_7seg[hour%10];
P2_1=1;
qinglin();
// P3=led_7seg[year/10%10];
P0=0xBF;
P2_2=1;
qinglin();
// P3=led_7seg[year%10];
P0=led_7seg[minute/10];
P2_3=1;
qinglin();
// P3=led_7seg[month/10];
P0=led_7seg[minute%10];
P2_4=1;
qinglin();
// P3=led_7seg[month%10];
P0=0xBF;
P2_5=1;
qinglin();
// P3=led_7seg[day/10];
P0=led_7seg[second/165];
P2_6=1;
qinglin();
// P3=led_7seg[day%10];
P0=led_7seg[second*10/165%10];
P2_7=1;
qinglin();
}
k.将日历数据显示在8为数码管上记为display2(void)
void display2(void)
{
P0=led_7seg[year/1000];
// P0=led_7seg[hour/10];
P2_0=1;
qinglin();
P0=led_7seg[year/100%10];
// P0=led_7seg[hour%10];
P2_1=1;
qinglin();
P0=led_7seg[year/10%10];
// P0=0xBF;
P2_2=1;
qinglin();
P0=led_7seg[year%10];
// P0=led_7seg[minute/10];
P2_3=1;
qinglin();
P0=led_7seg[month/10];
// P0=led_7seg[minute%10];
P2_4=1;
qinglin();
P0=led_7seg[month%10];
// P0=0xBF;
P2_5=1;
qinglin();
P0=led_7seg[day/10];
// P0=led_7seg[second/165];
P2_6=1;
qinglin();
P0=led_7seg[day%10];
// P0=led_7seg[second*10/165%10];
P2_7=1;
qinglin();
}
l.闰年情况的判断
void runnian(void)
{
if((year%4==0)&&(year%100!=0)||(year%400==0))
{
if((month==1)||(month==3)||(month==5)||(month==7)||(month==8)||(month==10)||(month==12))
{b=32;}
if((month==4)||(month==6)||(month==9)||(month==11))
{b=31;}
if(month==2)
{b=30;}
}
else{
if((month==1)||(month==3)||(month==5)||(month==7)||(month==8)||(month==10)||(month==12))
{b=32;}
if((month==4)||(month==6)||(month==9)||(month==11))
{b=31;}
if(month==2)
{b=29;}
}
}
m.1秒延迟定义,方便后续函数的编写
void dl_10ms(void)
{
int i;
for(i=1000;i>0;i--);
}
n.按键的初始化
uchar kbscan(void)
{
uchar sccode,recode;
P1=0xF0;
if((P1&0xF0)!=0xF0)
{
dl_10ms();
if((P1&0xF0)!=0xF0)
{
sccode=0xFE;
while((sccode&0x10)!=0)
{
P1=sccode;
if((P1&0xF0)!=0xF0)
{
recode=(P1&0xF0)|0x0F;
return((~sccode)+(~recode));
}
else
{
sccode=(sccode<<1)|0x01;
}
}
}
}
return 0;
}
o.主函数编写 引用上述提到的所有函数 实现按键控制时间 日历与温度的交替出现,并且可以对时间进行相应的调试
void main (void)
{
unsigned int temp;
uchar key;
init_timer0();
qinglin();
while(1)
{
// temp=ReadTemperature();
// display_tempmain(temp);
if(flag==0)
{
display1();
}
else if(flag==1)
{
display2();
}
else if(flag==2)
{
temp=ReadTemperature();
display_tempmain(temp);
}
key=kbscan();
switch(key)
{
case 0x11:
{
flag=0;
if(hour<23)hour++;
else
{
hour=0;
day++;
}
while(key==0x11)
{
key=kbscan();
}
}
break;
case 0x21:
{
flag=0;
if(minute<59) minute++;
else
{
minute=0;
hour++;
}
while(key==0x21)
{
key=kbscan();
}
}
break;
case 0x41:
{
flag=1;
runnian();
if(day<(b-1)) day++;
else
{
day=1;
month++;
if(month==13)
month=1;
}
while(key==0x41)
{
key=kbscan();
}
}
break;
case 0x81:
{
flag=1;
if(month<=11) month++;
else
{
month=1;
year++;
}
while(key==0x81)
{
key=kbscan();
}
}
break;
case 0x12:
{
flag=1;
year++;
while(key==0x12)
{
key=kbscan();
}
}
break;
case 0x22:
{
flag=1;
year--;
while(key==0x22)
{
key=kbscan();
}
}
break;
/* case 0x42:
{
flag=0;
while(key==0x42)
{
key=kbscan();
}
}
break;
case 0x82:
{
flag=1;
while(key==0x82)
{
key=kbscan();
}
}
break; */
case 0x42:
{
if(flag==0)
flag=1;
else if(flag==1)
flag=2;
else if(flag==2)
flag=0;
while(key==0x42)
{
key=kbscan();
}
}
break;
/* case 0x82:
{
if(flag==0||flag==1)
flag=2;
else flag=0;
while(key==0x82)
{
key=kbscan();
}
}
break; */
default:break;
}
}
}
p.中断函数 时间的进位
void timer0_int(void) interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
second++;
dl_10ms();
/* for(n=0;n<20;n++){
switch(n & 0x07)
{
case 0:{P0=0xFF; P2=0x00; P3=0xFF;
P0=led_7seg[hour%10];
P3=led_7seg[year/100%10];P2=0x02;}break;
case 1:{P0=0xFF; P2=0x00; P3=0xFF;
P0=led_7seg[hour/10];
P3=led_7seg[year/1000];P2=0x01;}break;
case 2:{P0=0xFF; P2=0x00; P3=0xFF;
P0=led_7seg[10];
P3=led_7seg[year/100%10];P2=0x04;}break;
case 3:{P0=0xFF; P2=0x00; P3=0xFF;
P0=led_7seg[minute%10];
P3=led_7seg[month/10];P2=0x10;}break;
case 4:{P0=0xFF; P2=0x00; P3=0xFF;
P0=led_7seg[minute/10];
P3=led_7seg[year%10];P2=0x08;}break;
case 5:{P0=0xFF; P2=0x00; P3=0xFF;
P0=led_7seg[10];
P3=led_7seg[month%10];P2=0x20;}break;
case 6:{P0=0xFF; P2=0x00; P3=0xFF;
P0=led_7seg[second%10];
P3=led_7seg[day%10];P2=0x80;}break;
case 7:{P0=0xFF; P2=0x00;
P0=led_7seg[second/10];
P3=led_7seg[day/10];P2=0x40;}break;
default:break;
}
} */
/* if(count==10)
{
count=0;
second++; */
if(second==990)
{
second=0;
minute++;
if(minute==60)
{
minute=0;
hour++;
if(hour==24)
{
hour=0;
day++;
runnian();
if(day==b)
{
day=1;
month++;
if(month==13)
{
month=1;
year++;
}
}
}
}
//}
}
}
将上述代码直接复制到工程的主函数内,通过编译出来.OBJ文件。调用到仿真器里的单片机文件内模拟烧录。即可出来原理图上的实验结果。
通过今次单片机实训,使我对单片机的认识有了更深刻的理解。通过键盘控制和数码管显示实现了基本时钟显示功能、时间调节功能,还有温度计DS18B20的显示等。能实现本设计题目的基本要求和发挥部分。
由于时间有限和本身知识水平的限制,本系统还存在一些不够完善的地方, 要作为实际应用还有一些具体细节问题需要解决。例如:不能实现只用两个按键来控制时钟时间,还不能实现闹钟等扩展功能。
希望本博客能在51单片机上给读者一定的帮助,省下找各种解决方案的时间成本。感谢各位观看,如有不足,欢迎在评论内留言与讨论。如果觉得写得好的,可以给我点赞+收藏+关注哦,再次感谢各位!